diff --git a/.gitignore b/.gitignore index 37b7bb556..078813cca 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,11 @@ target/* # Ignore system files .DS_Store Thumb.db +/bin/ +/build/ +/.gradle/ + +# Eclipse added files +.classpath +.project +.settings diff --git a/README.md b/README.md index 0b4d08fe2..1b8157d56 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,52 @@ # audiveris -This repository contains source code for the latest generation of Audiveris optical -music recognition (OMR) engine. +This repository contains source code for the latest generation of Audiveris Optical +Music Recognition (OMR) application. -## Main features - -As opposed to Audiveris [earlier generation][6], which was a stand-alone OMR application composed -of an engine and a (limited) user interface, this repository is focused on the OMR engine. - -The internals of the OMR engine are made publicly available, either directly by XML-based ".omr" -project files or via the Java API of this software. - -The engine can directly export data using [MusicXML][8] 3.0 format, via an integrated exporter. -Other exporters could build upon the engine to support other target formats. - -NOTA: The engine provides a small integrated UI which is meant for the developer to analyze, -tune or train the various parts of the engine, but not to correct the final score. -Full GUIs, meant for the end-user, are expected to be provided by external editors. - -## Building and running - -First of all, you'll need the following dependencies installed and working from -the command line: - -+ [Java Development Kit (JDK) version 7 or 8 (version 8 is recommended; version 9 is not yet supported)][1]. - **Please ensure you're running a 64-bit JVM. Audiveris doesn't support a 32-bit - JVM because deeplearning4j is 64-bit only.** -+ [Git](https://git-scm.com) version control system. +## Releases -Besides the above mentioned tools you'll need to have Tesseract language files for -[Tesseract OCR][2] to work properly. Please keep in mind that Tesseract is mandatory -for both building and running Audiveris. __It's currently not possible to use -Audiveris without Tesseract.__ +All releases are available on [Audiveris Releases][releases] page. -You'll need at least the english language data. Other required languages can be -installed, too. Please check [this guide][3] for further details. +The most recent stable version is release 5.1, published on December 13, 2018. -Moreover, opening PDFs containing vector graphics on Unix-like platforms -(including the Mac) requires [FreeType library][4] to be available in your $PATH. -Fortunately, every known OS distribution already contains a package for FreeType. - -To download Audiveris project, use the following command in a directory of your choice: - -`git clone https://github.com/Audiveris/audiveris.git` - -This will create a sub-directory named "audiveris" in your current directory and populate it with -project material (notably source code and build procedure). - -Now move to this "audiveris" project directory: - -`cd audiveris` +## Main features -Once in this "audiveris" project directory, you can: +Derived from Audiveris [earlier generation][audiveris-eg] (4.x versions), +which was limited to fast processing +of small high-quality scores in memory, this repository (starting with Audiveris 5.x versions) is +significantly more ambitious: -* Build the software via the command: +* Good recognition efficiency on real-world quality scores (as seen on [IMSLP][imslp] site) +* Effective support for large scores (with up to hundreds of pages) +* Convenient user-oriented interface to detect and correct most OMR errors +* Openness to external access +* Available on Windows, Linux and MacOS - `./gradlew build` (Linux & Mac) +The core of engine music information (OMR data) is fully documented and made publicly available, +either directly via XML-based `.omr` project files or via the Java API of this software: - `gradlew.bat build` (Windows) +* Audiveris includes an integrated exporter, which can write a subset of OMR data into +[MusicXML][musicxml] 3.0 format. +* Other exporters are expected to build upon this OMR data to support other target formats. -* Run the software, as GUI tool, via the command: +## Installing and running, for Windows only - `./gradlew run` (Linux & Mac) +Refer to HandBook [Binaries][binaries] section. - `gradlew.bat run` (Windows) +## Building and running, for Windows, MacOS, Linux and ArchLinux -### Arch Linux -For the AUR(Arch User Repository) there exist two packages which can be installed by hand(or with your AUR-helper of trust). One is [`audiveris`](https://aur.archlinux.org/packages/audiveris) which uses the [`5.1.0-rc` release](https://github.com/Audiveris/audiveris/releases/tag/5.1.0-rc) and the other one is [`audiveris-git`](https://aur.archlinux.org/packages/audiveris-git) which tracks the `master` branch. To install, simply execute: -```bash -git clone https://aur.archlinux.org/audiveris.git -#git clone https://aur.archlinux.org/audiveris-git.git -cd audiveris -makepkg -fsri -``` -Or with an AUR-helper(pikaur/yaourt/...): -```bash -pikaur -S audiveris -#pikaur -S audiveris-git -``` +Refer to HandBook [Sources][sources] section. ## Further Information -Users and Developers are encouraged to read our [Wiki][5]. - -[1]: http://www.oracle.com/technetwork/java/javase/downloads/index.html -[2]: https://github.com/tesseract-ocr/tesseract -[3]: https://github.com/tesseract-ocr/tesseract/wiki -[4]: https://www.freetype.org -[5]: https://github.com/Audiveris/audiveris/wiki -[6]: https://github.com/Audiveris/audiveris-eg -[7]: https://github.com/Audiveris -[8]: http://www.musicxml.com/ +Users and Developers are advised to read the specific [User Handbook for 5.1 release][handbook], +and the more general [Wiki][audiveris-wiki] set of articles. + +[audiveris-wiki]: https://github.com/Audiveris/audiveris/wiki +[audiveris-eg]: htps://github.com/Audiveris/audiveris-eg +[musicxml]: http://www.musicxml.com/ +[imslp]: https://imslp.org/ +[handbook]: https://bacchushlg.gitbooks.io/audiveris-5-1/content/ +[binaries]: https://bacchushlg.gitbooks.io/audiveris-5-1/content/install/binaries.html +[sources]: https://bacchushlg.gitbooks.io/audiveris-5-1/content/install/sources.html +[releases]: https://github.com/Audiveris/audiveris/releases diff --git a/build.gradle b/build.gradle index 6a317e306..4182b5b42 100644 --- a/build.gradle +++ b/build.gradle @@ -15,12 +15,14 @@ ext.companyId = "${programName}Ltd" ext.targetOSName = System.getProperty('os.name').toLowerCase()\ .startsWith('mac os x') ? 'macosx' :\ System.getProperty('os.name').split(' ')[0].toLowerCase() -ext.targetOSArch = ["i386":"x86", "i486":"x86", "i586":"x86", "i686":"x86", +ext.targetOSArch = ["i386":"x86", "i486":"x86", "i586":"x86", "i686":"x86", "x86":"x86", "amd64":"x86_64", "x86-64":"x86_64", "x86_64":"x86_64"]\ [System.getProperty('os.arch').toLowerCase()] ext.targetOS = "${project.ext.targetOSName}-${project.ext.targetOSArch}" println "targetOS=${project.ext.targetOS}" +ext.makensisPath = null + //ext.dl4jVersion = '0.8.0' //ext.nd4jVersion = '0.8.0' @@ -132,6 +134,13 @@ dependencies { ) } +// Specific configurations for specific OS dependencies +['windows-x86', 'windows-x86_64'].each { os -> + configurations.create("runtime-$os") + dependencies.add("runtime-$os", [group: 'org.bytedeco.javacpp-presets', name: 'leptonica', version: '1.73-1.3', classifier: "$os"]) + dependencies.add("runtime-$os", [group: 'org.bytedeco.javacpp-presets', name: 'tesseract', version: '3.04.01-1.3', classifier: "$os"]) +} + jar { // override default output archive name archiveName = "audiveris.jar" @@ -172,6 +181,8 @@ task "git_build"(type:Exec) { } task generateProgramId(dependsOn: git_build) { + group "build" + description "Generates ProgramId source" doLast{ project.ext.outputDir = file("$buildDir/generated-src/org/audiveris/omr") @@ -204,6 +215,88 @@ sourceSets { } } +// Make sure we have an NSIS compiler available and store it into project.ext.makensisPath +task findNsisCompiler { + description "Find path to NSIS compiler" + onlyIf {"windows" == "${project.ext.targetOSName}"} + + doLast { + // First test on PATH, then on standard program files locations + ["", "C:/Program Files (x86)/NSIS/", "C:/Program Files/NSIS/"].each { prefix -> + if (project.ext.makensisPath == null) { + try { + def path = "${prefix}makensis.exe" + def proc = [path, "/VERSION"].execute() + proc.waitFor() + println "NSIS compiler $path found, version " + proc.in.text.trim() + project.ext.makensisPath = path + } catch (ignored) { + } + } + } + + if (project.ext.makensisPath == null) { + throw new RuntimeException("Cannot find NSIS compiler (makensis.exe)!") + } + } +} + +// All installers +task installers { + group "Installers" + description "Builds all installers" +} + +// Windows installers for 32-bit and 64-bit +['windows-x86', 'windows-x86_64'].each { os -> + task "installer_$os"(type: Exec, dependsOn: [findNsisCompiler, jar, startScripts]) { + group "Installers" + description "Builds installer for $os" + onlyIf {"windows" == "${project.ext.targetOSName}"} + + // Filter line that starts with "CLASSPATH=" or "set CLASSPATH=" findClasspath, + // and update os-based classifier information. + def updateClassPath = { findClasspath, line -> + line.startsWith("${findClasspath}=") ? line.replaceAll("-${project.ext.targetOS}.jar", "-${os}.jar") : line + } + + doFirst { + mkdir "$buildDir/installers" + + // Fresh population of os-dependent folder with bin+lib items + delete "$buildDir/installers/$os" + + // bin: Scripts and icon + copy { + into "$buildDir/installers/$os/bin" + from("$buildDir/scripts/Audiveris") {filter updateClassPath.curry('CLASSPATH') } + from("$buildDir/scripts/Audiveris.bat") {filter updateClassPath.curry('set CLASSPATH')} + from "$projectDir/res/icon-256.ico" + } + + // lib: Libraries + copy { + into "$buildDir/installers/$os/lib" + from "$buildDir/jar" // audiveris.jar + from configurations.compile // Compile jars + from configurations."runtime-$os" // OS-specific jars + } + + // Set NSIS compiler command line + def suffix = (os == "windows-x86_64")? "" : "32" + commandLine "cmd", "/c",\ + "${project.ext.makensisPath}",\ + "/DPRODUCT_VERSION=$programVersion",\ + "/DPROJECT_DIR=$projectDir",\ + "/DTARGET_OS=$os",\ + "/DSUFFIX=$suffix",\ + "$projectDir/dev/installer-windows/Installer.nsi" + } + } + + installers.dependsOn("installer_$os") +} + javadoc { doFirst { copy { diff --git a/config/plugins/finale-notepad.js b/config/plugins/finale-notepad.js deleted file mode 100644 index d336fd3cd..000000000 --- a/config/plugins/finale-notepad.js +++ /dev/null @@ -1,20 +0,0 @@ -/* -------------------------------------------------------------------------- */ -/* */ -/* f i n a l e - n o t e p a d . j s */ -/* */ -/* -------------------------------------------------------------------------- */ - -/* Variable to modify according to your environment */ -var pathToExec = "C:/Program Files (x86)/Finale NotePad 2012/Finale NotePad.exe"; - -/* Title for menu item */ -pluginTitle = 'Finale Notepad'; - -/* Long description for tool tip */ -pluginTip = 'Invoke Finale Notepad on score XML'; - -/* Build sequence of command line parameters */ -function pluginCli(exportFilePath) { - importPackage(java.util); - return Arrays.asList([pathToExec, exportFilePath]); -} diff --git a/config/plugins/finale.js b/config/plugins/finale.js deleted file mode 100644 index 56e7faacd..000000000 --- a/config/plugins/finale.js +++ /dev/null @@ -1,20 +0,0 @@ -/* -------------------------------------------------------------------------- */ -/* */ -/* f i n a l e . j s */ -/* */ -/* -------------------------------------------------------------------------- */ - -/* Variable to modify according to your environment */ -var pathToExec = "C:/Program Files (x86)/Finale 2014/Finale.exe"; - -/* Title for menu item */ -pluginTitle = 'Finale'; - -/* Long description for tool tip */ -pluginTip = 'Invoke Finale on score XML'; - -/* Build sequence of command line parameters */ -function pluginCli(exportFilePath) { - importPackage(java.util); - return Arrays.asList([pathToExec, exportFilePath]); -} diff --git a/config/plugins/musescore.js b/config/plugins/musescore.js deleted file mode 100644 index a5b13fdec..000000000 --- a/config/plugins/musescore.js +++ /dev/null @@ -1,21 +0,0 @@ -/* -------------------------------------------------------------------------- */ -/* */ -/* m u s e s c o r e . j s */ -/* */ -/* -------------------------------------------------------------------------- */ - -/* Variable to modify according to your environment */ -//var pathToExec = "P:/MuseScore/bin/mscore.exe"; -var pathToExec = "C:/Program Files (x86)/MuseScore/bin/mscore.exe"; - -/* Title for menu item */ -pluginTitle = 'MuseScore'; - -/* Long description for tool tip */ -pluginTip = 'Invoke MuseScore on score XML'; - -/* Build sequence of command line parameters */ -function pluginCli(exportFilePath) { - importPackage(java.util); - return Arrays.asList([pathToExec, exportFilePath]); -} diff --git a/data/examples/autothreshold_test.JPG b/data/examples/BachInvention5.jpg similarity index 100% rename from data/examples/autothreshold_test.JPG rename to data/examples/BachInvention5.jpg diff --git a/data/train/samples.zip b/data/train/samples.zip deleted file mode 100644 index 4460bbf13..000000000 Binary files a/data/train/samples.zip and /dev/null differ diff --git a/dev/installer-windows/FileAssociationWithIcon.nsh b/dev/installer-windows/FileAssociationWithIcon.nsh new file mode 100644 index 000000000..57b76337d --- /dev/null +++ b/dev/installer-windows/FileAssociationWithIcon.nsh @@ -0,0 +1,202 @@ +/* +_____________________________________________________________________________ + + File Association With Icon +_____________________________________________________________________________ + + Based on code taken from http://nsis.sourceforge.net/File_Association + + Added icon as a 4th parameter in RegisterExtension, since the executable may + not always be a .exe, and thus we need a separate way to pass the precise + icon container, be it a .exe, a .dll, a .ico, etc, + optionally with an icon index within the icon container. + + Usage in script: + 1. !include "FileAssociation.nsh" + 2. [Section|Function] + ${FileAssociationFunction} "Param1" "Param2" "..." $var + [SectionEnd|FunctionEnd] + + FileAssociationFunction=[RegisterExtension|UnRegisterExtension] + +_____________________________________________________________________________ + + ${RegisterExtension} "[executable]" "[extension]" "[description]" "[icon]" + +"[executable]" ; executable which opens the file format + ; +"[extension]" ; extension, which represents the file format to open + ; +"[description]" ; description for the extension. This will be display in Windows Explorer. + ; +"[icon]" ; icon container[,index]. + ; + + + ${UnRegisterExtension} "[extension]" "[description]" + +"[extension]" ; extension, which represents the file format to open + ; +"[description]" ; description for the extension. This will be display in Windows Explorer. + ; + +_____________________________________________________________________________ + + Macros +_____________________________________________________________________________ + + Change log window verbosity (default: 3=no script) + + Example: + !include "FileAssociation.nsh" + !insertmacro RegisterExtension + ${FileAssociation_VERBOSE} 4 # all verbosity + !insertmacro UnRegisterExtension + ${FileAssociation_VERBOSE} 3 # no script +*/ + + +!ifndef FileAssociation_INCLUDED +!define FileAssociation_INCLUDED + +!include Util.nsh + +!verbose push +!verbose 3 +!ifndef _FileAssociation_VERBOSE + !define _FileAssociation_VERBOSE 3 +!endif +!verbose ${_FileAssociation_VERBOSE} +!define FileAssociation_VERBOSE `!insertmacro FileAssociation_VERBOSE` +!verbose pop + +!macro FileAssociation_VERBOSE _VERBOSE + !verbose push + !verbose 3 + !undef _FileAssociation_VERBOSE + !define _FileAssociation_VERBOSE ${_VERBOSE} + !verbose pop +!macroend + + + +!macro RegisterExtensionCall _EXECUTABLE _EXTENSION _DESCRIPTION _ICON + !verbose push + !verbose ${_FileAssociation_VERBOSE} + Push `${_ICON}` + Push `${_DESCRIPTION}` + Push `${_EXTENSION}` + Push `${_EXECUTABLE}` + ${CallArtificialFunction} RegisterExtension_ + !verbose pop +!macroend + +!macro UnRegisterExtensionCall _EXTENSION _DESCRIPTION + !verbose push + !verbose ${_FileAssociation_VERBOSE} + Push `${_EXTENSION}` + Push `${_DESCRIPTION}` + ${CallArtificialFunction} UnRegisterExtension_ + !verbose pop +!macroend + + + +!define RegisterExtension `!insertmacro RegisterExtensionCall` +!define un.RegisterExtension `!insertmacro RegisterExtensionCall` + +!macro RegisterExtension +!macroend + +!macro un.RegisterExtension +!macroend + +!macro RegisterExtension_ + !verbose push + !verbose ${_FileAssociation_VERBOSE} + + Exch $R3 ;exe + Exch + Exch $R2 ;ext + Exch + Exch 2 + Exch $R1 ;desc + Exch 2 + Exch 3 + Exch $R0 ;icon + Exch 3 + Push $0 + Push $1 + + ReadRegStr $1 HKCR $R2 "" ; read current file association + StrCmp "$1" "" NoBackup ; is it empty + StrCmp "$1" "$R1" NoBackup ; is it our own + WriteRegStr HKCR $R2 "backup_val" "$1" ; backup current value +NoBackup: + WriteRegStr HKCR $R2 "" "$R1" ; set our file association + + ReadRegStr $0 HKCR $R1 "" + StrCmp $0 "" 0 Skip + WriteRegStr HKCR "$R1" "" "$R1" + WriteRegStr HKCR "$R1\shell" "" "open" + WriteRegStr HKCR "$R1\DefaultIcon" "" "$R0" ; set icon +Skip: + WriteRegStr HKCR "$R1\shell\open\command" "" '"$R3" "%1"' + WriteRegStr HKCR "$R1\shell\edit" "" "Edit $R1" + WriteRegStr HKCR "$R1\shell\edit\command" "" '"$R3" "%1"' + + Pop $1 + Pop $0 + Pop $R3 + Pop $R2 + Pop $R1 + Pop $R0 + + !verbose pop +!macroend + + + +!define UnRegisterExtension `!insertmacro UnRegisterExtensionCall` +!define un.UnRegisterExtension `!insertmacro UnRegisterExtensionCall` + +!macro UnRegisterExtension +!macroend + +!macro un.UnRegisterExtension +!macroend + +!macro UnRegisterExtension_ + !verbose push + !verbose ${_FileAssociation_VERBOSE} + + Exch $R1 ;desc + Exch + Exch $R0 ;ext + Exch + Push $0 + Push $1 + + ReadRegStr $1 HKCR $R0 "" + StrCmp $1 $R1 0 NoOwn ; only do this if we own it + ReadRegStr $1 HKCR $R0 "backup_val" + StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key + DeleteRegKey HKCR $R0 + Goto NoOwn + +Restore: + WriteRegStr HKCR $R0 "" $1 + DeleteRegValue HKCR $R0 "backup_val" + DeleteRegKey HKCR $R1 ;Delete key with association name settings + +NoOwn: + + Pop $1 + Pop $0 + Pop $R1 + Pop $R0 + + !verbose pop +!macroend + +!endif # !FileAssociation_INCLUDED diff --git a/dev/installer-windows/Installer.nsi b/dev/installer-windows/Installer.nsi new file mode 100644 index 000000000..04efadbaf --- /dev/null +++ b/dev/installer-windows/Installer.nsi @@ -0,0 +1,183 @@ +; Script generated by the HM NIS Edit Script Wizard. + +; Expected symbols +; ---------------- +; PRODUCT_VERSION: Product version +; PROJECT_DIR: Project root directory +; TARGET_OS: OS name and architecture (windows-x86 or windows-x86_64) +; SUFFIX: Optional suffix ("32" or "") to cope with 32/64 coexisting installations +!ifndef SUFFIX + !define SUFFIX "" +!endif + +; HM NIS Edit Wizard helper defines +!define PRODUCT_NAME "Audiveris" +!define PRODUCT_PUBLISHER "Audiveris Team" +!define PRODUCT_WEB_SITE "http://www.audiveris.org" +!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}${SUFFIX}" +!define PRODUCT_UNINST_ROOT_KEY "HKLM" +!define PRODUCT_STARTMENU_REGVAL "NSIS:StartMenuDir" + +!include "LogicLib.nsh" + +; MUI 1.67 compatible ------ +!include "MUI.nsh" + +; code derived from "http://nsis.sourceforge.net/File_Association" +!include "FileAssociationWithIcon.nsh" + +; MUI Settings +!define MUI_ABORTWARNING +!define MUI_ICON "${PROJECT_DIR}\res\icon-256.ico" +!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" + +; Welcome page +!insertmacro MUI_PAGE_WELCOME + +; License page +!insertmacro MUI_PAGE_LICENSE "${PROJECT_DIR}\LICENSE" + +; Directory page +!insertmacro MUI_PAGE_DIRECTORY + +; Start menu page +var ICONS_GROUP +!define MUI_STARTMENUPAGE_NODISABLE +!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Audiveris${SUFFIX}" +!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}" +!define MUI_STARTMENUPAGE_REGISTRY_KEY "${PRODUCT_UNINST_KEY}" +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${PRODUCT_STARTMENU_REGVAL}" +!insertmacro MUI_PAGE_STARTMENU Application $ICONS_GROUP + +; Instfiles page +!insertmacro MUI_PAGE_INSTFILES + +; Finish page +!define MUI_FINISHPAGE_RUN "$INSTDIR\bin\Audiveris.bat" +!define MUI_FINISHPAGE_RUN_TEXT "Launch Audiveris" +!insertmacro MUI_PAGE_FINISH + +; Uninstaller pages +!insertmacro MUI_UNPAGE_INSTFILES + +; Language files +!insertmacro MUI_LANGUAGE "English" + +; MUI end ------ + +Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" +OutFile "${PROJECT_DIR}\build\installers\Audiveris_Setup-${PRODUCT_VERSION}-${TARGET_OS}.exe" +ShowInstDetails show +ShowUnInstDetails show + +Function .onInit + ${If} $INSTDIR == "" ; /D= was not used on the command line + ${If} "${SUFFIX}" == "32" + StrCpy $INSTDIR "$PROGRAMFILES\Audiveris${SUFFIX}" + ${Else} + StrCpy $INSTDIR "$PROGRAMFILES64\Audiveris${SUFFIX}" + ${EndIf} + ${EndIf} +FunctionEnd + +Section "Hauptgruppe" SEC01 + SetOutPath "$INSTDIR" + SetOverwrite ifnewer + File /r "${PROJECT_DIR}\build\installers\${TARGET_OS}\*" + + SetOutPath "$PROGRAMFILES\tesseract-ocr\tessdata" + SetOverwrite ifnewer + File "C:\Program Files (x86)\tesseract-ocr\tessdata\*.*" + + ; Shortcuts + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + !insertmacro MUI_STARTMENU_WRITE_END + + MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON1 "Do you want to associate '.omr' file extension with Audiveris?" IDNO noAsso + ${registerExtension} "$INSTDIR\bin\${PRODUCT_NAME}.bat" ".omr" "OpticalMusicRecognition_File" "$INSTDIR\bin\icon-256.ico" + noAsso: +SectionEnd + +Section -AdditionalIcons + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + WriteIniStr "$INSTDIR\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}" + CreateDirectory "$SMPROGRAMS\$ICONS_GROUP" + CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Audiveris.lnk" "$INSTDIR\bin\${PRODUCT_NAME}.bat" "" "$INSTDIR\bin\icon-256.ico" + CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Website.lnk" "$INSTDIR\${PRODUCT_NAME}.url" + CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk" "$INSTDIR\uninst.exe" + !insertmacro MUI_STARTMENU_WRITE_END +SectionEnd + +Section -Post + WriteUninstaller "$INSTDIR\uninst.exe" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" +SectionEnd + + +Function un.onUninstSuccess + HideWindow + MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) has been properly uninstalled." +FunctionEnd + +Function un.onInit + MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Do you want to uninstall $(^Name)?" IDYES +2 + Abort + MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Do you want to keep the configuration data?" IDYES +2 + RmDir /r "$AppData\AudiverisLtd\audiveris" +FunctionEnd + +!macro BadPathsCheck +StrCpy $R0 $INSTDIR "" -2 +StrCmp $R0 ":\" bad +StrCpy $R0 $INSTDIR "" -14 +StrCmp $R0 "\Program Files" bad +StrCpy $R0 $INSTDIR "" -19 +StrCmp $R0 "\Program Files(x86)" bad +StrCpy $R0 $INSTDIR "" -8 +StrCmp $R0 "\Windows" bad +StrCpy $R0 $INSTDIR "" -6 +StrCmp $R0 "\WinNT" bad +StrCpy $R0 $INSTDIR "" -9 +StrCmp $R0 "\system32" bad +StrCpy $R0 $INSTDIR "" -8 +StrCmp $R0 "\Desktop" bad +StrCpy $R0 $INSTDIR "" -23 +StrCmp $R0 "\Documents and Settings" bad +StrCpy $R0 $INSTDIR "" -13 +StrCmp $R0 "\My Documents" bad done +bad: + MessageBox MB_OK|MB_ICONSTOP "Install path invalid!" + Abort +done: +!macroend + +Section Uninstall + ; Check that the uninstall isn't dangerous. + !insertmacro BadPathsCheck + + ; Delete installed software + Delete "$INSTDIR\${PRODUCT_NAME}.url" + Delete "$INSTDIR\uninst.exe" + Delete "$INSTDIR\lib\*" + Delete "$INSTDIR\bin\*" + RMDir "$INSTDIR\lib" + RMDir "$INSTDIR\bin" + RMDir "$INSTDIR" + + ; Retrieve start menu folder from registry then delete folder and registry key + !insertmacro MUI_STARTMENU_GETFOLDER Application $ICONS_GROUP + Delete "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk" + Delete "$SMPROGRAMS\$ICONS_GROUP\Website.lnk" + Delete "$SMPROGRAMS\$ICONS_GROUP\Audiveris.lnk" + RMDir "$SMPROGRAMS\$ICONS_GROUP" + DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" + + ${unregisterExtension} ".omr" "OpticalMusicRecognition_File" + DeleteRegKey HKCR "OpticalMusicRecognition_File" ; brute force delete + + SetAutoClose true +SectionEnd \ No newline at end of file diff --git a/res/basic-classifier.zip b/res/basic-classifier.zip index a4adb36df..15d71092f 100644 Binary files a/res/basic-classifier.zip and b/res/basic-classifier.zip differ diff --git a/res/system-actions.xml b/res/system-actions.xml index a16efa104..34f3f52be 100644 --- a/res/system-actions.xml +++ b/res/system-actions.xml @@ -44,13 +44,14 @@ + - - + + @@ -64,17 +65,17 @@ - + - + - + - + - + @@ -86,7 +87,7 @@ - + diff --git a/src/main/org/audiveris/omr/CLI.java b/src/main/org/audiveris/omr/CLI.java index 484b0c794..6ebe3ffbb 100644 --- a/src/main/org/audiveris/omr/CLI.java +++ b/src/main/org/audiveris/omr/CLI.java @@ -44,9 +44,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -72,11 +74,9 @@ */ public class CLI { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(CLI.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Name of the program. */ private final String toolName; @@ -89,7 +89,6 @@ public class CLI /** CLI parser. */ private final CmdLineParser parser = new CmdLineParser(params); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new CLI object. * @@ -100,7 +99,6 @@ public CLI (final String toolName) this.toolName = toolName; } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // getCliTasks // //-------------// @@ -111,7 +109,7 @@ public CLI (final String toolName) */ public List getCliTasks () { - List tasks = new ArrayList(); + List tasks = new ArrayList<>(); // Task kind is fully determined by argument extension for (Path argument : params.arguments) { @@ -190,6 +188,19 @@ public boolean isHelpMode () return params.helpMode; } + //--------// + // isSave // + //--------// + /** + * Report whether we save every step result. + * + * @return true for saving every step + */ + public boolean isSave () + { + return params.save; + } + //-----------------// // parseParameters // //-----------------// @@ -237,7 +248,7 @@ public void printCommandLine () sb.append(toolName).append(" "); } - sb.append(trimmedArgs); + sb.append(Arrays.toString(trimmedArgs)); logger.info(sb.toString()); } @@ -295,7 +306,7 @@ private void checkParams () if (params.transcribe) { if ((params.step != null) && (params.step != Step.last())) { String msg = "'-transcribe' option not compatible with '-step " + params.step - + "' option"; + + "' option"; throw new CmdLineException(parser, msg); } @@ -303,7 +314,182 @@ private void checkParams () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------// + // BookTask // + //----------// + /** + * CLI task to process a book file. + */ + private class BookTask + extends ProcessingTask + { + + BookTask (Path path) + { + super(path); + } + + @Override + public String toString () + { + return "Book \"" + path + "\""; + } + + @Override + protected Book loadBook (Path path) + { + return OMR.engine.loadBook(path); + } + } + + //-----------// + // InputTask // + //-----------// + /** + * CLI task to process an input (image) file. + */ + private class InputTask + extends ProcessingTask + { + + InputTask (Path path) + { + super(path); + } + + @Override + public String toString () + { + return "Input \"" + path + "\""; + } + + @Override + protected Book loadBook (Path path) + { + return OMR.engine.loadInput(path); + } + } + + //----------------// + // ProcessingTask // + //----------------// + /** + * Processing common to both input (images) and books. + */ + private abstract class ProcessingTask + extends CliTask + { + + ProcessingTask (Path path) + { + super(path); + } + + @Override + protected void processBook (Book book) + { + final Path folder = BookManager.getDefaultBookFolder(book); + boolean cancelled = false; + try { + if (!Files.exists(folder)) { + Files.createDirectories(folder); + } + // In batch, log into specific log file within book folder + if (OMR.gui == null) { + LogUtil.addAppender(book.getRadix(), folder); + } + LogUtil.start(book); + // Specific sheets to process? + final SortedSet sheetIds = params.getSheetIds(); + // Make sure stubs are available + if (book.getStubs().isEmpty()) { + book.createStubs(sheetIds); + + // Save book to disk (global book info) + if (OMR.gui == null) { + book.store(BookManager.getDefaultSavePath(book), false); + } + } + if (OMR.gui != null) { + Integer focus = (sheetIds != null) ? sheetIds.first() : null; + book.createStubsTabs(focus); // Tabs are now accessible + } + // Specific step to reach on all sheets in the book? + if (params.step != null) { + boolean ok = book.reachBookStep(params.step, params.force, sheetIds); + + if (!ok) { + return; + } + } + if (params.transcribe) { + book.reduceScores(); + } + // Specific class to run? + if (params.runClass != null) { + try { + Constructor cons = params.runClass.getConstructor( + new Class[]{Book.class, SortedSet.class}); + RunClass instance = (RunClass) cons.newInstance(book, sheetIds); + instance.process(); + } catch (IllegalAccessException | + IllegalArgumentException | + InstantiationException | + NoSuchMethodException | + SecurityException | + InvocationTargetException ex) { + logger.warn("Error running {} {}", params.runClass, ex.toString(), ex); + } + } + // Book export? + if (params.export) { + logger.debug("Export book"); + book.export(); + } + // Book sample? + if (params.sample) { + logger.debug("Sample book"); + book.sample(); + } + // Book annotate? + if (params.annotate) { + logger.debug("Annotate book"); + book.annotate(); + } + // Book print? + if (params.print) { + logger.debug("Print book"); + book.print(); + } + } catch (ProcessingCancellationException pce) { + logger.warn("Cancelled " + book); + cancelled = true; + throw pce; + } catch (IOException ex) { + logger.warn("Exception occurred " + ex, ex); + throw new RuntimeException(ex); + } finally { + // Close (when in batch mode only) + if (OMR.gui == null) { + if (cancelled) { + // Make a backup if needed, then save book "in its current status" + book.store(BookManager.getDefaultSavePath(book), true); + } else { + book.store(BookManager.getDefaultSavePath(book), false); + } + + book.close(); + } + + LogUtil.stopBook(); + + if (OMR.gui == null) { + LogUtil.removeAppender(book.getRadix()); + } + } + } + } + //---------// // CliTask // //---------// @@ -313,7 +499,6 @@ private void checkParams () public abstract static class CliTask implements Callable { - //~ Instance fields ------------------------------------------------------------------------ /** Source file path. */ public final Path path; @@ -321,7 +506,11 @@ public abstract static class CliTask /** Radix. */ private final String radix; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a CliTask object. + * + * @param path the path to book file + */ public CliTask (Path path) { this.path = path; @@ -331,7 +520,6 @@ public CliTask (Path path) radix = (alias != null) ? alias : nameSansExt; } - //~ Methods -------------------------------------------------------------------------------- @Override public Void call () throws Exception @@ -361,14 +549,16 @@ public String getRadix () return radix; } - /** Getting the book instance. + /** + * Getting the book instance. * * @param path path to source * @return the loaded book */ protected abstract Book loadBook (Path path); - /** Processing the book instance. + /** + * Processing the book instance. * * @param book the book to process */ @@ -387,8 +577,14 @@ protected void processBook (Book book) public static class ClassOptionHandler extends OptionHandler { - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a ClassOptionHandler object. + * + * @param parser Command line argument owner + * @param option Run-time copy of the Option or Argument annotation + * @param setter Setter interface + */ public ClassOptionHandler (CmdLineParser parser, OptionDef option, Setter setter) @@ -396,7 +592,6 @@ public ClassOptionHandler (CmdLineParser parser, super(parser, option, setter); } - //~ Methods -------------------------------------------------------------------------------- @Override public String getDefaultMetaVariable () { @@ -414,7 +609,7 @@ public int parseArguments (org.kohsuke.args4j.spi.Parameters params) Class runClass = Class.forName(className); FieldSetter fs = setter.asFieldSetter(); fs.addValue(runClass); - } catch (Throwable ex) { + } catch (ClassNotFoundException ex) { throw new CmdLineException(owner, ex); } } @@ -436,8 +631,14 @@ public int parseArguments (org.kohsuke.args4j.spi.Parameters params) public static class IntArrayOptionHandler extends OptionHandler { - //~ Constructors --------------------------------------------------------------------------- + /** + * Create an IntArrayOptionHandler object. + * + * @param parser Command line argument owner + * @param option Run-time copy of the Option or Argument annotation + * @param setter Setter interface + */ public IntArrayOptionHandler (CmdLineParser parser, OptionDef option, Setter setter) @@ -445,7 +646,6 @@ public IntArrayOptionHandler (CmdLineParser parser, super(parser, option, setter); } - //~ Methods -------------------------------------------------------------------------------- @Override public String getDefaultMetaVariable () { @@ -465,7 +665,7 @@ public int parseArguments (org.kohsuke.args4j.spi.Parameters params) break; } - int minusPos = param.indexOf("-"); + int minusPos = param.indexOf('-'); if (minusPos != -1) { // " a - b " or a-b @@ -498,10 +698,10 @@ public int parseArguments (org.kohsuke.args4j.spi.Parameters params) */ public static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ + // Fields are kept in alphabetical order /** Should symbols annotations be produced?. */ - @Option(name = "-annotate", hidden = true, usage = "Annotate book symbols") + @Option(name = "-annotate", usage = "(advanced) Annotate book symbols") boolean annotate; /** Batch mode. */ @@ -517,11 +717,12 @@ public static class Parameters boolean force; /** Help mode. */ - @Option(name = "-help", help = true, usage = "Display general help then stops") + @Option(name = "-help", help = true, usage = "Display general help then stop") boolean helpMode; /** The map of application options. */ - @Option(name = "-option", usage = "Define an application constant", handler = PropertyOptionHandler.class) + @Option(name = "-option", usage = "Define an application constant", + handler = PropertyOptionHandler.class) Properties options; /** Output directory. */ @@ -533,23 +734,25 @@ public static class Parameters boolean print; /** Ability to run a class on each valid sheet. */ - @Option(name = "-run", usage = "Run provided class on valid sheets", handler = ClassOptionHandler.class) + @Option(name = "-run", usage = "(advanced) Run provided class on valid sheets", + handler = ClassOptionHandler.class) Class runClass; /** Should samples be produced?. */ - @Option(name = "-sample", usage = "Sample all book symbols") + @Option(name = "-sample", usage = "(advanced) Sample all book symbols") boolean sample; - /** Should book be saved?. */ - @Option(name = "-save", usage = "Save book") + /** Should book be saved on every successful batch step?. */ + @Option(name = "-save", usage = "Save book on every successful batch step") boolean save; /** The set of sheet IDs to load. */ - @Option(name = "-sheets", usage = "Select specific sheets numbers and ranges (like 2-5)", handler = IntArrayOptionHandler.class) + @Option(name = "-sheets", usage = "Select specific sheets numbers and ranges (like 2-5)", + handler = IntArrayOptionHandler.class) private ArrayList sheets; /** Specific step. */ - @Option(name = "-step", usage = "Define a specific processing step") + @Option(name = "-step", usage = "Define a specific target step") Step step; /** Should book be transcribed?. */ @@ -560,14 +763,12 @@ public static class Parameters @Argument @Option(name = "--", handler = StopOptionHandler.class) /** Final arguments. */ - List arguments = new ArrayList(); + List arguments = new ArrayList<>(); - //~ Constructors --------------------------------------------------------------------------- private Parameters () { } - //~ Methods -------------------------------------------------------------------------------- //-------------// // getSheetIds // //-------------// @@ -583,7 +784,7 @@ public SortedSet getSheetIds () return null; } - return new TreeSet(sheets); + return new TreeSet<>(sheets); } } @@ -596,8 +797,14 @@ public SortedSet getSheetIds () public static class PropertyOptionHandler extends OptionHandler { - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a PropertyOptionHandler. + * + * @param parser Command line argument owner + * @param option Run-time copy of the Option or Argument annotation + * @param setter Setter interface + */ public PropertyOptionHandler (CmdLineParser parser, OptionDef option, Setter setter) @@ -610,7 +817,6 @@ public PropertyOptionHandler (CmdLineParser parser, } } - //~ Methods -------------------------------------------------------------------------------- @Override public String getDefaultMetaVariable () { @@ -633,7 +839,7 @@ public int parseArguments (org.kohsuke.args4j.spi.Parameters params) try { props.load(new StringReader(pair)); - } catch (Exception ex) { + } catch (IOException ex) { throw new CmdLineException(owner, "Error in " + name + " " + pair, ex); } @@ -641,207 +847,6 @@ public int parseArguments (org.kohsuke.args4j.spi.Parameters params) } } - //----------// - // BookTask // - //----------// - /** - * CLI task to process a book file. - */ - private class BookTask - extends ProcessingTask - { - //~ Constructors --------------------------------------------------------------------------- - - public BookTask (Path path) - { - super(path); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return "Book \"" + path + "\""; - } - - @Override - protected Book loadBook (Path path) - { - return OMR.engine.loadBook(path); - } - } - - //-----------// - // InputTask // - //-----------// - /** - * CLI task to process an input (image) file. - */ - private class InputTask - extends ProcessingTask - { - //~ Constructors --------------------------------------------------------------------------- - - public InputTask (Path path) - { - super(path); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return "Input \"" + path + "\""; - } - - @Override - protected Book loadBook (Path path) - { - return OMR.engine.loadInput(path); - } - } - - //----------------// - // ProcessingTask // - //----------------// - /** - * Processing common to both input (images) and books. - */ - private abstract class ProcessingTask - extends CliTask - { - //~ Constructors --------------------------------------------------------------------------- - - public ProcessingTask (Path path) - { - super(path); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - protected void processBook (Book book) - { - final Path folder = BookManager.getDefaultBookFolder(book); - boolean cancelled = false; - - try { - if (!Files.exists(folder)) { - Files.createDirectories(folder); - } - - // In batch, log into specific log file within book folder - if (OMR.gui == null) { - LogUtil.addAppender(book.getRadix(), folder); - } - - LogUtil.start(book); - - // Specific sheets to process? - final SortedSet sheetIds = params.getSheetIds(); - - // Make sure stubs are available - if (book.getStubs().isEmpty()) { - book.createStubs(sheetIds); - - // Save book to disk (global book info) - if (OMR.gui == null) { - book.store(BookManager.getDefaultSavePath(book), false); - } - } - - if (OMR.gui != null) { - Integer focus = (sheetIds != null) ? sheetIds.first() : null; - book.createStubsTabs(focus); // Tabs are now accessible - } - - // Specific step to reach on all sheets in the book? - if (params.step != null) { - boolean ok = book.reachBookStep(params.step, params.force, sheetIds); - - if (!ok) { - return; - } - } - - if (params.transcribe) { - book.reduceScores(); - } - - // Specific class to run? - if (params.runClass != null) { - try { - Constructor cons = params.runClass.getConstructor( - new Class[]{Book.class, SortedSet.class}); - RunClass instance = (RunClass) cons.newInstance(book, sheetIds); - instance.process(); - } catch (Throwable ex) { - logger.warn("Error running {} {}", params.runClass, ex.toString(), ex); - } - } - - // Book print? - if (params.print) { - logger.debug("Print book"); - book.print(); - } - - // Book export? - if (params.export) { - logger.debug("Export book"); - book.export(); - } - - // Book sample? - if (params.sample) { - logger.debug("Sample book"); - book.sample(); - } - - // Book annotate? - if (params.annotate) { - logger.debug("Annotate book"); - book.annotate(); - } - - // Book save? - if (params.save) { - logger.debug("Save book"); - - if (book.isModified()) { - book.store(BookManager.getDefaultSavePath(book), false); - } else { - logger.info("No need to save {}", book); - } - } - } catch (ProcessingCancellationException pce) { - logger.warn("Cancelled " + book); - cancelled = true; - throw pce; - } catch (Throwable ex) { - logger.warn("Exception occurred " + ex, ex); - throw new RuntimeException(ex); - } finally { - // Close (when in batch mode only) - if (OMR.gui == null) { - if (cancelled) { - // Make a backup if needed, then save book "in its current status" - book.store(BookManager.getDefaultSavePath(book), true); - } else { - book.store(BookManager.getDefaultSavePath(book), false); - } - - book.close(); - } - - LogUtil.stopBook(); - - if (OMR.gui == null) { - LogUtil.removeAppender(book.getRadix()); - } - } - } - } - //-------------// // SamplesTask // //-------------// @@ -851,14 +856,12 @@ protected void processBook (Book book) private static class SamplesTask extends CliTask { - //~ Constructors --------------------------------------------------------------------------- - public SamplesTask (Path path) + SamplesTask (Path path) { super(path); } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { diff --git a/src/main/org/audiveris/omr/Debug.java b/src/main/org/audiveris/omr/Debug.java index 3e2814df1..0c88c1dbc 100644 --- a/src/main/org/audiveris/omr/Debug.java +++ b/src/main/org/audiveris/omr/Debug.java @@ -37,6 +37,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; /** * Convenient class meant to temporarily inject some debugging. @@ -47,11 +48,9 @@ public class Debug extends StubDependent { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Debug.class); - //~ Methods ------------------------------------------------------------------------------------ //--------------// // checkSources // //--------------// @@ -153,7 +152,7 @@ private static PrintWriter getPrintWriter (OutputStream os) new OutputStreamWriter(os, WellKnowns.FILE_ENCODING)); return new PrintWriter(bw); - } catch (Exception ex) { + } catch (UnsupportedEncodingException ex) { logger.warn("Error creating PrintWriter " + ex, ex); return null; diff --git a/src/main/org/audiveris/omr/Main.java b/src/main/org/audiveris/omr/Main.java index caa7d267d..20bc2968a 100644 --- a/src/main/org/audiveris/omr/Main.java +++ b/src/main/org/audiveris/omr/Main.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; /** @@ -52,13 +53,12 @@ * It launches the User Interface, unless batch mode is selected. * * @see CLI - * * @author Hervé Bitteur */ public class Main { - //~ Static fields/initializers ----------------------------------------------------------------- + // Don't move this statement! static { // We need class WellKnowns to be elaborated before anything else WellKnowns.ensureLoaded(); @@ -71,12 +71,10 @@ public class Main /** CLI parameters. */ private static CLI cli; - //~ Constructors ------------------------------------------------------------------------------- private Main () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // getCli // //--------// @@ -196,19 +194,16 @@ public static void main (String[] args) //--------------------------// // processSystemsInParallel // //--------------------------// + /** + * Tell whether we should process systems of a sheet in parallel. + * + * @return true if so + */ public static boolean processSystemsInParallel () { return constants.processSystemsInParallel.isSet(); } - //----------------------// - // saveSheetOnEveryStep // - //----------------------// - public static boolean saveSheetOnEveryStep () - { - return constants.saveSheetOnEveryStep.isSet(); - } - //-------------// // checkLocale // //-------------// @@ -296,7 +291,8 @@ private static boolean runBatchTasks () for (Future future : futures) { try { future.get(); - } catch (Exception ex) { + } catch (InterruptedException | + ExecutionException ex) { CliTask task = tasks.get(futures.indexOf(future)); final String radix = task.getRadix(); @@ -304,7 +300,7 @@ private static boolean runBatchTasks () failure = true; } } - } catch (Exception ex) { + } catch (InterruptedException ex) { logger.warn("Error in processing tasks", ex); failure = true; } @@ -336,26 +332,29 @@ private static void showEnvironment () { if (constants.showEnvironment.isSet()) { logger.info( - "Environment:\n" + "- Audiveris: {}\n" + "- OS: {}\n" - + "- Architecture: {}\n" + "- Java VM: {}\n" + "- OCR Engine: {}", + "Environment:\n" + "- Audiveris: {}\n" + + "- OS: {}\n" + + "- Architecture: {}\n" + + "- Java VM: {}\n" + + "- OCR Engine: {}", WellKnowns.TOOL_REF + ":" + WellKnowns.TOOL_BUILD, System.getProperty("os.name") + " " + System.getProperty("os.version"), System.getProperty("os.arch"), System.getProperty("java.vm.name") + " (build " - + System.getProperty("java.vm.version") + ", " + System.getProperty("java.vm.info") - + ")", + + System.getProperty("java.vm.version") + + ", " + + System.getProperty("java.vm.info") + + ")", TesseractOCR.getInstance().identify()); } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean showEnvironment = new Constant.Boolean( true, @@ -377,10 +376,6 @@ private static final class Constants false, "Should we process all systems in parallel in a sheet?"); - private final Constant.Boolean saveSheetOnEveryStep = new Constant.Boolean( - true, - "Should we save sheet after every successful step?"); - private final Constant.Integer sheetStepTimeOut = new Constant.Integer( "Seconds", 120, diff --git a/src/main/org/audiveris/omr/OMR.java b/src/main/org/audiveris/omr/OMR.java index 3fbd1c3cb..09bfe1811 100644 --- a/src/main/org/audiveris/omr/OMR.java +++ b/src/main/org/audiveris/omr/OMR.java @@ -32,7 +32,6 @@ */ public abstract class OMR { - //~ Static fields/initializers ----------------------------------------------------------------- /** The extension used for compressed score output files: {@value}. */ public static final String COMPRESSED_SCORE_EXTENSION = ".mxl"; @@ -43,8 +42,8 @@ public abstract class OMR /** The (double) extension used for opus output files: {@value}. */ public static final String OPUS_EXTENSION = ".opus.mxl"; - /** The extension used for compressed score print files: {@value}. */ - public static final String PDF_EXTENSION = ".pdf"; + /** The extension used for score print files: {@value}. */ + public static final String PRINT_EXTENSION = "-print.pdf"; /** The extension used for Audiveris book files: {@value}. */ public static final String BOOK_EXTENSION = ".omr"; @@ -61,7 +60,6 @@ public abstract class OMR /** Master view, if any. */ public static OmrGui gui; - //~ Constructors ------------------------------------------------------------------------------- /** Do not instantiate. */ private OMR () { diff --git a/src/main/org/audiveris/omr/OmrEngine.java b/src/main/org/audiveris/omr/OmrEngine.java index 43ad31c29..b8b465820 100644 --- a/src/main/org/audiveris/omr/OmrEngine.java +++ b/src/main/org/audiveris/omr/OmrEngine.java @@ -31,7 +31,8 @@ *

* OMR deals with instances of {@link Book} class. *

- * A Book instance can be obtained from:

    + * A Book instance can be obtained from: + *
      *
    • An input image file, via {@link #loadInput(java.nio.file.Path)},
    • *
    • A book file, via {@link #loadBook(java.nio.file.Path)}.
    • *
    @@ -42,7 +43,6 @@ */ public interface OmrEngine { - //~ Methods ------------------------------------------------------------------------------------ /** * Report the list of all books handled. diff --git a/src/main/org/audiveris/omr/RunPartCheck.java b/src/main/org/audiveris/omr/RunPartCheck.java index f6addaf52..516a65df2 100644 --- a/src/main/org/audiveris/omr/RunPartCheck.java +++ b/src/main/org/audiveris/omr/RunPartCheck.java @@ -45,11 +45,9 @@ public class RunPartCheck extends RunClass { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(RunPartCheck.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code RunPartCheck} object. * @@ -62,7 +60,6 @@ public RunPartCheck (Book book, super(book, sheetIds); } - //~ Methods ------------------------------------------------------------------------------------ @Override public void process () { @@ -74,7 +71,7 @@ public void process () Sheet sheet = stub.getSheet(); for (SystemInfo system : sheet.getSystems()) { - List staves = new ArrayList(system.getStaves()); + List staves = new ArrayList<>(system.getStaves()); for (Part part : system.getParts()) { staves.removeAll(part.getStaves()); diff --git a/src/main/org/audiveris/omr/RunSmallStaffCheck.java b/src/main/org/audiveris/omr/RunSmallStaffCheck.java index c74e9bde5..644adfc66 100644 --- a/src/main/org/audiveris/omr/RunSmallStaffCheck.java +++ b/src/main/org/audiveris/omr/RunSmallStaffCheck.java @@ -41,11 +41,9 @@ public class RunSmallStaffCheck extends RunClass { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(RunSmallStaffCheck.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code RunSmallStaffCheck} object. * @@ -58,7 +56,6 @@ public RunSmallStaffCheck (Book book, super(book, sheetIds); } - //~ Methods ------------------------------------------------------------------------------------ @Override public void process () { diff --git a/src/main/org/audiveris/omr/WellKnowns.java b/src/main/org/audiveris/omr/WellKnowns.java index f2236b8ad..896abd78f 100644 --- a/src/main/org/audiveris/omr/WellKnowns.java +++ b/src/main/org/audiveris/omr/WellKnowns.java @@ -39,6 +39,8 @@ import java.util.Locale; import java.util.jar.JarFile; +import javax.swing.filechooser.FileSystemView; + /** * Class {@code WellKnowns} gathers top public static final data to be shared within * Audiveris application. @@ -50,7 +52,6 @@ */ public abstract class WellKnowns { - //~ Static fields/initializers ----------------------------------------------------------------- //----------// // IDENTITY // @@ -97,8 +98,8 @@ public abstract class WellKnowns public static final String OS_ARCH = System.getProperty("os.arch"); /** Are we using Windows on 64 bit architecture?. */ - public static final boolean WINDOWS_64 = WINDOWS - && (System.getenv("ProgramFiles(x86)") != null); + public static final boolean WINDOWS_64 = WINDOWS && (System.getenv( + "ProgramFiles(x86)") != null); /** File character encoding. */ public static final String FILE_ENCODING = getFileEncoding(); @@ -129,12 +130,8 @@ public abstract class WellKnowns public static final boolean RUNNING_FROM_JAR = runningFromJar(); /** The uri where read-only resources are stored. */ - public static final URI RES_URI = RUNNING_FROM_JAR - ? toURI(WellKnowns.class.getClassLoader().getResource("res")) - : Paths.get("res").toUri(); - - /** The folder where Tesseract OCR material is stored. */ - public static final Path OCR_FOLDER = getOcrFolder(); + public static final URI RES_URI = RUNNING_FROM_JAR ? toURI( + WellKnowns.class.getClassLoader().getResource("res")) : Paths.get("res").toUri(); //-------------// read-write area // USER CONFIG // Configuration files the user can edit on his own @@ -143,9 +140,6 @@ public abstract class WellKnowns /** The folder where global configuration data is stored. */ public static final Path CONFIG_FOLDER = getFolder(FolderKind.CONFIG); - /** The folder where plugin scripts are found. */ - public static final Path PLUGINS_FOLDER = CONFIG_FOLDER.resolve("plugins"); - //-----------// read-write area // USER DATA // User-specific data, except configuration stuff //-----------// @@ -165,14 +159,11 @@ public abstract class WellKnowns */ public static final Path EXAMPLES_FOLDER = DATA_FOLDER.resolve("examples"); - /** The folder where temporary data can be stored. */ - public static final Path TEMP_FOLDER = DATA_FOLDER.resolve("temp"); - /** The folder where training material is stored. */ - public static final Path TRAIN_FOLDER = DATA_FOLDER.resolve("train"); + public static final Path TRAIN_FOLDER = CONFIG_FOLDER.resolve("train"); /** The default base for output folders. */ - public static final Path DEFAULT_BASE_FOLDER = DATA_FOLDER.resolve("output"); + public static final Path DEFAULT_BASE_FOLDER = DATA_FOLDER; // BHT: skip "output" //----------// // USER LOG // @@ -181,6 +172,9 @@ public abstract class WellKnowns /** The folder where log files are stored. */ public static final Path LOG_FOLDER = getFolder(FolderKind.LOG); + /** The folder where temporary data can be stored. */ + public static final Path TEMP_FOLDER = LOG_FOLDER.resolve("temp"); + static { /** Logging configuration. */ LogUtil.initialize(CONFIG_FOLDER, RES_URI); @@ -205,26 +199,18 @@ public abstract class WellKnowns disableMediaLib(); } - private static final String ocrNotFoundMsg = "Tesseract data could not be found. " - + "Try setting the TESSDATA_PREFIX environment variable to the parent folder of \"tessdata\"."; - - //~ Enumerations ------------------------------------------------------------------------------- private static enum FolderKind { - //~ Enumeration constant initializers ------------------------------------------------------ - - CONFIG, DATA, + CONFIG, LOG; } - //~ Constructors ------------------------------------------------------------------------------- /** Not meant to be instantiated. */ private WellKnowns () { } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // ensureLoaded // //--------------// @@ -319,7 +305,9 @@ private static Path getFolder (FolderKind kind) } else if (LINUX) { return getFolderForLinux(kind); } else { - throw new RuntimeException("Platform unknown"); + printError("Platform unknown: " + kind); + + return null; } } @@ -335,10 +323,10 @@ private static Path getFolderForLinux (FolderKind kind) final Path audiverisPath = Paths.get(xdg + TOOL_PREFIX); switch (kind) { - case CONFIG: + case DATA: return audiverisPath; - case DATA: + case CONFIG: return audiverisPath; default: @@ -351,16 +339,18 @@ private static Path getFolderForLinux (FolderKind kind) final String home = System.getenv("HOME"); if (home == null) { - throw new RuntimeException("HOME environment variable is not set"); + printError("HOME environment variable is not set"); + + return null; } switch (kind) { - case CONFIG: - return Paths.get(home + "/.config" + TOOL_PREFIX); - case DATA: return Paths.get(home + "/.local/share" + TOOL_PREFIX); + case CONFIG: + return Paths.get(home + "/.config" + TOOL_PREFIX); + default: case LOG: return Paths.get(home + "/.cache" + TOOL_PREFIX + "/log"); @@ -375,16 +365,18 @@ private static Path getFolderForMac (FolderKind kind) final String home = System.getenv("HOME"); if (home == null) { - throw new RuntimeException("HOME environment variable is not set"); + printError("HOME environment variable is not set"); + + return null; } switch (kind) { - case CONFIG: - return Paths.get(home + "/Library/Application Support/" + TOOL_PREFIX); - case DATA: return Paths.get(home + "/Library/" + TOOL_PREFIX + "/data"); + case CONFIG: + return Paths.get(home + "/Library/Application Support/" + TOOL_PREFIX); + default: case LOG: return Paths.get(home + "/Library/" + TOOL_PREFIX + "/log"); @@ -396,21 +388,27 @@ private static Path getFolderForMac (FolderKind kind) //---------------------// private static Path getFolderForWindows (FolderKind kind) { + // User Application Data final String appdata = System.getenv("APPDATA"); if (appdata == null) { - throw new RuntimeException("APPDATA environment variable is not set"); + printError("APPDATA environment variable is not set"); + + return null; } final Path audiverisPath = Paths.get(appdata + TOOL_PREFIX); + // User Documents + final String userDocs = FileSystemView.getFileSystemView().getDefaultDirectory().getPath(); + switch (kind) { + case DATA: + return Paths.get(userDocs + "/" + TOOL_NAME); + case CONFIG: return audiverisPath.resolve("config"); - case DATA: - return audiverisPath.resolve("data"); - default: case LOG: return audiverisPath.resolve("log"); @@ -435,60 +433,13 @@ private static JarFile getJarFile () return jarFile; } - } catch (Exception ex) { + } catch (IOException ex) { System.out.print("Error getting jar file " + ex); } return null; } - //--------------// - // getOcrFolder // - //--------------// - private static Path getOcrFolder () - { - // common Macintosh TESSDATA locations - final String[] macOcrLocations = { - "/opt/local/share", // Macports - "/usr/local/opt/tesseract/share" // Homebrew - }; - - // common Linux TESSDATA locations - final String[] linuxOcrLocations = { - "/usr/share/tesseract-ocr", // Debian, Ubuntu and derivatives - "/usr/share", // OpenSUSE - "/usr/share/tesseract" // Fedora - }; - - // First, try to use TESSDATA_PREFIX environment variable - // which might denote a Tesseract installation - final String TESSDATA_PREFIX = "TESSDATA_PREFIX"; - final String tessPrefix = System.getenv(TESSDATA_PREFIX); - - if (tessPrefix != null) { - Path dir = Paths.get(tessPrefix); - - if (Files.isDirectory(dir)) { - return dir; - } - } - - // Fallback to default directory on Windows - if (WINDOWS) { - final String pf32 = OS_ARCH.equals("x86") ? "ProgramFiles" : "ProgramFiles(x86)"; - - return Paths.get(System.getenv(pf32)).resolve("tesseract-ocr"); - - // scan common locations on Mac and Linux - } else if (LINUX) { - return scanOcrLocations(linuxOcrLocations); - } else if (MAC_OS_X) { - return scanOcrLocations(macOcrLocations); - } - - throw new InstallationException(ocrNotFoundMsg); - } - //-----------------// // logDeclaredData // //-----------------// @@ -497,10 +448,10 @@ private static void logDeclaredData () // Note: Logger initialization has been differed until now final Logger logger = LoggerFactory.getLogger(WellKnowns.class); - if (logger.isDebugEnabled()) { + if (logger.isTraceEnabled()) { for (Field field : WellKnowns.class.getDeclaredFields()) { try { - logger.debug("{}= {}", field.getName(), field.get(null)); + logger.trace("{}= {}", field.getName(), field.get(null)); } catch (IllegalAccessException ex) { ex.printStackTrace(); } @@ -508,6 +459,19 @@ private static void logDeclaredData () } } + //------------// + // printError // + //------------// + /** + * Fallback solution, since we cannot reliably use exception in static initializer. + * + * @param msg the error message + */ + private static void printError (String msg) + { + System.err.println("*** INIT_ERROR occurred in class WellKnowns: " + msg); + } + //----------------// // runningFromJar // //----------------// @@ -516,55 +480,21 @@ private static boolean runningFromJar () return CLASS_CONTAINER.toString().toLowerCase().endsWith(".jar"); } - //------------------// - // scanOcrLocations // - //------------------// - private static Path scanOcrLocations (String[] locations) - { - for (String loc : locations) { - final Path path = Paths.get(loc); - - if (Files.exists(path.resolve("tessdata"))) { - return path; - } - } - - throw new InstallationException(ocrNotFoundMsg); - } - //-------------// // xdgProperty // //-------------// private static String xdgProperty (FolderKind kind) { switch (kind) { - case CONFIG: - return "XDG_CONFIG_HOME"; - case DATA: return "XDG_DATA_HOME"; + case CONFIG: + return "XDG_CONFIG_HOME"; + default: case LOG: return "XDG_CACHE_HOME"; } } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------------------// - // InstallationException // - //-----------------------// - /** - * Exception used to signal an installation error. - */ - public static class InstallationException - extends RuntimeException - { - //~ Constructors --------------------------------------------------------------------------- - - public InstallationException (String message) - { - super(message); - } - } } diff --git a/src/main/org/audiveris/omr/check/Check.java b/src/main/org/audiveris/omr/check/Check.java index b5fb9d5b9..793d85341 100644 --- a/src/main/org/audiveris/omr/check/Check.java +++ b/src/main/org/audiveris/omr/check/Check.java @@ -32,9 +32,10 @@ * Class {@code Check} encapsulates the definition of a check, * which can later be used on a whole population of objects. *

    - * Checks are generally gathered in {@link CheckSuite} instances.

    + * Checks are generally gathered in {@link CheckSuite} instances. *

    - * The strategy is the following:

      + * The strategy is the following: + *
        *
      • A successful individual check may eventually result in an interpretation checked object (if * the suite of checks ends with an acceptable grade).
      • *
      • Any failed individual check triggers the immediate end of the containing suite but records @@ -42,16 +43,13 @@ *
      * * @param precise type of the objects to be checked - * * @author Hervé Bitteur */ public abstract class Check { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Check.class); - //~ Instance fields ---------------------------------------------------------------------------- /** * Specifies the Failure to be assigned to the Checkable object, * when the result of this individual check is not acceptable. @@ -73,7 +71,6 @@ public abstract class Check /** Covariant: higher is better, contravariant: lower is better. */ private final boolean covariant; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Check object. * @@ -121,7 +118,6 @@ protected Check (String name, this(name, description, new NamedDouble(low), new NamedDouble(high), covariant, redResult); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // getDescription // //----------------// @@ -298,7 +294,6 @@ public String toString () * proper data value from the given object passed as a parameter. * * @param obj the object to be checked - * * @return the data value relevant for the check */ protected abstract double getValue (C obj); @@ -316,7 +311,6 @@ private void verifyRange () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-------// // Grade // //-------// @@ -326,7 +320,6 @@ private void verifyRange () public static class Grade extends Constant.Double { - //~ Constructors --------------------------------------------------------------------------- /** * Specific constructor, where 'unit' and 'name' are assigned later diff --git a/src/main/org/audiveris/omr/check/CheckBoard.java b/src/main/org/audiveris/omr/check/CheckBoard.java index c61efe6ef..d03de0bf0 100644 --- a/src/main/org/audiveris/omr/check/CheckBoard.java +++ b/src/main/org/audiveris/omr/check/CheckBoard.java @@ -38,22 +38,17 @@ * information. * * @param The {@link Checkable} entity type to be checked - * * @author Hervé Bitteur */ public class CheckBoard extends Board { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(CheckBoard.class); - //~ Instance fields ---------------------------------------------------------------------------- - // - /** For display of check suite results */ + /** For display of check suite results. */ private final CheckPanel checkPanel; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a Check Board. * @@ -76,7 +71,7 @@ public CheckBoard (String name, false, // Count false, // Vip false); // Dump - checkPanel = new CheckPanel(suite); + checkPanel = new CheckPanel<>(suite); if (suite != null) { defineLayout(suite.getName()); @@ -86,7 +81,6 @@ public CheckBoard (String name, tellObject(null); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // applySuite // //------------// @@ -109,7 +103,6 @@ public synchronized void applySuite (CheckSuite suite, tellObject(object); } - // //---------// // onEvent // //---------// diff --git a/src/main/org/audiveris/omr/check/CheckPanel.java b/src/main/org/audiveris/omr/check/CheckPanel.java index 80f70fd45..f47b30a9a 100644 --- a/src/main/org/audiveris/omr/check/CheckPanel.java +++ b/src/main/org/audiveris/omr/check/CheckPanel.java @@ -52,12 +52,10 @@ * * @param the subtype of Checkable objects used in the homogeneous collection of checks of the * suite - * * @author Hervé Bitteur */ public class CheckPanel { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(CheckPanel.class); @@ -75,27 +73,24 @@ public class CheckPanel private static final int FIELD_WIDTH = 4; - //~ Instance fields ---------------------------------------------------------------------------- - // - /** The related check suite (the model) */ + /** The related check suite (the model). */ private CheckSuite suite; - /** The swing component that includes all the fields */ + /** The swing component that includes all the fields. */ private Panel component; - /** The field for global result */ + /** The field for global result. */ private final JTextField globalField; - /** Matrix of all value fields */ + /** Matrix of all value fields. */ private JTextField[][] values; - /** Matrix of all bound fields */ + /** Matrix of all bound fields. */ private JTextField[][] bounds; - /** Last object checked */ + /** Last object checked. */ private C checkable; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a check panel for a given suite. * @@ -111,7 +106,6 @@ public CheckPanel (CheckSuite suite) setSuite(suite); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // getComponent // //--------------// @@ -324,7 +318,7 @@ private void createBoundFields () field.setText(textOf(constant.getValue())); field.setToolTipText( "" + constant.getName() + "
      " + constant.getDescription() - + ""); + + ""); } } } @@ -414,15 +408,12 @@ private double valueOf (String text) return Double.NaN; } - //~ Inner Classes ------------------------------------------------------------------------------ - // //-------------// // ParamAction // //-------------// private class ParamAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- /** * Method run whenever user presses Return/Enter in one of @@ -434,7 +425,7 @@ public void actionPerformed (ActionEvent e) // Any & several bounds may have been modified by the user // Since the same constant can be used in several fields, we have to // take a snapshot of all constants values, before modifying any one - Map values = new HashMap(); + Map values = new HashMap<>(); for (Check check : suite.getChecks()) { values.put(check.getLowDouble(), check.getLowDouble().getValue()); @@ -475,8 +466,8 @@ public void actionPerformed (ActionEvent e) try { constant.setValue(valueOf(newString)); modified = true; - sb.append(" modified from ").append(oldString).append(" to ") - .append(newString); + sb.append(" modified from ").append(oldString).append(" to ").append( + newString); logger.info(sb.toString()); } catch (Exception ex) { logger.warn("Error in {}, {}", context, ex.getLocalizedMessage()); diff --git a/src/main/org/audiveris/omr/check/CheckResult.java b/src/main/org/audiveris/omr/check/CheckResult.java index ec0c43a60..27480fe2b 100644 --- a/src/main/org/audiveris/omr/check/CheckResult.java +++ b/src/main/org/audiveris/omr/check/CheckResult.java @@ -29,7 +29,6 @@ */ public class CheckResult { - //~ Instance fields ---------------------------------------------------------------------------- /** Numerical result value. */ public double value; @@ -37,7 +36,6 @@ public class CheckResult /** The resulting grade, in 0..1 range. */ public double grade; - //~ Methods ------------------------------------------------------------------------------------ //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/check/CheckSuite.java b/src/main/org/audiveris/omr/check/CheckSuite.java index 54de86b3c..fc82c73cc 100644 --- a/src/main/org/audiveris/omr/check/CheckSuite.java +++ b/src/main/org/audiveris/omr/check/CheckSuite.java @@ -48,18 +48,15 @@ * it can detect that a constraint is not matched and thus make the whole check * suite fail. * - * @param the subtype of Checkable objects used in the - * homogeneous collection of checks in this suite - * + * @param the subtype of Checkable objects used in the homogeneous collection of checks in this + * suite * @author Hervé Bitteur */ public class CheckSuite { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(CheckSuite.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Name of this suite. */ protected final String name; @@ -70,15 +67,14 @@ public class CheckSuite protected final double goodThreshold; /** List of checks in the suite. */ - private final List> checks = new ArrayList>(); + private final List> checks = new ArrayList<>(); /** Parallel list of related weights. */ - private final List weights = new ArrayList(); + private final List weights = new ArrayList<>(); /** Total checks weight. */ private double totalWeight = 0.0d; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a suite of checks with standard threshold values. * @@ -108,7 +104,6 @@ public CheckSuite (String name, this.goodThreshold = goodThreshold; } - //~ Methods ------------------------------------------------------------------------------------ //-----// // add // //-----// @@ -198,9 +193,9 @@ public List> getChecks () return checks; } - //-----------------// + //------------------// // getGoodThreshold // - //-----------------// + //------------------// /** * Report the assigned good threshold. * diff --git a/src/main/org/audiveris/omr/check/Checkable.java b/src/main/org/audiveris/omr/check/Checkable.java index cbbcab4e8..16b3264dc 100644 --- a/src/main/org/audiveris/omr/check/Checkable.java +++ b/src/main/org/audiveris/omr/check/Checkable.java @@ -32,7 +32,6 @@ public interface Checkable extends Vip { - //~ Methods ------------------------------------------------------------------------------------ /** * Store the check failure directly into the checkable entity. diff --git a/src/main/org/audiveris/omr/check/Failure.java b/src/main/org/audiveris/omr/check/Failure.java index ce6291c0d..ae4899147 100644 --- a/src/main/org/audiveris/omr/check/Failure.java +++ b/src/main/org/audiveris/omr/check/Failure.java @@ -28,14 +28,12 @@ */ public class Failure { - //~ Instance fields ---------------------------------------------------------------------------- /** * A readable comment about the failure. */ public final String comment; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new Failure object. * @@ -46,7 +44,6 @@ public Failure (String comment) this.comment = comment; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/check/SuiteImpacts.java b/src/main/org/audiveris/omr/check/SuiteImpacts.java index 4d381c910..7ab43f646 100644 --- a/src/main/org/audiveris/omr/check/SuiteImpacts.java +++ b/src/main/org/audiveris/omr/check/SuiteImpacts.java @@ -21,7 +21,7 @@ // package org.audiveris.omr.check; -import org.audiveris.omr.sig.BasicImpacts; +import org.audiveris.omr.sig.GradeImpacts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,16 +35,13 @@ * @author Hervé Bitteur */ public class SuiteImpacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SuiteImpacts.class); - //~ Instance fields ---------------------------------------------------------------------------- private final String suiteName; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SuiteImpacts object. * @@ -60,7 +57,46 @@ protected SuiteImpacts (String[] names, this.suiteName = suiteName; } - //~ Methods ------------------------------------------------------------------------------------ + //---------// + // getDump // + //---------// + @Override + public String getDump () + { + // final List> checks = suite.getChecks(); + // final StringBuilder sb = new StringBuilder(); + // sb.append(suite.getName()).append(" ").append(checkable); + // + // for (int i = 0; i < checks.size(); i++) { + // Check check = checks.get(i); + // sb.append(" ").append(check.getName()).append(":").append( + // String.format("%.2f", values[i])); + // } + // + // sb.append( + // String.format( + // " => %.2f (min %.2f, good %.2f)", + // grade, + // suite.getMinThreshold(), + // suite.getGoodThreshold())); + // + // return sb.toString(); + return suiteName; + } + + //----------// + // setGrade // + //----------// + /** + * Set the suite grade value + * + * @param grade suite grade value + */ + public void setGrade (double grade) + { + this.grade = grade; + } + //-------------// // newInstance // //-------------// @@ -101,39 +137,4 @@ public static SuiteImpacts newInstance (CheckSuite suite, return null; } } - - //---------// - // getDump // - //---------// - @Override - public String getDump () - { - // final List> checks = suite.getChecks(); - // final StringBuilder sb = new StringBuilder(); - // sb.append(suite.getName()).append(" ").append(checkable); - // - // for (int i = 0; i < checks.size(); i++) { - // Check check = checks.get(i); - // sb.append(" ").append(check.getName()).append(":").append( - // String.format("%.2f", values[i])); - // } - // - // sb.append( - // String.format( - // " => %.2f (min %.2f, good %.2f)", - // grade, - // suite.getMinThreshold(), - // suite.getGoodThreshold())); - // - // return sb.toString(); - return suiteName; - } - - //----------// - // setGrade // - //----------// - public void setGrade (double grade) - { - this.grade = grade; - } } diff --git a/src/main/org/audiveris/omr/classifier/AbstractClassifier.java b/src/main/org/audiveris/omr/classifier/AbstractClassifier.java index 2c587b6fe..0c635dde1 100644 --- a/src/main/org/audiveris/omr/classifier/AbstractClassifier.java +++ b/src/main/org/audiveris/omr/classifier/AbstractClassifier.java @@ -73,7 +73,8 @@ * (means and standard deviations). *

      * The classifier data is thus composed of two parts (model and norms) which are loaded as a whole - * according to the following algorithm:

        + * according to the following algorithm: + *
          *
        1. It first tries to find data in the application user local area ('train'). * If found, this data contains a custom definition of model+norms, typically after a user * training.
        2. @@ -85,13 +86,11 @@ * which will be picked up first when the application is run again. * * @param precise model class to be used - * * @author Hervé Bitteur */ public abstract class AbstractClassifier implements Classifier { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -110,13 +109,9 @@ public abstract class AbstractClassifier public static final String STDS_XML_ENTRY_NAME = "stds.xml"; /** A special evaluation array, used to report NOISE. */ - protected static final Evaluation[] noiseEvaluations = { - new Evaluation( - Shape.NOISE, - Evaluation.ALGORITHM) - }; + private static final Evaluation[] noiseEvaluations = { + new Evaluation(Shape.NOISE, Evaluation.ALGORITHM)}; - //~ Instance fields ---------------------------------------------------------------------------- /** Features means and standard deviations. */ protected Norms norms; @@ -126,7 +121,6 @@ public abstract class AbstractClassifier /** The glyph checker for additional specific checks. */ protected ShapeChecker glyphChecker = ShapeChecker.getInstance(); - //~ Methods ------------------------------------------------------------------------------------ //----------// // evaluate // //----------// @@ -249,8 +243,7 @@ protected Evaluation[] getSortedEvaluations (Glyph glyph, return noiseEvaluations; } else { Evaluation[] evals = getNaturalEvaluations(glyph, interline); - // Order the evals from best to worst - Arrays.sort(evals); + Arrays.sort(evals, Evaluation.byReverseGrade); // Order the evals from best to worst return evals; } @@ -301,7 +294,7 @@ protected M load (String fileName) if (!isCompatible(model, norms)) { final String msg = "Obsolete classifier user data in " + path - + ", trying default data"; + + ", trying default data"; logger.warn(msg); } else { // Tell user we are not using the default @@ -334,9 +327,9 @@ protected M load (String fileName) logger.debug("tmpFile={}", tmpFile); tmpFile.deleteOnExit(); - InputStream is = uri.toURL().openStream(); - FileUtils.copyInputStreamToFile(is, tmpFile); - is.close(); + try (InputStream is = uri.toURL().openStream()) { + FileUtils.copyInputStreamToFile(is, tmpFile); + } zipPath = tmpFile.toPath(); } else { zipPath = Paths.get(uri); @@ -349,7 +342,7 @@ protected M load (String fileName) if (!isCompatible(model, norms)) { final String msg = "Obsolete classifier default data in " + uri - + ", please retrain from scratch"; + + ", please retrain from scratch"; logger.warn(msg); } else { logger.info("Classifier data loaded from default uri {}", uri); @@ -412,20 +405,20 @@ protected Norms loadNorms (Path root) if (meansEntry != null) { InputStream is = Files.newInputStream(meansEntry); // READ by default - DataInputStream dis = new DataInputStream(new BufferedInputStream(is)); - means = Nd4j.read(dis); - logger.info("means:{}", means); - dis.close(); + try (DataInputStream dis = new DataInputStream(new BufferedInputStream(is))) { + means = Nd4j.read(dis); + logger.info("means:{}", means); + } } final Path stdsEntry = root.resolve(STDS_ENTRY_NAME); if (stdsEntry != null) { InputStream is = Files.newInputStream(stdsEntry); // READ by default - DataInputStream dis = new DataInputStream(new BufferedInputStream(is)); - stds = Nd4j.read(dis); - logger.info("stds:{}", stds); - dis.close(); + try (DataInputStream dis = new DataInputStream(new BufferedInputStream(is))) { + stds = Nd4j.read(dis); + logger.info("stds:{}", stds); + } } if ((means != null) && (stds != null)) { @@ -478,22 +471,18 @@ protected void store (String fileName) protected void storeNorms (Path root) throws Exception { - { - Path means = root.resolve(MEANS_ENTRY_NAME); - DataOutputStream dos = new DataOutputStream( - new BufferedOutputStream(Files.newOutputStream(means, CREATE))); + Path means = root.resolve(MEANS_ENTRY_NAME); + try (DataOutputStream dos = new DataOutputStream( + new BufferedOutputStream(Files.newOutputStream(means, CREATE)))) { Nd4j.write(norms.means, dos); dos.flush(); - dos.close(); } - { - Path stds = root.resolve(STDS_ENTRY_NAME); - DataOutputStream dos = new DataOutputStream( - new BufferedOutputStream(Files.newOutputStream(stds, CREATE))); + Path stds = root.resolve(STDS_ENTRY_NAME); + try (DataOutputStream dos = new DataOutputStream( + new BufferedOutputStream(Files.newOutputStream(stds, CREATE)))) { Nd4j.write(norms.stds, dos); dos.flush(); - dos.close(); } } @@ -507,7 +496,7 @@ private Evaluation[] evaluate (Glyph glyph, EnumSet conditions, int interline) { - List bests = new ArrayList(); + List bests = new ArrayList<>(); Evaluation[] evals = getSortedEvaluations(glyph, interline); EvalsLoop: @@ -542,7 +531,6 @@ private Evaluation[] evaluate (Glyph glyph, return bests.toArray(new Evaluation[bests.size()]); } - //~ Inner Classes ------------------------------------------------------------------------------ //-------// // Norms // //-------// @@ -551,7 +539,6 @@ private Evaluation[] evaluate (Glyph glyph, */ protected static class Norms { - //~ Instance fields ------------------------------------------------------------------------ /** Features means. */ final INDArray means; @@ -559,9 +546,14 @@ protected static class Norms /** Features standard deviations. */ final INDArray stds; - //~ Constructors --------------------------------------------------------------------------- - public Norms (INDArray means, - INDArray stds) + /** + * Creates a new {@code Norms} object. + * + * @param means + * @param stds + */ + Norms (INDArray means, + INDArray stds) { this.means = means; this.stds = stds; @@ -571,10 +563,9 @@ public Norms (INDArray means, //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/classifier/Annotations.java b/src/main/org/audiveris/omr/classifier/Annotations.java index c1a179844..bf082cbc4 100644 --- a/src/main/org/audiveris/omr/classifier/Annotations.java +++ b/src/main/org/audiveris/omr/classifier/Annotations.java @@ -1,52 +1,50 @@ -//------------------------------------------------------------------------------------------------// -// // -// A n n o t a t i o n s // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.classifier; - -/** - * Class {@code Annotations} gathers definitions about symbols annotations. - * - * @author Hervé Bitteur - */ -public abstract class Annotations -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - /** File name suffix for whole book annotations: {@value}. */ - public static final String BOOK_ANNOTATIONS_SUFFIX = "-annotations.zip"; - - /** File name suffix for single sheet annotations: {@value}. */ - public static final String SHEET_ANNOTATIONS_SUFFIX = "-annotations.xml"; - - /** File format for single sheet image: {@value}. */ - public static final String SHEET_IMAGE_FORMAT = "png"; - - /** File name suffix for single sheet image: {@value}. */ - public static final String SHEET_IMAGE_SUFFIX = "-image." + SHEET_IMAGE_FORMAT; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Not meant to be instantiated. - */ - private Annotations () - { - } -} +//------------------------------------------------------------------------------------------------// +// // +// A n n o t a t i o n s // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.classifier; + +/** + * Class {@code Annotations} gathers definitions about symbols annotations. + * + * @author Hervé Bitteur + */ +public abstract class Annotations +{ + + /** File name extension for whole book annotations: {@value}. */ + public static final String BOOK_ANNOTATIONS_EXTENSION = "-annotations.zip"; + + /** File name extension for single sheet annotations: {@value}. */ + public static final String SHEET_ANNOTATIONS_EXTENSION = "-annotations.xml"; + + /** File format for single sheet image: {@value}. */ + public static final String SHEET_IMAGE_FORMAT = "png"; + + /** File name extension for single sheet image: {@value}. */ + public static final String SHEET_IMAGE_EXTENSION = "-image." + SHEET_IMAGE_FORMAT; + + /** + * Not meant to be instantiated. + */ + private Annotations () + { + } +} diff --git a/src/main/org/audiveris/omr/classifier/AnnotationsBuilder.java b/src/main/org/audiveris/omr/classifier/AnnotationsBuilder.java index 9b8b10cc1..de6463e3f 100644 --- a/src/main/org/audiveris/omr/classifier/AnnotationsBuilder.java +++ b/src/main/org/audiveris/omr/classifier/AnnotationsBuilder.java @@ -1,610 +1,593 @@ -//------------------------------------------------------------------------------------------------// -// // -// A n n o t a t i o n s B u i l d e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.classifier; - -import org.audiveris.omr.WellKnowns; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.glyph.ShapeSet; -import org.audiveris.omr.math.GeoOrder; -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.AbstractChordInter; -import org.audiveris.omr.sig.inter.ArticulationInter; -import org.audiveris.omr.sig.inter.BarConnectorInter; -import org.audiveris.omr.sig.inter.BarlineInter; -import org.audiveris.omr.sig.inter.BeamHookInter; -import org.audiveris.omr.sig.inter.BeamInter; -import org.audiveris.omr.sig.inter.BraceInter; -import org.audiveris.omr.sig.inter.BracketConnectorInter; -import org.audiveris.omr.sig.inter.BracketInter; -import org.audiveris.omr.sig.inter.EndingInter; -import org.audiveris.omr.sig.inter.FermataArcInter; -import org.audiveris.omr.sig.inter.FermataDotInter; -import org.audiveris.omr.sig.inter.HeadChordInter; -import org.audiveris.omr.sig.inter.HeadInter; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.inter.Inters; -import org.audiveris.omr.sig.inter.KeyAlterInter; -import org.audiveris.omr.sig.inter.KeyInter; -import org.audiveris.omr.sig.inter.LedgerInter; -import org.audiveris.omr.sig.inter.PedalInter; -import org.audiveris.omr.sig.inter.RepeatDotInter; -import org.audiveris.omr.sig.inter.SegmentInter; -import org.audiveris.omr.sig.inter.SentenceInter; -import org.audiveris.omr.sig.inter.SlurInter; -import org.audiveris.omr.sig.inter.SmallBeamInter; -import org.audiveris.omr.sig.inter.StemInter; -import org.audiveris.omr.sig.inter.TimeNumberInter; -import org.audiveris.omr.sig.inter.TimePairInter; -import org.audiveris.omr.sig.inter.WedgeInter; -import org.audiveris.omr.sig.inter.WordInter; -import org.audiveris.omr.sig.relation.ChordArticulationRelation; -import org.audiveris.omr.sig.relation.HeadStemRelation; -import org.audiveris.omr.sig.relation.Relation; -import org.audiveris.omrdataset.api.OmrShape; -import org.audiveris.omrdataset.api.SheetAnnotations; -import org.audiveris.omrdataset.api.SheetAnnotations.SheetInfo; -import org.audiveris.omrdataset.api.SymbolInfo; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Dimension; -import java.awt.Rectangle; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.SortedSet; - -import javax.xml.bind.JAXBException; - -/** - * Class {@code AnnotationsBuilder} processes a sheet to build the symbols Annotations - * for an OMR DataSet. - * - * @author Hervé Bitteur - */ -public class AnnotationsBuilder -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(AnnotationsBuilder.class); - - /** Inter excluded classes. */ - private static final Set excludedInterClasses = new HashSet(); - - static { - excludedInterClasses.add(AbstractChordInter.class); - excludedInterClasses.add(BarConnectorInter.class); - excludedInterClasses.add(BeamInter.class); - excludedInterClasses.add(BeamHookInter.class); // TODO: should be defined in OmrShape? - excludedInterClasses.add(BracketConnectorInter.class); - excludedInterClasses.add(BracketInter.class); // TODO: should be defined in OmrShape? - excludedInterClasses.add(EndingInter.class); - excludedInterClasses.add(FermataArcInter.class); - excludedInterClasses.add(FermataDotInter.class); - excludedInterClasses.add(KeyInter.class); - excludedInterClasses.add(RepeatDotInter.class); // Processed via BarlineInter - excludedInterClasses.add(SegmentInter.class); - excludedInterClasses.add(SentenceInter.class); - excludedInterClasses.add(SlurInter.class); - excludedInterClasses.add(SmallBeamInter.class); - ///excludedInterClasses.add(StemInter.class); - excludedInterClasses.add(TimeNumberInter.class); // Processed via TimePairInter - excludedInterClasses.add(WedgeInter.class); - excludedInterClasses.add(WordInter.class); - } - - //~ Instance fields ---------------------------------------------------------------------------- - /** The sheet to process. */ - private final Sheet sheet; - - /** Target path for sheet annotations file. */ - private final Path path; - - /** The annotations structure being built. */ - private final SheetAnnotations annotations = new SheetAnnotations(); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code AnnotationsBuilder} object. - * - * @param sheet the sheet to export - * @param path path to annotations file - */ - public AnnotationsBuilder (Sheet sheet, - Path path) - { - this.sheet = sheet; - this.path = path; - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Process the sheet to generate the corresponding annotations. - * - * @throws IOException for any IO error - * @throws JAXBException for any JAXB error - */ - public void processSheet () - throws IOException, JAXBException - { - // Global informations - annotations.setVersion("1.0"); - annotations.setComplete(false); - annotations.setSource(WellKnowns.TOOL_NAME + " " + WellKnowns.TOOL_REF); - annotations.setSheetInfo( - new SheetInfo( - sheet.getId() + Annotations.SHEET_IMAGE_SUFFIX, - new Dimension(sheet.getWidth(), sheet.getHeight()))); - - // Populate system by system - for (SystemInfo system : sheet.getSystems()) { - new SystemAnnotator(system).processSystem(); - } - - // Marshall the result - annotations.marshall(path); - logger.info("Sheet annotated as {}", path); - } - - /** - * Check whether the provided Inter subclass is excluded for Annotations. - * - * @param interClass the inter class - * @return true if excluded - */ - private static boolean isExcluded (Class interClass) - { - for (Class classe : excludedInterClasses) { - if (classe.isAssignableFrom(interClass)) { - return true; - } - } - - return false; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------------// - // SystemAnnotator // - //-----------------// - /** - * Process a system for annotations. - */ - private class SystemAnnotator - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Related system. */ - private final SystemInfo system; - - /** System sig. */ - private final SIGraph sig; - - /** All system note heads, sorted by abscissa. */ - private List allHeads; - - //~ Constructors --------------------------------------------------------------------------- - public SystemAnnotator (SystemInfo system) - { - this.system = system; - sig = system.getSig(); - } - - //~ Methods -------------------------------------------------------------------------------- - /** - * Export a barline group, including repeat dots if any. - *

          - * We export something only when called on the first barline within the group. - * We build the whole group, including repeat dots. - * - * @param bar a barline - */ - private void exportBarlineGroup (BarlineInter bar) - { - SortedSet items = bar.getGroupItems(); - - // Make sure first barline is ours - for (Inter item : items) { - if (item instanceof BarlineInter) { - if (item != bar) { - return; - } - - break; - } - } - - final int interline = bar.getStaff().getSpecificInterline(); - - if (items.size() == 1) { - // Isolated barline - OmrShape oShape = OmrShapeMapping.SHAPE_TO_OMRSHAPE.get(bar.getShape()); - annotations.addSymbol( - new SymbolInfo(oShape, interline, bar.getId(), null, bar.getBounds())); - } else { - List inners = new ArrayList(); - - for (Inter item : items) { - OmrShape oShape = OmrShapeMapping.SHAPE_TO_OMRSHAPE.get(item.getShape()); - inners.add( - new SymbolInfo(oShape, interline, item.getId(), null, item.getBounds())); - } - - // Determine the outer shape - OmrShape oShape = getBarGroupShape(items); - SymbolInfo outer = new SymbolInfo( - oShape, - interline, - null, - null, - Inters.getBounds(items)); - - for (SymbolInfo inner : inners) { - outer.addInnerSymbol(inner); - } - - annotations.addSymbol(outer); - } - } - - /** - * (Try to) generate the symbol for the provided inter. - * - * @param inter the provided inter - */ - private void exportInter (Inter inter) - { - final Class interClass = inter.getClass(); - - // Excluded class? - if (isExcluded(interClass)) { - logger.debug("{} class is excluded.", inter); - - return; - } - - Shape interShape = inter.getShape(); - Rectangle interBounds = inter.getBounds(); - Staff staff = inter.getStaff(); - OmrShape omrShape; - - // Specific classes - if (inter instanceof TimePairInter) { - exportTimePair((TimePairInter) inter); - - return; - } else if (inter instanceof BarlineInter) { - exportBarlineGroup((BarlineInter) inter); - - return; - } else if (inter instanceof KeyAlterInter) { - omrShape = getOmrShape((KeyAlterInter) inter); - } else if (inter instanceof ArticulationInter) { - omrShape = getOmrShape((ArticulationInter) inter); - } else { - omrShape = OmrShapeMapping.SHAPE_TO_OMRSHAPE.get(interShape); - - if (omrShape == null) { - logger.info("{} shape is not mapped.", inter); - - return; - } - - if (inter instanceof LedgerInter) { - // Make sure we have no note head centered on this ledger - if (ledgerHasHead((LedgerInter) inter)) { - return; - } - } else if (inter instanceof BarlineInter) { - // Substitute a barlineDouble when relevant - BarlineInter sibling = ((BarlineInter) inter).getSibling(); - - if (sibling != null) { - if (inter.getCenter().x < sibling.getCenter().x) { - omrShape = OmrShape.barlineDouble; - interBounds = interBounds.union(sibling.getBounds()); - } else { - return; - } - } - } - } - - if (staff == null) { - if (inter instanceof BraceInter) { - // Simply pick up the first staff embraced by this brace - staff = ((BraceInter) inter).getFirstStaff(); - } else if (inter instanceof StemInter) { - // Use staff of first head found - staff = getStemStaff((StemInter) inter); - } else if (inter instanceof PedalInter) { - // Use bottom staff of related chord if any - staff = getPedalStaff((PedalInter) inter); - } - } - - if (omrShape == null) { - logger.warn("{} has no OmrShape, ignored.", inter); - - return; - } - - if (staff == null) { - logger.info("{} has no related staff, ignored.", inter); - - return; - } - - final int interline = staff.getSpecificInterline(); - annotations.addSymbol( - new SymbolInfo(omrShape, interline, inter.getId(), null, interBounds)); - } - - /** - * Export a TimePairInter (time signature defined as a pair num/den). - *

          - * It the pair is known as a predefined combo, we generate one outer symbol (the pair) with - * two inner symbols (num & den). - * Otherwise, we simply generate two stand-alone symbols (num & den). - * - * @param pair the inter to process - */ - private void exportTimePair (TimePairInter pair) - { - final int interline = pair.getStaff().getSpecificInterline(); - final List inners = new ArrayList(); - - for (Inter inter : pair.getMembers()) { - OmrShape oShape = OmrShapeMapping.SHAPE_TO_OMRSHAPE.get(inter.getShape()); - - if (oShape != null) { - inners.add( - new SymbolInfo(oShape, interline, inter.getId(), null, inter.getBounds())); - } - } - - final OmrShape pairShape = OmrShapeMapping.getTimeCombo((TimePairInter) pair); - - if (pairShape != null) { - SymbolInfo outer = new SymbolInfo( - pairShape, - interline, - pair.getId(), - null, - pair.getBounds()); - - for (SymbolInfo inner : inners) { - outer.addInnerSymbol(inner); - } - - annotations.addSymbol(outer); - } else { - logger.info("{} is not a predefined time combo.", pair); - - for (SymbolInfo inner : inners) { - annotations.addSymbol(inner); - } - } - } - - /** - * Determine the proper OmrShape for the provided bar group. - * - * @param items inters that compose the barline group (perhaps including dots) - * @return the corresponding OmrShape - */ - private OmrShape getBarGroupShape (SortedSet items) - { - final Inter first = items.first(); - final Inter last = items.last(); - - if (first instanceof RepeatDotInter) { - if (last instanceof RepeatDotInter) { - return OmrShape.repeatRightLeft; - } else { - return OmrShape.repeatRight; - } - } else { - if (last instanceof RepeatDotInter) { - return OmrShape.repeatLeft; - } else { - // No repeat dot on either side - final Shape firstShape = first.getShape(); - final Shape lastShape = last.getShape(); - - if (firstShape == Shape.THICK_BARLINE) { - if (lastShape == Shape.THICK_BARLINE) { - return OmrShape.barlineHeavyHeavy; - } else { - return OmrShape.barlineReverseFinal; - } - } else { - if (lastShape == Shape.THICK_BARLINE) { - return OmrShape.barlineFinal; - } else { - return OmrShape.barlineDouble; - } - } - } - } - } - - /** - * Report the OmrShape for a given ArticulationInter. - * - * @param inter the provided articulation - * @return the proper OmrShape value - */ - private OmrShape getOmrShape (ArticulationInter inter) - { - Boolean above = null; - - for (Relation rel : sig.getRelations(inter, ChordArticulationRelation.class)) { - final HeadChordInter chord = (HeadChordInter) sig.getOppositeInter(inter, rel); - above = inter.getCenter().y < chord.getCenter().y; - - break; - } - - if (above == null) { - // No relation, use position WRT related staff - if (inter.getStaff() != null) { - above = inter.getStaff().pitchPositionOf(inter.getCenter()) < 0; - } - } - - if (above == null) { - return null; - } - - switch (inter.getShape()) { - case ACCENT: - return above ? OmrShape.articAccentAbove : OmrShape.articAccentBelow; - - case STACCATO: - return above ? OmrShape.articStaccatoAbove : OmrShape.articStaccatoBelow; - - case TENUTO: - return above ? OmrShape.articTenutoAbove : OmrShape.articTenutoBelow; - - case STACCATISSIMO: - return above ? OmrShape.articStaccatissimoAbove : OmrShape.articStaccatissimoBelow; - - case STRONG_ACCENT: - return above ? OmrShape.articMarcatoAbove : OmrShape.articMarcatoBelow; - } - - return null; - } - - /** - * Report the OmrShape for a given KeyAlterInter. - * - * @param inter the provided KeyAlterInter - * @return the proper OmrShape value - */ - private OmrShape getOmrShape (KeyAlterInter inter) - { - switch (inter.getShape()) { - case FLAT: - return OmrShape.keyFlat; - - case NATURAL: - return OmrShape.keyNatural; - - case SHARP: - return OmrShape.keySharp; - } - - return null; - } - - /** - * Report a reasonable staff for a given pedal - * - * @param pedal the given pedal - * @return head staff or null - */ - private Staff getPedalStaff (PedalInter pedal) - { - AbstractChordInter chord = pedal.getChord(); - Staff staff = null; - - if (chord != null) { - staff = chord.getBottomStaff(); - } - - if (staff == null) { - staff = system.getStaffAtOrAbove(pedal.getCenter()); - } - - return staff; - } - - /** - * Report a reasonable staff for a given stem - * - * @param stem the given stem - * @return head staff or null - */ - private Staff getStemStaff (StemInter stem) - { - for (Relation relation : sig.getRelations(stem, HeadStemRelation.class)) { - Inter head = sig.getEdgeSource(relation); - - return head.getStaff(); - } - - return null; - } - - /** - * Check whether there is a note centered on provided ledger. - * - * @param ledger provided ledger - * @return true if there is a note head centered on ledger - */ - private boolean ledgerHasHead (LedgerInter ledger) - { - final Rectangle ledgerBox = ledger.getBounds(); - final Staff staff = ledger.getStaff(); - final Integer index = staff.getLedgerIndex(ledger); - final int ledgerPitch = Staff.getLedgerPitchPosition(index); - final List heads = Inters.intersectedInters( - allHeads, - GeoOrder.BY_ABSCISSA, - ledgerBox); - - for (Inter inter : heads) { - final HeadInter head = (HeadInter) inter; - final int notePitch = head.getIntegerPitch(); - - if (notePitch == ledgerPitch) { - return true; - } - } - - return false; - } - - /** - * Process the system at hand. - */ - private void processSystem () - { - allHeads = sig.inters(ShapeSet.Heads); - Collections.sort(allHeads, Inters.byAbscissa); - - for (Inter inter : sig.vertexSet()) { - exportInter(inter); - } - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// A n n o t a t i o n s B u i l d e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.classifier; + +import org.audiveris.omr.WellKnowns; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.glyph.ShapeSet; +import org.audiveris.omr.math.GeoOrder; +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.AbstractChordInter; +import org.audiveris.omr.sig.inter.ArticulationInter; +import org.audiveris.omr.sig.inter.BarConnectorInter; +import org.audiveris.omr.sig.inter.BarlineInter; +import org.audiveris.omr.sig.inter.BeamHookInter; +import org.audiveris.omr.sig.inter.BeamInter; +import org.audiveris.omr.sig.inter.BraceInter; +import org.audiveris.omr.sig.inter.BracketConnectorInter; +import org.audiveris.omr.sig.inter.BracketInter; +import org.audiveris.omr.sig.inter.EndingInter; +import org.audiveris.omr.sig.inter.FermataArcInter; +import org.audiveris.omr.sig.inter.FermataDotInter; +import org.audiveris.omr.sig.inter.HeadChordInter; +import org.audiveris.omr.sig.inter.HeadInter; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.inter.Inters; +import org.audiveris.omr.sig.inter.KeyAlterInter; +import org.audiveris.omr.sig.inter.KeyInter; +import org.audiveris.omr.sig.inter.LedgerInter; +import org.audiveris.omr.sig.inter.PedalInter; +import org.audiveris.omr.sig.inter.RepeatDotInter; +import org.audiveris.omr.sig.inter.SegmentInter; +import org.audiveris.omr.sig.inter.SentenceInter; +import org.audiveris.omr.sig.inter.SlurInter; +import org.audiveris.omr.sig.inter.SmallBeamInter; +import org.audiveris.omr.sig.inter.StaffBarlineInter; +import org.audiveris.omr.sig.inter.StemInter; +import org.audiveris.omr.sig.inter.TimeNumberInter; +import org.audiveris.omr.sig.inter.TimePairInter; +import org.audiveris.omr.sig.inter.WedgeInter; +import org.audiveris.omr.sig.inter.WordInter; +import org.audiveris.omr.sig.relation.ChordArticulationRelation; +import org.audiveris.omr.sig.relation.HeadStemRelation; +import org.audiveris.omr.sig.relation.Relation; +import org.audiveris.omrdataset.api.OmrShape; +import org.audiveris.omrdataset.api.SheetAnnotations; +import org.audiveris.omrdataset.api.SheetAnnotations.SheetInfo; +import org.audiveris.omrdataset.api.SymbolInfo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; + +import javax.xml.bind.JAXBException; + +/** + * Class {@code AnnotationsBuilder} processes a sheet to build the symbols Annotations + * for an OMR DataSet. + * + * @author Hervé Bitteur + */ +public class AnnotationsBuilder +{ + + private static final Logger logger = LoggerFactory.getLogger(AnnotationsBuilder.class); + + /** Inter excluded classes. */ + private static final Set excludedInterClasses = new HashSet<>(); + + static { + excludedInterClasses.add(AbstractChordInter.class); + excludedInterClasses.add(BarConnectorInter.class); + excludedInterClasses.add(BeamInter.class); + excludedInterClasses.add(BeamHookInter.class); // TODO: should be defined in OmrShape? + excludedInterClasses.add(BracketConnectorInter.class); + excludedInterClasses.add(BracketInter.class); // TODO: should be defined in OmrShape? + excludedInterClasses.add(EndingInter.class); + excludedInterClasses.add(FermataArcInter.class); + excludedInterClasses.add(FermataDotInter.class); + excludedInterClasses.add(KeyInter.class); + excludedInterClasses.add(RepeatDotInter.class); // Processed via BarlineInter + excludedInterClasses.add(SegmentInter.class); + excludedInterClasses.add(SentenceInter.class); + excludedInterClasses.add(SlurInter.class); + excludedInterClasses.add(SmallBeamInter.class); + excludedInterClasses.add(StaffBarlineInter.class); + ///excludedInterClasses.add(StemInter.class); + excludedInterClasses.add(TimeNumberInter.class); // Processed via TimePairInter + excludedInterClasses.add(WedgeInter.class); + excludedInterClasses.add(WordInter.class); + } + + /** The sheet to process. */ + private final Sheet sheet; + + /** Target path for sheet annotations file. */ + private final Path path; + + /** The annotations structure being built. */ + private final SheetAnnotations annotations = new SheetAnnotations(); + + /** + * Creates a new {@code AnnotationsBuilder} object. + * + * @param sheet the sheet to export + * @param path path to annotations file + */ + public AnnotationsBuilder (Sheet sheet, + Path path) + { + this.sheet = sheet; + this.path = path; + } + + /** + * Process the sheet to generate the corresponding annotations. + * + * @throws IOException for any IO error + * @throws JAXBException for any JAXB error + */ + public void processSheet () + throws IOException, + JAXBException + { + // Global informations + annotations.setVersion("1.0"); + annotations.setComplete(false); + annotations.setSource(WellKnowns.TOOL_NAME + " " + WellKnowns.TOOL_REF); + annotations.setSheetInfo(new SheetInfo(sheet.getId() + Annotations.SHEET_IMAGE_EXTENSION, + new Dimension(sheet.getWidth(), sheet.getHeight()))); + + // Populate system by system + for (SystemInfo system : sheet.getSystems()) { + new SystemAnnotator(system).processSystem(); + } + + // Marshall the result + annotations.marshall(path); + logger.info("Sheet annotated as {}", path); + } + + /** + * Check whether the provided Inter subclass is excluded for Annotations. + * + * @param interClass the inter class + * @return true if excluded + */ + private static boolean isExcluded (Class interClass) + { + for (Class classe : excludedInterClasses) { + if (classe.isAssignableFrom(interClass)) { + return true; + } + } + + return false; + } + + //-----------------// + // SystemAnnotator // + //-----------------// + /** + * Process a system for annotations. + */ + private class SystemAnnotator + { + + /** Related system. */ + private final SystemInfo system; + + /** System sig. */ + private final SIGraph sig; + + /** All system note heads, sorted by abscissa. */ + private List allHeads; + + SystemAnnotator (SystemInfo system) + { + this.system = system; + sig = system.getSig(); + } + + /** + * Export a barline group, including repeat dots if any. + *

          + * We export something only when called on the first barline within the group. + * We build the whole group, including repeat dots. + * + * @param bar a barline + */ + private void exportBarlineGroup (BarlineInter bar) + { + SortedSet items = bar.getGroupItems(); + + // Make sure first barline is ours + for (Inter item : items) { + if (item instanceof BarlineInter) { + if (item != bar) { + return; + } + + break; + } + } + + final int interline = bar.getStaff().getSpecificInterline(); + + if (items.size() == 1) { + // Isolated barline + OmrShape oShape = OmrShapeMapping.SHAPE_TO_OMRSHAPE.get(bar.getShape()); + annotations.addSymbol(new SymbolInfo(oShape, interline, bar.getId(), null, bar + .getBounds())); + } else { + List inners = new ArrayList<>(); + + for (Inter item : items) { + OmrShape oShape = OmrShapeMapping.SHAPE_TO_OMRSHAPE.get(item.getShape()); + inners.add(new SymbolInfo(oShape, interline, item.getId(), null, item + .getBounds())); + } + + // Determine the outer shape + OmrShape oShape = getBarGroupShape(items); + SymbolInfo outer = new SymbolInfo(oShape, interline, null, null, Inters.getBounds( + items)); + + for (SymbolInfo inner : inners) { + outer.addInnerSymbol(inner); + } + + annotations.addSymbol(outer); + } + } + + /** + * (Try to) generate the symbol for the provided inter. + * + * @param inter the provided inter + */ + private void exportInter (Inter inter) + { + final Class interClass = inter.getClass(); + + // Excluded class? + if (isExcluded(interClass)) { + logger.debug("{} class is excluded.", inter); + + return; + } + + Shape interShape = inter.getShape(); + Rectangle interBounds = inter.getBounds(); + Staff staff = inter.getStaff(); + OmrShape omrShape; + + // Specific classes + if (inter instanceof TimePairInter) { + exportTimePair((TimePairInter) inter); + + return; + } else if (inter instanceof BarlineInter) { + exportBarlineGroup((BarlineInter) inter); + + return; + } else if (inter instanceof KeyAlterInter) { + omrShape = getOmrShape((KeyAlterInter) inter); + } else if (inter instanceof ArticulationInter) { + omrShape = getOmrShape((ArticulationInter) inter); + } else { + omrShape = OmrShapeMapping.SHAPE_TO_OMRSHAPE.get(interShape); + + if (omrShape == null) { + logger.info("{} shape is not mapped.", inter); + + return; + } + + if (inter instanceof LedgerInter) { + // Make sure we have no note head centered on this ledger + if (ledgerHasHead((LedgerInter) inter)) { + return; + } + } else if (inter instanceof BarlineInter) { + // Substitute a barlineDouble when relevant + BarlineInter sibling = ((BarlineInter) inter).getSibling(); + + if (sibling != null) { + if (inter.getCenter().x < sibling.getCenter().x) { + omrShape = OmrShape.barlineDouble; + interBounds = interBounds.union(sibling.getBounds()); + } else { + return; + } + } + } + } + + if (staff == null) { + if (inter instanceof BraceInter) { + // Simply pick up the first staff embraced by this brace + staff = ((BraceInter) inter).getFirstStaff(); + } else if (inter instanceof StemInter) { + // Use staff of first head found + staff = getStemStaff((StemInter) inter); + } else if (inter instanceof PedalInter) { + // Use bottom staff of related chord if any + staff = getPedalStaff((PedalInter) inter); + } + } + + if (omrShape == null) { + logger.warn("{} has no OmrShape, ignored.", inter); + + return; + } + + if (staff == null) { + logger.info("{} has no related staff, ignored.", inter); + + return; + } + + final int interline = staff.getSpecificInterline(); + annotations.addSymbol(new SymbolInfo(omrShape, interline, inter.getId(), null, + interBounds)); + } + + /** + * Export a TimePairInter (time signature defined as a pair num/den). + *

          + * It the pair is known as a predefined combo, we generate one outer symbol (the pair) with + * two inner symbols (num & den). + * Otherwise, we simply generate two stand-alone symbols (num & den). + * + * @param pair the inter to process + */ + private void exportTimePair (TimePairInter pair) + { + final int interline = pair.getStaff().getSpecificInterline(); + final List inners = new ArrayList<>(); + + for (Inter inter : pair.getMembers()) { + OmrShape oShape = OmrShapeMapping.SHAPE_TO_OMRSHAPE.get(inter.getShape()); + + if (oShape != null) { + inners.add(new SymbolInfo(oShape, interline, inter.getId(), null, inter + .getBounds())); + } + } + + final OmrShape pairShape = OmrShapeMapping.getTimeCombo(pair); + + if (pairShape != null) { + SymbolInfo outer = new SymbolInfo(pairShape, interline, pair.getId(), null, pair + .getBounds()); + + for (SymbolInfo inner : inners) { + outer.addInnerSymbol(inner); + } + + annotations.addSymbol(outer); + } else { + logger.info("{} is not a predefined time combo.", pair); + + for (SymbolInfo inner : inners) { + annotations.addSymbol(inner); + } + } + } + + /** + * Determine the proper OmrShape for the provided bar group. + * + * @param items inters that compose the barline group (perhaps including dots) + * @return the corresponding OmrShape + */ + private OmrShape getBarGroupShape (SortedSet items) + { + final Inter first = items.first(); + final Inter last = items.last(); + + if (first instanceof RepeatDotInter) { + if (last instanceof RepeatDotInter) { + return OmrShape.repeatRightLeft; + } else { + return OmrShape.repeatRight; + } + } else { + if (last instanceof RepeatDotInter) { + return OmrShape.repeatLeft; + } else { + // No repeat dot on either side + final Shape firstShape = first.getShape(); + final Shape lastShape = last.getShape(); + + if (firstShape == Shape.THICK_BARLINE) { + if (lastShape == Shape.THICK_BARLINE) { + return OmrShape.barlineHeavyHeavy; + } else { + return OmrShape.barlineReverseFinal; + } + } else { + if (lastShape == Shape.THICK_BARLINE) { + return OmrShape.barlineFinal; + } else { + return OmrShape.barlineDouble; + } + } + } + } + } + + /** + * Report the OmrShape for a given ArticulationInter. + * + * @param inter the provided articulation + * @return the proper OmrShape value + */ + private OmrShape getOmrShape (ArticulationInter inter) + { + Boolean above = null; + + for (Relation rel : sig.getRelations(inter, ChordArticulationRelation.class)) { + final HeadChordInter chord = (HeadChordInter) sig.getOppositeInter(inter, rel); + above = inter.getCenter().y < chord.getCenter().y; + + break; + } + + if (above == null) { + // No relation, use position WRT related staff + if (inter.getStaff() != null) { + above = inter.getStaff().pitchPositionOf(inter.getCenter()) < 0; + } + } + + if (above == null) { + return null; + } + + switch (inter.getShape()) { + case ACCENT: + return above ? OmrShape.articAccentAbove : OmrShape.articAccentBelow; + + case STACCATO: + return above ? OmrShape.articStaccatoAbove : OmrShape.articStaccatoBelow; + + case TENUTO: + return above ? OmrShape.articTenutoAbove : OmrShape.articTenutoBelow; + + case STACCATISSIMO: + return above ? OmrShape.articStaccatissimoAbove : OmrShape.articStaccatissimoBelow; + + case STRONG_ACCENT: + return above ? OmrShape.articMarcatoAbove : OmrShape.articMarcatoBelow; + } + + return null; + } + + /** + * Report the OmrShape for a given KeyAlterInter. + * + * @param inter the provided KeyAlterInter + * @return the proper OmrShape value + */ + private OmrShape getOmrShape (KeyAlterInter inter) + { + switch (inter.getShape()) { + case FLAT: + return OmrShape.keyFlat; + + case NATURAL: + return OmrShape.keyNatural; + + case SHARP: + return OmrShape.keySharp; + } + + return null; + } + + /** + * Report a reasonable staff for a given pedal + * + * @param pedal the given pedal + * @return head staff or null + */ + private Staff getPedalStaff (PedalInter pedal) + { + AbstractChordInter chord = pedal.getChord(); + Staff staff = null; + + if (chord != null) { + staff = chord.getBottomStaff(); + } + + if (staff == null) { + staff = system.getStaffAtOrAbove(pedal.getCenter()); + } + + return staff; + } + + /** + * Report a reasonable staff for a given stem + * + * @param stem the given stem + * @return head staff or null + */ + private Staff getStemStaff (StemInter stem) + { + for (Relation relation : sig.getRelations(stem, HeadStemRelation.class)) { + Inter head = sig.getEdgeSource(relation); + + return head.getStaff(); + } + + return null; + } + + /** + * Check whether there is a note centered on provided ledger. + * + * @param ledger provided ledger + * @return true if there is a note head centered on ledger + */ + private boolean ledgerHasHead (LedgerInter ledger) + { + final Rectangle ledgerBox = ledger.getBounds(); + final Staff staff = ledger.getStaff(); + final Integer index = staff.getLedgerIndex(ledger); + final int ledgerPitch = Staff.getLedgerPitchPosition(index); + final List heads = Inters.intersectedInters(allHeads, GeoOrder.BY_ABSCISSA, + ledgerBox); + + for (Inter inter : heads) { + final HeadInter head = (HeadInter) inter; + final int notePitch = head.getIntegerPitch(); + + if (notePitch == ledgerPitch) { + return true; + } + } + + return false; + } + + /** + * Process the system at hand. + */ + private void processSystem () + { + allHeads = sig.inters(ShapeSet.Heads); + Collections.sort(allHeads, Inters.byAbscissa); + + for (Inter inter : sig.vertexSet()) { + exportInter(inter); + } + } + } +} diff --git a/src/main/org/audiveris/omr/classifier/ArtGlyphDescriptor.java b/src/main/org/audiveris/omr/classifier/ArtGlyphDescriptor.java index 025f00902..4da4ec705 100644 --- a/src/main/org/audiveris/omr/classifier/ArtGlyphDescriptor.java +++ b/src/main/org/audiveris/omr/classifier/ArtGlyphDescriptor.java @@ -34,12 +34,10 @@ public class ArtGlyphDescriptor extends GlyphDescriptor { - //~ Static fields/initializers ----------------------------------------------------------------- /** Use the ART moments + 3 GEO + weight + aspect. */ private static final int LENGTH = ARTMoments.MOMENT_COUNT + 5; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ArtGlyphDescriptor} object. */ @@ -48,7 +46,6 @@ public ArtGlyphDescriptor () super("art"); } - //~ Methods ------------------------------------------------------------------------------------ @Override public String[] getFeatureLabels () { @@ -91,18 +88,16 @@ public int length () return LENGTH; } - //~ Inner Classes ------------------------------------------------------------------------------ //--------------// // LabelsHolder // //--------------// /** * Descriptive strings for glyph characteristics. - * + *

          * NOTA: To be kept in sync method {@link #getFeatures} */ private static class LabelsHolder { - //~ Static fields/initializers ------------------------------------------------------------- /** Index -> Label */ public static final String[] labels = new String[LENGTH]; @@ -129,7 +124,6 @@ private static class LabelsHolder labels[i++] = "aspect"; } - //~ Constructors --------------------------------------------------------------------------- private LabelsHolder () { } diff --git a/src/main/org/audiveris/omr/classifier/BasicClassifier.java b/src/main/org/audiveris/omr/classifier/BasicClassifier.java index 99cd39cf7..e0e395fac 100644 --- a/src/main/org/audiveris/omr/classifier/BasicClassifier.java +++ b/src/main/org/audiveris/omr/classifier/BasicClassifier.java @@ -1,543 +1,531 @@ -//------------------------------------------------------------------------------------------------// -// // -// B a s i c C l a s s i f i e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.classifier; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.glyph.ShapeSet; -import org.audiveris.omr.math.NeuralNetwork; -import org.audiveris.omr.math.PoorManAlgebra.DataSet; -import org.audiveris.omr.math.PoorManAlgebra.INDArray; -import org.audiveris.omr.math.PoorManAlgebra.Nd4j; -import org.audiveris.omr.util.Jaxb; -import org.audiveris.omr.util.StopWatch; - -//import org.nd4j.linalg.api.ndarray.INDArray; -//import org.nd4j.linalg.dataset.DataSet; -//import org.nd4j.linalg.factory.Nd4j; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import static java.nio.file.StandardOpenOption.CREATE; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Unmarshaller; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code BasicClassifier} is the pre-DL4J classifier, based on a home-built - * shallow network operating on MixGlyphDescriptor. - * - * @author Hervé Bitteur - */ -public class BasicClassifier - extends AbstractClassifier -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger(BasicClassifier.class); - - /** The singleton. */ - private static volatile BasicClassifier INSTANCE; - - /** Classifier file name. */ - public static final String FILE_NAME = "basic-classifier.zip"; - - /** Model entry name. */ - public static final String MODEL_ENTRY_NAME = "model.xml"; - - //~ Instance fields ---------------------------------------------------------------------------- - /** The underlying (old) neural network. */ - private NeuralNetwork model; - - /** Training listener, if any. */ - private TrainingMonitor listener; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Private constructor, to create a glyph neural network. - */ - private BasicClassifier () - { - descriptor = new MixGlyphDescriptor(); - - // Unmarshal from user or default data, if compatible - model = load(FILE_NAME); - - if (model == null) { - model = createNetwork(); - } - } - - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // getInstance // - //-------------// - /** - * Report the single instance of BasicClassifier in the application. - * - * @return the instance - */ - public static BasicClassifier getInstance () - { - if (INSTANCE == null) { - synchronized (BasicClassifier.class) { - if (INSTANCE == null) { - INSTANCE = new BasicClassifier(); - } - } - } - - return INSTANCE; - } - - //--------------// - // getMaxEpochs // - //--------------// - /** - * Selector on the maximum number of training epochs. - * - * @return the upper limit on epochs counter - */ - @Override - public int getMaxEpochs () - { - return constants.maxEpochs.getValue(); - } - - //---------// - // getName // - //---------// - @Override - public final String getName () - { - return "Basic Classifier"; - } - - //-------------// - // addListener // - //-------------// - @Override - public void addListener (TrainingMonitor listener) - { - this.listener = listener; - } - - //-----------------------// - // getNaturalEvaluations // - //-----------------------// - @Override - public Evaluation[] getNaturalEvaluations (Glyph glyph, - int interline) - { - double[] ins = descriptor.getFeatures(glyph, interline); - final INDArray features = Nd4j.create(ins); - normalize(features); - - Shape[] values = Shape.values(); - Evaluation[] evals = new Evaluation[SHAPE_COUNT]; - - for (int i = 0; i < ins.length; i++) { - ins[i] = features.getDouble(i); - } - - double[] outs = new double[SHAPE_COUNT]; - model.run(ins, null, outs); - - for (int s = 0; s < SHAPE_COUNT; s++) { - evals[s] = new Evaluation(values[s], outs[s]); - } - - return evals; - } - - //-------// - // reset // - //-------// - @Override - public void reset () - { - model = createNetwork(); - } - - //--------------// - // setMaxEpochs // - //--------------// - /** - * Modify the upper limit on the number of epochs for the training process. - * - * @param maxEpochs new value for epochs limit - */ - @Override - public void setMaxEpochs (int maxEpochs) - { - model.setEpochs(maxEpochs); - constants.maxEpochs.setValue(maxEpochs); - } - - //------// - // stop // - //------// - @Override - public void stop () - { - model.stop(); - } - - //-------// - // train // - //-------// - @SuppressWarnings("unchecked") - @Override - public void train (Collection samples) - { - logger.info("Training on {} samples", samples.size()); - - if (samples.isEmpty()) { - logger.warn("No sample to retrain neural classifier"); - - return; - } - - StopWatch watch = new StopWatch("train"); - watch.start("shuffle"); - - // Shuffle the collection of samples - final List newSamples = new ArrayList(samples); - Collections.shuffle(newSamples); - - // Build raw dataset - watch.start("getRawDataSet"); - - final DataSet dataSet = getRawDataSet(newSamples); - final INDArray features = dataSet.getFeatures(); - - // Record mean and standard deviation for every feature - watch.start("norms"); - norms = new Norms(features.mean(0), features.std(0)); - norms.stds.addi(Nd4j.scalar(Nd4j.EPS_THRESHOLD)); // Safer, to avoid later division by 0 - logger.debug("means:{}", norms.means); - logger.debug("stds:{}", norms.stds); - watch.start("normalize"); - normalize(features); - - // Convert features for NeuralNetwork data format - int rows = features.rows(); - int cols = features.columns(); - logger.info("samples: {}", rows); - logger.info("features: {}", cols); - - INDArray labels = dataSet.getLabels(); - double[][] inputs = new double[newSamples.size()][]; - double[][] desiredOutputs = new double[newSamples.size()][]; - watch.start("build input & desiredOutputs"); - - for (int ig = 0; ig < rows; ig++) { - INDArray featureRow = features.getRow(ig); - double[] ins = new double[cols]; - inputs[ig] = ins; - - for (int j = 0; j < cols; j++) { - ins[j] = featureRow.getDouble(j); - } - - INDArray labelRow = labels.getRow(ig); - double[] des = new double[SHAPE_COUNT]; - desiredOutputs[ig] = des; - - for (int j = 0; j < SHAPE_COUNT; j++) { - des[j] = labelRow.getDouble(j); - } - } - - if (constants.printWatch.isSet()) { - watch.print(); - } - - // Train - model.train(inputs, desiredOutputs, listener, listener.getIterationPeriod()); - - // Store - store(FILE_NAME); - } - - //--------------// - // isCompatible // - //--------------// - @Override - protected boolean isCompatible (NeuralNetwork model, - Norms norms) - { - if (!Arrays.equals(model.getInputLabels(), descriptor.getFeatureLabels())) { - if (logger.isDebugEnabled()) { - logger.debug("Engine inputs: {}", Arrays.toString(model.getInputLabels())); - logger.debug("Shape inputs: {}", Arrays.toString(descriptor.getFeatureLabels())); - } - - return false; - } - - if (!Arrays.equals(model.getOutputLabels(), ShapeSet.getPhysicalShapeNames())) { - if (logger.isDebugEnabled()) { - logger.debug("Engine outputs: {}", Arrays.toString(model.getOutputLabels())); - logger.debug( - "Physical shapes: {}", - Arrays.toString(ShapeSet.getPhysicalShapeNames())); - } - - return false; - } - - return true; - } - - //-----------// - // loadModel // - //-----------// - @Override - protected NeuralNetwork loadModel (Path root) - throws Exception - { - Path modelPath = root.resolve(MODEL_ENTRY_NAME); - InputStream is = Files.newInputStream(modelPath); - NeuralNetwork nn = NeuralNetwork.unmarshal(is); - is.close(); - - return nn; - } - - //-----------// - // loadNorms // - //-----------// - /** - * {@inheritDoc}. - *

          - * Rather than binary we use XML format. - * - * @param root the root path to file system - * @return the loaded Norms instance, or exception is thrown - * @throws Exception - */ - @Override - protected Norms loadNorms (Path root) - throws Exception - { - final JAXBContext jaxbContext = JAXBContext.newInstance(MyVector.class); - final Unmarshaller um = jaxbContext.createUnmarshaller(); - - INDArray means = null; - INDArray stds = null; - - final Path meansEntry = root.resolve(MEANS_XML_ENTRY_NAME); - - if (meansEntry != null) { - InputStream is = Files.newInputStream(meansEntry); // READ by default - BufferedInputStream bis = new BufferedInputStream(is); - MyVector vector = (MyVector) um.unmarshal(bis); - means = Nd4j.create(vector.data); - logger.debug("means:{}", means); - bis.close(); - } - - final Path stdsEntry = root.resolve(STDS_XML_ENTRY_NAME); - - if (stdsEntry != null) { - InputStream is = Files.newInputStream(stdsEntry); // READ by default - BufferedInputStream bis = new BufferedInputStream(is); - MyVector vector = (MyVector) um.unmarshal(bis); - stds = Nd4j.create(vector.data); - logger.debug("stds:{}", stds); - bis.close(); - } - - if ((means != null) && (stds != null)) { - logger.info("Classifier loaded XML norms."); - - return new Norms(means, stds); - } - - return null; - } - - //------------// - // storeModel // - //------------// - @Override - protected void storeModel (Path root) - throws Exception - { - Path modelPath = root.resolve(MODEL_ENTRY_NAME); - OutputStream bos = new BufferedOutputStream(Files.newOutputStream(modelPath, CREATE)); - model.marshal(bos); - bos.flush(); - bos.close(); - logger.info("Engine marshalled to {}", modelPath); - } - - //------------// - // storeNorms // - //------------// - /** - * {@inheritDoc}. - *

          - * Rather than binary, we use XML format. - * - * @throws Exception - */ - @Override - protected void storeNorms (Path root) - throws Exception - { - final JAXBContext jaxbContext = JAXBContext.newInstance(MyVector.class); - - { - Path means = root.resolve(MEANS_XML_ENTRY_NAME); - OutputStream bos = new BufferedOutputStream(Files.newOutputStream(means, CREATE)); - MyVector vector = new MyVector(norms.means); - Jaxb.marshal(vector, bos, jaxbContext); - bos.flush(); - bos.close(); - } - - { - Path stds = root.resolve(STDS_XML_ENTRY_NAME); - OutputStream bos = new BufferedOutputStream(Files.newOutputStream(stds, CREATE)); - MyVector vector = new MyVector(norms.stds); - Jaxb.marshal(vector, bos, jaxbContext); - bos.flush(); - bos.close(); - } - } - - //---------------// - // createNetwork // - //---------------// - private NeuralNetwork createNetwork () - { - // Get a brand new one (not trained) - logger.info("Creating a brand new {}", getName()); - - // We allocate a hidden layer with as many cells as the output layer - return new NeuralNetwork( - descriptor.length(), - SHAPE_COUNT, - SHAPE_COUNT, - constants.amplitude.getValue(), - descriptor.getFeatureLabels(), // Input labels - ShapeSet.getPhysicalShapeNames(), // Output labels - constants.learningRate.getValue(), - constants.momentum.getValue(), - getMaxEpochs()); - } - - //-----------// - // normalize // - //-----------// - /** - * Apply the known norms on the provided (raw) features. - * - * @param features raw features, to be normalized in situ - */ - private void normalize (INDArray features) - { - features.subiRowVector(norms.means); - features.diviRowVector(norms.stds); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print out the stop watch?"); - - private final Constant.Ratio amplitude = new Constant.Ratio( - 0.5, - "Initial weight amplitude"); - - private final Constant.Ratio learningRate = new Constant.Ratio(0.1, "Learning Rate"); - - private final Constant.Integer maxEpochs = new Constant.Integer( - "Epochs", - 500, - "Maximum number of epochs in training"); - - private final Constant.Ratio momentum = new Constant.Ratio(0.2, "Training momentum"); - } - - //----------// - // MyVector // - //----------// - /** - * Meant to allow JAXB (un)marshalling of norms vectors. - */ - @XmlAccessorType(XmlAccessType.NONE) - @XmlRootElement(name = "vector") - private static class MyVector - { - //~ Instance fields ------------------------------------------------------------------------ - - @XmlElement(name = "value") - public double[] data; - - //~ Constructors --------------------------------------------------------------------------- - public MyVector (INDArray features) - { - int cols = features.columns(); - - data = new double[cols]; - - for (int j = 0; j < cols; j++) { - data[j] = features.getDouble(j); - } - } - - /** Meant for JAXB. */ - private MyVector () - { - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// B a s i c C l a s s i f i e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.classifier; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.glyph.ShapeSet; +import org.audiveris.omr.math.NeuralNetwork; +import org.audiveris.omr.math.PoorManAlgebra.DataSet; +import org.audiveris.omr.math.PoorManAlgebra.INDArray; +import org.audiveris.omr.math.PoorManAlgebra.Nd4j; +import org.audiveris.omr.util.Jaxb; +import org.audiveris.omr.util.StopWatch; + +//import org.nd4j.linalg.api.ndarray.INDArray; +//import org.nd4j.linalg.dataset.DataSet; +//import org.nd4j.linalg.factory.Nd4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import static java.nio.file.StandardOpenOption.CREATE; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code BasicClassifier} is the pre-DL4J classifier, based on a home-built + * shallow network operating on MixGlyphDescriptor. + * + * @author Hervé Bitteur + */ +public class BasicClassifier + extends AbstractClassifier +{ + + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(BasicClassifier.class); + + /** Classifier file name. */ + public static final String FILE_NAME = "basic-classifier.zip"; + + /** Model entry name. */ + public static final String MODEL_ENTRY_NAME = "model.xml"; + + /** The underlying (old) neural network. */ + private NeuralNetwork model; + + /** Training listener, if any. */ + private TrainingMonitor listener; + + /** + * Private constructor, to create a glyph neural network. + */ + private BasicClassifier () + { + descriptor = new MixGlyphDescriptor(); + + // Unmarshal from user or default data, if compatible + model = load(FILE_NAME); + + if (model == null) { + model = createNetwork(); + } + } + + //--------------// + // getMaxEpochs // + //--------------// + /** + * Selector on the maximum number of training epochs. + * + * @return the upper limit on epochs counter + */ + @Override + public int getMaxEpochs () + { + return constants.maxEpochs.getValue(); + } + + //--------------// + // setMaxEpochs // + //--------------// + /** + * Modify the upper limit on the number of epochs for the training process. + * + * @param maxEpochs new value for epochs limit + */ + @Override + public void setMaxEpochs (int maxEpochs) + { + model.setEpochs(maxEpochs); + constants.maxEpochs.setValue(maxEpochs); + } + + //---------// + // getName // + //---------// + @Override + public final String getName () + { + return "Basic Classifier"; + } + + //-------------// + // addListener // + //-------------// + @Override + public void addListener (TrainingMonitor listener) + { + this.listener = listener; + } + + //-----------------------// + // getNaturalEvaluations // + //-----------------------// + @Override + public Evaluation[] getNaturalEvaluations (Glyph glyph, + int interline) + { + double[] ins = descriptor.getFeatures(glyph, interline); + final INDArray features = Nd4j.create(ins); + normalize(features); + + Shape[] values = Shape.values(); + Evaluation[] evals = new Evaluation[SHAPE_COUNT]; + + for (int i = 0; i < ins.length; i++) { + ins[i] = features.getDouble(i); + } + + double[] outs = new double[SHAPE_COUNT]; + model.run(ins, null, outs); + + for (int s = 0; s < SHAPE_COUNT; s++) { + evals[s] = new Evaluation(values[s], outs[s]); + } + + return evals; + } + + //-------// + // reset // + //-------// + @Override + public void reset () + { + model = createNetwork(); + } + + //------// + // stop // + //------// + @Override + public void stop () + { + model.stop(); + } + + //-------// + // train // + //-------// + @SuppressWarnings("unchecked") + @Override + public void train (Collection samples) + { + logger.info("Training on {} samples", samples.size()); + + if (samples.isEmpty()) { + logger.warn("No sample to retrain neural classifier"); + + return; + } + + StopWatch watch = new StopWatch("train"); + watch.start("shuffle"); + + // Shuffle the collection of samples + final List newSamples = new ArrayList<>(samples); + Collections.shuffle(newSamples); + + // Build raw dataset + watch.start("getRawDataSet"); + + final DataSet dataSet = getRawDataSet(newSamples); + final INDArray features = dataSet.getFeatures(); + + // Record mean and standard deviation for every feature + watch.start("norms"); + norms = new Norms(features.mean(0), features.std(0)); + norms.stds.addi(Nd4j.scalar(Nd4j.EPS_THRESHOLD)); // Safer, to avoid later division by 0 + logger.debug("means:{}", norms.means); + logger.debug("stds:{}", norms.stds); + watch.start("normalize"); + normalize(features); + + // Convert features for NeuralNetwork data format + int rows = features.rows(); + int cols = features.columns(); + logger.info("samples: {}", rows); + logger.info("features: {}", cols); + + INDArray labels = dataSet.getLabels(); + double[][] inputs = new double[newSamples.size()][]; + double[][] desiredOutputs = new double[newSamples.size()][]; + watch.start("build input & desiredOutputs"); + + for (int ig = 0; ig < rows; ig++) { + INDArray featureRow = features.getRow(ig); + double[] ins = new double[cols]; + inputs[ig] = ins; + + for (int j = 0; j < cols; j++) { + ins[j] = featureRow.getDouble(j); + } + + INDArray labelRow = labels.getRow(ig); + double[] des = new double[SHAPE_COUNT]; + desiredOutputs[ig] = des; + + for (int j = 0; j < SHAPE_COUNT; j++) { + des[j] = labelRow.getDouble(j); + } + } + + if (constants.printWatch.isSet()) { + watch.print(); + } + + // Train + model.train(inputs, desiredOutputs, listener, listener.getIterationPeriod()); + + // Store + store(FILE_NAME); + } + + //--------------// + // isCompatible // + //--------------// + @Override + protected boolean isCompatible (NeuralNetwork model, + Norms norms) + { + if (!Arrays.equals(model.getInputLabels(), descriptor.getFeatureLabels())) { + if (logger.isDebugEnabled()) { + logger.debug("Engine inputs: {}", Arrays.toString(model.getInputLabels())); + logger.debug("Shape inputs: {}", Arrays.toString(descriptor.getFeatureLabels())); + } + + return false; + } + + if (!Arrays.equals(model.getOutputLabels(), ShapeSet.getPhysicalShapeNames())) { + if (logger.isDebugEnabled()) { + logger.debug("Engine outputs: {}", Arrays.toString(model.getOutputLabels())); + logger.debug( + "Physical shapes: {}", + Arrays.toString(ShapeSet.getPhysicalShapeNames())); + } + + return false; + } + + return true; + } + + //-----------// + // loadModel // + //-----------// + @Override + protected NeuralNetwork loadModel (Path root) + throws Exception + { + Path modelPath = root.resolve(MODEL_ENTRY_NAME); + + try (InputStream is = Files.newInputStream(modelPath)) { + return NeuralNetwork.unmarshal(is); + } + } + + //-----------// + // loadNorms // + //-----------// + /** + * {@inheritDoc}. + *

          + * Rather than binary we use XML format. + * + * @param root the root path to file system + * @return the loaded Norms instance, or exception is thrown + * @throws Exception if anything goes wrong + */ + @Override + protected Norms loadNorms (Path root) + throws Exception + { + final JAXBContext jaxbContext = JAXBContext.newInstance(MyVector.class); + final Unmarshaller um = jaxbContext.createUnmarshaller(); + + INDArray means = null; + INDArray stds = null; + + final Path meansEntry = root.resolve(MEANS_XML_ENTRY_NAME); + + if (meansEntry != null) { + try (InputStream is = Files.newInputStream(meansEntry); // READ by default + BufferedInputStream bis = new BufferedInputStream(is)) { + MyVector vector = (MyVector) um.unmarshal(bis); + means = Nd4j.create(vector.data); + logger.debug("means:{}", means); + } + } + + final Path stdsEntry = root.resolve(STDS_XML_ENTRY_NAME); + + if (stdsEntry != null) { + try (InputStream is = Files.newInputStream(stdsEntry); // READ by default + BufferedInputStream bis = new BufferedInputStream(is)) { + MyVector vector = (MyVector) um.unmarshal(bis); + stds = Nd4j.create(vector.data); + logger.debug("stds:{}", stds); + } + } + + if ((means != null) && (stds != null)) { + logger.info("Classifier loaded XML norms."); + + return new Norms(means, stds); + } + + return null; + } + + //------------// + // storeModel // + //------------// + @Override + protected void storeModel (Path root) + throws Exception + { + Path modelPath = root.resolve(MODEL_ENTRY_NAME); + + try (OutputStream bos = new BufferedOutputStream( + Files.newOutputStream(modelPath, CREATE))) { + model.marshal(bos); + bos.flush(); + } + + logger.info("Engine marshalled to {}", modelPath); + } + + //------------// + // storeNorms // + //------------// + /** + * {@inheritDoc}. + *

          + * Rather than binary, we use XML format. + * + * @throws Exception if anything goes wrong + */ + @Override + protected void storeNorms (Path root) + throws Exception + { + final JAXBContext jaxbContext = JAXBContext.newInstance(MyVector.class); + final Path means = root.resolve(MEANS_XML_ENTRY_NAME); + final Path stds = root.resolve(STDS_XML_ENTRY_NAME); + + try (OutputStream bos = new BufferedOutputStream(Files.newOutputStream(means, CREATE))) { + MyVector vector = new MyVector(norms.means); + Jaxb.marshal(vector, bos, jaxbContext); + bos.flush(); + } + + try (OutputStream bos = new BufferedOutputStream(Files.newOutputStream(stds, CREATE))) { + MyVector vector = new MyVector(norms.stds); + Jaxb.marshal(vector, bos, jaxbContext); + bos.flush(); + } + } + + //---------------// + // createNetwork // + //---------------// + private NeuralNetwork createNetwork () + { + // Get a brand new one (not trained) + logger.info("Creating a brand new {}", getName()); + + // We allocate a hidden layer with as many cells as the output layer + return new NeuralNetwork( + descriptor.length(), + SHAPE_COUNT, + SHAPE_COUNT, + constants.amplitude.getValue(), + descriptor.getFeatureLabels(), // Input labels + ShapeSet.getPhysicalShapeNames(), // Output labels + constants.learningRate.getValue(), + constants.momentum.getValue(), + getMaxEpochs()); + } + + //-----------// + // normalize // + //-----------// + /** + * Apply the known norms on the provided (raw) features. + * + * @param features raw features, to be normalized in situ + */ + private void normalize (INDArray features) + { + features.subiRowVector(norms.means); + features.diviRowVector(norms.stds); + } + + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of BasicClassifier in the application. + * + * @return the instance + */ + public static BasicClassifier getInstance () + { + return LazySingleton.INSTANCE; + } + + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { + + static final BasicClassifier INSTANCE = new BasicClassifier(); + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print out the stop watch?"); + + private final Constant.Ratio amplitude = new Constant.Ratio( + 0.5, + "Initial weight amplitude"); + + private final Constant.Ratio learningRate = new Constant.Ratio(0.1, "Learning Rate"); + + private final Constant.Integer maxEpochs = new Constant.Integer( + "Epochs", + 500, + "Maximum number of epochs in training"); + + private final Constant.Ratio momentum = new Constant.Ratio(0.2, "Training momentum"); + } + + //----------// + // MyVector // + //----------// + /** + * Meant to allow JAXB (un)marshalling of norms vectors. + */ + @XmlAccessorType(XmlAccessType.NONE) + @XmlRootElement(name = "vector") + private static class MyVector + { + + @XmlElement(name = "value") + public double[] data; + + MyVector (INDArray features) + { + int cols = features.columns(); + + data = new double[cols]; + + for (int j = 0; j < cols; j++) { + data[j] = features.getDouble(j); + } + } + + /** Meant for JAXB. */ + private MyVector () + { + } + } +} diff --git a/src/main/org/audiveris/omr/classifier/Classifier.java b/src/main/org/audiveris/omr/classifier/Classifier.java index 547d12378..2b38f8169 100644 --- a/src/main/org/audiveris/omr/classifier/Classifier.java +++ b/src/main/org/audiveris/omr/classifier/Classifier.java @@ -24,6 +24,7 @@ import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.sheet.SystemInfo; + // //import org.deeplearning4j.optimize.api.IterationListener; // @@ -39,7 +40,6 @@ */ public interface Classifier { - //~ Static fields/initializers ----------------------------------------------------------------- /** Number of shapes to differentiate. */ public static final int SHAPE_COUNT = 1 + Shape.LAST_PHYSICAL_SHAPE.ordinal(); @@ -47,19 +47,15 @@ public interface Classifier /** Empty conditions set. */ public static final EnumSet NO_CONDITIONS = EnumSet.noneOf(Condition.class); - //~ Enumerations ------------------------------------------------------------------------------- /** Optional conditions for evaluation. */ public static enum Condition { - //~ Enumeration constant initializers ------------------------------------------------------ - /** Make sure the shape is not blacklisted by the glyph at hand. */ ALLOWED, /** Make sure all specific checks are successfully passed. */ CHECKED; } - //~ Methods ------------------------------------------------------------------------------------ /** * Add a training listener * @@ -152,15 +148,14 @@ boolean isBigEnough (Glyph glyph, * @return true if not noise, false otherwise */ boolean isBigEnough (double weight); -// -// /** -// * Remove a training listener -// * -// * @param listener listener to unregister -// */ -// void removeListener (IterationListener listener); -// + // /** + // * Remove a training listener + // * + // * @param listener listener to unregister + // */ + // void removeListener (IterationListener listener); + // /** * Recreate a classifier from scratch. */ diff --git a/src/main/org/audiveris/omr/classifier/Evaluation.java b/src/main/org/audiveris/omr/classifier/Evaluation.java index a5e280017..c11d36762 100644 --- a/src/main/org/audiveris/omr/classifier/Evaluation.java +++ b/src/main/org/audiveris/omr/classifier/Evaluation.java @@ -24,6 +24,8 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.glyph.Shape; +import java.util.Comparator; + /** * Class {@code Evaluation} gathers a glyph shape, its grade and, if any, details about * its failure (name of the check that failed). @@ -31,9 +33,7 @@ * @author Hervé Bitteur */ public class Evaluation - implements Comparable { - //~ Static fields/initializers ----------------------------------------------------------------- /** Absolute confidence in shape manually assigned by the user. */ public static final double MANUAL = 3; @@ -41,7 +41,19 @@ public class Evaluation /** Confidence for in structurally assigned. */ public static final double ALGORITHM = 2; - //~ Instance fields ---------------------------------------------------------------------------- + /** + * For comparing Evaluation instances by decreasing grade. + */ + public static final Comparator byReverseGrade = new Comparator() + { + @Override + public int compare (Evaluation e1, + Evaluation e2) + { + return Double.compare(e2.grade, e1.grade); // Reverse order: highest to lowest + } + }; + /** The evaluated shape. */ public Shape shape; @@ -54,7 +66,6 @@ public class Evaluation /** The specific check that failed, if any. */ public Failure failure; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an initialized evaluation instance. * @@ -68,22 +79,6 @@ public Evaluation (Shape shape, this.grade = grade; } - //~ Methods ------------------------------------------------------------------------------------ - //-----------// - // compareTo // - //-----------// - /** - * To sort from best to worst. - * - * @param that the other evaluation instance - * @return -1,0 or +1 - */ - @Override - public int compareTo (Evaluation that) - { - return Double.compare(that.grade, this.grade); // Reverse order: highest to lowest - } - //----------// // toString // //----------// @@ -111,7 +106,6 @@ public String toString () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Failure // //---------// @@ -121,18 +115,20 @@ public String toString () */ public static class Failure { - //~ Instance fields ------------------------------------------------------------------------ /** The name of the test that failed. */ public final String test; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a {@code Failure} object. + * + * @param test the test which failed + */ public Failure (String test) { this.test = test; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { @@ -149,7 +145,6 @@ public String toString () public static class Grade extends Constant.Double { - //~ Constructors --------------------------------------------------------------------------- /** * Specific constructor, where unit & name are assigned later. diff --git a/src/main/org/audiveris/omr/classifier/GeoGlyphDescriptor.java b/src/main/org/audiveris/omr/classifier/GeoGlyphDescriptor.java index dd4e74a7b..075a61201 100644 --- a/src/main/org/audiveris/omr/classifier/GeoGlyphDescriptor.java +++ b/src/main/org/audiveris/omr/classifier/GeoGlyphDescriptor.java @@ -32,7 +32,6 @@ public class GeoGlyphDescriptor extends GlyphDescriptor { - //~ Static fields/initializers ----------------------------------------------------------------- /** Number of geometric moments used. */ public static final int MOMENT_COUNT = 10; @@ -40,7 +39,6 @@ public class GeoGlyphDescriptor /** Use the 10 first geometric moments + aspect. */ private static final int LENGTH = MOMENT_COUNT + 1; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code GeoGlyphDescriptor} object. */ @@ -49,7 +47,6 @@ public GeoGlyphDescriptor () super("geo"); } - //~ Methods ------------------------------------------------------------------------------------ @Override public String[] getFeatureLabels () { @@ -65,9 +62,7 @@ public double[] getFeatures (Glyph glyph, // We take all the first moments double[] k = glyph.getGeometricMoments(interline).getValues(); - for (int i = 0; i < MOMENT_COUNT; i++) { - ins[i] = k[i]; - } + System.arraycopy(k, 0, ins, 0, MOMENT_COUNT); // We append aspect int i = MOMENT_COUNT; @@ -82,18 +77,16 @@ public int length () return LENGTH; } - //~ Inner Classes ------------------------------------------------------------------------------ //--------------// // LabelsHolder // //--------------// /** * Descriptive strings for glyph characteristics. - * + *

          * NOTA: Keep in sync method {@link #getFeatures} */ private static class LabelsHolder { - //~ Static fields/initializers ------------------------------------------------------------- /** Index -> Label */ public static final String[] labels = new String[LENGTH]; @@ -109,7 +102,6 @@ private static class LabelsHolder /* 10 */ labels[i++] = "aspect"; } - //~ Constructors --------------------------------------------------------------------------- private LabelsHolder () { } diff --git a/src/main/org/audiveris/omr/classifier/GlyphDescriptor.java b/src/main/org/audiveris/omr/classifier/GlyphDescriptor.java index 93bed1634..1c1ad084a 100644 --- a/src/main/org/audiveris/omr/classifier/GlyphDescriptor.java +++ b/src/main/org/audiveris/omr/classifier/GlyphDescriptor.java @@ -1,145 +1,139 @@ -//------------------------------------------------------------------------------------------------// -// // -// G l y p h D e s c r i p t o r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.classifier; - -import org.audiveris.omr.WellKnowns; -import org.audiveris.omr.glyph.Glyph; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedWriter; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.nio.file.Path; -import java.util.Collection; - -/** - * Class {@code GlyphDescriptor} provides glyph features for shape classifiers. - * - * @author Hervé Bitteur - */ -public abstract class GlyphDescriptor -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(GlyphDescriptor.class); - - //~ Instance fields ---------------------------------------------------------------------------- - /** Descriptor name. */ - private final String name; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code GlyphDescriptor} object. - * - * @param name distinctive descriptor name - */ - public GlyphDescriptor (String name) - { - this.name = name; - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Export the provided collection of samples to a file (using CSV format). - * - * @param radix file name radix - * @param samples the samples to export - * @param withNorms should samples norms be exported as well? (case of a training set) - */ - public void export (String radix, - Collection samples, - boolean withNorms) - { - try { - final String ext = "." + name + ".csv"; - final Path path = WellKnowns.TRAIN_FOLDER.resolve(radix + ext); - final PrintWriter out = new PrintWriter( - new BufferedWriter( - new OutputStreamWriter( - new FileOutputStream(path.toFile()), - WellKnowns.FILE_ENCODING))); - - for (Sample sample : samples) { - for (double in : getFeatures(sample, sample.getInterline())) { - out.print(in); - out.print(","); - - // - // if (pop != null) { - // pop.includeValue(in); - // } - } - - ///out.println(sample.getShape().getPhysicalShape()); // Shape name - out.println(sample.getShape().getPhysicalShape().ordinal()); // Shape ordinal - } - - // - // if (withNorms) { - // logger.info("Img {}", pop); - // } - // - out.flush(); - out.close(); - logger.info("{} {} samples saved in {}", samples.size(), radix, path.toAbsolutePath()); - } catch (Exception ex) { - logger.warn("Could not save {} samples " + ex, radix, ex); - } - } - - /** - * Report the features labels. - * - * @return the array of feature labels, or null if none - */ - public abstract String[] getFeatureLabels (); - - /** - * Gather the various features meant to describe a glyph or a shape sample. - * - * @param glyph the glyph (or sample) to describe - * @param interline the related staff interline - * @return the glyph features, an array of size length() - */ - public abstract double[] getFeatures (Glyph glyph, - int interline); - - /** - * Report a name for this descriptor - * - * @return a typical name - */ - public String getName () - { - return name; - } - - /** - * Report the number of features provided. - * - * @return the number of features - */ - public abstract int length (); -} +//------------------------------------------------------------------------------------------------// +// // +// G l y p h D e s c r i p t o r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.classifier; + +import org.audiveris.omr.WellKnowns; +import org.audiveris.omr.glyph.Glyph; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.file.Path; +import java.util.Collection; + +/** + * Class {@code GlyphDescriptor} provides glyph features for shape classifiers. + * + * @author Hervé Bitteur + */ +public abstract class GlyphDescriptor +{ + + private static final Logger logger = LoggerFactory.getLogger(GlyphDescriptor.class); + + /** Descriptor name. */ + private final String name; + + /** + * Creates a new {@code GlyphDescriptor} object. + * + * @param name distinctive descriptor name + */ + public GlyphDescriptor (String name) + { + this.name = name; + } + + /** + * Export the provided collection of samples to a file (using CSV format). + * + * @param radix file name radix + * @param samples the samples to export + * @param withNorms should samples norms be exported as well? (case of a training set) + */ + public void export (String radix, + Collection samples, + boolean withNorms) + { + final String ext = "." + name + ".csv"; + final Path path = WellKnowns.TRAIN_FOLDER.resolve(radix + ext); + + try (FileOutputStream fos = new FileOutputStream(path.toFile()); + OutputStreamWriter osw = new OutputStreamWriter(fos, WellKnowns.FILE_ENCODING); + PrintWriter out = new PrintWriter(new BufferedWriter(osw))) { + for (Sample sample : samples) { + for (double in : getFeatures(sample, sample.getInterline())) { + out.print(in); + out.print(","); + + // + // if (pop != null) { + // pop.includeValue(in); + // } + } + + ///out.println(sample.getShape().getPhysicalShape()); // Shape name + out.println(sample.getShape().getPhysicalShape().ordinal()); // Shape ordinal + } + + // + // if (withNorms) { + // logger.info("Img {}", pop); + // } + // + out.flush(); + + logger.info("{} {} samples saved in {}", samples.size(), radix, path.toAbsolutePath()); + } catch (IOException ex) { + logger.warn("Could not save {} samples " + ex, radix, ex); + } + } + + /** + * Report the features labels. + * + * @return the array of feature labels, or null if none + */ + public abstract String[] getFeatureLabels (); + + /** + * Gather the various features meant to describe a glyph or a shape sample. + * + * @param glyph the glyph (or sample) to describe + * @param interline the related staff interline + * @return the glyph features, an array of size length() + */ + public abstract double[] getFeatures (Glyph glyph, + int interline); + + /** + * Report a name for this descriptor + * + * @return a typical name + */ + public String getName () + { + return name; + } + + /** + * Report the number of features provided. + * + * @return the number of features + */ + public abstract int length (); +} diff --git a/src/main/org/audiveris/omr/classifier/ImgGlyphDescriptor.java b/src/main/org/audiveris/omr/classifier/ImgGlyphDescriptor.java index 3f4a27e02..0708019ff 100644 --- a/src/main/org/audiveris/omr/classifier/ImgGlyphDescriptor.java +++ b/src/main/org/audiveris/omr/classifier/ImgGlyphDescriptor.java @@ -1,85 +1,85 @@ -//------------------------------------------------------------------------------------------------// -// // -// S h a p e D e s c r i p t o r P i x e l s // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.classifier; - -import ij.process.ByteProcessor; - -import org.audiveris.omr.glyph.Glyph; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Class {@code ImgGlyphDescriptor} is a glyph descriptor based directly on the pixels - * of glyph properly scaled image. - * - * @author Hervé Bitteur - */ -public class ImgGlyphDescriptor - extends GlyphDescriptor -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(ImgGlyphDescriptor.class); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code ImgGlyphDescriptor} object. - */ - public ImgGlyphDescriptor () - { - super(ScaledBuffer.HEIGHT + "x" + ScaledBuffer.WIDTH); - } - - //~ Methods ------------------------------------------------------------------------------------ - @Override - public String[] getFeatureLabels () - { - return null; - } - - @Override - public double[] getFeatures (Glyph glyph, - int interline) - { - final ByteProcessor buffer = ScaledBuffer.getBuffer(glyph, interline); - buffer.invert(); // 0 for background, 255 for foreground - - // Layout: row by row - final double[] doubles = new double[length()]; - int i = 0; - - for (int y = 0; y < ScaledBuffer.HEIGHT; y++) { - for (int x = 0; x < ScaledBuffer.WIDTH; x++) { - doubles[i++] = buffer.get(x, y); - } - } - - return doubles; - } - - @Override - public int length () - { - return ScaledBuffer.HEIGHT * ScaledBuffer.WIDTH; - } -} +//------------------------------------------------------------------------------------------------// +// // +// S h a p e D e s c r i p t o r P i x e l s // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.classifier; + +import ij.process.ByteProcessor; + +import org.audiveris.omr.glyph.Glyph; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class {@code ImgGlyphDescriptor} is a glyph descriptor based directly on the pixels + * of glyph properly scaled image. + * + * @author Hervé Bitteur + */ +public class ImgGlyphDescriptor + extends GlyphDescriptor +{ + + private static final Logger logger = LoggerFactory.getLogger(ImgGlyphDescriptor.class); + + /** Empty labels array. */ + private static final String[] NO_LABELS = new String[0]; + + /** + * Creates a new {@code ImgGlyphDescriptor} object. + */ + public ImgGlyphDescriptor () + { + super(ScaledBuffer.HEIGHT + "x" + ScaledBuffer.WIDTH); + } + + @Override + public String[] getFeatureLabels () + { + return NO_LABELS; + } + + @Override + public double[] getFeatures (Glyph glyph, + int interline) + { + final ByteProcessor buffer = ScaledBuffer.getBuffer(glyph, interline); + buffer.invert(); // 0 for background, 255 for foreground + + // Layout: row by row + final double[] doubles = new double[length()]; + int i = 0; + + for (int y = 0; y < ScaledBuffer.HEIGHT; y++) { + for (int x = 0; x < ScaledBuffer.WIDTH; x++) { + doubles[i++] = buffer.get(x, y); + } + } + + return doubles; + } + + @Override + public int length () + { + return ScaledBuffer.HEIGHT * ScaledBuffer.WIDTH; + } +} diff --git a/src/main/org/audiveris/omr/classifier/MixGlyphDescriptor.java b/src/main/org/audiveris/omr/classifier/MixGlyphDescriptor.java index e55f0790b..d44e6cad1 100644 --- a/src/main/org/audiveris/omr/classifier/MixGlyphDescriptor.java +++ b/src/main/org/audiveris/omr/classifier/MixGlyphDescriptor.java @@ -37,7 +37,6 @@ public class MixGlyphDescriptor extends GlyphDescriptor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(MixGlyphDescriptor.class); @@ -49,12 +48,14 @@ public class MixGlyphDescriptor /** Use the ART moments + GEO moments + aspect. */ private static final int LENGTH = artCount + geoCount + 1; + /** + * Creates a new {@code MixGlyphDescriptor} object. + */ public MixGlyphDescriptor () { super("mix"); } - //~ Methods ------------------------------------------------------------------------------------ @Override public String[] getFeatureLabels () { @@ -98,18 +99,16 @@ public int length () return LENGTH; } - //~ Inner Classes ------------------------------------------------------------------------------ //--------------// // LabelsHolder // //--------------// /** * Descriptive strings for glyph characteristics. - * + *

          * NOTA: Keep in sync method {@link #getFeatures} */ private static class LabelsHolder { - //~ Static fields/initializers ------------------------------------------------------------- /** Index -> Label */ public static final String[] labels = new String[LENGTH]; @@ -135,7 +134,6 @@ private static class LabelsHolder labels[i++] = "aspect"; } - //~ Constructors --------------------------------------------------------------------------- private LabelsHolder () { } diff --git a/src/main/org/audiveris/omr/classifier/OmrShapeMapping.java b/src/main/org/audiveris/omr/classifier/OmrShapeMapping.java index 4729ffc0c..fe45664ad 100644 --- a/src/main/org/audiveris/omr/classifier/OmrShapeMapping.java +++ b/src/main/org/audiveris/omr/classifier/OmrShapeMapping.java @@ -1,274 +1,282 @@ -//------------------------------------------------------------------------------------------------// -// // -// O m r S h a p e M a p p i n g // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.classifier; - -import java.util.EnumMap; -import java.util.Map; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.sig.inter.TimePairInter; -import org.audiveris.omrdataset.api.OmrShape; -import org.audiveris.omrdataset.api.OmrShapes; - -import static org.audiveris.omrdataset.api.OmrShapes.COMBO_MAP; - -/** - * Class {@code OmrShapeMapping} handles mappings between Shape and OmrShape. - * - * @author Hervé Bitteur - */ -public abstract class OmrShapeMapping -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - public static final Map SHAPE_TO_OMRSHAPE = buildShapeMap(); - - public static final Map OMRSHAPE_TO_SHAPE = buildOmrShapeMap(); - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Report the mapped OmrShape, if any, for a given TimePairInter. - * - * @param timePair the provided TimePairInter instance - * @return the corresponding OmrShape or null - */ - public static OmrShape getTimeCombo (TimePairInter timePair) - { - int num = timePair.getNum().getValue(); - int den = timePair.getDen().getValue(); - - for (Map.Entry entry : COMBO_MAP.entrySet()) { - OmrShapes.NumDen nd = entry.getValue(); - - if ((nd.num == num) && (nd.den == den)) { - return entry.getKey(); - } - } - - return null; - } - - /** - * Build the map (OmrShape -> Shape) as the reverse of SHAPE_TO_OMRSHAPE. - * - * @return the initialized map - */ - private static Map buildOmrShapeMap () - { - final Map map = new EnumMap(OmrShape.class); - - for (Map.Entry entry : SHAPE_TO_OMRSHAPE.entrySet()) { - final Shape shape = entry.getKey(); - final OmrShape omrShape = entry.getValue(); - - if (omrShape != null) { - map.put(omrShape, shape); - } - } - - return map; - } - - /** - * Build the map (Shape -> OmrShape). - * - * @return the initialized map - */ - private static Map buildShapeMap () - { - final Map map = new EnumMap(Shape.class); - - map.put(Shape.DAL_SEGNO, OmrShape.dalSegno); - map.put(Shape.DA_CAPO, OmrShape.daCapo); - map.put(Shape.SEGNO, OmrShape.segno); - map.put(Shape.CODA, OmrShape.coda); - map.put(Shape.BREATH_MARK, OmrShape.breathMarkComma); - map.put(Shape.CAESURA, OmrShape.caesura); - map.put(Shape.G_CLEF, OmrShape.gClef); - map.put(Shape.G_CLEF_SMALL, OmrShape.gClefChange); - map.put(Shape.G_CLEF_8VA, OmrShape.gClef8va); - map.put(Shape.G_CLEF_8VB, OmrShape.gClef8vb); - map.put(Shape.C_CLEF, OmrShape.cClef); - map.put(Shape.F_CLEF, OmrShape.fClef); - map.put(Shape.F_CLEF_SMALL, OmrShape.fClefChange); - map.put(Shape.F_CLEF_8VA, OmrShape.fClef8va); - map.put(Shape.F_CLEF_8VB, OmrShape.fClef8vb); - map.put(Shape.PERCUSSION_CLEF, OmrShape.unpitchedPercussionClef1); - map.put(Shape.FLAT, OmrShape.accidentalFlat); // If not in key - map.put(Shape.NATURAL, OmrShape.accidentalNatural); // If not in key - map.put(Shape.SHARP, OmrShape.accidentalSharp); // If not in key - map.put(Shape.DOUBLE_SHARP, OmrShape.accidentalDoubleSharp); - map.put(Shape.DOUBLE_FLAT, OmrShape.accidentalDoubleFlat); - map.put(Shape.TIME_ZERO, OmrShape.timeSig0); - map.put(Shape.TIME_ONE, OmrShape.timeSig1); - map.put(Shape.TIME_TWO, OmrShape.timeSig2); - map.put(Shape.TIME_THREE, OmrShape.timeSig3); - map.put(Shape.TIME_FOUR, OmrShape.timeSig4); - map.put(Shape.TIME_FIVE, OmrShape.timeSig5); - map.put(Shape.TIME_SIX, OmrShape.timeSig6); - map.put(Shape.TIME_SEVEN, OmrShape.timeSig7); - map.put(Shape.TIME_EIGHT, OmrShape.timeSig8); - map.put(Shape.TIME_NINE, OmrShape.timeSig9); - map.put(Shape.TIME_TWELVE, OmrShape.timeSig12); - map.put(Shape.TIME_SIXTEEN, OmrShape.timeSig16); - map.put(Shape.COMMON_TIME, OmrShape.timeSigCommon); - map.put(Shape.CUT_TIME, OmrShape.timeSigCutCommon); - map.put(Shape.TIME_FOUR_FOUR, OmrShape.timeSig4over4); - map.put(Shape.TIME_TWO_TWO, OmrShape.timeSig2over2); - map.put(Shape.TIME_TWO_FOUR, OmrShape.timeSig2over4); - map.put(Shape.TIME_THREE_FOUR, OmrShape.timeSig3over4); - map.put(Shape.TIME_FIVE_FOUR, OmrShape.timeSig5over4); - map.put(Shape.TIME_THREE_EIGHT, OmrShape.timeSig3over8); - map.put(Shape.TIME_SIX_EIGHT, OmrShape.timeSig6over8); - map.put(Shape.OTTAVA_ALTA, OmrShape.ottavaAlta); - map.put(Shape.OTTAVA_BASSA, OmrShape.ottavaBassaVb); - map.put(Shape.LONG_REST, OmrShape.restLonga); - map.put(Shape.BREVE_REST, OmrShape.restDoubleWhole); - map.put(Shape.QUARTER_REST, OmrShape.restQuarter); - map.put(Shape.EIGHTH_REST, OmrShape.rest8th); - map.put(Shape.ONE_16TH_REST, OmrShape.rest16th); - map.put(Shape.ONE_32ND_REST, OmrShape.rest32nd); - map.put(Shape.ONE_64TH_REST, OmrShape.rest64th); - map.put(Shape.ONE_128TH_REST, OmrShape.rest128th); - map.put(Shape.FLAG_1, OmrShape.flag8thUp); - map.put(Shape.FLAG_1_UP, OmrShape.flag8thDown); - map.put(Shape.FLAG_2, OmrShape.flag16thUp); - map.put(Shape.FLAG_2_UP, OmrShape.flag16thDown); - map.put(Shape.FLAG_3, OmrShape.flag32ndUp); - map.put(Shape.FLAG_3_UP, OmrShape.flag32ndDown); - map.put(Shape.FLAG_4, OmrShape.flag64thUp); - map.put(Shape.FLAG_4_UP, OmrShape.flag64thDown); - map.put(Shape.FLAG_5, OmrShape.flag128thUp); - map.put(Shape.FLAG_5_UP, OmrShape.flag128thDown); - map.put(Shape.SMALL_FLAG, OmrShape.flag8thUpSmall); - // map.put(Shape.SMALL_FLAG_SLASH, OmrShape.none); - map.put(Shape.BREVE, OmrShape.noteheadDoubleWhole); - // map.put(Shape.ACCENT, OmrShape.none); // articAccentAbove or articAccentBelow - // map.put(Shape.TENUTO, OmrShape.none); // articTenutoAbove or articTenutoBelow - // map.put(Shape.STACCATISSIMO, OmrShape.none); // articStaccatissimoAbove or articStaccatissimoBelow - // map.put(Shape.STRONG_ACCENT, OmrShape.none); // articMarcatoAbove or articMarcatoBelow - // map.put(Shape.STACCATO, OmrShape.none); // articStaccatoAbove or articStaccatoBelow - map.put(Shape.ARPEGGIATO, OmrShape.arpeggiato); - map.put(Shape.DYNAMICS_P, OmrShape.dynamicPiano); - map.put(Shape.DYNAMICS_PP, OmrShape.dynamicPP); - map.put(Shape.DYNAMICS_MP, OmrShape.dynamicMP); - map.put(Shape.DYNAMICS_F, OmrShape.dynamicForte); - map.put(Shape.DYNAMICS_FF, OmrShape.dynamicFF); - map.put(Shape.DYNAMICS_MF, OmrShape.dynamicMF); - map.put(Shape.DYNAMICS_FP, OmrShape.dynamicFortePiano); - map.put(Shape.DYNAMICS_SF, OmrShape.dynamicSforzando1); - map.put(Shape.DYNAMICS_SFZ, OmrShape.dynamicSforzato); - map.put(Shape.TR, OmrShape.ornamentTrill); - map.put(Shape.TURN, OmrShape.ornamentTurn); - map.put(Shape.TURN_INVERTED, OmrShape.ornamentTurnInverted); - map.put(Shape.TURN_UP, OmrShape.ornamentTurnUp); - map.put(Shape.TURN_SLASH, OmrShape.ornamentTurnSlash); - map.put(Shape.MORDENT, OmrShape.ornamentMordent); - map.put(Shape.MORDENT_INVERTED, OmrShape.ornamentMordentInverted); - map.put(Shape.TUPLET_THREE, OmrShape.tuplet3); - map.put(Shape.TUPLET_SIX, OmrShape.tuplet6); - map.put(Shape.PEDAL_MARK, OmrShape.keyboardPedalPed); - map.put(Shape.PEDAL_UP_MARK, OmrShape.keyboardPedalUp); - map.put(Shape.DIGIT_0, OmrShape.fingering0); - map.put(Shape.DIGIT_1, OmrShape.fingering1); - map.put(Shape.DIGIT_2, OmrShape.fingering2); - map.put(Shape.DIGIT_3, OmrShape.fingering3); - map.put(Shape.DIGIT_4, OmrShape.fingering4); - map.put(Shape.DIGIT_5, OmrShape.fingering5); - // map.put(Shape.ROMAN_I, OmrShape.none); - // map.put(Shape.ROMAN_II, OmrShape.none); - // map.put(Shape.ROMAN_III, OmrShape.none); - // map.put(Shape.ROMAN_IV, OmrShape.none); - // map.put(Shape.ROMAN_V, OmrShape.none); - // map.put(Shape.ROMAN_VI, OmrShape.none); - // map.put(Shape.ROMAN_VII, OmrShape.none); - // map.put(Shape.ROMAN_VIII, OmrShape.none); - // map.put(Shape.ROMAN_IX, OmrShape.none); - // map.put(Shape.ROMAN_X, OmrShape.none); - // map.put(Shape.ROMAN_XI, OmrShape.none); - // map.put(Shape.ROMAN_XII, OmrShape.none); - map.put(Shape.PLUCK_P, OmrShape.fingeringPLower); - map.put(Shape.PLUCK_I, OmrShape.fingeringILower); - map.put(Shape.PLUCK_M, OmrShape.fingeringMLower); - map.put(Shape.PLUCK_A, OmrShape.fingeringALower); - map.put(Shape.CLUTTER, OmrShape.none); - // map.put(Shape.TEXT, OmrShape.none); - // map.put(Shape.CHARACTER, OmrShape.none); - map.put(Shape.REPEAT_DOT, OmrShape.repeatDot); - map.put(Shape.AUGMENTATION_DOT, OmrShape.augmentationDot); - map.put(Shape.WHOLE_REST, OmrShape.restWhole); - map.put(Shape.HALF_REST, OmrShape.restHalf); - map.put(Shape.NOTEHEAD_BLACK, OmrShape.noteheadBlack); - map.put(Shape.NOTEHEAD_BLACK_SMALL, OmrShape.noteheadBlackSmall); - map.put(Shape.NOTEHEAD_VOID, OmrShape.noteheadHalf); - map.put(Shape.NOTEHEAD_VOID_SMALL, OmrShape.noteheadHalfSmall); - map.put(Shape.WHOLE_NOTE, OmrShape.noteheadWhole); - map.put(Shape.WHOLE_NOTE_SMALL, OmrShape.noteheadWholeSmall); - // map.put(Shape.BEAM, OmrShape.none); - // map.put(Shape.BEAM_SMALL, OmrShape.none); - // map.put(Shape.BEAM_HOOK, OmrShape.none); - // map.put(Shape.BEAM_HOOK_SMALL, OmrShape.none); - // map.put(Shape.SLUR, OmrShape.none); - // map.put(Shape.KEY_FLAT_7, OmrShape.none); - // map.put(Shape.KEY_FLAT_6, OmrShape.none); - // map.put(Shape.KEY_FLAT_5, OmrShape.none); - // map.put(Shape.KEY_FLAT_4, OmrShape.none); - // map.put(Shape.KEY_FLAT_3, OmrShape.none); - // map.put(Shape.KEY_FLAT_2, OmrShape.none); - // map.put(Shape.KEY_SHARP_2, OmrShape.none); - // map.put(Shape.KEY_SHARP_3, OmrShape.none); - // map.put(Shape.KEY_SHARP_4, OmrShape.none); - // map.put(Shape.KEY_SHARP_5, OmrShape.none); - // map.put(Shape.KEY_SHARP_6, OmrShape.none); - // map.put(Shape.KEY_SHARP_7, OmrShape.none); - map.put(Shape.THIN_BARLINE, OmrShape.barlineSingle); - // map.put(Shape.THIN_CONNECTOR, OmrShape.none); - map.put(Shape.THICK_BARLINE, OmrShape.barlineHeavy); - // map.put(Shape.THICK_CONNECTOR, OmrShape.none); - // map.put(Shape.BRACKET_CONNECTOR, OmrShape.none); - map.put(Shape.DOUBLE_BARLINE, OmrShape.barlineDouble); - map.put(Shape.FINAL_BARLINE, OmrShape.barlineFinal); - map.put(Shape.REVERSE_FINAL_BARLINE, OmrShape.barlineReverseFinal); - map.put(Shape.LEFT_REPEAT_SIGN, OmrShape.repeatLeft); - map.put(Shape.RIGHT_REPEAT_SIGN, OmrShape.repeatRight); - map.put(Shape.BACK_TO_BACK_REPEAT_SIGN, OmrShape.repeatRightLeft); - // map.put(Shape.ENDING, OmrShape.none); - // map.put(Shape.CRESCENDO, OmrShape.none); - // map.put(Shape.DIMINUENDO, OmrShape.none); - map.put(Shape.BRACE, OmrShape.brace); - // map.put(Shape.BRACKET, OmrShape.none); - map.put(Shape.REPEAT_DOT_PAIR, OmrShape.repeatDots); - //map.put(Shape.NOISE, OmrShape.none); - map.put(Shape.LEDGER, OmrShape.legerLine); - // map.put(Shape.ENDING_HORIZONTAL, OmrShape.none); - // map.put(Shape.ENDING_VERTICAL, OmrShape.none); - // map.put(Shape.SEGMENT, OmrShape.none); - map.put(Shape.STEM, OmrShape.stem); - map.put(Shape.KEY_FLAT_1, OmrShape.keyFlat); - map.put(Shape.KEY_SHARP_1, OmrShape.keySharp); - map.put(Shape.GRACE_NOTE_SLASH, OmrShape.graceNoteAcciaccaturaStemUp); - map.put(Shape.GRACE_NOTE, OmrShape.graceNoteAppoggiaturaStemUp); - map.put(Shape.FERMATA, OmrShape.fermataAbove); - map.put(Shape.FERMATA_BELOW, OmrShape.fermataBelow); - - return map; - } -} +//------------------------------------------------------------------------------------------------// +// // +// O m r S h a p e M a p p i n g // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.classifier; + +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.sig.inter.TimePairInter; +import org.audiveris.omrdataset.api.OmrShape; +import org.audiveris.omrdataset.api.OmrShapes; +import static org.audiveris.omrdataset.api.OmrShapes.COMBO_MAP; + +import java.util.EnumMap; +import java.util.Map; + +/** + * Class {@code OmrShapeMapping} handles mappings between Shape and OmrShape. + * + * @author Hervé Bitteur + */ +public abstract class OmrShapeMapping +{ + + /** + * Map from {@link Shape} to {@link OmrShape}. + */ + public static final Map SHAPE_TO_OMRSHAPE = buildShapeMap(); + + /** + * Map from {@link OmrShape} to {@link Shape}. + */ + public static final Map OMRSHAPE_TO_SHAPE = buildOmrShapeMap(); + + /** + * Report the mapped OmrShape, if any, for a given TimePairInter. + * + * @param timePair the provided TimePairInter instance + * @return the corresponding OmrShape or null + */ + public static OmrShape getTimeCombo (TimePairInter timePair) + { + int num = timePair.getNum().getValue(); + int den = timePair.getDen().getValue(); + + for (Map.Entry entry : COMBO_MAP.entrySet()) { + OmrShapes.NumDen nd = entry.getValue(); + + if ((nd.num == num) && (nd.den == den)) { + return entry.getKey(); + } + } + + return null; + } + + /** + * Build the map (OmrShape -> Shape) as the reverse of SHAPE_TO_OMRSHAPE. + * + * @return the initialized map + */ + private static Map buildOmrShapeMap () + { + final Map map = new EnumMap<>(OmrShape.class); + + for (Map.Entry entry : SHAPE_TO_OMRSHAPE.entrySet()) { + final Shape shape = entry.getKey(); + final OmrShape omrShape = entry.getValue(); + + if (omrShape != null) { + map.put(omrShape, shape); + } + } + + return map; + } + + /** + * Build the map (Shape -> OmrShape). + * + * @return the initialized map + */ + private static Map buildShapeMap () + { + final Map map = new EnumMap<>(Shape.class); + + map.put(Shape.DAL_SEGNO, OmrShape.dalSegno); + map.put(Shape.DA_CAPO, OmrShape.daCapo); + map.put(Shape.SEGNO, OmrShape.segno); + map.put(Shape.CODA, OmrShape.coda); + map.put(Shape.BREATH_MARK, OmrShape.breathMarkComma); + map.put(Shape.CAESURA, OmrShape.caesura); + map.put(Shape.G_CLEF, OmrShape.gClef); + map.put(Shape.G_CLEF_SMALL, OmrShape.gClefChange); + map.put(Shape.G_CLEF_8VA, OmrShape.gClef8va); + map.put(Shape.G_CLEF_8VB, OmrShape.gClef8vb); + map.put(Shape.C_CLEF, OmrShape.cClef); + map.put(Shape.F_CLEF, OmrShape.fClef); + map.put(Shape.F_CLEF_SMALL, OmrShape.fClefChange); + map.put(Shape.F_CLEF_8VA, OmrShape.fClef8va); + map.put(Shape.F_CLEF_8VB, OmrShape.fClef8vb); + map.put(Shape.PERCUSSION_CLEF, OmrShape.unpitchedPercussionClef1); + map.put(Shape.FLAT, OmrShape.accidentalFlat); // If not in key + map.put(Shape.NATURAL, OmrShape.accidentalNatural); // If not in key + map.put(Shape.SHARP, OmrShape.accidentalSharp); // If not in key + map.put(Shape.DOUBLE_SHARP, OmrShape.accidentalDoubleSharp); + map.put(Shape.DOUBLE_FLAT, OmrShape.accidentalDoubleFlat); + map.put(Shape.TIME_ZERO, OmrShape.timeSig0); + map.put(Shape.TIME_ONE, OmrShape.timeSig1); + map.put(Shape.TIME_TWO, OmrShape.timeSig2); + map.put(Shape.TIME_THREE, OmrShape.timeSig3); + map.put(Shape.TIME_FOUR, OmrShape.timeSig4); + map.put(Shape.TIME_FIVE, OmrShape.timeSig5); + map.put(Shape.TIME_SIX, OmrShape.timeSig6); + map.put(Shape.TIME_SEVEN, OmrShape.timeSig7); + map.put(Shape.TIME_EIGHT, OmrShape.timeSig8); + map.put(Shape.TIME_NINE, OmrShape.timeSig9); + map.put(Shape.TIME_TWELVE, OmrShape.timeSig12); + map.put(Shape.TIME_SIXTEEN, OmrShape.timeSig16); + map.put(Shape.COMMON_TIME, OmrShape.timeSigCommon); + map.put(Shape.CUT_TIME, OmrShape.timeSigCutCommon); + map.put(Shape.TIME_FOUR_FOUR, OmrShape.timeSig4over4); + map.put(Shape.TIME_TWO_TWO, OmrShape.timeSig2over2); + map.put(Shape.TIME_TWO_FOUR, OmrShape.timeSig2over4); + map.put(Shape.TIME_THREE_FOUR, OmrShape.timeSig3over4); + map.put(Shape.TIME_FIVE_FOUR, OmrShape.timeSig5over4); + map.put(Shape.TIME_THREE_EIGHT, OmrShape.timeSig3over8); + map.put(Shape.TIME_SIX_EIGHT, OmrShape.timeSig6over8); + map.put(Shape.OTTAVA_ALTA, OmrShape.ottavaAlta); + map.put(Shape.OTTAVA_BASSA, OmrShape.ottavaBassaVb); + map.put(Shape.LONG_REST, OmrShape.restLonga); + map.put(Shape.BREVE_REST, OmrShape.restDoubleWhole); + map.put(Shape.QUARTER_REST, OmrShape.restQuarter); + map.put(Shape.EIGHTH_REST, OmrShape.rest8th); + map.put(Shape.ONE_16TH_REST, OmrShape.rest16th); + map.put(Shape.ONE_32ND_REST, OmrShape.rest32nd); + map.put(Shape.ONE_64TH_REST, OmrShape.rest64th); + map.put(Shape.ONE_128TH_REST, OmrShape.rest128th); + map.put(Shape.FLAG_1, OmrShape.flag8thUp); + map.put(Shape.FLAG_1_UP, OmrShape.flag8thDown); + map.put(Shape.FLAG_2, OmrShape.flag16thUp); + map.put(Shape.FLAG_2_UP, OmrShape.flag16thDown); + map.put(Shape.FLAG_3, OmrShape.flag32ndUp); + map.put(Shape.FLAG_3_UP, OmrShape.flag32ndDown); + map.put(Shape.FLAG_4, OmrShape.flag64thUp); + map.put(Shape.FLAG_4_UP, OmrShape.flag64thDown); + map.put(Shape.FLAG_5, OmrShape.flag128thUp); + map.put(Shape.FLAG_5_UP, OmrShape.flag128thDown); + map.put(Shape.SMALL_FLAG, OmrShape.flag8thUpSmall); + // map.put(Shape.SMALL_FLAG_SLASH, OmrShape.none); + map.put(Shape.BREVE, OmrShape.noteheadDoubleWhole); + // map.put(Shape.ACCENT, OmrShape.none); // articAccentAbove or articAccentBelow + // map.put(Shape.TENUTO, OmrShape.none); // articTenutoAbove or articTenutoBelow + // map.put(Shape.STACCATISSIMO, OmrShape.none); // articStaccatissimoAbove or articStaccatissimoBelow + // map.put(Shape.STRONG_ACCENT, OmrShape.none); // articMarcatoAbove or articMarcatoBelow + // map.put(Shape.STACCATO, OmrShape.none); // articStaccatoAbove or articStaccatoBelow + map.put(Shape.ARPEGGIATO, OmrShape.arpeggiato); + map.put(Shape.DYNAMICS_P, OmrShape.dynamicPiano); + map.put(Shape.DYNAMICS_PP, OmrShape.dynamicPP); + map.put(Shape.DYNAMICS_MP, OmrShape.dynamicMP); + map.put(Shape.DYNAMICS_F, OmrShape.dynamicForte); + map.put(Shape.DYNAMICS_FF, OmrShape.dynamicFF); + map.put(Shape.DYNAMICS_MF, OmrShape.dynamicMF); + map.put(Shape.DYNAMICS_FP, OmrShape.dynamicFortePiano); + map.put(Shape.DYNAMICS_SF, OmrShape.dynamicSforzando1); + map.put(Shape.DYNAMICS_SFZ, OmrShape.dynamicSforzato); + map.put(Shape.TR, OmrShape.ornamentTrill); + map.put(Shape.TURN, OmrShape.ornamentTurn); + map.put(Shape.TURN_INVERTED, OmrShape.ornamentTurnInverted); + map.put(Shape.TURN_UP, OmrShape.ornamentTurnUp); + map.put(Shape.TURN_SLASH, OmrShape.ornamentTurnSlash); + map.put(Shape.MORDENT, OmrShape.ornamentMordent); + map.put(Shape.MORDENT_INVERTED, OmrShape.ornamentMordentInverted); + map.put(Shape.TUPLET_THREE, OmrShape.tuplet3); + map.put(Shape.TUPLET_SIX, OmrShape.tuplet6); + map.put(Shape.PEDAL_MARK, OmrShape.keyboardPedalPed); + map.put(Shape.PEDAL_UP_MARK, OmrShape.keyboardPedalUp); + map.put(Shape.DIGIT_0, OmrShape.fingering0); + map.put(Shape.DIGIT_1, OmrShape.fingering1); + map.put(Shape.DIGIT_2, OmrShape.fingering2); + map.put(Shape.DIGIT_3, OmrShape.fingering3); + map.put(Shape.DIGIT_4, OmrShape.fingering4); + map.put(Shape.DIGIT_5, OmrShape.fingering5); + // map.put(Shape.ROMAN_I, OmrShape.none); + // map.put(Shape.ROMAN_II, OmrShape.none); + // map.put(Shape.ROMAN_III, OmrShape.none); + // map.put(Shape.ROMAN_IV, OmrShape.none); + // map.put(Shape.ROMAN_V, OmrShape.none); + // map.put(Shape.ROMAN_VI, OmrShape.none); + // map.put(Shape.ROMAN_VII, OmrShape.none); + // map.put(Shape.ROMAN_VIII, OmrShape.none); + // map.put(Shape.ROMAN_IX, OmrShape.none); + // map.put(Shape.ROMAN_X, OmrShape.none); + // map.put(Shape.ROMAN_XI, OmrShape.none); + // map.put(Shape.ROMAN_XII, OmrShape.none); + map.put(Shape.PLUCK_P, OmrShape.fingeringPLower); + map.put(Shape.PLUCK_I, OmrShape.fingeringILower); + map.put(Shape.PLUCK_M, OmrShape.fingeringMLower); + map.put(Shape.PLUCK_A, OmrShape.fingeringALower); + map.put(Shape.CLUTTER, OmrShape.none); + // map.put(Shape.TEXT, OmrShape.none); + // map.put(Shape.CHARACTER, OmrShape.none); + map.put(Shape.REPEAT_DOT, OmrShape.repeatDot); + map.put(Shape.AUGMENTATION_DOT, OmrShape.augmentationDot); + map.put(Shape.WHOLE_REST, OmrShape.restWhole); + map.put(Shape.HALF_REST, OmrShape.restHalf); + map.put(Shape.NOTEHEAD_BLACK, OmrShape.noteheadBlack); + map.put(Shape.NOTEHEAD_BLACK_SMALL, OmrShape.noteheadBlackSmall); + map.put(Shape.NOTEHEAD_VOID, OmrShape.noteheadHalf); + map.put(Shape.NOTEHEAD_VOID_SMALL, OmrShape.noteheadHalfSmall); + map.put(Shape.WHOLE_NOTE, OmrShape.noteheadWhole); + map.put(Shape.WHOLE_NOTE_SMALL, OmrShape.noteheadWholeSmall); + // map.put(Shape.BEAM, OmrShape.none); + // map.put(Shape.BEAM_SMALL, OmrShape.none); + // map.put(Shape.BEAM_HOOK, OmrShape.none); + // map.put(Shape.BEAM_HOOK_SMALL, OmrShape.none); + // map.put(Shape.SLUR, OmrShape.none); + // map.put(Shape.KEY_FLAT_7, OmrShape.none); + // map.put(Shape.KEY_FLAT_6, OmrShape.none); + // map.put(Shape.KEY_FLAT_5, OmrShape.none); + // map.put(Shape.KEY_FLAT_4, OmrShape.none); + // map.put(Shape.KEY_FLAT_3, OmrShape.none); + // map.put(Shape.KEY_FLAT_2, OmrShape.none); + // map.put(Shape.KEY_SHARP_2, OmrShape.none); + // map.put(Shape.KEY_SHARP_3, OmrShape.none); + // map.put(Shape.KEY_SHARP_4, OmrShape.none); + // map.put(Shape.KEY_SHARP_5, OmrShape.none); + // map.put(Shape.KEY_SHARP_6, OmrShape.none); + // map.put(Shape.KEY_SHARP_7, OmrShape.none); + map.put(Shape.THIN_BARLINE, OmrShape.barlineSingle); + // map.put(Shape.THIN_CONNECTOR, OmrShape.none); + map.put(Shape.THICK_BARLINE, OmrShape.barlineHeavy); + // map.put(Shape.THICK_CONNECTOR, OmrShape.none); + // map.put(Shape.BRACKET_CONNECTOR, OmrShape.none); + map.put(Shape.DOUBLE_BARLINE, OmrShape.barlineDouble); + map.put(Shape.FINAL_BARLINE, OmrShape.barlineFinal); + map.put(Shape.REVERSE_FINAL_BARLINE, OmrShape.barlineReverseFinal); + map.put(Shape.LEFT_REPEAT_SIGN, OmrShape.repeatLeft); + map.put(Shape.RIGHT_REPEAT_SIGN, OmrShape.repeatRight); + map.put(Shape.BACK_TO_BACK_REPEAT_SIGN, OmrShape.repeatRightLeft); + // map.put(Shape.ENDING, OmrShape.none); + // map.put(Shape.CRESCENDO, OmrShape.none); + // map.put(Shape.DIMINUENDO, OmrShape.none); + map.put(Shape.BRACE, OmrShape.brace); + // map.put(Shape.BRACKET, OmrShape.none); + map.put(Shape.REPEAT_DOT_PAIR, OmrShape.repeatDots); + //map.put(Shape.NOISE, OmrShape.none); + map.put(Shape.LEDGER, OmrShape.legerLine); + // map.put(Shape.ENDING_HORIZONTAL, OmrShape.none); + // map.put(Shape.ENDING_VERTICAL, OmrShape.none); + // map.put(Shape.SEGMENT, OmrShape.none); + map.put(Shape.STEM, OmrShape.stem); + map.put(Shape.KEY_FLAT_1, OmrShape.keyFlat); + map.put(Shape.KEY_SHARP_1, OmrShape.keySharp); + map.put(Shape.GRACE_NOTE_SLASH, OmrShape.graceNoteAcciaccaturaStemUp); + map.put(Shape.GRACE_NOTE, OmrShape.graceNoteAppoggiaturaStemUp); + map.put(Shape.FERMATA, OmrShape.fermataAbove); + map.put(Shape.FERMATA_BELOW, OmrShape.fermataBelow); + + return map; + } + + private OmrShapeMapping () + { + } +} diff --git a/src/main/org/audiveris/omr/classifier/Sample.java b/src/main/org/audiveris/omr/classifier/Sample.java index 22e411f45..7682c8e1a 100644 --- a/src/main/org/audiveris/omr/classifier/Sample.java +++ b/src/main/org/audiveris/omr/classifier/Sample.java @@ -21,7 +21,6 @@ // package org.audiveris.omr.classifier; -import org.audiveris.omr.glyph.BasicGlyph; import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.run.RunTable; @@ -45,12 +44,10 @@ */ @XmlRootElement(name = "sample") public class Sample - extends BasicGlyph + extends Glyph { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - Sample.class); + private static final Logger logger = LoggerFactory.getLogger(Sample.class); /** For comparing Sample instances by shape. */ public static final Comparator byShape = new Comparator() @@ -96,8 +93,6 @@ public int compare (Sample s1, } }; - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -120,7 +115,6 @@ public int compare (Sample s1, /** True for artificial (font-based) sample. */ private boolean symbol; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ShapeSample} object. * @@ -160,14 +154,8 @@ public Sample (Glyph glyph, Shape shape, Double pitch) { - this( - glyph.getLeft(), - glyph.getTop(), - glyph.getRunTable(), - interline, - glyph.getId(), - shape, - pitch); + this(glyph.getLeft(), glyph.getTop(), glyph.getRunTable(), interline, glyph.getId(), shape, + pitch); } /** @@ -178,34 +166,6 @@ private Sample () this(0, 0, null, 0, 0, null, null); } - //~ Methods ------------------------------------------------------------------------------------ - //--------------------// - // getRecordableShape // - //--------------------// - /** - * Report the shape to record for the provided shape. - *

          - * For example shapes WHOLE_REST and HALF_REST differ only by their pitch position, so they both - * end up to HW_REST_set physical shape. - * - * @param shape the provided shape - * @return the recordable shape to use, or null - */ - public static Shape getRecordableShape (Shape shape) - { - if (shape == null) { - return null; - } - - Shape physicalShape = shape.getPhysicalShape(); - - if (physicalShape.isTrainable() && (physicalShape != Shape.NOISE)) { - return physicalShape; - } else { - return null; - } - } - /** * We need equality strictly based on reference. * @@ -218,6 +178,11 @@ public boolean equals (Object obj) return this == obj; } + /** + * Report the staff interline for this sample. + * + * @return related staff interline value + */ public int getInterline () { return interline; @@ -272,6 +237,11 @@ public Double getPitch () return pitch; } + /** + * Report the sample shape. + * + * @return sample shape + */ public Shape getShape () { return shape; @@ -326,4 +296,31 @@ protected String internals () return sb.toString(); } + + //--------------------// + // getRecordableShape // + //--------------------// + /** + * Report the shape to record for the provided shape. + *

          + * For example shapes WHOLE_REST and HALF_REST differ only by their pitch position, so they both + * end up to HW_REST_set physical shape. + * + * @param shape the provided shape + * @return the recordable shape to use, or null + */ + public static Shape getRecordableShape (Shape shape) + { + if (shape == null) { + return null; + } + + Shape physicalShape = shape.getPhysicalShape(); + + if (physicalShape.isTrainable() && (physicalShape != Shape.NOISE)) { + return physicalShape; + } else { + return null; + } + } } diff --git a/src/main/org/audiveris/omr/classifier/SampleRepository.java b/src/main/org/audiveris/omr/classifier/SampleRepository.java index d197e7e53..feb788719 100644 --- a/src/main/org/audiveris/omr/classifier/SampleRepository.java +++ b/src/main/org/audiveris/omr/classifier/SampleRepository.java @@ -96,28 +96,26 @@ */ public class SampleRepository { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - SampleRepository.class); - - /** Should we support tribes?. */ - public static final boolean USE_TRIBES = constants.useTribes.isSet(); + private static final Logger logger = LoggerFactory.getLogger(SampleRepository.class); /** Standard interline value: {@value}. (Any value could fit, if used consistently) */ public static final int STANDARD_INTERLINE = 20; + /** File name for samples material: {@value}. */ + public static final String SAMPLES_FILE_NAME = "samples.zip"; + + /** Should we support tribes?. */ + public static final boolean USE_TRIBES = constants.useTribes.isSet(); + /** The global repository instance, if any. */ private static volatile SampleRepository GLOBAL; /** File name for images material: {@value}. */ private static final String IMAGES_FILE_NAME = "images.zip"; - /** File name for samples material: {@value}. */ - public static final String SAMPLES_FILE_NAME = "samples.zip"; - /** Special name to refer to font-based samples: {@value}. */ private static final String SYMBOLS = "ALL_FONT_BASED_SYMBOLS"; @@ -130,15 +128,14 @@ public class SampleRepository */ private static final Pattern SAMPLES_PATTERN = Pattern.compile("(.*-)?(samples\\.zip)"); - //~ Instance fields ---------------------------------------------------------------------------- /** Sheets, mapped by their unique name. */ - private final Map nameMap = new TreeMap(); + private final Map nameMap = new TreeMap<>(); /** Sheets, mapped by their image. */ - private final Map imageMap = new HashMap(); + private final Map imageMap = new HashMap<>(); /** Sheets, mapped by their samples. */ - private final Map sampleMap = new HashMap(); + private final Map sampleMap = new HashMap<>(); /** Container for sheet descriptors. */ private SheetContainer sheetContainer = new SheetContainer(); @@ -150,7 +147,7 @@ public class SampleRepository private boolean imagesLoaded; /** Listeners on repository modifications. */ - private final Set listeners = new LinkedHashSet(); + private final Set listeners = new LinkedHashSet<>(); /** Book radix for this repository (empty string for the global repository). */ private final String bookRadix; @@ -164,7 +161,6 @@ public class SampleRepository /** To handle save on close. */ private Application.ExitListener exitListener; - //~ Constructors ------------------------------------------------------------------------------- /** * (Private) constructor. *

          @@ -198,7 +194,6 @@ private SampleRepository (Path samplesFile) } } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // addListener // //-------------// @@ -302,7 +297,7 @@ public void addSample (Sample sample, logger.info("{} added {} to {}", this, sample, sampleSheet); - fireStateChanged(new AdditionEvent(sample)); + fireStateChanged(new AdditionEvent(sample, this)); } //-----------------// @@ -328,23 +323,21 @@ public void checkAllSamples (Collection conflictings, List allSamples = getAllSamples(); // Sort by weight, then by sheet ID - Collections.sort( - allSamples, - new Comparator() - { - @Override - public int compare (Sample s1, - Sample s2) - { - int comp = Integer.compare(s1.getWeight(), s2.getWeight()); - - if (comp != 0) { - return comp; - } - - return getSheetName(s1).compareTo(getSheetName(s2)); - } - }); + Collections.sort(allSamples, new Comparator() + { + @Override + public int compare (Sample s1, + Sample s2) + { + int comp = Integer.compare(s1.getWeight(), s2.getWeight()); + + if (comp != 0) { + return comp; + } + + return getSheetName(s1).compareTo(getSheetName(s2)); + } + }); int n = allSamples.size(); logger.debug("Checking {} samples...", n); @@ -423,6 +416,9 @@ public void checkForSave () //-------// // close // //-------// + /** + * Close the repository. + */ public synchronized void close () { if (isGlobal()) { @@ -504,8 +500,9 @@ public SampleSheet findSampleSheet (Sheet sheet) * Find out (or create) the SampleSheet that corresponds to provided name and/or * image. *

          - * If sheet image is provided, the repository is searched for the image:

            - *
          • If an identical sheet image already exists, it is used and the provided name is kept as + * If sheet image is provided, the repository is searched for the image: + *
              + *
            • If an identical sheet image already exists, it is used and the provided name is kept as * an alias. *
            • Otherwise, a new sample sheet is created. If the provided name is already used, a suffix * is appended to the name to make it unique within the repository. @@ -627,7 +624,7 @@ public List getAllDescriptors () */ public List getAllSamples () { - final List allSamples = new ArrayList(); + final List allSamples = new ArrayList<>(); for (SampleSheet sheet : nameMap.values()) { allSamples.addAll(sheet.getAllSamples()); @@ -646,7 +643,7 @@ public List getAllSamples () */ public List getAllTribes () { - final List allTribes = new ArrayList(); + final List allTribes = new ArrayList<>(); for (SampleSheet sheet : nameMap.values()) { allTribes.addAll(sheet.getTribes()); @@ -675,87 +672,6 @@ public Descriptor getDescriptor (Sample sample) return null; } - //-------------------// - // getGlobalInstance // - //-------------------// - /** - * Report the (loaded) global repository, after creating it if needed. - * - * @return the global instance of SampleRepository - */ - public static SampleRepository getGlobalInstance () - { - return getGlobalInstance(true); - } - - //-------------------// - // getGlobalInstance // - //-------------------// - /** - * Report the global repository, after creating it if needed. - * - * @param load true for a loaded repository - * @return the global instance of SampleRepository - */ - public static synchronized SampleRepository getGlobalInstance (boolean load) - { - if (GLOBAL == null) { - GLOBAL = getInstance(WellKnowns.TRAIN_FOLDER.resolve(SAMPLES_FILE_NAME), load); - } - - if (load && (GLOBAL != null) && !GLOBAL.isLoaded()) { - GLOBAL.loadRepository(null); - } - - return GLOBAL; - } - - //-------------// - // getInstance // - //-------------// - /** - * Report the repository specifically related to the provided book. - * - * @param book the provided book - * @param load true for a loaded repository - * @return the specific sample repository for the provided book, or null - */ - public static synchronized SampleRepository getInstance (Book book, - boolean load) - { - return getInstance(getSamplesFile(book), load); - } - - //-------------// - // getInstance // - //-------------// - /** - * Report the repository specifically related to the provided samples archives. - * - * @param samplesFile path to the global or specific samples .zip archive - * @param load true for a loaded repository - * @return the specific sample repository, or null - */ - public static synchronized SampleRepository getInstance (Path samplesFile, - boolean load) - { - try { - final SampleRepository repo = new SampleRepository(samplesFile); - - if (load && !repo.isLoaded()) { - logger.info("Repository loading..."); - repo.loadRepository(null); - logger.info("Repository loaded."); - } - - return repo; - } catch (Exception ex) { - logger.warn("Could not get repository instance at {} " + ex, samplesFile, ex); - - return null; - } - } - //----------------// // getSampleSheet // //----------------// @@ -797,12 +713,12 @@ public SampleSheet getSampleSheet (Sample sample) public List getSamples (Collection descriptors, Collection shapes) { - List found = new ArrayList(); + List found = new ArrayList<>(); for (Descriptor descriptor : descriptors) { SampleSheet sampleSheet = nameMap.get(descriptor.getName()); - List sheetShapes = new ArrayList(sampleSheet.getShapes()); + List sheetShapes = new ArrayList<>(sampleSheet.getShapes()); sheetShapes.retainAll(shapes); for (Shape shape : sheetShapes) { @@ -882,50 +798,14 @@ public String getSheetName (Sample sample) return null; } - //-------------// - // hasInstance // - //-------------// - /** - * Report whether the global repository has been allocated. - * - * @return true if GLOBAL exists - */ - public static boolean hasInstance () - { - return GLOBAL != null; - } - - //-----------// - // isSymbols // - //-----------// - /** - * Report whether the provided sheet name is a font-based symbols sheet - * - * @param name provided sheet name - * @return true if font-based symbols - */ - public static boolean isSymbols (String name) - { - return SYMBOLS.equals(name); - } - - //------------------// - // repositoryExists // - //------------------// - /** - * Report whether a repository exists on disk for the provided book. - * - * @param book the provided book - * @return true if repository file(s) exist(s) - */ - public static boolean repositoryExists (Book book) - { - return Files.exists(getSamplesFile(book)); - } - //-----------------// // getExitListener // //-----------------// + /** + * Report the ExitListener called at closing time. + * + * @return specific exit listener that check if repository has unsaved modifications + */ public final synchronized Application.ExitListener getExitListener () { if (exitListener == null) { @@ -951,12 +831,20 @@ public boolean hasSheetImages () //-------------------// // includeRepository // //-------------------// + /** + * Include the content of another repository into this one. + * + * @param source the other repository to include + */ public void includeRepository (SampleRepository source) { source.loadAllImages(); for (SampleSheet sampleSheet : source.nameMap.values()) { - includeSampleSheet(sampleSheet); + // We process all but font-based samples + if (!isSymbols(sampleSheet.getDescriptor().getName())) { + includeSampleSheet(sampleSheet); + } } } @@ -1052,6 +940,11 @@ public boolean isLoaded () //------------// // isModified // //------------// + /** + * Report whether the repository has unsaved modifications. + * + * @return true if any modification has not been saved + */ public boolean isModified () { if (sheetContainer.isModified()) { @@ -1067,6 +960,18 @@ public boolean isModified () return false; } + //-------------// + // setModified // + //-------------// + private void setModified (boolean bool) + { + sheetContainer.setModified(bool); + + for (SampleSheet sampleSheet : nameMap.values()) { + sampleSheet.setModified(bool); + } + } + //---------------// // loadAllImages // //---------------// @@ -1095,6 +1000,12 @@ public void loadAllImages () //-----------// // loadImage // //-----------// + /** + * Load the background image, if any, of a sample sheet + * + * @param sampleSheet the sheet of samples + * @return the related image, or null if not found + */ public RunTable loadImage (SampleSheet sampleSheet) { final Descriptor descriptor = sampleSheet.getDescriptor(); @@ -1218,9 +1129,12 @@ public SampleSheet pokeSampleSheet (Sheet sheet) //------------------------// // purgeOrphanDescriptors // //------------------------// + /** + * + */ public void purgeOrphanDescriptors () { - for (Descriptor descriptor : new ArrayList(getAllDescriptors())) { + for (Descriptor descriptor : new ArrayList<>(getAllDescriptors())) { final SampleSheet sampleSheet = getSampleSheet(descriptor); if (sampleSheet == null) { @@ -1254,6 +1168,12 @@ public void purgeSheets () //----------------// // removeListener // //----------------// + /** + * remove a ChangeListener + * + * @param listener the listener to remove + * @return true if actually removed + */ public boolean removeListener (ChangeListener listener) { return listeners.remove(listener); @@ -1283,7 +1203,7 @@ public void removeSample (Sample sample) logger.info("{} removed {} from {}", this, sample, sampleSheet); - fireStateChanged(new RemovalEvent(sample)); + fireStateChanged(new RemovalEvent(sample, this)); } //-------------// @@ -1308,7 +1228,7 @@ public void removeSheet (Descriptor descriptor) } sheetContainer.removeDescriptor(descriptor); - fireStateChanged(new SheetRemovalEvent(descriptor)); + fireStateChanged(new SheetRemovalEvent(descriptor, this)); } //--------// @@ -1322,14 +1242,14 @@ public void removeSheet (Descriptor descriptor) public void shrink (int maxCount) { // Gather samples by shape - EnumMap> shapeSamples = new EnumMap>(Shape.class); + EnumMap> shapeSamples = new EnumMap<>(Shape.class); for (Sample sample : getAllSamples()) { Shape shape = sample.getShape(); List list = shapeSamples.get(shape); if (list == null) { - shapeSamples.put(shape, list = new ArrayList()); + shapeSamples.put(shape, list = new ArrayList<>()); } list.add(sample); @@ -1365,14 +1285,14 @@ public void splitTrainAndTest (List train, int maxCount) { // Gather samples by shape - EnumMap> shapeSamples = new EnumMap>(Shape.class); + EnumMap> shapeSamples = new EnumMap<>(Shape.class); for (Sample sample : getAllSamples()) { Shape shape = sample.getShape(); List list = shapeSamples.get(shape); if (list == null) { - shapeSamples.put(shape, list = new ArrayList()); + shapeSamples.put(shape, list = new ArrayList<>()); } list.add(sample); @@ -1422,7 +1342,7 @@ public void storeRepository () setModified(false); logger.info("{} stored to {}", this, samplesFile); - } catch (Throwable ex) { + } catch (IOException ex) { logger.warn("Error storing " + this + " to " + samplesFile + " " + ex, ex); } } @@ -1438,22 +1358,6 @@ public String toString () return name + " repository"; } - //----------------// - // getSamplesFile // - //----------------// - /** - * Report the path to the (theoretical) samples file for the provided book. - * - * @param book the provided book - * @return the theoretical path to samples file - */ - private static Path getSamplesFile (Book book) - { - final Path bookFolder = BookManager.getDefaultBookFolder(book); - - return bookFolder.resolve(book.getRadix() + "-" + SAMPLES_FILE_NAME); - } - //-------------------// // buildSymbolSample // //-------------------// @@ -1533,62 +1437,60 @@ private void fireStateChanged (ChangeEvent event) private void loadAllImages (final Path root) { try { - Files.walkFileTree( - root, - new SimpleFileVisitor() - { - @Override - public FileVisitResult preVisitDirectory (Path dir, - BasicFileAttributes attrs) - throws IOException - { - // Check whether we already have an image for this folder - final Path dirFile = dir.getFileName(); - - if (dirFile != null) { - String dirName = dirFile.toString(); - - if (dirName.endsWith("/")) { - dirName = dirName.substring(0, dirName.length() - 1); - } - - final SampleSheet sampleSheet = nameMap.get(dirName); - - if ((sampleSheet != null) && (sampleSheet.getImage() != null)) { - return FileVisitResult.SKIP_SUBTREE; - } - } - - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFile (Path file, - BasicFileAttributes attrs) - throws IOException - { - final String fileName = file.getFileName().toString(); - - if (fileName.equals(SampleSheet.IMAGE_FILE_NAME)) { - RunTable runTable = RunTable.unmarshal(file); - - if (runTable != null) { - Path folder = file.getParent().getFileName(); - SampleSheet sampleSheet = nameMap.get(folder.toString()); - - if (sampleSheet != null) { - sampleSheet.setImage(runTable, true); - logger.debug("Loaded {}", file); - } else { - logger.warn("No SampleSheet found for image {}", file); - } - } - } - - return FileVisitResult.CONTINUE; - } - }); - } catch (Throwable ex) { + Files.walkFileTree(root, new SimpleFileVisitor() + { + @Override + public FileVisitResult preVisitDirectory (Path dir, + BasicFileAttributes attrs) + throws IOException + { + // Check whether we already have an image for this folder + final Path dirFile = dir.getFileName(); + + if (dirFile != null) { + String dirName = dirFile.toString(); + + if (dirName.endsWith("/")) { + dirName = dirName.substring(0, dirName.length() - 1); + } + + final SampleSheet sampleSheet = nameMap.get(dirName); + + if ((sampleSheet != null) && (sampleSheet.getImage() != null)) { + return FileVisitResult.SKIP_SUBTREE; + } + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile (Path file, + BasicFileAttributes attrs) + throws IOException + { + final String fileName = file.getFileName().toString(); + + if (fileName.equals(SampleSheet.IMAGE_FILE_NAME)) { + RunTable runTable = RunTable.unmarshal(file); + + if (runTable != null) { + Path folder = file.getParent().getFileName(); + SampleSheet sampleSheet = nameMap.get(folder.toString()); + + if (sampleSheet != null) { + sampleSheet.setImage(runTable, true); + logger.debug("Loaded {}", file); + } else { + logger.warn("No SampleSheet found for image {}", file); + } + } + } + + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException ex) { logger.warn("Error loading binaries from " + imagesFile + " " + ex, ex); } } @@ -1603,54 +1505,52 @@ private void loadSamples (final Path root, final LoadListener loadListener) { try { - Files.walkFileTree( - root, - new SimpleFileVisitor() - { - @Override - public FileVisitResult visitFile (Path file, - BasicFileAttributes attrs) - throws IOException - { - final String fileName = file.getFileName().toString(); - - if (fileName.equals(SampleSheet.SAMPLES_FILE_NAME)) { - Path folder = file.getParent().getFileName(); - Descriptor desc = sheetContainer.getDescriptor(folder.toString()); - SampleSheet sampleSheet = null; - - if (desc == null) { - logger.warn( - "Samples entry {} not declared in {} is ignored.", - folder, - SheetContainer.CONTAINER_ENTRY_NAME); - } else { - boolean isSymbol = isSymbols(desc.getName()); - - if (isSymbol) { - logger.info("Skipping symbols entry"); - - return FileVisitResult.CONTINUE; - } - - sampleSheet = SampleSheet.unmarshal(file, desc); - nameMap.put(desc.getName(), sampleSheet); - - for (Sample sample : sampleSheet.getAllSamples()) { - sample.setSymbol(isSymbol); - sampleMap.put(sample, sampleSheet); - } - } - - if (loadListener != null) { - loadListener.loadedSheet(sampleSheet); - } - } - - return FileVisitResult.CONTINUE; - } - }); - } catch (Throwable ex) { + Files.walkFileTree(root, new SimpleFileVisitor() + { + @Override + public FileVisitResult visitFile (Path file, + BasicFileAttributes attrs) + throws IOException + { + final String fileName = file.getFileName().toString(); + + if (fileName.equals(SampleSheet.SAMPLES_FILE_NAME)) { + Path folder = file.getParent().getFileName(); + Descriptor desc = sheetContainer.getDescriptor(folder.toString()); + SampleSheet sampleSheet = null; + + if (desc == null) { + logger.warn( + "Samples entry {} not declared in {} is ignored.", + folder, + SheetContainer.CONTAINER_ENTRY_NAME); + } else { + boolean isSymbol = isSymbols(desc.getName()); + + if (isSymbol) { + logger.info("Skipping symbols entry"); + + return FileVisitResult.CONTINUE; + } + + sampleSheet = SampleSheet.unmarshal(file, desc); + nameMap.put(desc.getName(), sampleSheet); + + for (Sample sample : sampleSheet.getAllSamples()) { + sample.setSymbol(isSymbol); + sampleMap.put(sample, sampleSheet); + } + } + + if (loadListener != null) { + loadListener.loadedSheet(sampleSheet); + } + } + + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException ex) { logger.warn("Error loading " + samplesFile + " " + ex, ex); } } @@ -1664,51 +1564,218 @@ public FileVisitResult visitFile (Path file, private void loadTribes (final Path root) { try { - Files.walkFileTree( - root, - new SimpleFileVisitor() - { - @Override - public FileVisitResult visitFile (Path file, - BasicFileAttributes attrs) - throws IOException - { - final String fileName = file.getFileName().toString(); - - if (fileName.equals(SampleSheet.TRIBES_FILE_NAME)) { - Path folder = file.getParent().getFileName(); - SampleSheet sampleSheet = nameMap.get(folder.toString()); - - if (sampleSheet != null) { - TribeList tribeList = TribeList.unmarshal(file); - sampleSheet.setTribes(tribeList.getTribes()); - logger.debug("Loaded {}", file); - } else { - logger.warn("No SampleSheet found for tribes {}", file); - } - } - - return FileVisitResult.CONTINUE; - } - }); - } catch (Throwable ex) { + Files.walkFileTree(root, new SimpleFileVisitor() + { + @Override + public FileVisitResult visitFile (Path file, + BasicFileAttributes attrs) + throws IOException + { + final String fileName = file.getFileName().toString(); + + if (fileName.equals(SampleSheet.TRIBES_FILE_NAME)) { + Path folder = file.getParent().getFileName(); + SampleSheet sampleSheet = nameMap.get(folder.toString()); + + if (sampleSheet != null) { + TribeList tribeList = TribeList.unmarshal(file); + sampleSheet.setTribes(tribeList.getTribes()); + logger.debug("Loaded {}", file); + } else { + logger.warn("No SampleSheet found for tribes {}", file); + } + } + + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException ex) { logger.warn("Error loading tribes " + ex, ex); } } + //-------------------// + // getGlobalInstance // + //-------------------// + /** + * Report the (loaded) global repository, after creating it if needed. + * + * @return the global instance of SampleRepository + */ + public static SampleRepository getGlobalInstance () + { + return getGlobalInstance(true); + } + + //-------------------// + // getGlobalInstance // + //-------------------// + /** + * Report the global repository, after creating it if needed. + * + * @param load true for a loaded repository + * @return the global instance of SampleRepository + */ + public static synchronized SampleRepository getGlobalInstance (boolean load) + { + if (GLOBAL == null) { + GLOBAL = getInstance(WellKnowns.TRAIN_FOLDER.resolve(SAMPLES_FILE_NAME), load); + } + + if (load && (GLOBAL != null) && !GLOBAL.isLoaded()) { + GLOBAL.loadRepository(null); + } + + return GLOBAL; + } + //-------------// - // setModified // + // getInstance // //-------------// - private void setModified (boolean bool) + /** + * Report the repository specifically related to the provided book. + * + * @param book the provided book + * @param load true for a loaded repository + * @return the specific sample repository for the provided book, or null + */ + public static synchronized SampleRepository getInstance (Book book, + boolean load) { - sheetContainer.setModified(bool); + return getInstance(getSamplesFile(book), load); + } - for (SampleSheet sampleSheet : nameMap.values()) { - sampleSheet.setModified(bool); + //-------------// + // getInstance // + //-------------// + /** + * Report the repository specifically related to the provided samples archives. + * + * @param samplesFile path to the global or specific samples .zip archive + * @param load true for a loaded repository + * @return the specific sample repository, or null + */ + public static synchronized SampleRepository getInstance (Path samplesFile, + boolean load) + { + try { + final SampleRepository repo = new SampleRepository(samplesFile); + + if (load && !repo.isLoaded()) { + logger.info("Repository loading..."); + repo.loadRepository(null); + logger.info("Repository loaded."); + } + + return repo; + } catch (Exception ex) { + logger.warn("Could not get repository instance at {} " + ex, samplesFile, ex); + + return null; + } + } + + //-------------// + // hasInstance // + //-------------// + /** + * Report whether the global repository has been allocated. + * + * @return true if GLOBAL exists + */ + public static boolean hasInstance () + { + return GLOBAL != null; + } + + //-----------// + // isSymbols // + //-----------// + /** + * Report whether the provided sheet name is a font-based symbols sheet + * + * @param name provided sheet name + * @return true if font-based symbols + */ + public static boolean isSymbols (String name) + { + return SYMBOLS.equals(name); + } + + //------------------// + // repositoryExists // + //------------------// + /** + * Report whether a repository exists on disk for the provided book. + * + * @param book the provided book + * @return true if repository file(s) exist(s) + */ + public static boolean repositoryExists (Book book) + { + return Files.exists(getSamplesFile(book)); + } + + //----------------// + // getSamplesFile // + //----------------// + /** + * Report the path to the (theoretical) samples file for the provided book. + * + * @param book the provided book + * @return the theoretical path to samples file + */ + private static Path getSamplesFile (Book book) + { + final Path bookFolder = BookManager.getDefaultBookFolder(book); + + return bookFolder.resolve(book.getRadix() + "-" + SAMPLES_FILE_NAME); + } + + //------------------------// + // RepositoryExitListener // + //------------------------// + /** + * Listener called when application asks for exit and does exit. + */ + private class RepositoryExitListener + implements Application.ExitListener + { + + RepositoryExitListener () + { + } + + @Override + public boolean canExit (EventObject eo) + { + // Check whether the repository has been saved (or user has declined) + if (isModified()) { + SingleFrameApplication appli = (SingleFrameApplication) Application.getInstance(); + int answer = JOptionPane.showConfirmDialog( + appli.getMainFrame(), + "Save " + SampleRepository.this + "?"); + + if (answer == JOptionPane.YES_OPTION) { + storeRepository(); + + return true; // Here user has saved the repository + } + + // True: user specifically chooses NOT to save the script + // False: user says Oops!, cancelling the current close request + return answer == JOptionPane.NO_OPTION; + } + + return true; + } + + @Override + public void willExit (EventObject eo) + { } } - //~ Inner Interfaces --------------------------------------------------------------------------- //--------------// // LoadListener // //-------------// @@ -1718,7 +1785,6 @@ private void setModified (boolean bool) */ public static interface LoadListener { - //~ Methods -------------------------------------------------------------------------------- /** * Called whenever a new sample sheet has been loaded. @@ -1735,24 +1801,29 @@ public static interface LoadListener void totalSheets (int total); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------------// // AdditionEvent // //---------------// /** * Event used to carry information about sample addition performed. */ - public class AdditionEvent + public static class AdditionEvent extends ChangeEvent { - //~ Instance fields ------------------------------------------------------------------------ - public final Sample sample; // The added sample + /** The sample added. */ + public final Sample sample; - //~ Constructors --------------------------------------------------------------------------- - public AdditionEvent (Sample sample) + /** + * Create an [@code AdditionEvent}. + * + * @param sample the sample added + * @param repo the repository where sample is added + */ + public AdditionEvent (Sample sample, + SampleRepository repo) { - super(SampleRepository.this); + super(repo); this.sample = sample; } } @@ -1763,17 +1834,23 @@ public AdditionEvent (Sample sample) /** * Event used to carry information about sample removal performed. */ - public class RemovalEvent + public static class RemovalEvent extends ChangeEvent { - //~ Instance fields ------------------------------------------------------------------------ - public final Sample sample; // The removed sample + /** The removed sample. */ + public final Sample sample; - //~ Constructors --------------------------------------------------------------------------- - public RemovalEvent (Sample sample) + /** + * Create a removal event. + * + * @param sample the removed sample + * @param repo the impacted repository + */ + public RemovalEvent (Sample sample, + SampleRepository repo) { - super(SampleRepository.this); + super(repo); this.sample = sample; } } @@ -1784,17 +1861,23 @@ public RemovalEvent (Sample sample) /** * Event used to carry information about sheet removal performed. */ - public class SheetRemovalEvent + public static class SheetRemovalEvent extends ChangeEvent { - //~ Instance fields ------------------------------------------------------------------------ - public final Descriptor descriptor; // Descriptor of the removed sheet + /** Descriptor of the removed sheet. */ + public final Descriptor descriptor; - //~ Constructors --------------------------------------------------------------------------- - public SheetRemovalEvent (Descriptor descriptor) + /** + * Create a event to remove a sample sheet. + * + * @param descriptor sample sheet descriptor + * @param repo impacted repository + */ + public SheetRemovalEvent (Descriptor descriptor, + SampleRepository repo) { - super(SampleRepository.this); + super(repo); this.descriptor = descriptor; } } @@ -1802,10 +1885,9 @@ public SheetRemovalEvent (Descriptor descriptor) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, @@ -1815,50 +1897,4 @@ private static final class Constants false, "Should we support tribes?"); } - - //------------------------// - // RepositoryExitListener // - //------------------------// - /** - * Listener called when application asks for exit and does exit. - */ - private class RepositoryExitListener - implements Application.ExitListener - { - //~ Constructors --------------------------------------------------------------------------- - - public RepositoryExitListener () - { - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public boolean canExit (EventObject eo) - { - // Check whether the repository has been saved (or user has declined) - if (isModified()) { - SingleFrameApplication appli = (SingleFrameApplication) Application.getInstance(); - int answer = JOptionPane.showConfirmDialog( - appli.getMainFrame(), - "Save " + SampleRepository.this + "?"); - - if (answer == JOptionPane.YES_OPTION) { - storeRepository(); - - return true; // Here user has saved the repository - } - - // True: user specifically chooses NOT to save the script - // False: user says Oops!, cancelling the current close request - return answer == JOptionPane.NO_OPTION; - } - - return true; - } - - @Override - public void willExit (EventObject eo) - { - } - } } diff --git a/src/main/org/audiveris/omr/classifier/SampleSheet.java b/src/main/org/audiveris/omr/classifier/SampleSheet.java index 2b36a5cb7..b9a17d266 100644 --- a/src/main/org/audiveris/omr/classifier/SampleSheet.java +++ b/src/main/org/audiveris/omr/classifier/SampleSheet.java @@ -22,7 +22,6 @@ package org.audiveris.omr.classifier; import org.audiveris.omr.classifier.SheetContainer.Descriptor; -import org.audiveris.omr.glyph.BasicGlyph; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.run.RunTable; import org.audiveris.omr.util.FileUtil; @@ -51,6 +50,7 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.stream.XMLStreamException; /** * Class {@code SampleSheet} gathers samples from the same sheet. @@ -61,10 +61,11 @@ * display each sample within its context. * Real sheet uniqueness is provided via its binary image, since several names could refer to the * same image. + * + * @author Hervé Bitteur */ public class SampleSheet { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SampleSheet.class); @@ -80,20 +81,6 @@ public class SampleSheet /** Un/marshalling context for use with JAXB. */ private static volatile JAXBContext jaxbContext; - //~ Enumerations ------------------------------------------------------------------------------- - public enum ImageStatus - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** There is no recorded image for this sheet. */ - NO_IMAGE, - /** Sheet image is available on disk. */ - ON_DISK, - /** Sheet image is available in memory. */ - LOADED; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Full descriptor. */ private final Descriptor descriptor; @@ -107,8 +94,7 @@ public enum ImageStatus private boolean imageSaved = true; /** Samples gathered by shape. */ - private final EnumMap> shapeMap = new EnumMap>( - Shape.class); + private final EnumMap> shapeMap = new EnumMap<>(Shape.class); /** Has this sheet been modified?. */ private boolean modified; @@ -119,7 +105,6 @@ public enum ImageStatus /** Tribe being created by user. */ private Tribe currentTribe; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SampleSheet} object. * @@ -146,85 +131,13 @@ private SampleSheet (SampleList value, ArrayList list = shapeMap.get(shape); if (list == null) { - shapeMap.put(shape, list = new ArrayList()); + shapeMap.put(shape, list = new ArrayList<>()); } list.add(sample); } } - //~ Methods ------------------------------------------------------------------------------------ - /** - * Delete from disk the samples, tribes and image if any of a defunct sheet. - * - * @param descriptor descriptor of the defunct sheet - * @param samplesRoot root for samples (& tribes) - * @param imagesRoot root for images - */ - public static void delete (SheetContainer.Descriptor descriptor, - Path samplesRoot, - Path imagesRoot) - { - try { - logger.info("Deleting material for sheet {}", descriptor); - - { - // Samples (and tribes) - final Path folderPath = samplesRoot.resolve(descriptor.getName()); - - if (Files.exists(folderPath)) { - FileUtil.deleteDirectory(folderPath); - logger.info(" Samples: deleted {} folder", folderPath); - } - } - - { - // Images - final Path folderPath = imagesRoot.resolve(descriptor.getName()); - - if (Files.exists(folderPath)) { - FileUtil.deleteDirectory(folderPath); - logger.info(" Images: deleted {} folder", folderPath); - } - } - } catch (Exception ex) { - logger.error("Error deleting material for sheet " + descriptor + " " + ex, ex); - } - } - - //-----------// - // unmarshal // - //-----------// - /** - * Load a SampleSheet instance from the provided path. - * - * @param path the source path - * @param desc sheet descriptor - * @return the unmarshalled instance - * @throws IOException if something goes wrong during IO operations - */ - public static SampleSheet unmarshal (Path path, - Descriptor desc) - throws IOException - { - logger.debug("SampleSheet unmarshalling {}", path); - - try { - InputStream is = Files.newInputStream(path, StandardOpenOption.READ); - Unmarshaller um = getJaxbContext().createUnmarshaller(); - SampleList sampleList = (SampleList) um.unmarshal(is); - SampleSheet sampleSheet = new SampleSheet(sampleList, desc); - logger.debug("Unmarshalled {}", sampleSheet); - is.close(); - - return sampleSheet; - } catch (JAXBException ex) { - logger.warn("Error unmarshalling " + path + " " + ex, ex); - - return null; - } - } - //---------------// // getAllSamples // //---------------// @@ -235,7 +148,7 @@ public static SampleSheet unmarshal (Path path, */ public List getAllSamples () { - List allSamples = new ArrayList(); + List allSamples = new ArrayList<>(); for (List sampleList : shapeMap.values()) { allSamples.addAll(sampleList); @@ -247,6 +160,11 @@ public List getAllSamples () //-----------------// // getCurrentTribe // //-----------------// + /** + * Report the current tribe. + * + * @return current tribe + */ public Tribe getCurrentTribe () { return currentTribe; @@ -256,6 +174,8 @@ public Tribe getCurrentTribe () // getDescriptor // //---------------// /** + * Report the sheet descriptor. + * * @return the descriptor */ public Descriptor getDescriptor () @@ -267,7 +187,9 @@ public Descriptor getDescriptor () // getImage // //----------// /** - * @return the image + * Report the sheet underlying image, if any. + * + * @return the image or null */ public RunTable getImage () { @@ -349,7 +271,7 @@ public Tribe getTribe (Sample best) for (Tribe tribe : getTribes()) { Sample tribeBest = tribe.getHead(); - if (((BasicGlyph) tribeBest).equals(best) && (tribeBest.shape == best.shape)) { + if (tribeBest.equals(best) && (tribeBest.shape == best.shape)) { currentTribe = tribe; break; @@ -359,7 +281,7 @@ public Tribe getTribe (Sample best) if (currentTribe == null) { // Create a brand new one if (tribes == null) { - tribes = new ArrayList(); + tribes = new ArrayList<>(); } currentTribe = new Tribe(best); @@ -386,6 +308,19 @@ public List getTribes () return Collections.emptyList(); } + //-----------// + // setTribes // + //-----------// + /** + * Assign the set of tribes. + * + * @param tribes the tribes to set + */ + public void setTribes (List tribes) + { + this.tribes = tribes; + } + //------------// // isModified // //------------// @@ -397,6 +332,19 @@ public boolean isModified () return modified || !imageSaved; } + //-------------// + // setModified // + //-------------// + /** + * Flag the sample sheet as modified. + * + * @param modified the value to assign + */ + public void setModified (boolean modified) + { + this.modified = modified; + } + //---------// // marshal // //---------// @@ -439,7 +387,9 @@ public void marshal (Path samplesRoot, imageSaved = true; logger.info("Stored {}", imagePath); } - } catch (Exception ex) { + } catch (IOException | + JAXBException | + XMLStreamException ex) { logger.error("Error marshalling " + this + " " + ex, ex); } } @@ -461,28 +411,6 @@ public void setImage (RunTable image, imageStatus = ImageStatus.LOADED; } - //-------------// - // setModified // - //-------------// - /** - * @param modified the value to assign - */ - public void setModified (boolean modified) - { - this.modified = modified; - } - - //-----------// - // setTribes // - //-----------// - /** - * @param tribes the tribes to set - */ - public void setTribes (List tribes) - { - this.tribes = tribes; - } - //----------// // toString // //----------// @@ -516,7 +444,7 @@ void privateAddSample (Sample sample) ArrayList list = shapeMap.get(shape); if (list == null) { - shapeMap.put(shape, list = new ArrayList()); + shapeMap.put(shape, list = new ArrayList<>()); } list.add(sample); @@ -556,6 +484,75 @@ void privateRemoveSample (Sample sample) setModified(true); } + /** + * Delete from disk the samples, tribes and image if any of a defunct sheet. + * + * @param descriptor descriptor of the defunct sheet + * @param samplesRoot root for samples (& tribes) + * @param imagesRoot root for images + */ + public static void delete (SheetContainer.Descriptor descriptor, + Path samplesRoot, + Path imagesRoot) + { + try { + logger.info("Deleting material for sheet {}", descriptor); + + { + // Samples (and tribes) + final Path folderPath = samplesRoot.resolve(descriptor.getName()); + + if (Files.exists(folderPath)) { + FileUtil.deleteDirectory(folderPath); + logger.info(" Samples: deleted {} folder", folderPath); + } + } + + { + // Images + final Path folderPath = imagesRoot.resolve(descriptor.getName()); + + if (Files.exists(folderPath)) { + FileUtil.deleteDirectory(folderPath); + logger.info(" Images: deleted {} folder", folderPath); + } + } + } catch (IOException ex) { + logger.error("Error deleting material for sheet " + descriptor + " " + ex, ex); + } + } + + //-----------// + // unmarshal // + //-----------// + /** + * Load a SampleSheet instance from the provided path. + * + * @param path the source path + * @param desc sheet descriptor + * @return the unmarshalled instance + * @throws IOException if something goes wrong during IO operations + */ + public static SampleSheet unmarshal (Path path, + Descriptor desc) + throws IOException + { + logger.debug("SampleSheet unmarshalling {}", path); + + try (InputStream is = Files.newInputStream(path, StandardOpenOption.READ)) { + Unmarshaller um = getJaxbContext().createUnmarshaller(); + SampleList sampleList = (SampleList) um.unmarshal(is); + SampleSheet sampleSheet = new SampleSheet(sampleList, desc); + logger.debug("Unmarshalled {}", sampleSheet); + + return sampleSheet; + } catch (JAXBException ex) { + logger.warn("Error unmarshalling " + path + " " + ex, ex); + + return null; + } + } + //----------------// // getJaxbContext // //----------------// @@ -570,7 +567,19 @@ private static JAXBContext getJaxbContext () return jaxbContext; } - //~ Inner Classes ------------------------------------------------------------------------------ + /** + * Formalizes the status of sheet image, to avoid endless load attempts. + */ + public enum ImageStatus + { + /** There is no recorded image for this sheet. */ + NO_IMAGE, + /** Sheet image is available on disk. */ + ON_DISK, + /** Sheet image is available in memory. */ + LOADED + } + //------------// // SampleList // //------------// @@ -581,7 +590,6 @@ private static JAXBContext getJaxbContext () @XmlRootElement(name = "samples") private static class SampleList { - //~ Instance fields ------------------------------------------------------------------------ // Persistent data //---------------- @@ -590,10 +598,9 @@ private static class SampleList private final String name; @XmlElement(name = "sample") - private final ArrayList samples = new ArrayList(); + private final ArrayList samples = new ArrayList<>(); - //~ Constructors --------------------------------------------------------------------------- - public SampleList (SampleSheet sampleSheet) + SampleList (SampleSheet sampleSheet) { name = sampleSheet.getDescriptor().getName(); diff --git a/src/main/org/audiveris/omr/classifier/SampleSource.java b/src/main/org/audiveris/omr/classifier/SampleSource.java index bc086615c..0b82da396 100644 --- a/src/main/org/audiveris/omr/classifier/SampleSource.java +++ b/src/main/org/audiveris/omr/classifier/SampleSource.java @@ -30,7 +30,6 @@ */ public interface SampleSource { - //~ Methods ------------------------------------------------------------------------------------ /** * Get the collection of samples to test. @@ -46,21 +45,26 @@ public interface SampleSource */ List getTrainSamples (); - //~ Inner Classes ------------------------------------------------------------------------------ + /** + * A basic source of samples, the same set being used for training and for test. + */ static class ConstantSource implements SampleSource { - //~ Instance fields ------------------------------------------------------------------------ + /** The underlying collection of samples. */ private final List samples; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a ConstantSource object. + * + * @param samples the underlying collection of samples. + */ public ConstantSource (List samples) { this.samples = samples; } - //~ Methods -------------------------------------------------------------------------------- @Override public List getTestSamples () { diff --git a/src/main/org/audiveris/omr/classifier/ScaledBuffer.java b/src/main/org/audiveris/omr/classifier/ScaledBuffer.java index dfb4b4548..0cee31c02 100644 --- a/src/main/org/audiveris/omr/classifier/ScaledBuffer.java +++ b/src/main/org/audiveris/omr/classifier/ScaledBuffer.java @@ -1,94 +1,90 @@ -//------------------------------------------------------------------------------------------------// -// // -// S c a l e d B u f f e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.classifier; - -import ij.process.Blitter; -import ij.process.ByteProcessor; - -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.run.RunTable; -import org.audiveris.omr.util.ByteUtil; - -import java.awt.Point; - -/** - * Class {@code ScaledBuffer} produces a rectangular buffer, with size normalized by - * reference interline value, and centered on glyph centroid. - * - * @author Hervé Bitteur - */ -public class ScaledBuffer -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - /** Scaled interline value. */ - public static int INTERLINE = 5; - - /** Target width. */ - public static int WIDTH = 24; // 24 = 3 * 2**3 - - /** Target height. */ - public static int HEIGHT = 48; // 48 = 6 * 2**3 - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Compute the scaled buffer for the provided glyph, using related staff interline - * value. - * - * @param glyph the source glyph - * @param interline the related staff interline - * @return the computed buffer using 0 for black (foreground) and 255 for white (background) - */ - public static ByteProcessor getBuffer (Glyph glyph, - int interline) - { - final RunTable runTable = glyph.getRunTable(); - final ByteProcessor glyphBuffer = runTable.getBuffer(); - final double scale = (double) INTERLINE / interline; - - // Build scaled buffer, filled by (scaled) glyph - final int scaledWidth = (int) Math.ceil(runTable.getWidth() * scale); - final int scaledHeight = (int) Math.ceil(runTable.getHeight() * scale); - final ByteProcessor scaledBuffer = (ByteProcessor) glyphBuffer.resize( - scaledWidth, - scaledHeight, - true); // True => use averaging when down-scaling - - // Copy scaledBuffer into a WIDTH*HEIGHT target buffer centered on glyph centroid - final Point centroid = glyph.getCentroid(); - final Point center = glyph.getCenter(); - final int dx = centroid.x - center.x; // X shift of centroid WRT center - final int dy = centroid.y - center.y; // Y shift of centroid WRT center - final int targetDx = (int) Math.rint(dx * scale); // Scaled x shift - final int targetDy = (int) Math.rint(dy * scale); // Scaled y shift - - final ByteProcessor buffer = new ByteProcessor(WIDTH, HEIGHT); // Same dim for any symbol - ByteUtil.raz(buffer); // Correct - ///ByteUtil.fill(targetBuffer, 100); // Not correct, just meant to visualize limits... - - final int xOffset = ((WIDTH - scaledWidth) / 2) - targetDx; - final int yOffset = ((HEIGHT - scaledHeight) / 2) - targetDy; - buffer.copyBits(scaledBuffer, xOffset, yOffset, Blitter.COPY); - - return buffer; - } -} +//------------------------------------------------------------------------------------------------// +// // +// S c a l e d B u f f e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.classifier; + +import ij.process.Blitter; +import ij.process.ByteProcessor; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.run.RunTable; +import org.audiveris.omr.util.ByteUtil; + +import java.awt.Point; + +/** + * Class {@code ScaledBuffer} produces a rectangular buffer, with size normalized by + * reference interline value, and centered on glyph centroid. + * + * @author Hervé Bitteur + */ +public class ScaledBuffer +{ + + /** Scaled interline value. */ + public static final int INTERLINE = 5; + + /** Target width. */ + public static final int WIDTH = 24; // 24 = 3 * 2**3 + + /** Target height. */ + public static final int HEIGHT = 48; // 48 = 6 * 2**3 + + /** + * Compute the scaled buffer for the provided glyph, using related staff interline + * value. + * + * @param glyph the source glyph + * @param interline the related staff interline + * @return the computed buffer using 0 for black (foreground) and 255 for white (background) + */ + public static ByteProcessor getBuffer (Glyph glyph, + int interline) + { + final RunTable runTable = glyph.getRunTable(); + final ByteProcessor glyphBuffer = runTable.getBuffer(); + final double scale = (double) INTERLINE / interline; + + // Build scaled buffer, filled by (scaled) glyph + final int scaledWidth = (int) Math.ceil(runTable.getWidth() * scale); + final int scaledHeight = (int) Math.ceil(runTable.getHeight() * scale); + final ByteProcessor scaledBuffer = (ByteProcessor) glyphBuffer.resize(scaledWidth, + scaledHeight, true); // True => use averaging when down-scaling + + // Copy scaledBuffer into a WIDTH*HEIGHT target buffer centered on glyph centroid + final Point centroid = glyph.getCentroid(); + final Point center = glyph.getCenter(); + final int dx = centroid.x - center.x; // X shift of centroid WRT center + final int dy = centroid.y - center.y; // Y shift of centroid WRT center + final int targetDx = (int) Math.rint(dx * scale); // Scaled x shift + final int targetDy = (int) Math.rint(dy * scale); // Scaled y shift + + final ByteProcessor buffer = new ByteProcessor(WIDTH, HEIGHT); // Same dim for any symbol + ByteUtil.raz(buffer); // Correct + ///ByteUtil.fill(targetBuffer, 100); // Not correct, just meant to visualize limits... + + final int xOffset = ((WIDTH - scaledWidth) / 2) - targetDx; + final int yOffset = ((HEIGHT - scaledHeight) / 2) - targetDy; + buffer.copyBits(scaledBuffer, xOffset, yOffset, Blitter.COPY); + + return buffer; + } +} diff --git a/src/main/org/audiveris/omr/classifier/ShapeClassifier.java b/src/main/org/audiveris/omr/classifier/ShapeClassifier.java index 575f851bb..a070aef92 100644 --- a/src/main/org/audiveris/omr/classifier/ShapeClassifier.java +++ b/src/main/org/audiveris/omr/classifier/ShapeClassifier.java @@ -37,7 +37,6 @@ */ public abstract class ShapeClassifier { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -65,13 +64,11 @@ public Void call () } }); - //~ Constructors ------------------------------------------------------------------------------- /** Not meant to be instantiated. */ private ShapeClassifier () { } - //~ Methods ------------------------------------------------------------------------------------ /** * Report the classifier instance in use. * @@ -87,7 +84,6 @@ public static Classifier getInstance () // } } - // // /** // * Report the second classifier instance in use. // * @@ -112,8 +108,6 @@ public static void preload () { } - //~ Inner Classes ------------------------------------------------------------------------------ - // // /** // * Tell whether we are using DeepClassifier (rather than old BasicClassifier). // * @@ -127,7 +121,7 @@ public static void preload () //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { // diff --git a/src/main/org/audiveris/omr/classifier/SheetContainer.java b/src/main/org/audiveris/omr/classifier/SheetContainer.java index 181378de3..91c6d6bd3 100644 --- a/src/main/org/audiveris/omr/classifier/SheetContainer.java +++ b/src/main/org/audiveris/omr/classifier/SheetContainer.java @@ -27,6 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -35,11 +36,13 @@ import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; @@ -47,6 +50,7 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import javax.xml.stream.XMLStreamException; /** * Class {@code SheetContainer} contains descriptions of sample sheets, notably their @@ -60,10 +64,8 @@ @XmlRootElement(name = "container") public class SheetContainer { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - SheetContainer.class); + private static final Logger logger = LoggerFactory.getLogger(SheetContainer.class); /** Name of the specific entry for container. */ public static final String CONTAINER_ENTRY_NAME = "META-INF/container.xml"; @@ -71,13 +73,15 @@ public class SheetContainer /** Regex pattern for unique names. */ private static final Pattern UNIQUE_PATTERN = Pattern.compile("(.*)(_[0-9][0-9])"); - //~ Instance fields ---------------------------------------------------------------------------- + /** Un/marshalling context for use with JAXB. */ + private static volatile JAXBContext jaxbContext; + // Persistent data //---------------- /** Map (RunTable Hash code => list of sheet descriptors). */ @XmlElement(name = "sheets") @XmlJavaTypeAdapter(Adapter.class) - private HashMap> hashMap = new HashMap>(); + private HashMap> hashMap = new HashMap<>(); // Transient data //--------------- @@ -85,9 +89,8 @@ public class SheetContainer private boolean modified; /** Descriptors to be deleted from disk. */ - private Set defunctDescriptors = new LinkedHashSet(); + private Set defunctDescriptors = new LinkedHashSet<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SheetContainer} object. Needed for JAXB. */ @@ -95,7 +98,6 @@ public SheetContainer () { } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // addDescriptor // //---------------// @@ -109,7 +111,7 @@ public void addDescriptor (Descriptor desc) List descriptors = hashMap.get(desc.hash); if (descriptors == null) { - hashMap.put(desc.hash, descriptors = new ArrayList()); + hashMap.put(desc.hash, descriptors = new ArrayList<>()); } descriptors.add(desc); @@ -119,6 +121,9 @@ public void addDescriptor (Descriptor desc) //------// // dump // //------// + /** + * Dump internals of the SheetContainer. + */ public void dump () { logger.info("SheetContainer: {}", hashMap); @@ -144,7 +149,7 @@ public String forgeUnique (final String name) radix = name; } - final List similars = new ArrayList(); + final List similars = new ArrayList<>(); boolean collided = false; for (List descriptors : hashMap.values()) { @@ -188,7 +193,7 @@ public String forgeUnique (final String name) */ public List getAllDescriptors () { - List all = new ArrayList(); + List all = new ArrayList<>(); for (List descriptors : hashMap.values()) { all.addAll(descriptors); @@ -271,6 +276,19 @@ public boolean isModified () return modified; } + //-------------// + // setModified // + //-------------// + /** + * Set the modified status of this container. + * + * @param modified the modified to set + */ + public void setModified (boolean modified) + { + this.modified = modified; + } + //---------// // marshal // //---------// @@ -292,8 +310,7 @@ public void marshal (Path samplesRoot, Files.createDirectories(path.getParent()); // Container - JAXBContext jaxbContext = JAXBContext.newInstance(SheetContainer.class); - Jaxb.marshal(this, path, jaxbContext); + Jaxb.marshal(this, path, getJaxbContext()); logger.info("Stored {}", path); // Remove defunct sheets if any @@ -304,11 +321,27 @@ public void marshal (Path samplesRoot, defunctDescriptors.clear(); setModified(false); - } catch (Exception ex) { + } catch (IOException | + JAXBException | + XMLStreamException ex) { logger.error("Error marshalling " + this + " " + ex, ex); } } + //----------------// + // getJaxbContext // + //----------------// + private static JAXBContext getJaxbContext () + throws JAXBException + { + // Lazy creation + if (jaxbContext == null) { + jaxbContext = JAXBContext.newInstance(SheetContainer.class); + } + + return jaxbContext; + } + //------------------// // removeDescriptor // //------------------// @@ -332,17 +365,6 @@ public void removeDescriptor (Descriptor desc) setModified(true); } - //-------------// - // setModified // - //-------------// - /** - * @param modified the modified to set - */ - public void setModified (boolean modified) - { - this.modified = modified; - } - //----------// // toString // //----------// @@ -372,26 +394,27 @@ public static SheetContainer unmarshal (Path root) final Path path = root.resolve(CONTAINER_ENTRY_NAME); logger.debug("SheetContainer unmarshalling {}", path); - JAXBContext jaxbContext = JAXBContext.newInstance(SheetContainer.class); - SheetContainer sheetContainer = (SheetContainer) Jaxb.unmarshal(path, jaxbContext); + SheetContainer sheetContainer = (SheetContainer) Jaxb.unmarshal(path, getJaxbContext()); logger.info("Unmarshalled {}", sheetContainer); return sheetContainer; - } catch (Exception ex) { + } catch (IOException | + JAXBException ex) { logger.warn("Error unmarshalling SheetContainer " + ex, ex); return null; } } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Adapter // //---------// + /** + * JAXB adapter to support a HashMap. + */ public static class Adapter extends XmlAdapter>> { - //~ Methods -------------------------------------------------------------------------------- @Override public ContainerValue marshal (HashMap> map) @@ -404,13 +427,13 @@ public ContainerValue marshal (HashMap> map) public HashMap> unmarshal (ContainerValue value) throws Exception { - HashMap> map = new HashMap>(); + HashMap> map = new HashMap<>(); for (Descriptor desc : value.descriptors) { List descriptors = map.get(desc.hash); if (descriptors == null) { - map.put(desc.hash, descriptors = new ArrayList()); + map.put(desc.hash, descriptors = new ArrayList<>()); } if (!descriptors.contains(desc)) { @@ -425,16 +448,22 @@ public HashMap> unmarshal (ContainerValue value) //----------------// // ContainerValue // //----------------// + /** + * A flat list of descriptors to (un)marshal descriptors map. + */ @XmlAccessorType(XmlAccessType.FIELD) public static class ContainerValue { - //~ Instance fields ------------------------------------------------------------------------ /** The collection of sheet descriptors. */ @XmlElement(name = "sheet") - private final List descriptors = new ArrayList(); + private final List descriptors = new ArrayList<>(); - //~ Constructors --------------------------------------------------------------------------- + /** + * Populate the flat list. + * + * @param map the map of descriptors. + */ public ContainerValue (HashMap> map) { for (List list : map.values()) { @@ -449,17 +478,13 @@ private ContainerValue () } } - //------------// - // Descriptor // - //------------// /** - * Descriptor of a SampleSheet. To be kept in memory and save loading. + * A descriptor for a sample sheet. */ @XmlAccessorType(XmlAccessType.FIELD) public static class Descriptor implements Comparable { - //~ Instance fields ------------------------------------------------------------------------ /** Short unique sheet name. */ @XmlAttribute(name = "name") @@ -471,15 +496,27 @@ public static class Descriptor /** Collection of all name aliases, perhaps empty. */ @XmlElement(name = "alias") - private final ArrayList aliases = new ArrayList(); - - //~ Constructors --------------------------------------------------------------------------- + private final ArrayList aliases = new ArrayList<>(); + + /** + * Create descriptor for a sample sheet. + * + * @param name sheet name + * @param hash hash code of related image run table or null + */ public Descriptor (String name, Integer hash) { this(name, hash, EMPTY_LIST); } + /** + * Create descriptor for a sample sheet and its aliases. + * + * @param name sheet name + * @param hash hash code of related image run table or null + * @param aliases other names for the same sheet + */ public Descriptor (String name, Integer hash, List aliases) @@ -494,7 +531,11 @@ private Descriptor () { } - //~ Methods -------------------------------------------------------------------------------- + /** + * Register an alias for the sheet. + * + * @param alias new alias + */ public void addAlias (String alias) { if ((alias != null) && !isAlias(alias)) { @@ -509,11 +550,35 @@ public int compareTo (Descriptor other) return name.compareTo(other.name); } + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + + if (obj instanceof Descriptor) { + return compareTo((Descriptor) obj) == 0; + } + + return false; + } + + /** + * Report the collection of aliases. + * + * @return the sheet aliases, perhaps empty. + */ public List getAliases () { return aliases; } + /** + * Report concatenated aliases. + * + * @return string of aliases + */ public String getAliasesString () { if (aliases.isEmpty()) { @@ -533,11 +598,41 @@ public String getAliasesString () return sb.toString(); } + /** + * Report the sheet name. + * + * @return sheet name + */ public String getName () { return name; } + /** + * Set sheet name. + * + * @param name name for sheet + */ + public void setName (String name) + { + this.name = name; + } + + @Override + public int hashCode () + { + int hash = 7; + hash = (59 * hash) + Objects.hashCode(this.name); + + return hash; + } + + /** + * Check whether the provided name is an alias of this sheet. + * + * @param str provided name + * @return true if so + */ public boolean isAlias (String str) { if (str.equalsIgnoreCase(name)) { @@ -553,11 +648,6 @@ public boolean isAlias (String str) return false; } - public void setName (String name) - { - this.name = name; - } - @Override public String toString () { diff --git a/src/main/org/audiveris/omr/classifier/TrainingMonitor.java b/src/main/org/audiveris/omr/classifier/TrainingMonitor.java index 518b180bf..d050279aa 100644 --- a/src/main/org/audiveris/omr/classifier/TrainingMonitor.java +++ b/src/main/org/audiveris/omr/classifier/TrainingMonitor.java @@ -1,41 +1,56 @@ -//------------------------------------------------------------------------------------------------// -// // -// T r a i n i n g M o n i t o r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.classifier; - -// -//import org.deeplearning4j.optimize.api.IterationListener; -// -/** - * Monitoring interface about the training status of a classifier. - */ -public interface TrainingMonitor // extends IterationListener - -{ - //~ Methods ------------------------------------------------------------------------------------ - - public void epochStarted (int epoch); - - public int getIterationPeriod (); - - public void iterationPeriodDone (int iteration, - double score); -} +//------------------------------------------------------------------------------------------------// +// // +// T r a i n i n g M o n i t o r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.classifier; + +// +//import org.deeplearning4j.optimize.api.IterationListener; +// +/** + * Monitoring interface about the training status of a classifier. + */ +public interface TrainingMonitor // extends IterationListener + +{ + + /** + * Call-back at epoch start. + * + * @param epoch epoch number. + */ + public void epochStarted (int epoch); + + /** + * Report the number of iterations in a period. + * + * @return number of iterations between reporting + */ + public int getIterationPeriod (); + + /** + * Call-back at end of iteration period. + * + * @param iteration iteration number + * @param score current loss value + */ + public void iterationPeriodDone (int iteration, + double score); +} diff --git a/src/main/org/audiveris/omr/classifier/Tribe.java b/src/main/org/audiveris/omr/classifier/Tribe.java index 64e5016fa..fd9a5465a 100644 --- a/src/main/org/audiveris/omr/classifier/Tribe.java +++ b/src/main/org/audiveris/omr/classifier/Tribe.java @@ -41,7 +41,6 @@ @XmlRootElement(name = "tribe") public class Tribe { - //~ Instance fields ---------------------------------------------------------------------------- /** The sample that best defines the tribe underlying symbol. */ @XmlElement @@ -49,13 +48,12 @@ public class Tribe /** Samples that are considered as compatible with best. */ @XmlElement(name = "good") - private final List goods = new ArrayList(); + private final List goods = new ArrayList<>(); /** Samples that must be classified with lower grade than best sample. */ @XmlElement(name = "member") - private final List members = new ArrayList(); + private final List members = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code Tribe} object. * @@ -74,7 +72,11 @@ private Tribe () this.head = null; } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Add a good sample. + * + * @param good the good sample to add + */ public void addGood (Sample good) { if (!goods.contains(good)) { @@ -82,6 +84,11 @@ public void addGood (Sample good) } } + /** + * Add a basic sample. + * + * @param other a plain sample to add + */ public void addOther (Sample other) { if (!members.contains(other)) { @@ -90,6 +97,8 @@ public void addOther (Sample other) } /** + * Report all the good samples. + * * @return the goods */ public List getGoods () @@ -98,6 +107,8 @@ public List getGoods () } /** + * Report the best sample in tribe. + * * @return the best */ public Sample getHead () @@ -106,6 +117,8 @@ public Sample getHead () } /** + * Return the other samples in tribe (apart the best). + * * @return the others */ public List getMembers () diff --git a/src/main/org/audiveris/omr/classifier/TribeList.java b/src/main/org/audiveris/omr/classifier/TribeList.java index 6efe14c6f..cb4363424 100644 --- a/src/main/org/audiveris/omr/classifier/TribeList.java +++ b/src/main/org/audiveris/omr/classifier/TribeList.java @@ -41,6 +41,7 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.stream.XMLStreamException; /** * Value class meant for JAXB. @@ -49,15 +50,12 @@ @XmlRootElement(name = "tribes") class TribeList { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - TribeList.class); + private static final Logger logger = LoggerFactory.getLogger(TribeList.class); /** Un/marshalling context for use with JAXB. */ private static volatile JAXBContext jaxbContext; - //~ Instance fields ---------------------------------------------------------------------------- // Persistent data //---------------- /** Used only to include sheet-name within the written file. */ @@ -66,15 +64,14 @@ class TribeList /** The collection of tribes in sample sheet. */ @XmlElement(name = "tribe") - private final ArrayList tribes = new ArrayList(); + private final ArrayList tribes = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TribeList} object. * * @param sampleSheet the containing sample sheet */ - public TribeList (SampleSheet sampleSheet) + TribeList (SampleSheet sampleSheet) { name = sampleSheet.getDescriptor().getName(); @@ -89,7 +86,6 @@ private TribeList () name = null; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getTribes // //-----------// @@ -115,11 +111,27 @@ public void marshal (Path tribesPath) logger.debug("Marshalling {}", this); Jaxb.marshal(this, tribesPath, getJaxbContext()); logger.info("Stored {}", tribesPath); - } catch (Exception ex) { + } catch (IOException | + JAXBException | + XMLStreamException ex) { logger.error("Error marshalling " + this + " " + ex, ex); } } + //----------------// + // getJaxbContext // + //----------------// + private static JAXBContext getJaxbContext () + throws JAXBException + { + // Lazy creation + if (jaxbContext == null) { + jaxbContext = JAXBContext.newInstance(TribeList.class); + } + + return jaxbContext; + } + //-----------// // unmarshal // //-----------// @@ -135,31 +147,13 @@ static TribeList unmarshal (Path path) { logger.debug("TribeList unmarshalling {}", path); - try { - InputStream is = Files.newInputStream(path, StandardOpenOption.READ); + try (InputStream is = Files.newInputStream(path, StandardOpenOption.READ)) { Unmarshaller um = getJaxbContext().createUnmarshaller(); TribeList tribeList = (TribeList) um.unmarshal(is); - is.close(); - return tribeList; } catch (JAXBException ex) { logger.warn("Error unmarshalling " + path + " " + ex, ex); - return null; } } - - //----------------// - // getJaxbContext // - //----------------// - private static JAXBContext getJaxbContext () - throws JAXBException - { - // Lazy creation - if (jaxbContext == null) { - jaxbContext = JAXBContext.newInstance(TribeList.class); - } - - return jaxbContext; - } } diff --git a/src/main/org/audiveris/omr/classifier/ui/SampleBoard.java b/src/main/org/audiveris/omr/classifier/ui/SampleBoard.java index c3e0e374a..049e24c83 100644 --- a/src/main/org/audiveris/omr/classifier/ui/SampleBoard.java +++ b/src/main/org/audiveris/omr/classifier/ui/SampleBoard.java @@ -57,7 +57,6 @@ public class SampleBoard extends EntityBoard { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -65,7 +64,6 @@ public class SampleBoard private static final String DBL_FORMAT = "%.3f"; // Format for double output - //~ Instance fields ---------------------------------------------------------------------------- /** User controller for samples. */ private final SampleController controller; @@ -104,7 +102,6 @@ public class SampleBoard /** To reassign. */ private final AssignAction assignAction; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SampleBoard} object. * @@ -137,7 +134,6 @@ public SampleBoard (SampleController controller) defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // getFormLayout // //---------------// @@ -147,66 +143,6 @@ protected FormLayout getFormLayout () return Panel.makeFormLayout(6, 3); } - //--------------// - // defineLayout // - //--------------// - /** - * Define the layout for SampleBoard specific fields. - */ - private void defineLayout () - { - final CellConstraints cst = new CellConstraints(); - - // Layout - int r = 1; // ----------------------------- - - JButton removeButton = new JButton(controller.getRemoveAction()); - removeButton.setHorizontalTextPosition(SwingConstants.LEFT); - removeButton.setHorizontalAlignment(SwingConstants.RIGHT); - removeAction.setEnabled(false); - builder.add(removeButton, cst.xyw(5, r, 3)); - - assignButton = new JButton(assignAction); - assignButton.setHorizontalTextPosition(SwingConstants.LEFT); - assignButton.setHorizontalAlignment(SwingConstants.RIGHT); - assignAction.setEnabled(false); - builder.add(assignButton, cst.xyw(9, r, 3)); - - r += 2; // -------------------------------- - - // Shape Icon (start, spans several rows) - builder.add(shapeIcon, cst.xywh(3, r, 1, 9)); - - builder.add(sheetName.getLabel(), cst.xy(1, r)); - builder.add(sheetName.getField(), cst.xyw(3, r, 9)); - - r += 2; // -------------------------------- - - builder.add(shapeField.getLabel(), cst.xy(5, r)); - builder.add(shapeField.getField(), cst.xyw(7, r, 5)); - - r += 2; // -------------------------------- - - builder.add(iLine.getLabel(), cst.xy(5, r)); - builder.add(iLine.getField(), cst.xy(7, r)); - - builder.add(width.getLabel(), cst.xy(9, r)); - builder.add(width.getField(), cst.xy(11, r)); - - r += 2; // -------------------------------- - - builder.add(weight.getLabel(), cst.xy(5, r)); - builder.add(weight.getField(), cst.xy(7, r)); - - builder.add(height.getLabel(), cst.xy(9, r)); - builder.add(height.getField(), cst.xy(11, r)); - - r += 2; // -------------------------------- - - builder.add(pitch.getLabel(), cst.xy(9, r)); - builder.add(pitch.getField(), cst.xy(11, r)); - } - //-----------------------// // handleEntityListEvent // //-----------------------// @@ -276,14 +212,72 @@ protected void handleEntityListEvent (EntityListEvent sampleListEvent) } } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------------// + // defineLayout // + //--------------// + /** + * Define the layout for SampleBoard specific fields. + */ + private void defineLayout () + { + final CellConstraints cst = new CellConstraints(); + + // Layout + int r = 1; // ----------------------------- + + JButton removeButton = new JButton(controller.getRemoveAction()); + removeButton.setHorizontalTextPosition(SwingConstants.LEFT); + removeButton.setHorizontalAlignment(SwingConstants.RIGHT); + removeAction.setEnabled(false); + builder.add(removeButton, cst.xyw(5, r, 3)); + + assignButton = new JButton(assignAction); + assignButton.setHorizontalTextPosition(SwingConstants.LEFT); + assignButton.setHorizontalAlignment(SwingConstants.RIGHT); + assignAction.setEnabled(false); + builder.add(assignButton, cst.xyw(9, r, 3)); + + r += 2; // -------------------------------- + + // Shape Icon (start, spans several rows) + builder.add(shapeIcon, cst.xywh(3, r, 1, 9)); + + builder.add(sheetName.getLabel(), cst.xy(1, r)); + builder.add(sheetName.getField(), cst.xyw(3, r, 9)); + + r += 2; // -------------------------------- + + builder.add(shapeField.getLabel(), cst.xy(5, r)); + builder.add(shapeField.getField(), cst.xyw(7, r, 5)); + + r += 2; // -------------------------------- + + builder.add(iLine.getLabel(), cst.xy(5, r)); + builder.add(iLine.getField(), cst.xy(7, r)); + + builder.add(width.getLabel(), cst.xy(9, r)); + builder.add(width.getField(), cst.xy(11, r)); + + r += 2; // -------------------------------- + + builder.add(weight.getLabel(), cst.xy(5, r)); + builder.add(weight.getField(), cst.xy(7, r)); + + builder.add(height.getLabel(), cst.xy(9, r)); + builder.add(height.getField(), cst.xy(11, r)); + + r += 2; // -------------------------------- + + builder.add(pitch.getLabel(), cst.xy(9, r)); + builder.add(pitch.getField(), cst.xy(11, r)); + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final PixelCount shapeIconHeight = new PixelCount( 70, diff --git a/src/main/org/audiveris/omr/classifier/ui/SampleBrowser.java b/src/main/org/audiveris/omr/classifier/ui/SampleBrowser.java index 9b86da8e6..0f6441b49 100644 --- a/src/main/org/audiveris/omr/classifier/ui/SampleBrowser.java +++ b/src/main/org/audiveris/omr/classifier/ui/SampleBrowser.java @@ -52,7 +52,6 @@ import org.jdesktop.application.Action; import org.jdesktop.application.Application; -import org.jdesktop.application.ApplicationAction; import org.jdesktop.application.ApplicationActionMap; import org.jdesktop.application.ResourceMap; import org.jdesktop.application.SingleFrameApplication; @@ -91,10 +90,8 @@ import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; - import static javax.swing.JSplitPane.HORIZONTAL_SPLIT; import static javax.swing.JSplitPane.VERTICAL_SPLIT; - import javax.swing.ListCellRenderer; import javax.swing.border.EmptyBorder; import javax.swing.border.TitledBorder; @@ -108,12 +105,13 @@ * recorded for classifier training, in order to visually check the correctness of their * assigned shape, and to remove or reassign spurious samples when necessary. *

              - * The user can select sheets, shapes and samples:

                - *
              • For the current sample, detailed characteristics are listed, while the top 5 shape + * The user can select sheets, shapes and samples: + *
                  + *
                • For the current sample, detailed characteristics are listed, while the top 5 shape * evaluations are computed on-line by the shape classifier. - *
                • If the binary image of containing sheet is available, the sample is displayed with its + *
                • If the binary image of containing sheet is available, the sample is displayed with its * context. - *
                • The user is able to remove the sample or to re-assign it to a different shape. + *
                • The user is able to remove the sample or to re-assign it to a different shape. *
                * This class can also be launched as a stand-alone program. * @@ -123,7 +121,6 @@ public class SampleBrowser extends SingleFrameApplication implements ChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SampleBrowser.class); @@ -136,7 +133,6 @@ public class SampleBrowser /** Events that can be published on the local sample service. */ private static final Class[] eventsAllowed = new Class[]{EntityListEvent.class}; - //~ Instance fields ---------------------------------------------------------------------------- /** Repository of training samples. */ private final SampleRepository repository; @@ -167,7 +163,6 @@ public class SampleBrowser /** Controller for sample handling. */ private final SampleController sampleController; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an instance of {@code SampleBrowser}. * @@ -178,7 +173,7 @@ public SampleBrowser (SampleRepository repository) this.repository = repository; sampleContext = new SampleContext(repository); - sampleService = new EntityService("sampleService", null, eventsAllowed); + sampleService = new EntityService<>("sampleService", null, eventsAllowed); sampleContext.connect(sampleService); sampleModel = new SampleModel(repository, sampleService); sampleController = new SampleController(sampleModel); @@ -203,64 +198,6 @@ public SampleBrowser (SampleRepository repository) } } - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // getInstance // - //-------------// - /** - * Give access to the global instance of this class. - * - * @return the SampleBrowser instance - */ - public static synchronized SampleBrowser getInstance () - { - if (INSTANCE == null) { - INSTANCE = new SampleBrowser(SampleRepository.getGlobalInstance()); - } - - return INSTANCE; - } - - //-------------// - // getInstance // - //-------------// - /** - * Give access to a specific book instance of this class. - * - * @param book provided book - * @return the SampleBrowser instance - */ - public static SampleBrowser getInstance (Book book) - { - SampleRepository repo = book.getSampleRepository(); - - return new SampleBrowser(repo); - } - - //------// - // main // - //------// - /** - * Just to allow stand-alone running of this class - * - * @param args not used - */ - public static void main (String... args) - { - standAlone = true; - - // Load repository, with sheet images - SampleRepository repo = SampleRepository.getGlobalInstance(); - repo.loadAllImages(); - - // Set UI Look and Feel - UILookAndFeel.setUI(null); - Locale.setDefault(Locale.ENGLISH); - - // Off we go... - Application.launch(SampleBrowser.class, args); - } - //-----------------// // checkRepository // //-----------------// @@ -276,8 +213,8 @@ public void checkRepository (ActionEvent e) repository.purgeOrphanDescriptors(); - Set conflictings = new LinkedHashSet(); - Set redundants = new LinkedHashSet(); + Set conflictings = new LinkedHashSet<>(); + Set redundants = new LinkedHashSet<>(); repository.checkAllSamples(conflictings, redundants); if (conflictings.isEmpty() && redundants.isEmpty()) { @@ -392,7 +329,7 @@ public void displayAll (Collection samples) connectSelectors(false); // Disable standard triggers: sheets -> shapes -> samples // Select proper Sheets - Set descSet = new LinkedHashSet(); + Set descSet = new LinkedHashSet<>(); for (Sample sample : samples) { descSet.add(repository.getDescriptor(sample)); @@ -411,7 +348,7 @@ public void displayAll (Collection samples) shapeSelector.select(shapeSet); // Populate samples - List sorted = new ArrayList(samples); + List sorted = new ArrayList<>(samples); Collections.sort(sorted, Sample.byShape); // Must be ordered by shape for listing sampleListing.populateWith(sorted); @@ -637,7 +574,7 @@ public void setVisible () @Action public void shrinkRepository (ActionEvent e) { - repository.shrink(2000); + repository.shrink(2_000); } //--------------// @@ -679,6 +616,11 @@ public void stateChanged (ChangeEvent event) //----------------// // validateSheets // //----------------// + /** + * Launch the shape classifier on the selected sheets. + * + * @param e the triggering event + */ @Action public void validateSheets (ActionEvent e) { @@ -716,8 +658,7 @@ protected void ready () { logger.debug("SampleBrowser. 3/ready"); - frame.addWindowListener( - new WindowAdapter() + frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing (WindowEvent e) @@ -749,39 +690,6 @@ protected void startup () show(frame); // Here we go... } - /** - * (Package private) Report the selected shapes - * - * @return the selected shapes - */ - List getSelectedShapes () - { - return shapeSelector.list.getSelectedValuesList(); - } - - /** - * (Package private) Report the selected sheets descriptors - * - * @return the selected sheet descriptors - */ - List getSelectedSheets () - { - return sheetSelector.list.getSelectedValuesList(); - } - - //---------------// - // publishSample // - //---------------// - void publishSample (Sample sample) - { - sampleService.publish( - new EntityListEvent( - this, - SelectionHint.ENTITY_INIT, - MouseMovement.PRESSING, - sample)); - } - //--------------// // buildMenuBar // //--------------// @@ -793,52 +701,37 @@ void publishSample (Sample sample) private JMenuBar buildMenuBar () { JMenuBar menuBar = new JMenuBar(); - JMenu repoMenu = new JMenu(); repoMenu.setName("SampleBrowserRepoMenu"); menuBar.add(repoMenu); - ApplicationActionMap actionMap = OmrGui.getApplication().getContext().getActionMap(this); - // Refresh viewer - repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("refresh"))); - + repoMenu.add(new JMenuItem(actionMap.get("refresh"))); // Load sheet images - repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("loadImages"))); - + repoMenu.add(new JMenuItem(actionMap.get("loadImages"))); repoMenu.addSeparator(); // ----------------------- - // Check for sample duplicates/conflicts - repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("checkRepository"))); - + repoMenu.add(new JMenuItem(actionMap.get("checkRepository"))); if (SampleRepository.USE_TRIBES) { // Check for tribes classification - repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("checkTribes"))); + repoMenu.add(new JMenuItem(actionMap.get("checkTribes"))); } - repoMenu.addSeparator(); // ----------------------- - if (repository.isGlobal()) { // Launch trainer - repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("launchTrainer"))); + repoMenu.add(new JMenuItem(actionMap.get("launchTrainer"))); } else { // Push local repository to global repository - repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("pushRepository"))); + repoMenu.add(new JMenuItem(actionMap.get("pushRepository"))); } - repoMenu.addSeparator(); // ----------------------- - // Save repository - repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("save"))); - + repoMenu.add(new JMenuItem(actionMap.get("save"))); // Export to CSV file - repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("exportFeatures"))); - + repoMenu.add(new JMenuItem(actionMap.get("exportFeatures"))); repoMenu.addSeparator(); // ----------------------- - // Purge sheets - repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("purgeSheets"))); - + repoMenu.add(new JMenuItem(actionMap.get("purgeSheets"))); // // Shrink the whole repository // repoMenu.add(new JMenuItem((ApplicationAction) actionMap.get("shrinkRepository"))); // @@ -867,42 +760,40 @@ private void connectSelectors (boolean bool) * * @param frame the bare frame * @return the populated frame - * */ private JFrame defineLayout (JFrame frame) { frame.setName("SampleBrowserFrame"); // For SAF life cycle - /* - * |- left --||-- center ---|--------------- right ---------------| - * - * +=========++=============+=====================================+ - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . sheet . . | . . . . . . . . shape 1 pane. . . . | - * | . . . . || . . . . . . | . . sample. . . . . . . . . . . . . | - * | . . . . || . selector. | . . . . . . . . shape 2 pane. . . . | - * | . . . . || . . . . . . | . . listing . . . . . . . . . . . . | - * | . . . . ||=============| . . . . . . . . shape 3 pane. . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . sample. . |=====================================| - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . board . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | shape . ||-------------| . . . . . . . . . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | selector|| . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . eval. . . | . . . . sample. . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . board . . | . . . . context . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | - * +=========++=============+=====================================+ - */ + // |- left --||-- center ---|--------------- right ---------------| + // + // +=========++=============+=====================================+ + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . sheet . . | . . . . . . . . shape 1 pane. . . . | + // | . . . . || . . . . . . | . . sample. . . . . . . . . . . . . | + // | . . . . || . selector. | . . . . . . . . shape 2 pane. . . . | + // | . . . . || . . . . . . | . . listing . . . . . . . . . . . . | + // | . . . . ||=============| . . . . . . . . shape 3 pane. . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . sample. . |=====================================| + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . board . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | shape . ||-------------| . . . . . . . . . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | selector|| . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . eval. . . | . . . . sample. . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . board . . | . . . . context . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // | . . . . || . . . . . . | . . . . . . . . . . . . . . . . . . | + // +=========++=============+=====================================+ + // // Left = shapeSelector shapeSelector.setName("shapeSelector"); @@ -911,9 +802,10 @@ private JFrame defineLayout (JFrame frame) boardsPane.addBoard(new SampleBoard(sampleController)); boardsPane.addBoard( new SampleEvaluationBoard(sampleController, BasicClassifier.getInstance())); -// boardsPane.addBoard( -// new SampleEvaluationBoard(sampleController, DeepClassifier.getInstance())); -// + + // boardsPane.addBoard( + // new SampleEvaluationBoard(sampleController, DeepClassifier.getInstance())); + // JSplitPane centerPane = new JSplitPane( VERTICAL_SPLIT, sheetSelector, @@ -962,28 +854,94 @@ private JFrame defineLayout (JFrame frame) return frame; } - //~ Inner Classes ------------------------------------------------------------------------------ + /** + * (Package private) Report the selected shapes + * + * @return the selected shapes + */ + List getSelectedShapes () + { + return shapeSelector.list.getSelectedValuesList(); + } + + /** + * (Package private) Report the selected sheets descriptors + * + * @return the selected sheet descriptors + */ + List getSelectedSheets () + { + return sheetSelector.list.getSelectedValuesList(); + } + + //---------------// + // publishSample // + //---------------// + void publishSample (Sample sample) + { + sampleService.publish( + new EntityListEvent<>( + this, + SelectionHint.ENTITY_INIT, + MouseMovement.PRESSING, + sample)); + } + //-------------// - // TitledPanel // + // getInstance // //-------------// /** - * A panel surrounded by an EmptyBorder and a title. + * Give access to the global instance of this class. + * + * @return the SampleBrowser instance */ - static class TitledPanel - extends Panel + public static synchronized SampleBrowser getInstance () { - //~ Constructors --------------------------------------------------------------------------- - - public TitledPanel (String title) - { - setBorder( - BorderFactory.createTitledBorder( - new EmptyBorder(20, 5, 0, 0), // TLBR - title, - TitledBorder.LEFT, - TitledBorder.TOP)); - this.setInsets(25, 5, 0, 0); + if (INSTANCE == null) { + INSTANCE = new SampleBrowser(SampleRepository.getGlobalInstance()); } + + return INSTANCE; + } + + //-------------// + // getInstance // + //-------------// + /** + * Give access to a specific book instance of this class. + * + * @param book provided book + * @return the SampleBrowser instance + */ + public static SampleBrowser getInstance (Book book) + { + SampleRepository repo = book.getSampleRepository(); + + return new SampleBrowser(repo); + } + + //------// + // main // + //------// + /** + * Just to allow stand-alone running of this class + * + * @param args not used + */ + public static void main (String... args) + { + standAlone = true; + + // Load repository, with sheet images + SampleRepository repo = SampleRepository.getGlobalInstance(); + repo.loadAllImages(); + + // Set UI Look and Feel + UILookAndFeel.setUI(null); + Locale.setDefault(Locale.ENGLISH); + + // Off we go... + Application.launch(SampleBrowser.class, args); } //----------------// @@ -992,7 +950,6 @@ public TitledPanel (String title) private class ClosingAdapter extends WindowAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public void windowClosing (WindowEvent e) @@ -1014,24 +971,112 @@ public void windowClosing (WindowEvent e) } } - //----------// - // Selector // - //----------// + //---------------// + // ShapeSelector // + //---------------// /** - * Class {@code Selector} is the basis for sheet and shape selectors. - * Each selector is made of a list of names, which can be selected and deselected at will. + * Display a list of available shapes (within selected sheets) and let user make a + * selection. */ - private abstract static class Selector - extends TitledPanel - implements ChangeListener + private class ShapeSelector + extends Selector { - //~ Instance fields ------------------------------------------------------------------------ - // The title base for this selector - private final String title; + ShapeSelector () + { + super("Shapes"); + setMinimumSize(new Dimension(100, 0)); + list.setCellRenderer(new ShapeRenderer()); + list.setComponentPopupMenu(new ShapePopup()); + } - // Other entity interested in items selected by this selector - private ChangeListener listener; + @Override + public void stateChanged (ChangeEvent e) + { + // Called from sheetSelector: Populate with shape names found in selected sheets + final EnumSet shapeSet = EnumSet.noneOf(Shape.class); + + for (Descriptor desc : sheetSelector.list.getSelectedValuesList()) { + shapeSet.addAll(repository.getShapes(desc)); + } + + populateWith(shapeSet); + } + + /** + * Popup on selected Shape instances (within selected SampleSheet instances). + */ + private class ShapePopup + extends JPopupMenu + { + + ShapePopup () + { + super("ShapePopup"); + ApplicationActionMap actionMap = OmrGui.getApplication().getContext().getActionMap( + SampleBrowser.this); + add(new JMenuItem(actionMap.get("removeShapes"))); + } + } + } + + //---------------// + // SheetSelector // + //---------------// + /** + * Display a list of available sheets and let user make a selection. + */ + private class SheetSelector + extends Selector + { + + SheetSelector () + { + super("Sheets"); + + list.setComponentPopupMenu(new SheetPopup()); + } + + public List getTestSamples () + { + List descriptors = list.getSelectedValuesList(); + List samples = new ArrayList<>(); + for (Descriptor descriptor : descriptors) { + for (Shape shape : repository.getShapes(descriptor)) { + samples.addAll(repository.getSamples(descriptor.getName(), shape)); + } + } + return samples; + } + + @Override + public void stateChanged (ChangeEvent e) + { + populateWith(repository.getAllDescriptors()); + } + + /** + * Popup on selected SampleSheet instances. + */ + private class SheetPopup + extends JPopupMenu + { + + SheetPopup () + { + super("SheetPopup"); + ApplicationActionMap actionMap = OmrGui.getApplication().getContext().getActionMap( + SampleBrowser.this); + add(new JMenuItem(actionMap.get("removeSheets"))); + add(new JMenuItem(actionMap.get("validateSheets"))); + } + } + } + + private static abstract class Selector + extends TitledPanel + implements ChangeListener + { // Buttons protected final JButton selectAll = new JButton("Select All"); @@ -1039,21 +1084,26 @@ private abstract static class Selector protected final JButton cancelAll = new JButton("Cancel All"); // Underlying list model - protected final DefaultListModel model = new DefaultListModel(); + protected final DefaultListModel model = new DefaultListModel<>(); // Underlying list - protected JList list = new JList(model); + protected JList list = new JList<>(model); // ScrollPane around the list protected JScrollPane scrollPane = new JScrollPane(list); - //~ Constructors --------------------------------------------------------------------------- + // The title base for this selector + private final String title; + + // Other entity interested in items selected by this selector + private ChangeListener listener; + /** * Create a selector. * * @param title label for this selector */ - public Selector (String title) + Selector (String title) { super(title); this.title = title; @@ -1063,8 +1113,7 @@ public Selector (String title) setPreferredSize(new Dimension(180, 200)); // To be informed of mouse (de)selections (not programmatic) - list.addListSelectionListener( - new ListSelectionListener() + list.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged (ListSelectionEvent e) @@ -1076,8 +1125,7 @@ public void valueChanged (ListSelectionEvent e) }); // Same action whatever the subclass: select all items - selectAll.addActionListener( - new ActionListener() + selectAll.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) @@ -1087,8 +1135,7 @@ public void actionPerformed (ActionEvent e) }); // Same action whatever the subclass: deselect all items - cancelAll.addActionListener( - new ActionListener() + cancelAll.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) @@ -1111,7 +1158,6 @@ public void actionPerformed (ActionEvent e) add(buttons, BorderLayout.SOUTH); } - //~ Methods -------------------------------------------------------------------------------- public void populateWith (Collection items) { model.removeAllElements(); @@ -1172,64 +1218,18 @@ protected void update () } } - //---------------// - // ShapeRenderer // - //---------------// - /** - * Render a shape item within the ShapeSelector list. - */ - private static class ShapeRenderer - extends JLabel - implements ListCellRenderer - { - //~ Constructors --------------------------------------------------------------------------- - - public ShapeRenderer () - { - setOpaque(true); - } - - //~ Methods -------------------------------------------------------------------------------- - /** - * Find the image and text corresponding to the provided shape and returns the - * label, ready to be displayed. - */ - @Override - public Component getListCellRendererComponent (JList list, - Shape shape, - int index, - boolean isSelected, - boolean cellHasFocus) - { - if (isSelected) { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); - } else { - setBackground(list.getBackground()); - setForeground(shape.getColor()); - } - - setFont(list.getFont()); - setText(shape.toString()); - setIcon(new FixedWidthIcon(shape.getDecoratedSymbol())); - - return this; - } - } - //-----------------------// // SampleEvaluationBoard // //-----------------------// /** * An evaluation board dedicated to evaluation / reassign of samples. */ - private class SampleEvaluationBoard + private static class SampleEvaluationBoard extends EvaluationBoard { - //~ Constructors --------------------------------------------------------------------------- - public SampleEvaluationBoard (SampleController controller, - Classifier classifier) + SampleEvaluationBoard (SampleController controller, + Classifier classifier) { super( true, @@ -1240,139 +1240,87 @@ public SampleEvaluationBoard (SampleController controller, true); } - //~ Methods -------------------------------------------------------------------------------- @Override protected void evaluate (Glyph glyph) { - if (glyph == null) { - // Blank the output - selector.setEvals(null, null); - } else { + if (glyph instanceof Sample) { + final Sample sample = (Sample) glyph; selector.setEvals( classifier.evaluate( glyph, - ((Sample) glyph).getInterline(), + sample.getInterline(), selector.evalCount(), 0.0, // minGrade Classifier.NO_CONDITIONS), glyph); + } else { + // Blank the output + selector.setEvals(null, null); } } } //---------------// - // ShapeSelector // + // ShapeRenderer // //---------------// /** - * Display a list of available shapes (within selected sheets) and let user make a - * selection. + * Render a shape item within the ShapeSelector list. */ - private class ShapeSelector - extends Selector + private static class ShapeRenderer + extends JLabel + implements ListCellRenderer { - //~ Constructors --------------------------------------------------------------------------- - - public ShapeSelector () - { - super("Shapes"); - setMinimumSize(new Dimension(100, 0)); - list.setCellRenderer(new ShapeRenderer()); - list.setComponentPopupMenu(new ShapePopup()); - } - //~ Methods -------------------------------------------------------------------------------- - @Override - public void stateChanged (ChangeEvent e) + ShapeRenderer () { - // Called from sheetSelector: Populate with shape names found in selected sheets - final EnumSet shapeSet = EnumSet.noneOf(Shape.class); - - for (Descriptor desc : sheetSelector.list.getSelectedValuesList()) { - shapeSet.addAll(repository.getShapes(desc)); - } - - populateWith(shapeSet); + setOpaque(true); } - //~ Inner Classes -------------------------------------------------------------------------- /** - * Popup on selected Shape instances (within selected SampleSheet instances). + * Find the image and text corresponding to the provided shape and returns the + * label, ready to be displayed. */ - private class ShapePopup - extends JPopupMenu + @Override + public Component getListCellRendererComponent (JList list, + Shape shape, + int index, + boolean isSelected, + boolean cellHasFocus) { - //~ Constructors ----------------------------------------------------------------------- - - public ShapePopup () - { - super("ShapePopup"); + if (isSelected) { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } else { + setBackground(list.getBackground()); + setForeground(shape.getColor()); + } - ApplicationActionMap actionMap = OmrGui.getApplication().getContext().getActionMap( - SampleBrowser.this); + setFont(list.getFont()); + setText(shape.toString()); + setIcon(new FixedWidthIcon(shape.getDecoratedSymbol())); - add(new JMenuItem((ApplicationAction) actionMap.get("removeShapes"))); - } + return this; } } - //---------------// - // SheetSelector // - //---------------// + //-------------// + // TitledPanel // + //-------------// /** - * Display a list of available sheets and let user make a selection. + * A panel surrounded by an EmptyBorder and a title. */ - private class SheetSelector - extends Selector + static class TitledPanel + extends Panel { - //~ Constructors --------------------------------------------------------------------------- - - public SheetSelector () - { - super("Sheets"); - - list.setComponentPopupMenu(new SheetPopup()); - } - - //~ Methods -------------------------------------------------------------------------------- - public List getTestSamples () - { - List descriptors = list.getSelectedValuesList(); - List samples = new ArrayList(); - for (Descriptor descriptor : descriptors) { - for (Shape shape : repository.getShapes(descriptor)) { - samples.addAll(repository.getSamples(descriptor.getName(), shape)); - } - } - - return samples; - } - - @Override - public void stateChanged (ChangeEvent e) + TitledPanel (String title) { - populateWith(repository.getAllDescriptors()); - } - - //~ Inner Classes -------------------------------------------------------------------------- - /** - * Popup on selected SampleSheet instances. - */ - private class SheetPopup - extends JPopupMenu - { - //~ Constructors ----------------------------------------------------------------------- - - public SheetPopup () - { - super("SheetPopup"); - - ApplicationActionMap actionMap = OmrGui.getApplication().getContext().getActionMap( - SampleBrowser.this); - - add(new JMenuItem((ApplicationAction) actionMap.get("removeSheets"))); - add(new JMenuItem((ApplicationAction) actionMap.get("validateSheets"))); - } + setBorder(BorderFactory.createTitledBorder( + new EmptyBorder(20, 5, 0, 0), // TLBR + title, + TitledBorder.LEFT, + TitledBorder.TOP)); + this.setInsets(25, 5, 0, 0); } } } diff --git a/src/main/org/audiveris/omr/classifier/ui/SampleContext.java b/src/main/org/audiveris/omr/classifier/ui/SampleContext.java index f12751963..823143f74 100644 --- a/src/main/org/audiveris/omr/classifier/ui/SampleContext.java +++ b/src/main/org/audiveris/omr/classifier/ui/SampleContext.java @@ -58,7 +58,6 @@ public class SampleContext extends ZoomAssembly { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SampleContext.class); @@ -66,7 +65,6 @@ public class SampleContext private static final Point NO_OFFSET = new Point(0, 0); - //~ Instance fields ---------------------------------------------------------------------------- private final SampleRepository repository; private final ContextView contextView; @@ -77,7 +75,6 @@ public class SampleContext private EntityService sampleService; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SampleContext} object. * @@ -92,10 +89,14 @@ public SampleContext (SampleRepository repository) defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // connect // //---------// + /** + * Connect to the provided SampleService + * + * @param sampleService the SampleService to connect to + */ public void connect (EntityService sampleService) { this.sampleService = sampleService; @@ -106,6 +107,9 @@ public void connect (EntityService sampleService) //---------// // refresh // //---------// + /** + * Update the context view with the current sample. + */ public void refresh () { Sample sample = sampleService.getSelectedEntity(); @@ -123,14 +127,12 @@ private void defineLayout () component.add(new ScrollView(contextView).getComponent(), BorderLayout.CENTER); } - //~ Inner Classes ------------------------------------------------------------------------------ //-------------// // ContextView // //-------------// private class ContextView extends RubberPanel { - //~ Instance fields ------------------------------------------------------------------------ /** Current sample, if any. */ private Sample sample; @@ -138,17 +140,12 @@ private class ContextView /** RunTable of sheet image, if any. */ private RunTable sheetTable; - //~ Constructors --------------------------------------------------------------------------- - public ContextView (Zoom zoom, - Rubber rubber) + ContextView (Zoom zoom, + Rubber rubber) { super(zoom, rubber); } - //~ Methods -------------------------------------------------------------------------------- - //---------// - // onEvent // - //---------// @Override public void onEvent (UserEvent event) { @@ -168,9 +165,6 @@ public void onEvent (UserEvent event) } } - //-----------------------// - // handleEntityListEvent // - //-----------------------// /** * Interest in EntityList * @@ -182,9 +176,6 @@ protected void handleEntityListEvent (EntityListEvent listEvent) display(listEvent.getEntity()); } - //---------------------// - // handleLocationEvent // - //---------------------// @Override protected void handleLocationEvent (LocationEvent locationEvent) { @@ -206,14 +197,12 @@ protected void renderItems (Graphics2D g) { if (sample != null) { g.setColor(Color.BLUE); - sample.getRunTable() - .render(g, (sheetTable != null) ? sample.getTopLeft() : NO_OFFSET); + sample.getRunTable().render( + g, + (sheetTable != null) ? sample.getTopLeft() : NO_OFFSET); } } - //---------// - // display // - //---------// private void display (Sample newSample) { sample = newSample; diff --git a/src/main/org/audiveris/omr/classifier/ui/SampleController.java b/src/main/org/audiveris/omr/classifier/ui/SampleController.java index a870c6b4b..125886373 100644 --- a/src/main/org/audiveris/omr/classifier/ui/SampleController.java +++ b/src/main/org/audiveris/omr/classifier/ui/SampleController.java @@ -57,18 +57,15 @@ public class SampleController extends GlyphsController { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SampleController.class); - //~ Instance fields ---------------------------------------------------------------------------- private final SampleRepository repository; private final ApplicationAction removeAction; private final AssignAction assignAction; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SampleController} object. * @@ -84,7 +81,12 @@ public SampleController (SampleModel sampleModel) assignAction = new AssignAction(); } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Assign a new shape to a sample. + * + * @param sample sample at hand + * @param newShape new sample shape + */ public void assignSample (Sample sample, Shape newShape) { @@ -92,14 +94,9 @@ public void assignSample (Sample sample, final SampleSheet sampleSheet = repository.getSampleSheet(sample); // Add new sample - Sample newSample = new Sample( - sample.getLeft(), - sample.getTop(), - sample.getRunTable(), - sample.getInterline(), - sample.getId(), - newShape, - sample.getPitch()); + Sample newSample = new Sample(sample.getLeft(), sample.getTop(), sample.getRunTable(), + sample.getInterline(), sample.getId(), newShape, sample + .getPitch()); sampleModel.addSample(newSample, sampleSheet); // Remove old sample @@ -116,6 +113,11 @@ public Task asyncAssignGlyph (Glyph glyph, return null; } + /** + * Report the Assign action. + * + * @return the Assign action + */ public AssignAction getAssignAction () { return assignAction; @@ -127,11 +129,21 @@ public SelectionService getLocationService () return null; } + /** + * Report the Remove action + * + * @return the Remove action + */ public ApplicationAction getRemoveAction () { return removeAction; } + /** + * Remove the provided sample + * + * @param sample sample to remove + */ public void removeSample (Sample sample) { final SampleModel sampleModel = (SampleModel) model; @@ -141,6 +153,11 @@ public void removeSample (Sample sample) //--------------// // RemoveSample // //--------------// + /** + * Action to remove current sample + * + * @param e triggering event + */ @Action public void removeSample (ActionEvent e) { @@ -149,17 +166,28 @@ public void removeSample (ActionEvent e) SampleController.this.removeSample(sample); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------------// // AssignAction // //--------------// + /** + * Action to assign a new shape for a given sample. + *

                + * This action can be accessed by different ways:

                  + *
                • By pressing the assignButton in {@link SampleBoard}, + *
                • By selecting the assign item in the private class SamplePopup menu, triggered by a + * right-click in {@link SampleListing}. + *
                + */ public class AssignAction extends AbstractAction { - //~ Instance fields ------------------------------------------------------------------------ + /** Popup used by assign button in SampleBoard. */ public JPopupMenu popup = new JPopupMenu(); + /** Menu used by context popup in SampleListing. */ + private final JMenu menu; + ActionListener actionListener = new ActionListener() { @Override @@ -173,16 +201,21 @@ public void actionPerformed (ActionEvent e) } }; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create an {@code AssignAction} object. + */ public AssignAction () { super("Assign to..."); putValue(javax.swing.Action.SHORT_DESCRIPTION, "Assign a new shape"); - ShapeSet.addAllShapes(popup, actionListener); + + // Build menu for SamplePopup + menu = new JMenu("Assign to"); + ShapeSet.addAllShapes(menu, actionListener); + menu.setToolTipText("Assign a new shape"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -194,14 +227,23 @@ public void actionPerformed (ActionEvent e) } } + /** + * Context popup menu. + * + * @return related menu + */ public JMenu getMenu () { - JMenu menu = new JMenu("Assign to"); + return menu; + } - ShapeSet.addAllShapes(menu, actionListener); - menu.setToolTipText("Assign a new shape"); + @Override + public void setEnabled (boolean newValue) + { + super.setEnabled(newValue); - return menu; + // Forward new value to containing menu in popup + menu.setEnabled(newValue); } } } diff --git a/src/main/org/audiveris/omr/classifier/ui/SampleListing.java b/src/main/org/audiveris/omr/classifier/ui/SampleListing.java index 65705aa0b..7fe724346 100644 --- a/src/main/org/audiveris/omr/classifier/ui/SampleListing.java +++ b/src/main/org/audiveris/omr/classifier/ui/SampleListing.java @@ -91,7 +91,6 @@ class SampleListing extends JScrollPane implements ChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SampleListing.class); @@ -105,7 +104,6 @@ class SampleListing private static final Point SAMPLE_OFFSET = new Point(SAMPLE_MARGIN, SAMPLE_MARGIN); - //~ Instance fields ---------------------------------------------------------------------------- private final String title = "Samples"; private final ScrollablePanel scrollablePanel = new ScrollablePanel(); @@ -119,22 +117,20 @@ class SampleListing /** Listener on all shape lists. */ private final ListMouseListener listener = new ListMouseListener(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SampleListing} object. */ - public SampleListing (SampleBrowser browser, - SampleRepository repository) + SampleListing (SampleBrowser browser, + SampleRepository repository) { this.browser = browser; this.repository = repository; - setBorder( - BorderFactory.createTitledBorder( - new EmptyBorder(20, 5, 0, 0), // TLBR - title, - TitledBorder.LEFT, - TitledBorder.TOP)); + setBorder(BorderFactory.createTitledBorder( + new EmptyBorder(20, 5, 0, 0), // TLBR + title, + TitledBorder.LEFT, + TitledBorder.TOP)); scrollablePanel.setLayout(new BoxLayout(scrollablePanel, BoxLayout.Y_AXIS)); scrollablePanel.setComponentPopupMenu(new SamplePopup()); @@ -144,16 +140,15 @@ public SampleListing (SampleBrowser browser, setAlignmentX(LEFT_ALIGNMENT); } - //~ Methods ------------------------------------------------------------------------------------ @Override public void stateChanged (ChangeEvent e) { // Called from shapeSelector: Gather all samples of selected shapes in selected sheets - final List allSamples = new ArrayList(); + final List allSamples = new ArrayList<>(); final List descriptors = browser.getSelectedSheets(); for (Shape shape : browser.getSelectedShapes()) { - final ArrayList shapeSamples = new ArrayList(); + final ArrayList shapeSamples = new ArrayList<>(); for (SheetContainer.Descriptor desc : descriptors) { shapeSamples.addAll(repository.getSamples(desc.getName(), shape)); @@ -167,6 +162,44 @@ public void stateChanged (ChangeEvent e) populateWith(allSamples); } + /** + * Report the ShapePane instance that relates to the provided shape. + * + * @param shape provided shape + * @return related ShapePane, or null + */ + private ShapePane getShapePane (Shape shape) + { + for (Component comp : scrollablePanel.getComponents()) { + ShapePane shapePane = (ShapePane) comp; + + if (shapePane.getShape() == shape) { + return shapePane; + } + } + + return null; + } + + /** + * Sort the ShapePane by the provided comparator + * + * @param comparator the provided sample comparator + */ + private void sortBy (Comparator comparator) + { + final Sample currentSample = (Sample) browser.getSampleController().getGlyphService() + .getSelectedEntity(); + final ShapePane shapePane = getShapePane(currentSample.getShape()); + final List samples = Collections.list(shapePane.model.elements()); + Collections.sort(samples, comparator); + shapePane.model.clear(); + + for (Sample sample : samples) { + shapePane.model.addElement(sample); + } + } + /** * Add a sample to the listing, only if there is already a ShapePane for the shape. * @@ -194,7 +227,7 @@ void populateWith (List samples) // Rebuild ShapePane instances as needed Shape currentShape = null; - List shapeSamples = new ArrayList(); + List shapeSamples = new ArrayList<>(); for (Sample sample : samples) { final Shape shape = sample.getShape(); @@ -241,192 +274,6 @@ void removeSample (Sample sample) } } - /** - * Report the ShapePane instance that relates to the provided shape. - * - * @param shape provided shape - * @return related ShapePane, or null - */ - private ShapePane getShapePane (Shape shape) - { - for (Component comp : scrollablePanel.getComponents()) { - ShapePane shapePane = (ShapePane) comp; - - if (shapePane.getShape() == shape) { - return shapePane; - } - } - - return null; - } - - /** - * Sort the ShapePane by the provided comparator - * - * @param comparator the provided sample comparator - */ - private void sortBy (Comparator comparator) - { - final Sample currentSample = (Sample) browser.getSampleController().getGlyphService() - .getSelectedEntity(); - final ShapePane shapePane = getShapePane(currentSample.getShape()); - final List samples = Collections.list(shapePane.model.elements()); - Collections.sort(samples, comparator); - shapePane.model.clear(); - - for (Sample sample : samples) { - shapePane.model.addElement(sample); - } - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //----------------// - // SampleRenderer // - //----------------// - /** - * Render a sample cell within a ShapePane. - */ - private static class SampleRenderer - extends JPanel - implements ListCellRenderer - { - //~ Instance fields ------------------------------------------------------------------------ - - /** The sample being rendered. */ - private Sample sample; - - //~ Constructors --------------------------------------------------------------------------- - public SampleRenderer (Dimension maxDimension) - { - setOpaque(true); - setPreferredSize( - new Dimension( - maxDimension.width + (2 * SAMPLE_MARGIN), - maxDimension.height + (2 * SAMPLE_MARGIN))); - setBorder(SAMPLE_BORDER); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public Component getListCellRendererComponent (JList list, - Sample sample, - int index, - boolean isSelected, - boolean cellHasFocus) - { - if (isSelected) { - setBackground(list.getSelectionBackground()); - } else { - setBackground(sample.isSymbol() ? SYMBOL_BACKGROUND : SAMPLE_BACKGROUND); - } - - this.sample = sample; - - return this; - } - - @Override - protected void paintComponent (Graphics g) - { - super.paintComponent(g); // Paint background - - RunTable table = sample.getRunTable(); - g.translate(SAMPLE_OFFSET.x, SAMPLE_OFFSET.y); - - // Draw the (properly scaled) run table over a white rectangle of same bounds - final double ratio = (double) STANDARD_INTERLINE / sample.getInterline(); - Graphics2D g2 = (Graphics2D) g.create(); - g2.scale(ratio, ratio); - - g2.setColor(Color.WHITE); - g2.fillRect(0, 0, table.getWidth(), table.getHeight()); - - g2.setColor(Color.BLACK); - table.render(g2, new Point(0, 0)); - - g2.dispose(); - - g.translate(-SAMPLE_OFFSET.x, -SAMPLE_OFFSET.y); - } - } - - //-----------------// - // ScrollablePanel // - //-----------------// - private static class ScrollablePanel - extends JPanel - implements Scrollable - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public Dimension getPreferredScrollableViewportSize () - { - return getPreferredSize(); - } - - /** - * Returns the distance to scroll to expose the next or previous block. - *

                - * For JList: - *

                  - *
                • if scrolling down, returns the distance to scroll so that the last - * visible element becomes the first completely visible element - *
                • if scrolling up, returns the distance to scroll so that the first - * visible element becomes the last completely visible element - *
                • returns {@code visibleRect.height} if the list is empty - *
                - *

                - * For us: - *

                - * "Element" could be the next/previous shapePane? - * - * @param visibleRect the view area visible within the viewport - * @param orientation {@code SwingConstants.HORIZONTAL} or {@code SwingConstants.VERTICAL} - * @param direction less or equal to zero to scroll up, greater than zero for down - * @return the "block" increment for scrolling in the specified direction; always positive - */ - @Override - public int getScrollableBlockIncrement (Rectangle visibleRect, - int orientation, - int direction) - { - if (orientation == SwingConstants.HORIZONTAL) { - return visibleRect.width; - } else { - return visibleRect.height; - } - } - - @Override - public boolean getScrollableTracksViewportHeight () - { - return false; - } - - @Override - public boolean getScrollableTracksViewportWidth () - { - return true; - } - - /** - * Returns the distance to scroll to expose the next or previous row. - * - * @param visibleRect the view area visible within the viewport - * @param orientation {@code SwingConstants.HORIZONTAL} or {@code SwingConstants.VERTICAL} - * @param direction less or equal to zero to scroll up, greater than zero for down - * @return the "unit" increment for scrolling in the specified direction; always positive - */ - @Override - public int getScrollableUnitIncrement (Rectangle visibleRect, - int orientation, - int direction) - { - return 40; // Minimum cell height. TODO: Could be improved. - } - } - //-------------------// // ListMouseListener // //-------------------// @@ -435,11 +282,9 @@ private class ListMouseListener extends MouseInputAdapter implements ListSelectionListener { - //~ Instance fields ------------------------------------------------------------------------ boolean alt; // True if Alt key is pressed down - //~ Methods -------------------------------------------------------------------------------- @Override public void mousePressed (MouseEvent e) { @@ -503,7 +348,7 @@ public void valueChanged (ListSelectionEvent e) */ private void checkAlternative (Sample sample) { - List alternatives = new ArrayList(); + List alternatives = new ArrayList<>(); if (sample.getShape() == Shape.CLUTTER) { final Rectangle box = sample.getBounds(); @@ -514,9 +359,8 @@ private void checkAlternative (Sample sample) for (Sample alternative : sampleSheet.getSamples(shape)) { Rectangle common = alternative.getBounds().intersection(box); - if (!common.isEmpty() - && (common.width >= (box.width / 2)) - && (common.height >= (box.height / 2))) { + if (!common.isEmpty() && (common.width >= (box.width / 2)) + && (common.height >= (box.height / 2))) { logger.debug("alternative: {}", alternative); alternatives.add(alternative); } @@ -554,18 +398,14 @@ private void checkAlternative (Sample sample) private class SamplePopup extends JPopupMenu { - //~ Instance fields ------------------------------------------------------------------------ - private JMenu assignMenu; - - //~ Constructors --------------------------------------------------------------------------- - public SamplePopup () + SamplePopup () { super("SamplePopup"); add(new JMenuItem(browser.getSampleController().getRemoveAction())); - add(assignMenu = browser.getSampleController().getAssignAction().getMenu()); + add(browser.getSampleController().getAssignAction().getMenu()); final JMenu sortMenu = new JMenu("Sort by"); sortMenu.add(new JMenuItem(new SortByWidthAction())); @@ -574,12 +414,6 @@ public SamplePopup () sortMenu.add(new JMenuItem(new SortByGradeAction())); add(sortMenu); } - - //~ Methods -------------------------------------------------------------------------------- - public void setAssignEnabled (boolean bool) - { - assignMenu.setEnabled(bool); - } } //-----------// @@ -591,24 +425,22 @@ public void setAssignEnabled (boolean bool) private class ShapePane extends SampleBrowser.TitledPanel { - //~ Instance fields ------------------------------------------------------------------------ private final Shape shape; - private final DefaultListModel model = new DefaultListModel(); + private final DefaultListModel model = new DefaultListModel<>(); /** Underlying list of all samples for the shape. */ - private final JList list = new JList(model); + private final JList list = new JList<>(model); - //~ Constructors --------------------------------------------------------------------------- /** * Build a ShapePane instance for the provided shape. * * @param shape provided shape * @param samples all samples (within selected sheets) for that shape */ - public ShapePane (Shape shape, - List samples) + ShapePane (Shape shape, + List samples) { super(shape + " (" + samples.size() + ")"); this.shape = shape; @@ -628,8 +460,7 @@ public ShapePane (Shape shape, list.setCellRenderer(new SampleRenderer(maxDimensionOf(samples))); // Specific left/right keys to go through the whole list (and not only the current row) - list.addKeyListener( - new KeyAdapter() + list.addKeyListener(new KeyAdapter() { @Override public void keyPressed (KeyEvent ke) @@ -668,7 +499,6 @@ public void keyPressed (KeyEvent ke) list.setComponentPopupMenu(null); } - //~ Methods -------------------------------------------------------------------------------- public Shape getShape () { return shape; @@ -721,14 +551,12 @@ private Dimension maxDimensionOf (List samples) (int) Math.ceil(h * STANDARD_INTERLINE)); } - //~ Inner Classes -------------------------------------------------------------------------- /** * Action to remove the selected sample in this ShapePane. */ private class RemoveAction extends AbstractAction { - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -748,27 +576,23 @@ public void actionPerformed (ActionEvent e) private class SortByGradeAction extends AbstractAction { - //~ Instance fields ------------------------------------------------------------------------ final Classifier classifier = ShapeClassifier.getInstance(); - //~ Constructors --------------------------------------------------------------------------- - public SortByGradeAction () + SortByGradeAction () { super("Grade"); putValue(SHORT_DESCRIPTION, "Sort items by grade in this ShapePane"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { // To avoid repetitive grade computing, we save grade into GradedSample entities - final Sample currentSample = (Sample) browser.getSampleController() - .getGlyphService() + final Sample currentSample = (Sample) browser.getSampleController().getGlyphService() .getSelectedEntity(); final ShapePane shapePane = getShapePane(currentSample.getShape()); - final List list = new ArrayList(); + final List list = new ArrayList<>(); logger.info("Computing grades..."); @@ -782,40 +606,16 @@ public void actionPerformed (ActionEvent e) } logger.info("All grades computed."); - Collections.sort(list); + + Collections.sort(list, GradedSample.byReverseGrade); logger.info("Samples sorted."); + shapePane.model.clear(); for (GradedSample gradedSample : list) { shapePane.model.addElement(gradedSample.sample); } } - - //~ Inner Classes -------------------------------------------------------------------------- - private class GradedSample - implements Comparable - { - //~ Instance fields -------------------------------------------------------------------- - - final double grade; - - final Sample sample; - - //~ Constructors ----------------------------------------------------------------------- - public GradedSample (double grade, - Sample sample) - { - this.grade = grade; - this.sample = sample; - } - - //~ Methods ---------------------------------------------------------------------------- - @Override - public int compareTo (GradedSample that) - { - return Double.compare(that.grade, this.grade); // By decreasing grade - } - } } /** @@ -824,15 +624,13 @@ public int compareTo (GradedSample that) private class SortByHeightAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public SortByHeightAction () + SortByHeightAction () { super("Height"); putValue(SHORT_DESCRIPTION, "Sort items by height in this ShapePane"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -846,15 +644,13 @@ public void actionPerformed (ActionEvent e) private class SortByWeightAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public SortByWeightAction () + SortByWeightAction () { super("Weight"); putValue(SHORT_DESCRIPTION, "Sort items by weight in this ShapePane"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -868,19 +664,188 @@ public void actionPerformed (ActionEvent e) private class SortByWidthAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public SortByWidthAction () + SortByWidthAction () { super("Width"); putValue(SHORT_DESCRIPTION, "Sort items by width in this ShapePane"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { sortBy(Sample.byNormalizedWidth); } } + + //--------------// + // GradedSample // + //--------------// + private static class GradedSample + { + + public static final Comparator byReverseGrade = new Comparator() + { + @Override + public int compare (GradedSample o1, + GradedSample o2) + { + return Double.compare(o2.grade, o1.grade); // By decreasing grade + } + }; + + final double grade; + + final Sample sample; + + GradedSample (double grade, + Sample sample) + { + this.grade = grade; + this.sample = sample; + } + } + + //----------------// + // SampleRenderer // + //----------------// + /** + * Render a sample cell within a ShapePane. + */ + private static class SampleRenderer + extends JPanel + implements ListCellRenderer + { + + /** The sample being rendered. */ + private Sample sample; + + SampleRenderer (Dimension maxDimension) + { + setOpaque(true); + setPreferredSize( + new Dimension( + maxDimension.width + (2 * SAMPLE_MARGIN), + maxDimension.height + (2 * SAMPLE_MARGIN))); + setBorder(SAMPLE_BORDER); + } + + @Override + public Component getListCellRendererComponent (JList list, + Sample sample, + int index, + boolean isSelected, + boolean cellHasFocus) + { + if (isSelected) { + setBackground(list.getSelectionBackground()); + } else { + setBackground(sample.isSymbol() ? SYMBOL_BACKGROUND : SAMPLE_BACKGROUND); + } + + this.sample = sample; + + return this; + } + + @Override + protected void paintComponent (Graphics g) + { + super.paintComponent(g); // Paint background + + RunTable table = sample.getRunTable(); + g.translate(SAMPLE_OFFSET.x, SAMPLE_OFFSET.y); + + // Draw the (properly scaled) run table over a white rectangle of same bounds + final double ratio = (double) STANDARD_INTERLINE / sample.getInterline(); + Graphics2D g2 = (Graphics2D) g.create(); + g2.scale(ratio, ratio); + + g2.setColor(Color.WHITE); + g2.fillRect(0, 0, table.getWidth(), table.getHeight()); + + g2.setColor(Color.BLACK); + table.render(g2, new Point(0, 0)); + + g2.dispose(); + + g.translate(-SAMPLE_OFFSET.x, -SAMPLE_OFFSET.y); + } + } + + //-----------------// + // ScrollablePanel // + //-----------------// + private static class ScrollablePanel + extends JPanel + implements Scrollable + { + + @Override + public Dimension getPreferredScrollableViewportSize () + { + return getPreferredSize(); + } + + /** + * Returns the distance to scroll to expose the next or previous block. + *

                + * For JList: + *

                  + *
                • if scrolling down, returns the distance to scroll so that the last + * visible element becomes the first completely visible element + *
                • if scrolling up, returns the distance to scroll so that the first + * visible element becomes the last completely visible element + *
                • returns {@code visibleRect.height} if the list is empty + *
                + *

                + * For us: + *

                + * "Element" could be the next/previous shapePane? + * + * @param visibleRect the view area visible within the viewport + * @param orientation {@code SwingConstants.HORIZONTAL} or {@code SwingConstants.VERTICAL} + * @param direction less or equal to zero to scroll up, greater than zero for down + * @return the "block" increment for scrolling in the specified direction; always positive + */ + @Override + public int getScrollableBlockIncrement (Rectangle visibleRect, + int orientation, + int direction) + { + if (orientation == SwingConstants.HORIZONTAL) { + return visibleRect.width; + } else { + return visibleRect.height; + } + } + + @Override + public boolean getScrollableTracksViewportHeight () + { + return false; + } + + @Override + public boolean getScrollableTracksViewportWidth () + { + return true; + } + + /** + * Returns the distance to scroll to expose the next or previous row. + * + * @param visibleRect the view area visible within the viewport + * @param orientation {@code SwingConstants.HORIZONTAL} or {@code SwingConstants.VERTICAL} + * @param direction less or equal to zero to scroll up, greater than zero for down + * @return the "unit" increment for scrolling in the specified direction; always positive + */ + @Override + public int getScrollableUnitIncrement (Rectangle visibleRect, + int orientation, + int direction) + { + return 40; // Minimum cell height. TODO: Could be improved. + } + } } diff --git a/src/main/org/audiveris/omr/classifier/ui/SampleMenu.java b/src/main/org/audiveris/omr/classifier/ui/SampleMenu.java index 841060268..cbbaa3902 100644 --- a/src/main/org/audiveris/omr/classifier/ui/SampleMenu.java +++ b/src/main/org/audiveris/omr/classifier/ui/SampleMenu.java @@ -49,18 +49,15 @@ public class SampleMenu extends SeparableMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SampleMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Containing sheet. */ private final Sheet sheet; /** Selected glyph. */ private final Glyph glyph; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SampleMenu} object. * @@ -76,7 +73,6 @@ public SampleMenu (Glyph glyph, populateMenu(); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getGlyph // //----------// @@ -133,14 +129,12 @@ private void populateMenu () add(new SelectMenu()); } - //~ Inner Classes ------------------------------------------------------------------------------ //------------// // AssignMenu // //------------// private class AssignMenu extends JMenu { - //~ Instance fields ------------------------------------------------------------------------ private final ActionListener listener = new ActionListener() { @@ -153,15 +147,13 @@ public void actionPerformed (ActionEvent e) } }; - //~ Constructors --------------------------------------------------------------------------- - public AssignMenu (Set shapes) + AssignMenu (Set shapes) { super("Assign sample"); populate(shapes); } - //~ Methods -------------------------------------------------------------------------------- private void populate (Set shapes) { for (Shape shape : shapes) { @@ -178,30 +170,26 @@ private void populate (Set shapes) private class SelectMenu extends JMenu { - //~ Constructors --------------------------------------------------------------------------- - public SelectMenu () + SelectMenu () { super("Select sample"); populate(); } - //~ Methods -------------------------------------------------------------------------------- private void populate () { - ShapeSet.addAllShapes( - this, - new ActionListener() - { - @Override - public void actionPerformed (ActionEvent e) - { - JMenuItem source = (JMenuItem) e.getSource(); - Shape shape = Shape.valueOf(source.getText()); - addSample(shape); - } - }); + ShapeSet.addAllShapes(this, new ActionListener() + { + @Override + public void actionPerformed (ActionEvent e) + { + JMenuItem source = (JMenuItem) e.getSource(); + Shape shape = Shape.valueOf(source.getText()); + addSample(shape); + } + }); } } } diff --git a/src/main/org/audiveris/omr/classifier/ui/SampleModel.java b/src/main/org/audiveris/omr/classifier/ui/SampleModel.java index c0fa0360a..438f3a88f 100644 --- a/src/main/org/audiveris/omr/classifier/ui/SampleModel.java +++ b/src/main/org/audiveris/omr/classifier/ui/SampleModel.java @@ -36,11 +36,9 @@ public class SampleModel extends GlyphsModel { - //~ Instance fields ---------------------------------------------------------------------------- private final SampleRepository repository; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SampleModel} object. * @@ -54,7 +52,12 @@ public SampleModel (SampleRepository repository, this.repository = repository; } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Add a sample. + * + * @param sample the sample to add + * @param sampleSheet the containing sheet + */ public void addSample (Sample sample, SampleSheet sampleSheet) { @@ -62,6 +65,8 @@ public void addSample (Sample sample, } /** + * Report the underlying repository + * * @return the repository */ public SampleRepository getRepository () @@ -69,6 +74,11 @@ public SampleRepository getRepository () return repository; } + /** + * Remove a sample. + * + * @param sample the sample to remove + */ public void removeSample (Sample sample) { repository.removeSample(sample); diff --git a/src/main/org/audiveris/omr/classifier/ui/SelectionPanel.java b/src/main/org/audiveris/omr/classifier/ui/SelectionPanel.java index de1698211..95841a4c1 100644 --- a/src/main/org/audiveris/omr/classifier/ui/SelectionPanel.java +++ b/src/main/org/audiveris/omr/classifier/ui/SelectionPanel.java @@ -34,7 +34,6 @@ import org.audiveris.omr.classifier.SampleSource; import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.glyph.ShapeSet; import org.audiveris.omr.ui.Colors; import org.audiveris.omr.ui.field.LLabel; import org.audiveris.omr.ui.util.Panel; @@ -69,13 +68,11 @@ class SelectionPanel implements SampleSource, SampleRepository.LoadListener, ChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(SelectionPanel.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Swing component. */ private final Panel component; @@ -109,11 +106,10 @@ class SelectionPanel /** Sample collection for testing. */ private List tests; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SelectionPanel object. */ - public SelectionPanel () + SelectionPanel () { component = new Panel(); component.setNoInsets(); @@ -134,15 +130,6 @@ public SelectionPanel () } } - //~ Methods ------------------------------------------------------------------------------------ - //------------------------// - // getMinShapeSampleCount // - //------------------------// - public static int getMinShapeSampleCount () - { - return constants.minShapeSampleCount.getValue(); - } - //--------------// // getComponent // //--------------// @@ -187,8 +174,8 @@ public synchronized List getTrainSamples () { if (trains == null) { progressBar.setValue(0); - trains = new ArrayList(); - tests = new ArrayList(); + trains = new ArrayList<>(); + tests = new ArrayList<>(); if (!repository.isLoaded()) { repository.loadRepository(this); @@ -304,75 +291,17 @@ private void setTotalSamples (int total) nbRepoSamples.setText(Integer.toString(total)); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Integer maxShapeSampleCount = new Constant.Integer( - "samples", - 100, - "Maximum sample count per shape for training"); - - private final Constant.Integer minShapeSampleCount = new Constant.Integer( - "samples", - 10, - "Minimum sample count per shape for training"); - } - - //--------------// - // GradedSample // - //--------------// - /** - * Handle a sample together with its grade. - */ - private static class GradedSample + //------------------------// + // getMinShapeSampleCount // + //------------------------// + public static int getMinShapeSampleCount () { - //~ Static fields/initializers ------------------------------------------------------------- - - /** For comparing GradedSample instances in reverse grade order. */ - static final Comparator reverseGradeComparator = new Comparator() - { - @Override - public int compare (GradedSample gs1, - GradedSample gs2) - { - return Double.compare(gs2.grade, gs1.grade); - } - }; - - //~ Instance fields ------------------------------------------------------------------------ - final Sample sample; - - final double grade; - - //~ Constructors --------------------------------------------------------------------------- - public GradedSample (Sample sample, - double grade) - { - this.sample = sample; - this.grade = grade; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return "GradedSample{" + sample + " " + grade + "}"; - } + return constants.minShapeSampleCount.getValue(); } - //-------------// - // ParamAction // - //-------------// private class ParamAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- // Purpose is just to read and remember the data from the various input fields. // Triggered when user presses Enter in one of these fields. @@ -382,28 +311,29 @@ public void actionPerformed (ActionEvent e) inputParams(); displayParams(); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } - //--------------// - // SelectAction // - //--------------// private class SelectAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public SelectAction () + SelectAction () { super("Select"); putValue(Action.SHORT_DESCRIPTION, "Build samples selection"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { - executor.execute( - new Runnable() + executor.execute(new Runnable() { @Override public void run () @@ -416,28 +346,28 @@ public void run () } }); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } - //-------------// - // StoreAction // - //-------------// private class StoreAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public StoreAction () + StoreAction () { super("Store"); putValue(Action.SHORT_DESCRIPTION, "Store train/test selections as .csv files"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { - System.out.println(ShapeSet.getPhysicalShapeNamesString()); - GlyphDescriptor imgDesc = new ImgGlyphDescriptor(); imgDesc.export("train", getTrainSamples(), true); imgDesc.export("test", getTestSamples(), false); @@ -446,5 +376,70 @@ public void actionPerformed (ActionEvent e) mixDesc.export("train", getTrainSamples(), true); mixDesc.export("test", getTestSamples(), false); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Integer maxShapeSampleCount = new Constant.Integer( + "samples", + 100, + "Maximum sample count per shape for training"); + + private final Constant.Integer minShapeSampleCount = new Constant.Integer( + "samples", + 10, + "Minimum sample count per shape for training"); } + + //--------------// + // GradedSample // + //--------------// + /** + * Handle a sample together with its grade. + */ + private static class GradedSample + { + + /** For comparing GradedSample instances in reverse grade order. */ + static final Comparator reverseGradeComparator + = new Comparator() + { + @Override + public int compare (GradedSample gs1, + GradedSample gs2) + { + return Double.compare(gs2.grade, gs1.grade); + } + }; + + final Sample sample; + + final double grade; + + GradedSample (Sample sample, + double grade) + { + this.sample = sample; + this.grade = grade; + } + + @Override + public String toString () + { + return "GradedSample{" + sample + " " + grade + "}"; + } + } + } diff --git a/src/main/org/audiveris/omr/classifier/ui/ShapeMenu.java b/src/main/org/audiveris/omr/classifier/ui/ShapeMenu.java index 322dd799b..ac938194a 100644 --- a/src/main/org/audiveris/omr/classifier/ui/ShapeMenu.java +++ b/src/main/org/audiveris/omr/classifier/ui/ShapeMenu.java @@ -1,130 +1,148 @@ -//------------------------------------------------------------------------------------------------// -// // -// S h a p e M e n u // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.classifier.ui; - -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.glyph.ShapeSet; -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.ui.util.SeparableMenu; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenu; -import javax.swing.JMenuItem; - -/** - * Class {@code ShapeMenu} is a menu dedicated to assigning an inter to a glyph. - * - * @author Hervé Bitteur - */ -public class ShapeMenu - extends SeparableMenu -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(ShapeMenu.class); - - //~ Instance fields ---------------------------------------------------------------------------- - /** Containing sheet. */ - private final Sheet sheet; - - /** Selected glyph. */ - private final Glyph glyph; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code ShapeMenu} object. - * - * @param glyph the selected glyph - * @param sheet the containing sheet - */ - public ShapeMenu (Glyph glyph, - Sheet sheet) - { - this.sheet = sheet; - this.glyph = glyph; - - populateMenu(); - } - - //~ Methods ------------------------------------------------------------------------------------ - //----------// - // getGlyph // - //----------// - /** - * @return the glyph - */ - public Glyph getGlyph () - { - return glyph; - } - - //--------------// - // populateMenu // - //--------------// - private void populateMenu () - { - setText(Integer.toString(glyph.getId())); - - // Manual shape selection - add(new AssignMenu()); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //------------// - // AssignMenu // - //------------// - private class AssignMenu - extends JMenu - { - //~ Constructors --------------------------------------------------------------------------- - - public AssignMenu () - { - super("Assign"); - - populate(); - } - - //~ Methods -------------------------------------------------------------------------------- - private void populate () - { - ShapeSet.addAllShapes( - this, - new ActionListener() - { - @Override - public void actionPerformed (ActionEvent e) - { - JMenuItem source = (JMenuItem) e.getSource(); - Shape shape = Shape.valueOf(source.getText()); - sheet.getInterController().assignGlyph(glyph, shape); - } - }); - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// S h a p e M e n u // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.classifier.ui; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.glyph.ShapeSet; +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.ui.util.SeparableMenu; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +/** + * Class {@code ShapeMenu} is a menu dedicated to assigning an inter to a glyph. + * + * @author Hervé Bitteur + */ +public class ShapeMenu + extends SeparableMenu +{ + + private static final Logger logger = LoggerFactory.getLogger(ShapeMenu.class); + + /** Containing sheet. */ + private final Sheet sheet; + + /** Selected glyph. */ + private final Glyph glyph; + + private final ActionListener shapeListener; + + /** + * Creates a new {@code ShapeMenu} object. + * + * @param glyph the selected glyph + * @param sheet the containing sheet + */ + public ShapeMenu (final Glyph glyph, + final Sheet sheet) + { + this.sheet = sheet; + this.glyph = glyph; + + shapeListener = new ActionListener() + { + @Override + public void actionPerformed (ActionEvent e) + { + JMenuItem source = (JMenuItem) e.getSource(); + Shape shape = Shape.valueOf(source.getText()); + sheet.getInterController().assignGlyph(glyph, shape); + } + }; + + populateMenu(); + } + + //----------// + // getGlyph // + //----------// + /** + * @return the glyph + */ + public Glyph getGlyph () + { + return glyph; + } + + //-----------------// + // addRecentShapes // + //-----------------// + private void addRecentShapes () + { + List shapes = sheet.getSymbolsEditor().getShapeBoard().getHistory(); + + if (!shapes.isEmpty()) { + for (Shape shape : shapes) { + JMenuItem menuItem = new JMenuItem(shape.toString(), shape.getDecoratedSymbol()); + menuItem.setToolTipText(shape.getDescription()); + menuItem.addActionListener(shapeListener); + add(menuItem); + } + + addSeparator(); + } + } + + //--------------// + // populateMenu // + //--------------// + private void populateMenu () + { + setText(Integer.toString(glyph.getId())); + + // Convenient assignment to most recent shapes + addRecentShapes(); + + // Manual shape selection + add(new AssignMenu()); + } + + //------------// + // AssignMenu // + //------------// + private class AssignMenu + extends JMenu + { + + AssignMenu () + { + super("Assign"); + + populate(); + } + + private void populate () + { + ShapeSet.addAllShapes(this, shapeListener); + } + } +} diff --git a/src/main/org/audiveris/omr/classifier/ui/Trainer.java b/src/main/org/audiveris/omr/classifier/ui/Trainer.java index 146cb2d3a..2d717ede6 100644 --- a/src/main/org/audiveris/omr/classifier/ui/Trainer.java +++ b/src/main/org/audiveris/omr/classifier/ui/Trainer.java @@ -73,12 +73,14 @@ public class Trainer extends SingleFrameApplication { - //~ Static fields/initializers ----------------------------------------------------------------- + // Don't move this statement + // @formatter:off static { // We need class WellKnowns to be elaborated before anything else (when in standalone mode) WellKnowns.ensureLoaded(); } + // @formatter:on private static final Logger logger = LoggerFactory.getLogger(Trainer.class); @@ -88,12 +90,6 @@ public class Trainer /** Stand-alone run (vs part of Audiveris). */ private static boolean standAlone = false; - /** Standard width for labels in DLUs. */ - static final String LABEL_WIDTH = "50dlu"; - - /** Standard width for fields/buttons in DLUs. */ - static final String FIELD_WIDTH = "30dlu"; - /** An adapter triggered on window closing. */ private static final WindowAdapter windowCloser = new WindowAdapter() { @@ -108,14 +104,18 @@ public void windowClosing (WindowEvent e) } }; - //~ Instance fields ---------------------------------------------------------------------------- + /** Standard width for labels in DLUs. */ + static final String LABEL_WIDTH = "50dlu"; + + /** Standard width for fields/buttons in DLUs. */ + static final String FIELD_WIDTH = "30dlu"; + /** Related frame. */ private JFrame frame; /** Panel for selection in repository. */ private final SelectionPanel selectionPanel; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an instance of Glyph Trainer (there should be just one) */ @@ -132,55 +132,6 @@ public Trainer () } } - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // getInstance // - //-------------// - public static synchronized Trainer getInstance () - { - if (INSTANCE == null) { - INSTANCE = new Trainer(); - } - - return INSTANCE; - } - - //--------// - // launch // - //--------// - /** - * (Re)activate the trainer tool - */ - public static void launch () - { - if (standAlone) { - } else { - final JFrame frame = getInstance().frame; - OmrGui.getApplication().show(frame); - UIUtil.unMinimize(frame); - } - } - - //------// - // main // - //------// - /** - * Just to allow stand-alone running of this class - * - * @param args not used - */ - public static void main (String... args) - { - standAlone = true; - - // Set UI Look and Feel - UILookAndFeel.setUI(null); - Locale.setDefault(Locale.ENGLISH); - - // Off we go... - Application.launch(Trainer.class, args); - } - //------------// // initialize // //------------// @@ -214,14 +165,6 @@ protected void startup () show(frame); // Here we go... } - //--------------// - // displayFrame // - //--------------// - void displayFrame () - { - frame.toFront(); - } - //--------------// // defineLayout // //--------------// @@ -230,31 +173,29 @@ void displayFrame () * * @param frame the bare frame * @return the populated frame - * */ private JFrame defineLayout (final JFrame frame) { frame.setName("TrainerFrame"); // For SAF life cycle - /* - * +=============================================================+ - * | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | - * | . . . . . . . . . . . . Selection . . . . . . . . . . . . . | - * | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | - * |-------------------------------------------------------------| - * | . . . . . . . . . . . . . . || . . . . . . . . . . . . . . .| - * | . Training. . . . . . . . . || . Training. . . . . . . . . .| - * | . . . . . . . . . . . . . . || . . . . . . . . . . . . . . .| - * |-----------------------------||------------------------------| - * | . . . . . . . . . . . . . . || . . . . . . . . . . . . . . .| - * | . Validation [train set]. . || . Validation [train set]. . .| - * | . . . . . . . . . . . . . . || . . . . . . . . . . . . . . .| - * |-----------------------------||------------------------------| - * | . . . . . . . . . . . . . . || . . . . . . . . . . . . . . .| - * | . Validation [test set] . . || . Validation [test set] . . .| - * | . . . . . . . . . . . . . . || . . . . . . . . . . . . . . .| - * +=============================================================+ - */ + // +=============================+ + // | . . . . . . . . . . . . . . | + // | . Selection . . . . . . . . | + // | . . . . . . . . . . . . . . | + // |-----------------------------| + // | . . . . . . . . . . . . . . | + // | . Training. . . . . . . . . | + // | . . . . . . . . . . . . . . | + // |-----------------------------| + // | . . . . . . . . . . . . . . | + // | . Validation [train set]. . | + // | . . . . . . . . . . . . . . | + // |-----------------------------| + // | . . . . . . . . . . . . . . | + // | . Validation [test set] . . | + // | . . . . . . . . . . . . . . | + // +=============================+ + // FormLayout layout = new FormLayout("pref, 10dlu, pref", "pref, 10dlu, pref"); CellConstraints cst = new CellConstraints(); PanelBuilder builder = new PanelBuilder(layout, new Panel()); @@ -296,48 +237,87 @@ private JPanel definePanel (Classifier classifier) return builder.getPanel(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------------// + // displayFrame // + //--------------// + void displayFrame () + { + frame.toFront(); + } + + //-------------// + // getInstance // + //-------------// + /** + * Report the singleton. + * + * @return the single Trainer instance + */ + public static synchronized Trainer getInstance () + { + if (INSTANCE == null) { + INSTANCE = new Trainer(); + } + + return INSTANCE; + } + + //--------// + // launch // + //--------// + /** + * (Re)activate the trainer tool + */ + public static void launch () + { + if (standAlone) { + } else { + final JFrame frame = getInstance().frame; + OmrGui.getApplication().show(frame); + UIUtil.unMinimize(frame); + } + } + //------// - // Task // + // main // //------// /** - * Class {@code Task} handles, for a given classifier, which activity is currently - * being carried out, only one being current at any time. + * Just to allow stand-alone running of this class + * + * @param args not used */ - public static class Task - extends Observable + public static void main (String... args) { - //~ Enumerations --------------------------------------------------------------------------- + standAlone = true; - /** - * Enum {@code Activity} defines all activities in training. - */ - static enum Activity - { - //~ Enumeration constant initializers -------------------------------------------------- + // Set UI Look and Feel + UILookAndFeel.setUI(null); + Locale.setDefault(Locale.ENGLISH); - /** No ongoing activity */ - INACTIVE, - /** Training on samples */ - TRAINING, - /** Validating classifier */ - VALIDATION; - } + // Off we go... + Application.launch(Trainer.class, args); + } + + public static class Task + extends Observable + { - //~ Instance fields ------------------------------------------------------------------------ /** Managed classifier. */ public final Classifier classifier; /** Current activity. */ private Activity activity = Activity.INACTIVE; - //~ Constructors --------------------------------------------------------------------------- + /** + * The shape classifier to use + * + * @param classifier selected classifier + */ public Task (Classifier classifier) { this.classifier = classifier; } - //~ Methods -------------------------------------------------------------------------------- /** * Report the current training activity * @@ -359,6 +339,19 @@ public void setActivity (Activity activity) setChanged(); notifyObservers(); } + + /** + * Enum {@code Activity} defines all activities in training. + */ + static enum Activity + { + /** No ongoing activity */ + INACTIVE, + /** Training on samples */ + TRAINING, + /** Validating classifier */ + VALIDATION; + } } //-------------// @@ -367,9 +360,8 @@ public void setActivity (Activity activity) private static class TitledPanel extends Panel { - //~ Constructors --------------------------------------------------------------------------- - public TitledPanel (String title) + TitledPanel (String title) { setBorder( BorderFactory.createTitledBorder( diff --git a/src/main/org/audiveris/omr/classifier/ui/TrainingPanel.java b/src/main/org/audiveris/omr/classifier/ui/TrainingPanel.java index 0b893d53f..b1e4fd771 100644 --- a/src/main/org/audiveris/omr/classifier/ui/TrainingPanel.java +++ b/src/main/org/audiveris/omr/classifier/ui/TrainingPanel.java @@ -69,13 +69,11 @@ class TrainingPanel implements TrainingMonitor, Observer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(TrainingPanel.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The swing component. */ protected final Panel component; @@ -120,15 +118,14 @@ class TrainingPanel /* Useful? */ private boolean invoked; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TrainingPanel object. * * @param task the current training task * @param selectionPanel user panel for samples selection */ - public TrainingPanel (Trainer.Task task, - SelectionPanel selectionPanel) + TrainingPanel (Trainer.Task task, + SelectionPanel selectionPanel) { this.task = task; this.selectionPanel = selectionPanel; @@ -155,7 +152,6 @@ public TrainingPanel (Trainer.Task task, inputParams(); } - //~ Methods ------------------------------------------------------------------------------------ @Override public void epochStarted (int epoch) { @@ -173,7 +169,6 @@ public int getIterationPeriod () return constants.listenerPeriod.getValue(); } - // // @Override // public void invoke () // { @@ -185,23 +180,23 @@ public int getIterationPeriod () // { // return invoked; // } -// -// @Override -// public void iterationDone (Model model, -// int iteration) -// { -// iterCount++; -// -// if ((iterCount % constants.listenerPeriod.getValue()) == 0) { -// ///invoke(); -// -// final double score = model.score(); -// final int count = (int) iterCount; -// logger.info(String.format("Score at iteration %d is %.5f", count, score)); -// display(epoch, count, score); -// } -// } -// + // + // @Override + // public void iterationDone (Model model, + // int iteration) + // { + // iterCount++; + // + // if ((iterCount % constants.listenerPeriod.getValue()) == 0) { + // ///invoke(); + // + // final double score = model.score(); + // final int count = (int) iterCount; + // logger.info(String.format("Score at iteration %d is %.5f", count, score)); + // display(epoch, count, score); + // } + // } + // @Override public void iterationPeriodDone (int iter, double score) @@ -243,7 +238,7 @@ public void update (Observable obs, */ private List checkPopulation (List samples) { - EnumMap> shapeSamples = new EnumMap>(Shape.class); + EnumMap> shapeSamples = new EnumMap<>(Shape.class); for (Iterator it = samples.iterator(); it.hasNext();) { Sample sample = it.next(); @@ -256,7 +251,7 @@ private List checkPopulation (List samples) List list = shapeSamples.get(shape); if (list == null) { - shapeSamples.put(shape, list = new ArrayList()); + shapeSamples.put(shape, list = new ArrayList<>()); } list.add(sample); @@ -273,7 +268,7 @@ private List checkPopulation (List samples) final Shape[] shapes = Shape.values(); final int iMax = LAST_PHYSICAL_SHAPE.ordinal(); final int minCount = SelectionPanel.getMinShapeSampleCount(); - final List newSamples = new ArrayList(); + final List newSamples = new ArrayList<>(); for (int is = 0; is <= iMax; is++) { Shape shape = shapes[is]; @@ -353,8 +348,7 @@ private void display (final int epoch, final int iter, final double score) { - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { // This part is run on swing thread @Override @@ -391,22 +385,16 @@ private void inputParams () progressBar.setMaximum(maxEpochs.getValue()); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-------------// - // ResetAction // - //-------------// protected class ResetAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public ResetAction () + ResetAction () { super("Reset"); putValue(Action.SHORT_DESCRIPTION, "Restart from scratch"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -417,45 +405,49 @@ public void actionPerformed (ActionEvent e) task.classifier.reset(); } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } - //------------// - // StopAction // - //------------// protected class StopAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public StopAction () + StopAction () { super("Stop"); putValue(Action.SHORT_DESCRIPTION, "Stop the training"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { task.classifier.stop(); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } - //-------------// - // TrainAction // - //-------------// protected class TrainAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public TrainAction () + TrainAction () { super("Train"); putValue(Action.SHORT_DESCRIPTION, "Train the classifier"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -487,29 +479,18 @@ public void run () worker.setPriority(Thread.MIN_PRIORITY); worker.start(); } - } - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Integer listenerPeriod = new Constant.Integer( - "period", - 50, - "Period (in iterations) between listener calls"); + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } - //-------------// - // ParamAction // - //-------------// private class ParamAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- // Purpose is just to read and remember the data from the various input fields. // Triggered when user presses Enter in one of these fields. @@ -519,5 +500,25 @@ public void actionPerformed (ActionEvent e) inputParams(); displayParams(); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Integer listenerPeriod = new Constant.Integer( + "period", + 50, + "Period (in iterations) between listener calls"); } } diff --git a/src/main/org/audiveris/omr/classifier/ui/TribeMenu.java b/src/main/org/audiveris/omr/classifier/ui/TribeMenu.java index fc25568b8..d888cdc90 100644 --- a/src/main/org/audiveris/omr/classifier/ui/TribeMenu.java +++ b/src/main/org/audiveris/omr/classifier/ui/TribeMenu.java @@ -33,7 +33,6 @@ import org.audiveris.omr.ui.util.SeparableMenu; import org.jdesktop.application.Action; -import org.jdesktop.application.ApplicationAction; import org.jdesktop.application.ApplicationActionMap; import org.slf4j.Logger; @@ -53,11 +52,9 @@ public class TribeMenu extends SeparableMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SampleMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Selected glyph. */ private final Glyph glyph; @@ -67,7 +64,6 @@ public class TribeMenu /** Related sample sheet, if any. */ private SampleSheet sampleSheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TribeMenu} object. * @@ -86,10 +82,14 @@ public TribeMenu (Glyph glyph, populateMenu(); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // addGood // //---------// + /** + * Add a good sample + * + * @param e triggering event + */ @Action public void addGood (ActionEvent e) { @@ -110,6 +110,11 @@ public void addGood (ActionEvent e) //----------// // addOther // //----------// + /** + * Add a plain sample + * + * @param e triggering event + */ @Action public void addOther (ActionEvent e) { @@ -157,10 +162,10 @@ private void populateMenu () if (currentTribe != null) { // Good: Add compatible glyph to current tribe - add(new JMenuItem((ApplicationAction) actionMap.get("addGood"))); + add(new JMenuItem(actionMap.get("addGood"))); // Other: Add sub-optimal glyph to current tribe - add(new JMenuItem((ApplicationAction) actionMap.get("addOther"))); + add(new JMenuItem(actionMap.get("addOther"))); } } @@ -180,37 +185,32 @@ private void selectBest (Shape shape) sampleSheet.getTribe(best); } - //~ Inner Classes ------------------------------------------------------------------------------ //------------// // SelectMenu // //------------// private class SelectMenu extends JMenu { - //~ Constructors --------------------------------------------------------------------------- - public SelectMenu () + SelectMenu () { super("Set as Best"); populate(); } - //~ Methods -------------------------------------------------------------------------------- private void populate () { - ShapeSet.addAllShapes( - this, - new ActionListener() - { - @Override - public void actionPerformed (ActionEvent e) - { - JMenuItem source = (JMenuItem) e.getSource(); - Shape shape = Shape.valueOf(source.getText()); - selectBest(shape); - } - }); + ShapeSet.addAllShapes(this, new ActionListener() + { + @Override + public void actionPerformed (ActionEvent e) + { + JMenuItem source = (JMenuItem) e.getSource(); + Shape shape = Shape.valueOf(source.getText()); + selectBest(shape); + } + }); } } } diff --git a/src/main/org/audiveris/omr/classifier/ui/TribesMenu.java b/src/main/org/audiveris/omr/classifier/ui/TribesMenu.java index 97f8ddb5a..9ed913e29 100644 --- a/src/main/org/audiveris/omr/classifier/ui/TribesMenu.java +++ b/src/main/org/audiveris/omr/classifier/ui/TribesMenu.java @@ -47,16 +47,13 @@ public class TribesMenu extends LocationDependentMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TribesMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- private final GlyphListener glyphListener = new GlyphListener(); private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TribeMenu} object. * @@ -68,7 +65,6 @@ public TribesMenu (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ @Override public void updateUserLocation (Rectangle rect) { @@ -99,7 +95,6 @@ public void updateUserLocation (Rectangle rect) super.updateUserLocation(rect); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------------// // GlyphListener // //---------------// @@ -109,7 +104,6 @@ public void updateUserLocation (Rectangle rect) private class GlyphListener extends AbstractMouseListener { - //~ Methods -------------------------------------------------------------------------------- @Override public void mouseEntered (MouseEvent e) @@ -118,7 +112,7 @@ public void mouseEntered (MouseEvent e) Glyph glyph = tribeMenu.getGlyph(); sheet.getGlyphIndex().getEntityService().publish( - new EntityListEvent( + new EntityListEvent<>( this, SelectionHint.ENTITY_INIT, MouseMovement.PRESSING, diff --git a/src/main/org/audiveris/omr/classifier/ui/ValidationPanel.java b/src/main/org/audiveris/omr/classifier/ui/ValidationPanel.java index 8ef160660..3a5401ef1 100644 --- a/src/main/org/audiveris/omr/classifier/ui/ValidationPanel.java +++ b/src/main/org/audiveris/omr/classifier/ui/ValidationPanel.java @@ -62,11 +62,9 @@ public class ValidationPanel implements Observer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ValidationPanel.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Swing component. */ private final Panel component; @@ -107,7 +105,7 @@ public class ValidationPanel "Number of samples incorrectly recognized"); /** Collection of samples leading to false positives. */ - private final List falsePositives = new ArrayList(); + private final List falsePositives = new ArrayList<>(); /** User action to investigate on false positives. */ private final FalsePositiveAction falsePositiveAction = new FalsePositiveAction(); @@ -118,7 +116,7 @@ public class ValidationPanel "Number of samples weakly recognized"); /** Collection of samples not recognized (false negatives). */ - private final List weakPositives = new ArrayList(); + private final List weakPositives = new ArrayList<>(); /** User action to investigate on weak positives. */ private final WeakPositiveAction weakPositiveAction = new WeakPositiveAction(); @@ -129,12 +127,11 @@ public class ValidationPanel "Number of samples weakly negative"); /** Collection of samples weakly negatives. */ - private final List weakNegatives = new ArrayList(); + private final List weakNegatives = new ArrayList<>(); /** User action to investigate on weak negatives. */ private final WeakNegativeAction weakNegativeAction = new WeakNegativeAction(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ValidationPanel object. * @@ -162,7 +159,6 @@ public ValidationPanel (Trainer.Task task, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // getComponent // //--------------// @@ -364,27 +360,31 @@ private void runValidation () // } } - //~ Inner Classes ------------------------------------------------------------------------------ //---------------------// // FalsePositiveAction // //---------------------// private class FalsePositiveAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public FalsePositiveAction () + FalsePositiveAction () { super("View"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { SampleBrowser.getInstance().displayAll(falsePositives); SampleBrowser.getInstance().setVisible(); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } //----------------// @@ -393,19 +393,16 @@ public void actionPerformed (ActionEvent e) private class ValidateAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public ValidateAction () + ValidateAction () { super("Test"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { - executor.execute( - new Runnable() + executor.execute(new Runnable() { @Override public void run () @@ -430,6 +427,13 @@ public void run () } }); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } //--------------------// @@ -438,20 +442,25 @@ public void run () private class WeakNegativeAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public WeakNegativeAction () + WeakNegativeAction () { super("View"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { SampleBrowser.getInstance().displayAll(weakNegatives); SampleBrowser.getInstance().setVisible(); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } //--------------------// @@ -460,19 +469,24 @@ public void actionPerformed (ActionEvent e) private class WeakPositiveAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public WeakPositiveAction () + WeakPositiveAction () { super("View"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { SampleBrowser.getInstance().displayAll(weakPositives); SampleBrowser.getInstance().setVisible(); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } } diff --git a/src/main/org/audiveris/omr/classifier/ui/resources/Trainer.properties b/src/main/org/audiveris/omr/classifier/ui/resources/Trainer.properties index 0f443474f..6c0902234 100644 --- a/src/main/org/audiveris/omr/classifier/ui/resources/Trainer.properties +++ b/src/main/org/audiveris/omr/classifier/ui/resources/Trainer.properties @@ -15,6 +15,6 @@ Application.vendorId = AudiverisLtd # Reference to selected Crystal icons (needed when run stand-alone, w/o MainGui) icons.root = /crystal/22x22 -TrainerFrame.title = Classifiers training +TrainerFrame.title = Classifier training Application.lookAndFeel = com.jgoodies.looks.plastic.Plastic3DLookAndFeel diff --git a/src/main/org/audiveris/omr/constant/Constant.java b/src/main/org/audiveris/omr/constant/Constant.java index 37d19bb5c..55dfda82e 100644 --- a/src/main/org/audiveris/omr/constant/Constant.java +++ b/src/main/org/audiveris/omr/constant/Constant.java @@ -41,29 +41,25 @@ * The class {@code Constant} is not meant to be used directly (it is abstract), but rather through * any of its subclasses: *

                  - *
                • {@link Constant.Angle}
                • - *
                • {@link Constant.Boolean}
                • - *
                • {@link Constant.Color}
                • - *
                • {@link Constant.Double}
                • - *
                • {@link Constant.Integer}
                • - *
                • {@link Constant.Ratio}
                • - *
                • {@link Constant.String}
                • + *
                • {@link Constant.Angle}
                • + *
                • {@link Constant.Boolean}
                • + *
                • {@link Constant.Color}
                • + *
                • {@link Constant.Double}
                • + *
                • {@link Constant.Integer}
                • + *
                • {@link Constant.Ratio}
                • + *
                • {@link Constant.String}
                • *
                • and others...
                • *
                * * @author Hervé Bitteur - * * @param specific constant type */ @ThreadSafe public abstract class Constant { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Constant.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Data assigned at construction time //----------------------------------- /** Unit (if relevant) used by the quantity measured. */ @@ -86,16 +82,14 @@ public abstract class Constant // Data modified at any time //-------------------------- /** Current data. */ - private AtomicReference tuple = new AtomicReference(); + private AtomicReference tuple = new AtomicReference<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a constant instance, while providing a default value, * in case the external property is not yet defined. * * @param quantityUnit Unit used as base for measure, if relevant - * @param sourceString Source value, expressed by a string literal which - * cannot be null + * @param sourceString Source value, expressed by a string literal which cannot be null * @param description A quick description of the purpose of this constant */ protected Constant (java.lang.String quantityUnit, @@ -110,13 +104,8 @@ protected Constant (java.lang.String quantityUnit, this.quantityUnit = quantityUnit; this.sourceString = sourceString; this.description = description; - - // System.out.println( - // Thread.currentThread().getName() + ": " + "-- Creating Constant: " + - // description); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // getDescription // //----------------// @@ -208,6 +197,19 @@ public java.lang.String getStringValue () return getTuple().currentString; } + //----------------// + // setStringValue // + //----------------// + /** + * Modify the current value of the constant. + * + * @param string the new value, as a string to be checked + */ + public void setStringValue (java.lang.String string) + { + setValue(decode(string)); + } + //----------// // getValue // //----------// @@ -221,6 +223,19 @@ public E getValue () return (E) getCachedValue(); } + //----------// + // setValue // + //----------// + /** + * Assign a new value to the constant. + * + * @param value new value + */ + public void setValue (E value) + { + setTuple(value.toString(), value); + } + //---------------// // isSourceValue // //---------------// @@ -246,32 +261,6 @@ public void resetToSource () setTuple(sourceString, decode(sourceString)); } - //----------------// - // setStringValue // - //----------------// - /** - * Modify the current value of the constant. - * - * @param string the new value, as a string to be checked - */ - public void setStringValue (java.lang.String string) - { - setValue(decode(string)); - } - - //----------// - // setValue // - //----------// - /** - * Assign a new value to the constant. - * - * @param value new value - */ - public void setValue (E value) - { - setTuple(value.toString(), value); - } - //------------------// // toDetailedString // //------------------// @@ -369,9 +358,6 @@ protected void setTuple (java.lang.String str, protected void setUnitAndName (java.lang.String unit, java.lang.String name) { - // System.out.println( - // Thread.currentThread().getName() + ": " + "Assigning unit:" + unit + - // " name:" + name); this.name = name; final java.lang.String qName = (unit != null) ? (unit + "." + name) : name; @@ -402,33 +388,6 @@ protected void setUnitAndName (java.lang.String unit, } } - //----------------// - // getValueOrigin // - //----------------// - /** - * Convenient method, reporting the origin of the current value for - * this constant, either SRC or USR. - * - * @return a mnemonic for the value origin - */ - java.lang.String getValueOrigin () - { - ConstantManager mgr = ConstantManager.getInstance(); - java.lang.String cur = getStringValue(); - java.lang.String usr = mgr.getConstantUserValue(qualifiedName); - java.lang.String src = sourceString; - - if (cur.equals(src)) { - return "SRC"; - } - - if (cur.equals(usr)) { - return "USR"; - } - - return "???"; - } - //------------------// // checkInitialized // //------------------// @@ -445,12 +404,6 @@ private void checkInitialized () i++; UnitManager.getInstance().checkDirtySets(); } - - // For monitoring/debugging only - if (i > 1) { - System.out.println( - "*** " + Thread.currentThread().getName() + " checkInitialized loop:" + i); - } } //----------// @@ -470,7 +423,33 @@ private Tuple getTuple () return tuple.get(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------------// + // getValueOrigin // + //----------------// + /** + * Convenient method, reporting the origin of the current value for + * this constant, either SRC or USR. + * + * @return a mnemonic for the value origin + */ + java.lang.String getValueOrigin () + { + ConstantManager mgr = ConstantManager.getInstance(); + java.lang.String cur = getStringValue(); + java.lang.String usr = mgr.getConstantUserValue(qualifiedName); + java.lang.String src = sourceString; + + if (cur.equals(src)) { + return "SRC"; + } + + if (cur.equals(usr)) { + return "USR"; + } + + return "???"; + } + //-------// // Angle // //-------// @@ -480,7 +459,6 @@ private Tuple getTuple () public static class Angle extends Constant.Double { - //~ Constructors --------------------------------------------------------------------------- /** * Specific constructor, where 'unit' and 'name' are assigned later @@ -504,7 +482,6 @@ public Angle (double defaultValue, public static class Boolean extends Constant { - //~ Constructors --------------------------------------------------------------------------- /** * Specific constructor, where 'unit' and 'name' are assigned later @@ -518,7 +495,6 @@ public Boolean (boolean defaultValue, super(null, java.lang.Boolean.toString(defaultValue), description); } - //~ Methods -------------------------------------------------------------------------------- /** * Convenient method to access this boolean value * @@ -547,7 +523,6 @@ protected java.lang.Boolean decode (java.lang.String str) public static class Color extends Constant { - //~ Constructors --------------------------------------------------------------------------- /** * Normal constructor, with a String type for default value @@ -566,10 +541,27 @@ public Color (java.lang.String unit, setUnitAndName(unit, name); } - //~ Methods -------------------------------------------------------------------------------- + @Override + public void setValue (java.awt.Color val) + { + setTuple(encodeColor(val), val); + } + + @Override + protected java.awt.Color decode (java.lang.String str) + { + return decodeColor(str); + } + //-------------// // decodeColor // //-------------// + /** + * Decode a color string. + * + * @param str input string + * @return the color object + */ public static java.awt.Color decodeColor (java.lang.String str) { return java.awt.Color.decode(str); @@ -578,6 +570,12 @@ public static java.awt.Color decodeColor (java.lang.String str) //-------------// // encodeColor // //-------------// + /** + * Encode a color as a string + * + * @param color Color object to encode + * @return color string + */ public static java.lang.String encodeColor (java.awt.Color color) { return java.lang.String.format( @@ -586,18 +584,6 @@ public static java.lang.String encodeColor (java.awt.Color color) color.getGreen(), color.getBlue()); } - - @Override - public void setValue (java.awt.Color val) - { - setTuple(encodeColor(val), val); - } - - @Override - protected java.awt.Color decode (java.lang.String str) - { - return decodeColor(str); - } } //--------// @@ -609,7 +595,6 @@ protected java.awt.Color decode (java.lang.String str) public static class Double extends Constant { - //~ Static fields/initializers ------------------------------------------------------------- public static final Double ZERO = new Double("none", 0, "Zero"); @@ -626,7 +611,13 @@ public static class Double TWO.setUnitAndName(Constant.class.getName(), "doubleTwo"); } - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a Double object. + * + * @param quantityUnit name of unit used + * @param defaultValue default value + * @param description user description + */ public Double (java.lang.String quantityUnit, double defaultValue, java.lang.String description) @@ -634,7 +625,6 @@ public Double (java.lang.String quantityUnit, super(quantityUnit, java.lang.Double.toString(defaultValue), description); } - //~ Methods -------------------------------------------------------------------------------- @Override protected java.lang.Double decode (java.lang.String str) { @@ -645,14 +635,24 @@ protected java.lang.Double decode (java.lang.String str) //------// // Enum // //------// + /** + * An enumerated constant. + * + * @param underlying enum type + */ public static class Enum> extends Constant> { - //~ Instance fields ------------------------------------------------------------------------ private final Class classe; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create an enum constant. + * + * @param classe enum class + * @param defaultValue default value + * @param description user description + */ public Enum (Class classe, E defaultValue, java.lang.String description) @@ -661,7 +661,6 @@ public Enum (Class classe, this.classe = classe; } - //~ Methods -------------------------------------------------------------------------------- @Override public E getSourceValue () { @@ -690,7 +689,6 @@ protected E decode (java.lang.String str) public static class Integer extends Constant { - //~ Constructors --------------------------------------------------------------------------- /** * Specific constructor, where 'unit' and 'name' are assigned later @@ -706,7 +704,6 @@ public Integer (java.lang.String quantityUnit, super(quantityUnit, java.lang.Integer.toString(defaultValue), description); } - //~ Methods -------------------------------------------------------------------------------- @Override protected java.lang.Integer decode (java.lang.String str) { @@ -723,7 +720,6 @@ protected java.lang.Integer decode (java.lang.String str) public static class Ratio extends Constant.Double { - //~ Static fields/initializers ------------------------------------------------------------- public static final Ratio ZERO = new Ratio(0, "zero"); @@ -731,7 +727,6 @@ public static class Ratio ZERO.setUnitAndName(Constant.class.getName(), "ratioZero"); } - //~ Constructors --------------------------------------------------------------------------- /** * Specific constructor, where 'unit' and 'name' are assigned later * @@ -754,7 +749,6 @@ public Ratio (double defaultValue, public static class String extends Constant { - //~ Constructors --------------------------------------------------------------------------- /** * Normal constructor, with a string type for default value @@ -785,7 +779,6 @@ public String (java.lang.String defaultValue, super(null, defaultValue, description); } - //~ Methods -------------------------------------------------------------------------------- @Override protected java.lang.String decode (java.lang.String str) { @@ -801,15 +794,13 @@ protected java.lang.String decode (java.lang.String str) */ private static class Tuple { - //~ Instance fields ------------------------------------------------------------------------ final java.lang.String currentString; final Object cachedValue; - //~ Constructors --------------------------------------------------------------------------- - public Tuple (java.lang.String currentString, - Object cachedValue) + Tuple (java.lang.String currentString, + Object cachedValue) { /** Current string Value */ this.currentString = currentString; @@ -818,11 +809,11 @@ public Tuple (java.lang.String currentString, this.cachedValue = cachedValue; } - //~ Methods -------------------------------------------------------------------------------- @Override public java.lang.String toString () { return currentString; } } + } diff --git a/src/main/org/audiveris/omr/constant/ConstantManager.java b/src/main/org/audiveris/omr/constant/ConstantManager.java index 4f8604acb..e6a71963b 100644 --- a/src/main/org/audiveris/omr/constant/ConstantManager.java +++ b/src/main/org/audiveris/omr/constant/ConstantManager.java @@ -53,36 +53,40 @@ * The actual value of an application "constant", as returned by the method * {@link Constant#getStringValue}, is determined in the following order, any definition * overriding the previous ones: - *
                1. First, SOURCE values are always provided within source + *
                    + *
                  1. First, SOURCE values are always provided within source * declaration of the constants in the Java source file itself. * For example, in the "omr/sheet/ScaleBuilder.java" file, we can find the following * declaration which defines the minimum value for sheet resolution, here specified in pixels (the * application has difficulties with scans of lower resolution). + * *
                      * Constant.Integer minResolution = new Constant.Integer(
                    - *    "Pixels",
                    - *    11,
                    - *    "Minimum resolution, expressed as number of pixels per interline");
                    - * 
                    This declaration must be read as follows:
                      + * "Pixels", + * 11, + * "Minimum resolution, expressed as number of pixels per interline"); + * * + * This declaration must be read as follows: + *
                        *
                      • {@code minResolution} is the Java object used in the application. * It is defined as a Constant.Integer, a subtype of Constant meant to host Integer values
                      • - * *
                      • {@code "Pixels"} specifies the unit used. Here we are counting in pixels.
                      • - * *
                      • {@code 11} is the constant value. This is the value used by the application, provided it is * not overridden in the USER properties file or later via a dedicated GUI tool.
                      • - * *
                      • "Minimum resolution, expressed as number of pixels per interline" is the * constant description, which will be used as a tool tip in the GUI interface in charge of editing - * these constants.
                      + * these constants. + *
                    *
                  2. - * *
                  3. Then, USER values, contained in a property file named "run.properties" * can assign overriding values to some constants. For example, the {@code minInterline} constant * above could be altered by the following line in this user file: + * *
                    - * omr.sheet.ScaleBuilder.minInterline=12
                    + * omr.sheet.ScaleBuilder.minInterline = 12 + * + * * This file is modified every time the user updates the value of a constant by means of the * provided Constant user interface at run-time. * The file is not mandatory, and is located in the user application data {@code config} folder. @@ -90,26 +94,26 @@ * Typically, these USER values represent some modification made by the end user at run-time and * thus saved from one run to the other. * The file is not meant to be edited manually, but rather through the provided GUI tool.
                  4. - * *
                  5. Then, CLI values, as set on the command line interface, by means of the * "-option" key=value command. For further details on this command, refer to the * {@link org.audiveris.omr.CLI} class documentation. - *
                    Persistency here depends on the way Audiveris is running:
                      + *
                      + * Persistency here depends on the way Audiveris is running: + *
                        *
                      • When running in batch mode, these CLI-defined constant values are not persisted * in the USER file, unless the constant {@code omr.Main.persistBatchCliConstants} is set to * true.
                      • *
                      • When running in interactive mode, these CLI-defined constant values are always - * persisted in the USER file.
                      - * + * persisted in the USER file. + *
                    + *
                  6. *
                  7. Finally, UI Options Menu values, as set online through the graphical user interface. - * These constant values defined at the GUI level are persisted in the USER file.
                  - * - *

                  + * These constant values defined at the GUI level are persisted in the USER file.

                2. + *
                * The whole set of constant values is stored on disk when the application is closed. Doing so, the * disk values are always kept in synch with the program values, provided the application is * normally closed rather than killed. It can also be stored programmatically by calling the * {@link #storeResource} method. - * *

                * Only the USER property file is written, the SOURCE values in the source code are not altered. * Moreover, if the user has modified a value in such a way that the final value is the same as in @@ -121,10 +125,8 @@ @ThreadSafe public class ConstantManager { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - ConstantManager.class); + private static final Logger logger = LoggerFactory.getLogger(ConstantManager.class); /** User properties file name */ private static final String USER_FILE_NAME = "run.properties"; @@ -132,23 +134,20 @@ public class ConstantManager /** The singleton */ private static final ConstantManager INSTANCE = new ConstantManager(); - //~ Instance fields ---------------------------------------------------------------------------- /** * Map of all constants created in the application, regardless whether these * constants are enclosed in a ConstantSet or defined as standalone entities. */ - protected final ConcurrentHashMap constants = new ConcurrentHashMap(); + protected final ConcurrentHashMap constants = new ConcurrentHashMap<>(); /** User properties. */ private final UserHolder userHolder = new UserHolder( WellKnowns.CONFIG_FOLDER.resolve(USER_FILE_NAME)); - //~ Constructors ------------------------------------------------------------------------------- private ConstantManager () { } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // addConstant // //-------------// @@ -216,7 +215,7 @@ public static ConstantManager getInstance () */ public Collection getAllProperties () { - SortedSet props = new TreeSet(userHolder.getKeys()); + SortedSet props = new TreeSet<>(userHolder.getKeys()); return props; } @@ -275,13 +274,11 @@ String getConstantUserValue (String qName) return userHolder.getProperty(qName); } - //~ Inner Classes ------------------------------------------------------------------------------ //----------------// // AbstractHolder // //----------------// private class AbstractHolder { - //~ Instance fields ------------------------------------------------------------------------ /** Related file. */ protected final Path path; @@ -289,16 +286,14 @@ private class AbstractHolder /** The handled properties. */ protected Properties properties; - //~ Constructors --------------------------------------------------------------------------- public AbstractHolder (Path path) { this.path = path; } - //~ Methods -------------------------------------------------------------------------------- public Collection getKeys () { - Collection strings = new ArrayList(); + Collection strings = new ArrayList<>(); for (Object obj : properties.keySet()) { strings.add((String) obj); @@ -314,7 +309,7 @@ public String getProperty (String key) public Collection getUnusedKeys () { - SortedSet props = new TreeSet(); + SortedSet props = new TreeSet<>(); for (Object obj : properties.keySet()) { if (!constants.containsKey((String) obj)) { @@ -327,7 +322,7 @@ public Collection getUnusedKeys () public Collection getUselessKeys () { - SortedSet props = new TreeSet(); + SortedSet props = new TreeSet<>(); for (Entry entry : properties.entrySet()) { Constant constant = constants.get((String) entry.getKey()); @@ -383,7 +378,6 @@ private void loadFromFile () private class UserHolder extends AbstractHolder { - //~ Constructors --------------------------------------------------------------------------- public UserHolder (Path path) { @@ -392,7 +386,6 @@ public UserHolder (Path path) load(); } - //~ Methods -------------------------------------------------------------------------------- /** * Remove from the USER collection the properties that are * already in the source with identical value, diff --git a/src/main/org/audiveris/omr/constant/ConstantSet.java b/src/main/org/audiveris/omr/constant/ConstantSet.java index c0498ee86..6a0a2d6c0 100644 --- a/src/main/org/audiveris/omr/constant/ConstantSet.java +++ b/src/main/org/audiveris/omr/constant/ConstantSet.java @@ -45,12 +45,9 @@ @ThreadSafe public abstract class ConstantSet { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - ConstantSet.class); + private static final Logger logger = LoggerFactory.getLogger(ConstantSet.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Name of the containing unit/class */ private final String unit; @@ -64,7 +61,6 @@ public abstract class ConstantSet */ private volatile SortedMap map; - //~ Constructors ------------------------------------------------------------------------------- /** * A new ConstantSet instance is created, and registered at the * UnitManager singleton, but its map of internal constants will @@ -81,7 +77,6 @@ public ConstantSet () UnitManager.getInstance().addSet(this); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // dumpOf // //--------// @@ -110,8 +105,8 @@ public String dumpOf () "%-25s %12s %-14s =%5s %-25s\t%s%n", constant.getName(), constant.getClass().getSimpleName(), - (constant.getQuantityUnit() != null) ? ("(" + constant.getQuantityUnit() + ")") - : "", + (constant.getQuantityUnit() != null) ? ("(" + constant.getQuantityUnit() + + ")") : "", origin, constant.getStringValue(), constant.getDescription())); @@ -240,7 +235,7 @@ private SortedMap getMap () */ private void initMap () { - SortedMap tempMap = new TreeMap(); + SortedMap tempMap = new TreeMap<>(); // Retrieve values of all fields Class cl = getClass(); @@ -265,12 +260,8 @@ private void initMap () constant.setUnitAndName(unit, name); tempMap.put(name, constant); } else { - logger.error( - "ConstantSet in unit ''{}'' contains a non" - + " Constant field ''{}'' obj= {}", - unit, - name, - obj); + logger.error("ConstantSet in unit ''{}'' contains a non" + + " Constant field ''{}'' obj= {}", unit, name, obj); } } diff --git a/src/main/org/audiveris/omr/constant/Node.java b/src/main/org/audiveris/omr/constant/Node.java index 196e4595b..e42de9fe5 100644 --- a/src/main/org/audiveris/omr/constant/Node.java +++ b/src/main/org/audiveris/omr/constant/Node.java @@ -31,7 +31,6 @@ */ public abstract class Node { - //~ Static fields/initializers ----------------------------------------------------------------- /** For comparing Node instances according to their name */ public static final Comparator nameComparator = new Comparator() @@ -44,11 +43,9 @@ public int compare (Node n1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** (Fully qualified) name of the node */ private final String name; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new Node. * @@ -59,7 +56,6 @@ public Node (String name) this.name = name; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getName // //---------// diff --git a/src/main/org/audiveris/omr/constant/PackageNode.java b/src/main/org/audiveris/omr/constant/PackageNode.java index f28e423ef..11113a392 100644 --- a/src/main/org/audiveris/omr/constant/PackageNode.java +++ b/src/main/org/audiveris/omr/constant/PackageNode.java @@ -34,16 +34,14 @@ public class PackageNode extends Node { - //~ Instance fields ---------------------------------------------------------------------------- /** * The children, composed of either other {@code PackageNode} or * {@code ConstantSet}. */ - private final ConcurrentSkipListSet children = new ConcurrentSkipListSet( + private final ConcurrentSkipListSet children = new ConcurrentSkipListSet<>( Node.nameComparator); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new PackageNode. * @@ -61,7 +59,6 @@ public PackageNode (String name, } } - //~ Methods ------------------------------------------------------------------------------------ //----------// // addChild // //----------// diff --git a/src/main/org/audiveris/omr/constant/UnitManager.java b/src/main/org/audiveris/omr/constant/UnitManager.java index 99d79fe6d..3a58e7f8f 100644 --- a/src/main/org/audiveris/omr/constant/UnitManager.java +++ b/src/main/org/audiveris/omr/constant/UnitManager.java @@ -62,33 +62,27 @@ @ThreadSafe public class UnitManager { - //~ Static fields/initializers ----------------------------------------------------------------- + + private static final Logger logger = LoggerFactory.getLogger(UnitManager.class); /** The single instance of this class. */ private static final UnitManager INSTANCE = new UnitManager(); - private static final Logger logger = LoggerFactory.getLogger( - UnitManager.class); - - //~ Instance fields ---------------------------------------------------------------------------- - // /** The root node. */ private final PackageNode root = new PackageNode("", null); /** Map of PackageNodes and UnitNodes. */ - private final ConcurrentHashMap mapOfNodes = new ConcurrentHashMap(); + private final ConcurrentHashMap mapOfNodes = new ConcurrentHashMap<>(); /** Set of names of ConstantSets that still need to be initialized. */ - private final ConcurrentSkipListSet dirtySets = new ConcurrentSkipListSet(); + private final ConcurrentSkipListSet dirtySets = new ConcurrentSkipListSet<>(); - //~ Constructors ------------------------------------------------------------------------------- /** This is a singleton. */ private UnitManager () { mapOfNodes.put("", root); } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // getInstance // //-------------// @@ -128,7 +122,7 @@ public void addSet (ConstantSet set) */ public void checkAllUnits () { - SortedSet constants = new TreeSet(); + SortedSet constants = new TreeSet<>(); for (Node node : mapOfNodes.values()) { if (node instanceof UnitNode) { @@ -150,9 +144,8 @@ public void checkAllUnits () props.removeAll(constants); dumpStrings("Non set-enclosed properties", props); - dumpStrings( - "Unused User properties", - ConstantManager.getInstance().getUnusedUserProperties()); + dumpStrings("Unused User properties", ConstantManager.getInstance() + .getUnusedUserProperties()); } //----------------// @@ -199,7 +192,7 @@ public void dumpAllUnits () sb.append(String.format("UnitManager. All Units:%n")); // Use alphabetical order for easier reading - List nodes = new ArrayList(mapOfNodes.values()); + List nodes = new ArrayList<>(mapOfNodes.values()); Collections.sort(nodes, Node.nameComparator); for (Node node : nodes) { @@ -323,7 +316,7 @@ public void resetAllUnits () public Set searchUnits (String string) { String str = string.toLowerCase(Locale.ENGLISH); - Set found = new LinkedHashSet(); + Set found = new LinkedHashSet<>(); for (Node node : mapOfNodes.values()) { if (node instanceof UnitNode) { @@ -341,8 +334,8 @@ public Set searchUnits (String string) for (int i = 0; i < set.size(); i++) { Constant constant = set.getConstant(i); - if (constant.getName().toLowerCase(Locale.US).contains(str) - || constant.getDescription().toLowerCase(Locale.US).contains(str)) { + if (constant.getName().toLowerCase(Locale.US).contains(str) || constant + .getDescription().toLowerCase(Locale.US).contains(str)) { found.add(constant); } } @@ -457,8 +450,8 @@ private void updateParents (UnitNode unit) return; } else { - Exception e = new IllegalStateException( - "unexpected node type " + obj.getClass() + " in map."); + Exception e = new IllegalStateException("unexpected node type " + obj.getClass() + + " in map."); e.printStackTrace(); return; diff --git a/src/main/org/audiveris/omr/constant/UnitModel.java b/src/main/org/audiveris/omr/constant/UnitModel.java index ba81d5566..4bb8a6545 100644 --- a/src/main/org/audiveris/omr/constant/UnitModel.java +++ b/src/main/org/audiveris/omr/constant/UnitModel.java @@ -51,18 +51,14 @@ public class UnitModel extends AbstractTreeTableModel { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(UnitModel.class); - //~ Enumerations ------------------------------------------------------------------------------- /** * Enumeration type to describe each column of the JTreeTable */ public static enum Column { - //~ Enumeration constant initializers ------------------------------------------------------ - /** * The left column, assigned to tree structure, allows expansion * and collapsing of sub-tree portions. @@ -99,7 +95,6 @@ public static enum Column * Column dedicated to constant description. */ DESC("Description", false, 350, String.class); - //~ Instance fields ------------------------------------------------------------------------ /** Java class to handle column content. */ private final Class type; @@ -113,7 +108,6 @@ public static enum Column /** Width for column display. */ private final int width; - //~ Constructors --------------------------------------------------------------------------- Column (String header, boolean editable, int width, @@ -125,7 +119,6 @@ public static enum Column this.type = type; } - //~ Methods -------------------------------------------------------------------------------- //----------// // getWidth // //----------// @@ -135,7 +128,6 @@ public int getWidth () } } - //~ Constructors ------------------------------------------------------------------------------- /** * Builds the model. */ @@ -144,7 +136,6 @@ public UnitModel () super(UnitManager.getInstance().getRoot()); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getChild // //----------// @@ -175,8 +166,8 @@ public Object getChild (Object parent, } } - System.err.println( - "*** getChild. Unexpected node " + parent + ", type=" + parent.getClass().getName()); + System.err.println("*** getChild. Unexpected node " + parent + ", type=" + parent.getClass() + .getName()); return null; } @@ -208,9 +199,8 @@ public int getChildCount (Object parent) return 0; } - System.err.println( - "*** getChildCount. Unexpected node " + parent + ", type=" - + parent.getClass().getName()); + System.err.println("*** getChildCount. Unexpected node " + parent + ", type=" + parent + .getClass().getName()); return 0; } @@ -329,9 +319,8 @@ public Object getValueAt (Object node, if (node instanceof Constant) { Constant constant = (Constant) node; - if (constant instanceof Scale.Fraction - || constant instanceof Scale.LineFraction - || constant instanceof Scale.AreaFraction) { + if (constant instanceof Scale.Fraction || constant instanceof Scale.LineFraction + || constant instanceof Scale.AreaFraction) { // Compute the equivalent in pixels of this interline-based // fraction, line or area fraction, provided that we have a // current sheet and its scale is available. @@ -342,15 +331,14 @@ public Object getValueAt (Object node, if (scale != null) { if (constant instanceof Scale.Fraction) { - return String.format( - "%.1f", - scale.toPixelsDouble((Scale.Fraction) constant)); + return String.format("%.1f", scale.toPixelsDouble( + (Scale.Fraction) constant)); } else if (constant instanceof Scale.LineFraction) { - return Integer.valueOf( - scale.toPixels((Scale.LineFraction) constant)); + return Integer + .valueOf(scale.toPixels((Scale.LineFraction) constant)); } else if (constant instanceof Scale.AreaFraction) { - return Integer.valueOf( - scale.toPixels((Scale.AreaFraction) constant)); + return Integer + .valueOf(scale.toPixels((Scale.AreaFraction) constant)); } } } else { diff --git a/src/main/org/audiveris/omr/constant/UnitNode.java b/src/main/org/audiveris/omr/constant/UnitNode.java index 145253bc5..7861213df 100644 --- a/src/main/org/audiveris/omr/constant/UnitNode.java +++ b/src/main/org/audiveris/omr/constant/UnitNode.java @@ -32,7 +32,6 @@ public class UnitNode extends Node { - //~ Instance fields ---------------------------------------------------------------------------- /** The contained Constant set if any */ private ConstantSet set; @@ -40,7 +39,6 @@ public class UnitNode /** The logger if any */ private Logger logger; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new UnitNode. * @@ -51,7 +49,6 @@ public UnitNode (String name) super(name); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // getConstantSet // //----------------// diff --git a/src/main/org/audiveris/omr/constant/UnitTreeTable.java b/src/main/org/audiveris/omr/constant/UnitTreeTable.java index 8acfd6cde..f4a2978ec 100644 --- a/src/main/org/audiveris/omr/constant/UnitTreeTable.java +++ b/src/main/org/audiveris/omr/constant/UnitTreeTable.java @@ -57,19 +57,16 @@ public class UnitTreeTable extends JTreeTable { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(UnitTreeTable.class); /** Alternate color for zebra appearance */ private static final Color zebraColor = new Color(248, 248, 255); - //~ Instance fields ---------------------------------------------------------------------------- private final TableCellRenderer valueRenderer = new ValueRenderer(); private final TableCellRenderer pixelRenderer = new PixelRenderer(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a User Interface JTreeTable dedicated to the handling of * unit constants. @@ -99,7 +96,6 @@ public UnitTreeTable (UnitModel model) preExpandPackages(); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // getCellEditor // //---------------// @@ -221,7 +217,7 @@ public void scrollRowToVisible (int row) */ public List setNodesSelection (Collection matches) { - List paths = new ArrayList(); + List paths = new ArrayList<>(); for (Object object : matches) { if (object instanceof Constant) { @@ -241,7 +237,7 @@ public List setNodesSelection (Collection matches) // Selection on table side clearSelection(); - List rows = new ArrayList(); + List rows = new ArrayList<>(); for (TreePath path : paths) { int row = tree.getRowForPath(path); @@ -283,7 +279,7 @@ private TreePath getPath (Object object, String fullName) { UnitManager unitManager = UnitManager.getInstance(); - List objects = new ArrayList(); + List objects = new ArrayList<>(); objects.add(unitManager.getRoot()); int dotPos = -1; @@ -330,14 +326,12 @@ private void preExpandPackages () } } - //~ Inner Classes ------------------------------------------------------------------------------ //---------------// // PixelRenderer // //---------------// private static class PixelRenderer extends DefaultTableCellRenderer { - //~ Methods -------------------------------------------------------------------------------- @Override public Component getTableCellRendererComponent (JTable table, @@ -362,7 +356,6 @@ public Component getTableCellRendererComponent (JTable table, private static class ValueRenderer extends DefaultTableCellRenderer { - //~ Methods -------------------------------------------------------------------------------- @Override public Component getTableCellRendererComponent (JTable table, diff --git a/src/main/org/audiveris/omr/glyph/AbstractWeightedEntity.java b/src/main/org/audiveris/omr/glyph/AbstractWeightedEntity.java index 7811f129f..a7abbe9cb 100644 --- a/src/main/org/audiveris/omr/glyph/AbstractWeightedEntity.java +++ b/src/main/org/audiveris/omr/glyph/AbstractWeightedEntity.java @@ -50,12 +50,9 @@ public abstract class AbstractWeightedEntity extends AbstractEntity implements WeightedEntity { - //~ Static fields/initializers ----------------------------------------------------------------- private static final EnumSet NO_GROUP = EnumSet.noneOf(GlyphGroup.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -70,7 +67,6 @@ public abstract class AbstractWeightedEntity /** Potential attachments. */ protected AttachmentHolder attachments; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code AbstractSymbol} object. */ @@ -78,7 +74,6 @@ protected AbstractWeightedEntity () { } - //~ Methods ------------------------------------------------------------------------------------ @Override public void addAttachment (String id, java.awt.Shape attachment) diff --git a/src/main/org/audiveris/omr/glyph/BasicGlyph.java b/src/main/org/audiveris/omr/glyph/BasicGlyph.java deleted file mode 100644 index 48bbc0ae2..000000000 --- a/src/main/org/audiveris/omr/glyph/BasicGlyph.java +++ /dev/null @@ -1,671 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// B a s i c G l y p h // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.glyph; - -import ij.process.ByteProcessor; - -import org.audiveris.omr.image.Table; -import org.audiveris.omr.math.BasicLine; -import org.audiveris.omr.math.LineUtil; -import org.audiveris.omr.math.PointsCollector; -import org.audiveris.omr.moments.ARTMoments; -import org.audiveris.omr.moments.GeometricMoments; -import org.audiveris.omr.run.Orientation; -import static org.audiveris.omr.run.Orientation.HORIZONTAL; -import org.audiveris.omr.run.Run; -import org.audiveris.omr.run.RunTable; -import org.audiveris.omr.util.Navigable; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.geom.Line2D; -import java.awt.geom.Point2D; -import java.util.Iterator; -import java.util.Objects; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.adapters.XmlAdapter; - -/** - * Class {@code BasicGlyph} is the basis for Glyph implementation. - * - * @author Hervé Bitteur - */ -@XmlAccessorType(XmlAccessType.NONE) -@XmlRootElement(name = "glyph") -public class BasicGlyph - extends AbstractWeightedEntity - implements Glyph -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger( - BasicGlyph.class); - - //~ Instance fields ---------------------------------------------------------------------------- - // - // Persistent data - //---------------- - // - /** Absolute abscissa of the glyph top left corner. */ - @XmlAttribute(name = "left") - protected final int left; - - /** Absolute ordinate of the glyph top left corner. */ - @XmlAttribute(name = "top") - protected final int top; - - /** Runs of pixels that compose the glyph. Gives all runs, thus width, height, etc... */ - @XmlElement(name = "run-table") - protected final RunTable runTable; - - // Transient data - //--------------- - // - /** The containing glyph index, if any. */ - @Navigable(false) - protected GlyphIndex index; - - /** Computed ART Moments. */ - protected ARTMoments artMoments; - - /** Computed geometric Moments. */ - protected GeometricMoments geoMoments; - - /** Mass center coordinates. */ - protected Point centroid; - - /** Box center coordinates. */ - protected Point center; - - /** Best straight line equation. */ - protected Line2D line; - - /** Line elements. */ - protected BasicLine basicLine; - - /** Absolute slope of the line WRT abscissa axis. */ - protected Double slope; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code BasicGlyph} object. - * - * @param left abscissa of top left corner - * @param top ordinate of top left corner - * @param runTable table of runs (cannot be null) - */ - public BasicGlyph (int left, - int top, - RunTable runTable) - { - this.left = left; - this.top = top; - - ///Objects.requireNonNull(runTable, "BasicGlyph created with null runTable"); - // NOTA: We must accept null RunTable for the Sample no-arg constructor - this.runTable = runTable; - } - - /** - * No-arg constructor meant for JAXB. - */ - private BasicGlyph () - { - this.left = 0; - this.top = 0; - this.runTable = null; - } - - //~ Methods ------------------------------------------------------------------------------------ - //----------// - // contains // - //----------// - @Override - public boolean contains (Point point) - { - if (getBounds().contains(point)) { - final Point relPoint = new Point(point.x - left, point.y - top); - - return runTable.contains(relPoint); - } - - return false; - } - - @Override - public String dumpOf () - { - StringBuilder sb = new StringBuilder(); - - // Admin - sb.append( - String.format( - "%s#%s @%s%n", - getClass().getSimpleName(), - id, - Integer.toHexString(hashCode()))); - - if (isVip()) { - sb.append(String.format(" vip%n", getId())); - } - - sb.append(String.format(" bounds=%s%n", getBounds())); - sb.append(String.format(" groups=%s%n", groups)); - - ///sb.append(String.format(" nest=%s%n", getIndex())); - // Display - if (attachments != null) { - sb.append(String.format(" attachments=%s%n", attachments)); - } - - return sb.toString(); - } - - @Override - public boolean equals (Object obj) - { - if (this == obj) { - return true; - } - - if (obj == null) { - return false; - } - - if (getClass() != obj.getClass()) { - return false; - } - - final BasicGlyph other = (BasicGlyph) obj; - - if (this.left != other.left) { - return false; - } - - if (this.top != other.top) { - return false; - } - - if (!Objects.equals(this.runTable, other.runTable)) { - return false; - } - - return true; - } - - @Override - public void fillTable (Table.UnsignedByte table, - Point tableOrigin, - boolean fat) - { - runTable.fillTable(table, tableOrigin, getTopLeft(), fat); - } - - @Override - public ARTMoments getARTMoments () - { - if (artMoments == null) { - artMoments = runTable.computeArtMoments(left, top); - } - - return artMoments; - } - - @Override - public Rectangle getBounds () - { - return new Rectangle(left, top, runTable.getWidth(), runTable.getHeight()); - } - - @Override - public ByteProcessor getBuffer () - { - return runTable.getBuffer(); - } - - @Override - public Point getCenter () - { - if (center == null) { - center = new Point(left + (runTable.getWidth() / 2), top + (runTable.getHeight() / 2)); - } - - return center; - } - - @Override - public Point getCentroid () - { - if (centroid == null) { - centroid = runTable.computeCentroid(left, top); - } - - return centroid; - } - - @Override - public GeometricMoments getGeometricMoments (int interline) - { - if (geoMoments == null) { - geoMoments = runTable.computeGeometricMoments(left, top, interline); - } - - return geoMoments; - } - - @Override - public int getHeight () - { - return runTable.getHeight(); - } - - @Override - public GlyphIndex getIndex () - { - return index; - } - - @Override - public double getInvertedSlope () - { - checkLine(); - - return LineUtil.getInvertedSlope(line); - } - - @Override - public int getLeft () - { - return left; - } - - @Override - public int getLength (Orientation orientation) - { - if (orientation == HORIZONTAL) { - return runTable.getWidth(); - } else { - return runTable.getHeight(); - } - } - - @Override - public Line2D getLine () - { - checkLine(); - - return line; - } - - @Override - public double getMeanDistance () - { - checkLine(); - - return basicLine.getMeanDistance(); - } - - @Override - public RunTable getRunTable () - { - return runTable; - } - - @Override - public double getSlope () - { - if (slope == null) { - checkLine(); - - slope = LineUtil.getSlope(line); - } - - return slope; - } - - @Override - public Point2D getStartPoint (Orientation orientation) - { - checkLine(); - - if (orientation == HORIZONTAL) { - // Use left side - if (line.getX1() <= line.getX2()) { - return line.getP1(); - } else { - return line.getP2(); - } - } else if (line.getY1() <= line.getY2()) { - return line.getP1(); - } else { - return line.getP2(); - } - } - - @Override - public Point2D getStopPoint (Orientation orientation) - { - checkLine(); - - if (orientation == HORIZONTAL) { - // Use right side - if (line.getX2() >= line.getX1()) { - return line.getP2(); - } else { - return line.getP1(); - } - } else if (line.getY2() >= line.getY1()) { - return line.getP2(); - } else { - return line.getP1(); - } - } - - @Override - public int getTop () - { - return top; - } - - @Override - public Point getTopLeft () - { - return new Point(left, top); - } - - @Override - public int getWeight () - { - return runTable.getWeight(); - } - - @Override - public int getWidth () - { - return runTable.getWidth(); - } - - @Override - public int hashCode () - { - int hash = 5; - hash = (79 * hash) + this.left; - hash = (79 * hash) + this.top; - hash = (79 * hash) + Objects.hashCode(this.runTable); - - return hash; - } - - @Override - public String idString () - { - return "glyph#" + id; - } - - @Override - public boolean intersects (Table.UnsignedByte table, - Point tableOrigin) - { - return runTable.intersects(table, tableOrigin, getTopLeft()); - } - - @Override - public boolean intersects (Shape shape) - { - // First make a rough test - Rectangle bounds = getBounds(); - - if (shape.intersects(bounds)) { - Rectangle clip = bounds.intersection(shape.getBounds()); - - if (runTable.getOrientation() == HORIZONTAL) { - final int minSeq = clip.y - top; - final int maxSeq = (clip.y - top + clip.height) - 1; - - for (int iSeq = minSeq; iSeq <= maxSeq; iSeq++) { - for (Iterator it = runTable.iterator(iSeq); it.hasNext();) { - final Run run = it.next(); - - if (shape.intersects(left + run.getStart(), top + iSeq, run.getLength(), 1)) { - return true; - } - } - } - } else { - final int minSeq = clip.x - left; - final int maxSeq = (clip.x - left + clip.width) - 1; - - for (int iSeq = minSeq; iSeq <= maxSeq; iSeq++) { - for (Iterator it = runTable.iterator(iSeq); it.hasNext();) { - Run run = it.next(); - - if (shape.intersects(left + iSeq, top + run.getStart(), 1, run.getLength())) { - return true; - } - } - } - } - } - - return false; - } - - @Override - public boolean isIdentical (Glyph that) - { - if (this.getTop() != that.getTop()) { - return false; - } - - if (this.getLeft() != that.getLeft()) { - return false; - } - - if (this.getWeight() != that.getWeight()) { - return false; - } - - //TODO: we should accept different runTable orientations? - return this.runTable.equals(((BasicGlyph) that).runTable); - } - - @Override - public boolean isTransient () - { - return index == null; - } - - @Override - public boolean isVirtual () - { - return false; - } - - @Override - public void renderLine (Graphics2D g) - { - Rectangle clip = g.getClipBounds(); - - if ((clip == null) || clip.intersects(getBounds())) { - checkLine(); // To make sure the line has been computed - - if (line != null) { - ///g.draw(line); - g.draw( - new Line2D.Double( - line.getX1(), - line.getY1() + 0.5, - line.getX2() + 1, - line.getY2() + 0.5)); - } - } - } - - @Override - public void setIndex (GlyphIndex index) - { - this.index = index; - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(); - - sb.append(getClass().getSimpleName()).append("{").append("#").append(getId()); - - sb.append(internals()); - - sb.append("}"); - - return sb.toString(); - } - - //-----------// - // internals // - //-----------// - /** - * Return the internals of this class, typically for inclusion in a toString. - * - * @return the string of internals - */ - @Override - protected String internals () - { - StringBuilder sb = new StringBuilder(super.internals()); - - if ((groups != null) && !groups.isEmpty()) { - sb.append(' ').append(groups); - } - - return sb.toString(); - } - - //-----------// - // checkLine // - //-----------// - /** - * Make sure the approximating line is available - */ - private void checkLine () - { - if (line == null) { - computeLine(); - } - } - - //-------------// - // computeLine // - //-------------// - private void computeLine () - { - basicLine = new BasicLine(); - - final boolean isHori = runTable.getOrientation() == HORIZONTAL; - - for (int iSeq = 0, iBreak = runTable.getSize(); iSeq < iBreak; iSeq++) { - for (Iterator it = runTable.iterator(iSeq); it.hasNext();) { - Run run = it.next(); - int start = run.getStart(); - - for (int ic = run.getLength() - 1; ic >= 0; ic--) { - if (isHori) { - basicLine.includePoint(left + start + ic, top + iSeq); - } else { - basicLine.includePoint(left + iSeq, top + start + ic); - } - } - } - } - - // We have a problem if glyph is just 0 or 1 pixel: no computable slope! - switch (basicLine.getNumberOfPoints()) { - case 0: - throw new IllegalStateException("Glyph has no pixel, cannot compute line."); - - case 1: - slope = 0d; // we just need a value. - - break; - - default: - slope = basicLine.getSlope(); - - break; - } - - line = basicLine.toDouble(); - } - - //--------------------// - // getPointsCollector // - //--------------------// - /** - * Cumulate absolute points from all runs. - * - * @return a populated point collector - */ - private PointsCollector getPointsCollector () - { - final PointsCollector collector = new PointsCollector(null, getWeight()); - runTable.cumulate(collector, new Point(left, top)); - - return collector; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //---------// - // Adapter // - //---------// - /** - * Meant for JAXB handling of Glyph interface. - */ - public static class Adapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public BasicGlyph marshal (Glyph glyph) - throws Exception - { - return (BasicGlyph) glyph; - } - - @Override - public Glyph unmarshal (BasicGlyph basicGlyph) - throws Exception - { - return basicGlyph; - } - } -} diff --git a/src/main/org/audiveris/omr/glyph/Glyph.java b/src/main/org/audiveris/omr/glyph/Glyph.java index 98ecfda40..d4082f864 100644 --- a/src/main/org/audiveris/omr/glyph/Glyph.java +++ b/src/main/org/audiveris/omr/glyph/Glyph.java @@ -24,18 +24,37 @@ import ij.process.ByteProcessor; import org.audiveris.omr.image.Table; +import org.audiveris.omr.math.BasicLine; +import org.audiveris.omr.math.LineUtil; +import org.audiveris.omr.math.PointsCollector; import org.audiveris.omr.moments.ARTMoments; import org.audiveris.omr.moments.GeometricMoments; +import org.audiveris.omr.run.Orientation; +import static org.audiveris.omr.run.Orientation.HORIZONTAL; +import org.audiveris.omr.run.Run; import org.audiveris.omr.run.RunTable; -import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.util.Navigable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Graphics2D; import java.awt.Point; +import java.awt.Rectangle; import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.Iterator; +import java.util.Objects; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; /** - * Interface {@code Glyph} is a symbol made of a fixed set of pixels. + * Class {@code Glyph} is a symbol made of a fixed set of pixels. *

                * A glyph is un-mutable, meaning one cannot add or remove pixels to/from an existing instance, * although one can always create another glyph instance with the proper collection of pixels. @@ -48,18 +67,166 @@ * A glyph has no intrinsic orientation, hence some methods such as {@link #getLength} require that * view orientation be provided as a parameter. *

                - * A glyph has no shape, see {@link Inter} class for glyph interpretation and - * {@link org.audiveris.omr.classifier.Sample} class for training of shape classifiers. + * A glyph has no shape, see {@link org.audiveris.omr.sig.inter.Inter} class for glyph + * interpretation and {@link org.audiveris.omr.classifier.Sample} class for training of shape + * classifiers. *

                * Additional features are made available via the {@link Glyphs} class. * * @author Hervé Bitteur */ -@XmlJavaTypeAdapter(BasicGlyph.Adapter.class) -public interface Glyph - extends WeightedEntity, NearLine +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = "glyph") +public class Glyph + extends AbstractWeightedEntity + implements NearLine { - //~ Methods ------------------------------------------------------------------------------------ + + private static final Logger logger = LoggerFactory.getLogger(Glyph.class); + + // Persistent data + //---------------- + // + /** Absolute abscissa of the glyph top left corner. */ + @XmlAttribute(name = "left") + protected final int left; + + /** Absolute ordinate of the glyph top left corner. */ + @XmlAttribute(name = "top") + protected final int top; + + /** Runs of pixels that compose the glyph. Gives all runs, thus width, height, etc... */ + @XmlElement(name = "run-table") + protected final RunTable runTable; + + // Transient data + //--------------- + // + /** The containing glyph index, if any. */ + @Navigable(false) + protected GlyphIndex index; + + /** Computed ART Moments. */ + protected ARTMoments artMoments; + + /** Computed geometric Moments. */ + protected GeometricMoments geoMoments; + + /** Mass center coordinates. */ + protected Point centroid; + + /** Box center coordinates. */ + protected Point center; + + /** Best straight line equation. */ + protected Line2D line; + + /** Line elements. */ + protected BasicLine basicLine; + + /** Absolute slope of the line WRT abscissa axis. */ + protected Double slope; + + /** + * Creates a new {@code BasicGlyph} object. + * + * @param left abscissa of top left corner + * @param top ordinate of top left corner + * @param runTable table of runs (cannot be null) + */ + public Glyph (int left, + int top, + RunTable runTable) + { + this.left = left; + this.top = top; + + ///Objects.requireNonNull(runTable, "Glyph created with null runTable"); + // NOTA: We must accept null RunTable for the Sample no-arg constructor + this.runTable = runTable; + } + + /** + * No-arg constructor meant for JAXB. + */ + private Glyph () + { + this.left = 0; + this.top = 0; + this.runTable = null; + } + + //----------// + // contains // + //----------// + @Override + public boolean contains (Point point) + { + if (getBounds().contains(point)) { + final Point relPoint = new Point(point.x - left, point.y - top); + + return runTable.contains(relPoint); + } + + return false; + } + + @Override + public String dumpOf () + { + StringBuilder sb = new StringBuilder(); + + // Admin + sb.append( + String.format( + "%s#%s @%s%n", + getClass().getSimpleName(), + id, + Integer.toHexString(hashCode()))); + + if (isVip()) { + sb.append(String.format(" vip%n")); + } + + sb.append(String.format(" bounds=%s%n", getBounds())); + sb.append(String.format(" groups=%s%n", groups)); + + ///sb.append(String.format(" nest=%s%n", getIndex())); + // Display + if (attachments != null) { + sb.append(String.format(" attachments=%s%n", attachments)); + } + + return sb.toString(); + } + + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (getClass() != obj.getClass()) { + return false; + } + + final Glyph other = (Glyph) obj; + + if (this.left != other.left) { + return false; + } + + if (this.top != other.top) { + return false; + } + + return Objects.equals(this.runTable, other.runTable); + } /** * Fill the provided table with glyph foreground pixels. @@ -68,23 +235,62 @@ public interface Glyph * @param tableOrigin absolute origin of table * @param fat true to add touching locations */ - void fillTable (Table.UnsignedByte table, - Point tableOrigin, - boolean fat); + public void fillTable (Table.UnsignedByte table, + Point tableOrigin, + boolean fat) + { + runTable.fillTable(table, tableOrigin, getTopLeft(), fat); + } /** * Report the glyph ART moments. * * @return the glyph ART moments */ - ARTMoments getARTMoments (); + public ARTMoments getARTMoments () + { + if (artMoments == null) { + artMoments = runTable.computeArtMoments(left, top); + } + + return artMoments; + } + + @Override + public Rectangle getBounds () + { + return new Rectangle(left, top, runTable.getWidth(), runTable.getHeight()); + } /** * Report a buffer of the glyph (which can be handed to the OCR) * * @return a black & white buffer (contour box size) */ - ByteProcessor getBuffer (); + public ByteProcessor getBuffer () + { + return runTable.getBuffer(); + } + + @Override + public Point getCenter () + { + if (center == null) { + center = new Point(left + (runTable.getWidth() / 2), top + (runTable.getHeight() / 2)); + } + + return center; + } + + @Override + public Point getCentroid () + { + if (centroid == null) { + centroid = runTable.computeCentroid(left, top); + } + + return centroid; + } /** * Report the glyph geometric moments. @@ -92,28 +298,185 @@ void fillTable (Table.UnsignedByte table, * @param interline the global sheet interline * @return the glyph geometric moments */ - GeometricMoments getGeometricMoments (int interline); + public GeometricMoments getGeometricMoments (int interline) + { + if (geoMoments == null) { + geoMoments = runTable.computeGeometricMoments(left, top, interline); + } + + return geoMoments; + } + + @Override + public int getHeight () + { + return runTable.getHeight(); + } /** * Report the containing glyph index * * @return the containing index */ - GlyphIndex getIndex (); + public GlyphIndex getIndex () + { + return index; + } + + /** + * The setter for glyph index. + * + * @param index the containing glyph index + */ + public void setIndex (GlyphIndex index) + { + this.index = index; + } + + @Override + public double getInvertedSlope () + { + checkLine(); + + return LineUtil.getInvertedSlope(line); + } + + @Override + public int getLeft () + { + return left; + } + + @Override + public int getLength (Orientation orientation) + { + if (orientation == HORIZONTAL) { + return runTable.getWidth(); + } else { + return runTable.getHeight(); + } + } + + @Override + public Line2D getLine () + { + checkLine(); + + return line; + } + + @Override + public double getMeanDistance () + { + checkLine(); + + return basicLine.getMeanDistance(); + } /** * Report the underlying table of runs * * @return the glyph runTable */ - RunTable getRunTable (); + public RunTable getRunTable () + { + return runTable; + } + + @Override + public double getSlope () + { + if (slope == null) { + checkLine(); + + slope = LineUtil.getSlope(line); + } + + return slope; + } + + @Override + public Point2D getStartPoint (Orientation orientation) + { + checkLine(); + + if (orientation == HORIZONTAL) { + // Use left side + if (line.getX1() <= line.getX2()) { + return line.getP1(); + } else { + return line.getP2(); + } + } else if (line.getY1() <= line.getY2()) { + return line.getP1(); + } else { + return line.getP2(); + } + } + + @Override + public Point2D getStopPoint (Orientation orientation) + { + checkLine(); + + if (orientation == HORIZONTAL) { + // Use right side + if (line.getX2() >= line.getX1()) { + return line.getP2(); + } else { + return line.getP1(); + } + } else if (line.getY2() >= line.getY1()) { + return line.getP2(); + } else { + return line.getP1(); + } + } + + @Override + public int getTop () + { + return top; + } + + @Override + public Point getTopLeft () + { + return new Point(left, top); + } + + @Override + public int getWeight () + { + return runTable.getWeight(); + } + + @Override + public int getWidth () + { + return runTable.getWidth(); + } + + @Override + public int hashCode () + { + int hash = 5; + hash = (79 * hash) + this.left; + hash = (79 * hash) + this.top; + hash = (79 * hash) + Objects.hashCode(this.runTable); + + return hash; + } /** * Report a short glyph reference * * @return glyph reference */ - String idString (); + public String idString () + { + return "glyph#" + id; + } /** * Report whether the glyph has a pixel in common with the provided table. @@ -122,16 +485,65 @@ void fillTable (Table.UnsignedByte table, * @param tableOrigin top-left corner of table * @return true if connection found */ - boolean intersects (Table.UnsignedByte table, - Point tableOrigin); + public boolean intersects (Table.UnsignedByte table, + Point tableOrigin) + { + return runTable.intersects(table, tableOrigin, getTopLeft()); + } /** - * Check whether the glyph intersects the provided AWT shape. + * Report whether the glyph intersects the provided AWT shape. * - * @param shape the provided awt shape + * @param shape the provided AWT shape * @return true if intersection is not empty, false otherwise */ - boolean intersects (Shape shape); + public boolean intersects (Shape shape) + { + // First make a rough test + Rectangle bounds = getBounds(); + + if (shape.intersects(bounds)) { + Rectangle clip = bounds.intersection(shape.getBounds()); + + if (runTable.getOrientation() == HORIZONTAL) { + final int minSeq = clip.y - top; + final int maxSeq = (clip.y - top + clip.height) - 1; + + for (int iSeq = minSeq; iSeq <= maxSeq; iSeq++) { + for (Iterator it = runTable.iterator(iSeq); it.hasNext();) { + final Run run = it.next(); + + if (shape.intersects( + left + run.getStart(), + top + iSeq, + run.getLength(), + 1)) { + return true; + } + } + } + } else { + final int minSeq = clip.x - left; + final int maxSeq = (clip.x - left + clip.width) - 1; + + for (int iSeq = minSeq; iSeq <= maxSeq; iSeq++) { + for (Iterator it = runTable.iterator(iSeq); it.hasNext();) { + Run run = it.next(); + + if (shape.intersects( + left + iSeq, + top + run.getStart(), + 1, + run.getLength())) { + return true; + } + } + } + } + } + + return false; + } /** * Report whether this glyph is identical to that glyph @@ -139,26 +551,147 @@ boolean intersects (Table.UnsignedByte table, * @param that the other glyph * @return true if their pixels are identical */ - boolean isIdentical (Glyph that); + public boolean isIdentical (Glyph that) + { + if (this.getTop() != that.getTop()) { + return false; + } + if (this.getLeft() != that.getLeft()) { + return false; + } + + if (this.getWeight() != that.getWeight()) { + return false; + } + + //TODO: we should accept different runTable orientations? + return this.runTable.equals(that.runTable); + } + + @Override + public void renderLine (Graphics2D g) + { + Rectangle clip = g.getClipBounds(); + + if ((clip == null) || clip.intersects(getBounds())) { + checkLine(); // To make sure the line has been computed + + if (line != null) { + ///g.draw(line); + g.draw( + new Line2D.Double( + line.getX1(), + line.getY1() + 0.5, + line.getX2() + 1, + line.getY2() + 0.5)); + } + } + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(); + + sb.append(getClass().getSimpleName()).append("{").append("#").append(getId()); + + sb.append(internals()); + + sb.append("}"); + + return sb.toString(); + } + + //-----------// + // internals // + //-----------// /** - * Test whether the glyph is transient (not yet inserted into the index) + * Return the internals of this class, typically for inclusion in a toString. * - * @return true if transient + * @return the string of internals */ - boolean isTransient (); + @Override + protected String internals () + { + StringBuilder sb = new StringBuilder(super.internals()); + + if ((groups != null) && !groups.isEmpty()) { + sb.append(' ').append(groups); + } + + return sb.toString(); + } + //-----------// + // checkLine // + //-----------// /** - * Report whether this glyph is virtual (rather than real) - * - * @return true if virtual + * Make sure the approximating line is available */ - boolean isVirtual (); + private void checkLine () + { + if (line == null) { + computeLine(); + } + } + + //-------------// + // computeLine // + //-------------// + private void computeLine () + { + basicLine = new BasicLine(); + + final boolean isHori = runTable.getOrientation() == HORIZONTAL; + + for (int iSeq = 0, iBreak = runTable.getSize(); iSeq < iBreak; iSeq++) { + for (Iterator it = runTable.iterator(iSeq); it.hasNext();) { + Run run = it.next(); + int start = run.getStart(); + + for (int ic = run.getLength() - 1; ic >= 0; ic--) { + if (isHori) { + basicLine.includePoint(left + start + ic, top + iSeq); + } else { + basicLine.includePoint(left + iSeq, top + start + ic); + } + } + } + } + + // We have a problem if glyph is just 0 or 1 pixel: no computable slope! + switch (basicLine.getNumberOfPoints()) { + case 0: + throw new IllegalStateException("Glyph has no pixel, cannot compute line."); + + case 1: + slope = 0d; // we just need a value. + break; + + default: + slope = basicLine.getSlope(); + + break; + } + + line = basicLine.toDouble(); + } + + //--------------------// + // getPointsCollector // + //--------------------// /** - * The setter for glyph nest. + * Cumulate absolute points from all runs. * - * @param index the containing glyph index + * @return a populated point collector */ - void setIndex (GlyphIndex index); + private PointsCollector getPointsCollector () + { + final PointsCollector collector = new PointsCollector(null, getWeight()); + runTable.cumulate(collector, new Point(left, top)); + + return collector; + } } diff --git a/src/main/org/audiveris/omr/glyph/GlyphCluster.java b/src/main/org/audiveris/omr/glyph/GlyphCluster.java index 707a550a8..de5eaf33f 100644 --- a/src/main/org/audiveris/omr/glyph/GlyphCluster.java +++ b/src/main/org/audiveris/omr/glyph/GlyphCluster.java @@ -21,8 +21,6 @@ // package org.audiveris.omr.glyph; -import org.audiveris.omr.glyph.GlyphGroup; - import org.jgrapht.Graphs; import org.jgrapht.graph.SimpleGraph; @@ -41,29 +39,28 @@ * Class {@code GlyphCluster} handles a cluster of connected glyphs, to retrieve all * acceptable compounds built on subsets of these glyphs. *

                - * The processing of any given subset consists in the following:

                  + * The processing of any given subset consists in the following: + *
                    *
                  1. Build the compound of chosen vertices, and record acceptable evaluations.
                  2. *
                  3. Build the set of new reachable vertices.
                  4. *
                  5. For each reachable vertex, recursively process the new set composed of current set + the - * reachable vertex.
                  + * reachable vertex. + *
                * TODO: implement a non-recursive version for better efficiency? * * @author Hervé Bitteur */ public class GlyphCluster { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GlyphCluster.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Environment adapter. */ private final Adapter adapter; /** Group, if any, to be assigned to created glyphs. */ private final GlyphGroup group; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Cluster object, with an adapter to the environment. * @@ -77,49 +74,6 @@ public GlyphCluster (Adapter adapter, this.group = group; } - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // getSubGraph // - //-------------// - /** - * Extract a subgraph limited to the provided set of glyphs. - * - * @param set the provided set of glyphs - * @param graph the global graph to extract from - * @param checkEdges true if glyph edges may point outside the provided set. - * @return the graph limited to glyph set and related edges - */ - public static SimpleGraph getSubGraph (Set set, - SimpleGraph graph, - boolean checkEdges) - { - // Which edges should be extracted for this set? - Set setEdges = new LinkedHashSet(); - - for (Glyph glyph : set) { - Set glyphEdges = graph.edgesOf(glyph); - - if (!checkEdges) { - setEdges.addAll(glyphEdges); // Take all edges - } else { - // Keep only the edges that link within the set - for (GlyphLink link : glyphEdges) { - Glyph opposite = Graphs.getOppositeVertex(graph, link, glyph); - - if (set.contains(opposite)) { - setEdges.add(link); - } - } - } - } - - SimpleGraph subGraph = new SimpleGraph(GlyphLink.class); - Graphs.addAllVertices(subGraph, set); - Graphs.addAllEdges(subGraph, graph, setEdges); - - return subGraph; - } - //-----------// // decompose // //-----------// @@ -128,7 +82,7 @@ public static SimpleGraph getSubGraph (Set set, */ public void decompose () { - final Set considered = new LinkedHashSet(); // Parts considered so far + final Set considered = new LinkedHashSet<>(); // Parts considered so far //TODO: we could truncate this list by discarding the smallest items // since a too large list would result in explosion of combinations @@ -151,7 +105,7 @@ public void decompose () */ private Set getOutliers (Set set) { - Set outliers = new LinkedHashSet(); + Set outliers = new LinkedHashSet<>(); for (Glyph part : set) { outliers.addAll(adapter.getNeighbors(part)); @@ -212,7 +166,7 @@ private void process (Set parts, ///logger.debug(" {}", Glyphs.ids("outliers", outliers)); Rectangle setBox = Glyphs.getBounds(parts); - Set newConsidered = new LinkedHashSet(seen); + Set newConsidered = new LinkedHashSet<>(seen); for (Glyph outlier : outliers) { newConsidered.add(outlier); @@ -221,20 +175,60 @@ private void process (Set parts, Rectangle symBox = outlier.getBounds().union(setBox); if (!adapter.isTooLarge(symBox)) { - Set largerSet = new LinkedHashSet(parts); + Set largerSet = new LinkedHashSet<>(parts); largerSet.add(outlier); process(largerSet, newConsidered); } } } - //~ Inner Interfaces --------------------------------------------------------------------------- + //-------------// + // getSubGraph // + //-------------// + /** + * Extract a subgraph limited to the provided set of glyphs. + * + * @param set the provided set of glyphs + * @param graph the global graph to extract from + * @param checkEdges true if glyph edges may point outside the provided set. + * @return the graph limited to glyph set and related edges + */ + public static SimpleGraph getSubGraph (Set set, + SimpleGraph graph, + boolean checkEdges) + { + // Which edges should be extracted for this set? + Set setEdges = new LinkedHashSet<>(); + for (Glyph glyph : set) { + Set glyphEdges = graph.edgesOf(glyph); + + if (!checkEdges) { + setEdges.addAll(glyphEdges); // Take all edges + } else { + // Keep only the edges that link within the set + for (GlyphLink link : glyphEdges) { + Glyph opposite = Graphs.getOppositeVertex(graph, link, glyph); + + if (set.contains(opposite)) { + setEdges.add(link); + } + } + } + } + SimpleGraph subGraph = new SimpleGraph<>(GlyphLink.class); + Graphs.addAllVertices(subGraph, set); + Graphs.addAllEdges(subGraph, graph, setEdges); + return subGraph; + } + //---------// // Adapter // //---------// + /** + * Interface to be implemented by a user of GlyphCluster. + */ public static interface Adapter { - //~ Methods -------------------------------------------------------------------------------- /** * Evaluate a provided glyph and create all acceptable inter instances. @@ -293,25 +287,16 @@ void evaluateGlyph (Glyph glyph, boolean isTooSmall (Rectangle bounds); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------------// - // AbstractAdapter // - //-----------------// - /** - * Basis for implementing an adapter. - */ public abstract static class AbstractAdapter implements Adapter { - //~ Instance fields ------------------------------------------------------------------------ + + /** For debug only */ + public int trials = 0; /** Graph of the connected glyphs, with their distance edges if any. */ protected final SimpleGraph graph; - // For debug only - public int trials = 0; - - //~ Constructors --------------------------------------------------------------------------- /** * Build an adapter from a set of parts and the maximum gap between parts. * The connectivity graph is built internally. @@ -336,7 +321,6 @@ public AbstractAdapter (SimpleGraph graph) this.graph = graph; } - //~ Methods -------------------------------------------------------------------------------- @Override public List getNeighbors (Glyph part) { @@ -346,7 +330,7 @@ public List getNeighbors (Glyph part) @Override public List getParts () { - return new ArrayList(graph.vertexSet()); + return new ArrayList<>(graph.vertexSet()); } @Override diff --git a/src/main/org/audiveris/omr/glyph/GlyphDistances.java b/src/main/org/audiveris/omr/glyph/GlyphDistances.java index ba6de54d1..77c709aaf 100644 --- a/src/main/org/audiveris/omr/glyph/GlyphDistances.java +++ b/src/main/org/audiveris/omr/glyph/GlyphDistances.java @@ -37,7 +37,6 @@ */ public class GlyphDistances { - //~ Instance fields ---------------------------------------------------------------------------- /** Table of distances around the glyph. */ private final DistanceTable distTable; @@ -45,7 +44,6 @@ public class GlyphDistances /** Table bounds. (Generally somewhat larger than glyph bounds) */ private final Rectangle tableBox; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new GlyphDistances object, around a provided glyph. * @@ -59,7 +57,6 @@ public GlyphDistances (Glyph glyph, distTable = new Distances().compute(glyph, tableBox); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // distanceTo // //------------// @@ -141,14 +138,12 @@ public double distanceTo (Glyph other) return (double) bestDist / distTable.getNormalizer(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Distances // //-----------// private static class Distances extends ChamferDistance.Short { - //~ Methods -------------------------------------------------------------------------------- public DistanceTable compute (Glyph glyph, Rectangle box) diff --git a/src/main/org/audiveris/omr/glyph/GlyphFactory.java b/src/main/org/audiveris/omr/glyph/GlyphFactory.java index 1d4e93913..c5334ec18 100644 --- a/src/main/org/audiveris/omr/glyph/GlyphFactory.java +++ b/src/main/org/audiveris/omr/glyph/GlyphFactory.java @@ -23,11 +23,8 @@ import ij.process.ByteProcessor; -import org.audiveris.omr.glyph.GlyphGroup; import org.audiveris.omr.run.MarkedRun; - import static org.audiveris.omr.run.Orientation.VERTICAL; - import org.audiveris.omr.run.Run; import org.audiveris.omr.run.RunTable; import org.audiveris.omr.run.RunTableFactory; @@ -60,11 +57,9 @@ */ public class GlyphFactory { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GlyphFactory.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Source runs. */ private final RunTable runTable; @@ -75,7 +70,7 @@ public class GlyphFactory private final GlyphGroup group; /** Global list of all glyphs created. */ - private final List created = new ArrayList(); + private final List created = new ArrayList<>(); /** Global id to assign glyph marks. */ private int globalMark; @@ -84,12 +79,11 @@ public class GlyphFactory private final List> markedTable; /** Merges (child => parent). (numerical invariant: child > parent) */ - private final Map merges = new HashMap(); + private final Map merges = new HashMap<>(); /** Most efficient way to use merging information. */ private int[] lut; - //~ Constructors ------------------------------------------------------------------------------- private GlyphFactory (RunTable runTable, Point offset, GlyphGroup group) @@ -99,72 +93,13 @@ private GlyphFactory (RunTable runTable, this.group = group; // Allocate & initialize markedTable - markedTable = new ArrayList>(runTable.getSize()); + markedTable = new ArrayList<>(runTable.getSize()); for (int iseq = 0, size = runTable.getSize(); iseq < size; iseq++) { markedTable.add(new ArrayList()); } } - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // buildGlyph // - //------------// - /** - * Build one glyph from a collection of glyph parts. - * - * @param parts the provided glyph parts - * @return the glyph compound - */ - public static Glyph buildGlyph (Collection parts) - { - final Rectangle box = Glyphs.getBounds(parts); - final ByteProcessor buffer = new ByteProcessor(box.width, box.height); - ByteUtil.raz(buffer); // buffer.invert(); - - for (Glyph part : parts) { - part.getRunTable().write(buffer, part.getLeft() - box.x, part.getTop() - box.y); - } - - final RunTable runTable = new RunTableFactory(VERTICAL).createTable(buffer); - - return new BasicGlyph(box.x, box.y, runTable); - } - - //-------------// - // buildGlyphs // - //-------------// - /** - * Create a collection of glyphs out of the provided RunTable. - * - * @param runTable the source table of runs - * @param offset offset of runTable WRT absolute origin - * @return the list of glyphs created - */ - public static List buildGlyphs (RunTable runTable, - Point offset) - { - return new GlyphFactory(runTable, offset, null).process(); - } - - //-------------// - // buildGlyphs // - //-------------// - /** - * Create a collection of glyphs out of the provided RunTable. - * - * @param runTable the source table of runs - * @param offset offset of runTable WRT absolute origin - * @param group targeted group, if any - * @return the list of glyphs created - */ - public static List buildGlyphs (RunTable runTable, - Point offset, - GlyphGroup group) - { - return new GlyphFactory(runTable, offset, group).process(); - } - /** * Build all the ancestor glyphs from the markedTable. */ @@ -173,7 +108,7 @@ private void buildAllGlyphs () logger.debug("glyphs: {}", globalMark - merges.size()); // Allocate & initialize glyph bufs - final List> bufs = new ArrayList>(lut.length); + final List> bufs = new ArrayList<>(lut.length); for (int i = 0, len = lut.length; i < len; i++) { if ((i > 0) && (lut[i] == i)) { @@ -251,7 +186,7 @@ private void buildGlyph (int mark, } // Store created glyph - final Glyph glyph = new BasicGlyph(offset.x + dx, offset.y + dy, table); + final Glyph glyph = new Glyph(offset.x + dx, offset.y + dy, table); glyph.addGroup(group); created.add(glyph); } @@ -390,7 +325,64 @@ private void scanTable () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------// + // buildGlyph // + //------------// + /** + * Build one glyph from a collection of glyph parts. + * + * @param parts the provided glyph parts + * @return the glyph compound + */ + public static Glyph buildGlyph (Collection parts) + { + final Rectangle box = Glyphs.getBounds(parts); + final ByteProcessor buffer = new ByteProcessor(box.width, box.height); + ByteUtil.raz(buffer); // buffer.invert(); + + for (Glyph part : parts) { + part.getRunTable().write(buffer, part.getLeft() - box.x, part.getTop() - box.y); + } + + final RunTable runTable = new RunTableFactory(VERTICAL).createTable(buffer); + + return new Glyph(box.x, box.y, runTable); + } + + //-------------// + // buildGlyphs // + //-------------// + /** + * Create a collection of glyphs out of the provided RunTable. + * + * @param runTable the source table of runs + * @param offset offset of runTable WRT absolute origin + * @return the list of glyphs created + */ + public static List buildGlyphs (RunTable runTable, + Point offset) + { + return new GlyphFactory(runTable, offset, null).process(); + } + + //-------------// + // buildGlyphs // + //-------------// + /** + * Create a collection of glyphs out of the provided RunTable. + * + * @param runTable the source table of runs + * @param offset offset of runTable WRT absolute origin + * @param group targeted group, if any + * @return the list of glyphs created + */ + public static List buildGlyphs (RunTable runTable, + Point offset, + GlyphGroup group) + { + return new GlyphFactory(runTable, offset, group).process(); + } + //----------// // Sequence // //----------// @@ -400,14 +392,12 @@ private void scanTable () */ private static class Sequence { - //~ Instance fields ------------------------------------------------------------------------ final int iSeq; // Index in runTable - final List runs = new ArrayList(); // Sequence of glyph marked runs + final List runs = new ArrayList<>(); // Sequence of glyph marked runs - //~ Constructors --------------------------------------------------------------------------- - public Sequence (int iSeq) + Sequence (int iSeq) { this.iSeq = iSeq; } diff --git a/src/main/org/audiveris/omr/glyph/GlyphGroup.java b/src/main/org/audiveris/omr/glyph/GlyphGroup.java index 3656250b3..8ea1ce7da 100644 --- a/src/main/org/audiveris/omr/glyph/GlyphGroup.java +++ b/src/main/org/audiveris/omr/glyph/GlyphGroup.java @@ -1,59 +1,60 @@ -//------------------------------------------------------------------------------------------------// -// // -// G l y p h G r o u p // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.glyph; - -/** - * This enumeration is used to group glyph instances by their intended use. - *

                - * A single glyph instance can be assigned several groups. - * - * @author Hervé Bitteur - */ -public enum GlyphGroup -{ - /** Only the first 4 ones are needed. */ - BEAM_SPOT("Beam-oriented spot"), - BLACK_HEAD_SPOT("BlackHead-oriented spot"), - BLACK_STACK_SPOT("Stack of blackHead-oriented spot"), - HEAD_SPOT("Head-oriented spot"), - VERTICAL_SEED("Vertical seed"), - SYMBOL("Fixed symbol"), - /** - * The remaining ones below are not needed. But may still exist in .omr files... - */ - STAFF_LINE("Staff Line"), - LEDGER("Ledger"), - LEDGER_CANDIDATE("Ledger candidate"), - WEAK_PART("Optional part"), - TIME_PART("Part of time sig"), - ALTER_PART("Part of alteration"), - CLEF_PART("Part of clef"), - DROP("DnD glyph"); - - /** Role of the group. */ - public final String description; - - GlyphGroup (String description) - { - this.description = description; - } -} +//------------------------------------------------------------------------------------------------// +// // +// G l y p h G r o u p // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.glyph; + +/** + * This enumeration is used to group glyph instances by their intended use. + *

                + * A single glyph instance can be assigned several groups. + * + * @author Hervé Bitteur + */ +public enum GlyphGroup +{ + /** Only the first 4 ones are needed. */ + BEAM_SPOT("Beam-oriented spot"), + BLACK_HEAD_SPOT("BlackHead-oriented spot"), + BLACK_STACK_SPOT("Stack of blackHead-oriented spot"), + HEAD_SPOT("Head-oriented spot"), + VERTICAL_SEED("Vertical seed"), + SYMBOL("Fixed symbol"), + + /** + * The remaining ones below are not needed. But may still exist in .omr files... + */ + STAFF_LINE("Staff Line"), + LEDGER("Ledger"), + LEDGER_CANDIDATE("Ledger candidate"), + WEAK_PART("Optional part"), + TIME_PART("Part of time sig"), + ALTER_PART("Part of alteration"), + CLEF_PART("Part of clef"), + DROP("DnD glyph"); + + /** Role of the group. */ + public final String description; + + GlyphGroup (String description) + { + this.description = description; + } +} diff --git a/src/main/org/audiveris/omr/glyph/GlyphIndex.java b/src/main/org/audiveris/omr/glyph/GlyphIndex.java index 74cb0aaaf..565aa91a8 100644 --- a/src/main/org/audiveris/omr/glyph/GlyphIndex.java +++ b/src/main/org/audiveris/omr/glyph/GlyphIndex.java @@ -65,20 +65,17 @@ public class GlyphIndex implements EntityIndex { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - GlyphIndex.class); + private static final Logger logger = LoggerFactory.getLogger(GlyphIndex.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- /** - * See getEntities()/setEntities() methods which are called by - * BasicSheet#getGlyphIndexContent() and BasicSheet#setGlyphIndexContent() + * See {@link #getEntities()} and {@link #setEntities(java.util.ArrayList)} methods + * which are called by private methods + * Sheet#getGlyphIndexContent() and Sheet#setGlyphIndexContent() * triggered by JAXB (un)marshalling. */ // @@ -89,12 +86,11 @@ public class GlyphIndex private final WeakGlyphIndex weakIndex = new WeakGlyphIndex(); /** Collection of original glyph instances, non sorted. */ - private final ConcurrentHashMap originals = new ConcurrentHashMap(); + private final ConcurrentHashMap originals = new ConcurrentHashMap<>(); /** Selection service, if any. */ - private EntityService glyphService; + private GlyphService glyphService; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code GlyphIndex} object. */ @@ -102,7 +98,6 @@ public GlyphIndex () { } - //~ Methods ------------------------------------------------------------------------------------ //----------------------// // getContainedEntities // //----------------------// @@ -128,7 +123,7 @@ public List getContainingEntities (Point point) public ArrayList getEntities () { Collection weaks = weakIndex.getEntities(); - ArrayList glyphs = new ArrayList(); + ArrayList glyphs = new ArrayList<>(); for (WeakGlyph weak : weaks) { final Glyph glyph = weak.get(); @@ -141,6 +136,23 @@ public ArrayList getEntities () return glyphs; } + //-------------// + // setEntities // + //-------------// + /** + * Populate the weak index. + * + * @param glyphs populating glyphs + */ + public void setEntities (ArrayList glyphs) + { + for (Glyph glyph : glyphs) { + WeakGlyph weak = new WeakGlyph(glyph); + weakIndex.insert(weak); + originals.putIfAbsent(weak, weak); + } + } + @Override public Glyph getEntity (int id) { @@ -154,11 +166,21 @@ public Glyph getEntity (int id) } @Override - public EntityService getEntityService () + public GlyphService getEntityService () { return glyphService; } + //------------------// + // setEntityService // + //------------------// + @Override + public void setEntityService (EntityService entityService) + { + this.glyphService = (GlyphService) entityService; + entityService.connect(); + } + @Override public int getIdAfter (int id) { @@ -177,6 +199,15 @@ public int getLastId () return weakIndex.getLastId(); } + //-----------// + // setLastId // + //-----------// + @Override + public void setLastId (int lastId) + { + weakIndex.setLastId(lastId); + } + //---------// // getName // //---------// @@ -228,6 +259,11 @@ public List getSelectedGlyphList () //----------------// // initTransients // //----------------// + /** + * Initialize the transient members of the class. + * + * @param sheet containing sheet + */ public final void initTransients (Sheet sheet) { // ID generator @@ -289,14 +325,13 @@ public Iterator iterator () public void publish (final Glyph glyph) { if (glyphService != null) { - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () { glyphService.publish( - new EntityListEvent( + new EntityListEvent<>( this, SelectionHint.ENTITY_INIT, MouseMovement.PRESSING, @@ -370,37 +405,6 @@ public void reset () originals.clear(); } - //-------------// - // setEntities // - //-------------// - public void setEntities (ArrayList glyphs) - { - for (Glyph glyph : glyphs) { - WeakGlyph weak = new WeakGlyph(glyph); - weakIndex.insert(weak); - originals.putIfAbsent(weak, weak); - } - } - - //------------------// - // setEntityService // - //------------------// - @Override - public void setEntityService (EntityService entityService) - { - this.glyphService = entityService; - entityService.connect(); - } - - //-----------// - // setLastId // - //-----------// - @Override - public void setLastId (int lastId) - { - weakIndex.setLastId(lastId); - } - //-----------------// // privateRegister // //-----------------// @@ -427,69 +431,38 @@ private int privateRegister (Glyph glyph) return id; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String vipGlyphs = new Constant.String( "", "(Debug) Comma-separated values of VIP glyphs IDs"); } - //----------------// - // WeakGlyphIndex // - //----------------// - private static class WeakGlyphIndex - extends BasicIndex - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public void insert (WeakGlyph weak) - { - super.insert(weak); - } - - @Override - protected boolean isValid (WeakGlyph weak) - { - return (weak != null) && (weak.get() != null); - } - - void setIdGenerator (AtomicInteger lastId) - { - this.lastId = lastId; - } - } - //------------------// // SkippingIterator // //------------------// /** * This iterator skips the weak references that are no longer valid. */ - private class SkippingIterator + private static class SkippingIterator implements Iterator { - //~ Instance fields ------------------------------------------------------------------------ private final Iterator weakIt; // Underlying iterator on all weak references private Glyph nextGlyph = null; // Concrete glyph to be returned by next() - //~ Constructors --------------------------------------------------------------------------- - public SkippingIterator (Iterator weakIt) + SkippingIterator (Iterator weakIt) { this.weakIt = weakIt; nextGlyph = findNextGlyph(); } - //~ Methods -------------------------------------------------------------------------------- @Override public boolean hasNext () { @@ -529,4 +502,29 @@ private Glyph findNextGlyph () return null; } } + + //----------------// + // WeakGlyphIndex // + //----------------// + private static class WeakGlyphIndex + extends BasicIndex + { + + @Override + public void insert (WeakGlyph weak) + { + super.insert(weak); + } + + @Override + protected boolean isValid (WeakGlyph weak) + { + return (weak != null) && (weak.get() != null); + } + + void setIdGenerator (AtomicInteger lastId) + { + this.lastId = lastId; + } + } } diff --git a/src/main/org/audiveris/omr/glyph/GlyphLayer.java b/src/main/org/audiveris/omr/glyph/GlyphLayer.java index be2e33de1..f6a2540cd 100644 --- a/src/main/org/audiveris/omr/glyph/GlyphLayer.java +++ b/src/main/org/audiveris/omr/glyph/GlyphLayer.java @@ -23,22 +23,21 @@ /** * Enum {@code GlyphLayer} defines populations of glyph instances. + * + * @author Hervé Bitteur */ public enum GlyphLayer { /** Glyph instances from initial binary image. */ DEFAULT("B", "Glyphs from initial binary image"), - /** Horizontal glyph instances from ledgers and endings. */ LEDGER("L", "Horizontal glyphs from ledgers and endings"), - /** Glyph instances from foreground spots. */ SPOT("S", "Glyphs from foreground spots"), /** Glyph instances from symbols. */ SYMBOL("Y", "Glyphs from symbols"), /** Virtual glyph instances from Drag and Drop. */ DROP("D", "Virtual glyphs from Drag n' Drop"), - /** Glyph instances unmarshalled from XML file. */ XML("X", "Sample glyphs unmarshalled from XML file"); @@ -58,6 +57,11 @@ private GlyphLayer (String key, //----------------// // concreteValues // //----------------// + /** + * Report which layers are really used. + * + * @return layers in use + */ public static GlyphLayer[] concreteValues () { return concreteValues; diff --git a/src/main/org/audiveris/omr/glyph/GlyphLink.java b/src/main/org/audiveris/omr/glyph/GlyphLink.java index 263e3ef23..36e8db3f9 100644 --- a/src/main/org/audiveris/omr/glyph/GlyphLink.java +++ b/src/main/org/audiveris/omr/glyph/GlyphLink.java @@ -29,7 +29,6 @@ */ public interface GlyphLink { - //~ Inner Classes ------------------------------------------------------------------------------ /** * Neighborhood relationship. @@ -37,12 +36,10 @@ public interface GlyphLink public static class Nearby implements GlyphLink { - //~ Instance fields ------------------------------------------------------------------------ /** Measured distance between the two glyph instances. */ private final double distance; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Nearby object. * @@ -53,7 +50,11 @@ public Nearby (double distance) this.distance = distance; } - //~ Methods -------------------------------------------------------------------------------- + /** + * Report the link distance + * + * @return distance + */ public double getDistance () { return distance; diff --git a/src/main/org/audiveris/omr/glyph/GlyphSignature.java b/src/main/org/audiveris/omr/glyph/GlyphSignature.java deleted file mode 100644 index 6c2fef9ef..000000000 --- a/src/main/org/audiveris/omr/glyph/GlyphSignature.java +++ /dev/null @@ -1,150 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// G l y p h S i g n a t u r e // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.glyph; - -import org.audiveris.omr.moments.GeometricMoments; - -/** - * Class {@code GlyphSignature} is used to implement a map of glyphs, - * based only on their physical properties. - *

                - * The signature is implemented using the glyph moments.

                - * - * @author Hervé Bitteur - */ -public class GlyphSignature - implements Comparable -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** Glyph absolute weight */ - private final int weight; - - /** Glyph normalized moments */ - private final GeometricMoments moments; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new GlyphSignature object. - * - * @param glyph the glyph to compute signature upon - * @param interline global sheet interline - */ - public GlyphSignature (Glyph glyph, - int interline) - { - weight = glyph.getWeight(); - moments = new GeometricMoments(glyph.getGeometricMoments(interline)); - } - - //----------------// - // GlyphSignature // - //----------------// - /** - * Needed by JAXB. - */ - private GlyphSignature () - { - weight = 0; - moments = null; - } - - //~ Methods ------------------------------------------------------------------------------------ - //-----------// - // compareTo // - //-----------// - @Override - public int compareTo (GlyphSignature other) - { - if (weight < other.weight) { - return -1; - } else if (weight > other.weight) { - return 1; - } - - final double[] values = moments.getValues(); - final double[] otherValues = other.moments.getValues(); - - for (int i = 0; i < values.length; i++) { - int cmp = Double.compare(values[i], otherValues[i]); - - if (cmp != 0) { - return cmp; - } - } - - return 0; // Equal - } - - //--------// - // equals // - //--------// - @Override - public boolean equals (Object obj) - { - if (obj == this) { - return true; - } - - if (obj instanceof GlyphSignature) { - return compareTo((GlyphSignature) obj) == 0; - } else { - return false; - } - } - - //-----------// - // getWeight // - //-----------// - public int getWeight () - { - return weight; - } - - //----------// - // hashCode // - //----------// - @Override - public int hashCode () - { - int hash = 7; - hash = (41 * hash) + this.weight; - - return hash; - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("{GSig"); - - sb.append(" weight=").append(weight); - - sb.append(" moments=").append(moments); - sb.append("}"); - - return sb.toString(); - } -} diff --git a/src/main/org/audiveris/omr/glyph/Glyphs.java b/src/main/org/audiveris/omr/glyph/Glyphs.java index c3301fc94..2c8d0135d 100644 --- a/src/main/org/audiveris/omr/glyph/Glyphs.java +++ b/src/main/org/audiveris/omr/glyph/Glyphs.java @@ -50,11 +50,11 @@ */ public abstract class Glyphs { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Glyphs.class); - /** To compare glyphs according to their id. + /** + * To compare glyphs according to their id. * This comparator, which requires that all handled glyphs have an ID, can be used for a set. */ public static final Comparator byId = new Comparator() @@ -82,7 +82,8 @@ public int compare (Glyph g1, } }; - /** To compare glyphs according to their left abscissa (then top ordinate, then id). + /** + * To compare glyphs according to their left abscissa (then top ordinate, then id). * This comparator, which requires that all handled glyphs have an ID, can be used for a set. */ public static final Comparator byFullAbscissa = new Comparator() @@ -189,13 +190,11 @@ public int compare (Glyph g1, } }; - //~ Constructors ------------------------------------------------------------------------------- // Class is not meant to be instantiated. private Glyphs () { } - //~ Methods ------------------------------------------------------------------------------------ //------------// // buildLinks // //------------// @@ -210,11 +209,11 @@ public static SimpleGraph buildLinks (Collection glyphs double maxGap) { final int gapInt = (int) Math.ceil(maxGap); - final List sortedGlyphs = new ArrayList(glyphs); + final List sortedGlyphs = new ArrayList<>(glyphs); Collections.sort(sortedGlyphs, byAbscissa); /** Graph of glyph instances, linked by their distance. */ - SimpleGraph graph = new SimpleGraph(GlyphLink.class); + SimpleGraph graph = new SimpleGraph<>(GlyphLink.class); // Populate graph with all glyphs as vertices for (Glyph glyph : sortedGlyphs) { @@ -271,7 +270,7 @@ public static SimpleGraph buildLinks (Collection glyphs public static Set containedActualGlyphs (Collection collection, Rectangle rect) { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (WeakGlyph weak : collection) { final Glyph glyph = weak.get(); @@ -298,7 +297,7 @@ public static Set containedActualGlyphs (Collection public static Set containedGlyphs (Collection collection, Rectangle rect) { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if (rect.contains(glyph.getBounds())) { @@ -323,7 +322,7 @@ public static Set containedGlyphs (Collection collection public static Set containedGlyphs (Collection collection, Polygon polygon) { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if (polygon.contains(glyph.getBounds())) { @@ -371,7 +370,7 @@ public static Glyph containingGlyph (Collection collection, public static Set containingGlyphs (Collection collection, Point point) { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if (glyph.contains(point)) { @@ -402,6 +401,13 @@ public static boolean contains (Collection glyphs, //------------// // containsId // //------------// + /** + * Tell whether the provided ID appears in the glyph collection. + * + * @param glyphs glyph collection + * @param id provided ID + * @return true if so + */ public static boolean containsId (Collection glyphs, int id) { @@ -588,7 +594,7 @@ public static boolean intersect (Glyph one, public static Set intersectedActualGlyphs (Collection collection, Rectangle rect) { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (WeakGlyph weak : collection) { final Glyph glyph = weak.get(); @@ -615,7 +621,7 @@ public static Set intersectedActualGlyphs (Collection intersectedGlyphs (Collection collection, Rectangle rect) { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if (rect.intersects(glyph.getBounds())) { @@ -640,7 +646,7 @@ public static Set intersectedGlyphs (Collection collecti public static Set intersectedGlyphs (Collection collection, Area area) { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if (area.intersects(glyph.getBounds())) { @@ -666,7 +672,7 @@ public static Set intersectedGlyphs (Collection collecti public static Set lookupGlyphs (Collection collection, Predicate predicate) { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if ((predicate == null) || predicate.check(glyph)) { diff --git a/src/main/org/audiveris/omr/glyph/GlyphsModel.java b/src/main/org/audiveris/omr/glyph/GlyphsModel.java index 91a131946..e3b3b49a0 100644 --- a/src/main/org/audiveris/omr/glyph/GlyphsModel.java +++ b/src/main/org/audiveris/omr/glyph/GlyphsModel.java @@ -40,12 +40,9 @@ */ public class GlyphsModel { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - GlyphsModel.class); + private static final Logger logger = LoggerFactory.getLogger(GlyphsModel.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying glyph service. */ protected final EntityService glyphService; @@ -55,7 +52,6 @@ public class GlyphsModel /** Latest shape assigned, if any. */ protected Shape latestShape; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an instance of GlyphsModel, with its underlying glyph glyphService. * @@ -71,7 +67,6 @@ public GlyphsModel (Sheet sheet, this.glyphService = glyphService; } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // assignGlyph // //-------------// @@ -93,71 +88,71 @@ public Glyph assignGlyph (Glyph glyph, double grade) { logger.error("HB. Not yet implemented"); -// -// final Inter ghost = SymbolFactory.createGhost(shape, grade); -// final SystemInfo system = staff.getSystem(); -// ghost.setStaff(staff); -// ghost.setGlyph(glyph); -// ghost.setBounds(glyph.getBounds()); -// system.getSig().addVertex(ghost); -// sheet.getStub().setModified(true); -// -// // Edges? this depends on ghost class... -// Collection links = ghost.searchLinks(system, true); -// -// if (links.isEmpty()) { -// logger.info("No partners for {}", ghost); -// } -// -// sheet.getGlyphIndex().publish(null); -// sheet.getInterIndex().publish(ghost); -// logger.info("Added {}", ghost); -// + + // + // final Inter ghost = SymbolFactory.createGhost(shape, grade); + // final SystemInfo system = staff.getSystem(); + // ghost.setStaff(staff); + // ghost.setGlyph(glyph); + // ghost.setBounds(glyph.getBounds()); + // system.getSig().addVertex(ghost); + // sheet.getStub().setModified(true); + // + // // Edges? this depends on ghost class... + // Collection links = ghost.searchLinks(system, true); + // + // if (links.isEmpty()) { + // logger.info("No partners for {}", ghost); + // } + // + // sheet.getGlyphIndex().publish(null); + // sheet.getInterIndex().publish(ghost); + // logger.info("Added {}", ghost); + // // final Book book = sheet.getStub().getBook(); // final SampleRepository repository = book.getSampleRepository(); // final SampleSheet sampleSheet = repository.findSampleSheet(sheet); // // // TODO: we need staff information (-> interline and pitch) // repository.addSample(shape, glyph, interline, sampleSheet, null); - -// -// if (glyph == null) { -// return null; -// } -// -// if (shape != null) { -// List systems = sheet.getSystemManager().getSystemsOf(glyph); -// -// // if (system != null) { -// // glyph = system.register(glyph); // System then nest -// // } else { -// // // Insert in nest directly, which assigns an id to the glyph -// glyph = nest.register(glyph); -// -// // } -// boolean isTransient = glyph.isTransient(); -// logger.debug( -// "Assign {}{} to {}", -// isTransient ? "compound " : "", -// glyph.idString(), -// shape); -// -// // Remember the latest shape assigned -// setLatestShape(shape); -// } -// -// // Do the assignment of the shape to the glyph -// glyph.setShape(shape); -// -// // Should we persist the assigned glyph? -// if ((shape != null) -// && (grade == Evaluation.MANUAL) -// && (OMR.gui != null) -// && ScoreActions.getInstance().isManualPersisted()) { -// // Record the glyph description to disk -// SampleRepository.getInstance().recordOneGlyph(glyph, sheet); -// } -// + // + // if (glyph == null) { + // return null; + // } + // + // if (shape != null) { + // List systems = sheet.getSystemManager().getSystemsOf(glyph); + // + // // if (system != null) { + // // glyph = system.register(glyph); // System then nest + // // } else { + // // // Insert in nest directly, which assigns an id to the glyph + // glyph = nest.register(glyph); + // + // // } + // boolean isTransient = glyph.isTransient(); + // logger.debug( + // "Assign {}{} to {}", + // isTransient ? "compound " : "", + // glyph.idString(), + // shape); + // + // // Remember the latest shape assigned + // setLatestShape(shape); + // } + // + // // Do the assignment of the shape to the glyph + // glyph.setShape(shape); + // + // // Should we persist the assigned glyph? + // if ((shape != null) + // && (grade == Evaluation.MANUAL) + // && (OMR.gui != null) + // && ScoreActions.getInstance().isManualPersisted()) { + // // Record the glyph description to disk + // SampleRepository.getInstance().recordOneGlyph(glyph, sheet); + // } + // return glyph; } @@ -208,19 +203,6 @@ public Shape getLatestShape () return latestShape; } - //----------// - // getSheet // - //----------// - /** - * Report the model underlying sheet. - * - * @return the underlying sheet instance - */ - public Sheet getSheet () - { - return sheet; - } - //----------------// // setLatestShape // //----------------// @@ -236,6 +218,19 @@ public void setLatestShape (Shape shape) } } + //----------// + // getSheet // + //----------// + /** + * Report the model underlying sheet. + * + * @return the underlying sheet instance + */ + public Sheet getSheet () + { + return sheet; + } + //---------------// // deassignGlyph // //---------------// @@ -253,6 +248,11 @@ protected void deassignGlyph (Glyph glyph) //-------------// // deleteGlyph // //-------------// + /** + * Delete a glyph. + * + * @param glyph the glyph to delete + */ protected void deleteGlyph (Glyph glyph) { logger.error("HB. Not yet implemented"); diff --git a/src/main/org/audiveris/omr/glyph/Grades.java b/src/main/org/audiveris/omr/glyph/Grades.java index 54fb3e579..1b435ca54 100644 --- a/src/main/org/audiveris/omr/glyph/Grades.java +++ b/src/main/org/audiveris/omr/glyph/Grades.java @@ -33,66 +33,63 @@ */ public abstract class Grades { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - /** Ratio applied on intrinsic value, to leave room for contextual. */ - public static final double intrinsicRatio = constants.intrinsicRatio.getValue(); + /** Minimum grade for a validation (during training phase). */ + public static final double validationMinGrade = constants.validationMinGrade.getValue(); - // Minimum global values - //---------------------- - /** The minimum grade to consider an interpretation as acceptable. */ - public static final double minInterGrade = intrinsicRatio * constants.minInterGrade.getValue(); + /** Minimum grade for a time glyph. */ + public static final double timeMinGrade = constants.timeMinGrade.getValue(); - /** The minimum contextual grade for an interpretation. */ - public static final double minContextualGrade = constants.minContextualGrade.getValue(); + /** Minimum grade for a symbol glyph. */ + public static final double symbolMinGrade = constants.symbolMinGrade.getValue(); - /** The minimum grade to consider an interpretation as good. */ - public static final double goodInterGrade = intrinsicRatio * constants.goodInterGrade.getValue(); + /** Minimum grade for a key signature item, phase #2 (staff slice-based). */ + public static final double keyAlterMinGrade2 = constants.keyAlterMinGrade2.getValue(); - /** The minimum grade to consider a relation as good. */ - public static final double goodRelationGrade = constants.goodRelationGrade.getValue(); + /** Minimum grade for a key signature item, phase #1 (component-based). */ + public static final double keyAlterMinGrade1 = constants.keyAlterMinGrade1.getValue(); - /** The minimum grade to consider a BarConnector as good. */ - public static final double goodBarConnectorGrade = constants.goodBarConnectorGrade.getValue(); + /** Minimum grade for a key signature. */ + public static final double keySigMinGrade = constants.keySigMinGrade.getValue(); // Minimum specific values //------------------------ /** Minimum grade for a clef glyph. */ public static final double clefMinGrade = constants.clefMinGrade.getValue(); - /** Minimum grade for a key signature. */ - public static final double keySigMinGrade = constants.keySigMinGrade.getValue(); + /** The minimum grade to consider a BarConnector as good. */ + public static final double goodBarConnectorGrade = constants.goodBarConnectorGrade.getValue(); - /** Minimum grade for a key signature item, phase #1 (component-based). */ - public static final double keyAlterMinGrade1 = constants.keyAlterMinGrade1.getValue(); + /** The minimum grade to consider a relation as good. */ + public static final double goodRelationGrade = constants.goodRelationGrade.getValue(); - /** Minimum grade for a key signature item, phase #2 (staff slice-based). */ - public static final double keyAlterMinGrade2 = constants.keyAlterMinGrade2.getValue(); + /** The minimum contextual grade for an interpretation. */ + public static final double minContextualGrade = constants.minContextualGrade.getValue(); - /** Minimum grade for a symbol glyph. */ - public static final double symbolMinGrade = constants.symbolMinGrade.getValue(); + /** Ratio applied on intrinsic value, to leave room for contextual. */ + public static final double intrinsicRatio = constants.intrinsicRatio.getValue(); - /** Minimum grade for a time glyph. */ - public static final double timeMinGrade = constants.timeMinGrade.getValue(); + /** The minimum grade to consider an interpretation as good. */ + public static final double goodInterGrade = intrinsicRatio * constants.goodInterGrade + .getValue(); - /** Minimum grade for a validation (during training phase). */ - public static final double validationMinGrade = constants.validationMinGrade.getValue(); + // Minimum global values + //---------------------- + /** The minimum grade to consider an interpretation as acceptable. */ + public static final double minInterGrade = intrinsicRatio * constants.minInterGrade.getValue(); - //~ Constructors ------------------------------------------------------------------------------- private Grades () { } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio intrinsicRatio = new Constant.Ratio( 0.8, diff --git a/src/main/org/audiveris/omr/glyph/NearLine.java b/src/main/org/audiveris/omr/glyph/NearLine.java index 0eebfb687..8d78c5dc2 100644 --- a/src/main/org/audiveris/omr/glyph/NearLine.java +++ b/src/main/org/audiveris/omr/glyph/NearLine.java @@ -36,7 +36,6 @@ public interface NearLine extends Entity { - //~ Methods ------------------------------------------------------------------------------------ /** * Report the co-tangent of glyph line angle with abscissa axis. diff --git a/src/main/org/audiveris/omr/glyph/SectionSets.java b/src/main/org/audiveris/omr/glyph/SectionSets.java index 874663901..9efe1712b 100644 --- a/src/main/org/audiveris/omr/glyph/SectionSets.java +++ b/src/main/org/audiveris/omr/glyph/SectionSets.java @@ -50,12 +50,9 @@ @XmlAccessorType(XmlAccessType.NONE) public class SectionSets { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - SectionSets.class); + private static final Logger logger = LoggerFactory.getLogger(SectionSets.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The collection of sections sets. */ protected Collection> sets; @@ -63,7 +60,6 @@ public class SectionSets @XmlElement(name = "sections") private Collection descSets; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SectionSets object. * @@ -81,50 +77,6 @@ private SectionSets () { } - //~ Methods ------------------------------------------------------------------------------------ - //------------------// - // createFromGlyphs // - //------------------// - /** - * Convenient method to create the proper SectionSets out of a provided - * collection of glyphs - * - * @param glyphs the provided glyphs - * @return a newly built SectionSets instance - */ - public static SectionSets createFromGlyphs (Collection glyphs) - { - throw new RuntimeException("HB. Not implemented yet"); - - // SectionSets sectionSets = new SectionSets(); - // sectionSets.sets = new ArrayList>(); - // - // for (Glyph glyph : glyphs) { - // sectionSets.sets.add(new ArrayList
                (glyph.getMembers())); - // } - // - // return sectionSets; - } - - //--------------------// - // createFromSections // - //--------------------// - /** - * Convenient method to create the proper SectionSets out of a provided - * collection of sections - * - * @param sections the provided sections - * @return a newly built SectionSets instance (a singleton actually) - */ - public static SectionSets createFromSections (Collection
                sections) - { - SectionSets sectionSets = new SectionSets(); - sectionSets.sets = new ArrayList>(); - sectionSets.sets.add(sections); - - return sectionSets; - } - //---------// // getSets // //---------// @@ -137,14 +89,15 @@ public static SectionSets createFromSections (Collection
                sections) public Collection> getSets (Sheet sheet) { if (sets == null) { - sets = new ArrayList>(); + sets = new ArrayList<>(); for (SectionDescSet idSet : descSets) { - List
                sectionSet = new ArrayList
                (); + List
                sectionSet = new ArrayList<>(); for (SectionDesc sectionId : idSet.sections) { Lag lag = sheet.getLagManager().getLag( - (sectionId.orientation == Orientation.VERTICAL) ? Lags.VLAG : Lags.HLAG); + (sectionId.orientation == Orientation.VERTICAL) ? Lags.VLAG + : Lags.HLAG); Section section = lag.getEntity(sectionId.id); if (section == null) { @@ -196,7 +149,7 @@ private void beforeMarshal (Marshaller m) { // Convert sections -> ids if (sets != null) { - descSets = new ArrayList(); + descSets = new ArrayList<>(); for (Collection
                set : sets) { SectionDescSet descSet = new SectionDescSet(); @@ -211,7 +164,48 @@ private void beforeMarshal (Marshaller m) } } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------------// + // createFromGlyphs // + //------------------// + /** + * Convenient method to create the proper SectionSets out of a provided + * collection of glyphs + * + * @param glyphs the provided glyphs + * @return a newly built SectionSets instance + */ + public static SectionSets createFromGlyphs (Collection glyphs) + { + throw new RuntimeException("HB. Not implemented yet"); + + // SectionSets sectionSets = new SectionSets(); + // sectionSets.sets = new ArrayList<>(); + // + // for (Glyph glyph : glyphs) { + // sectionSets.sets.add(new ArrayList
                (glyph.getMembers())); + // } + // + // return sectionSets; + } + + //--------------------// + // createFromSections // + //--------------------// + /** + * Convenient method to create the proper SectionSets out of a provided + * collection of sections + * + * @param sections the provided sections + * @return a newly built SectionSets instance (a singleton actually) + */ + public static SectionSets createFromSections (Collection
                sections) + { + SectionSets sectionSets = new SectionSets(); + sectionSets.sets = new ArrayList<>(); + sectionSets.sets.add(sections); + return sectionSets; + } + //-------------// // SectionDesc // //-------------// @@ -220,13 +214,12 @@ private void beforeMarshal (Marshaller m) */ private static class SectionDesc { - //~ Instance fields ------------------------------------------------------------------------ // Annotation to get all ids, space-separated, in one single element: //@XmlList // Annotation to avoid any wrapper: //@XmlValue - //private Collection ids = new ArrayList(); + //private Collection ids = new ArrayList<>(); /** Section id */ @XmlAttribute(name = "id") int id; @@ -235,20 +228,18 @@ private static class SectionDesc @XmlAttribute(name = "orientation") Orientation orientation; - //~ Constructors --------------------------------------------------------------------------- // For JAXB - public SectionDesc () + SectionDesc () { } - public SectionDesc (int id, - Orientation orientation) + SectionDesc (int id, + Orientation orientation) { this.id = id; this.orientation = orientation; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { @@ -271,13 +262,12 @@ public String toString () */ private static class SectionDescSet { - //~ Instance fields ------------------------------------------------------------------------ // // Annotation to get all ids, space-separated, in one single element: // @XmlList // // Annotation to avoid any wrapper: // @XmlValue @XmlElement(name = "section") - private Collection sections = new ArrayList(); + private Collection sections = new ArrayList<>(); } } diff --git a/src/main/org/audiveris/omr/glyph/Shape.java b/src/main/org/audiveris/omr/glyph/Shape.java index d18031ffd..af3de6180 100644 --- a/src/main/org/audiveris/omr/glyph/Shape.java +++ b/src/main/org/audiveris/omr/glyph/Shape.java @@ -82,15 +82,14 @@ public enum Shape * won't detect this and you'll have to retrain them on your own. * ============================================================================================= */ - // - // Sets -------------------------------------------------------------------- + // Sets --- // DOT_set("Dot set"), HW_REST_set("Half & Whole Rest set"), // - // Bars -------------------------------------------------------------------- + // Bars --- // DAL_SEGNO("D.S.: Repeat from the sign"), DA_CAPO("D.C.: Repeat from the beginning"), @@ -102,7 +101,7 @@ public enum Shape FERMATA_ARC_BELOW("Fermata arc below, without dot"), // - // Clefs ------------------------------------------------------------------- + // Clefs --- // G_CLEF("Treble Clef"), G_CLEF_SMALL("Small Treble Clef"), @@ -116,7 +115,7 @@ public enum Shape PERCUSSION_CLEF("Percussion Clef"), // - // Accidentals ------------------------------------------------------------- + // Accidentals --- // FLAT("Minus one half step"), NATURAL("Natural value"), @@ -125,7 +124,7 @@ public enum Shape DOUBLE_FLAT("Double Flat"), // - // Time -------------------------------------------------------------------- + // Time --- // TIME_ZERO("Time digit 0"), TIME_ONE("Time digit 1"), @@ -143,6 +142,7 @@ public enum Shape // Whole time sigs COMMON_TIME("Alpha = 4/4"), CUT_TIME("Semi-Alpha = 2/2"), + // Predefined time combos TIME_FOUR_FOUR("Rational 4/4"), TIME_TWO_TWO("Rational 2/2"), @@ -152,11 +152,14 @@ public enum Shape TIME_THREE_EIGHT("Rational 3/8"), TIME_SIX_EIGHT("Rational 6/8"), - // Octave shifts + // + // Octave shifts --- + // OTTAVA_ALTA("8 va"), OTTAVA_BASSA("8 vb"), + // - // Rests ------------------------------------------------------------------- + // Rests --- // LONG_REST("Rest for 4 measures"), BREVE_REST("Rest for 2 measures"), @@ -168,7 +171,7 @@ public enum Shape ONE_128TH_REST("Rest for a 1/128"), // - // Flags ------------------------------------------------------------------- + // Flags --- // FLAG_1("Single flag down"), FLAG_1_UP("Single flag up"), @@ -188,11 +191,12 @@ public enum Shape SMALL_FLAG_SLASH("Flag for slashed grace note"), // - // StemLessHeads ----------------------------------------------------------- + // StemLessHeads --- // BREVE("Double Whole"), + // - // Articulation ------------------------------------------------------------ + // Articulations --- // ACCENT, TENUTO, @@ -201,7 +205,7 @@ public enum Shape ARPEGGIATO, // - // Dynamics ---------------------------------------------------------------- + // Dynamics --- // // DYNAMICS_CHAR_M("m character"), // DYNAMICS_CHAR_R("r character"), @@ -226,7 +230,7 @@ public enum Shape DYNAMICS_SFZ("Sforzando"), // - // Ornaments --------------------------------------------------------------- + // Ornaments --- // TR("Trill"), TURN("Turn"), @@ -237,7 +241,7 @@ public enum Shape MORDENT_INVERTED("Mordent with a Slash"), // - // Tuplets ----------------------------------------------------------------- + // Tuplets --- // TUPLET_THREE("3"), TUPLET_SIX("6"), @@ -245,7 +249,7 @@ public enum Shape PEDAL_UP_MARK("Pedal downup"), // - // Small digits ------------------------------------------------------------ + // Small digits --- // DIGIT_0("Digit 0"), DIGIT_1("Digit 1"), @@ -253,13 +257,13 @@ public enum Shape DIGIT_3("Digit 3"), DIGIT_4("Digit 4"), DIGIT_5("Digit 5"), - // DIGIT_6("Digit 6"), // DIGIT_7("Digit 7"), // DIGIT_8("Digit 8"), // DIGIT_9("Digit 9"), + // - // Roman numerals ---------------------------------------------------------- + // Roman numerals --- // ROMAN_I("Roman number 1"), ROMAN_II("Roman number 2"), @@ -275,7 +279,7 @@ public enum Shape ROMAN_XII("Roman number 12"), // - // Plucking ---------------------------------------------------------------- + // Plucking --- // PLUCK_P("Plucking pouce/pulgar/thumb"), PLUCK_I("Plucking index/indicio/index"), @@ -283,7 +287,7 @@ public enum Shape PLUCK_A("Plucking annulaire/anular/ring"), // - // Miscellaneous ----------------------------------------------------------- + // Miscellaneous --- // CLUTTER("Pure clutter", Colors.SHAPE_UNKNOWN), /** @@ -295,19 +299,21 @@ public enum Shape CHARACTER("Any letter"), // - // Shapes from shape sets -------------------------------------------------- + // Shapes from DOT_set --- // REPEAT_DOT("Repeat dot", DOT_set), AUGMENTATION_DOT("Augmentation Dot", DOT_set), FERMATA_DOT("Fermata Dot", DOT_set), STACCATO("Staccato dot", DOT_set), + // + // Shapes from HW_REST_set --- // WHOLE_REST("Rest for whole measure", HW_REST_set), HALF_REST("Rest for a 1/2", HW_REST_set), // - // Noteheads --------------------------------------------------------------- + // Noteheads --- // NOTEHEAD_BLACK("Filled node head for quarters and less"), NOTEHEAD_BLACK_SMALL("Small filled note head for grace or cue"), @@ -315,13 +321,13 @@ public enum Shape NOTEHEAD_VOID_SMALL("Small hollow note head for grace or cue"), // - // StemLessHeads ----------------------------------------------------------- + // StemLessHeads --- // WHOLE_NOTE("Hollow node head for wholes"), WHOLE_NOTE_SMALL("Small hollow node head for grace or cue wholes"), // - // Beams and slurs --------------------------------------------------------- + // Beams and slurs --- // BEAM("Beam between two stems"), BEAM_SMALL("Small beam for cue notes"), @@ -330,7 +336,7 @@ public enum Shape SLUR("Slur tying notes"), // - // Key signatures ---------------------------------------------------------- + // Key signatures --- // KEY_FLAT_7("Seven Flats"), KEY_FLAT_6("Six Flats"), @@ -348,7 +354,7 @@ public enum Shape KEY_SHARP_7("Seven Sharps"), // - // Bars -------------------------------------------------------------------- + // Bars --- // THIN_BARLINE("Thin bar line"), THIN_CONNECTOR("Connector between thin barlines", Colors.SCORE_FRAME), @@ -362,13 +368,15 @@ public enum Shape RIGHT_REPEAT_SIGN("Repeat dots + Thin / Thick bar line"), BACK_TO_BACK_REPEAT_SIGN("Repeat dots + Thin / Thick / Thin + REPEAT_DOTS"), ENDING("Alternate ending"), + // - // Wedges ------------------------------------------------------------------ + // Wedges --- // CRESCENDO("Crescendo"), DIMINUENDO("Diminuendo"), + // - // Miscellaneous + // Miscellaneous --- // BRACE("Brace"), BRACKET("Bracket"), @@ -381,23 +389,24 @@ public enum Shape LYRICS("Lyrics", Colors.SCORE_LYRICS), // - // Stems + // Stems --- // STEM("Stem"), + // - // Ornaments --------------------------------------------------------------- + // Ornaments --- // GRACE_NOTE_SLASH("Grace Note with a Slash"), GRACE_NOTE("Grace Note with no slash"), // - // Full fermatas ----------------------------------------------------------- + // Full fermatas --- // FERMATA("Fermata with dot"), FERMATA_BELOW("Fermata below with dot"), // - // Other stuff ------------------------------------------------------------- + // Other stuff --- // FORWARD("To indicate a forward"), NON_DRAGGABLE("Non draggable shape"), @@ -405,18 +414,17 @@ public enum Shape CUSTOM_TIME("Time signature defined by user"), NO_LEGAL_TIME("No Legal Time Shape"); - // // ============================================================================================= // This is the end of shape enumeration // ============================================================================================= // private static final Logger logger = LoggerFactory.getLogger(Shape.class); - /** Last physical shape */ + /** Last physical shape. */ public static final Shape LAST_PHYSICAL_SHAPE = CLUTTER; - /** A comparator based on shape name */ - public static Comparator alphaComparator = new Comparator() + /** A comparator based on shape name. */ + public static final Comparator alphaComparator = new Comparator() { @Override public int compare (Shape o1, @@ -426,33 +434,30 @@ public int compare (Shape o1, } }; - // - /** Explanation of the glyph shape */ + /** Explanation of the glyph shape. */ private final String description; - /** Potential related symbol */ + /** Potential related symbol. */ private ShapeSymbol symbol; - /** Potential related decorated symbol for menus */ + /** Potential related decorated symbol for menus. */ private ShapeSymbol decoratedSymbol; - /** Remember the fact that this shape has no related symbol */ + /** Remember the fact that this shape has no related symbol. */ private boolean hasNoSymbol; - /** Remember the fact that this shape has no related decorated symbol */ + /** Remember the fact that this shape has no related decorated symbol. */ private boolean hasNoDecoratedSymbol; - /** Potential related physical shape */ + /** Potential related physical shape. */ private Shape physicalShape; - /** Related color */ + /** Related color. */ private Color color; - /** Related color constant */ + /** Related color constant. */ private Constant.Color constantColor; - //-------------------------------------------------------------------------- - // //-------// // Shape // //-------// @@ -560,7 +565,7 @@ public boolean isRest () public boolean isPersistent () { return ShapeSet.Clefs.contains(this) || ShapeSet.Times.contains(this) - || ShapeSet.Accidentals.contains(this); + || ShapeSet.Accidentals.contains(this); } //--------// @@ -817,7 +822,7 @@ public boolean isDraggable () */ public static void dumpShapeColors () { - List names = new ArrayList(); + List names = new ArrayList<>(); for (Shape shape : Shape.values()) { names.add(shape + " " + Constant.Color.encodeColor(shape.getColor())); diff --git a/src/main/org/audiveris/omr/glyph/ShapeChecker.java b/src/main/org/audiveris/omr/glyph/ShapeChecker.java index f030feaf4..4306c298f 100644 --- a/src/main/org/audiveris/omr/glyph/ShapeChecker.java +++ b/src/main/org/audiveris/omr/glyph/ShapeChecker.java @@ -36,6 +36,7 @@ import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.EnumMap; import java.util.List; @@ -53,18 +54,11 @@ */ public class ShapeChecker { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - ShapeChecker.class); + private static final Logger logger = LoggerFactory.getLogger(ShapeChecker.class); - /** Singleton */ - private static ShapeChecker INSTANCE; - - //~ Instance fields ---------------------------------------------------------------------------- - // // /** Small dynamics with no 'P' or 'F' */ // private static final EnumSet SmallDynamics = EnumSet.copyOf( // shapesOf(DYNAMICS_CHAR_M, DYNAMICS_CHAR_R, DYNAMICS_CHAR_S, DYNAMICS_CHAR_Z)); @@ -93,14 +87,12 @@ public class ShapeChecker /** Map of Shape => Sequence of checkers */ private final EnumMap> checkerMap; - //~ Constructors ------------------------------------------------------------------------------- private ShapeChecker () { - checkerMap = new EnumMap>(Shape.class); + checkerMap = new EnumMap<>(Shape.class); registerChecks(); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // annotate // //----------// @@ -146,18 +138,6 @@ public void annotate (SystemInfo system, } } - //-------------// - // getInstance // - //-------------// - public static ShapeChecker getInstance () - { - if (INSTANCE == null) { - INSTANCE = new ShapeChecker(); - } - - return INSTANCE; - } - //------------// // addChecker // //------------// @@ -168,13 +148,13 @@ public static ShapeChecker getInstance () * @param shapes the shape(s) for which the check applies */ private void addChecker (Checker checker, - Shape... shapes) + Collection shapes) { for (Shape shape : shapes) { Collection checks = checkerMap.get(shape); if (checks == null) { - checks = new ArrayList(); + checks = new ArrayList<>(); checkerMap.put(shape, checks); } @@ -195,7 +175,7 @@ private void addChecker (Checker checker, ShapeSet... shapeRanges) { for (ShapeSet range : shapeRanges) { - addChecker(checker, range.getShapes().toArray(new Shape[0])); + addChecker(checker, range.getShapes()); } } @@ -265,21 +245,15 @@ public boolean check (SystemInfo system, // Except a few shapes Shape shape = eval.shape; - if ((shape == BRACKET) - || (shape == BRACE) - || (shape == TEXT) - || (shape == CHARACTER)) { + if ((shape == BRACKET) || (shape == BRACE) || (shape == TEXT) + || (shape == CHARACTER)) { return true; } Rectangle glyphBox = glyph.getBounds(); - if (((glyphBox.x + glyphBox.width) < system.getLeft()) - || (glyphBox.x > system.getRight())) { - return false; - } - - return true; + return !(((glyphBox.x + glyphBox.width) < system.getLeft()) || (glyphBox.x > system + .getRight())); } }; @@ -295,7 +269,7 @@ public boolean check (SystemInfo system, * These half / whole rest signs are generally on standard pitch values: * Standard pitch for whole: -1.5 * Standard pitch for half: -0.5 - * + *

                * But because of other notes in the same staff-measure, they may appear * on different pitch values, as they see fit. See Telemann example. * Whole is always stuck to an upper line, half is always stuck to a lower line. @@ -493,11 +467,7 @@ public boolean check (SystemInfo system, } }; - new Checker( - "StaffGap", - Rests.getShapes(), - Dynamics.getShapes(), - Articulations.getShapes()) + new Checker("StaffGap", Rests.getShapes(), Dynamics.getShapes(), Articulations.getShapes()) { @Override public boolean check (SystemInfo system, @@ -700,7 +670,7 @@ public boolean check (SystemInfo system, } }; - new Checker("SystemTop", DAL_SEGNO, DA_CAPO, SEGNO, CODA, BREATH_MARK) + new Checker("SystemTop", Arrays.asList(DAL_SEGNO, DA_CAPO, SEGNO, CODA, BREATH_MARK)) { @Override public boolean check (SystemInfo system, @@ -709,9 +679,7 @@ public boolean check (SystemInfo system, { // Check that these markers are just above first system staff Rectangle bounds = glyph.getBounds(); - Point bottom = new Point( - bounds.x + (bounds.width / 2), - bounds.y + bounds.height); + Point bottom = new Point(bounds.x + (bounds.width / 2), bounds.y + bounds.height); Staff staff = system.getClosestStaff(bottom); if (staff != system.getFirstStaff()) { @@ -720,13 +688,33 @@ public boolean check (SystemInfo system, double pitch = staff.pitchPositionOf(bottom); - return (constants.minDirectionPitchPosition.getValue() <= pitch) - && (pitch <= -5); + return (constants.minDirectionPitchPosition.getValue() <= pitch) && (pitch <= -5); } }; } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of ShapeChecker in the application. + * + * @return the instance + */ + public static ShapeChecker getInstance () + { + return LazySingleton.INSTANCE; + } + + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { + + static final ShapeChecker INSTANCE = new ShapeChecker(); + } + //---------// // Checker // //---------// @@ -736,74 +724,64 @@ public boolean check (SystemInfo system, */ private abstract class Checker { - //~ Instance fields ------------------------------------------------------------------------ /** Unique name for this check */ public final String name; - //~ Constructors --------------------------------------------------------------------------- - public Checker (String name, - Shape... shapes) + Checker (String name, + Collection shapes) { this.name = name; addChecker(this, shapes); } - public Checker (String name, - Collection shapes) - { - this.name = name; - addChecker(this, shapes.toArray(new Shape[0])); - } - @SuppressWarnings({"unchecked", "varargs"}) - public Checker (String name, - Collection... shapes) + Checker (String name, + Collection... shapes) { this.name = name; - Collection allShapes = new ArrayList(); + Collection allShapes = new ArrayList<>(); for (Collection col : shapes) { allShapes.addAll(col); } - addChecker(this, allShapes.toArray(new Shape[allShapes.size()])); + addChecker(this, allShapes); } - public Checker (String name, - ShapeSet... shapeSets) + Checker (String name, + ShapeSet... shapeSets) { this.name = name; addChecker(this, shapeSets); } - public Checker (String name, - Shape shape, - Collection collection) + Checker (String name, + Shape shape, + Collection collection) { this.name = name; - List all = new ArrayList(); + List all = new ArrayList<>(); all.add(shape); all.addAll(collection); - addChecker(this, all.toArray(new Shape[0])); + addChecker(this, all); } - public Checker (String name, - Shape shape) + Checker (String name, + Shape shape) { this.name = name; - List all = new ArrayList(); + List all = new ArrayList<>(); all.add(shape); - addChecker(this, all.toArray(new Shape[0])); + addChecker(this, all); } - //~ Methods -------------------------------------------------------------------------------- /** * Run the specific test. * @@ -827,10 +805,9 @@ public String toString () //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean applySpecificCheck = new Constant.Boolean( true, diff --git a/src/main/org/audiveris/omr/glyph/ShapeSet.java b/src/main/org/audiveris/omr/glyph/ShapeSet.java index 116ccd471..4a9e01009 100644 --- a/src/main/org/audiveris/omr/glyph/ShapeSet.java +++ b/src/main/org/audiveris/omr/glyph/ShapeSet.java @@ -53,14 +53,16 @@ * It handles additional properties over a simple EnumSet, especially assigned colors and its * automatic insertion in shape menus and palette hierarchy. * So don't remove any of the ShapeSet's, unless you know what you are doing. + * + * @author Hervé Bitteur */ public class ShapeSet { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ShapeSet.class); - /** Half time numbers. These shapes are used for upper or lower part of a time signature. + /** + * Half time numbers. These shapes are used for upper or lower part of a time signature. */ public static final List PartialTimes = Arrays.asList( TIME_TWO, @@ -74,7 +76,8 @@ public class ShapeSet TIME_TWELVE, TIME_SIXTEEN); - /** Measure counts. + /** + * Measure counts. * These time-looking shapes may appear right above a staff containing just a long measure rest, * to indicate the number of measures the rest represents. */ @@ -92,10 +95,10 @@ public class ShapeSet TIME_TWELVE, TIME_SIXTEEN); - /** Single-symbols for whole time signature */ + /** Single-symbols for whole time signature. */ public static final EnumSet SingleWholeTimes = EnumSet.of(COMMON_TIME, CUT_TIME); - /** Single-symbols and predefined combos for whole time signature */ + /** Single-symbols and predefined combos for whole time signature. */ public static final EnumSet WholeTimes = EnumSet.of( COMMON_TIME, CUT_TIME, @@ -107,21 +110,21 @@ public class ShapeSet TIME_THREE_EIGHT, TIME_SIX_EIGHT); - /** All sorts of F clefs */ + /** All sorts of F clefs. */ public static final EnumSet BassClefs = EnumSet.of( F_CLEF, F_CLEF_SMALL, F_CLEF_8VA, F_CLEF_8VB); - /** All sorts of G clefs */ + /** All sorts of G clefs. */ public static final EnumSet TrebleClefs = EnumSet.of( G_CLEF, G_CLEF_SMALL, G_CLEF_8VA, G_CLEF_8VB); - /** All flags down */ + /** All flags down. */ public static final EnumSet FlagsDown = EnumSet.of( FLAG_1, FLAG_2, @@ -129,10 +132,10 @@ public class ShapeSet FLAG_4, FLAG_5); - /** Small flags */ + /** Small flags. */ public static final EnumSet SmallFlags = EnumSet.of(SMALL_FLAG, SMALL_FLAG_SLASH); - /** All flags up */ + /** All flags up. */ public static final EnumSet FlagsUp = EnumSet.of( FLAG_1_UP, FLAG_2_UP, @@ -140,7 +143,7 @@ public class ShapeSet FLAG_4_UP, FLAG_5_UP); - /** All SHARP-based keys */ + /** All SHARP-based keys. */ public static final EnumSet SharpKeys = EnumSet.of( KEY_SHARP_1, KEY_SHARP_2, @@ -150,7 +153,7 @@ public class ShapeSet KEY_SHARP_6, KEY_SHARP_7); - /** All FLAT-based keys */ + /** All FLAT-based keys. */ public static final EnumSet FlatKeys = EnumSet.of( KEY_FLAT_1, KEY_FLAT_2, @@ -160,12 +163,12 @@ public class ShapeSet KEY_FLAT_6, KEY_FLAT_7); - /** All black note heads */ + /** All black note heads. */ public static final EnumSet BlackNoteHeads = EnumSet.of( NOTEHEAD_BLACK, NOTEHEAD_BLACK_SMALL); - /** All void note heads */ + /** All void note heads. */ public static final EnumSet VoidNoteHeads = EnumSet.of( NOTEHEAD_VOID, NOTEHEAD_VOID_SMALL); @@ -279,7 +282,7 @@ public class ShapeSet public static final ShapeSet Flags = new ShapeSet( FLAG_1, Colors.SCORE_NOTES, - shapesOf(new ArrayList(FlagsDown), SmallFlags, FlagsUp)); + shapesOf(new ArrayList<>(FlagsDown), SmallFlags, FlagsUp)); public static final ShapeSet Holds = new ShapeSet( FERMATA, @@ -289,7 +292,7 @@ public class ShapeSet public static final ShapeSet Keys = new ShapeSet( KEY_SHARP_3, Colors.SCORE_MODIFIERS, - shapesOf(new ArrayList(FlatKeys), SharpKeys)); + shapesOf(new ArrayList<>(FlatKeys), SharpKeys)); public static final ShapeSet HeadsAndDot = new ShapeSet( NOTEHEAD_BLACK, @@ -338,13 +341,7 @@ public class ShapeSet public static final ShapeSet Digits = new ShapeSet( DIGIT_1, Colors.SCORE_MODIFIERS, - shapesOf( - DIGIT_0, - DIGIT_1, - DIGIT_2, - DIGIT_3, - DIGIT_4, - DIGIT_5 // , + shapesOf(DIGIT_0, DIGIT_1, DIGIT_2, DIGIT_3, DIGIT_4, DIGIT_5 // , // DIGIT_6, // DIGIT_7, // DIGIT_8, @@ -382,6 +379,7 @@ public class ShapeSet // Below are EnumSet instances, used programmatically. // They do not lead to shape submenus as the ShapeSet instances do. // ========================================================================= + // /** All physical shapes. Here the use of EnumSet.range is OK */ public static final EnumSet allPhysicalShapes = EnumSet.range( Shape.values()[0], @@ -422,8 +420,6 @@ public class ShapeSet ///dumpShapeColors(); } - //~ Instance fields ---------------------------------------------------------------------------- - // /** Name of the set. */ private String name; @@ -442,7 +438,6 @@ public class ShapeSet /** Related color constant. */ private Constant.Color constantColor; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ShapeSet object from a collection of shapes. * @@ -466,13 +461,142 @@ public ShapeSet (Shape rep, // Keep a specific order? if (shapes instanceof List) { - this.sortedShapes = new ArrayList(shapes); + this.sortedShapes = new ArrayList<>(shapes); } else { this.sortedShapes = null; } } - //~ Methods ------------------------------------------------------------------------------------ + //--------// + // getRep // + //--------// + /** + * Report the representative shape of the set, if any. + * + * @return the rep shape, or null + */ + public Shape getRep () + { + return rep; + } + + //-----------// + // getShapes // + //-----------// + /** + * Exports the set of shapes. + * + * @return the proper enum set + */ + public EnumSet getShapes () + { + return shapes; + } + + //------------------// + // setConstantColor // + //------------------// + /** + * Define a specific color for the set. + * + * @param color the specified color + */ + public void setConstantColor (Color color) + { + constantColor.setValue(color); + setColor(color); + } + + //----------// + // contains // + //----------// + /** + * Convenient method to check if encapsulated shapes set does contain the provided + * object. + * + * @param shape the Shape object to check for inclusion + * @return true if contained, false otherwise + */ + public boolean contains (Shape shape) + { + return shapes.contains(shape); + } + + //----------// + // getColor // + //----------// + /** + * Report the color currently assigned to the range, if any. + * + * @return the related color, or null + */ + public Color getColor () + { + return color; + } + + //----------// + // setColor // + //----------// + /** + * Assign a display color to the shape set. + * + * @param color the display color + */ + private void setColor (Color color) + { + this.color = color; + } + + //---------// + // getName // + //---------// + /** + * Report the name of the set. + * + * @return the set name + */ + public String getName () + { + return name; + } + + //---------// + // setName // + //---------// + private void setName (String name) + { + this.name = name; + + constantColor = new Constant.Color( + getClass().getName(), + name + ".color", + Constant.Color.encodeColor(color), + "Color code for set " + name); + + // Check for a user-modified value + if (!constantColor.isSourceValue()) { + setColor(constantColor.getValue()); + } + } + + //-----------------// + // getSortedShapes // + //-----------------// + /** + * Exports the sorted collection of shapes. + * + * @return the proper enum set + */ + public List getSortedShapes () + { + if (sortedShapes != null) { + return sortedShapes; + } else { + return new ArrayList<>(shapes); + } + } + //-----------------// // addAllShapeSets // //-----------------// @@ -590,7 +714,7 @@ public static String getPhysicalShapeNamesString () for (int i = 0; i < names.size(); i++) { String comma = (i < (names.size() - 1)) ? "," : ""; - sb.append(String.format("\"%-18s // %3d\n", names.get(i) + "\"" + comma, i)); + sb.append(String.format("\"%-18s // %3d%n", names.get(i) + "\"" + comma, i)); } sb.append("};"); @@ -638,22 +762,15 @@ public static EnumSet getTemplateNotes (Sheet sheet) return set; } - //--------// - // getRep // - //--------// - /** - * Report the representative shape of the set, if any. - * - * @return the rep shape, or null - */ - public Shape getRep () - { - return rep; - } - //-------------// // getShapeSet // //-------------// + /** + * Report the ShapeSet for the provided name + * + * @param name provided name + * @return corresponding ShapeSet instance + */ public static ShapeSet getShapeSet (String name) { return Sets.map.get(name); @@ -662,22 +779,14 @@ public static ShapeSet getShapeSet (String name) //--------------// // getShapeSets // //--------------// - public static List getShapeSets () - { - return Sets.setList; - } - - //-----------// - // getShapes // - //-----------// /** - * Exports the set of shapes. + * Report the list of all ShapeSet instances * - * @return the proper enum set + * @return the list of ShapeSet instances */ - public EnumSet getShapes () + public static List getShapeSets () { - return shapes; + return Sets.setList; } //----------------------// @@ -740,20 +849,6 @@ public static EnumSet getVoidTemplateNotes (Sheet sheet) return set; } - //------------------// - // setConstantColor // - //------------------// - /** - * Define a specific color for the set. - * - * @param color the specified color - */ - public void setConstantColor (Color color) - { - constantColor.setValue(color); - setColor(color); - } - //----------// // shapesOf // //----------// @@ -876,62 +971,20 @@ public static ShapeSet valueOf (String str) return Sets.map.get(str); } - //----------// - // contains // - //----------// - /** - * Convenient method to check if encapsulated shapes set does - * contain the provided object. - * - * @param shape the Shape object to check for inclusion - * @return true if contained, false otherwise - */ - public boolean contains (Shape shape) - { - return shapes.contains(shape); - } - - //----------// - // getColor // - //----------// - /** - * Report the color currently assigned to the range, if any. - * - * @return the related color, or null - */ - public Color getColor () - { - return color; - } - - //---------// - // getName // - //---------// - /** - * Report the name of the set. - * - * @return the set name - */ - public String getName () - { - return name; - } - - //-----------------// - // getSortedShapes // - //-----------------// - /** - * Exports the sorted collection of shapes. - * - * @return the proper enum set - */ - public List getSortedShapes () + //----------------// + // addColoredItem // + //----------------// + private static void addColoredItem (JComponent top, + JMenuItem item, + Color color) { - if (sortedShapes != null) { - return sortedShapes; + if (color != null) { + item.setForeground(color); } else { - return new ArrayList(shapes); + item.setForeground(Color.black); } + + top.add(item); } //----------------------// @@ -977,66 +1030,16 @@ static void defineAllShapeColors () } } - //----------------// - // addColoredItem // - //----------------// - private static void addColoredItem (JComponent top, - JMenuItem item, - Color color) - { - if (color != null) { - item.setForeground(color); - } else { - item.setForeground(Color.black); - } - - top.add(item); - } - - //----------// - // setColor // - //----------// - /** - * Assign a display color to the shape set. - * - * @param color the display color - */ - private void setColor (Color color) - { - this.color = color; - } - - //---------// - // setName // - //---------// - private void setName (String name) - { - this.name = name; - - constantColor = new Constant.Color( - getClass().getName(), - name + ".color", - Constant.Color.encodeColor(color), - "Color code for set " + name); - - // Check for a user-modified value - if (!constantColor.isSourceValue()) { - setColor(constantColor.getValue()); - } - } - - //~ Inner Classes ------------------------------------------------------------------------------ //------// // Sets // //------// /** Build the set map in a lazy way */ private static class Sets { - //~ Static fields/initializers ------------------------------------------------------------- - static final Map map = new HashMap(); + static final Map map = new HashMap<>(); - static final List setList = new ArrayList(); + static final List setList = new ArrayList<>(); static { for (Field field : ShapeSet.class.getDeclaredFields()) { @@ -1052,7 +1055,6 @@ private static class Sets } } - //~ Constructors --------------------------------------------------------------------------- private Sets () { } diff --git a/src/main/org/audiveris/omr/glyph/SymbolSample.java b/src/main/org/audiveris/omr/glyph/SymbolSample.java index 2f91218a7..cdb238349 100644 --- a/src/main/org/audiveris/omr/glyph/SymbolSample.java +++ b/src/main/org/audiveris/omr/glyph/SymbolSample.java @@ -46,11 +46,9 @@ public class SymbolSample extends Sample { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SymbolSample.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Build an (artificial) sample out of a symbol icon. * This is meant to populate and train on shapes for which we have no real sample yet. @@ -66,10 +64,17 @@ protected SymbolSample (Shape shape, super(0, 0, runTable, interline, 0, shape, null); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // create // //--------// + /** + * Create a {@code SymbolSample}. + * + * @param shape assigned shape + * @param symbol the font-based symbol + * @param interline the related interline value + * @return the created SymbolSample + */ public static SymbolSample create (Shape shape, ShapeSymbol symbol, int interline) diff --git a/src/main/org/audiveris/omr/glyph/VirtualGlyph.java b/src/main/org/audiveris/omr/glyph/VirtualGlyph.java deleted file mode 100644 index 7c48c9011..000000000 --- a/src/main/org/audiveris/omr/glyph/VirtualGlyph.java +++ /dev/null @@ -1,68 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// V i r t u a l G l y p h // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.glyph; - -import java.awt.Point; - -/** - * Class {@code VirtualGlyph} is an artificial glyph specifically build from a - * MusicFont-based symbol, to carry a shape and features just like a standard glyph - * would. - * - * @author Hervé Bitteur - */ -public class VirtualGlyph - extends SymbolSample -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Create a new VirtualGlyph object - * - * @param shape the assigned shape - * @param interline the related interline scaling value - * @param center where the glyph area center will be located - */ - public VirtualGlyph (Shape shape, - int interline, - Point center) - { - super(shape, interline, null); - throw new RuntimeException("HB. Not implemented yet"); - - // // Build a glyph of proper size - // super(shape, Symbols.getSymbol(shape), interline, Group.DROP, null); - // - // // Translation from generic center to actual center - // translate(GeoUtil.vectorOf(getCenter(), center)); - } - - //~ Methods ------------------------------------------------------------------------------------ - //-----------// - // isVirtual // - //-----------// - @Override - public boolean isVirtual () - { - return true; - } -} diff --git a/src/main/org/audiveris/omr/glyph/WeakGlyph.java b/src/main/org/audiveris/omr/glyph/WeakGlyph.java index d44534175..ba7368b96 100644 --- a/src/main/org/audiveris/omr/glyph/WeakGlyph.java +++ b/src/main/org/audiveris/omr/glyph/WeakGlyph.java @@ -36,7 +36,6 @@ public class WeakGlyph extends WeakReference implements Entity, Comparable { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code WeakGlyph} object. @@ -48,18 +47,21 @@ public WeakGlyph (Glyph referent) super(referent); } - //~ Methods ------------------------------------------------------------------------------------ @Override public int compareTo (WeakGlyph that) { + if (this == that) { + return 0; + } + final Glyph thisGlyph = this.get(); final Glyph thatGlyph = that.get(); - if (thisGlyph == null) { - if (thatGlyph == null) { - return 0; - } + if (thisGlyph == thatGlyph) { + return 0; + } + if (thisGlyph == null) { return -1; } @@ -146,6 +148,16 @@ public int getId () return 0; } + @Override + public void setId (int id) + { + final Glyph glyph = get(); + + if (glyph != null) { + glyph.setId(id); + } + } + //----------// // hashCode // //----------// @@ -175,16 +187,6 @@ public boolean isVip () return false; } - @Override - public void setId (int id) - { - final Glyph glyph = get(); - - if (glyph != null) { - glyph.setId(id); - } - } - @Override public void setVip (boolean vip) { diff --git a/src/main/org/audiveris/omr/glyph/WeightedEntity.java b/src/main/org/audiveris/omr/glyph/WeightedEntity.java index 473895308..946b9382d 100644 --- a/src/main/org/audiveris/omr/glyph/WeightedEntity.java +++ b/src/main/org/audiveris/omr/glyph/WeightedEntity.java @@ -37,7 +37,6 @@ public interface WeightedEntity extends Entity, AttachmentHolder { - //~ Methods ------------------------------------------------------------------------------------ /** * Assign a group to this symbol. diff --git a/src/main/org/audiveris/omr/glyph/dynamic/CompoundFactory.java b/src/main/org/audiveris/omr/glyph/dynamic/CompoundFactory.java index 347b71ed7..038766d47 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/CompoundFactory.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/CompoundFactory.java @@ -41,11 +41,9 @@ */ public class CompoundFactory { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(CompoundFactory.class); - //~ Methods ------------------------------------------------------------------------------------ //---------------// // buildCompound // //---------------// @@ -90,7 +88,7 @@ public static SectionCompound buildCompoundFromParts ( throw new IllegalArgumentException("Building a SectionCompound out of no parts"); } - List

                sections = new ArrayList
                (); + List
                sections = new ArrayList<>(); for (SectionCompound part : parts) { sections.addAll(part.getMembers()); @@ -117,10 +115,10 @@ public static List buildCompounds (Collection
                sections CompoundConstructor constructor) { // Build a temporary graph of all sections with "touching" relations - List
                list = new ArrayList
                (sections); + List
                list = new ArrayList<>(sections); ///Collections.sort(list, Section.byAbscissa); - SimpleGraph graph = new SimpleGraph(Touching.class); + SimpleGraph graph = new SimpleGraph<>(Touching.class); // Populate graph with all sections as vertices for (Section section : list) { @@ -139,12 +137,11 @@ public static List buildCompounds (Collection
                sections } // Retrieve all the clusters of sections (sets of touching sections) - ConnectivityInspector inspector = new ConnectivityInspector( - graph); + ConnectivityInspector inspector = new ConnectivityInspector<>(graph); List> sets = inspector.connectedSets(); logger.debug("sets: {}", sets.size()); - List compounds = new ArrayList(); + List compounds = new ArrayList<>(); for (Set
                set : sets) { compounds.add(buildCompound(set, constructor)); @@ -153,7 +150,6 @@ public static List buildCompounds (Collection
                sections return compounds; } - //~ Inner Interfaces --------------------------------------------------------------------------- //---------------------// // CompoundConstructor // //---------------------// @@ -162,12 +158,15 @@ public static List buildCompounds (Collection
                sections */ public interface CompoundConstructor { - //~ Methods -------------------------------------------------------------------------------- + /** + * Actual creation of compound instance. + * + * @return the created compound + */ SectionCompound newInstance (); } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // Touching // //----------// @@ -177,4 +176,8 @@ public interface CompoundConstructor private static class Touching { } + + private CompoundFactory () + { + } } diff --git a/src/main/org/audiveris/omr/glyph/dynamic/Compounds.java b/src/main/org/audiveris/omr/glyph/dynamic/Compounds.java index ee6d9d512..3f47dc6e9 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/Compounds.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/Compounds.java @@ -40,7 +40,6 @@ */ public abstract class Compounds { - //~ Constructors ------------------------------------------------------------------------------- /** * No meant to be instantiated. @@ -49,7 +48,6 @@ private Compounds () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // byReverseLength // //-----------------// @@ -192,7 +190,7 @@ public static double getThicknessAt (double coord, */ public static Set
                sectionsOf (Collection compounds) { - Set
                sections = new TreeSet
                (); + Set
                sections = new TreeSet<>(); for (SectionCompound compound : compounds) { sections.addAll(compound.getMembers()); diff --git a/src/main/org/audiveris/omr/glyph/dynamic/CurvedFilament.java b/src/main/org/audiveris/omr/glyph/dynamic/CurvedFilament.java index b026b2f41..1907af505 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/CurvedFilament.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/CurvedFilament.java @@ -52,11 +52,9 @@ public class CurvedFilament extends Filament { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(CurvedFilament.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Typical length between points. */ protected final int segmentLength; @@ -66,7 +64,6 @@ public class CurvedFilament /** Curved line across all defining points. */ protected NaturalSpline spline; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code CurvedFilament} object. * @@ -80,7 +77,6 @@ public CurvedFilament (int interline, this.segmentLength = segmentLength; } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // computeLine // //-------------// @@ -97,8 +93,7 @@ public void computeLine () // We need a rough orientation right now Orientation orientation = getRoughOrientation(); - Point2D orientedStart = (startPoint == null) ? null : orientation.oriented( - startPoint); + Point2D orientedStart = (startPoint == null) ? null : orientation.oriented(startPoint); Point2D orientedStop = (stopPoint == null) ? null : orientation.oriented(stopPoint); Rectangle oBounds = orientation.oriented(getBounds()); double oStart = (orientedStart != null) ? orientedStart.getX() : oBounds.x; @@ -113,7 +108,7 @@ public void computeLine () // Determine the number of segments and their precise length int segCount = (int) Math.rint(length / segmentLength); double segLength = length / segCount; - List newPoints = new ArrayList(segCount + 1); + List newPoints = new ArrayList<>(segCount + 1); // First point if (startPoint == null) { @@ -212,16 +207,16 @@ public double getPositionAt (double coord, if (orientation == Orientation.HORIZONTAL) { if ((coord < startPoint.getX()) || (coord > stopPoint.getX())) { - double sl = (stopPoint.getY() - startPoint.getY()) / (stopPoint.getX() - - startPoint.getX()); + double sl = (stopPoint.getY() - startPoint.getY()) / (stopPoint.getX() - startPoint + .getX()); return startPoint.getY() + (sl * (coord - startPoint.getX())); } else { return spline.yAtX(coord); } } else if ((coord < startPoint.getY()) || (coord > stopPoint.getY())) { - double sl = (stopPoint.getX() - startPoint.getX()) / (stopPoint.getY() - - startPoint.getY()); + double sl = (stopPoint.getX() - startPoint.getX()) / (stopPoint.getY() - startPoint + .getY()); return startPoint.getX() + (sl * (coord - startPoint.getY())); } else { @@ -250,6 +245,11 @@ public double getSlopeAt (double coord, //-----------// // getSpline // //-----------// + /** + * Report the underlying NaturalSpline. + * + * @return the computed spline + */ public NaturalSpline getSpline () { if (spline == null) { @@ -295,7 +295,7 @@ public void polishCurvature (int minimumRadius) final List bisectors = getBisectors(); // Compute radius values (using same index as points) - final List radii = new ArrayList(); + final List radii = new ArrayList<>(); radii.add(null); // To skip index 0 for which we have no value (???) for (int i = 1, iBreak = points.size() - 1; i < iBreak; i++) { @@ -338,11 +338,11 @@ public void polishCurvature (int minimumRadius) final Point2D point = points.get(idx); Point2D orientedPt = orientation.oriented(points.get(idx)); Rectangle2D rect = new Rectangle2D.Double( - orientedPt.getX() - (probeWidth / 2), - orientedPt.getY() - (probeWidth / 2), + orientedPt.getX() - (probeWidth / 2.0), + orientedPt.getY() - (probeWidth / 2.0), probeWidth, probeWidth); - List
                found = new ArrayList
                (); + List
                found = new ArrayList<>(); for (Section section : getMembers()) { if (rect.intersects(section.getOrientedBounds())) { @@ -352,19 +352,17 @@ public void polishCurvature (int minimumRadius) if (found.size() > 1) { // Pick up the section closest to the point - Collections.sort( - found, - new Comparator
                () - { - @Override - public int compare (Section s1, - Section s2) - { - return Double.compare( - point.distance(s1.getCentroid()), - point.distance(s2.getCentroid())); - } - }); + Collections.sort(found, new Comparator
                () + { + @Override + public int compare (Section s1, + Section s2) + { + return Double.compare( + point.distance(s1.getCentroid()), + point.distance(s2.getCentroid())); + } + }); } Section section = found.isEmpty() ? null : found.get(0); @@ -429,19 +427,27 @@ public void renderLine (Graphics2D g, //-----------// // findPoint // //-----------// + /** + * Report the defining point, if any, near the provided coordinate. + * + * @param coord the provided coordinate value + * @param orientation the rough filament orientation + * @param margin the maximum acceptable delta on coordinate + * @return the defining point or null if none + */ protected Point2D findPoint (int coord, Orientation orientation, int margin) { Point2D best = null; - double bestDeltacoord = Integer.MAX_VALUE; + double bestDeltaCoord = Integer.MAX_VALUE; for (Point2D p : points) { double dc = Math.abs( coord - ((orientation == Orientation.HORIZONTAL) ? p.getX() : p.getY())); - if ((dc <= margin) && (dc < bestDeltacoord)) { - bestDeltacoord = dc; + if ((dc <= margin) && (dc < bestDeltaCoord)) { + bestDeltaCoord = dc; best = p; } } @@ -452,6 +458,9 @@ protected Point2D findPoint (int coord, //-----------------// // invalidateCache // //-----------------// + /** + * + */ @Override protected void invalidateCache () { @@ -471,7 +480,7 @@ protected void invalidateCache () */ private List getBisectors () { - List bisectors = new ArrayList(); + List bisectors = new ArrayList<>(); for (int i = 0; i < (points.size() - 1); i++) { bisectors.add(LineUtil.bisector(new Line2D.Double(points.get(i), points.get(i + 1)))); @@ -510,4 +519,43 @@ private double getRadius (int i, return Math.hypot(inter.getX() - point.getX(), inter.getY() - point.getY()); } + + //-------------// + // Constructor // + //-------------// + /** + * A constructor for a CurvedFilament. + */ + public static class Constructor + implements CompoundFactory.CompoundConstructor + { + + private final int interline; + + private final int segmentLength; + + /** + * Creates the constructor. + * + * @param interline related interline value + * @param segmentLength typical segmenting length + */ + public Constructor (int interline, + int segmentLength) + { + this.interline = interline; + this.segmentLength = segmentLength; + } + + /** + * Creates the CurvedFilament instance + * + * @return CurvedFilament instance + */ + @Override + public SectionCompound newInstance () + { + return new CurvedFilament(interline, segmentLength); + } + } } diff --git a/src/main/org/audiveris/omr/glyph/dynamic/Filament.java b/src/main/org/audiveris/omr/glyph/dynamic/Filament.java index 792277b9f..cf4e4e8bf 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/Filament.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/Filament.java @@ -41,11 +41,12 @@ * alignments. *
              • {@link StraightFilament} implements a straight filament, for stems and legs of endings.
              • * + * + * @author Hervé Bitteur */ public abstract class Filament extends SectionCompound { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -65,7 +66,6 @@ public int compare (Filament f1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** Absolute beginning point. */ protected Point2D startPoint; @@ -75,7 +75,6 @@ public int compare (Filament f1, /** Scaling interline. */ protected int interline; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code Filament} object. * @@ -86,7 +85,6 @@ public Filament (int interline) this.interline = interline; } - //~ Methods ------------------------------------------------------------------------------------ /** * Force line (re)computation. */ @@ -134,19 +132,6 @@ public abstract void renderLine (Graphics2D g, boolean showPoints, double pointWidth); - //---------------// - // getProbeWidth // - //---------------// - /** - * Report the width of the window used to determine filament position - * - * @return the scale-independent probe width - */ - public static Scale.Fraction getProbeWidth () - { - return constants.probeWidth; - } - //----------// // getSlope // //----------// @@ -193,6 +178,14 @@ public Point2D getStopPoint () //-----------------// // setEndingPoints // //-----------------// + /** + * Assign the ending points. + *

                + * This triggers a (re)-computing of line and bounds. + * + * @param startPoint the provided starting point + * @param stopPoint the providing stopping point + */ public void setEndingPoints (Point2D startPoint, Point2D stopPoint) { @@ -212,6 +205,11 @@ public void setEndingPoints (Point2D startPoint, //---------------------// // getRoughOrientation // //---------------------// + /** + * Report a (rough) orientation of the filament (vertical or horizontal) + * + * @return rough orientation + */ protected Orientation getRoughOrientation () { checkBounds(); @@ -222,6 +220,9 @@ protected Orientation getRoughOrientation () //-----------------// // invalidateCache // //-----------------// + /** + * Invalidate the cached data. + */ @Override protected void invalidateCache () { @@ -229,14 +230,25 @@ protected void invalidateCache () startPoint = stopPoint = null; } - //~ Inner Classes ------------------------------------------------------------------------------ + //---------------// + // getProbeWidth // + //---------------// + /** + * Report the width of the window used to determine filament position + * + * @return the scale-independent probe width + */ + public static Scale.Fraction getProbeWidth () + { + return constants.probeWidth; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction probeWidth = new Scale.Fraction( 0.5, diff --git a/src/main/org/audiveris/omr/glyph/dynamic/FilamentBoard.java b/src/main/org/audiveris/omr/glyph/dynamic/FilamentBoard.java index 5870ede9e..aba68e866 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/FilamentBoard.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/FilamentBoard.java @@ -33,7 +33,6 @@ public class FilamentBoard extends EntityBoard { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code FilamentBoard} object. diff --git a/src/main/org/audiveris/omr/glyph/dynamic/FilamentFactory.java b/src/main/org/audiveris/omr/glyph/dynamic/FilamentFactory.java index 4650cba4a..19cbf7b1b 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/FilamentFactory.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/FilamentFactory.java @@ -44,13 +44,14 @@ import java.awt.Rectangle; import java.awt.geom.Point2D; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -67,7 +68,8 @@ * The factory aims at a given filaments orientation, though the collection of input sections may * exhibit mixed orientations. *

                - * The factory works in two phases:

                  + * The factory works in two phases: + *
                    *
                  1. The first phase, by default, discovers skeletons lines using the long input sections and * merges them as much as possible. * This strategy fits well the case of a population of sections with no organization known a priori. @@ -75,26 +77,23 @@ * focus on them only. *
                  2. *
                  3. The second phase completes these skeletons whenever possible by short sections left over, and - * merges them again.
                  + * merges them again. + *
                *

                * Customization: Default parameters values are defined via a ConstantSet. * Before launching filaments retrieval by {@link #retrieveFilaments}, parameters can be modified * individually by calling proper setXXX() methods. * * @param precise filament type - * * @author Hervé Bitteur */ public class FilamentFactory { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(FilamentFactory.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Related scale. */ private final Scale scale; @@ -111,12 +110,11 @@ public class FilamentFactory private final Parameters params; /** Processed sections. true/false */ - private final Set

                processedSections = new LinkedHashSet
                (); + private final Set
                processedSections = new LinkedHashSet<>(); /** Fat sections. unknown/true/false */ - private final Map fatSections = new HashMap(); + private final Map fatSections = new HashMap<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a factory of filaments. * @@ -138,7 +136,8 @@ public FilamentFactory (Scale scale, try { filamentConstructor = filamentClass.getConstructor(int.class); - } catch (Exception ex) { + } catch (NoSuchMethodException | + SecurityException ex) { logger.error(null, ex); } @@ -146,10 +145,14 @@ public FilamentFactory (Scale scale, params.initialize(); } - //~ Methods ------------------------------------------------------------------------------------ //------// // dump // //------// + /** + * Dump the factory parameters. + * + * @param title optional title + */ public void dump (String title) { if (constants.printParameters.isSet()) { @@ -216,7 +219,7 @@ public boolean isSectionFat (Section section) return setFat( section, (startThickness > params.maxThickness) - || (stopThickness > params.maxThickness)); + || (stopThickness > params.maxThickness)); } else { return setFat(section, bounds.height > params.maxThickness); } @@ -239,7 +242,7 @@ public boolean isSectionFat (Section section) public List retrieveFilaments (Collection
                source) { StopWatch watch = new StopWatch("FilamentsFactory " + orientation); - List filaments = new ArrayList(); + List filaments = new ArrayList<>(); try { // Create a filament for each section long & slim @@ -550,7 +553,7 @@ private boolean canMerge (Filament one, // Check thickness consistency if ((-coordGap <= params.maxInvolvingLength) - && (thickness > maxConsistentThickness)) { + && (thickness > maxConsistentThickness)) { if (logger.isDebugEnabled() || areVips) { logger.info( "{}Non consistent thickness: {} vs {} {} {}", @@ -565,9 +568,11 @@ private boolean canMerge (Filament one, } // Check space between overlapped filaments - double space = thickness - - (Compounds.getThicknessAt(midCoord, orientation, scale, one) - + Compounds.getThicknessAt(midCoord, orientation, scale, two)); + double space = thickness - (Compounds.getThicknessAt( + midCoord, + orientation, + scale, + one) + Compounds.getThicknessAt(midCoord, orientation, scale, two)); if (space > maxSpace) { if (logger.isDebugEnabled() || areVips) { @@ -608,8 +613,8 @@ private boolean canMerge (Filament one, } // Compute position gap, taking thickness into account - double oneThickness = one.getWeight() / one.getLength(orientation); - double twoThickness = two.getWeight() / two.getLength(orientation); + double oneThickness = (double) one.getWeight() / one.getLength(orientation); + double twoThickness = (double) two.getWeight() / two.getLength(orientation); int posMargin = (int) Math.rint(Math.max(oneThickness, twoThickness) / 2); double posGap = Math.abs(gapStop.getY() - gapStart.getY()) - posMargin; @@ -648,7 +653,7 @@ private boolean canMerge (Filament one, double twoLength = twoStop.getX() - twoStart.getX() + 1; if ((oneLength >= params.minLengthForDeltaSlope) - && (twoLength >= params.minLengthForDeltaSlope)) { + && (twoLength >= params.minLengthForDeltaSlope)) { double oneSlope = LineUtil.getSlope(oneStart, oneStop); double twoSlope = LineUtil.getSlope(twoStart, twoStop); double deltaSlope = Math.abs(twoSlope - oneSlope); @@ -716,7 +721,10 @@ private F createFilament (Section section) } return fil; - } catch (Exception ex) { + } catch (IllegalAccessException | + IllegalArgumentException | + InstantiationException | + InvocationTargetException ex) { logger.error(null, ex); return null; @@ -781,7 +789,7 @@ private List expandFilaments (List filaments, { try { // Sort sections by first position - List
                sections = new ArrayList
                (); + List
                sections = new ArrayList<>(); for (Section section : source) { if (!isProcessed(section) && !isSectionFat(section)) { @@ -795,7 +803,7 @@ private List expandFilaments (List filaments, // We allocate one glyph per candidate section // (simply to be able to reuse the canMerge() method !!!!!!!) - List sectionGlyphs = new ArrayList(sections.size()); + List sectionGlyphs = new ArrayList<>(sections.size()); for (Section section : sections) { Filament sectionFil = createFilament(section); @@ -924,7 +932,7 @@ private void mergeFilaments (List filaments) head.stealSections(candidate); candidate = head; // This is the new candidate - break HeadsLoop; + break; } } else if (head.isVip() && candidate.isVip()) { logger.info( @@ -976,9 +984,9 @@ private F populateLine (Collection
                source, } } else { Point centroid = section.getCentroid(); - double gap = (orientation == HORIZONTAL) - ? (line.yAtXExt(centroid.x) - centroid.y) - : (line.xAtYExt(centroid.y) - centroid.x); + double gap = (orientation == HORIZONTAL) ? (line.yAtXExt(centroid.x) + - centroid.y) : (line + .xAtYExt(centroid.y) - centroid.x); if (Math.abs(gap) <= params.maxPosGap) { fil.addSection(section); @@ -1028,126 +1036,6 @@ private void setProcessed (Section section) processedSections.add(section); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print out the stop watch?"); - - private final Constant.Boolean printParameters = new Constant.Boolean( - false, - "Should we print out the factory parameters?"); - - private final Constant.Double maxGapSlope = new Constant.Double( - "tangent", - 0.5, - "Maximum absolute slope for a gap"); - - private final Constant.Ratio minSectionAspect = new Constant.Ratio( - 3, - "Minimum section aspect (length / thickness)"); - - private final Constant.Ratio maxConsistentRatio = new Constant.Ratio( - 1.7, - "Maximum thickness ratio for consistent merge"); - - private final Constant.Ratio maxDeltaSlope = new Constant.Ratio( - 0.01, - "Maximum slope difference between long filaments"); - - // Constants specified WRT mean line thickness - // ------------------------------------------- - // - private final Scale.LineFraction maxFilamentThickness = new Scale.LineFraction( - 1.5, - "Maximum filament thickness WRT mean line height"); - - private final Scale.LineFraction maxPosGap = new Scale.LineFraction( - 0.75, - "Maximum delta position for a gap between filaments"); - - // Constants specified WRT mean interline - // -------------------------------------- - // - private final Scale.Fraction minCoreSectionLength = new Scale.Fraction( - 1, - "Minimum length for a section to be considered as core"); - - private final Scale.Fraction maxOverlapDeltaPos = new Scale.Fraction( - 0.5, - "Maximum delta position between two overlapping filaments"); - - private final Scale.Fraction maxCoordGap = new Scale.Fraction( - 1, - "Maximum delta coordinate for a gap between filaments"); - - private final Scale.Fraction maxOverlapSpace = new Scale.Fraction( - 0.16, - "Maximum space between overlapping filaments"); - - private final Scale.Fraction maxExpansionSpace = new Scale.Fraction( - 0.02, - "Maximum space when expanding filaments"); - - private final Scale.Fraction maxPosGapForSlope = new Scale.Fraction( - 0.1, - "Maximum delta Y to check slope for a gap between filaments"); - - private final Scale.Fraction maxInvolvingLength = new Scale.Fraction( - 2, - "Maximum filament length to apply thickness test"); - - private final Scale.Fraction minLengthForDeltaSlope = new Scale.Fraction( - 10, - "Minimum filament length to apply delta slope test"); - } - - //----------------// - // DistantSection // - //----------------// - /** - * Meant to ease sorting of sections according to their distance to line. - */ - private static class DistantSection - implements Comparable - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Underlying section. */ - final Section section; - - /** Distance to line. */ - final double dist; - - //~ Constructors --------------------------------------------------------------------------- - public DistantSection (Section section, - double dist) - { - this.section = section; - this.dist = dist; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public int compareTo (DistantSection that) - { - return Double.compare(dist, that.dist); - } - - @Override - public String toString () - { - return dist + "/" + section; - } - } - //------------// // Parameters // //------------// @@ -1156,7 +1044,6 @@ public String toString () */ private class Parameters { - //~ Instance fields ------------------------------------------------------------------------ /** Maximum thickness for filaments */ public int maxThickness; @@ -1198,7 +1085,6 @@ private class Parameters public double maxDeltaSlope; - //~ Methods -------------------------------------------------------------------------------- public void dump (String title) { new Dumping().dump(this, title); @@ -1231,4 +1117,83 @@ public void initialize () } } } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print out the stop watch?"); + + private final Constant.Boolean printParameters = new Constant.Boolean( + false, + "Should we print out the factory parameters?"); + + private final Constant.Double maxGapSlope = new Constant.Double( + "tangent", + 0.5, + "Maximum absolute slope for a gap"); + + private final Constant.Ratio minSectionAspect = new Constant.Ratio( + 3, + "Minimum section aspect (length / thickness)"); + + private final Constant.Ratio maxConsistentRatio = new Constant.Ratio( + 1.7, + "Maximum thickness ratio for consistent merge"); + + private final Constant.Ratio maxDeltaSlope = new Constant.Ratio( + 0.01, + "Maximum slope difference between long filaments"); + + // Constants specified WRT mean line thickness + // ------------------------------------------- + // + private final Scale.LineFraction maxFilamentThickness = new Scale.LineFraction( + 1.5, + "Maximum filament thickness WRT mean line height"); + + private final Scale.LineFraction maxPosGap = new Scale.LineFraction( + 0.75, + "Maximum delta position for a gap between filaments"); + + // Constants specified WRT mean interline + // -------------------------------------- + // + private final Scale.Fraction minCoreSectionLength = new Scale.Fraction( + 1, + "Minimum length for a section to be considered as core"); + + private final Scale.Fraction maxOverlapDeltaPos = new Scale.Fraction( + 0.5, + "Maximum delta position between two overlapping filaments"); + + private final Scale.Fraction maxCoordGap = new Scale.Fraction( + 1, + "Maximum delta coordinate for a gap between filaments"); + + private final Scale.Fraction maxOverlapSpace = new Scale.Fraction( + 0.16, + "Maximum space between overlapping filaments"); + + private final Scale.Fraction maxExpansionSpace = new Scale.Fraction( + 0.02, + "Maximum space when expanding filaments"); + + private final Scale.Fraction maxPosGapForSlope = new Scale.Fraction( + 0.1, + "Maximum delta Y to check slope for a gap between filaments"); + + private final Scale.Fraction maxInvolvingLength = new Scale.Fraction( + 2, + "Maximum filament length to apply thickness test"); + + private final Scale.Fraction minLengthForDeltaSlope = new Scale.Fraction( + 10, + "Minimum filament length to apply delta slope test"); + } } diff --git a/src/main/org/audiveris/omr/glyph/dynamic/FilamentIndex.java b/src/main/org/audiveris/omr/glyph/dynamic/FilamentIndex.java index 1abab6152..1ef92e9ca 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/FilamentIndex.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/FilamentIndex.java @@ -50,7 +50,6 @@ public class FilamentIndex extends BasicIndex { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -58,15 +57,13 @@ public class FilamentIndex /** Events that can be published on filament service. */ private static final Class[] eventsAllowed = new Class[]{ - EntityListEvent.class, IdEvent.class - }; + EntityListEvent.class, + IdEvent.class}; - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ @Navigable(false) private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code FilamentIndex} object. * @@ -88,7 +85,7 @@ public FilamentIndex (Sheet sheet) // User filament service? if (OMR.gui != null) { - FilamentService filamentService = new FilamentService(); + FilamentService filamentService = new FilamentService(this, sheet); setEntityService(filamentService); // @@ -116,7 +113,6 @@ public FilamentIndex (Sheet sheet) } } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getName // //---------// @@ -148,14 +144,13 @@ public Sheet getSheet () public void publish (final Filament filament) { if (entityService != null) { - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () { entityService.publish( - new EntityListEvent( + new EntityListEvent<>( this, SelectionHint.ENTITY_INIT, MouseMovement.PRESSING, @@ -165,14 +160,12 @@ public void run () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String vipFilaments = new Constant.String( "", @@ -182,14 +175,14 @@ private static final class Constants //-----------------// // FilamentService // //-----------------// - private class FilamentService + private static class FilamentService extends EntityService { - //~ Constructors --------------------------------------------------------------------------- - public FilamentService () + FilamentService (FilamentIndex index, + Sheet sheet) { - super(FilamentIndex.this, sheet.getLocationService(), eventsAllowed); + super(index, sheet.getLocationService(), eventsAllowed); } } } diff --git a/src/main/org/audiveris/omr/glyph/dynamic/FilamentView.java b/src/main/org/audiveris/omr/glyph/dynamic/FilamentView.java index 603ccfc9a..2548a7dd4 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/FilamentView.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/FilamentView.java @@ -36,17 +36,14 @@ /** * Class {@code FilamentView} handles a view on sheet filaments. * - * * @author Hervé Bitteur */ public class FilamentView extends EntityView { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(FilamentView.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code FilamentView} object. * @@ -57,7 +54,6 @@ public FilamentView (EntityService entityService) super(entityService); } - //~ Methods ------------------------------------------------------------------------------------ @Override protected void render (Graphics2D g) { diff --git a/src/main/org/audiveris/omr/glyph/dynamic/LinkedSection.java b/src/main/org/audiveris/omr/glyph/dynamic/LinkedSection.java index ce7d40cf9..ab24bc107 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/LinkedSection.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/LinkedSection.java @@ -64,7 +64,6 @@ public class LinkedSection implements Section { - //~ Instance fields ---------------------------------------------------------------------------- /** Concrete underlying section, to which most features are delegated. */ private final Section section; @@ -77,7 +76,6 @@ public class LinkedSection private SectionCompound compound; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code LinkedSection} object. * @@ -88,14 +86,18 @@ public LinkedSection (Section section) this.section = section; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // addSource // //-----------// + /** + * Add a source to the section. + * + * @param source a source section + */ public void addSource (LinkedSection source) { if (sources == null) { - sources = new ArrayList(); + sources = new ArrayList<>(); } sources.add(source); @@ -104,10 +106,15 @@ public void addSource (LinkedSection source) //-----------// // addTarget // //-----------// + /** + * Add a target to the section. + * + * @param target a target section + */ public void addTarget (LinkedSection target) { if (targets == null) { - targets = new ArrayList(); + targets = new ArrayList<>(); } targets.add(target); @@ -204,11 +211,29 @@ public Point2D getCentroid2D () //-------------// // getCompound // //-------------// + /** + * Report the underlying compound. + * + * @return underlying section compound + */ public SectionCompound getCompound () { return compound; } + //-------------// + // setCompound // + //-------------// + /** + * Set the underlying compound section + * + * @param compound underlying section + */ + public void setCompound (SectionCompound compound) + { + this.compound = compound; + } + @Override public int getFirstPos () { @@ -227,12 +252,24 @@ public int getId () return section.getId(); } + @Override + public void setId (int id) + { + section.setId(id); + } + @Override public Lag getLag () { return section.getLag(); } + @Override + public void setLag (Lag lag) + { + section.setLag(lag); + } + @Override public int getLastPos () { @@ -402,6 +439,11 @@ public boolean intersects (Section that) //-------------// // isProcessed // //-------------// + /** + * Tell whether this LinkedSection has already been processed + * + * @return true if so + */ public boolean isProcessed () { return processed; @@ -419,6 +461,12 @@ public boolean isVip () return section.isVip(); } + @Override + public void setVip (boolean vip) + { + section.setVip(vip); + } + @Override public boolean render (Graphics g, boolean drawBorders, @@ -433,40 +481,17 @@ public boolean renderSelected (Graphics g) return section.renderSelected(g); } - //-------------// - // setCompound // - //-------------// - public void setCompound (SectionCompound compound) - { - this.compound = compound; - } - - @Override - public void setId (int id) - { - section.setId(id); - } - - @Override - public void setLag (Lag lag) - { - section.setLag(lag); - } - //--------------// // setProcessed // //--------------// + /** + * Flag the instance as processed. + */ public void setProcessed () { processed = true; } - @Override - public void setVip (boolean vip) - { - section.setVip(vip); - } - //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/glyph/dynamic/SectionCompound.java b/src/main/org/audiveris/omr/glyph/dynamic/SectionCompound.java index 928874a3e..50ad5fa32 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/SectionCompound.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/SectionCompound.java @@ -24,7 +24,6 @@ import ij.process.ByteProcessor; import org.audiveris.omr.glyph.AbstractWeightedEntity; -import org.audiveris.omr.glyph.BasicGlyph; import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.GlyphGroup; import org.audiveris.omr.lag.Section; @@ -32,9 +31,7 @@ import org.audiveris.omr.math.GeoUtil; import org.audiveris.omr.math.PointUtil; import org.audiveris.omr.run.Orientation; - import static org.audiveris.omr.run.Orientation.*; - import org.audiveris.omr.run.RunTable; import org.audiveris.omr.run.RunTableFactory; import org.audiveris.omr.util.ByteUtil; @@ -57,14 +54,16 @@ public class SectionCompound extends AbstractWeightedEntity { - //~ Instance fields ---------------------------------------------------------------------------- + + /** Cached bounds. */ + protected Rectangle bounds; /** * Sections that compose this compound. * The collection is kept sorted on natural Section order (abscissa then ordinate then id, even * with mixed section orientations). */ - private final SortedSet
                members = new TreeSet
                (Section.byFullAbscissa); + private final SortedSet
                members = new TreeSet<>(Section.byFullAbscissa); /** Link to the compound, if any, this one is a part of. */ private SectionCompound partOf; @@ -72,10 +71,6 @@ public class SectionCompound /** Cached weight. */ private Integer weight; - /** Cached bounds. */ - protected Rectangle bounds; - - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new {@code SectionCompound} object. */ @@ -92,7 +87,6 @@ public SectionCompound (int interline) { } - //~ Methods ------------------------------------------------------------------------------------ //------------// // addSection // //------------// @@ -178,6 +172,19 @@ public Rectangle getBounds () return new Rectangle(bounds); } + //-----------// + // setBounds // + //-----------// + /** + * Force the compound contour bounds (when start and stop points are forced). + * + * @param bounds the forced contour box + */ + public void setBounds (Rectangle bounds) + { + this.bounds = bounds; + } + //-----------// // getCenter // //-----------// @@ -306,6 +313,19 @@ public SectionCompound getPartOf () return partOf; } + //-----------// + // setPartOf // + //-----------// + /** + * Record the link to the compound which has "stolen" the sections of this compound. + * + * @param compound the containing compound, if any + */ + public void setPartOf (SectionCompound compound) + { + partOf = compound; + } + //--------// // getTop // //--------// @@ -375,32 +395,6 @@ public boolean removeSection (Section section, return bool; } - //-----------// - // setBounds // - //-----------// - /** - * Force the compound contour bounds (when start and stop points are forced). - * - * @param bounds the forced contour box - */ - public void setBounds (Rectangle bounds) - { - this.bounds = bounds; - } - - //-----------// - // setPartOf // - //-----------// - /** - * Record the link to the compound which has "stolen" the sections of this compound. - * - * @param compound the containing compound, if any - */ - public void setPartOf (SectionCompound compound) - { - partOf = compound; - } - //---------------// // stealSections // //---------------// @@ -423,6 +417,11 @@ public void stealSections (SectionCompound that) //----------// // toBuffer // //----------// + /** + * Build a buffer with compounds pixels. + * + * @return the populated buffer + */ public ByteProcessor toBuffer () { checkBounds(); @@ -453,13 +452,12 @@ public Glyph toGlyph (GlyphGroup group) final ByteProcessor buffer = toBuffer(); // Allocate and populate properly oriented run table - final RunTableFactory factory = new RunTableFactory( - (buffer.getWidth() > buffer.getHeight()) ? HORIZONTAL : VERTICAL, - null); + final RunTableFactory factory = new RunTableFactory((buffer.getWidth() > buffer.getHeight()) + ? HORIZONTAL : VERTICAL, null); final RunTable runTable = factory.createTable(buffer); // Allocate glyph with proper offset - final Glyph glyph = new BasicGlyph(bounds.x, bounds.y, runTable); + final Glyph glyph = new Glyph(bounds.x, bounds.y, runTable); glyph.addGroup(group); return glyph; @@ -488,6 +486,9 @@ public boolean touches (Section section) //-------------// // checkBounds // //-------------// + /** + * + */ protected void checkBounds () { if (bounds == null) { @@ -523,9 +524,46 @@ protected String internals () //-----------------// // invalidateCache // //-----------------// + /** + * Invalidate cached data. + */ protected void invalidateCache () { weight = null; bounds = null; } + + //-------------// + // Constructor // + //-------------// + /** + * A constructor for a SectionCompound. + */ + public static class Constructor + implements CompoundFactory.CompoundConstructor + { + + private final int interline; + + /** + * Create the constructor. + * + * @param interline related interline + */ + public Constructor (int interline) + { + this.interline = interline; + } + + /** + * Create the SectionCompound instance + * + * @return SectionCompound instance + */ + @Override + public SectionCompound newInstance () + { + return new SectionCompound(interline); + } + } } diff --git a/src/main/org/audiveris/omr/glyph/dynamic/StickFactory.java b/src/main/org/audiveris/omr/glyph/dynamic/StickFactory.java index 6f7d4a0c9..782c47bcb 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/StickFactory.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/StickFactory.java @@ -78,13 +78,11 @@ */ public class StickFactory { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(StickFactory.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Sticks orientation. */ private final Orientation orientation; @@ -112,7 +110,6 @@ public class StickFactory /** Scale-dependent constants. */ private final Parameters params; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StickFactory} object. * @@ -142,7 +139,6 @@ public StickFactory (Orientation orientation, params = new Parameters(maxStickThickness, minCoreSectionLength, minSideRatio); } - //~ Methods ------------------------------------------------------------------------------------ /** * At system level, retrieve all candidate sticks (stem seeds / ledgers). * @@ -186,7 +182,7 @@ public List retrieveSticks (List
                systemSections, //-------------// // isProcessed // //-------------// - private final boolean isProcessed (LinkedSection section) + private boolean isProcessed (LinkedSection section) { return section.isProcessed(); } @@ -194,7 +190,7 @@ private final boolean isProcessed (LinkedSection section) //--------------// // setProcessed // //--------------// - private final void setProcessed (LinkedSection section) + private void setProcessed (LinkedSection section) { section.setProcessed(); } @@ -210,7 +206,7 @@ private final void setProcessed (LinkedSection section) private void addStickers (Filament fil) { final Set
                members = fil.getMembers(); - final Set
                stickers = new LinkedHashSet
                (); + final Set
                stickers = new LinkedHashSet<>(); for (boolean reverse : new boolean[]{true, false}) { for (Section s : members) { @@ -219,9 +215,8 @@ private void addStickers (Filament fil) // Main orientation for (LinkedSection linked : getNeighbors(ls, reverse)) { // Must be thin & isolated on the border - if ((linked.getCompound() == null) - && (linked.getRunCount() == 1) - && getNeighbors(linked, reverse).isEmpty()) { + if ((linked.getCompound() == null) && (linked.getRunCount() == 1) + && getNeighbors(linked, reverse).isEmpty()) { stickers.add(linked); } } @@ -230,11 +225,7 @@ && getNeighbors(linked, reverse).isEmpty()) { if (oppStickers != null) { Run endRun = reverse ? s.getFirstRun() : s.getLastRun(); int x = reverse ? (s.getFirstPos() - 1) : (s.getLastPos() + 1); - Rectangle luArea = new Rectangle( - x, - endRun.getStart(), - 1, - endRun.getLength()); + Rectangle luArea = new Rectangle(x, endRun.getStart(), 1, endRun.getLength()); List
                col = oppStickers.get(x); if (col != null) { @@ -270,7 +261,7 @@ private List buildSectionGraph (List
                sections) "buildSectionGraph S#" + system.getId() + " size:" + sections.size()); watch.start("create list"); - List list = new ArrayList(); + List list = new ArrayList<>(); for (Section section : sections) { list.add(new LinkedSection(section)); @@ -278,9 +269,8 @@ private List buildSectionGraph (List
                sections) watch.start("populate starts"); - final int posCount = orientation.isVertical() ? sheet.getWidth() - : sheet.getHeight(); - final SectionTally tally = new SectionTally(posCount, list); + final int posCount = orientation.isVertical() ? sheet.getWidth() : sheet.getHeight(); + final SectionTally tally = new SectionTally<>(posCount, list); // Detect and record connections watch.start("connections"); @@ -323,7 +313,7 @@ private List buildSectionGraph (List
                sections) private List buildSticks (List cores) { final int interline = scale.getInterline(); - final List fils = new ArrayList(); + final List fils = new ArrayList<>(); for (LinkedSection core : cores) { if (core.isVip()) { @@ -361,12 +351,12 @@ private List buildSticks (List cores) */ private List getCoreSections () { - List candidates = new ArrayList(); + List candidates = new ArrayList<>(); // Discard too thick or too short sections for (LinkedSection ls : allSections) { - if ((ls.getRunCount() <= params.maxStickThickness) - && (ls.getLength(orientation) >= params.minCoreSectionLength)) { + if ((ls.getRunCount() <= params.maxStickThickness) && (ls.getLength( + orientation) >= params.minCoreSectionLength)) { if ((predicate == null) || predicate.check(ls)) { candidates.add(ls); } @@ -374,19 +364,16 @@ private List getCoreSections () } // Sort candidates by decreasing length - Collections.sort( - candidates, - new Comparator
                () - { - @Override - public int compare (Section ls1, - Section ls2) - { - return Integer.compare( - ls2.getLength(orientation), - ls1.getLength(orientation)); - } - }); + Collections.sort(candidates, new Comparator
                () + { + @Override + public int compare (Section ls1, + Section ls2) + { + return Integer.compare(ls2.getLength(orientation), ls1.getLength( + orientation)); + } + }); return candidates; } @@ -412,7 +399,7 @@ private List getNeighbors (LinkedSection section, */ private Map> getOppositeStickers (List
                externalStickers) { - Map> map = new TreeMap>(); + Map> map = new TreeMap<>(); Collections.sort(externalStickers, Section.byCoordinate); int iStart = -1; @@ -450,7 +437,7 @@ private Map> getOppositeStickers (List
                externalS private Set getSideSections (Filament fil, boolean reverse) { - Set sideSections = new LinkedHashSet(); + Set sideSections = new LinkedHashSet<>(); // Look for members with no included section yet on desired side // TODO: this may be too restrictive? @@ -485,7 +472,7 @@ private void growFilament (Filament fil) boolean grown; // Map: reverse -> finished - Map finished = new HashMap(); + Map finished = new HashMap<>(); if (fil.isVip()) { index.publish(fil); @@ -502,22 +489,19 @@ private void growFilament (Filament fil) continue; } - final int filMeanThickness = (int) Math.rint( - fil.getMeanThickness(orientation)); + final int filMeanThickness = (int) Math.rint(fil.getMeanThickness(orientation)); // Determine the section(s) on this side of filament Set sideSections = getSideSections(fil, reverse); - Map contribs = new HashMap(); + Map contribs = new HashMap<>(); // Look for possible extensions on this side - final TreeSet allNeighbors = new TreeSet( - Section.byCoordinate); + final TreeSet allNeighbors = new TreeSet<>(Section.byCoordinate); int total = 0; int count = 0; for (LinkedSection sideSection : sideSections) { - Run sideRun = reverse ? sideSection.getFirstRun() - : sideSection.getLastRun(); + Run sideRun = reverse ? sideSection.getFirstRun() : sideSection.getLastRun(); List neighbors = getNeighbors(sideSection, reverse); for (Iterator it = neighbors.iterator(); it.hasNext();) { @@ -529,7 +513,7 @@ private void growFilament (Filament fil) final int thickness = neighbor.getRunCount(); if (((thickness + filMeanThickness) > params.maxStickThickness) - || ((predicate != null) && !predicate.check(neighbor))) { + || ((predicate != null) && !predicate.check(neighbor))) { it.remove(); } else { int length = neighbor.getLength(orientation); @@ -582,18 +566,16 @@ private void growFilament (Filament fil) finished.put(reverse, Boolean.TRUE); // We can't go any further on this side } } - } while (grown - && (Math.rint(fil.getMeanThickness(orientation)) < params.maxStickThickness)); + } while (grown && (Math.rint( + fil.getMeanThickness(orientation)) < params.maxStickThickness)); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, @@ -603,9 +585,8 @@ private static final class Constants //------------// // Parameters // //------------// - private class Parameters + private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ public final int maxStickThickness; @@ -613,10 +594,9 @@ private class Parameters public final double minSideRatio; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (int maxStickThickness, - int minCoreSectionLength, - double minSideRatio) + Parameters (int maxStickThickness, + int minCoreSectionLength, + double minSideRatio) { this.maxStickThickness = maxStickThickness; this.minCoreSectionLength = minCoreSectionLength; diff --git a/src/main/org/audiveris/omr/glyph/dynamic/StraightFilament.java b/src/main/org/audiveris/omr/glyph/dynamic/StraightFilament.java index d53793e0d..8f5860d08 100644 --- a/src/main/org/audiveris/omr/glyph/dynamic/StraightFilament.java +++ b/src/main/org/audiveris/omr/glyph/dynamic/StraightFilament.java @@ -46,11 +46,10 @@ public class StraightFilament extends Filament implements NearLine { - //~ Instance fields ---------------------------------------------------------------------------- + /** The approximating line. */ protected BasicLine line; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StraightFilament} object. * @@ -61,7 +60,6 @@ public StraightFilament (int interline) super(interline); } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // computeLine // //-------------// @@ -110,6 +108,11 @@ public void computeLine () //--------------// // getBasicLine // //--------------// + /** + * Return the approximating line as a BasicLine + * + * @return the BasicLine instance + */ public BasicLine getBasicLine () { checkLine(); @@ -271,4 +274,38 @@ private void checkLine () computeLine(); } } + + //-------------// + // Constructor // + //-------------// + /** + * Kind of 'constructor' for a StraightFilament. + */ + public static class Constructor + implements CompoundFactory.CompoundConstructor + { + + private final int interline; + + /** + * Create the constructor. + * + * @param interline the related interline + */ + public Constructor (int interline) + { + this.interline = interline; + } + + /** + * Create a new instance of StraightFilament. + * + * @return the new instance + */ + @Override + public SectionCompound newInstance () + { + return new StraightFilament(interline); + } + } } diff --git a/src/main/org/audiveris/omr/glyph/package-info.java b/src/main/org/audiveris/omr/glyph/package-info.java index 72c51a128..ddd126eb1 100644 --- a/src/main/org/audiveris/omr/glyph/package-info.java +++ b/src/main/org/audiveris/omr/glyph/package-info.java @@ -1,18 +1,4 @@ /** * Package for handling glyphs, seen as assemblies of pixels. */ -@XmlJavaTypeAdapters({ - @XmlJavaTypeAdapter(value = Jaxb.PointAdapter.class, type = Point.class), - @XmlJavaTypeAdapter(value = Jaxb.Point2DAdapter.class, type = Point2D.class), - @XmlJavaTypeAdapter(value = Jaxb.RectangleAdapter.class, type = Rectangle.class) -}) package org.audiveris.omr.glyph; - -import org.audiveris.omr.util.Jaxb; - -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.Point2D; - -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; diff --git a/src/main/org/audiveris/omr/glyph/ui/AbstractActionMenu.java b/src/main/org/audiveris/omr/glyph/ui/AbstractActionMenu.java index 553912776..8aac503e1 100644 --- a/src/main/org/audiveris/omr/glyph/ui/AbstractActionMenu.java +++ b/src/main/org/audiveris/omr/glyph/ui/AbstractActionMenu.java @@ -39,26 +39,26 @@ * Class {@code AbstractActionMenu} *

                * In a menu, actions are physically grouped by semantic tag and - * separators are inserted between such groups.

                + * separators are inserted between such groups. + *

                *

                * Actions are also organized according to their target menu level, * to allow actions to be dispatched into a hierarchy of menus. - * Although currently all levels are set to 0.

                + * Although currently all levels are set to 0. + *

                * * @author Hervé Bitteur */ public abstract class AbstractActionMenu extends AbstractGlyphMenu { - //~ Instance fields ---------------------------------------------------------------------------- /** Map action -> tag to update according to context */ - private final Map dynActions = new LinkedHashMap(); + private final Map dynActions = new LinkedHashMap<>(); /** Map action -> menu level */ - private final Map levels = new LinkedHashMap(); + private final Map levels = new LinkedHashMap<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AbstractActionMenu object. * @@ -71,7 +71,6 @@ public AbstractActionMenu (Sheet sheet, super(sheet, text); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // updateMenu // //------------// @@ -97,7 +96,7 @@ protected void initMenu () super.initMenu(); // Sort actions on their tag - SortedSet tags = new TreeSet(dynActions.values()); + SortedSet tags = new TreeSet<>(dynActions.values()); // Retrieve the highest menu level int maxLevel = 0; @@ -110,7 +109,8 @@ protected void initMenu () JMenu prevMenu = getMenu(); for (int level = 0; level <= maxLevel; level++) { - JMenu currentMenu = (level == 0) ? getMenu() : new SeparableMenu("Continued ..."); + SeparableMenu currentMenu = (level == 0) ? getMenu() + : new SeparableMenu("Continued ..."); for (Integer tag : tags) { for (Map.Entry entry : dynActions.entrySet()) { @@ -126,9 +126,7 @@ protected void initMenu () currentMenu.addSeparator(); } - if (currentMenu instanceof SeparableMenu) { - ((SeparableMenu) currentMenu).trimSeparator(); - } + currentMenu.trimSeparator(); if ((level > 0) && (currentMenu.getMenuComponentCount() > 0)) { // Insert this menu as a submenu of the previous one @@ -155,7 +153,6 @@ protected void register (int menuLevel, dynActions.put(action, action.tag); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // DynAction // //-----------// @@ -163,21 +160,23 @@ protected void register (int menuLevel, * Base implementation, to register the dynamic actions that need * to be updated according to the current glyph selection context. */ - protected abstract class DynAction + protected abstract static class DynAction extends AbstractAction { - //~ Instance fields ------------------------------------------------------------------------ /** Semantic tag */ protected final int tag; - //~ Constructors --------------------------------------------------------------------------- - public DynAction (int tag) + /** + * Create a DynAction instance. + * + * @param tag + */ + DynAction (int tag) { this.tag = tag; } - //~ Methods -------------------------------------------------------------------------------- /** * Report which item class should be used to the related menu item * @@ -192,5 +191,12 @@ public JMenuItem getMenuItem () * Method to update the action according to the current context */ public abstract void update (); + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } } diff --git a/src/main/org/audiveris/omr/glyph/ui/AbstractGlyphMenu.java b/src/main/org/audiveris/omr/glyph/ui/AbstractGlyphMenu.java index 048308c71..a24c154f9 100644 --- a/src/main/org/audiveris/omr/glyph/ui/AbstractGlyphMenu.java +++ b/src/main/org/audiveris/omr/glyph/ui/AbstractGlyphMenu.java @@ -38,11 +38,9 @@ */ public abstract class AbstractGlyphMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(AbstractGlyphMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Concrete menu. */ protected final SeparableMenu menu = new SeparableMenu(); @@ -61,7 +59,6 @@ public abstract class AbstractGlyphMenu /** To manage elaboration. */ protected boolean initDone = false; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AbstractGlyphMenu object. * @@ -76,7 +73,6 @@ public AbstractGlyphMenu (Sheet sheet, menu.setText(text); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getMenu // //---------// diff --git a/src/main/org/audiveris/omr/glyph/ui/EvaluationBoard.java b/src/main/org/audiveris/omr/glyph/ui/EvaluationBoard.java index a903559d5..887f57d4b 100644 --- a/src/main/org/audiveris/omr/glyph/ui/EvaluationBoard.java +++ b/src/main/org/audiveris/omr/glyph/ui/EvaluationBoard.java @@ -75,7 +75,6 @@ public class EvaluationBoard extends Board { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -90,7 +89,6 @@ public class EvaluationBoard /** Color for hardly recognized glyphs */ private static final Color EVAL_SOSO_COLOR = new Color(150, 150, 150); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying glyph classifier. */ protected final Classifier classifier; @@ -110,7 +108,6 @@ public class EvaluationBoard /** True for active buttons, false for passive fields. */ protected final boolean isActive; - //~ Constructors ------------------------------------------------------------------------------- //-----------------// // EvaluationBoard // //-----------------// @@ -151,7 +148,6 @@ public EvaluationBoard (boolean isActive, useAnnotations = false; //true; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // onEvent // //---------// @@ -238,9 +234,7 @@ private void defineLayout () String colSpec = Panel.makeColumns(3); FormLayout layout = new FormLayout(colSpec, ""); - int visibleButtons = Math.min( - constants.visibleButtons.getValue(), - selector.buttons.size()); + int visibleButtons = Math.min(constants.visibleButtons.getValue(), selector.buttons.size()); for (int i = 0; i < visibleButtons; i++) { if (i != 0) { @@ -261,18 +255,15 @@ private void defineLayout () } } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // Selector // //----------// protected class Selector { - //~ Instance fields ------------------------------------------------------------------------ // A collection of EvalButton's - final List buttons = new ArrayList(); + final List buttons = new ArrayList<>(); - //~ Constructors --------------------------------------------------------------------------- public Selector () { for (int i = 0; i < evalCount(); i++) { @@ -282,7 +273,6 @@ public Selector () setEvals(null, null); } - //~ Methods -------------------------------------------------------------------------------- //-----------// // evalCount // //-----------// @@ -362,10 +352,9 @@ private int positiveEvals (Evaluation[] evals) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Evaluation.Grade minGrade = new Evaluation.Grade( 0.0, @@ -383,7 +372,6 @@ private static final class Constants private class EvalButton implements ActionListener { - //~ Instance fields ------------------------------------------------------------------------ // Shape button or text field. Only one of them will be created and used final JButton button; @@ -393,7 +381,6 @@ private class EvalButton // The related grade JLabel grade = new JLabel("", SwingConstants.RIGHT); - //~ Constructors --------------------------------------------------------------------------- public EvalButton () { grade.setToolTipText("Grade of the evaluation"); @@ -413,7 +400,6 @@ public EvalButton () } } - //~ Methods -------------------------------------------------------------------------------- // Triggered by button @Override public void actionPerformed (ActionEvent e) diff --git a/src/main/org/audiveris/omr/glyph/ui/GlyphBoard.java b/src/main/org/audiveris/omr/glyph/ui/GlyphBoard.java index 232746474..73effed0b 100644 --- a/src/main/org/audiveris/omr/glyph/ui/GlyphBoard.java +++ b/src/main/org/audiveris/omr/glyph/ui/GlyphBoard.java @@ -49,18 +49,15 @@ public class GlyphBoard extends EntityBoard { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GlyphBoard.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The related glyph model. */ protected final GlyphsController controller; /** Output : group info. */ protected final JLabel groupField = new JLabel(); - //~ Constructors ------------------------------------------------------------------------------- /** * Basic constructor, to set common characteristics. * @@ -101,7 +98,6 @@ public GlyphBoard (EntityService glyphService, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // getFormLayout // //---------------// @@ -111,23 +107,6 @@ protected FormLayout getFormLayout () return Panel.makeFormLayout(3, 3); } - //--------------// - // defineLayout // - //--------------// - /** - * Define the layout for common fields of all GlyphBoard classes. - */ - private void defineLayout () - { - final CellConstraints cst = new CellConstraints(); - - int r = 1; // -------------------------------- - - builder.add(groupField, cst.xyw(5, r, 3)); - - r += 2; // -------------------------------- - } - //-----------------------// // handleEntityListEvent // //-----------------------// @@ -158,4 +137,19 @@ protected void handleEntityListEvent (EntityListEvent listEvent) groupField.setText(""); } } + + //--------------// + // defineLayout // + //--------------// + /** + * Define the layout for common fields of all GlyphBoard classes. + */ + private void defineLayout () + { + final CellConstraints cst = new CellConstraints(); + + int r = 1; // -------------------------------- + + builder.add(groupField, cst.xyw(5, r, 3)); + } } diff --git a/src/main/org/audiveris/omr/glyph/ui/GlyphService.java b/src/main/org/audiveris/omr/glyph/ui/GlyphService.java index 936bc3c30..5ca295d64 100644 --- a/src/main/org/audiveris/omr/glyph/ui/GlyphService.java +++ b/src/main/org/audiveris/omr/glyph/ui/GlyphService.java @@ -49,16 +49,14 @@ public class GlyphService extends EntityService { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GlyphService.class); /** Events that can be published on a glyph service. */ private static final Class[] eventsAllowed = new Class[]{ - IdEvent.class, EntityListEvent.class - }; + IdEvent.class, + EntityListEvent.class}; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code GlyphService} object. * @@ -71,7 +69,6 @@ public GlyphService (EntityIndex index, super(index, locationService, eventsAllowed); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // getMostRelevant // //-----------------// @@ -87,7 +84,7 @@ protected Glyph getMostRelevant (List list) default: - List copy = new ArrayList(list); + List copy = new ArrayList<>(list); Collections.sort(copy, Glyphs.byWeight); return copy.get(0); @@ -113,7 +110,7 @@ protected void handleLocationEvent (LocationEvent locationEvent) // Build compound on-the-fly and publish it (no impact on basket) Glyph compound = GlyphFactory.buildGlyph(basket); publish( - new EntityListEvent( + new EntityListEvent<>( this, SelectionHint.ENTITY_TRANSIENT, locationEvent.movement, diff --git a/src/main/org/audiveris/omr/glyph/ui/GlyphsController.java b/src/main/org/audiveris/omr/glyph/ui/GlyphsController.java index 9a16f7810..6f20df410 100644 --- a/src/main/org/audiveris/omr/glyph/ui/GlyphsController.java +++ b/src/main/org/audiveris/omr/glyph/ui/GlyphsController.java @@ -45,18 +45,15 @@ */ public class GlyphsController { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GlyphsController.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related model. */ protected final GlyphsModel model; /** Cached sheet, if any. */ protected final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an instance of GlyphsController, with its underlying GlyphsModel instance. * @@ -69,7 +66,6 @@ public GlyphsController (GlyphsModel model) sheet = model.getSheet(); } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // asyncAssignGlyphs // //-------------------// diff --git a/src/main/org/audiveris/omr/glyph/ui/NestView.java b/src/main/org/audiveris/omr/glyph/ui/NestView.java index 8c809d6a5..669d957fa 100644 --- a/src/main/org/audiveris/omr/glyph/ui/NestView.java +++ b/src/main/org/audiveris/omr/glyph/ui/NestView.java @@ -53,24 +53,21 @@ public class NestView extends EntityView { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(NestView.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The underlying glyph index */ protected final GlyphIndex glyphIndex; /** The sequence of lags. */ - protected final List lags = new ArrayList(); + protected final List lags = new ArrayList<>(); /** Related sheet, if any. */ @Navigable(false) private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a nest view. * @@ -92,7 +89,6 @@ public NestView (EntityService glyphService, setName("NestView"); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // render // //--------// @@ -100,7 +96,8 @@ public NestView (EntityService glyphService, public void render (Graphics2D g) { // Should we draw the section borders? - final boolean drawBorders = ViewParameters.getInstance().getSelectionMode() == SelectionMode.MODE_SECTION; + final boolean drawBorders = ViewParameters.getInstance() + .getSelectionMode() == SelectionMode.MODE_SECTION; // Stroke for borders final Stroke oldStroke = UIUtil.setAbsoluteStroke(g, 1f); @@ -207,8 +204,6 @@ protected void renderItems (Graphics2D g) } } - //~ Inner Classes ------------------------------------------------------------------------------ - // // //---------------------// // // renderGlyphSentence // // //---------------------// @@ -332,10 +327,9 @@ protected void renderItems (Graphics2D g) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean showSentenceBaseline = new Constant.Boolean( true, diff --git a/src/main/org/audiveris/omr/glyph/ui/RelationVector.java b/src/main/org/audiveris/omr/glyph/ui/RelationVector.java index 9f25fcb7a..982ccef5f 100644 --- a/src/main/org/audiveris/omr/glyph/ui/RelationVector.java +++ b/src/main/org/audiveris/omr/glyph/ui/RelationVector.java @@ -1,163 +1,159 @@ -//------------------------------------------------------------------------------------------------// -// // -// R e l a t i o n V e c t o r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.glyph.ui; - -import org.audiveris.omr.math.PointUtil; -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.inter.Inters; -import org.audiveris.omr.sig.relation.Relation; -import org.audiveris.omr.sig.relation.Relations; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Point; -import java.awt.geom.Line2D; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * Class {@code RelationVector} represents a dynamic vector from starting inter(s) to - * potential stopping inter(s), in order to finally set a relation between them. - * - * @author Hervé Bitteur - */ -public class RelationVector -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(RelationVector.class); - - //~ Instance fields ---------------------------------------------------------------------------- - /** Underling sheet. */ - private final Sheet sheet; - - /** Line from starting point to current stopping point. */ - final Line2D line; - - /** Starting inters, needed to initially create a vector. */ - private final List starts; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Create a useful vector. - * - * @param p1 starting point - * @param starts starting inters (cannot be null or empty) - */ - public RelationVector (Point p1, - List starts) - { - line = new Line2D.Double(p1, p1); - this.starts = starts; - sheet = starts.get(0).getSig().getSystem().getSheet(); - logger.debug("Created {}", this); - } - - //~ Methods ------------------------------------------------------------------------------------ - //----------// - // extendTo // - //----------// - /** - * Modify vector stopping point. - * - * @param pt new stopping point - */ - public void extendTo (Point pt) - { - line.setLine(line.getP1(), pt); - } - - //---------// - // process // - //---------// - /** - * Process the vector into a relation. - * - * @param doit (unused) true to actually set the relation, false for just a dry run - */ - public void process (boolean doit) - { - final Point p2 = PointUtil.rounded(line.getP2()); - final List stops = sheet.getInterIndex().getContainingEntities(p2); - - if (!stops.isEmpty()) { - stops.removeAll(starts); // No looping vector! - } - - if (stops.isEmpty()) { - return; - } - - Collections.sort(stops, Inters.membersFirst); - logger.debug("process starts:{} stops{}", starts, stops); - - for (Inter start : starts) { - for (Inter stop : stops) { - for (boolean reverse : new boolean[]{false, true}) { - final Inter source = reverse ? stop : start; - final Inter target = reverse ? start : stop; - final Set> suggestions; - suggestions = Relations.suggestedRelationsBetween(source, target); - - if (suggestions.isEmpty()) { - continue; - } - - logger.debug("src:{} tgt:{} suggestions:{}", source, target, suggestions); - - try { - SIGraph sig = source.getSig(); - Class relClass = suggestions.iterator().next(); - - // Allocate relation to be added - Relation relation = relClass.newInstance(); - relation.setManual(true); - - sheet.getInterController().link(sig, source, target, relation); - - return; - } catch (Exception ex) { - logger.warn("Error linking {}", ex.toString(), ex); - } - } - } - } - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("Vector{"); - sb.append("[").append(line.getX1()).append(",").append(line.getY1()).append("]"); - sb.append(" starts:").append(starts); - sb.append("}"); - - return sb.toString(); - } -} +//------------------------------------------------------------------------------------------------// +// // +// R e l a t i o n V e c t o r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.glyph.ui; + +import org.audiveris.omr.math.PointUtil; +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.inter.Inters; +import org.audiveris.omr.sig.relation.Relation; +import org.audiveris.omr.sig.relation.Relations; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Point; +import java.awt.geom.Line2D; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Class {@code RelationVector} represents a dynamic vector from starting inter(s) to + * potential stopping inter(s), in order to finally set a relation between them. + * + * @author Hervé Bitteur + */ +public class RelationVector +{ + + private static final Logger logger = LoggerFactory.getLogger(RelationVector.class); + + /** Underling sheet. */ + private final Sheet sheet; + + /** Line from starting point to current stopping point. */ + final Line2D line; + + /** Starting inters, needed to initially create a vector. */ + private final List starts; + + /** + * Create a useful vector. + * + * @param p1 starting point + * @param starts starting inters (cannot be null or empty) + */ + public RelationVector (Point p1, + List starts) + { + line = new Line2D.Double(p1, p1); + this.starts = starts; + sheet = starts.get(0).getSig().getSystem().getSheet(); + logger.debug("Created {}", this); + } + + //----------// + // extendTo // + //----------// + /** + * Modify vector stopping point. + * + * @param pt new stopping point + */ + public void extendTo (Point pt) + { + line.setLine(line.getP1(), pt); + } + + //---------// + // process // + //---------// + /** + * Process the vector into a relation. + * + * @param doit (unused) true to actually set the relation, false for just a dry run + */ + public void process (boolean doit) + { + final Point p2 = PointUtil.rounded(line.getP2()); + final List stops = sheet.getInterIndex().getContainingEntities(p2); + + if (!stops.isEmpty()) { + stops.removeAll(starts); // No looping vector! + } + + if (stops.isEmpty()) { + return; + } + + Collections.sort(stops, Inters.membersFirst); + logger.debug("process starts:{} stops{}", starts, stops); + + for (Inter start : starts) { + for (Inter stop : stops) { + for (boolean reverse : new boolean[]{false, true}) { + final Inter source = reverse ? stop : start; + final Inter target = reverse ? start : stop; + final Set> suggestions; + suggestions = Relations.suggestedRelationsBetween(source, target); + + if (suggestions.isEmpty()) { + continue; + } + + logger.debug("src:{} tgt:{} suggestions:{}", source, target, suggestions); + + try { + SIGraph sig = source.getSig(); + Class relClass = suggestions.iterator().next(); + + // Allocate relation to be added + Relation relation = relClass.newInstance(); + relation.setManual(true); + + sheet.getInterController().link(sig, source, target, relation); + + return; + } catch (Exception ex) { + logger.warn("Error linking {}", ex.toString(), ex); + } + } + } + } + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("Vector{"); + sb.append("[").append(line.getX1()).append(",").append(line.getY1()).append("]"); + sb.append(" starts:").append(starts); + sb.append("}"); + + return sb.toString(); + } +} diff --git a/src/main/org/audiveris/omr/glyph/ui/ShapeColorChooser.java b/src/main/org/audiveris/omr/glyph/ui/ShapeColorChooser.java index ca97e90c7..6c135cad3 100644 --- a/src/main/org/audiveris/omr/glyph/ui/ShapeColorChooser.java +++ b/src/main/org/audiveris/omr/glyph/ui/ShapeColorChooser.java @@ -72,13 +72,11 @@ public class ShapeColorChooser implements ChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ShapeColorChooser.class); private static JFrame frame; - //~ Instance fields ---------------------------------------------------------------------------- /** The classic color chooser utility */ private final JColorChooser colorChooser; @@ -94,7 +92,6 @@ public class ShapeColorChooser /** To select shape (within selected range) */ private final ShapesPane shapes; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an instance of ShapeColorChooser (should be improved to always * reuse the same instance. TODO) @@ -122,7 +119,6 @@ private ShapeColorChooser () component.add(colorChooser, BorderLayout.EAST); } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // showFrame // //-----------// @@ -164,20 +160,17 @@ public void stateChanged (ChangeEvent e) shapes.colorChanged(); } - //~ Inner Classes ------------------------------------------------------------------------------ //------// // Pane // //------// private abstract class Pane extends JPanel { - //~ Instance fields ------------------------------------------------------------------------ public JLabel banner = new JLabel("", JLabel.CENTER); public JPopupMenu menu = new JPopupMenu(); - //~ Constructors --------------------------------------------------------------------------- public Pane (String title) { setLayout(new BorderLayout()); @@ -190,7 +183,6 @@ public Pane (String title) add(banner, BorderLayout.CENTER); } - //~ Methods -------------------------------------------------------------------------------- public abstract void colorChanged (); protected abstract void refreshBanner (); @@ -202,7 +194,6 @@ public Pane (String title) private class RangesPane extends Pane { - //~ Instance fields ------------------------------------------------------------------------ public ShapeSet current; @@ -243,7 +234,6 @@ public void actionPerformed (ActionEvent e) private JButton pasteButton = new JButton(paste); - //~ Constructors --------------------------------------------------------------------------- public RangesPane () { super("Shape Range"); @@ -256,7 +246,6 @@ public RangesPane () buildRangesMenu(); } - //~ Methods -------------------------------------------------------------------------------- // When color chooser selection has been made @Override public void colorChanged () @@ -280,18 +269,15 @@ private void buildRangesMenu () ShapeSet.addAllShapeSets(menu, selectionListener); } - //~ Inner Classes -------------------------------------------------------------------------- private class PasteAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- public PasteAction () { super("Paste"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -308,14 +294,12 @@ public void actionPerformed (ActionEvent e) private class SelectAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- public SelectAction () { super("Select"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -331,7 +315,6 @@ public void actionPerformed (ActionEvent e) private class ShapesPane extends Pane { - //~ Instance fields ------------------------------------------------------------------------ public Shape current; @@ -370,7 +353,6 @@ public void actionPerformed (ActionEvent e) private boolean isSpecific; - //~ Constructors --------------------------------------------------------------------------- public ShapesPane () { super("Individual Shape"); @@ -393,7 +375,6 @@ public ShapesPane () paste.setEnabled(false); } - //~ Methods -------------------------------------------------------------------------------- // When color chooser selection has been made @Override public void colorChanged () @@ -463,18 +444,15 @@ private void updateActions () } } - //~ Inner Classes -------------------------------------------------------------------------- private class CopyAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- public CopyAction () { super("Copy"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -485,14 +463,12 @@ public void actionPerformed (ActionEvent e) private class CutAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- public CutAction () { super("Cut"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -509,14 +485,12 @@ public void actionPerformed (ActionEvent e) private class PasteAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- public PasteAction () { super("Paste"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -533,14 +507,12 @@ public void actionPerformed (ActionEvent e) private class SelectAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- public SelectAction () { super("Select"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { diff --git a/src/main/org/audiveris/omr/glyph/ui/ShapeFocusBoard.java b/src/main/org/audiveris/omr/glyph/ui/ShapeFocusBoard.java deleted file mode 100644 index 439891704..000000000 --- a/src/main/org/audiveris/omr/glyph/ui/ShapeFocusBoard.java +++ /dev/null @@ -1,490 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// S h a p e F o c u s B o a r d // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.glyph.ui; - -import com.jgoodies.forms.builder.PanelBuilder; -import com.jgoodies.forms.layout.CellConstraints; -import com.jgoodies.forms.layout.FormLayout; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.glyph.ShapeSet; -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.ui.Board; -import org.audiveris.omr.ui.field.SpinnerUtil; -import org.audiveris.omr.ui.selection.EntityListEvent; -import org.audiveris.omr.ui.selection.IdEvent; -import org.audiveris.omr.ui.selection.SelectionHint; -import org.audiveris.omr.ui.selection.UserEvent; -import org.audiveris.omr.ui.util.Panel; -import org.audiveris.omr.util.Navigable; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.Comparator; - -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.JSpinner; -import javax.swing.SpinnerListModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -/** - * Class {@code ShapeFocusBoard} handles a user iteration within a collection of glyphs. - * The collection may be built from glyphs of a given shape, or from glyphs similar to a given - * glyph, etc. - * - * @author Hervé Bitteur - */ -public class ShapeFocusBoard - extends Board -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger(ShapeFocusBoard.class); - - /** Events this board is interested in */ - private static final Class[] eventsRead = new Class[]{EntityListEvent.class}; - - //~ Enumerations ------------------------------------------------------------------------------- - /** Filter on which symbols should be displayed */ - private static enum Filter - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Display all symbols */ - ALL, - /** Display only known symbols */ - KNOWN, - /** Display only unknown symbols */ - UNKNOWN, - /** Display only translated - * symbols */ - TRANSLATED, - /** Display only untranslated - * symbols */ - UNTRANSLATED; - } - - //~ Instance fields ---------------------------------------------------------------------------- - @Navigable(false) - private final Sheet sheet; - - /** Browser on the collection of glyph instances */ - private final Browser browser = new Browser(); - - /** Button to select the shape focus */ - private JButton selectButton = new JButton(); - - /** Filter for known / unknown symbol display */ - private final JComboBox filterButton = new JComboBox(Filter.values()); - - /** Popup menu to allow shape selection */ - private JPopupMenu pm = new JPopupMenu(); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Create the instance to handle the shape focus, with pointers to - * needed companions. - * - * @param sheet the related sheet - * @param controller the related glyph controller - * @param filterListener the action linked to filter button - * @param selected true for pre-selected board - */ - public ShapeFocusBoard (Sheet sheet, - GlyphsController controller, - ActionListener filterListener, - boolean selected) - { - super(Board.FOCUS, controller.getGlyphService(), eventsRead, selected, false, false, false); - - this.sheet = sheet; - - // Tool Tips - selectButton.setToolTipText("Select candidate shape"); - selectButton.setHorizontalAlignment(SwingConstants.LEFT); - selectButton.addActionListener( - new ActionListener() - { - @Override - public void actionPerformed (ActionEvent e) - { - pm.show(selectButton, selectButton.getX(), selectButton.getY()); - } - }); - - // Filter - filterButton.addActionListener(filterListener); - filterButton.setToolTipText("Select displayed glyphs according to their current state"); - - // Popup menu for shape selection - JMenuItem noFocus = new JMenuItem("No Focus"); - noFocus.setToolTipText("Cancel any focus"); - noFocus.addActionListener( - new ActionListener() - { - @Override - public void actionPerformed (ActionEvent e) - { - setCurrentShape(null); - } - }); - pm.add(noFocus); - ShapeSet.addAllShapes( - pm, - new ActionListener() - { - @Override - public void actionPerformed (ActionEvent e) - { - JMenuItem source = (JMenuItem) e.getSource(); - setCurrentShape(Shape.valueOf(source.getText())); - } - }); - - defineLayout(); - - // Initially, no focus - setCurrentShape(null); - } - - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // isDisplayed // - //-------------// - /** - * Report whether the glyph at hand is to be displayed, according to - * the current filter - * - * @param glyph the glyph at hand, perhaps null - * @return true if to be displayed - */ - public boolean isDisplayed (Glyph glyph) - { - // switch ((Filter) filterButton.getSelectedItem()) { - // case KNOWN: - // return (glyph != null) && glyph.isKnown(); - // - // case UNKNOWN: - // return (glyph == null) || !glyph.isKnown(); - // - // default: - // case ALL: - return true; - - // } - } - - //---------// - // onEvent // - //---------// - /** - * Notification about selection objects. - * We used to use it on a just modified glyph, to set the new shape focus - * But this conflicts with the ability to browse a collection of similar - * glyphs and assign them on the fly - * - * @param event the notified event - */ - @Override - public void onEvent (UserEvent event) - { - // Empty - } - - //-----------------// - // setCurrentShape // - //-----------------// - /** - * Define the new current shape. - * - * @param currentShape the shape to be considered as current - */ - public void setCurrentShape (Shape currentShape) - { - throw new RuntimeException("HB. Not implemented yet"); - - // browser.resetIds(); - // - // if (currentShape != null) { - // // Update the shape button - // selectButton.setText(currentShape.toString()); - // selectButton.setIcon(currentShape.getDecoratedSymbol()); - // - // // Count the number of glyphs assigned to current shape - // for (GlyphLayer layer : GlyphLayer.concreteValues()) { - // for (Glyph glyph : sheet.getGlyphNest().getGlyphs(layer)) { - // if (glyph.getShape() == currentShape) { - // browser.addId(glyph.getId()); - // } - // } - // } - // - // setSelected(true); - // setVisible(true); - // } else { - // // Void the shape button - // selectButton.setText("- No Focus -"); - // selectButton.setIcon(null); - // } - // - // browser.refresh(); - } - - //-----------------// - // setSimilarGlyph // - //-----------------// - /** - * Define the glyphs collection as all glyphs whose physical - * appearance is "similar" to the appearance of the provided glyph - * example. - * - * @param example the provided example - */ - public void setSimilarGlyph (Glyph example) - { - throw new RuntimeException("HB. Not implemented yet"); - - // browser.resetIds(); - // - // if (example != null) { - // LinearClassifier evaluator = LinearClassifier.getInstance(); - // double[] pattern = ShapeDescription.features(example); - // List pairs = new ArrayList(); - // - // // Retrieve the glyphs similar to the example - // for (GlyphLayer layer : GlyphLayer.concreteValues()) { - // for (Glyph glyph : sheet.getGlyphNest().getGlyphs(layer)) { - // double dist = evaluator.measureDistance(glyph, pattern); - // pairs.add(new DistIdPair(dist, glyph.getId())); - // } - // } - // - // Collections.sort(pairs, DistIdPair.distComparator); - // - // for (DistIdPair pair : pairs) { - // browser.addId(pair.id); - // } - // - // // To get a detailed table of the distances (debugging) - // if (constants.printDistances.getValue()) { - // Printer printer = evaluator.getEngine().new Printer(11); - // String indent = " "; - // System.out.println(indent + printer.getDefaults()); - // System.out.println(indent + printer.getNames()); - // System.out.println(indent + printer.getDashes()); - // - // for (DistIdPair pair : pairs) { - // Glyph glyph = sheet.getGlyphNest().getEntity(pair.id); - // double[] gPat = ShapeDescription.features(glyph); - // Shape shape = glyph.getShape(); - // System.out.printf("%18s", (shape != null) ? shape.toString() : ""); - // System.out.println(printer.getDeltas(gPat, pattern)); - // System.out.printf("g#%04d d:%9f", pair.id, pair.dist); - // System.out.println(printer.getWeightedDeltas(gPat, pattern)); - // } - // } - // - // // Update the shape button - // selectButton.setText("Glyphs similar to #" + example.getId()); - // selectButton.setIcon(null); - // - // setSelected(true); - // setVisible(true); - // } else { - // // Void the shape button - // selectButton.setText("- No Focus -"); - // selectButton.setIcon(null); - // } - // - // browser.refresh(); - } - - //--------------// - // defineLayout // - //--------------// - private void defineLayout () - { - final String fieldInterline = Panel.getFieldInterline(); - - String colSpec = Panel.makeColumns(3); - FormLayout layout = new FormLayout(colSpec, "pref," + fieldInterline + "," + "pref"); - - PanelBuilder builder = new PanelBuilder(layout, getBody()); - - ///builder.setDefaultDialogBorder(); - CellConstraints cst = new CellConstraints(); - - int r = 1; // -------------------------------- - builder.add(browser.count, cst.xy(1, r)); - builder.add(browser.spinner, cst.xy(3, r)); - builder.add(selectButton, cst.xywh(7, r, 5, 3)); - - r += 2; // -------------------------------- - builder.add(filterButton, cst.xyw(1, r, 3)); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //------------// - // DistIdPair // - //------------// - /** - * Needed to sort glyphs id according to their distance - */ - private static class DistIdPair - { - //~ Static fields/initializers ------------------------------------------------------------- - - private static final Comparator distComparator = new Comparator() - { - @Override - public int compare (DistIdPair o1, - DistIdPair o2) - { - return Double.compare(o1.dist, o2.dist); - } - }; - - //~ Instance fields ------------------------------------------------------------------------ - final double dist; - - final int id; - - //~ Constructors --------------------------------------------------------------------------- - public DistIdPair (double dist, - int id) - { - this.dist = dist; - this.id = id; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return "dist:" + dist + " glyph#" + id; - } - } - - //---------// - // Browser // - //---------// - private class Browser - implements ChangeListener - { - //~ Instance fields ------------------------------------------------------------------------ - - // Spinner on these glyphs - ArrayList ids = new ArrayList(); - - // Number of glyphs - JLabel count = new JLabel("", SwingConstants.RIGHT); - - JSpinner spinner = new JSpinner(new SpinnerListModel()); - - //~ Constructors --------------------------------------------------------------------------- - public Browser () - { - resetIds(); - spinner.addChangeListener(this); - SpinnerUtil.setList(spinner, ids); - refresh(); - } - - //~ Methods -------------------------------------------------------------------------------- - //-------// - // addId // - //-------// - public void addId (String id) - { - ids.add(id); - } - - //---------// - // refresh // - //---------// - public void refresh () - { - if (ids.size() > 1) { // To skip first NO_VALUE item - count.setText(0 + "/" + (ids.size() - 1)); - spinner.setEnabled(true); - } else { - count.setText(""); - spinner.setEnabled(false); - } - - spinner.setValue(null); - } - - //----------// - // resetIds // - //----------// - public void resetIds () - { - ids.clear(); - } - - //--------------// - // stateChanged // - //--------------// - @Override - public void stateChanged (ChangeEvent e) - { - Integer id = (Integer) spinner.getValue(); - - int index = ids.indexOf(id); - count.setText(index + "/" + (ids.size() - 1)); - - if (id != null) { - getSelectionService().publish( - new IdEvent(this, SelectionHint.ENTITY_INIT, null, id)); - } - } - } - - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean printDistances = new Constant.Boolean( - false, - "Should we print out distance details when looking for similar glyphs?"); - } -} diff --git a/src/main/org/audiveris/omr/glyph/ui/SpinnerGlyphModel.java b/src/main/org/audiveris/omr/glyph/ui/SpinnerGlyphModel.java index a0eaa0071..4783ef46a 100644 --- a/src/main/org/audiveris/omr/glyph/ui/SpinnerGlyphModel.java +++ b/src/main/org/audiveris/omr/glyph/ui/SpinnerGlyphModel.java @@ -45,11 +45,9 @@ public class SpinnerGlyphModel extends AbstractSpinnerModel { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SpinnerGlyphModel.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying glyph nest */ private final GlyphIndex nest; @@ -59,7 +57,6 @@ public class SpinnerGlyphModel /** Current glyph id */ private int currentId; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SpinnerGlyphModel object, on all nest glyph instances. * @@ -87,7 +84,6 @@ public SpinnerGlyphModel (GlyphIndex nest, this.predicate = predicate; } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // getNextValue // //--------------// diff --git a/src/main/org/audiveris/omr/glyph/ui/SymbolGlyphBoard.java b/src/main/org/audiveris/omr/glyph/ui/SymbolGlyphBoard.java index 7196a10ae..49c70afcc 100644 --- a/src/main/org/audiveris/omr/glyph/ui/SymbolGlyphBoard.java +++ b/src/main/org/audiveris/omr/glyph/ui/SymbolGlyphBoard.java @@ -41,11 +41,9 @@ public class SymbolGlyphBoard extends GlyphBoard { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SymbolGlyphBoard.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Glyph characteristics : normalized weight. */ private final LDoubleField weight = new LDoubleField( false, @@ -63,7 +61,6 @@ public class SymbolGlyphBoard "Normalized height", "%.3f"); - //~ Constructors ------------------------------------------------------------------------------- /** * Create the symbol glyph board. * @@ -86,33 +83,6 @@ public SymbolGlyphBoard (GlyphsController glyphsController, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ - //--------------// - // defineLayout // - //--------------// - /** - * Define a specific layout for this Symbol GlyphBoard. - */ - private void defineLayout () - { - final CellConstraints cst = new CellConstraints(); - - int r = 1; // -------------------------------- - // id + width - - builder.add(width.getLabel(), cst.xy(9, r)); - builder.add(width.getField(), cst.xy(11, r)); - - r += 2; // -------------------------------- - // weight + height - - builder.add(weight.getLabel(), cst.xy(5, r)); - builder.add(weight.getField(), cst.xy(7, r)); - - builder.add(height.getLabel(), cst.xy(9, r)); - builder.add(height.getField(), cst.xy(11, r)); - } - //-----------------------// // handleEntityListEvent // //-----------------------// @@ -144,4 +114,30 @@ protected void handleEntityListEvent (EntityListEvent listEvent) height.setEnabled(glyph != null); weight.setEnabled(glyph != null); } + + //--------------// + // defineLayout // + //--------------// + /** + * Define a specific layout for this Symbol GlyphBoard. + */ + private void defineLayout () + { + final CellConstraints cst = new CellConstraints(); + + int r = 1; // -------------------------------- + // id + width + + builder.add(width.getLabel(), cst.xy(9, r)); + builder.add(width.getField(), cst.xy(11, r)); + + r += 2; // -------------------------------- + // weight + height + + builder.add(weight.getLabel(), cst.xy(5, r)); + builder.add(weight.getField(), cst.xy(7, r)); + + builder.add(height.getLabel(), cst.xy(9, r)); + builder.add(height.getField(), cst.xy(11, r)); + } } diff --git a/src/main/org/audiveris/omr/glyph/ui/SymbolsEditor.java b/src/main/org/audiveris/omr/glyph/ui/SymbolsEditor.java index 6515f174a..b32c87093 100644 --- a/src/main/org/audiveris/omr/glyph/ui/SymbolsEditor.java +++ b/src/main/org/audiveris/omr/glyph/ui/SymbolsEditor.java @@ -42,6 +42,7 @@ import org.audiveris.omr.sheet.rhythm.Slot; import org.audiveris.omr.sheet.ui.PixelBoard; import org.audiveris.omr.sheet.ui.SelectionPainter; +import org.audiveris.omr.sheet.ui.SheetAssembly; import org.audiveris.omr.sheet.ui.SheetGradedPainter; import org.audiveris.omr.sheet.ui.SheetResultPainter; import org.audiveris.omr.sheet.ui.SheetTab; @@ -57,7 +58,6 @@ import org.audiveris.omr.ui.Board; import org.audiveris.omr.ui.BoardsPane; import org.audiveris.omr.ui.Colors; -import org.audiveris.omr.ui.PixelCount; import org.audiveris.omr.ui.ViewParameters; import org.audiveris.omr.ui.ViewParameters.SelectionMode; import org.audiveris.omr.ui.selection.EntityListEvent; @@ -98,13 +98,11 @@ public class SymbolsEditor implements PropertyChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(SymbolsEditor.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ @Navigable(false) private final Sheet sheet; @@ -120,7 +118,6 @@ public class SymbolsEditor /** View parameters. */ private final ViewParameters viewParams = ViewParameters.getInstance(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create the DATA_TAB view in the sheet assembly tabs, dedicated to the display and * handling of glyphs and inters. @@ -140,7 +137,7 @@ public SymbolsEditor (Sheet sheet, view = new MyView(sheet.getGlyphIndex()); view.setLocationService(sheet.getLocationService()); - List boards = new ArrayList(); + List boards = new ArrayList<>(); boards.add(new PixelBoard(sheet, constants.selectPixelBoard.isSet())); Lag hLag = sheet.getLagManager().getLag(Lags.HLAG); @@ -206,11 +203,11 @@ public SymbolsEditor (Sheet sheet, BoardsPane boardsPane = new BoardsPane(boards); // Create a hosting pane for the view - ScrollView slv = new ScrollView(view); - sheet.getStub().getAssembly().addViewTab(SheetTab.DATA_TAB, slv, boardsPane); + final SheetAssembly assembly = sheet.getStub().getAssembly(); + assembly.addViewTab(SheetTab.DATA_TAB, new ScrollView(view), boardsPane); + assembly.lockViewTab(SheetTab.DATA_TAB); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // getShapeBoard // //---------------// @@ -307,8 +304,7 @@ public NestView getView () */ public void highLight (final Slot slot) { - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () @@ -339,18 +335,12 @@ public void refresh () view.repaint(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ - - private final PixelCount measureMargin = new PixelCount( - 10, - "Number of pixels as margin when highlighting a measure"); private final Constant.Boolean selectPixelBoard = new Constant.Boolean( false, @@ -379,10 +369,6 @@ private static final class Constants private final Constant.Boolean selectBasicClassifierBoard = new Constant.Boolean( true, "Should we select Basic Classifier board by default?"); - - private final Constant.Boolean selectDeepClassifierBoard = new Constant.Boolean( - false, - "Should we select Deep Classifier board by default?"); } //--------// @@ -391,7 +377,6 @@ private static final class Constants private final class MyView extends NestView { - //~ Instance fields ------------------------------------------------------------------------ /** Currently highlighted slot, if any. */ private Slot highlightedSlot; @@ -399,7 +384,6 @@ private final class MyView /** Current vector. */ private RelationVector vector; - //~ Constructors --------------------------------------------------------------------------- private MyView (GlyphIndex glyphIndex) { super( @@ -418,7 +402,6 @@ private MyView (GlyphIndex glyphIndex) sheet.getInterIndex().getEntityService().subscribeStrongly(EntityListEvent.class, this); } - //~ Methods -------------------------------------------------------------------------------- //--------------// // contextAdded // //--------------// @@ -596,7 +579,8 @@ public void render (Graphics2D g) if (viewParams.isInputPainting()) { // Sections - final boolean drawBorders = viewParams.getSelectionMode() == SelectionMode.MODE_SECTION; + final boolean drawBorders = viewParams + .getSelectionMode() == SelectionMode.MODE_SECTION; final Stroke oldStroke = (drawBorders) ? UIUtil.setAbsoluteStroke(g, 1f) : null; for (Lag lag : lags) { diff --git a/src/main/org/audiveris/omr/image/AbstractGrayFilter.java b/src/main/org/audiveris/omr/image/AbstractGrayFilter.java index d7c274696..553586a8b 100644 --- a/src/main/org/audiveris/omr/image/AbstractGrayFilter.java +++ b/src/main/org/audiveris/omr/image/AbstractGrayFilter.java @@ -35,7 +35,6 @@ */ public abstract class AbstractGrayFilter { - //~ Methods ------------------------------------------------------------------------------------ //--------// // filter // diff --git a/src/main/org/audiveris/omr/image/AdaptiveDescriptor.java b/src/main/org/audiveris/omr/image/AdaptiveDescriptor.java index f3d24da92..e2ed6b045 100644 --- a/src/main/org/audiveris/omr/image/AdaptiveDescriptor.java +++ b/src/main/org/audiveris/omr/image/AdaptiveDescriptor.java @@ -44,15 +44,11 @@ public class AdaptiveDescriptor extends FilterDescriptor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - AdaptiveDescriptor.class); + private static final Logger logger = LoggerFactory.getLogger(AdaptiveDescriptor.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Coefficient for mean. */ @XmlAttribute(name = "mean-coeff") public final double meanCoeff; @@ -61,7 +57,6 @@ public class AdaptiveDescriptor @XmlAttribute(name = "std-dev-coeff") public final double stdDevCoeff; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AdaptiveDescriptor object. * @@ -82,57 +77,6 @@ private AdaptiveDescriptor () stdDevCoeff = 0; } - //~ Methods ------------------------------------------------------------------------------------ - //-------------------// - // defaultIsSpecific // - //-------------------// - public static boolean defaultIsSpecific () - { - return !constants.meanCoeff.isSourceValue() || !constants.stdDevCoeff.isSourceValue(); - } - - //------------// - // getDefault // - //------------// - public static AdaptiveDescriptor getDefault () - { - return new AdaptiveDescriptor(getDefaultMeanCoeff(), getDefaultStdDevCoeff()); - } - - //---------------------// - // getDefaultMeanCoeff // - //---------------------// - public static double getDefaultMeanCoeff () - { - return constants.meanCoeff.getValue(); - } - - //-----------------------// - // getDefaultStdDevCoeff // - //-----------------------// - public static double getDefaultStdDevCoeff () - { - return constants.stdDevCoeff.getValue(); - } - - //----------------// - // getSourceValue // - //----------------// - public static AdaptiveDescriptor getSourceValue () - { - return new AdaptiveDescriptor( - constants.meanCoeff.getSourceValue(), - constants.stdDevCoeff.getSourceValue()); - } - - //---------------------// - // setDefaultMeanCoeff // - //---------------------// - public static void setDefaultMeanCoeff (double meanCoeff) - { - constants.meanCoeff.setValue(meanCoeff); - } - //-----------// // getFilter // //-----------// @@ -159,33 +103,14 @@ public FilterKind getKind () public int hashCode () { int hash = 5; - hash = (97 * hash) - + (int) (Double.doubleToLongBits(this.meanCoeff) - ^ (Double.doubleToLongBits(this.meanCoeff) >>> 32)); - hash = (97 * hash) - + (int) (Double.doubleToLongBits(this.stdDevCoeff) - ^ (Double.doubleToLongBits(this.stdDevCoeff) >>> 32)); + hash = (97 * hash) + (int) (Double.doubleToLongBits(this.meanCoeff) ^ (Double + .doubleToLongBits(this.meanCoeff) >>> 32)); + hash = (97 * hash) + (int) (Double.doubleToLongBits(this.stdDevCoeff) ^ (Double + .doubleToLongBits(this.stdDevCoeff) >>> 32)); return hash; } - //---------------// - // resetToSource // - //---------------// - public static void resetToSource () - { - constants.meanCoeff.resetToSource(); - constants.stdDevCoeff.resetToSource(); - } - - //-----------------------// - // setDefaultStdDevCoeff // - //-----------------------// - public static void setDefaultStdDevCoeff (double stdDevCoeff) - { - constants.stdDevCoeff.setValue(stdDevCoeff); - } - //--------// // equals // //--------// @@ -196,8 +121,8 @@ public boolean equals (Object obj) AdaptiveDescriptor that = (AdaptiveDescriptor) obj; final double epsilon = 0.00001; - return (Math.abs(this.meanCoeff - that.meanCoeff) < epsilon) - && (Math.abs(this.stdDevCoeff - that.stdDevCoeff) < epsilon); + return (Math.abs(this.meanCoeff - that.meanCoeff) < epsilon) && (Math.abs( + this.stdDevCoeff - that.stdDevCoeff) < epsilon); } return false; @@ -216,14 +141,79 @@ protected String internalsString () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------------// + // defaultIsSpecific // + //-------------------// + public static boolean defaultIsSpecific () + { + return !constants.meanCoeff.isSourceValue() || !constants.stdDevCoeff.isSourceValue(); + } + + //------------// + // getDefault // + //------------// + public static AdaptiveDescriptor getDefault () + { + return new AdaptiveDescriptor(getDefaultMeanCoeff(), getDefaultStdDevCoeff()); + } + + //---------------------// + // getDefaultMeanCoeff // + //---------------------// + public static double getDefaultMeanCoeff () + { + return constants.meanCoeff.getValue(); + } + + //---------------------// + // setDefaultMeanCoeff // + //---------------------// + public static void setDefaultMeanCoeff (double meanCoeff) + { + constants.meanCoeff.setValue(meanCoeff); + } + + //-----------------------// + // getDefaultStdDevCoeff // + //-----------------------// + public static double getDefaultStdDevCoeff () + { + return constants.stdDevCoeff.getValue(); + } + + //-----------------------// + // setDefaultStdDevCoeff // + //-----------------------// + public static void setDefaultStdDevCoeff (double stdDevCoeff) + { + constants.stdDevCoeff.setValue(stdDevCoeff); + } + + //----------------// + // getSourceValue // + //----------------// + public static AdaptiveDescriptor getSourceValue () + { + return new AdaptiveDescriptor( + constants.meanCoeff.getSourceValue(), + constants.stdDevCoeff.getSourceValue()); + } + + //---------------// + // resetToSource // + //---------------// + public static void resetToSource () + { + constants.meanCoeff.resetToSource(); + constants.stdDevCoeff.resetToSource(); + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio meanCoeff = new Constant.Ratio( 0.7, diff --git a/src/main/org/audiveris/omr/image/AdaptiveFilter.java b/src/main/org/audiveris/omr/image/AdaptiveFilter.java index 1a8eeb47d..6c01c1acd 100644 --- a/src/main/org/audiveris/omr/image/AdaptiveFilter.java +++ b/src/main/org/audiveris/omr/image/AdaptiveFilter.java @@ -36,18 +36,18 @@ * Class {@code AdaptiveFilter} is an abstract implementation of {@code PixelFilter} * which provides foreground information based on mean value and standard deviation in * pixel neighborhood. - *

                - * See work of Sauvola et al. - * here. + *
                + * See work of + * Sauvola et al. *

                * The mean value and the standard deviation value are provided thanks to underlying integrals * {@link Tile} instances. * The precise tile size and behavior is the responsibility of subclasses of this class. - *

                - * See work of Shafait et al. - * here. + *
                + * See work of + * Shafait et al. + * *

                  * 0---------------------------------------------+---------------+
                  * |                                             |               |
                @@ -63,13 +63,14 @@
                  * |                                            c|              d|
                  * +---------------------------------------------+---------------+
                  * 
                + * * Key table features: *
                  *
                • Assumption: The integral of any rectangle with origin at (0,0) is stored * in the bottom right cell of the rectangle.
                • - *
                • As a consequence the integral of any rectangle, whatever its origin, can be simply computed - * as: a + d - b - c
                • - *
                • In particular if lower right rectangle is reduced to a single cell, then + *
                • As a consequence, the integral of any rectangle, whatever its origin, can be simply computed + * as: a + d - b - c
                • + *
                • In particular, if lower right rectangle is reduced to a single cell, then * d = pixel_value + top + left - topLeft *
                  * This property is used to incrementally populate the table.
                • @@ -82,14 +83,11 @@ public abstract class AdaptiveFilter extends SourceWrapper implements PixelFilter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(AdaptiveFilter.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Default value for (half of) window size. */ protected final int HALF_WINDOW_SIZE = constants.halfWindowSize.getValue(); @@ -105,7 +103,6 @@ public abstract class AdaptiveFilter /** Table for integrals of squared values. */ protected Tile sqrTile; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an adaptive wrapper on a pixel source. * @@ -123,7 +120,6 @@ public AdaptiveFilter (ByteProcessor source, this.STD_DEV_COEFF = stdDevCoeff; } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // filteredImage // //---------------// @@ -181,7 +177,6 @@ public Context getContext (int x, } } - // // -------// // isFore // // -------// @@ -212,33 +207,6 @@ private double getThreshold (double mean, return (MEAN_COEFF * mean) + (STD_DEV_COEFF * stdDev); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------------// - // AdaptiveContext // - //-----------------// - public static class AdaptiveContext - extends Context - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Mean pixel value in the neighborhood. */ - public final double mean; - - /** Standard deviation of pixel values in the neighborhood. */ - public final double standardDeviation; - - //~ Constructors --------------------------------------------------------------------------- - public AdaptiveContext (double mean, - double standardDeviation, - double threshold) - { - super(threshold); - this.mean = mean; - this.standardDeviation = standardDeviation; - } - } - - // //------// // Tile // //------// @@ -247,7 +215,6 @@ public AdaptiveContext (double mean, */ protected class Tile { - //~ Instance fields ------------------------------------------------------------------------ /** Width of the tile circular buffer. */ protected final int TILE_WIDTH; @@ -264,7 +231,6 @@ protected class Tile /** Circular buffer for integrals. */ protected final long[][] sums; - //~ Constructors --------------------------------------------------------------------------- /** * Create a tile instance. * @@ -272,9 +238,9 @@ protected class Tile * @param height tile height = image height * @param squared true for squared values, false for plain values */ - public Tile (int tileWidth, - int height, - boolean squared) + Tile (int tileWidth, + int height, + boolean squared) { this.TILE_WIDTH = tileWidth; this.height = height; @@ -287,7 +253,6 @@ public Tile (int tileWidth, Arrays.fill(sums[TILE_WIDTH - 1], 0); } - //~ Methods -------------------------------------------------------------------------------- /** * Make sure that the sliding window is positioned around the * provided location, and return mean data. @@ -381,13 +346,45 @@ protected void shiftTile (int x2) } } + //-----------------// + // AdaptiveContext // + //-----------------// + /** + * + */ + public static class AdaptiveContext + extends Context + { + + /** Mean pixel value in the neighborhood. */ + public final double mean; + + /** Standard deviation of pixel values in the neighborhood. */ + public final double standardDeviation; + + /** + * Create an AdaptiveContext object. + * + * @param mean mean pixel value in neighborhood + * @param standardDeviation standard deviation of pixel value in neighborhood + * @param threshold threshold to apply on pixel value + */ + public AdaptiveContext (double mean, + double standardDeviation, + double threshold) + { + super(threshold); + this.mean = mean; + this.standardDeviation = standardDeviation; + } + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer halfWindowSize = new Constant.Integer( "Pixels", diff --git a/src/main/org/audiveris/omr/image/Anchored.java b/src/main/org/audiveris/omr/image/Anchored.java index 4618b0554..dbe57533a 100644 --- a/src/main/org/audiveris/omr/image/Anchored.java +++ b/src/main/org/audiveris/omr/image/Anchored.java @@ -31,87 +31,65 @@ */ public interface Anchored { - //~ Enumerations ------------------------------------------------------------------------------- /** Specifies a reference relative location. */ public enum Anchor { - //~ Enumeration constant initializers ------------------------------------------------------ - /** * Area Center. */ - CENTER, + CENTER("C"), /** * X at symbol left abscissa, Y at middle. */ - MIDDLE_LEFT, + MIDDLE_LEFT("ML"), /** * X at symbol right abscissa, Y at middle. */ - MIDDLE_RIGHT, + MIDDLE_RIGHT("MR"), /** * X at symbol left stem, Y at high stem ordinate. */ - TOP_LEFT_STEM, + TOP_LEFT_STEM("TLS"), /** * X at symbol left stem, Y at middle. */ - LEFT_STEM, + LEFT_STEM("LS"), /** * X at symbol left stem, Y at low stem ordinate. */ - BOTTOM_LEFT_STEM, + BOTTOM_LEFT_STEM("BLS"), /** * X at symbol right stem, Y at high stem ordinate. */ - TOP_RIGHT_STEM, + TOP_RIGHT_STEM("TRS"), /** * X at symbol right stem, Y at middle. */ - RIGHT_STEM, + RIGHT_STEM("RS"), /** * X at symbol right stem, Y at low stem ordinate. */ - BOTTOM_RIGHT_STEM; - //~ Methods -------------------------------------------------------------------------------- - - public String abbreviation () - { - switch (this) { - case CENTER: - return "C"; - - case MIDDLE_LEFT: - return "ML"; - - case MIDDLE_RIGHT: - return "MR"; - - case TOP_LEFT_STEM: - return "TLS"; - - case LEFT_STEM: - return "LS"; + BOTTOM_RIGHT_STEM("BRS"); - case BOTTOM_LEFT_STEM: - return "BLS"; + final String abbreviation; - case TOP_RIGHT_STEM: - return "TRS"; - - case RIGHT_STEM: - return "RS"; - - case BOTTOM_RIGHT_STEM: - return "BRS"; - } + Anchor (String a) + { + abbreviation = a; + } - return null; + /** + * Report the abbreviation based on anchor + * + * @return anchor abbreviation + */ + public String abbreviation () + { + return abbreviation; } } - //~ Methods ------------------------------------------------------------------------------------ /** * Assign a relative offset for an anchor type. * diff --git a/src/main/org/audiveris/omr/image/AnchoredTemplate.java b/src/main/org/audiveris/omr/image/AnchoredTemplate.java index 2c5576ee4..c7a5d1b4e 100644 --- a/src/main/org/audiveris/omr/image/AnchoredTemplate.java +++ b/src/main/org/audiveris/omr/image/AnchoredTemplate.java @@ -30,13 +30,13 @@ */ public class AnchoredTemplate { - //~ Instance fields ---------------------------------------------------------------------------- + /** Specific anchor. */ public final Anchor anchor; + /** Related template. */ public final Template template; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code AnchoredTemplate} object. * @@ -50,7 +50,6 @@ public AnchoredTemplate (Anchor anchor, this.template = template; } - //~ Methods ------------------------------------------------------------------------------------ @Override public String toString () { diff --git a/src/main/org/audiveris/omr/image/AreaMask.java b/src/main/org/audiveris/omr/image/AreaMask.java index 6a109eeba..582a59f16 100644 --- a/src/main/org/audiveris/omr/image/AreaMask.java +++ b/src/main/org/audiveris/omr/image/AreaMask.java @@ -35,14 +35,12 @@ */ public class AreaMask { - //~ Instance fields ---------------------------------------------------------------------------- /** Mask area. */ private final Area area; private final Rectangle rect; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AreaMask object. * @@ -54,7 +52,6 @@ public AreaMask (Area area) rect = area.getBounds(); } - //~ Methods ------------------------------------------------------------------------------------ //-------// // apply // //-------// @@ -99,13 +96,14 @@ public int fore (final Wrapper fore, return apply(new ForeCounter(filter, fore)); } - //~ Inner Interfaces --------------------------------------------------------------------------- //---------// // Adapter // //---------// + /** + * + */ public static interface Adapter { - //~ Methods -------------------------------------------------------------------------------- /** * Method called on each mask relevant point. @@ -117,14 +115,12 @@ public void process (int x, int y); } - //~ Inner Classes ------------------------------------------------------------------------------ //-------------// // ForeCounter // //-------------// private static class ForeCounter implements Adapter { - //~ Instance fields ------------------------------------------------------------------------ private final ByteProcessor filter; @@ -134,9 +130,8 @@ private static class ForeCounter private final Wrapper fore; - //~ Constructors --------------------------------------------------------------------------- - public ForeCounter (ByteProcessor filter, - Wrapper fore) + ForeCounter (ByteProcessor filter, + Wrapper fore) { this.filter = filter; this.fore = fore; @@ -144,16 +139,15 @@ public ForeCounter (ByteProcessor filter, filterHeight = filter.getHeight(); } - //~ Methods -------------------------------------------------------------------------------- @Override public void process (int x, int y) { if ((x >= 0) - && (x < filterWidth) - && (y >= 0) - && (y < filterHeight) - && (filter.get(x, y) == 0)) { + && (x < filterWidth) + && (y >= 0) + && (y < filterHeight) + && (filter.get(x, y) == 0)) { fore.value++; } } diff --git a/src/main/org/audiveris/omr/image/BufferedSink.java b/src/main/org/audiveris/omr/image/BufferedSink.java index 81a118682..694a0c053 100644 --- a/src/main/org/audiveris/omr/image/BufferedSink.java +++ b/src/main/org/audiveris/omr/image/BufferedSink.java @@ -32,7 +32,6 @@ public class BufferedSink implements PixelSink { - //~ Instance fields ---------------------------------------------------------------------------- /** The wrapped BufferedImage instance. */ private final BufferedImage image; @@ -43,7 +42,6 @@ public class BufferedSink /** Buffer to write pixel value. */ private final int[] pixelArray; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BufferedSink object around a given BufferedImage * instance. @@ -57,7 +55,6 @@ public BufferedSink (BufferedImage image) pixelArray = new int[4]; } - //~ Methods ------------------------------------------------------------------------------------ @Override public int getHeight () { diff --git a/src/main/org/audiveris/omr/image/BufferedSource.java b/src/main/org/audiveris/omr/image/BufferedSource.java index dcf2aa190..4e1f30882 100644 --- a/src/main/org/audiveris/omr/image/BufferedSource.java +++ b/src/main/org/audiveris/omr/image/BufferedSource.java @@ -33,7 +33,6 @@ public class BufferedSource implements PixelSource { - //~ Instance fields ---------------------------------------------------------------------------- /** The wrapped BufferedImage instance. */ private final BufferedImage image; @@ -47,7 +46,6 @@ public class BufferedSource /** Buffer to read pixel value. */ private final int[] pixelArray; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BufferedSource object around a given BufferedImage * instance. @@ -64,7 +62,6 @@ public BufferedSource (BufferedImage image) pixelArray = new int[4]; } - //~ Methods ------------------------------------------------------------------------------------ @Override public int get (int x, int y) diff --git a/src/main/org/audiveris/omr/image/ChamferDistance.java b/src/main/org/audiveris/omr/image/ChamferDistance.java index bb7179c1d..84773c452 100644 --- a/src/main/org/audiveris/omr/image/ChamferDistance.java +++ b/src/main/org/audiveris/omr/image/ChamferDistance.java @@ -11,12 +11,12 @@ * Class {@code ChamferDistance} implements a Distance Transform operation using * chamfer masks. * - * @author Code by Xavier Philippeau
                  Kernels by Verwer, Borgefors and Thiel + * @author Code by Xavier Philippeau
                  + * Kernels by Verwer, Borgefors and Thiel * @author Hervé Bitteur for interface and type-specific implementations */ public interface ChamferDistance { - //~ Static fields/initializers ----------------------------------------------------------------- /** Value when on target location. */ public static final int VALUE_TARGET = 0; @@ -26,34 +26,39 @@ public interface ChamferDistance /** Chessboard mask. */ public static final int[][] chessboard = new int[][]{ - new int[]{1, 0, 1}, new int[]{1, 1, 1} - }; + new int[]{1, 0, 1}, + new int[]{1, 1, 1}}; /** 3x3 mask. */ - public static final int[][] chamfer3 = new int[][]{new int[]{1, 0, 3}, new int[]{1, 1, 4}}; + public static final int[][] chamfer3 = new int[][]{ + new int[]{1, 0, 3}, + new int[]{1, 1, 4}}; /** 5x5 mask. */ public static final int[][] chamfer5 = new int[][]{ - new int[]{1, 0, 5}, new int[]{1, 1, 7}, - new int[]{2, 1, 11} - }; + new int[]{1, 0, 5}, + new int[]{1, 1, 7}, + new int[]{2, 1, 11}}; /** 7x7 mask. */ public static final int[][] chamfer7 = new int[][]{ - new int[]{1, 0, 14}, new int[]{1, 1, 20}, - new int[]{2, 1, 31}, new int[]{3, 1, 44} - }; + new int[]{1, 0, 14}, + new int[]{1, 1, 20}, + new int[]{2, 1, 31}, + new int[]{3, 1, 44}}; /** 13x13 mask. */ public static final int[][] chamfer13 = new int[][]{ - new int[]{1, 0, 68}, new int[]{1, 1, 96}, - new int[]{2, 1, 152}, new int[]{3, 1, 215}, - new int[]{3, 2, 245}, new int[]{4, 1, 280}, - new int[]{4, 3, 340}, new int[]{5, 1, 346}, - new int[]{6, 1, 413} - }; - - //~ Methods ------------------------------------------------------------------------------------ + new int[]{1, 0, 68}, + new int[]{1, 1, 96}, + new int[]{2, 1, 152}, + new int[]{3, 1, 215}, + new int[]{3, 2, 245}, + new int[]{4, 1, 280}, + new int[]{4, 3, 340}, + new int[]{5, 1, 346}, + new int[]{6, 1, 413}}; + //---------// // compute // //---------// @@ -93,11 +98,12 @@ public interface ChamferDistance */ DistanceTable computeToFore (ByteProcessor input); - //~ Inner Classes ------------------------------------------------------------------------------ + /** + * Abstract implementation. + */ public abstract class Abstract implements ChamferDistance { - //~ Instance fields ------------------------------------------------------------------------ /** The local distance mask to apply. */ private final int[][] chamfer; @@ -105,7 +111,6 @@ public abstract class Abstract /** Mask normalizer. */ private final int normalizer; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Abstract object, with chamfer3 as default mask. */ @@ -125,7 +130,6 @@ public Abstract (int[][] chamfer) normalizer = chamfer[0][2]; } - //~ Methods -------------------------------------------------------------------------------- //---------// // compute // //---------// @@ -200,17 +204,14 @@ public void process (DistanceTable output) continue; } - for (int k = 0; k < chamfer.length; k++) { - int dx = chamfer[k][0]; - int dy = chamfer[k][1]; - int dt = chamfer[k][2]; - + for (int[] cf : chamfer) { + int dx = cf[0]; + int dy = cf[1]; + int dt = cf[2]; testAndSet(output, x + dx, y + dy, v + dt); - if (dy != 0) { testAndSet(output, x - dx, y + dy, v + dt); } - if (dx != dy) { testAndSet(output, x + dy, y + dx, v + dt); @@ -231,17 +232,14 @@ public void process (DistanceTable output) continue; } - for (int k = 0; k < chamfer.length; k++) { - int dx = chamfer[k][0]; - int dy = chamfer[k][1]; - int dt = chamfer[k][2]; - + for (int[] chamfer1 : chamfer) { + int dx = chamfer1[0]; + int dy = chamfer1[1]; + int dt = chamfer1[2]; testAndSet(output, x - dx, y - dy, v + dt); - if (dy != 0) { testAndSet(output, x + dx, y - dy, v + dt); } - if (dx != dy) { testAndSet(output, x - dy, y - dx, v + dt); @@ -257,8 +255,8 @@ public void process (DistanceTable output) /** * Get Table instance of the proper type and size. * - * @param width desired width - * @param height desired height + * @param width desired width + * @param height desired height * @param normalizer the normalizing value * @return the table of proper type and dimension */ @@ -327,10 +325,12 @@ private void testAndSet (DistanceTable output, //---------// // Integer // //---------// + /** + * Integer-based chamfer distance. + */ public class Integer extends Abstract { - //~ Methods -------------------------------------------------------------------------------- @Override protected DistanceTable allocateOutput (int width, @@ -344,10 +344,12 @@ protected DistanceTable allocateOutput (int width, //-------// // Short // //-------// + /** + * Short-based chamfer distance. + */ public class Short extends Abstract { - //~ Methods -------------------------------------------------------------------------------- @Override protected DistanceTable allocateOutput (int width, diff --git a/src/main/org/audiveris/omr/image/DistanceFilter.java b/src/main/org/audiveris/omr/image/DistanceFilter.java index 706d18dca..820850e59 100644 --- a/src/main/org/audiveris/omr/image/DistanceFilter.java +++ b/src/main/org/audiveris/omr/image/DistanceFilter.java @@ -32,12 +32,10 @@ public class DistanceFilter implements PixelFilter { - //~ Instance fields ---------------------------------------------------------------------------- /** The underlying distance image (distances to foreground). */ private final Table distances; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new DistanceFilter object. * @@ -48,7 +46,6 @@ public DistanceFilter (Table distances) this.distances = distances; } - //~ Methods ------------------------------------------------------------------------------------ @Override public ByteProcessor filteredImage () { diff --git a/src/main/org/audiveris/omr/image/DistanceMatching.java b/src/main/org/audiveris/omr/image/DistanceMatching.java index fb97e4b54..2241b6da5 100644 --- a/src/main/org/audiveris/omr/image/DistanceMatching.java +++ b/src/main/org/audiveris/omr/image/DistanceMatching.java @@ -32,7 +32,6 @@ */ public class DistanceMatching { - //~ Instance fields ---------------------------------------------------------------------------- /** * The DistanceTransform image. @@ -41,7 +40,6 @@ public class DistanceMatching */ private final DistanceTable distances; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new DistanceMatching object from a distant transform image. * @@ -52,7 +50,6 @@ public DistanceMatching (DistanceTable distances) this.distances = distances; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // matchAll // //----------// @@ -69,7 +66,7 @@ public List matchAll (Template template, { final int scanWidth = distances.getWidth() - template.getWidth(); final int scanHeight = distances.getHeight() - template.getHeight(); - final List locations = new ArrayList(); + final List locations = new ArrayList<>(); for (int x = 0; x < scanWidth; x++) { for (int y = 0; y < scanHeight; y++) { diff --git a/src/main/org/audiveris/omr/image/DistanceTable.java b/src/main/org/audiveris/omr/image/DistanceTable.java index bb6c38f44..2a9c9f3b2 100644 --- a/src/main/org/audiveris/omr/image/DistanceTable.java +++ b/src/main/org/audiveris/omr/image/DistanceTable.java @@ -39,7 +39,6 @@ public interface DistanceTable extends Table { - //~ Methods ------------------------------------------------------------------------------------ /** * Report an image built with distance data. @@ -57,24 +56,20 @@ public interface DistanceTable */ int getNormalizer (); - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // Abstract // //----------// public abstract class Abstract implements DistanceTable { - //~ Instance fields ------------------------------------------------------------------------ protected final int normalizer; - //~ Constructors --------------------------------------------------------------------------- public Abstract (int normalizer) { this.normalizer = normalizer; } - //~ Methods -------------------------------------------------------------------------------- @Override public void dump (String title) { @@ -200,11 +195,9 @@ private int[] getLut (int rawDistMax) public static class Integer extends Abstract { - //~ Instance fields ------------------------------------------------------------------------ private final Table.Integer table; - //~ Constructors --------------------------------------------------------------------------- public Integer (int width, int height, int normalizer) @@ -220,7 +213,6 @@ protected Integer (Table.Integer table, this.table = table; } - //~ Methods -------------------------------------------------------------------------------- @Override public DistanceTable.Integer getCopy (Rectangle roi) { @@ -246,11 +238,9 @@ protected final Table getTable () public static class Short extends Abstract { - //~ Instance fields ------------------------------------------------------------------------ private final Table.Short table; - //~ Constructors --------------------------------------------------------------------------- public Short (int width, int height, int normalizer) @@ -266,7 +256,6 @@ protected Short (Table.Short table, this.table = table; } - //~ Methods -------------------------------------------------------------------------------- @Override public DistanceTable.Short getCopy (Rectangle roi) { diff --git a/src/main/org/audiveris/omr/image/FilterDescriptor.java b/src/main/org/audiveris/omr/image/FilterDescriptor.java index 9ee003cac..3199e10f9 100644 --- a/src/main/org/audiveris/omr/image/FilterDescriptor.java +++ b/src/main/org/audiveris/omr/image/FilterDescriptor.java @@ -33,20 +33,19 @@ /** * Management data meant to describe an implementation instance of a PixelFilter. * (kind of filter + related parameters) + * + * @author Hervé Bitteur */ public abstract class FilterDescriptor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - FilterDescriptor.class); + private static final Logger logger = LoggerFactory.getLogger(FilterDescriptor.class); /** Default param. */ public static final Param defaultFilter = new Default(); - //~ Methods ------------------------------------------------------------------------------------ //--------// // equals // //--------// @@ -68,7 +67,6 @@ public boolean equals (Object obj) */ public abstract PixelFilter getFilter (ByteProcessor source); - // //---------// // getKind // //---------// @@ -79,14 +77,6 @@ public boolean equals (Object obj) */ public abstract FilterKind getKind (); - //----------------// - // getDefaultKind // - //----------------// - public static FilterKind getDefaultKind () - { - return constants.defaultKind.getValue(); - } - //----------// // hashCode // //----------// @@ -98,14 +88,6 @@ public int hashCode () return hash; } - //----------------// - // setDefaultKind // - //----------------// - public static void setDefaultKind (FilterKind kind) - { - constants.defaultKind.setValue(kind); - } - //----------// // toString // //----------// @@ -131,16 +113,30 @@ protected String internalsString () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------------// + // getDefaultKind // + //----------------// + public static FilterKind getDefaultKind () + { + return constants.defaultKind.getValue(); + } + + //----------------// + // setDefaultKind // + //----------------// + public static void setDefaultKind (FilterKind kind) + { + constants.defaultKind.setValue(kind); + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ - private final Constant.Enum defaultKind = new Constant.Enum( + private final Constant.Enum defaultKind = new Constant.Enum<>( FilterKind.class, FilterKind.ADAPTIVE, "Default kind of PixelFilter (GLOBAL or ADAPTIVE)"); @@ -152,7 +148,6 @@ private static final class Constants private static class Default extends Param { - //~ Methods -------------------------------------------------------------------------------- @Override public FilterDescriptor getSourceValue () diff --git a/src/main/org/audiveris/omr/image/FilterKind.java b/src/main/org/audiveris/omr/image/FilterKind.java index c2055efd5..ab9fc2595 100644 --- a/src/main/org/audiveris/omr/image/FilterKind.java +++ b/src/main/org/audiveris/omr/image/FilterKind.java @@ -24,6 +24,8 @@ /** * Class {@code FilterKind} handles the various kinds of {@link PixelFilter} * implementations. + * + * @author Hervé Bitteur */ public enum FilterKind { diff --git a/src/main/org/audiveris/omr/image/FilterParam.java b/src/main/org/audiveris/omr/image/FilterParam.java index b4de5b39e..da54baea0 100644 --- a/src/main/org/audiveris/omr/image/FilterParam.java +++ b/src/main/org/audiveris/omr/image/FilterParam.java @@ -1,91 +1,92 @@ -//------------------------------------------------------------------------------------------------// -// // -// F i l t e r P a r a m // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.image; - -import org.audiveris.omr.util.param.Param; - -import javax.xml.bind.annotation.XmlElementRef; -import javax.xml.bind.annotation.XmlElementRefs; -import javax.xml.bind.annotation.adapters.XmlAdapter; - -/** - * Class {@code FilterParam} is a param on FilterDescriptor. - * - * @author Hervé Bitteur - */ -public class FilterParam - extends Param -{ - //~ Inner Classes ------------------------------------------------------------------------------ - - public static class Adapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public Value marshal (FilterParam fp) - throws Exception - { - if (fp == null) { - return null; - } - - FilterDescriptor specific = fp.getSpecific(); - - if (specific == null) { - return null; - } - - Value value = new Value(); - value.filter = specific; - - return value; - } - - @Override - public FilterParam unmarshal (Value value) - throws Exception - { - if (value == null) { - return null; - } - - FilterParam fp = new FilterParam(); - fp.setSpecific(value.filter); - - return fp; - } - - //~ Inner Classes -------------------------------------------------------------------------- - protected static class Value - { - //~ Instance fields -------------------------------------------------------------------- - - @XmlElementRefs({ - @XmlElementRef(type = GlobalDescriptor.class) - , @XmlElementRef(type = AdaptiveDescriptor.class) - }) - FilterDescriptor filter; - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// F i l t e r P a r a m // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.image; + +import org.audiveris.omr.util.param.Param; + +import javax.xml.bind.annotation.XmlElementRef; +import javax.xml.bind.annotation.XmlElementRefs; +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * Class {@code FilterParam} is a param on FilterDescriptor. + * + * @author Hervé Bitteur + */ +public class FilterParam + extends Param +{ + + /** + * JAXB adapter. + */ + public static class Adapter + extends XmlAdapter + { + + @Override + public Value marshal (FilterParam fp) + throws Exception + { + if (fp == null) { + return null; + } + + FilterDescriptor specific = fp.getSpecific(); + + if (specific == null) { + return null; + } + + Value value = new Value(); + value.filter = specific; + + return value; + } + + @Override + public FilterParam unmarshal (Value value) + throws Exception + { + if (value == null) { + return null; + } + + FilterParam fp = new FilterParam(); + fp.setSpecific(value.filter); + + return fp; + } + + /** + * Flattened value for un/marshalling. + */ + protected static class Value + { + + @XmlElementRefs({ + @XmlElementRef(type = GlobalDescriptor.class), + @XmlElementRef(type = AdaptiveDescriptor.class)}) + FilterDescriptor filter; + } + } +} diff --git a/src/main/org/audiveris/omr/image/FloodFiller.java b/src/main/org/audiveris/omr/image/FloodFiller.java index 581cbd641..a0de1da93 100644 --- a/src/main/org/audiveris/omr/image/FloodFiller.java +++ b/src/main/org/audiveris/omr/image/FloodFiller.java @@ -30,7 +30,6 @@ */ public class FloodFiller { - //~ Instance fields ---------------------------------------------------------------------------- private final BufferedImage image; @@ -38,7 +37,6 @@ public class FloodFiller private final int height; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new FloodFiller object. * @@ -51,7 +49,6 @@ public FloodFiller (BufferedImage image) height = image.getHeight(); } - //~ Methods ------------------------------------------------------------------------------------ /** * Flood fill the area from provided (x,y) location by recursively converting * pixels from oldColor to newColor. diff --git a/src/main/org/audiveris/omr/image/GaussianGrayFilter.java b/src/main/org/audiveris/omr/image/GaussianGrayFilter.java index a960b87f8..5de211bac 100644 --- a/src/main/org/audiveris/omr/image/GaussianGrayFilter.java +++ b/src/main/org/audiveris/omr/image/GaussianGrayFilter.java @@ -37,7 +37,6 @@ public class GaussianGrayFilter extends AbstractGrayFilter { - //~ Instance fields ---------------------------------------------------------------------------- /** Radius of the kernel. */ private final float radius; @@ -45,7 +44,6 @@ public class GaussianGrayFilter /** The kernel to apply. */ private final Kernel kernel; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new GaussianGrayFilter object with a default radius value. */ @@ -65,7 +63,6 @@ public GaussianGrayFilter (float radius) kernel = makeKernel(radius); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // filter // //--------// @@ -113,6 +110,50 @@ public float getRadius () return radius; } + //----------------------// + // convolveAndTranspose // + //----------------------// + private void convolveAndTranspose (byte[] inPixels, + byte[] outPixels, + int width, + int height) + { + float[] matrix = kernel.getKernelData(null); + int cols = kernel.getWidth(); + int cols2 = cols / 2; + + for (int y = 0; y < height; y++) { + int index = y; + int ioffset = y * width; + + for (int x = 0; x < width; x++) { + float p = 0; + int moffset = cols2; + + for (int col = -cols2; col <= cols2; col++) { + float f = matrix[moffset + col]; + + if (f != 0) { + int ix = x + col; + + if (ix < 0) { + ix = 0; + } else if (ix >= width) { + ix = width - 1; + } + + int pix = inPixels[ioffset + ix] & 0xff; + p += (f * pix); + } + } + + int ip = clamp((int) (p + 0.5)); + outPixels[index] = (byte) ip; + index += height; + } + } + } + //------------// // makeKernel // //------------// @@ -177,47 +218,4 @@ private static int clamp (int val) return val; } - //----------------------// - // convolveAndTranspose // - //----------------------// - private void convolveAndTranspose (byte[] inPixels, - byte[] outPixels, - int width, - int height) - { - float[] matrix = kernel.getKernelData(null); - int cols = kernel.getWidth(); - int cols2 = cols / 2; - - for (int y = 0; y < height; y++) { - int index = y; - int ioffset = y * width; - - for (int x = 0; x < width; x++) { - float p = 0; - int moffset = cols2; - - for (int col = -cols2; col <= cols2; col++) { - float f = matrix[moffset + col]; - - if (f != 0) { - int ix = x + col; - - if (ix < 0) { - ix = 0; - } else if (ix >= width) { - ix = width - 1; - } - - int pix = inPixels[ioffset + ix] & 0xff; - p += (f * pix); - } - } - - int ip = clamp((int) (p + 0.5)); - outPixels[index] = (byte) ip; - index += height; - } - } - } } diff --git a/src/main/org/audiveris/omr/image/Ghostscript.java b/src/main/org/audiveris/omr/image/Ghostscript.java index a7fe5cfa7..018069135 100644 --- a/src/main/org/audiveris/omr/image/Ghostscript.java +++ b/src/main/org/audiveris/omr/image/Ghostscript.java @@ -43,7 +43,6 @@ */ public class Ghostscript { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -52,8 +51,6 @@ public class Ghostscript /** Path to exec. */ private static volatile String path; - //~ Methods ------------------------------------------------------------------------------------ - // //---------// // getPath // //---------// @@ -98,7 +95,7 @@ private static List getRegistryOutputs () "HKLM\\SOFTWARE\\Wow6432Node\\GPL Ghostscript" // Wow (64/32) }; - List outputs = new ArrayList(); + List outputs = new ArrayList<>(); // Access registry twice, one for win32 & win64 and one for Wow for (String radix : radices) { @@ -115,7 +112,7 @@ private static List getRegistryOutputs () /** * Retrieve the path to suitable ghostscript executable on Windows * environments. - * + *

                  * This is implemented on registry informations, using CLI "reg" command: * reg query "HKLM\SOFTWARE\GPL Ghostscript" /s * @@ -130,8 +127,9 @@ private static String getWindowsPath () /** Regex for registry key line. */ final Pattern keyPattern = Pattern.compile( - "^HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\(Wow6432Node\\\\)?GPL Ghostscript\\\\" - + group(VERSION, "\\d+\\.\\d+") + "$"); + "^HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\(Wow6432Node\\\\)?GPL Ghostscript\\\\" + group( + VERSION, + "\\d+\\.\\d+") + "$"); /** Regex for registry value line. */ final Pattern valPattern = Pattern.compile( @@ -160,8 +158,8 @@ private static String getWindowsPath () Double version = Double.valueOf(versionStr); - if ((version >= constants.minVersion.getValue()) - && (version <= constants.maxVersion.getValue())) { + if ((version >= constants.minVersion.getValue()) && (version <= constants.maxVersion + .getValue())) { // We have an acceptable version if ((bestVersion == null) || (bestVersion < version)) { bestVersion = version; @@ -209,14 +207,12 @@ private static String getWindowsPath () return null; // Abnormal exit } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Double minVersion = new Constant.Double( "version", @@ -225,7 +221,11 @@ private static final class Constants private final Constant.Double maxVersion = new Constant.Double( "version", - 9999, + 9_999, "Maximum Ghostscript acceptable version"); } + + private Ghostscript () + { + } } diff --git a/src/main/org/audiveris/omr/image/GlobalDescriptor.java b/src/main/org/audiveris/omr/image/GlobalDescriptor.java index edb57f68b..6477abdf3 100644 --- a/src/main/org/audiveris/omr/image/GlobalDescriptor.java +++ b/src/main/org/audiveris/omr/image/GlobalDescriptor.java @@ -41,16 +41,13 @@ public class GlobalDescriptor extends FilterDescriptor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Instance fields ---------------------------------------------------------------------------- /** The threshold value for the whole pixel source. */ @XmlAttribute(name = "threshold") public final int threshold; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new GlobalDescriptor object. * @@ -67,55 +64,6 @@ private GlobalDescriptor () threshold = 0; } - //~ Methods ------------------------------------------------------------------------------------ - //-------------------// - // defaultIsSpecific // - //-------------------// - public static boolean defaultIsSpecific () - { - return !constants.defaultThreshold.isSourceValue(); - } - - //------------// - // getDefault // - //------------// - public static GlobalDescriptor getDefault () - { - return new GlobalDescriptor(getDefaultThreshold()); - } - - //---------------------// - // getDefaultThreshold // - //---------------------// - public static int getDefaultThreshold () - { - return constants.defaultThreshold.getValue(); - } - - //----------------// - // getSourceValue // - //----------------// - public static GlobalDescriptor getSourceValue () - { - return new GlobalDescriptor(constants.defaultThreshold.getSourceValue()); - } - - //---------------// - // resetToSource // - //---------------// - public static void resetToSource () - { - constants.defaultThreshold.resetToSource(); - } - - //---------------------// - // setDefaultThreshold // - //---------------------// - public static void setDefaultThreshold (int threshold) - { - constants.defaultThreshold.setValue(threshold); - } - //--------// // equals // //--------// @@ -140,7 +88,6 @@ public PixelFilter getFilter (ByteProcessor source) return new GlobalFilter(source, threshold); } - // //---------// // getKind // //---------// @@ -174,14 +121,60 @@ protected String internalsString () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------------// + // defaultIsSpecific // + //-------------------// + public static boolean defaultIsSpecific () + { + return !constants.defaultThreshold.isSourceValue(); + } + + //------------// + // getDefault // + //------------// + public static GlobalDescriptor getDefault () + { + return new GlobalDescriptor(getDefaultThreshold()); + } + + //---------------------// + // getDefaultThreshold // + //---------------------// + public static int getDefaultThreshold () + { + return constants.defaultThreshold.getValue(); + } + + //---------------------// + // setDefaultThreshold // + //---------------------// + public static void setDefaultThreshold (int threshold) + { + constants.defaultThreshold.setValue(threshold); + } + + //----------------// + // getSourceValue // + //----------------// + public static GlobalDescriptor getSourceValue () + { + return new GlobalDescriptor(constants.defaultThreshold.getSourceValue()); + } + + //---------------// + // resetToSource // + //---------------// + public static void resetToSource () + { + constants.defaultThreshold.resetToSource(); + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer defaultThreshold = new Constant.Integer( "GrayLevel", diff --git a/src/main/org/audiveris/omr/image/GlobalFilter.java b/src/main/org/audiveris/omr/image/GlobalFilter.java index 62f534c84..ae1d74bfb 100644 --- a/src/main/org/audiveris/omr/image/GlobalFilter.java +++ b/src/main/org/audiveris/omr/image/GlobalFilter.java @@ -36,12 +36,10 @@ public class GlobalFilter extends SourceWrapper implements PixelFilter { - //~ Instance fields ---------------------------------------------------------------------------- /** Global threshold. */ private final int threshold; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a binary wrapper on a raw pixel source. * @@ -55,7 +53,6 @@ public GlobalFilter (ByteProcessor source, this.threshold = threshold; } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // filteredImage // //---------------// diff --git a/src/main/org/audiveris/omr/image/ImageFormatException.java b/src/main/org/audiveris/omr/image/ImageFormatException.java index 589a4e682..ef096a5eb 100644 --- a/src/main/org/audiveris/omr/image/ImageFormatException.java +++ b/src/main/org/audiveris/omr/image/ImageFormatException.java @@ -30,7 +30,6 @@ public class ImageFormatException extends Exception { - //~ Constructors ------------------------------------------------------------------------------- /** * Construct an {@code ImageFormatException} with provided detail message. diff --git a/src/main/org/audiveris/omr/image/ImageLoading.java b/src/main/org/audiveris/omr/image/ImageLoading.java index a7615458e..cd94523da 100644 --- a/src/main/org/audiveris/omr/image/ImageLoading.java +++ b/src/main/org/audiveris/omr/image/ImageLoading.java @@ -59,16 +59,19 @@ * Class {@code ImageLoading} handles the loading of one or several images out of an * input file. *

                  - * It works in two phases:

                    + * It works in two phases: + *
                      *
                    1. An initial call to {@link #getLoader(Path)} tries to return a {@link Loader} instance that * fits the provided input file.
                    2. - *
                    3. Then this Loader instance can be used via:
                        + *
                      • Then this Loader instance can be used via: + *
                          *
                        • {@link Loader#getImageCount()} to know how many images are available in the input file,
                        • *
                        • {@link Loader#getImage(int)} to return any specific image,
                        • *
                        • {@link Loader#dispose()} to finally release any resources.
                        • *
                        *
                    - * This class leverages several software pieces, each with its own Loader subclass:
                      + * This class leverages several software pieces, each with its own Loader subclass: + *
                        *
                      • JPod for PDF files. This replaces former use of GhostScript sub-process.
                      • *
                      • ImageIO for all files except PDF.
                      • *
                      • JAI if ImageIO failed. Note that JAI can find only one image per file.
                      • @@ -80,13 +83,11 @@ */ public abstract class ImageLoading { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(ImageLoading.class); - //~ Constructors ------------------------------------------------------------------------------- /** * To disallow instantiation. */ @@ -94,7 +95,6 @@ private ImageLoading () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getLoader // //-----------// @@ -185,7 +185,7 @@ private static Loader getImageIOLoader (Path imgPath) int imageCount = reader.getNumImages(true); return new ImageIOLoader(reader, imageCount); - } catch (Exception ex) { + } catch (IOException ex) { logger.warn("ImageIO failed for " + imgPath, ex); return null; @@ -253,7 +253,6 @@ private static Loader getJaiLoader (Path imgPath) return null; } - //~ Inner Interfaces --------------------------------------------------------------------------- //--------// // Loader // //--------// @@ -262,7 +261,6 @@ private static Loader getJaiLoader (Path imgPath) */ public static interface Loader { - //~ Methods -------------------------------------------------------------------------------- /** * Release any loader resources. @@ -287,14 +285,12 @@ BufferedImage getImage (int id) int getImageCount (); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer pdfResolution = new Constant.Integer( "DPI", @@ -308,18 +304,15 @@ private static final class Constants private abstract static class AbstractLoader implements Loader { - //~ Instance fields ------------------------------------------------------------------------ /** Count of images available in input file. */ protected final int imageCount; - //~ Constructors --------------------------------------------------------------------------- - public AbstractLoader (int imageCount) + AbstractLoader (int imageCount) { this.imageCount = imageCount; } - //~ Methods -------------------------------------------------------------------------------- @Override public void dispose () { @@ -345,19 +338,16 @@ protected void checkId (int id) private static class ImageIOLoader extends AbstractLoader { - //~ Instance fields ------------------------------------------------------------------------ private final ImageReader reader; - //~ Constructors --------------------------------------------------------------------------- - public ImageIOLoader (ImageReader reader, - int imageCount) + ImageIOLoader (ImageReader reader, + int imageCount) { super(imageCount); this.reader = reader; } - //~ Methods -------------------------------------------------------------------------------- @Override public void dispose () { @@ -382,19 +372,16 @@ public BufferedImage getImage (int id) private static class JPodLoader extends AbstractLoader { - //~ Instance fields ------------------------------------------------------------------------ private final PDDocument doc; - //~ Constructors --------------------------------------------------------------------------- - public JPodLoader (PDDocument doc, - int imageCount) + JPodLoader (PDDocument doc, + int imageCount) { super(imageCount); this.doc = doc; } - //~ Methods -------------------------------------------------------------------------------- @Override public void dispose () { @@ -489,18 +476,15 @@ public BufferedImage getImage (int id) private static class JaiLoader extends AbstractLoader { - //~ Instance fields ------------------------------------------------------------------------ private final BufferedImage image; // The single image - //~ Constructors --------------------------------------------------------------------------- - public JaiLoader (BufferedImage image) + JaiLoader (BufferedImage image) { super(1); // JAI can return just one image this.image = image; } - //~ Methods -------------------------------------------------------------------------------- @Override public BufferedImage getImage (int id) throws IOException diff --git a/src/main/org/audiveris/omr/image/ImageUtil.java b/src/main/org/audiveris/omr/image/ImageUtil.java index c234a8415..7d19a0dbc 100644 --- a/src/main/org/audiveris/omr/image/ImageUtil.java +++ b/src/main/org/audiveris/omr/image/ImageUtil.java @@ -48,11 +48,9 @@ */ public abstract class ImageUtil { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ImageUtil.class); - //~ Methods ------------------------------------------------------------------------------------ //--------// // invert // //--------// @@ -161,9 +159,7 @@ public static BufferedImage rgbToGray (BufferedImage rgb) logger.info("Converting RGB to gray ..."); // We use luminance value based on standard RGB combination - double[][] matrix = { - {0.114d, 0.587d, 0.299d, 0.0d} - }; + double[][] matrix = {{0.114d, 0.587d, 0.299d, 0.0d}}; return JAI.create("bandcombine", new ParameterBlock().addSource(rgb).add(matrix), null) .getAsBufferedImage(); @@ -290,4 +286,8 @@ private static String typeOf (int type) return "?"; } } + + private ImageUtil () + { + } } diff --git a/src/main/org/audiveris/omr/image/LocalHistogram.java b/src/main/org/audiveris/omr/image/LocalHistogram.java index 210edfefb..ec3456cf2 100644 --- a/src/main/org/audiveris/omr/image/LocalHistogram.java +++ b/src/main/org/audiveris/omr/image/LocalHistogram.java @@ -27,12 +27,11 @@ /** * Class {@code LocalHistogram} * - * @author Hervé Bitteur + * @author ? */ public class LocalHistogram implements MorphoConstants { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(LocalHistogram.class); @@ -40,7 +39,6 @@ public class LocalHistogram private static final int MIN = 0; - //~ Instance fields ---------------------------------------------------------------------------- // private int[] hist=new int[256]; private int[] counts = new int[256]; @@ -50,7 +48,6 @@ public class LocalHistogram private int binCount = 0; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LocalHistogram object. * @@ -92,29 +89,12 @@ public LocalHistogram (int index, int[][] pg, int type) { - int binCount = Math.min(pg.length, 256); + binCount = Math.min(pg.length, 256); //hist=new int[256]; counts = new int[256]; init(index, width, height, pixels, pg, type); } - //~ Methods ------------------------------------------------------------------------------------ - public void Log () - { - // IJ.log("min: "+min + " max: "+max); - StringBuffer sb = new StringBuffer(200); - - for (int h = 0; h <= 255; h++) { - if (counts[h] != 0) { - sb.append(h + " " + counts[h] + " \n"); - } - - // sb.append(counts[h]+" "); - } - - logger.info("histogram +\n" + sb.toString() + "\n"); - } - public void add (LocalHistogram bh) { int u = Math.min(min, bh.min); @@ -126,7 +106,7 @@ public void add (LocalHistogram bh) // IJ.log("u "+u+" v "+v); //int tmin=u; int tmax=v; for (int i = u; i <= v; i++) { - counts[i] = counts[i] + bh.counts[i]; + counts[i] += bh.counts[i]; // if (counts[i]<0) IJ.log("less than zero: "+i); } @@ -288,6 +268,22 @@ public void init (int index, //Log(); } + public void log () + { + // IJ.log("min: "+min + " max: "+max); + StringBuilder sb = new StringBuilder(200); + + for (int h = 0; h <= 255; h++) { + if (counts[h] != 0) { + sb.append(h).append(" ").append(counts[h]).append(" \n"); + } + + // sb.append(counts[h]+" "); + } + + logger.info("histogram +\n" + sb.toString() + "\n"); + } + public void sub (LocalHistogram bh) { int u = Math.min(min, bh.min); @@ -303,7 +299,7 @@ public void sub (LocalHistogram bh) int tmax = v; for (int i = u; i <= v; i++) { - counts[i] = counts[i] - bh.counts[i]; + counts[i] -= bh.counts[i]; if (counts[i] < 0) { counts[i] = 0; diff --git a/src/main/org/audiveris/omr/image/Mask.java b/src/main/org/audiveris/omr/image/Mask.java index 09253f418..3dbae572a 100644 --- a/src/main/org/audiveris/omr/image/Mask.java +++ b/src/main/org/audiveris/omr/image/Mask.java @@ -37,11 +37,9 @@ */ public class Mask { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Mask.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Mask area. */ private final Area area; @@ -52,7 +50,6 @@ public class Mask /** Number of relevant points. */ private int pointCount; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Mask object. * @@ -66,7 +63,6 @@ public Mask (Area area) bitmap = computeRelevantPoints(area); } - //~ Methods ------------------------------------------------------------------------------------ //-------// // apply // //-------// @@ -95,6 +91,11 @@ public void apply (Adapter adapter) //------// // dump // //------// + /** + * Dump this mask. + * + * @param title dump title if any + */ public void dump (String title) { bitmap.dump(title); @@ -147,13 +148,11 @@ private Table.UnsignedByte computeRelevantPoints (Area area) return table; } - //~ Inner Interfaces --------------------------------------------------------------------------- //---------// // Adapter // //---------// public static interface Adapter { - //~ Methods -------------------------------------------------------------------------------- /** * Method called on each mask relevant point. diff --git a/src/main/org/audiveris/omr/image/MedianGrayFilter.java b/src/main/org/audiveris/omr/image/MedianGrayFilter.java index 138a2f582..5b994c52e 100644 --- a/src/main/org/audiveris/omr/image/MedianGrayFilter.java +++ b/src/main/org/audiveris/omr/image/MedianGrayFilter.java @@ -34,12 +34,10 @@ public class MedianGrayFilter extends AbstractGrayFilter { - //~ Instance fields ---------------------------------------------------------------------------- /** Desired radius for the filter. */ private final int radius; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new MedianGrayFilter object. * @@ -50,7 +48,6 @@ public MedianGrayFilter (int radius) this.radius = radius; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // filter // //--------// diff --git a/src/main/org/audiveris/omr/image/MorphoConstants.java b/src/main/org/audiveris/omr/image/MorphoConstants.java index db5817470..33b621877 100644 --- a/src/main/org/audiveris/omr/image/MorphoConstants.java +++ b/src/main/org/audiveris/omr/image/MorphoConstants.java @@ -28,7 +28,6 @@ */ public interface MorphoConstants { - //~ Static fields/initializers ----------------------------------------------------------------- public static final int FREE = -1; diff --git a/src/main/org/audiveris/omr/image/MorphoProcessor.java b/src/main/org/audiveris/omr/image/MorphoProcessor.java index c72d90a06..10751c0a1 100644 --- a/src/main/org/audiveris/omr/image/MorphoProcessor.java +++ b/src/main/org/audiveris/omr/image/MorphoProcessor.java @@ -34,42 +34,39 @@ public class MorphoProcessor implements MorphoConstants { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(MorphoProcessor.class); + public static final int BINF = -256; + private static final int ORIG = 0; private static final int PLUS = 1; private static final int MINUS = -1; - public static final int BINF = -256; - - //~ Instance fields ---------------------------------------------------------------------------- - private StructureElement se; //, down_se, up_se; + private final StructureElement se; //, down_se, up_se; - private StructureElement minus_se; //, down_se, up_se; + private final StructureElement minus_se; //, down_se, up_se; - private StructureElement plus_se; //, down_se, up_se; + private final StructureElement plus_se; //, down_se, up_se; - private LocalHistogram bh; + private final LocalHistogram bh; - private LocalHistogram p_h; + private final LocalHistogram p_h; - private LocalHistogram m_h; + private final LocalHistogram m_h; - private int[][] pg; + private final int[][] pg; - private int[][] pg_plus; + private final int[][] pg_plus; - private int[][] pg_minus; + private final int[][] pg_minus; int width; int height; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new instance of MorphoProcessor. * @@ -90,7 +87,6 @@ public MorphoProcessor (StructureElement se) pg_minus = minus_se.getVect(); } - //~ Methods ------------------------------------------------------------------------------------ //-------// // close // //-------// @@ -99,8 +95,7 @@ public MorphoProcessor (StructureElement se) * with arbitrary structural element * * @param ip the ImageProcessor - * - * */ + */ public void close (ByteProcessor ip) { int width = ip.getWidth(); @@ -156,7 +151,8 @@ public void close (ByteProcessor ip) //--------// // dilate // //--------// - /** Performs gray level dilation + /** + * Performs gray level dilation * * @param ip the ImageProcessor */ @@ -164,7 +160,7 @@ public void dilate (ByteProcessor ip) { int width = ip.getWidth(); int height = ip.getHeight(); - int max = 32768; //,k=0,x=0,y=0; + int max = 32_768; //,k=0,x=0,y=0; //int[][]pg=se.getVect(); // IJ.log("pg: "+pg.length); @@ -200,7 +196,7 @@ public void erode (ByteProcessor ip) { int width = ip.getWidth(); int height = ip.getHeight(); - int min = -32767; //,k=0,x=0,y=0; + int min = -32_767; //,k=0,x=0,y=0; int sz = pg.length; //se.getWidth()*se.getHeight(); // byte[] p=(byte[])ip.convertToByte(false).getValues(); @@ -235,7 +231,7 @@ public void fclose (ByteProcessor ip) //fastErode(ip,se); int width = ip.getWidth(); int height = ip.getHeight(); - int max = 32767; //,k=0,x=0,y=0; + int max = 32_767; //,k=0,x=0,y=0; //int pgzise=pg.length; byte[] pixels = (byte[]) ip.getPixels(); @@ -275,7 +271,7 @@ public void fclose (ByteProcessor ip) } //odd loop } - int min = -32767; //,k=0,x=0,y=0; + int min = -32_767; //,k=0,x=0,y=0; byte[] newpix2 = new byte[pixels.length]; @@ -326,8 +322,8 @@ public void open (ByteProcessor ip) { int width = ip.getWidth(); int height = ip.getHeight(); - int min = -32767; //,k=0,x=0,y=0; - int max = 32768; + int min = -32_767; //,k=0,x=0,y=0; + int max = 32_768; int w = this.width; //se.getWidth(); int h = this.height; //se.getHeight(); // int[][] pg=se.getVect(); @@ -419,11 +415,11 @@ private int[] getMinMax (int index, } if (type == DILATE) { - k = k + pg[g][2]; + k += pg[g][2]; } if (type == ERODE) { - k = k - pg[g][2]; + k -= pg[g][2]; } if (k < min) { diff --git a/src/main/org/audiveris/omr/image/PixelBuffer.java b/src/main/org/audiveris/omr/image/PixelBuffer.java index 6c5f21a5a..f42a79512 100644 --- a/src/main/org/audiveris/omr/image/PixelBuffer.java +++ b/src/main/org/audiveris/omr/image/PixelBuffer.java @@ -49,11 +49,9 @@ public class PixelBuffer extends Table.UnsignedByte implements PixelFilter, PixelSink { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(PixelBuffer.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new PixelBuffer object. * @@ -123,7 +121,6 @@ public PixelBuffer (PixelFilter filter) ///watch.print(); } - //~ Methods ------------------------------------------------------------------------------------ @Override public ByteProcessor filteredImage () { @@ -209,6 +206,11 @@ public boolean isFore (int x, //-----------------// // toBufferedImage // //-----------------// + /** + * Report the BufferedImage for this buffer. + * + * @return corresponding image + */ public BufferedImage toBufferedImage () { StopWatch watch = new StopWatch("PixelBuffer"); diff --git a/src/main/org/audiveris/omr/image/PixelDistance.java b/src/main/org/audiveris/omr/image/PixelDistance.java index e6dcb3a59..eceabcb0e 100644 --- a/src/main/org/audiveris/omr/image/PixelDistance.java +++ b/src/main/org/audiveris/omr/image/PixelDistance.java @@ -29,9 +29,7 @@ * @author Hervé Bitteur */ public class PixelDistance - { - //~ Static fields/initializers ----------------------------------------------------------------- /** To sort by increasing value, regardless of (x,y). */ public static final Comparator byValue = new Comparator() @@ -44,7 +42,6 @@ public int compare (PixelDistance o1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** Location abscissa. */ public final int x; @@ -54,7 +51,6 @@ public int compare (PixelDistance o1, /** Distance. */ public final double d; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new PixelDistance object. * @@ -71,7 +67,6 @@ public PixelDistance (int x, this.d = d; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/image/PixelFilter.java b/src/main/org/audiveris/omr/image/PixelFilter.java index 09fd848bb..a9d01c24f 100644 --- a/src/main/org/audiveris/omr/image/PixelFilter.java +++ b/src/main/org/audiveris/omr/image/PixelFilter.java @@ -31,7 +31,6 @@ public interface PixelFilter extends PixelSource { - //~ Methods ------------------------------------------------------------------------------------ /** * Run the filter on source image and report the filtered image. @@ -65,19 +64,21 @@ Context getContext (int x, boolean isFore (int x, int y); - //~ Inner Classes ------------------------------------------------------------------------------ /** * Structure used to report precise context of the source. * It can be extended for more specialized data. */ class Context { - //~ Instance fields ------------------------------------------------------------------------ /** Threshold used on pixel value. */ public final double threshold; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create Context object. + * + * @param threshold value + */ public Context (double threshold) { this.threshold = threshold; diff --git a/src/main/org/audiveris/omr/image/PixelSink.java b/src/main/org/audiveris/omr/image/PixelSink.java index bb3c1be64..10e418cb9 100644 --- a/src/main/org/audiveris/omr/image/PixelSink.java +++ b/src/main/org/audiveris/omr/image/PixelSink.java @@ -29,7 +29,6 @@ */ public interface PixelSink { - //~ Methods ------------------------------------------------------------------------------------ /** * Report the height of the rectangular sink diff --git a/src/main/org/audiveris/omr/image/PixelSource.java b/src/main/org/audiveris/omr/image/PixelSource.java index 5d6c44f36..0bc5b79cd 100644 --- a/src/main/org/audiveris/omr/image/PixelSource.java +++ b/src/main/org/audiveris/omr/image/PixelSource.java @@ -33,7 +33,6 @@ */ public interface PixelSource { - //~ Static fields/initializers ----------------------------------------------------------------- /** Default value for background pixel. */ public static final int BACKGROUND = 255; @@ -41,13 +40,11 @@ public interface PixelSource /** Default value for foreground pixel. */ public static final int FOREGROUND = 0; - //~ Methods ------------------------------------------------------------------------------------ /** * Report the pixel element, as read at location (x, y) in the source. * * @param x abscissa value * @param y ordinate value - * * @return the pixel value using range 0..255 (0/black for foreground, * 255/white for background) */ diff --git a/src/main/org/audiveris/omr/image/RandomFilter.java b/src/main/org/audiveris/omr/image/RandomFilter.java index d87ac09bf..b494495a1 100644 --- a/src/main/org/audiveris/omr/image/RandomFilter.java +++ b/src/main/org/audiveris/omr/image/RandomFilter.java @@ -43,11 +43,9 @@ public class RandomFilter extends AdaptiveFilter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(RandomFilter.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Create an adaptive wrapper on a raw pixel source. * @@ -68,8 +66,6 @@ public RandomFilter (ByteProcessor source, true); } - //~ Inner Classes ------------------------------------------------------------------------------ - // //--------// // MyTile // //--------// @@ -80,9 +76,8 @@ public RandomFilter (ByteProcessor source, private class MyTile extends Tile { - //~ Constructors --------------------------------------------------------------------------- - public MyTile (boolean squared) + MyTile (boolean squared) { // Allocate a tile as big as the source super(source.getWidth(), source.getHeight(), squared); diff --git a/src/main/org/audiveris/omr/image/ShapeDescriptor.java b/src/main/org/audiveris/omr/image/ShapeDescriptor.java index 62db38923..49f02be46 100644 --- a/src/main/org/audiveris/omr/image/ShapeDescriptor.java +++ b/src/main/org/audiveris/omr/image/ShapeDescriptor.java @@ -46,7 +46,7 @@ /** * Class {@code ShapeDescriptor} handles all the templates for a shape at a given - * global interline value. + * pointSize value. *

                        * Today there is just a single template per ShapeDescriptor. *

                        @@ -66,7 +66,6 @@ */ public class ShapeDescriptor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -91,7 +90,6 @@ public class ShapeDescriptor /** Color for irrelevant pixels. */ private static final int IRRELEVANT = new Color(0, 0, 0, 0).getRGB(); // Fully transparent - //~ Instance fields ---------------------------------------------------------------------------- private final Shape shape; private final int pointSize; @@ -104,7 +102,6 @@ public class ShapeDescriptor /** Symbol height. */ private int height = -1; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ShapeDescriptor object. * @@ -120,33 +117,6 @@ public ShapeDescriptor (Shape shape, template = createTemplate(shape, pointSize); } - //~ Methods ------------------------------------------------------------------------------------ - //------// - // main // - //------// - /** - * An entry point to allow generation and visual check of a bunch of templates. - * - * @param args minimum and maximum interline values - */ - public static void main (String... args) - { - if ((args == null) || (args.length == 0)) { - logger.warn("Expected args: minInterline maxInterline"); - - return; - } - - final int min = Integer.decode(args[0]); - final int max = (args.length > 1) ? Integer.decode(args[1]) : min; - - for (int interline = min; interline <= max; interline++) { - for (Shape shape : ShapeSet.getTemplateNotes(null)) { - new ShapeDescriptor(shape, interline); - } - } - } - //----------// // evaluate // //----------// @@ -204,6 +174,11 @@ public Rectangle getBounds (Rectangle symBox) //-----------// // getHeight // //-----------// + /** + * Report symbol height. + * + * @return symbol height + */ public int getHeight () { return height; @@ -212,6 +187,12 @@ public int getHeight () //-----------// // getOffset // //-----------// + /** + * Report the offset for desired anchor. + * + * @param anchor desired anchor + * @return point offset + */ public Point getOffset (Anchored.Anchor anchor) { return template.getOffset(anchor); @@ -220,6 +201,11 @@ public Point getOffset (Anchored.Anchor anchor) //----------// // getShape // //----------// + /** + * Report the related shape. + * + * @return related shape + */ public Shape getShape () { return shape; @@ -247,6 +233,11 @@ public Rectangle getSymbolBoundsAt (int x, //-------------// // getTemplate // //-------------// + /** + * Report the related template. + * + * @return related template + */ public Template getTemplate () { return template; @@ -274,6 +265,11 @@ public Rectangle getTemplateBoundsAt (int x, //----------// // getWidth // //----------// + /** + * Report the symbol width. + * + * @return symbol width + */ public int getWidth () { return width; @@ -294,123 +290,6 @@ public String toString () return sb.toString(); } - //------------------// - // computeDistances // - //------------------// - /** - * Compute all distances to nearest foreground pixel. - * For this we work as if there was a foreground rectangle right around the image. - * Similarly, the non-relevant pixels are assumed to be foreground. - * This is to allow the detection of reliable background key points. - * - * @param img the source image - * @param shape the template shape - * @return the table of distances to foreground - */ - private static DistanceTable computeDistances (BufferedImage img, - Shape shape) - { - // Retrieve foreground pixels - final int width = img.getWidth(); - final int height = img.getHeight(); - final boolean[][] fore = new boolean[width + 2][height + 2]; - - // Fill with img foreground pixels and irrelevant pixels - for (int y = 1; y < (height + 1); y++) { - for (int x = 1; x < (width + 1); x++) { - Color pix = new Color(img.getRGB(x - 1, y - 1), true); - - if (pix.equals(Color.BLACK) || (pix.getAlpha() == 0)) { - fore[x][y] = true; - } - } - } - - // Surround with a rectangle of foreground pixels - for (int y = 0; y < (height + 2); y++) { - fore[0][y] = true; - fore[width + 1][y] = true; - } - - for (int x = 0; x < (width + 2); x++) { - fore[x][0] = true; - fore[x][height + 1] = true; - } - - // Compute template distance transform - final DistanceTable distances = new ChamferDistance.Short().compute(fore); - - if (logger.isDebugEnabled()) { - distances.dump(shape + " distances"); - } - - // Trim the distance table of its surrounding rectangle - final Rectangle roi = new Rectangle(1, 1, width, height); - final DistanceTable trimmed = (DistanceTable) distances.getView(roi); - - return trimmed; - } - - //---------// - // getCode // - //---------// - private static int getCode (Shape shape) - { - switch (shape) { - case NOTEHEAD_BLACK: - case NOTEHEAD_BLACK_SMALL: - return 207; - - case NOTEHEAD_VOID: - case NOTEHEAD_VOID_SMALL: - return 250; - - case WHOLE_NOTE: - case WHOLE_NOTE_SMALL: - return 119; - } - - logger.error(shape + " is not supported!"); - - return 0; - } - - //--------------// - // getKeyPoints // - //--------------// - /** - * Build the collection of key points to be used for matching tests. - * These are the locations where the image distance value will be checked against the recorded - * template distance value. - * - * @param img the template source image - * @param distances the template distances (extended on each direction) - * @return the collection of key locations, with their corresponding distance value - */ - private static List getKeyPoints (BufferedImage img, - DistanceTable distances) - { - List keyPoints = new ArrayList(); - - for (int y = 0, h = img.getHeight(); y < h; y++) { - for (int x = 0, w = img.getWidth(); x < w; x++) { - int pix = img.getRGB(x, y); - - // We consider only relevant pixels - if (pix != IRRELEVANT) { - // For hole, use *NEGATIVE* dist to nearest foreground - // For background, use dist to nearest foreground - // For foreground, dist to nearest foreground is 0 by definition - final int val = distances.getValue(x, y); - final int d = (pix == HOLE) ? (-val) : ((pix == BACK) ? val : 0); - keyPoints.add(new PixelDistance(x, y, d)); - } - } - } - - return keyPoints; - } - //------------// // addAnchors // //------------// @@ -473,7 +352,7 @@ private void addHoles (BufferedImage img, { if (shapesWithHoles.contains(shape)) { // Identify holes - final List holeSeeds = new ArrayList(); + final List holeSeeds = new ArrayList<>(); // We have no ledger, just a big hole in the symbol center holeSeeds.add(new Point(box.x + (box.width / 2), box.y + (box.height / 2))); @@ -567,7 +446,7 @@ private Template createTemplate (Shape shape, final List keyPoints = getKeyPoints(img, distances); // Generate the template instance - Template template = new Template( + Template tpl = new Template( shape, pointSize, symbol, @@ -577,15 +456,15 @@ private Template createTemplate (Shape shape, symbolBounds); // Add specific anchor points, if any - addAnchors(template, img); + addAnchors(tpl, img); // Store a copy on disk for visual check? if (constants.keepTemplates.isSet()) { - BufferedImage output = template.buildDecoratedImage(img); + BufferedImage output = tpl.buildDecoratedImage(img); ImageUtil.saveOnDisk(output, shape + ".tpl" + pointSize); } - return template; + return tpl; } //----------------------// @@ -630,7 +509,7 @@ private void flagIrrelevantPixels (BufferedImage img, //------------// private Set getBorders (BufferedImage img) { - final Set borders = new LinkedHashSet(); + final Set borders = new LinkedHashSet<>(); for (int y = 0, h = img.getHeight(); y < h; y++) { for (int x = 0, w = img.getWidth(); x < w; x++) { @@ -876,14 +755,153 @@ private Rectangle realisticBounds (Rectangle sym, return new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1); } - //~ Inner Classes ------------------------------------------------------------------------------ + //------// + // main // + //------// + /** + * An entry point to allow generation and visual check of a bunch of templates. + * + * @param args minimum and maximum interline values + */ + public static void main (String... args) + { + if ((args == null) || (args.length == 0)) { + logger.warn("Expected args: minInterline maxInterline"); + + return; + } + + final int min = Integer.decode(args[0]); + final int max = (args.length > 1) ? Integer.decode(args[1]) : min; + + for (int interline = min; interline <= max; interline++) { + for (Shape shape : ShapeSet.getTemplateNotes(null)) { + new ShapeDescriptor(shape, interline); + } + } + } + + //------------------// + // computeDistances // + //------------------// + /** + * Compute all distances to nearest foreground pixel. + * For this we work as if there was a foreground rectangle right around the image. + * Similarly, the non-relevant pixels are assumed to be foreground. + * This is to allow the detection of reliable background key points. + * + * @param img the source image + * @param shape the template shape + * @return the table of distances to foreground + */ + private static DistanceTable computeDistances (BufferedImage img, + Shape shape) + { + // Retrieve foreground pixels + final int width = img.getWidth(); + final int height = img.getHeight(); + final boolean[][] fore = new boolean[width + 2][height + 2]; + + // Fill with img foreground pixels and irrelevant pixels + for (int y = 1; y < (height + 1); y++) { + for (int x = 1; x < (width + 1); x++) { + Color pix = new Color(img.getRGB(x - 1, y - 1), true); + + if (pix.equals(Color.BLACK) || (pix.getAlpha() == 0)) { + fore[x][y] = true; + } + } + } + + // Surround with a rectangle of foreground pixels + for (int y = 0; y < (height + 2); y++) { + fore[0][y] = true; + fore[width + 1][y] = true; + } + + for (int x = 0; x < (width + 2); x++) { + fore[x][0] = true; + fore[x][height + 1] = true; + } + + // Compute template distance transform + final DistanceTable distances = new ChamferDistance.Short().compute(fore); + + if (logger.isDebugEnabled()) { + distances.dump(shape + " distances"); + } + + // Trim the distance table of its surrounding rectangle + final Rectangle roi = new Rectangle(1, 1, width, height); + final DistanceTable trimmed = (DistanceTable) distances.getView(roi); + + return trimmed; + } + + //---------// + // getCode // + //---------// + private static int getCode (Shape shape) + { + switch (shape) { + case NOTEHEAD_BLACK: + case NOTEHEAD_BLACK_SMALL: + return 207; + + case NOTEHEAD_VOID: + case NOTEHEAD_VOID_SMALL: + return 250; + + case WHOLE_NOTE: + case WHOLE_NOTE_SMALL: + return 119; + } + + logger.error(shape + " is not supported!"); + + return 0; + } + + //--------------// + // getKeyPoints // + //--------------// + /** + * Build the collection of key points to be used for matching tests. + * These are the locations where the image distance value will be checked against the recorded + * template distance value. + * + * @param img the template source image + * @param distances the template distances (extended on each direction) + * @return the collection of key locations, with their corresponding distance value + */ + private static List getKeyPoints (BufferedImage img, + DistanceTable distances) + { + List keyPoints = new ArrayList<>(); + for (int y = 0, h = img.getHeight(); y < h; y++) { + for (int x = 0, w = img.getWidth(); x < w; x++) { + int pix = img.getRGB(x, y); + + // We consider only relevant pixels + if (pix != IRRELEVANT) { + // For hole, use *NEGATIVE* dist to nearest foreground + // For background, use dist to nearest foreground + // For foreground, dist to nearest foreground is 0 by definition + final int val = distances.getValue(x, y); + final int d = (pix == HOLE) ? (-val) : ((pix == BACK) ? val : 0); + keyPoints.add(new PixelDistance(x, y, d)); + } + } + } + return keyPoints; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean keepTemplates = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/image/SheetCounter.java b/src/main/org/audiveris/omr/image/SheetCounter.java index f9bd75ba1..3cc22ee4f 100644 --- a/src/main/org/audiveris/omr/image/SheetCounter.java +++ b/src/main/org/audiveris/omr/image/SheetCounter.java @@ -23,7 +23,6 @@ import org.audiveris.omr.OMR; import org.audiveris.omr.image.ImageLoading.Loader; -import org.audiveris.omr.sheet.BasicBook; import org.audiveris.omr.sheet.Book; import org.kohsuke.args4j.Argument; @@ -45,7 +44,6 @@ */ public class SheetCounter { - //~ Methods ------------------------------------------------------------------------------------ /** * Main entry point. @@ -74,7 +72,7 @@ public static void main (String[] args) try { if (str.endsWith(OMR.BOOK_EXTENSION)) { - Book book = BasicBook.loadBook(path); + Book book = Book.loadBook(path); int all = book.getStubs().size(); int valid = book.getValidStubs().size(); printResult(all, valid, path); @@ -97,7 +95,6 @@ private static void printResult (int all, System.out.println(String.format("%3d %3d %s", all, valid, path)); } - //~ Inner Classes ------------------------------------------------------------------------------ ///private static void result() //------------// // Parameters // @@ -107,14 +104,12 @@ private static void printResult (int all, */ public static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ /** Final arguments, with optional "--" separator. */ @Argument @Option(name = "--", handler = StopOptionHandler.class) - List arguments = new ArrayList(); + List arguments = new ArrayList<>(); - //~ Constructors --------------------------------------------------------------------------- private Parameters () { } diff --git a/src/main/org/audiveris/omr/image/SourceWrapper.java b/src/main/org/audiveris/omr/image/SourceWrapper.java index 28f880cee..a55fe99cc 100644 --- a/src/main/org/audiveris/omr/image/SourceWrapper.java +++ b/src/main/org/audiveris/omr/image/SourceWrapper.java @@ -31,12 +31,10 @@ public class SourceWrapper implements PixelSource { - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying pixel source. */ protected final ByteProcessor source; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SourceWrapper object. * @@ -47,7 +45,6 @@ public SourceWrapper (ByteProcessor source) this.source = source; } - //~ Methods ------------------------------------------------------------------------------------ //-----// // get // //-----// @@ -58,7 +55,6 @@ public int get (int x, return source.get(x, y); } - // //-----------// // getHeight // //-----------// diff --git a/src/main/org/audiveris/omr/image/StructureElement.java b/src/main/org/audiveris/omr/image/StructureElement.java index 5aca17783..b079d9d47 100644 --- a/src/main/org/audiveris/omr/image/StructureElement.java +++ b/src/main/org/audiveris/omr/image/StructureElement.java @@ -29,18 +29,23 @@ /** * Class {@code StructureElement} * - * @author Hervé Bitteur + * @author ? */ public class StructureElement implements MorphoConstants { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(StructureElement.class); + /** Epsilon value meant for equality testing: {@value}. */ + private static final double EPSILON = 1E-5; + static final String EOL = System.getProperty("line.separator"); - //~ Instance fields ---------------------------------------------------------------------------- + public int type = FREE; + + public boolean offsetmodified = false; + private int[] mask; private int width = 1; @@ -49,17 +54,12 @@ public class StructureElement private double radius = 0; - private int[][] vect; + private final int[][] vect; private int[] offset = OFFSET0; private int shift = 1; - public int type = FREE; - - public boolean offsetmodified = false; - - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new StructureElement object. * @@ -181,7 +181,6 @@ public StructureElement (int type, vect = calcVect(mask, width); } - //~ Methods ------------------------------------------------------------------------------------ public int[] Delta (int[] offset) { int[] astrel = this.T(offset); @@ -367,7 +366,7 @@ public double getDistance (int[] X, } case DIAMOND: { - d = (double) (Math.abs(X[0]) + Math.abs(X[1])); + d = (Math.abs(X[0]) + Math.abs(X[1])); break; } @@ -395,6 +394,11 @@ public int[] getMask () return mask; } + public void setMask (int[] amask) + { + this.mask = amask; + } + public int getMaskAt (int index) { if (index <= mask.length) { @@ -437,6 +441,12 @@ public int[] getOffset () return this.offset; } + public void setOffset (int[] offset) + { + this.offset = offset; + offsetmodified = true; + } + public double getR () { return radius; @@ -452,6 +462,11 @@ public int getType () return type; } + public void setType (int type) + { + this.type = type; + } + public int[][] getVect () { return vect; //calcVect(this.mask , this.width); @@ -462,40 +477,6 @@ public int getWidth () return width; } - public void setMask (int[] amask) - { - this.mask = amask; - } - - public void setOffset (int[] offset) - { - this.offset = offset; - offsetmodified = true; - } - - public void setType (int type) - { - this.type = type; - } - - double getNum (StringTokenizer st) - { - Double d; - String token = st.nextToken(); - - try { - d = new Double(token); - } catch (NumberFormatException e) { - d = null; - } - - if (d != null) { - return (d.doubleValue()); - } else { - return 0.0; - } - } - private int[] createDiamondMask (int shift, double radius, int[] offset) @@ -548,13 +529,13 @@ private int[] createLineMask (int shift, int sz = this.width * this.height; int[] mask = new int[sz]; - if ((alpha == 0) || (alpha == Math.PI)) { + if ((Math.abs(alpha) < EPSILON) || (Math.abs(alpha - Math.PI) < EPSILON)) { //for (int i=0;i - * There are several topics to consider in a template specification:

                        + * There are several topics to consider in a template specification: + *
                        *
                        Base shape
                        *
                        Supported shapes are NOTEHEAD_BLACK, NOTEHEAD_VOID and WHOLE_NOTE and possibly their small * (cue) counterparts
                        @@ -74,7 +76,6 @@ public class Template implements Anchored { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -83,7 +84,6 @@ public class Template /** Ratio applied to small symbols (cue / grace). */ public static final double smallRatio = constants.smallRatio.getValue(); - //~ Instance fields ---------------------------------------------------------------------------- /** Template shape. */ private final Shape shape; @@ -110,9 +110,8 @@ public class Template * An offset is defined as the translation from template upper left corner * to the precise anchor location in the symbol. */ - private final Map offsets = new EnumMap(Anchor.class); + private final Map offsets = new EnumMap<>(Anchor.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Template object with a provided set of points. * @@ -135,13 +134,12 @@ public Template (Shape shape, this.shape = shape; this.pointSize = pointSize; this.symbol = symbol; - this.keyPoints = new ArrayList(keyPoints); + this.keyPoints = new ArrayList<>(keyPoints); this.width = width; this.height = height; this.symbolBounds = symbolBounds; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // addAnchor // //-----------// @@ -200,7 +198,7 @@ public BufferedImage buildDecoratedImage (BufferedImage src) g.drawRoundRect(pt.x * r, pt.y * r, r, r, r / 2, r / 2); final Anchor anchor = entry.getKey(); - final String str = anchor.abbreviation().toLowerCase(); + final String str = anchor.abbreviation().toLowerCase(Locale.US); final TextLayout layout = textFont.layout(str); if ((anchor == Anchor.LEFT_STEM) || (anchor == Anchor.RIGHT_STEM)) { @@ -256,13 +254,14 @@ public BufferedImage buildDecoratedImage (BufferedImage src) //------// // dump // //------// + /** + * Print this template on standard output. + */ public void dump () { int[][] vals = new int[width][height]; - for (int x = 0; x < vals.length; x++) { - int[] col = vals[x]; - + for (int[] col : vals) { for (int y = 0; y < col.length; y++) { col[y] = -1; } @@ -272,8 +271,6 @@ public void dump () vals[pix.x][pix.y] = (int) Math.rint(pix.d); } - System.out.println("Template " + shape + ":"); - final String yFormat = TableUtil.printAbscissae(width, height, 3); for (int y = 0; y < height; y++) { @@ -339,7 +336,8 @@ public double evaluate (int x, // pix.d < 0 for expected hole, expected negative distance to nearest foreground // pix.d == 0 for expected foreground, 0 distance // pix.d > 0 for expected background, expected distance to nearest foreground - double weight = (pix.d == 0) ? foreWeight : ((pix.d > 0) ? backWeight : holeWeight); + double weight = (pix.d == 0) ? foreWeight + : ((pix.d > 0) ? backWeight : holeWeight); double expected = (pix.d == 0) ? 0 : 1; double actual = (actualDist == 0) ? 0 : 1; double dist = Math.abs(actual - expected); @@ -456,7 +454,7 @@ public List getForegroundPixels (Rectangle box, { final int imgWidth = image.getWidth(); final int imgHeight = image.getHeight(); - final List fores = new ArrayList(); + final List fores = new ArrayList<>(); for (PixelDistance pix : keyPoints) { if (pix.d != 0) { @@ -542,14 +540,6 @@ public int getHeight () return height; } - //--------------// - // getPointSize // - //--------------// - public int getPointSize () - { - return pointSize; - } - //--------------// // getKeyPoints // //--------------// @@ -576,6 +566,19 @@ public Point getOffset (Anchor anchor) return offset; } + //--------------// + // getPointSize // + //--------------// + /** + * Report the pointSize for this template. + * + * @return pointSize value + */ + public int getPointSize () + { + return pointSize; + } + //----------// // getShape // //----------// @@ -644,6 +647,56 @@ public int getWidth () return width; } + //----------// + // toString // + //----------// + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("{Template"); + + sb.append(" ").append(shape); + + sb.append(" w:").append(width).append(",h:").append(height); + + if ((symbolBounds.width != width) || (symbolBounds.height != height)) { + sb.append(" sym:").append(symbolBounds); + } + + for (Entry entry : offsets.entrySet()) { + sb.append(" ").append(entry.getKey()).append(":(").append(entry.getValue().x).append( + ",").append(entry.getValue().y).append(")"); + } + + sb.append(" keyPoints:").append(keyPoints.size()); + + sb.append("}"); + + return sb.toString(); + } + + //-----------// + // upperLeft // + //-----------// + private Point upperLeft (int x, + int y, + Anchor anchor) + { + // Offsets to apply to location? + if (anchor != null) { + Point offset = getOffset(anchor); + + if (offset != null) { + x -= offset.x; + y -= offset.y; + } else { + logger.error("No {} anchor defined for {} template", anchor, shape); + } + } + + return new Point(x, y); + } + //----------// // impactOf // //----------// @@ -699,64 +752,12 @@ public static double reallyBadDistance () return constants.reallyBadDistance.getValue(); } - //----------// - // toString // - //----------// - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("{Template"); - - sb.append(" ").append(shape); - - sb.append(" w:").append(width).append(",h:").append(height); - - if ((symbolBounds.width != width) || (symbolBounds.height != height)) { - sb.append(" sym:").append(symbolBounds); - } - - for (Entry entry : offsets.entrySet()) { - sb.append(" ").append(entry.getKey()).append(":(").append(entry.getValue().x).append( - ",").append(entry.getValue().y).append(")"); - } - - sb.append(" keyPoints:").append(keyPoints.size()); - - sb.append("}"); - - return sb.toString(); - } - - //-----------// - // upperLeft // - //-----------// - private Point upperLeft (int x, - int y, - Anchor anchor) - { - // Offsets to apply to location? - if (anchor != null) { - Point offset = getOffset(anchor); - - if (offset != null) { - x -= offset.x; - y -= offset.y; - } else { - logger.error("No {} anchor defined for {} template", anchor, shape); - } - } - - return new Point(x, y); - } - - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio smallRatio = new Constant.Ratio( 0.67, diff --git a/src/main/org/audiveris/omr/image/TemplateFactory.java b/src/main/org/audiveris/omr/image/TemplateFactory.java index 8ed613438..056dbcc2c 100644 --- a/src/main/org/audiveris/omr/image/TemplateFactory.java +++ b/src/main/org/audiveris/omr/image/TemplateFactory.java @@ -39,34 +39,21 @@ */ public class TemplateFactory { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TemplateFactory.class); /** Singleton. */ private static final TemplateFactory INSTANCE = new TemplateFactory(); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Catalog of all templates already allocated, mapped by point size. */ private final Map allSizes; - //~ Constructors ------------------------------------------------------------------------------- /** * (Private) Creates the singleton object. */ private TemplateFactory () { - allSizes = new HashMap(); - } - - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // getInstance // - //-------------// - public static TemplateFactory getInstance () - { - return INSTANCE; + allSizes = new HashMap<>(); } //------------// @@ -95,35 +82,54 @@ public Catalog getCatalog (int pointSize) return catalog; } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------// + // getInstance // + //-------------// + /** + * Report this singleton instance. + * + * @return the TemplateFactory single instance + */ + public static TemplateFactory getInstance () + { + return INSTANCE; + } + //---------// // Catalog // //---------// /** - * Handles all templates for a given interline value. + * Handles all templates for a given pointSize value. */ public static class Catalog { - //~ Instance fields ------------------------------------------------------------------------ /** Point size value for this catalog. */ final int pointSize; /** Map of all descriptors for this catalog. */ - final Map descriptors = new EnumMap( - Shape.class); + final Map descriptors = new EnumMap<>(Shape.class); - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a {@code Catalog} object. + * + * @param pointSize provided pointSize value + */ public Catalog (int pointSize) { this.pointSize = pointSize; buildAllTemplates(); } - //~ Methods -------------------------------------------------------------------------------- //---------------// // getDescriptor // //---------------// + /** + * Report the descriptor for a given shape. + * + * @param shape given shape + * @return corresponding descriptor (not null, since all have been constructed) + */ public ShapeDescriptor getDescriptor (Shape shape) { return descriptors.get(shape); @@ -132,6 +138,12 @@ public ShapeDescriptor getDescriptor (Shape shape) //-------------// // getTemplate // //-------------// + /** + * Report the template for the desired shape + * + * @param shape desired shape + * @return the template + */ public Template getTemplate (Shape shape) { ShapeDescriptor descriptor = descriptors.get(shape); diff --git a/src/main/org/audiveris/omr/image/VerticalFilter.java b/src/main/org/audiveris/omr/image/VerticalFilter.java index d49cfd8aa..1d1c70636 100644 --- a/src/main/org/audiveris/omr/image/VerticalFilter.java +++ b/src/main/org/audiveris/omr/image/VerticalFilter.java @@ -39,6 +39,7 @@ * It uses a vertical window which performs the computation in constant time, provided that the * vertical window always moves to the right. Instead of a whole table of integrals, this class uses * a vertical tile whose width equals the window size, and the height equals the picture height. + *
                        *
                          *                                              +----------------+
                          *                                              |   TILE_WIDTH   |
                        @@ -59,6 +60,7 @@
                          * |                                            c|              d|
                          * +---------------------------------------------+---------------+
                          * 
                        + *

                        * Since only the (1 + WINDOW_SIZE) last columns are relevant, a tile uses a circular buffer to * handle only those columns. *

                        @@ -72,11 +74,9 @@ public class VerticalFilter extends AdaptiveFilter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(VerticalFilter.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Create an adaptive wrapper on a raw pixel source. * @@ -97,8 +97,6 @@ public VerticalFilter (ByteProcessor source, true); } - //~ Inner Classes ------------------------------------------------------------------------------ - // //--------// // MyTile // //--------// @@ -108,14 +106,12 @@ public VerticalFilter (ByteProcessor source, private class MyTile extends Tile { - //~ Constructors --------------------------------------------------------------------------- - public MyTile (boolean squared) + MyTile (boolean squared) { super(2 + (2 * HALF_WINDOW_SIZE), source.getHeight(), squared); } - //~ Methods -------------------------------------------------------------------------------- @Override protected void shiftTile (int x2) { diff --git a/src/main/org/audiveris/omr/image/WatershedGrayLevel.java b/src/main/org/audiveris/omr/image/WatershedGrayLevel.java index ee218c83a..1642e6705 100644 --- a/src/main/org/audiveris/omr/image/WatershedGrayLevel.java +++ b/src/main/org/audiveris/omr/image/WatershedGrayLevel.java @@ -14,7 +14,6 @@ */ public class WatershedGrayLevel { - //~ Static fields/initializers ----------------------------------------------------------------- /** Number of gray level values. */ private static final int GRAYLEVEL = 256; @@ -28,9 +27,8 @@ public class WatershedGrayLevel /** Ordinate offsets of the 8 neighbors, clockwise. */ private static final int[] dy8 = new int[]{-1, -1, -1, 0, 1, 1, 1, 0}; - //~ Instance fields ---------------------------------------------------------------------------- /** Original gray-level image, organized row per row. */ - private Table image; + private final Table image; /** Image width. */ private final int width; @@ -52,7 +50,6 @@ public class WatershedGrayLevel /** List of pixels (one per level) to process. */ private ListOfPixels[] exploreList; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new WatershedGrayLevel object. * @@ -79,7 +76,6 @@ public WatershedGrayLevel (Table image, } } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // getRegionCount // //----------------// @@ -170,7 +166,6 @@ private void clear () exploreList = null; } - // // private void dumpRmap () // { // System.out.println("rmap:"); @@ -306,7 +301,6 @@ private Pixel nextPixel (int level, return null; } - //~ Inner Classes ------------------------------------------------------------------------------ //--------------// // ListOfPixels // //--------------// @@ -316,6 +310,12 @@ private Pixel nextPixel (int level, private static class ListOfPixels extends LinkedList { + + @Override + public Object clone () + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } //-------// @@ -326,7 +326,6 @@ private static class ListOfPixels */ private static class Pixel { - //~ Instance fields ------------------------------------------------------------------------ int x; @@ -334,17 +333,15 @@ private static class Pixel int level; - //~ Constructors --------------------------------------------------------------------------- - public Pixel (int x, - int y, - int level) + Pixel (int x, + int y, + int level) { this.x = x; this.y = y; this.level = level; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { diff --git a/src/main/org/audiveris/omr/image/jai/JaiDewarper.java b/src/main/org/audiveris/omr/image/jai/JaiDewarper.java index 36b9952d6..4d11620e4 100644 --- a/src/main/org/audiveris/omr/image/jai/JaiDewarper.java +++ b/src/main/org/audiveris/omr/image/jai/JaiDewarper.java @@ -34,18 +34,16 @@ /** * Class {@code JaiDewarper} is meant to keep JAI-based de-warping features separate - * from the rest of Audiveris application, and thus saving on jar download. + * from the rest of Audiveris application, and thus save on jar download. * * @author Hervé Bitteur */ public class JaiDewarper { - //~ Instance fields ---------------------------------------------------------------------------- /** The dewarp grid. */ private Warp dewarpGrid; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new JaiDewarper object. */ @@ -53,11 +51,18 @@ public JaiDewarper () { } - //~ Methods ------------------------------------------------------------------------------------ - // //----------------// // createWarpGrid // //----------------// + /** + * @param xStart starting abscissa + * @param xStep cell width + * @param xNumCells horizontal number of cells + * @param yStart starting ordinate + * @param yStep cell height + * @param yNumCells vertical number of cells + * @param warpPositions all source (warped) positions as a flat array of (x, y) float values + */ public void createWarpGrid (int xStart, int xStep, int xNumCells, @@ -79,6 +84,12 @@ public void createWarpGrid (int xStart, //-------------// // dewarpImage // //-------------// + /** + * Actually de-warp the provided image. + * + * @param image the provided (warped) image + * @return a de-warped version of input image + */ public BufferedImage dewarpImage (BufferedImage image) { assert dewarpGrid != null : "dewarpGrid not defined"; diff --git a/src/main/org/audiveris/omr/image/jai/JaiLoader.java b/src/main/org/audiveris/omr/image/jai/JaiLoader.java index 09b5045c7..53f4b8a7c 100644 --- a/src/main/org/audiveris/omr/image/jai/JaiLoader.java +++ b/src/main/org/audiveris/omr/image/jai/JaiLoader.java @@ -40,11 +40,9 @@ */ public abstract class JaiLoader { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(JaiLoader.class); - //~ Constructors ------------------------------------------------------------------------------- // /** A future which reflects whether JAI has been initialized * */ // private static final Future loading = OmrExecutors.getCachedLowExecutor() // .submit( @@ -63,7 +61,6 @@ private JaiLoader () { } - //~ Methods ------------------------------------------------------------------------------------ //---------// // loadJAI // //---------// @@ -81,7 +78,7 @@ public static SortedMap loadJAI (File imgFile) try { if ((image.getWidth() > 0) && (image.getHeight() > 0)) { - SortedMap images = new TreeMap(); + SortedMap images = new TreeMap<>(); images.put(1, image); return images; diff --git a/src/main/org/audiveris/omr/lag/BasicLag.java b/src/main/org/audiveris/omr/lag/BasicLag.java index d5aaa58d8..c7a6d25ea 100644 --- a/src/main/org/audiveris/omr/lag/BasicLag.java +++ b/src/main/org/audiveris/omr/lag/BasicLag.java @@ -44,11 +44,9 @@ public class BasicLag extends BasicIndex

                        implements Lag { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(BasicLag.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Orientation of the lag. */ private final Orientation orientation; @@ -58,7 +56,6 @@ public class BasicLag /** Lag name. */ private final String name; - //~ Constructors ------------------------------------------------------------------------------- /** * Constructor with specified orientation * @@ -75,7 +72,6 @@ public BasicLag (String name, logger.debug("Created lag {}", name); } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // addRunTable // //-------------// diff --git a/src/main/org/audiveris/omr/lag/BasicSection.java b/src/main/org/audiveris/omr/lag/BasicSection.java index 507dbb405..0fd9d9864 100644 --- a/src/main/org/audiveris/omr/lag/BasicSection.java +++ b/src/main/org/audiveris/omr/lag/BasicSection.java @@ -71,12 +71,9 @@ public class BasicSection extends AbstractEntity implements Section { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - BasicSection.class); + private static final Logger logger = LoggerFactory.getLogger(BasicSection.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Position of first run */ @XmlAttribute(name = "first-pos") protected int firstPos; @@ -87,7 +84,7 @@ public class BasicSection /** The collection of runs that make up the section */ @XmlElement(name = "run") - protected final List runs = new ArrayList(); + protected final List runs = new ArrayList<>(); /** Containing lag, if any. */ protected Lag lag; @@ -110,7 +107,6 @@ public class BasicSection /** Approximating oriented line for this section */ protected Line orientedLine; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BasicSection. * @@ -140,28 +136,6 @@ public BasicSection (DynamicSection ds) orientedLine = ds.getOrientedLine(); } - //~ Methods ------------------------------------------------------------------------------------ - //---------------// - // allocateTable // - //---------------// - /** - * For basic print out, allocate a drawing table, to be later filled - * with section pixels - * - * @param box the limits of the drawing table - * @return the table ready to be filled - */ - public static char[][] allocateTable (Rectangle box) - { - char[][] table = new char[box.height + 1][box.width + 1]; - - for (int i = 0; i < table.length; i++) { - Arrays.fill(table[i], ' '); - } - - return table; - } - //----------// // contains // //----------// @@ -316,40 +290,6 @@ public void drawAscii () drawingOfTable(table, box); } - //----------------// - // drawingOfTable // - //----------------// - /** - * Printout the filled drawing table - * - * @param table the filled table - * @param box the table limits in the image - * @return the drawing as a string - */ - public static String drawingOfTable (char[][] table, - Rectangle box) - { - StringBuilder sb = new StringBuilder(); - sb.append(String.format("%n")); - - sb.append(String.format("xMin=%d, xMax=%d%n", box.x, (box.x + box.width) - 1)); - sb.append(String.format("yMin=%d, yMax=%d%n", box.y, (box.y + box.height) - 1)); - - for (int iy = 0; iy < table.length; iy++) { - sb.append(String.format("%d:", iy + box.y)); - - char[] line = table[iy]; - - for (int ix = 0; ix < line.length; ix++) { - sb.append(line[ix]); - } - - sb.append(String.format("%n")); - } - - return sb.toString(); - } - //--------// // equals // //--------// @@ -471,7 +411,7 @@ public Point getAreaCenter () @Override public double getAspect (Orientation orientation) { - return (double) getLength(orientation) / (double) getThickness(orientation); + return getLength(orientation) / (double) getThickness(orientation); } //-----------// @@ -544,6 +484,19 @@ public Lag getLag () return lag; } + //--------// + // setLag // + //--------// + @Override + public void setLag (Lag lag) + { + this.lag = lag; + + if (lag != null) { + orientation = lag.getOrientation(); + } + } + //------------// // getLastPos // //------------// @@ -670,9 +623,7 @@ public Point getRectangleCentroid (Rectangle absRoi) cumulate(barycenter, absRoi); if (barycenter.getWeight() != 0) { - return new Point( - (int) Math.rint(barycenter.getX()), - (int) Math.rint(barycenter.getY())); + return new Point((int) Math.rint(barycenter.getX()), (int) Math.rint(barycenter.getY())); } else { return null; } @@ -770,8 +721,8 @@ public boolean intersects (java.awt.Shape shape) for (Run run : runs) { final int start = run.getStart(); - final Rectangle runBox = (orientation == HORIZONTAL) - ? new Rectangle(start, pos, run.getLength(), 1) + final Rectangle runBox = (orientation == HORIZONTAL) ? new Rectangle(start, pos, run + .getLength(), 1) : new Rectangle(pos, start, 1, run.getLength()); if (shape.intersects(runBox)) { @@ -876,24 +827,6 @@ public boolean renderSelected (Graphics g) } } - //--------// - // setLag // - //--------// - /** - * (package access from lag?) - * - * @param lag the containing lag - */ - @Override - public void setLag (Lag lag) - { - this.lag = lag; - - if (lag != null) { - orientation = lag.getOrientation(); - } - } - //---------// // touches // //---------// @@ -912,8 +845,8 @@ public boolean touches (Section that) for (Run run : runs) { final int start = run.getStart(); - final Rectangle r1 = (orientation == HORIZONTAL) - ? new Rectangle(start, pos, run.getLength(), 1) + final Rectangle r1 = (orientation == HORIZONTAL) ? new Rectangle(start, pos, run + .getLength(), 1) : new Rectangle(pos, start, 1, run.getLength()); if (thatFatBox.intersects(r1)) { @@ -923,8 +856,8 @@ public boolean touches (Section that) for (Run thatRun : that.getRuns()) { final int thatStart = thatRun.getStart(); final int thatLength = thatRun.getLength(); - final Rectangle r2 = (that.getOrientation() == HORIZONTAL) - ? new Rectangle(thatStart, thatPos, thatLength, 1) + final Rectangle r2 = (that.getOrientation() == HORIZONTAL) ? new Rectangle( + thatStart, thatPos, thatLength, 1) : new Rectangle(thatPos, thatStart, 1, thatLength); if (GeoUtil.touch(r1, r2)) { @@ -973,6 +906,11 @@ public void translateAbsolute (int dx, //---------------------// // computeOrientedLine // //---------------------// + /** + * Compute the oriented approximating line. + * + * @return the oriented line + */ protected Line computeOrientedLine () { // Compute the section line @@ -1002,7 +940,59 @@ protected String internals () return orientation.isVertical() ? "V" : "H"; } - //~ Inner Classes ------------------------------------------------------------------------------ + //---------------// + // allocateTable // + //---------------// + /** + * For basic print out, allocate a drawing table, to be later filled + * with section pixels + * + * @param box the limits of the drawing table + * @return the table ready to be filled + */ + public static char[][] allocateTable (Rectangle box) + { + char[][] table = new char[box.height + 1][box.width + 1]; + for (char[] table1 : table) { + Arrays.fill(table1, ' '); + } + return table; + } + + //----------------// + // drawingOfTable // + //----------------// + /** + * Printout the filled drawing table + * + * @param table the filled table + * @param box the table limits in the image + * @return the drawing as a string + */ + public static String drawingOfTable (char[][] table, + Rectangle box) + { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("%n")); + + sb.append(String.format("xMin=%d, xMax=%d%n", box.x, (box.x + box.width) - 1)); + sb.append(String.format("yMin=%d, yMax=%d%n", box.y, (box.y + box.height) - 1)); + + for (int iy = 0; iy < table.length; iy++) { + sb.append(String.format("%d:", iy + box.y)); + + char[] line = table[iy]; + + for (int ix = 0; ix < line.length; ix++) { + sb.append(line[ix]); + } + + sb.append(String.format("%n")); + } + + return sb.toString(); + } + //---------// // Adapter // //---------// @@ -1012,7 +1002,6 @@ protected String internals () public static class Adapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public BasicSection marshal (Section s) diff --git a/src/main/org/audiveris/omr/lag/DynamicSection.java b/src/main/org/audiveris/omr/lag/DynamicSection.java index 010e5b2a2..e82bfbdc1 100644 --- a/src/main/org/audiveris/omr/lag/DynamicSection.java +++ b/src/main/org/audiveris/omr/lag/DynamicSection.java @@ -1,387 +1,400 @@ -//------------------------------------------------------------------------------------------------// -// // -// D y n a m i c S e c t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.lag; - -import org.audiveris.omr.math.Line; -import org.audiveris.omr.run.Orientation; -import org.audiveris.omr.run.Run; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Point; -import java.awt.Polygon; -import java.awt.Rectangle; - -/** - * Class {@code DynamicSection} is a section that can evolve by adding runs or - * translating its location. - * - * @author Hervé Bitteur - */ -public class DynamicSection - extends BasicSection -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(DynamicSection.class); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code DynamicSection} object. - * - * @param orientation provided orientation for the section - */ - public DynamicSection (Orientation orientation) - { - super(orientation); - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // append // - //--------// - /** - * Extend a section with the given run. - * This new run is assumed to be contiguous to the current last run of the section, - * no check is performed. - * - * @param run the new last run - */ - public void append (Run run) - { - run = new Run(run); - runs.add(run); - addRun(run); - - logger.debug("Appended {} to {}", run, this); - } - - //-------------// - // getCentroid // - //-------------// - @Override - public Point getCentroid () - { - if (centroid == null) { - centroid = computeCentroid(); - } - - return centroid; - } - - //-------------------// - // getOrientedBounds // - //-------------------// - @Override - public Rectangle getOrientedBounds () - { - if (orientedBounds == null) { - orientedBounds = orientation.oriented(getBounds()); - } - - return orientedBounds; - } - - //-----------------// - // getOrientedLine // - //-----------------// - @Override - public Line getOrientedLine () - { - if ((orientedLine == null) && (getWeight() > 1)) { - orientedLine = computeOrientedLine(); - } - - return orientedLine; - } - - //------------// - // getPolygon // - //------------// - @Override - public Polygon getPolygon () - { - if (polygon == null) { - polygon = computePolygon(); - } - - return polygon; - } - - //-----------// - // getWeight // - //-----------// - @Override - public int getWeight () - { - if (weight == 0) { - computeParameters(); - } - - return weight; - } - - //---------// - // prepend // - //---------// - /** - * Add a run at the beginning rather than at the end of the section. - * - * @param run the new first run - */ - public void prepend (Run run) - { - run = new Run(run); - logger.debug("Prepending {} to {}", run, this); - - firstPos--; - runs.add(0, run); - addRun(run); - - logger.debug("Prepended {}", this); - } - - //-------------// - // setFirstPos // - //-------------// - /** - * Set the position of the first run of the section. - * - * @param firstPos position of the first run, abscissa for a vertical run, - * ordinate for a horizontal run. - */ - public void setFirstPos (int firstPos) - { - this.firstPos = firstPos; - } - - //-----------// - // translate // - //-----------// - /** - * Apply an absolute translation vector to this section. - * - * @param vector the translation vector - */ - public void translate (Point vector) - { - // Get the coord/pos equivalent of dx/dy vector - Point cp = orientation.oriented(vector); - int dc = cp.x; - int dp = cp.y; - - // Apply the needed modifications - firstPos += dp; - - for (Run run : runs) { - run.translate(dc); - } - - // Force update - invalidateCache(); - } - - //--------// - // addRun // - //--------// - /** - * Compute incrementally the cached parameters. - * - * @param run the run to be processed - */ - protected void addRun (Run run) - { - // Invalidate cached data - invalidateCache(); - - // Compute contribution of this run - computeRunContribution(run); - } - - //-----------------// - // computeCentroid // - //-----------------// - protected Point computeCentroid () - { - Point orientedPoint = new Point(0, 0); - int y = firstPos; - - for (Run run : runs) { - final int length = run.getLength(); - orientedPoint.y += (length * (2 * y)); - orientedPoint.x += (length * ((2 * run.getStart()) + length)); - y++; - } - - orientedPoint.x /= (2 * getWeight()); - orientedPoint.y /= (2 * getWeight()); - - return orientation.absolute(orientedPoint); - } - - //-------------------// - // computeParameters // - //-------------------// - protected void computeParameters () - { - // weight & maxRunLength - weight = 0; - maxRunLength = 0; - - // maxRunLength - for (Run run : runs) { - computeRunContribution(run); - } - - // Invalidate cached data - invalidateCache(); - - logger.debug( - "Parameters of {} maxRunLength={} meanRunLength={}" + " weight={}", - this, - maxRunLength, - getMeanRunLength(), - weight); - } - - //----------------// - // computePolygon // - //----------------// - /** - * Compute the arrays of points needed to draw the section runs. - * This is an absolute definition. - * - * @return the created polygon that represents the section geometry - */ - protected Polygon computePolygon () - { - final int maxNb = 1 + (4 * getRunCount()); // Upper value - final int[] xx = new int[maxNb]; - final int[] yy = new int[maxNb]; - int idx = 0; // Current filling index in xx & yy arrays - - if (isVertical()) { - idx = populatePolygon(yy, xx, idx, 1); - idx = populatePolygon(yy, xx, idx, -1); - } else { - idx = populatePolygon(xx, yy, idx, 1); - idx = populatePolygon(xx, yy, idx, -1); - } - - Polygon poly = new Polygon(xx, yy, idx); - - return poly; - } - - //------------------------// - // computeRunContribution // - //------------------------// - protected void computeRunContribution (Run run) - { - final int length = run.getLength(); - weight += length; - maxRunLength = Math.max(maxRunLength, length); - } - - //-----------------// - // invalidateCache // - //-----------------// - protected void invalidateCache () - { - orientedBounds = null; - centroid = null; - polygon = null; - orientedLine = null; - } - - //-----------------// - // populatePolygon // - //-----------------// - /** - * Compute the arrays of points needed to draw the section runs - * - * @param xpoints to receive abscissae - * @param ypoints to receive coordinates - * @param dir direction for browsing runs - * @param index first index available in arrays - * @return last index value - */ - protected int populatePolygon (int[] xpoints, - int[] ypoints, - int index, - int dir) - { - // Precise delimitating points - int runNb = getRunCount(); - int iStart = (dir > 0) ? 0 : (runNb - 1); - int iBreak = (dir > 0) ? runNb : (-1); - int y = (dir > 0) ? getFirstPos() : (getFirstPos() + runNb); - int xPrev = -1; - - for (int i = iStart; i != iBreak; i += dir) { - Run run = runs.get(i); - - // +----------------------------+ - // +--+-------------------------+ - // +----------------------+--+ - // +----------------------+ - // - // Order of the 4 angle points for a run is - // Vertical lag: Horizontal lag: - // 1 2 1 4 - // 4 3 2 3 - int x = (dir > 0) ? run.getStart() : (run.getStop() + 1); - - if (x != xPrev) { - if (xPrev != -1) { - // Insert last vertex - xpoints[index] = xPrev; - ypoints[index] = y; - index++; - } - - // Insert new vertex - xpoints[index] = x; - ypoints[index] = y; - index++; - xPrev = x; - } - - y += dir; - } - - // Complete the sequence, with a new vertex - xpoints[index] = xPrev; - ypoints[index] = y; - index++; - - if (dir < 0) { - // Finish with starting point - xpoints[index] = runs.get(0).getStart(); - ypoints[index] = getFirstPos(); - index++; - } - - return index; - } -} +//------------------------------------------------------------------------------------------------// +// // +// D y n a m i c S e c t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.lag; + +import org.audiveris.omr.math.Line; +import org.audiveris.omr.run.Orientation; +import org.audiveris.omr.run.Run; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Point; +import java.awt.Polygon; +import java.awt.Rectangle; + +/** + * Class {@code DynamicSection} is a section that can evolve by adding runs or + * translating its location. + * + * @author Hervé Bitteur + */ +public class DynamicSection + extends BasicSection +{ + + private static final Logger logger = LoggerFactory.getLogger(DynamicSection.class); + + /** + * Creates a new {@code DynamicSection} object. + * + * @param orientation provided orientation for the section + */ + public DynamicSection (Orientation orientation) + { + super(orientation); + } + + //--------// + // append // + //--------// + /** + * Extend a section with the given run. + * This new run is assumed to be contiguous to the current last run of the section, + * no check is performed. + * + * @param run the new last run + */ + public void append (Run run) + { + run = new Run(run); + runs.add(run); + addRun(run); + + logger.debug("Appended {} to {}", run, this); + } + + //-------------// + // getCentroid // + //-------------// + @Override + public Point getCentroid () + { + if (centroid == null) { + centroid = computeCentroid(); + } + + return centroid; + } + + //-------------------// + // getOrientedBounds // + //-------------------// + @Override + public Rectangle getOrientedBounds () + { + if (orientedBounds == null) { + orientedBounds = orientation.oriented(getBounds()); + } + + return orientedBounds; + } + + //-----------------// + // getOrientedLine // + //-----------------// + @Override + public Line getOrientedLine () + { + if ((orientedLine == null) && (getWeight() > 1)) { + orientedLine = computeOrientedLine(); + } + + return orientedLine; + } + + //------------// + // getPolygon // + //------------// + @Override + public Polygon getPolygon () + { + if (polygon == null) { + polygon = computePolygon(); + } + + return polygon; + } + + //-----------// + // getWeight // + //-----------// + @Override + public int getWeight () + { + if (weight == 0) { + computeParameters(); + } + + return weight; + } + + //---------// + // prepend // + //---------// + /** + * Add a run at the beginning rather than at the end of the section. + * + * @param run the new first run + */ + public void prepend (Run run) + { + run = new Run(run); + logger.debug("Prepending {} to {}", run, this); + + firstPos--; + runs.add(0, run); + addRun(run); + + logger.debug("Prepended {}", this); + } + + //-------------// + // setFirstPos // + //-------------// + /** + * Set the position of the first run of the section. + * + * @param firstPos position of the first run, abscissa for a vertical run, + * ordinate for a horizontal run. + */ + public void setFirstPos (int firstPos) + { + this.firstPos = firstPos; + } + + //-----------// + // translate // + //-----------// + /** + * Apply an absolute translation vector to this section. + * + * @param vector the translation vector + */ + public void translate (Point vector) + { + // Get the coord/pos equivalent of dx/dy vector + Point cp = orientation.oriented(vector); + int dc = cp.x; + int dp = cp.y; + + // Apply the needed modifications + firstPos += dp; + + for (Run run : runs) { + run.translate(dc); + } + + // Force update + invalidateCache(); + } + + //--------// + // addRun // + //--------// + /** + * Compute incrementally the cached parameters. + * + * @param run the run to be processed + */ + protected void addRun (Run run) + { + // Invalidate cached data + invalidateCache(); + + // Compute contribution of this run + computeRunContribution(run); + } + + //-----------------// + // computeCentroid // + //-----------------// + /** + * Compute section centroid. + * + * @return absolute centroid + */ + protected Point computeCentroid () + { + Point orientedPoint = new Point(0, 0); + int y = firstPos; + + for (Run run : runs) { + final int length = run.getLength(); + orientedPoint.y += (length * (2 * y)); + orientedPoint.x += (length * ((2 * run.getStart()) + length)); + y++; + } + + orientedPoint.x /= (2 * getWeight()); + orientedPoint.y /= (2 * getWeight()); + + return orientation.absolute(orientedPoint); + } + + //-------------------// + // computeParameters // + //-------------------// + /** + * Update cached data for this section. + */ + protected void computeParameters () + { + // weight & maxRunLength + weight = 0; + maxRunLength = 0; + + // maxRunLength + for (Run run : runs) { + computeRunContribution(run); + } + + // Invalidate cached data + invalidateCache(); + + logger.debug( + "Parameters of {} maxRunLength={} meanRunLength={}" + " weight={}", + this, + maxRunLength, + getMeanRunLength(), + weight); + } + + //----------------// + // computePolygon // + //----------------// + /** + * Compute the arrays of points needed to draw the section runs. + * This is an absolute definition. + * + * @return the created polygon that represents the section geometry + */ + protected Polygon computePolygon () + { + final int maxNb = 1 + (4 * getRunCount()); // Upper value + final int[] xx = new int[maxNb]; + final int[] yy = new int[maxNb]; + int idx = 0; // Current filling index in xx & yy arrays + + if (isVertical()) { + idx = populatePolygon(yy, xx, idx, 1); + idx = populatePolygon(yy, xx, idx, -1); + } else { + idx = populatePolygon(xx, yy, idx, 1); + idx = populatePolygon(xx, yy, idx, -1); + } + + Polygon poly = new Polygon(xx, yy, idx); + + return poly; + } + + //------------------------// + // computeRunContribution // + //------------------------// + /** + * Compute the contribution of provided run to section data. + * + * @param run provided run + */ + protected void computeRunContribution (Run run) + { + final int length = run.getLength(); + weight += length; + maxRunLength = Math.max(maxRunLength, length); + } + + //-----------------// + // invalidateCache // + //-----------------// + /** + * Nullify all cached data. + */ + protected void invalidateCache () + { + orientedBounds = null; + centroid = null; + polygon = null; + orientedLine = null; + } + + //-----------------// + // populatePolygon // + //-----------------// + /** + * Compute the arrays of points needed to draw the section runs + * + * @param xpoints to receive abscissae + * @param ypoints to receive coordinates + * @param dir direction for browsing runs + * @param index first index available in arrays + * @return last index value + */ + protected int populatePolygon (int[] xpoints, + int[] ypoints, + int index, + int dir) + { + // Precise delimitating points + int runNb = getRunCount(); + int iStart = (dir > 0) ? 0 : (runNb - 1); + int iBreak = (dir > 0) ? runNb : (-1); + int y = (dir > 0) ? getFirstPos() : (getFirstPos() + runNb); + int xPrev = -1; + + for (int i = iStart; i != iBreak; i += dir) { + Run run = runs.get(i); + + // +----------------------------+ + // +--+-------------------------+ + // +----------------------+--+ + // +----------------------+ + // + // Order of the 4 angle points for a run is + // Vertical lag: Horizontal lag: + // 1 2 1 4 + // 4 3 2 3 + int x = (dir > 0) ? run.getStart() : (run.getStop() + 1); + + if (x != xPrev) { + if (xPrev != -1) { + // Insert last vertex + xpoints[index] = xPrev; + ypoints[index] = y; + index++; + } + + // Insert new vertex + xpoints[index] = x; + ypoints[index] = y; + index++; + xPrev = x; + } + + y += dir; + } + + // Complete the sequence, with a new vertex + xpoints[index] = xPrev; + ypoints[index] = y; + index++; + + if (dir < 0) { + // Finish with starting point + xpoints[index] = runs.get(0).getStart(); + ypoints[index] = getFirstPos(); + index++; + } + + return index; + } +} diff --git a/src/main/org/audiveris/omr/lag/JunctionAllPolicy.java b/src/main/org/audiveris/omr/lag/JunctionAllPolicy.java index fee18eb1c..1e4d1e016 100644 --- a/src/main/org/audiveris/omr/lag/JunctionAllPolicy.java +++ b/src/main/org/audiveris/omr/lag/JunctionAllPolicy.java @@ -32,12 +32,10 @@ public class JunctionAllPolicy implements JunctionPolicy { - //~ Static fields/initializers ----------------------------------------------------------------- /** singleton. */ public static final JunctionAllPolicy INSTANCE = new JunctionAllPolicy(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates an instance of this policy. */ @@ -45,7 +43,6 @@ public JunctionAllPolicy () { } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // consistentRun // //---------------// diff --git a/src/main/org/audiveris/omr/lag/JunctionDeltaPolicy.java b/src/main/org/audiveris/omr/lag/JunctionDeltaPolicy.java index a5ba088c4..488c52c19 100644 --- a/src/main/org/audiveris/omr/lag/JunctionDeltaPolicy.java +++ b/src/main/org/audiveris/omr/lag/JunctionDeltaPolicy.java @@ -32,14 +32,12 @@ public class JunctionDeltaPolicy implements JunctionPolicy { - //~ Instance fields ---------------------------------------------------------------------------- /** * Maximum value acceptable for delta length. */ private final int maxDeltaLength; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates an instance of policy based on delta run length. * @@ -51,7 +49,6 @@ public JunctionDeltaPolicy (int maxDeltaLength) this.maxDeltaLength = maxDeltaLength; } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // consistentRun // //---------------// diff --git a/src/main/org/audiveris/omr/lag/JunctionPolicy.java b/src/main/org/audiveris/omr/lag/JunctionPolicy.java index cbf9d935f..8745fce86 100644 --- a/src/main/org/audiveris/omr/lag/JunctionPolicy.java +++ b/src/main/org/audiveris/omr/lag/JunctionPolicy.java @@ -33,7 +33,6 @@ */ public interface JunctionPolicy { - //~ Methods ------------------------------------------------------------------------------------ //---------------// // consistentRun // diff --git a/src/main/org/audiveris/omr/lag/JunctionRatioPolicy.java b/src/main/org/audiveris/omr/lag/JunctionRatioPolicy.java index 31be76715..1256be73d 100644 --- a/src/main/org/audiveris/omr/lag/JunctionRatioPolicy.java +++ b/src/main/org/audiveris/omr/lag/JunctionRatioPolicy.java @@ -34,13 +34,14 @@ public class JunctionRatioPolicy implements JunctionPolicy { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); + /** + * JunctionRatioPolicy based on ratio default value. + */ public static final JunctionRatioPolicy DEFAULT = new JunctionRatioPolicy(); - //~ Instance fields ---------------------------------------------------------------------------- /** * Maximum value acceptable for length ratio. */ @@ -51,7 +52,6 @@ public class JunctionRatioPolicy */ private final double minLengthRatio; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a policy based on default length ratio. */ @@ -72,7 +72,6 @@ public JunctionRatioPolicy (double maxLengthRatio) this.minLengthRatio = 1f / maxLengthRatio; } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // consistentRun // //---------------// @@ -101,17 +100,15 @@ public boolean consistentRun (Run run, public String toString () { return "{JunctionRatioPolicy" + " maxLengthRatio=" + maxLengthRatio + " minLengthRatio=" - + minLengthRatio + "}"; + + minLengthRatio + "}"; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio maxLengthRatio = new Constant.Ratio( 1.25, diff --git a/src/main/org/audiveris/omr/lag/JunctionShiftPolicy.java b/src/main/org/audiveris/omr/lag/JunctionShiftPolicy.java index 502482fb6..d590d5a28 100644 --- a/src/main/org/audiveris/omr/lag/JunctionShiftPolicy.java +++ b/src/main/org/audiveris/omr/lag/JunctionShiftPolicy.java @@ -32,14 +32,12 @@ public class JunctionShiftPolicy implements JunctionPolicy { - //~ Instance fields ---------------------------------------------------------------------------- /** * Maximum value acceptable for shift. */ private final int maxShift; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates an instance of policy based on shift of runs. * @@ -50,7 +48,6 @@ public JunctionShiftPolicy (int maxShift) this.maxShift = maxShift; } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // consistentRun // //---------------// @@ -69,8 +66,8 @@ public boolean consistentRun (Run run, // Check based on positions of the two runs Run last = section.getLastRun(); - return (Math.abs(run.getStart() - last.getStart()) <= maxShift) - && (Math.abs(run.getStop() - last.getStop()) <= maxShift); + return (Math.abs(run.getStart() - last.getStart()) <= maxShift) && (Math.abs( + run.getStop() - last.getStop()) <= maxShift); } //----------// diff --git a/src/main/org/audiveris/omr/lag/Lag.java b/src/main/org/audiveris/omr/lag/Lag.java index 2287732d9..6c6d4c5e4 100644 --- a/src/main/org/audiveris/omr/lag/Lag.java +++ b/src/main/org/audiveris/omr/lag/Lag.java @@ -50,7 +50,6 @@ public interface Lag extends EntityIndex
                        , Oriented { - //~ Methods ------------------------------------------------------------------------------------ /** * Include the content of runs table to the lag. diff --git a/src/main/org/audiveris/omr/lag/LagManager.java b/src/main/org/audiveris/omr/lag/LagManager.java index 542ffdaa6..d9d30aab0 100644 --- a/src/main/org/audiveris/omr/lag/LagManager.java +++ b/src/main/org/audiveris/omr/lag/LagManager.java @@ -51,20 +51,17 @@ */ public class LagManager { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - LagManager.class); + private static final Logger logger = LoggerFactory.getLogger(LagManager.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ @Navigable(false) private final Sheet sheet; /** Map of all public lags. */ - private final Map lagMap = new TreeMap(); + private final Map lagMap = new TreeMap<>(); /** Id of last long horizontal section. */ private int lastLongHSectionId; @@ -72,7 +69,6 @@ public class LagManager /** (Debug)Predefined IDs for VIP sections. */ private final EnumMap> vipMap; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code LagManager} object. * @@ -85,7 +81,6 @@ public LagManager (Sheet sheet) vipMap = getVipSections(); } - //~ Methods ------------------------------------------------------------------------------------ //--------------------// // buildHorizontalLag // //--------------------// @@ -159,21 +154,18 @@ public RunTable filterRuns (RunTable sourceTable, return null; } - final int minVerticalRunLength = 1 - + (int) Math.rint( - sheet.getScale().getMaxFore() * constants.ledgerThickness.getValue()); + final int minVerticalRunLength = 1 + (int) Math.rint( + sheet.getScale().getMaxFore() * constants.ledgerThickness.getValue()); // Remove runs whose height is larger than line thickness - RunTable shortVertTable = sourceTable.copy().purge( - new Predicate() + RunTable shortVertTable = sourceTable.copy().purge(new Predicate() { @Override public final boolean check (Run run) { return run.getLength() >= minVerticalRunLength; } - }, - vertTable); + }, vertTable); RunTableFactory runFactory = new RunTableFactory(HORIZONTAL); RunTable horiTable = runFactory.createTable(shortVertTable.getBuffer()); @@ -217,6 +209,9 @@ public Lag getLag (String key) rebuildBothLags(); return lagMap.get(key); + + default: + break; } } @@ -236,6 +231,19 @@ public int getLongSectionMaxId () return lastLongHSectionId; } + //---------------------// + // setLongSectionMaxId // + //---------------------// + /** + * Remember the id of the last long horizontal section + * + * @param id the id of the last long horizontal section + */ + public void setLongSectionMaxId (int id) + { + lastLongHSectionId = id; + } + //-------------// // rebuildHLag // //-------------// @@ -278,22 +286,14 @@ public void setLag (String key, } } - //---------------------// - // setLongSectionMaxId // - //---------------------// - /** - * Remember the id of the last long horizontal section - * - * @param id the id of the last long horizontal section - */ - public void setLongSectionMaxId (int id) - { - lastLongHSectionId = id; - } - //----------------// // setVipSections // //----------------// + /** + * Assign VIP flag to sections based on map of sections IDs. + * + * @param orientation section orientation to be processed + */ public void setVipSections (Orientation orientation) { List ids = vipMap.get(orientation); @@ -315,12 +315,10 @@ public void setVipSections (Orientation orientation) //----------------// private EnumMap> getVipSections () { - EnumMap> map = new EnumMap>( - Orientation.class); + EnumMap> map = new EnumMap<>(Orientation.class); for (Orientation orientation : Orientation.values()) { - String vipStr = orientation.isVertical() - ? constants.verticalVipSections.getValue() + String vipStr = orientation.isVertical() ? constants.verticalVipSections.getValue() : constants.horizontalVipSections.getValue(); List ids = IntUtil.parseInts(vipStr); @@ -357,14 +355,12 @@ private void rebuildBothLags () buildVerticalLag(vertTable); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxVerticalRunShift = new Scale.Fraction( 0.05, diff --git a/src/main/org/audiveris/omr/lag/Lags.java b/src/main/org/audiveris/omr/lag/Lags.java index 3595fd120..146a4737a 100644 --- a/src/main/org/audiveris/omr/lag/Lags.java +++ b/src/main/org/audiveris/omr/lag/Lags.java @@ -34,7 +34,6 @@ */ public class Lags { - //~ Static fields/initializers ----------------------------------------------------------------- /** Horizontal (partial) lag. It complements vLag. */ public static final String HLAG = "hLag"; @@ -51,7 +50,6 @@ public class Lags /** Symbol lag. (for symbols) */ public static final String SYMBOL_LAG = "symLag"; - //~ Methods ------------------------------------------------------------------------------------ //-------------// // buildBuffer // //-------------// @@ -79,4 +77,8 @@ public static ByteProcessor buildBuffer (int width, return buf; } + + private Lags () + { + } } diff --git a/src/main/org/audiveris/omr/lag/Section.java b/src/main/org/audiveris/omr/lag/Section.java index a06150c4c..3084743e2 100644 --- a/src/main/org/audiveris/omr/lag/Section.java +++ b/src/main/org/audiveris/omr/lag/Section.java @@ -51,11 +51,13 @@ * {@link org.audiveris.omr.glyph.dynamic.LinkedSection} class. *

                        * A section carries orientation information, which is the orientation for all runs in this section. - *

                        1. Positions increase in parallel with run numbers, so the thickness of a section is - * defined as the delta between last and first positions, in other words its number of runs.
                        2. - *
                        3. Coordinates increase along any section run, so the section start is the minimum of all run + *
                            + *
                          1. Positions increase in parallel with run numbers, so the thickness of a section is + * defined as the delta between last and first positions, in other words its number of runs.
                          2. + *
                          3. Coordinates increase along any section run, so the section start is the minimum of all run * starting coordinates, and the section stop is the maximum of all run stopping coordinates. - * We define section length as the value: stop - start +1
                          + * We define section length as the value: stop - start +1
                        4. + *
                        *

                        * Beware, the section orientation only governs the runs orientation. * It by no means implies that the section dimension is longer in the direction along the runs than @@ -69,7 +71,6 @@ public interface Section extends Entity, Oriented { - //~ Static fields/initializers ----------------------------------------------------------------- /** A section comparator, using section id. */ public static final Comparator

                        idComparator = new Comparator
                        () @@ -180,7 +181,6 @@ public int compare (Section s1, } }; - //~ Methods ------------------------------------------------------------------------------------ /** * Predicate to check whether the given absolute point is located * inside the section. diff --git a/src/main/org/audiveris/omr/lag/SectionFactory.java b/src/main/org/audiveris/omr/lag/SectionFactory.java index af799f046..4ae896e4a 100644 --- a/src/main/org/audiveris/omr/lag/SectionFactory.java +++ b/src/main/org/audiveris/omr/lag/SectionFactory.java @@ -44,7 +44,8 @@ /** * Class {@code SectionFactory} builds a collection of sections out of provided runs. *

                        - * To do so, this factory needs:

                          + * To do so, this factory needs: + *
                            *
                          • A source of runs, which is generally provided by a {@link RunTable}. * Alternatively, a pixel buffer ({@link ByteProcessor}) can be provided, in this case a RunTable * instance is then built on the fly from the pixel buffer.
                          • @@ -65,11 +66,9 @@ @NotThreadSafe public class SectionFactory { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SectionFactory.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The lag to populate, if any. */ private final Lag lag; @@ -80,9 +79,8 @@ public class SectionFactory private final JunctionPolicy junctionPolicy; /** Processed sections. true/false */ - private final Set processedSections = new LinkedHashSet(); + private final Set processedSections = new LinkedHashSet<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create an instance of SectionFactory with a target lag. * @@ -113,7 +111,6 @@ public SectionFactory (Orientation orientation, lag = null; } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // createSections // //----------------// @@ -196,7 +193,7 @@ public List
                            createSections (RunTable runTable, */ private List
                            getImmutables (List dynSections) { - final List
                            sections = new ArrayList
                            (dynSections.size()); + final List
                            sections = new ArrayList<>(dynSections.size()); for (DynamicSection dynSection : dynSections) { sections.add(new BasicSection(dynSection)); @@ -221,7 +218,6 @@ private void setProcessed (DynamicSection dynSection) processedSections.add(dynSection); } - //~ Inner Classes ------------------------------------------------------------------------------ //-------// // Build // //-------// @@ -237,27 +233,25 @@ private void setProcessed (DynamicSection dynSection) */ private class Build { - //~ Instance fields ------------------------------------------------------------------------ /** Counter to set dynamicsection ids when no lag is used. */ private int localId; /** Global list of all sections created. */ - private final List created = new ArrayList(); + private final List created = new ArrayList<>(); /** All Active sections in the next sequence. */ - private final List nextActives = new ArrayList(); + private final List nextActives = new ArrayList<>(); /** List of sections in previous sequence that overlap given run in next sequence. */ - private final List overlappingSections = new ArrayList(); + private final List overlappingSections = new ArrayList<>(); /** * List of all Active sections in the previous sequence, which means only * sections that have a run in previous sequence. */ - private final List prevActives = new ArrayList(); + private final List prevActives = new ArrayList<>(); - //~ Methods -------------------------------------------------------------------------------- //--------------// // buidSections // //--------------// diff --git a/src/main/org/audiveris/omr/lag/SectionService.java b/src/main/org/audiveris/omr/lag/SectionService.java index 1da936f9b..bdbf103e1 100644 --- a/src/main/org/audiveris/omr/lag/SectionService.java +++ b/src/main/org/audiveris/omr/lag/SectionService.java @@ -37,14 +37,12 @@ public class SectionService extends EntityService
                            { - //~ Static fields/initializers ----------------------------------------------------------------- /** Events that can be published on section service. */ private static final Class[] eventsAllowed = new Class[]{ - IdEvent.class, EntityListEvent.class - }; + IdEvent.class, + EntityListEvent.class}; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SectionService} object. * @@ -57,7 +55,6 @@ public SectionService (EntityIndex
                            index, super(index, locationService, eventsAllowed); } - //~ Methods ------------------------------------------------------------------------------------ //---------------------// // handleLocationEvent // //---------------------// @@ -70,7 +67,8 @@ public SectionService (EntityIndex
                            index, protected void handleLocationEvent (LocationEvent locationEvent) { // Search only when in MODE_SECTION - if (ViewParameters.getInstance().getSelectionMode() == ViewParameters.SelectionMode.MODE_SECTION) { + if (ViewParameters.getInstance() + .getSelectionMode() == ViewParameters.SelectionMode.MODE_SECTION) { super.handleLocationEvent(locationEvent); } } diff --git a/src/main/org/audiveris/omr/lag/SectionTally.java b/src/main/org/audiveris/omr/lag/SectionTally.java index 576861b28..c4298d243 100644 --- a/src/main/org/audiveris/omr/lag/SectionTally.java +++ b/src/main/org/audiveris/omr/lag/SectionTally.java @@ -29,16 +29,15 @@ * A easy way to access all sections that begin at a given position. * * @param precise section type + * @author Hervé Bitteur */ public class SectionTally { - //~ Instance fields ---------------------------------------------------------------------------- private final List list; private final int[] starts; - //~ Constructors ------------------------------------------------------------------------------- /** * Build a tally on a sorted list of sections * @@ -72,7 +71,6 @@ public SectionTally (int posCount, starts[starts.length - 1] = list.size(); // End mark } - //~ Methods ------------------------------------------------------------------------------------ //------------// // getSubList // //------------// diff --git a/src/main/org/audiveris/omr/lag/Sections.java b/src/main/org/audiveris/omr/lag/Sections.java index ff8e6c543..4ae1f7323 100644 --- a/src/main/org/audiveris/omr/lag/Sections.java +++ b/src/main/org/audiveris/omr/lag/Sections.java @@ -41,16 +41,13 @@ */ public class Sections { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Sections.class); - //~ Constructors ------------------------------------------------------------------------------- private Sections () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // byReverseLength // //-----------------// @@ -127,7 +124,7 @@ public static int[] indexByPosition (int posCount, public static Set
                            intersectedSections (Rectangle rect, Collection sections) { - Set
                            found = new LinkedHashSet
                            (); + Set
                            found = new LinkedHashSet<>(); for (Section section : sections) { if (section.intersects(rect)) { diff --git a/src/main/org/audiveris/omr/lag/ui/SectionBoard.java b/src/main/org/audiveris/omr/lag/ui/SectionBoard.java index 2dd3115ae..c1f822111 100644 --- a/src/main/org/audiveris/omr/lag/ui/SectionBoard.java +++ b/src/main/org/audiveris/omr/lag/ui/SectionBoard.java @@ -46,11 +46,9 @@ public class SectionBoard extends EntityBoard
                            { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SectionBoard.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying lag */ protected final Lag lag; @@ -78,7 +76,6 @@ public class SectionBoard "Weight", "Number of pixels in this section"); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a Section Board * @@ -90,10 +87,10 @@ public SectionBoard (Lag lag, { super( new Desc( - Board.SECTION.name - + ((lag.getOrientation() == Orientation.VERTICAL) ? " Vert" : " Hori"), - Board.SECTION.position - + ((lag.getOrientation() == Orientation.VERTICAL) ? 100 : 0)), + Board.SECTION.name + ((lag.getOrientation() == Orientation.VERTICAL) + ? " Vert" : " Hori"), + Board.SECTION.position + ((lag.getOrientation() == Orientation.VERTICAL) + ? 100 : 0)), lag.getEntityService(), selected); @@ -109,32 +106,6 @@ public SectionBoard (Lag lag, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ - //--------------// - // defineLayout // - //--------------// - private void defineLayout () - { - CellConstraints cst = new CellConstraints(); - - int r = 1; // -------------------------------- - builder.add(x.getLabel(), cst.xy(5, r)); - builder.add(x.getField(), cst.xy(7, r)); - - builder.add(width.getLabel(), cst.xy(9, r)); - builder.add(width.getField(), cst.xy(11, r)); - - r += 2; // -------------------------------- - builder.add(weight.getLabel(), cst.xy(1, r)); - builder.add(weight.getField(), cst.xy(3, r)); - - builder.add(y.getLabel(), cst.xy(5, r)); - builder.add(y.getField(), cst.xy(7, r)); - - builder.add(height.getLabel(), cst.xy(9, r)); - builder.add(height.getField(), cst.xy(11, r)); - } - //-----------------------// // handleEntityListEvent // //-----------------------// @@ -170,4 +141,29 @@ protected void handleEntityListEvent (EntityListEvent
                            listEvent) width.setEnabled(section != null); height.setEnabled(section != null); } + + //--------------// + // defineLayout // + //--------------// + private void defineLayout () + { + CellConstraints cst = new CellConstraints(); + + int r = 1; // -------------------------------- + builder.add(x.getLabel(), cst.xy(5, r)); + builder.add(x.getField(), cst.xy(7, r)); + + builder.add(width.getLabel(), cst.xy(9, r)); + builder.add(width.getField(), cst.xy(11, r)); + + r += 2; // -------------------------------- + builder.add(weight.getLabel(), cst.xy(1, r)); + builder.add(weight.getField(), cst.xy(3, r)); + + builder.add(y.getLabel(), cst.xy(5, r)); + builder.add(y.getField(), cst.xy(7, r)); + + builder.add(height.getLabel(), cst.xy(9, r)); + builder.add(height.getField(), cst.xy(11, r)); + } } diff --git a/src/main/org/audiveris/omr/lag/ui/SectionView.java b/src/main/org/audiveris/omr/lag/ui/SectionView.java index 1c8bd3f3a..dc6a47052 100644 --- a/src/main/org/audiveris/omr/lag/ui/SectionView.java +++ b/src/main/org/audiveris/omr/lag/ui/SectionView.java @@ -32,7 +32,6 @@ */ public interface SectionView { - //~ Methods ------------------------------------------------------------------------------------ /** * Return the display rectangle used by the rendering of the section diff --git a/src/main/org/audiveris/omr/log/LogGuiAppender.java b/src/main/org/audiveris/omr/log/LogGuiAppender.java index 93f62e7ba..04286ee62 100644 --- a/src/main/org/audiveris/omr/log/LogGuiAppender.java +++ b/src/main/org/audiveris/omr/log/LogGuiAppender.java @@ -40,22 +40,38 @@ public class LogGuiAppender extends AppenderBase { - //~ Static fields/initializers ----------------------------------------------------------------- /** * Size of the mail box. * (This cannot be an application Constant, for elaboration dependencies) */ - private static final int LOG_MBX_SIZE = 10000; + private static final int LOG_MBX_SIZE = 10_000; /** Temporary mail box for logged messages. */ - private static final ArrayBlockingQueue logMbx = new ArrayBlockingQueue( + private static final ArrayBlockingQueue logMbx = new ArrayBlockingQueue<>( LOG_MBX_SIZE); - //~ Methods ------------------------------------------------------------------------------------ + //--------// + // append // + //--------// + @Override + protected void append (ILoggingEvent event) + { + logMbx.offer(event); + + if (OMR.gui != null) { + OMR.gui.notifyLog(); + } + } + //---------------// // getEventCount // //---------------// + /** + * Report count of pending events. + * + * @return pending count + */ public static int getEventCount () { return logMbx.size(); @@ -64,21 +80,13 @@ public static int getEventCount () //-----------// // pollEvent // //-----------// + /** + * Poll mail box for next pending event, if any. + * + * @return next event or null + */ public static ILoggingEvent pollEvent () { return logMbx.poll(); } - - //--------// - // append // - //--------// - @Override - protected void append (ILoggingEvent event) - { - logMbx.offer(event); - - if (OMR.gui != null) { - OMR.gui.notifyLog(); - } - } } diff --git a/src/main/org/audiveris/omr/log/LogPane.java b/src/main/org/audiveris/omr/log/LogPane.java index a02d6318c..21ffbfdb0 100644 --- a/src/main/org/audiveris/omr/log/LogPane.java +++ b/src/main/org/audiveris/omr/log/LogPane.java @@ -51,13 +51,11 @@ */ public class LogPane { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(LogPane.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The scrolling text area */ private final JScrollPane component; @@ -68,7 +66,6 @@ public class LogPane private final SimpleAttributeSet attributes = new SimpleAttributeSet(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create the log pane, with a standard mailbox. */ @@ -88,7 +85,6 @@ public LogPane () component.setViewportView(logArea); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // clearLog // //----------// @@ -123,8 +119,7 @@ public JComponent getComponent () */ public void notifyLog () { - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () @@ -134,19 +129,13 @@ public void run () if (event != null) { // Color - StyleConstants.setForeground( - attributes, - getLevelColor(event.getLevel())); + StyleConstants.setForeground(attributes, getLevelColor(event.getLevel())); // Font name - StyleConstants.setFontFamily( - attributes, - constants.fontName.getValue()); + StyleConstants.setFontFamily(attributes, constants.fontName.getValue()); // Font size - StyleConstants.setFontSize( - attributes, - constants.fontSize.getValue()); + StyleConstants.setFontSize(attributes, constants.fontSize.getValue()); try { Map mdc = event.getMDCPropertyMap(); @@ -194,14 +183,12 @@ private Color getLevelColor (Level level) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer fontSize = new Constant.Integer( "Points", diff --git a/src/main/org/audiveris/omr/log/LogStepAppender.java b/src/main/org/audiveris/omr/log/LogStepAppender.java index 78bce2e7e..b0092fcab 100644 --- a/src/main/org/audiveris/omr/log/LogStepAppender.java +++ b/src/main/org/audiveris/omr/log/LogStepAppender.java @@ -38,7 +38,6 @@ public class LogStepAppender extends AppenderBase { - //~ Methods ------------------------------------------------------------------------------------ @Override protected void append (ILoggingEvent event) diff --git a/src/main/org/audiveris/omr/log/LogUtil.java b/src/main/org/audiveris/omr/log/LogUtil.java index d74b21940..9e9c67f0d 100644 --- a/src/main/org/audiveris/omr/log/LogUtil.java +++ b/src/main/org/audiveris/omr/log/LogUtil.java @@ -41,6 +41,7 @@ import org.slf4j.MDC; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.nio.file.Files; @@ -60,7 +61,6 @@ */ public abstract class LogUtil { - //~ Static fields/initializers ----------------------------------------------------------------- /** MDC key for book context. */ public static final String BOOK = "BOOK"; @@ -75,9 +75,8 @@ public abstract class LogUtil private static final String LOGBACK_FILE_NAME = "logback.xml"; /** Initial messages before logging is fully set. */ - private static final List initialMessages = new ArrayList(); + private static final List initialMessages = new ArrayList<>(); - //~ Methods ------------------------------------------------------------------------------------ //-------------// // addAppender // //-------------// @@ -235,9 +234,10 @@ public static void initialize (Path CONFIG_FOLDER, File tmpFile = File.createTempFile("logback-", ".xml"); tmpFile.deleteOnExit(); - InputStream is = configUri.toURL().openStream(); - FileUtils.copyInputStreamToFile(is, tmpFile); - is.close(); + try (InputStream is = configUri.toURL().openStream()) { + FileUtils.copyInputStreamToFile(is, tmpFile); + } + localPath = tmpFile.toPath(); } else { localPath = Paths.get(configUri); @@ -251,7 +251,7 @@ public static void initialize (Path CONFIG_FOLDER, } else { initMessage("LogUtil. No " + localPath + ", skipped."); } - } catch (Exception ex) { + } catch (IOException ex) { ex.printStackTrace(); } @@ -365,6 +365,12 @@ public static void stopStub () //---------// // toLevel // //---------// + /** + * Decode a string as a Level value. + * + * @param str the input string + * @return the decoded Level value + */ public static Level toLevel (final String str) { switch (str.toUpperCase()) { @@ -404,4 +410,8 @@ private static void initMessage (String str) { initialMessages.add(str); } + + private LogUtil () + { + } } diff --git a/src/main/org/audiveris/omr/log/LoggingStream.java b/src/main/org/audiveris/omr/log/LoggingStream.java index 41d57c932..589a01fba 100644 --- a/src/main/org/audiveris/omr/log/LoggingStream.java +++ b/src/main/org/audiveris/omr/log/LoggingStream.java @@ -27,13 +27,11 @@ public class LoggingStream extends ByteArrayOutputStream { - //~ Instance fields ---------------------------------------------------------------------------- private final Logger logger; private final Level level; - //~ Constructors ------------------------------------------------------------------------------- /** * Constructor * @@ -48,7 +46,6 @@ public LoggingStream (Logger logger, this.level = level; } - //~ Methods ------------------------------------------------------------------------------------ /** * Upon flush(), write the existing contents of the OutputStream to * the logger as a log record. diff --git a/src/main/org/audiveris/omr/math/AreaUtil.java b/src/main/org/audiveris/omr/math/AreaUtil.java index 0956c73b7..780c61474 100644 --- a/src/main/org/audiveris/omr/math/AreaUtil.java +++ b/src/main/org/audiveris/omr/math/AreaUtil.java @@ -36,7 +36,11 @@ */ public abstract class AreaUtil { - //~ Methods ------------------------------------------------------------------------------------ + + // Not meant to be instantiated. + private AreaUtil () + { + } //-------------------------// // horizontalParallelogram // @@ -218,7 +222,6 @@ public static Area verticalRibbon (Shape median, return new Area(path); } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // CoreData // //----------// @@ -227,7 +230,6 @@ public static Area verticalRibbon (Shape median, */ public static class CoreData { - //~ Instance fields ------------------------------------------------------------------------ /** Total area length. (height for vertical, width for horizontal) */ public final int length; @@ -238,7 +240,13 @@ public static class CoreData /** Ratio of white elements on total length. */ public final double whiteRatio; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a CoreData object. + * + * @param length area length to measure + * @param gap longest gag found + * @param whiteRatio ratio of white on total length + */ public CoreData (int length, int gap, double whiteRatio) @@ -248,7 +256,6 @@ public CoreData (int length, this.whiteRatio = whiteRatio; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { diff --git a/src/main/org/audiveris/omr/math/Barycenter.java b/src/main/org/audiveris/omr/math/Barycenter.java index 363588a12..855446d93 100644 --- a/src/main/org/audiveris/omr/math/Barycenter.java +++ b/src/main/org/audiveris/omr/math/Barycenter.java @@ -25,10 +25,11 @@ /** * Class {@code Barycenter} is meant to cumulate data when computing barycenter. + * + * @author Hervé Bitteur */ public class Barycenter { - //~ Instance fields ---------------------------------------------------------------------------- /** * The total weight (such as the number of pixels). @@ -43,7 +44,6 @@ public class Barycenter /** The weighted ordinate */ private double yy; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Barycenter object. */ @@ -51,10 +51,14 @@ public Barycenter () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getWeight // //-----------// + /** + * Report the total weight. + * + * @return total weight + */ public final double getWeight () { return weight; diff --git a/src/main/org/audiveris/omr/math/BasicLine.java b/src/main/org/audiveris/omr/math/BasicLine.java index cc9c97aac..a552bcd2c 100644 --- a/src/main/org/audiveris/omr/math/BasicLine.java +++ b/src/main/org/audiveris/omr/math/BasicLine.java @@ -36,14 +36,12 @@ * * @author Hervé Bitteur */ -public class BasicLine +public final class BasicLine implements Line { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(BasicLine.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Flag to indicate that data needs to be recomputed. */ private boolean dirty; @@ -89,7 +87,6 @@ public class BasicLine /** Maximum ordinate among all defining points. */ private double yMax = Double.MIN_VALUE; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a line, with no data. * The line is no yet usable, except for including further defining points. @@ -157,7 +154,6 @@ public BasicLine (Collection points) checkLineParameters(); } - //~ Methods ------------------------------------------------------------------------------------ //---------------------// // checkLineParameters // //---------------------// @@ -192,6 +188,12 @@ public double distanceOf (double x, //------------// // distanceOf // //------------// + /** + * Report the algebraic distance from the provided Point2D to the line. + * + * @param point the provided point + * @return the algebraic distance + */ public double distanceOf (Point2D point) { return distanceOf(point.getX(), point.getY()); @@ -252,8 +254,10 @@ public double getMeanDistance () checkLineParameters(); - double distSq = ((a * a * sx2) + (b * b * sy2) + (c * c * n) + (2 * a * b * sxy) - + (2 * a * c * sx) + (2 * b * c * sy)) / n; + double distSq = ((a * a * sx2) + (b * b * sy2) + (c * c * n) + (2 * a * b * sxy) + (2 * a + * c + * sx) + + (2 * b * c * sy)) / n; if (distSq < 0) { distSq = 0; @@ -336,6 +340,11 @@ public Line includeLine (Line other) //--------------// // includePoint // //--------------// + /** + * Add a defining point to the line. + * + * @param point the point to include + */ public void includePoint (Point2D point) { includePoint(point.getX(), point.getY()); @@ -415,12 +424,16 @@ public Line swappedCoordinates () */ public Line2D.Double toDouble () { - checkLineParameters(); + try { + checkLineParameters(); - if (isRatherVertical) { - return new Line2D.Double(xAtY(yMin), yMin, xAtY(yMax), yMax); - } else { - return new Line2D.Double(xMin, yAtX(xMin), xMax, yAtX(xMax)); + if (isRatherVertical) { + return new Line2D.Double(xAtY(yMin), yMin, xAtY(yMax), yMax); + } else { + return new Line2D.Double(xMin, yAtX(xMin), xMax, yAtX(xMax)); + } + } catch (UndefinedLineException ulex) { + return null; // Not enough points } } @@ -550,36 +563,6 @@ public double yAtXExt (double x) return yAtX(x); } - //------// - // getA // Meant for test - //------// - double getA () - { - checkLineParameters(); - - return a; - } - - //------// - // getB // Meant for test - //------// - double getB () - { - checkLineParameters(); - - return b; - } - - //------// - // getC // Meant for test - //------// - double getC () - { - checkLineParameters(); - - return c; - } - //---------// // compute // //---------// @@ -588,6 +571,8 @@ public double yAtXExt (double x) */ private void compute () { + dirty = false; + if (n < 2) { throw new UndefinedLineException("Not enough defining points : " + n); } @@ -612,7 +597,6 @@ private void compute () } normalize(); - dirty = false; } //-----------// @@ -628,4 +612,35 @@ private void normalize () b /= norm; c /= norm; } + + //------// + // getA // Meant for test + //------// + double getA () + { + checkLineParameters(); + + return a; + } + + //------// + // getB // Meant for test + //------// + double getB () + { + checkLineParameters(); + + return b; + } + + //------// + // getC // Meant for test + //------// + double getC () + { + checkLineParameters(); + + return c; + } + } diff --git a/src/main/org/audiveris/omr/math/Circle.java b/src/main/org/audiveris/omr/math/Circle.java index c316f301a..25f2f5382 100644 --- a/src/main/org/audiveris/omr/math/Circle.java +++ b/src/main/org/audiveris/omr/math/Circle.java @@ -53,14 +53,12 @@ */ public class Circle { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Circle.class); /** Size for matrices used to compute the circle. */ private static final int DIM = 4; - //~ Instance fields ---------------------------------------------------------------------------- /** Center. */ private Point2D.Double center; @@ -85,7 +83,6 @@ public class Circle /** Bézier curve for circle arc. */ private CubicCurve2D curve; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new instance of Circle, defined by a sequence of points. * @@ -130,7 +127,6 @@ public Circle (Point2D first, computeAngles(first, middle, last); } - //~ Methods ------------------------------------------------------------------------------------ //-----// // ccw // //-----// @@ -247,6 +243,19 @@ public double getDistance () return distance; } + //-------------// + // getDistance // + //-------------// + /** + * Record the mean distance (useful for 2-point definitions) + * + * @param distance the computed mean distance + */ + public void setDistance (double distance) + { + this.distance = distance; + } + //---------------// // getFirstAngle // //---------------// @@ -350,19 +359,6 @@ public void reverse () ccw = -ccw; } - //-------------// - // getDistance // - //-------------// - /** - * Record the mean distance (useful for 2-point definitions) - * - * @param distance the computed mean distance - */ - public void setDistance (double distance) - { - this.distance = distance; - } - //----------// // toString // //----------// @@ -379,7 +375,10 @@ public String toString () if ((firstAngle != null) && (lastAngle != null)) { sb.append( - String.format(" degrees=(%.0f,%.0f)", toDegrees(firstAngle), toDegrees(lastAngle))); + String.format( + " degrees=(%.0f,%.0f)", + toDegrees(firstAngle), + toDegrees(lastAngle))); } sb.append("}"); @@ -415,7 +414,7 @@ private void computeAngles (double[] xx, } final double bucketSize = (2 * PI) / BUCKET_NB; - ArrayList angles = new ArrayList(); + ArrayList angles = new ArrayList<>(); for (int i = 0; i < xx.length; i++) { // Get an angle between 0 and 2*PI @@ -503,8 +502,8 @@ private void computeAngles (Point2D first, private void computeCurve () { // Make sure we do have an arc defined, rather than a full circle - if (((lastAngle == null) || (lastAngle.isNaN())) - || ((firstAngle == null) || (firstAngle.isNaN()))) { + if (((lastAngle == null) || (lastAngle.isNaN())) || ((firstAngle == null) || (firstAngle + .isNaN()))) { return; } @@ -524,62 +523,26 @@ private void computeCurve () ///System.out.println("angleDeg/2=" + toDegrees(theta)); final Matrix rotation = new Matrix( - new double[][]{ - {cos(theta), -sin(theta), 0}, - {sin(theta), cos(theta), 0}, - {0, 0, 1} - }); + new double[][]{{cos(theta), -sin(theta), 0}, {sin(theta), cos(theta), 0}, {0, 0, 1}}); // Scaling final Matrix scaling = new Matrix( - new double[][]{ - {radius, 0, 0}, - {0, radius, 0}, - {0, 0, 1} - }); + new double[][]{{radius, 0, 0}, {0, radius, 0}, {0, 0, 1}}); // Translation final Matrix translation = new Matrix( - new double[][]{ - {1, 0, center.x}, - {0, 1, center.y}, - {0, 0, 1} - }); + new double[][]{{1, 0, center.x}, {0, 1, center.y}, {0, 0, 1}}); // Composite operation final Matrix op = translation.times(scaling).times(rotation); - final Matrix M0 = op.times( - new Matrix( - new double[][]{ - {x0}, - {y0}, - {1} - })); - - final Matrix M1 = op.times( - new Matrix( - new double[][]{ - {x1}, - {y1}, - {1} - })); - - final Matrix M2 = op.times( - new Matrix( - new double[][]{ - {x2}, - {y2}, - {1} - })); - - final Matrix M3 = op.times( - new Matrix( - new double[][]{ - {x3}, - {y3}, - {1} - })); + final Matrix M0 = op.times(new Matrix(new double[][]{{x0}, {y0}, {1}})); + + final Matrix M1 = op.times(new Matrix(new double[][]{{x1}, {y1}, {1}})); + + final Matrix M2 = op.times(new Matrix(new double[][]{{x2}, {y2}, {1}})); + + final Matrix M3 = op.times(new Matrix(new double[][]{{x3}, {y3}, {1}})); // Bezier curve (make sure the curve goes from left to right) if (M0.get(0, 0) <= M3.get(0, 0)) { diff --git a/src/main/org/audiveris/omr/math/Clustering.java b/src/main/org/audiveris/omr/math/Clustering.java index 8b389cb6a..5a718f7fb 100644 --- a/src/main/org/audiveris/omr/math/Clustering.java +++ b/src/main/org/audiveris/omr/math/Clustering.java @@ -12,14 +12,14 @@ * Class {@code Clustering} gathers objects according to their similarity. * It uses the implementation of Expectation-Maximization algorithm published by Xavier Philippeau * on + * href= + * "http://www.developpez.net/forums/d740672/autres-langages/algorithmes/contribuez/java-algorithme-expectation-maximization-em/"> * this site * * @author Xavier Philippeau */ public class Clustering { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Clustering.class); @@ -29,7 +29,11 @@ public class Clustering private static final int MAX_ITER = 10; - //~ Methods ------------------------------------------------------------------------------------ + // Not meant to be instantiated. + private Clustering () + { + } + /** * Compute the mixture coefficients using Expectation-Maximization algorithm. * @@ -104,10 +108,11 @@ public static double[] EM (double[] x, return pi; } - //~ Inner Interfaces --------------------------------------------------------------------------- + /** + * Model description. + */ public static interface Law { - //~ Methods -------------------------------------------------------------------------------- /** * improve law parameters @@ -127,17 +132,17 @@ void improveParameters (int N, double proba (double x); } - //~ Inner Classes ------------------------------------------------------------------------------ + /** + * Gaussian implementation of Law. + */ public static class Gaussian implements Law { - //~ Instance fields ------------------------------------------------------------------------ private double mean = 0; private double sigma = 0; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Gaussian object. * @@ -151,7 +156,11 @@ public Gaussian (double mean, this.sigma = sigma; } - //~ Methods -------------------------------------------------------------------------------- + /** + * Report gaussian mean value + * + * @return mean value + */ public double getMean () { return mean; diff --git a/src/main/org/audiveris/omr/math/Combinations.java b/src/main/org/audiveris/omr/math/Combinations.java deleted file mode 100644 index c944c3a94..000000000 --- a/src/main/org/audiveris/omr/math/Combinations.java +++ /dev/null @@ -1,119 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// C o m b i n a t i o n s // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.math; - -/** - * Class {@code Combinations} - * - * @author Hervé Bitteur - */ -public abstract class Combinations -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - public static final boolean[] TRUE_FALSE = new boolean[]{true, false}; - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // dumpOf // - //--------// - public static String dumpOf (boolean[][] vectors) - { - StringBuilder sb = new StringBuilder(); - - for (boolean[] vector : vectors) { - sb.append("\n"); - sb.append(dumpOf(vector)); - } - - return sb.toString(); - } - - //--------// - // dumpOf // - //--------// - public static String dumpOf (boolean[] vector) - { - StringBuilder sb = new StringBuilder(); - - for (boolean b : vector) { - sb.append(b ? "1 " : "0 "); - } - - return sb.toString(); - } - - //------------// - // getVectors // - //------------// - /** - * Return the array of possible combinations in a sequence of n items. - *

                            - * The last positions vary first, here is an example for a sequence of 4 items: - *

                            -     * 1 1 1 1
                            -     * 1 1 1 0
                            -     * 1 1 0 1
                            -     * 1 1 0 0
                            -     * 1 0 1 1
                            -     * 1 0 1 0
                            -     * 1 0 0 1
                            -     * 1 0 0 0
                            -     * 0 1 1 1
                            -     * 0 1 1 0
                            -     * 0 1 0 1
                            -     * 0 1 0 0
                            -     * 0 0 1 1
                            -     * 0 0 1 0
                            -     * 0 0 0 1
                            -     * 0 0 0 0
                            -     * 
                            - * - * @param n the sequence size - * @return the array of possible combinations - */ - public static boolean[][] getVectors (int n) - { - final int combNb = (int) Math.pow(2, n); // Nb of combinations - final boolean[][] bools = new boolean[combNb][n]; - int span = 1; - - for (int i = n - 1; i >= 0; i--) { - ///for (int i = 0; i < n; i++) { - int offset = 0; - - while (offset < combNb) { - for (boolean b : TRUE_FALSE) { - for (int j = 0; j < span; j++) { - bools[j + offset][i] = b; - } - - offset += span; - } - } - - span *= 2; - } - - return bools; - } -} diff --git a/src/main/org/audiveris/omr/math/CubicUtil.java b/src/main/org/audiveris/omr/math/CubicUtil.java index e8a6dd58f..5bf28ddc0 100644 --- a/src/main/org/audiveris/omr/math/CubicUtil.java +++ b/src/main/org/audiveris/omr/math/CubicUtil.java @@ -1,62 +1,60 @@ -//------------------------------------------------------------------------------------------------// -// // -// C u b i c U t i l // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.math; - -import java.awt.geom.CubicCurve2D; -import java.awt.geom.Point2D; - -/** - * Class {@code CubicUtil} gathers utility functions related to cubic bezier curves - * ({@link CubicCurve2D}) - * - * @author Hervé Bitteur - */ -public abstract class CubicUtil -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Not meant to be instantiated. - */ - private CubicUtil () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Report the point on the curve, located at t = 1-t = 0.5. - * It splits the curve length equally. - * P: middle of segment P1..P2 - * C: middle of segment CP1..CP2 - * M: middle of curve - * PM = 3/4 * PC - * - * @param c the provided curve - * @return the mid point on curve - */ - public static Point2D getMidPoint (CubicCurve2D c) - { - return new Point2D.Double( - (c.getX1() + (3 * c.getCtrlX1()) + (3 * c.getCtrlX2()) + c.getX2()) / 8, - (c.getY1() + (3 * c.getCtrlY1()) + (3 * c.getCtrlY2()) + c.getY2()) / 8); - } -} +//------------------------------------------------------------------------------------------------// +// // +// C u b i c U t i l // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.math; + +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Point2D; + +/** + * Class {@code CubicUtil} gathers utility functions related to cubic bezier curves + * ({@link CubicCurve2D}) + * + * @author Hervé Bitteur + */ +public abstract class CubicUtil +{ + + /** + * Not meant to be instantiated. + */ + private CubicUtil () + { + } + + /** + * Report the point on the curve, located at t = 1-t = 0.5. + * It splits the curve length equally. + * P: middle of segment P1..P2 + * C: middle of segment CP1..CP2 + * M: middle of curve + * PM = 3/4 * PC + * + * @param c the provided curve + * @return the mid point on curve + */ + public static Point2D getMidPoint (CubicCurve2D c) + { + return new Point2D.Double( + (c.getX1() + (3 * c.getCtrlX1()) + (3 * c.getCtrlX2()) + c.getX2()) / 8, + (c.getY1() + (3 * c.getCtrlY1()) + (3 * c.getCtrlY2()) + c.getY2()) / 8); + } +} diff --git a/src/main/org/audiveris/omr/math/Ellipse.java b/src/main/org/audiveris/omr/math/Ellipse.java index fc634402a..0b241bf59 100644 --- a/src/main/org/audiveris/omr/math/Ellipse.java +++ b/src/main/org/audiveris/omr/math/Ellipse.java @@ -36,40 +36,32 @@ * approximates a collection of points. *

                            * The ellipse is defined through the 6 coefficients of its algebraic equation: + * *

                              * A*x**2 + B*x*y + C*y**2 + D*x + E*y + F = 0
                              * 
                            + * * It can also compute the ellipse characteristics (center, theta, major, minor) from its algebraic * equation. * * @author Hervé Bitteur */ -public class Ellipse +public final class Ellipse { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Ellipse.class); /** Constraint such that 4*A*C - B**2 = 1 */ private static final Matrix C1 = new Matrix( - new double[][]{ - {0, 0, 2}, - {0, -1, 0}, - {2, 0, 0} - }); + new double[][]{{0, 0, 2}, {0, -1, 0}, {2, 0, 0}}); /** Inverse of Constraint */ private static final Matrix C1inv = new Matrix( - new double[][]{ - {0, 0, 0.5}, - {0, -1, 0}, - {0.5, 0, 0} - }); + new double[][]{{0, 0, 0.5}, {0, -1, 0}, {0.5, 0, 0}}); /** Epsilon value for vertical or horizontal ellipses */ private static final double EPSILON = 1.0e-15; - //~ Instance fields ---------------------------------------------------------------------------- /** * Array of coefficients that define ellipse algebraic equation */ @@ -109,7 +101,6 @@ public class Ellipse /** 1/2 Minor axis */ protected Double minor; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new instance of Ellipse, defined by a set of points * @@ -130,7 +121,6 @@ protected Ellipse () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getAngle // //----------// @@ -291,27 +281,27 @@ protected Point2D.Double computeCenter () * Let's consider the points where the ellipse is crossed by the * vertical line located at abscissa x : We have an equation in y, of * degree 2, governed by its discriminent. - * + *

                            * A*x**2 + B*x*y + C*y**2 + D*x + E*y + F = 0 becomes : - * + *

                            * C*y**2 + y*(B*x + E) + (Ax**2 + D*x + F) - * + *

                            * For the vertical tangents, we have a double root, thus with a null * discriminent (which gives us the two x values of these tangents) - * + *

                            * (B*x + E)**2 - 4*C*(Ax**2 + D*x + F) = 0 - * + *

                            * Rewritten as an x-equation : - * + *

                            * (B**2 -4*A*C)*x**2 + (2*B*E - 4*C*D)*x + E**2 -4*C*F - * + *

                            * By symmetry, the ellipse center is right in the middle, so its * abscissa is half of the sum of the two roots (-b/2a) : - * + *

                            * centerX = (2*C*D - B*E) / (B**2 -4*A*C) - * + *

                            * And a similar approach on horizontal tangents would give : - * + *

                            * centerY = (2*A*E - B*D) / (B**2 -4*A*C) */ double den = (B * B) - (4 * A * C); @@ -364,7 +354,6 @@ protected void computeCenterTranslation () */ protected void computeCharacteristics () { - System.out.println("-- computeCharacteristics"); // Compute ellipse center center = computeCenter(); @@ -389,7 +378,6 @@ protected void computeCharacteristics () protected void fit (double[] x, double[] y) { - System.out.println("-- fit"); // Check input if (x.length != y.length) { @@ -405,7 +393,7 @@ protected void fit (double[] x, * (C1 | 0 ) * (---+---) * ( 0 | 0 ) - * */ + */ ///print(C1, "C1"); ///print(C1inv, "C1inv"); /** number of points */ @@ -451,26 +439,28 @@ protected void fit (double[] x, ///print(S3, "S3"); /** * Initial equation S.A = lambda.C.A can be rewritten. : + * *

                                      * (S1 | S2)   (A1)            (C1 | 0 )   (A1)
                                      * (---+---) . (--) = lambda . (---+---) . (--)
                                      * (S2'| S3)   (A2)            ( 0 | 0 )   (A2)
                                      * 
                            + *

                            * which is equivalent to : * S1.A1 + S2.A2 = lambda.C1.A1 * S2'.A1 + S3.A2 = 0 - * + *

                            * So * A2 = -S3inv.S2'.A1 * (S1 - S2.S3inv.S2').A1 = lambda.C1.A1 or * C1inv.(S1 - S2.S3inv.S2').A1 = lambda.A1 - * + *

                            * Constraint is now * A1'.C1.A1 = 1 - * + *

                            * w/ Reduced scatter matrix M = C1inv.S1 - S2.S3inv.S2' * we now have : - * + *

                            * M.A1 = lambda.A1 * A1'.C1.A1 = 1 * A2 = -S3inv.S2'.A1 @@ -574,6 +564,12 @@ protected void fit (double[] x, //-------// // print // //-------// + /** + * Print out the provided matrix. + * + * @param m matrix + * @param title print title + */ protected static void print (Matrix m, String title) { diff --git a/src/main/org/audiveris/omr/math/GCD.java b/src/main/org/audiveris/omr/math/GCD.java index 25ecdf746..a1caba906 100644 --- a/src/main/org/audiveris/omr/math/GCD.java +++ b/src/main/org/audiveris/omr/math/GCD.java @@ -31,14 +31,12 @@ */ public abstract class GCD { - //~ Constructors ------------------------------------------------------------------------------- /** Not meant to be instantiated */ private GCD () { } - //~ Methods ------------------------------------------------------------------------------------ //-----// // gcd // //-----// diff --git a/src/main/org/audiveris/omr/math/GeoPath.java b/src/main/org/audiveris/omr/math/GeoPath.java index ab9dc33dd..dd8a5928d 100644 --- a/src/main/org/audiveris/omr/math/GeoPath.java +++ b/src/main/org/audiveris/omr/math/GeoPath.java @@ -36,7 +36,6 @@ public class GeoPath extends Path2D.Double { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new GeoPath object. @@ -67,7 +66,6 @@ public GeoPath (Shape s, super(s, at); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // getFirstPoint // //---------------// @@ -128,38 +126,6 @@ public Point2D getLastPoint () return new Point2D.Double(x, y); } - //---------// - // labelOf // - //---------// - /** - * Report the kind label of a segment. - * - * @param segmentKind the int-based segment kind - * @return the label for the curve - */ - public static String labelOf (int segmentKind) - { - switch (segmentKind) { - case SEG_MOVETO: - return "SEG_MOVETO"; - - case SEG_LINETO: - return "SEG_LINETO"; - - case SEG_QUADTO: - return "SEG_QUADTO"; - - case SEG_CUBICTO: - return "SEG_CUBICTO"; - - case SEG_CLOSE: - return "SEG_CLOSE"; - - default: - throw new RuntimeException("Illegal segmentKind " + segmentKind); - } - } - //----------// // toString // //----------// @@ -185,8 +151,8 @@ public String toString () firstCoord = false; } - sb.append("[").append((float) buffer[ic]).append(",").append( - (float) buffer[ic + 1]).append("]"); + sb.append("[").append((float) buffer[ic]).append(",").append((float) buffer[ic + 1]) + .append("]"); } sb.append(")"); @@ -230,8 +196,9 @@ public double xAtY (double y) double cpx1 = coords[0]; double cpx2 = coords[2]; - return (p1.x * u * u * u) + (3 * cpx1 * t * u * u) + (3 * cpx2 * t * t * u) - + (p2.x * t * t * t); + return (p1.x * u * u * u) + (3 * cpx1 * t * u * u) + (3 * cpx2 * t * t * u) + (p2.x * t + * t + * t); } default: @@ -255,8 +222,8 @@ public double xAtYExt (double y) Point2D stopPoint = getLastPoint(); if ((y < startPoint.getY()) || (y > stopPoint.getY())) { - double sl = (stopPoint.getX() - startPoint.getX()) / (stopPoint.getY() - - startPoint.getY()); + double sl = (stopPoint.getX() - startPoint.getX()) / (stopPoint.getY() - startPoint + .getY()); return startPoint.getX() + (sl * (y - startPoint.getY())); } else { @@ -297,8 +264,9 @@ public double yAtX (double x) double cpy1 = coords[1]; double cpy2 = coords[3]; - return (p1.y * u * u * u) + (3 * cpy1 * t * u * u) + (3 * cpy2 * t * t * u) - + (p2.y * t * t * t); + return (p1.y * u * u * u) + (3 * cpy1 * t * u * u) + (3 * cpy2 * t * t * u) + (p2.y * t + * t + * t); } default: @@ -322,8 +290,8 @@ public double yAtXExt (double x) Point2D stopPoint = getLastPoint(); if ((x < startPoint.getX()) || (x > stopPoint.getX())) { - double sl = (stopPoint.getY() - startPoint.getY()) / (stopPoint.getX() - - startPoint.getX()); + double sl = (stopPoint.getY() - startPoint.getY()) / (stopPoint.getX() - startPoint + .getX()); return startPoint.getY() + (sl * (x - startPoint.getX())); } else { @@ -331,36 +299,6 @@ public double yAtXExt (double x) } } - //---------// - // countOf // - //---------// - /** - * Report how many coordinate values a path segment contains. - * - * @param segmentKind the int-based segment kind - * @return the number of coordinates values - */ - protected static int countOf (int segmentKind) - { - switch (segmentKind) { - case SEG_MOVETO: - case SEG_LINETO: - return 2; - - case SEG_QUADTO: - return 4; - - case SEG_CUBICTO: - return 6; - - case SEG_CLOSE: - return 0; - - default: - throw new RuntimeException("Illegal segmentKind " + segmentKind); - } - } - //-------------// // getXSegment // //-------------// @@ -452,4 +390,66 @@ protected int getYSegment (double y, // Not found throw new RuntimeException("Ordinate not in range: " + y); } + + //---------// + // labelOf // + //---------// + /** + * Report the kind label of a segment. + * + * @param segmentKind the int-based segment kind + * @return the label for the curve + */ + public static String labelOf (int segmentKind) + { + switch (segmentKind) { + case SEG_MOVETO: + return "SEG_MOVETO"; + + case SEG_LINETO: + return "SEG_LINETO"; + + case SEG_QUADTO: + return "SEG_QUADTO"; + + case SEG_CUBICTO: + return "SEG_CUBICTO"; + + case SEG_CLOSE: + return "SEG_CLOSE"; + + default: + throw new RuntimeException("Illegal segmentKind " + segmentKind); + } + } + + //---------// + // countOf // + //---------// + /** + * Report how many coordinate values a path segment contains. + * + * @param segmentKind the int-based segment kind + * @return the number of coordinates values + */ + protected static int countOf (int segmentKind) + { + switch (segmentKind) { + case SEG_MOVETO: + case SEG_LINETO: + return 2; + + case SEG_QUADTO: + return 4; + + case SEG_CUBICTO: + return 6; + + case SEG_CLOSE: + return 0; + + default: + throw new RuntimeException("Illegal segmentKind " + segmentKind); + } + } } diff --git a/src/main/org/audiveris/omr/math/GeoUtil.java b/src/main/org/audiveris/omr/math/GeoUtil.java index 838ec0aee..537c31dce 100644 --- a/src/main/org/audiveris/omr/math/GeoUtil.java +++ b/src/main/org/audiveris/omr/math/GeoUtil.java @@ -32,7 +32,11 @@ */ public abstract class GeoUtil { - //~ Methods ------------------------------------------------------------------------------------ + + // Not meant to be instantiated. + private GeoUtil () + { + } //----------// // centerOf // diff --git a/src/main/org/audiveris/omr/math/HiLoPeakFinder.java b/src/main/org/audiveris/omr/math/HiLoPeakFinder.java index 2753b791a..497b983f5 100644 --- a/src/main/org/audiveris/omr/math/HiLoPeakFinder.java +++ b/src/main/org/audiveris/omr/math/HiLoPeakFinder.java @@ -49,11 +49,9 @@ */ public class HiLoPeakFinder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(HiLoPeakFinder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Entity title. */ public final String name; @@ -72,8 +70,8 @@ public class HiLoPeakFinder /** Threshold for y values. */ private int minValue; - /** Threshold for peak top y value, if any. */ - private Integer minTopValue; + /** Quorum on y value, if any. */ + private Quorum quorum; /** Threshold for y derivatives. */ private int minDerivative; @@ -95,7 +93,6 @@ public int compare (Range e1, } }; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code HiLoPeakFinder} object on (sub-)domain of provided function. * @@ -140,7 +137,6 @@ public HiLoPeakFinder (String name, xMax = function.getXMax(); } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // findPeaks // //-----------// @@ -149,36 +145,31 @@ public HiLoPeakFinder (String name, * A peak can extend from HiLo.min -1 to HiLo.max, unless there is a HiLo or even a peak nearby. * * @param minValue minimum value to be part of a peak - * @param minTopValue peak top value threshold or null * @param minDerivative peak derivative threshold * @param minGainRatio value gain ratio for peak widening * @return the (perhaps empty but not null) collection of peaks, sorted by decreasing count */ public List findPeaks (int minValue, - Integer minTopValue, int minDerivative, double minGainRatio) { this.minValue = minValue; - this.minTopValue = minTopValue; this.minDerivative = minDerivative; this.minGainRatio = minGainRatio; - final List peaks = new ArrayList(); + final List peaks = new ArrayList<>(); retrieveHiLos(); // Map: originating hilo -> corresponding peak - Map hiloToPeak = new HashMap(); + Map hiloToPeak = new HashMap<>(); // Process hilos by decreasing main value - List decreasing = new ArrayList(hilos); + List decreasing = new ArrayList<>(hilos); Collections.sort(decreasing, byReverseMainValue); // Convert each hilo to a peak with adjusted limits for (Range hilo : decreasing) { int i = hilos.indexOf(hilo); - - ///int pMin = Math.max(hilo.min - 2, 1); int pMin = Math.max(hilo.min - 1, 1); if (i > 0) { @@ -187,15 +178,8 @@ public List findPeaks (int minValue, pMin = Math.max(pMin, (prevPeak != null) ? (prevPeak.max + 1) : (prevHiLo.max + 1)); } - ///int pMax = Math.min(hilo.max + 1, counts.length - 1); int pMax = hilo.max; - // - // if (i < (hilos.size() - 1)) { - // Range nextHiLo = hilos.get(i + 1); - // Range nextPeak = hiloToPeak.get(nextHiLo); - // pMax = Math.min(pMax, (nextPeak != null) ? (nextPeak.min - 1) : (nextHiLo.min - 1)); - // } Range peak = createPeak(pMin, hilo.main, pMax); hiloToPeak.put(hilo, peak); peaks.add(peak); @@ -205,11 +189,11 @@ public List findPeaks (int minValue, Collections.sort(peaks, byReverseMainValue); // Is there a min top value to check? - if (minTopValue != null) { + if (quorum != null) { for (int i = 0; i < peaks.size(); i++) { Range peak = peaks.get(i); - if (function.getValue(peak.main) < minTopValue) { + if (function.getValue(peak.main) < quorum.minTop) { peaks.retainAll(peaks.subList(0, i)); } } @@ -308,9 +292,9 @@ public XYSeries getHiloSeries (int x1, for (int x = hilo.min; x <= hilo.max; x++) { hiloSeries.add(x, function.getDerivative(x)); } - } - hiloSeries.add(hilo.max, null); // No line between hilos + hiloSeries.add(hilo.max, null); // No line between hilos + } } return hiloSeries; @@ -412,10 +396,20 @@ public XYSeries getValueSeries (int x1, // Function values XYSeries valueSeries = function.getValueSeries(x1, x2); - if (minTopValue != null) { + if (quorum != null) { valueSeries.add(x1, null); // Cut link with function values - valueSeries.add(x1, minTopValue); - valueSeries.add(x2, minTopValue); + + if (quorum.xMin != null) { + x1 = quorum.xMin; + } + + valueSeries.add(x1, quorum.minTop); + + if (quorum.xMax != null) { + x2 = quorum.xMax; + } + + valueSeries.add(x2, quorum.minTop); } return valueSeries; @@ -458,12 +452,12 @@ public ChartPlotter plot (ChartPlotter plotter, { // Peaks if ((getPeaks() != null) && !peaks.isEmpty()) { - plotter.add(getPeakSeries(), Colors.CHART_PEAK); + plotter.add(getPeakSeries(x1, x2), Colors.CHART_PEAK); } // HiLos if (hilos != null) { - plotter.add(getHiloSeries(), Colors.CHART_HILO, true); // With shapes + plotter.add(getHiloSeries(x1, x2), Colors.CHART_HILO, true); // With shapes } // Values (w/ threshold?) @@ -479,6 +473,19 @@ public ChartPlotter plot (ChartPlotter plotter, return plotter; } + //-----------// + // setQuorum // + //-----------// + /** + * Assign quorum information. + * + * @param quorum quorum level and perhaps range + */ + public void setQuorum (Quorum quorum) + { + this.quorum = quorum; + } + //------------// // createPeak // //------------// @@ -534,14 +541,14 @@ private Range createPeak (int pMin, private TreeMap replay (Range peak) { - TreeMap thresholds = new TreeMap(); + TreeMap thresholds = new TreeMap<>(); final int main = peak.main; // argMax(peak.min, peak.max); // for merged peak? final int pMin = peak.min; final int pMax = peak.max; int total = function.getValue(main); int start = main; int stop = main; - thresholds.put(main, new Double(0)); + thresholds.put(main, 0.0); do { int before = (start == pMin) ? 0 : function.getValue(start - 1); @@ -577,7 +584,7 @@ private void retrieveHiLos () { logger.debug("Retrieving {} hilos", name); - hilos = new ArrayList(); + hilos = new ArrayList<>(); DerPeak hiPeak = null; DerPeak loPeak = null; @@ -633,7 +640,57 @@ private void retrieveHiLos () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------// + // Quorum // + //--------// + /** + * Defines a quorum level, with optional x range. + */ + public static class Quorum + { + + /** + * Quorum minimum value. + */ + public final int minTop; + + /** + * Range start if any. + */ + public final Integer xMin; + + /** + * Range stop if any. + */ + public final Integer xMax; + + /** + * Create a Quorum object, with specified range. + * + * @param minTop the minimum count + * @param xMin range starting x value + * @param xMax range stopping x value + */ + public Quorum (int minTop, + Integer xMin, + Integer xMax) + { + this.minTop = minTop; + this.xMin = xMin; + this.xMax = xMax; + } + + /** + * Create a Quorum object. + * + * @param minTop the minimum count + */ + public Quorum (int minTop) + { + this(minTop, null, null); + } + } + //---------// // DerPeak // //---------// @@ -642,7 +699,6 @@ private void retrieveHiLos () */ private static class DerPeak { - //~ Instance fields ------------------------------------------------------------------------ /** x at beginning of range. */ public final int min; @@ -653,15 +709,13 @@ private static class DerPeak /** True when peak cannot be extended anymore. */ private boolean finished; - //~ Constructors --------------------------------------------------------------------------- - public DerPeak (int min, - int max) + DerPeak (int min, + int max) { this.min = min; this.max = max; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { diff --git a/src/main/org/audiveris/omr/math/Histogram.java b/src/main/org/audiveris/omr/math/Histogram.java index 6c81836c0..c69ab5dd8 100644 --- a/src/main/org/audiveris/omr/math/Histogram.java +++ b/src/main/org/audiveris/omr/math/Histogram.java @@ -38,12 +38,10 @@ * in buckets, the buckets identities being values of type K. * * @param the precise number type for histogram buckets - * * @author Hervé Bitteur */ public class Histogram { - //~ Instance fields ---------------------------------------------------------------------------- /** To sort peaks by decreasing value. */ public final Comparator> reversePeakComparator = new Comparator>() @@ -58,7 +56,8 @@ public int compare (PeakEntry e1, }; /** To sort double peaks by decreasing value. */ - public final Comparator> reverseDoublePeakComparator = new Comparator>() + public final Comparator> reverseDoublePeakComparator + = new Comparator>() { @Override public int compare (PeakEntry e1, @@ -86,12 +85,11 @@ public int compare (MaxEntry e1, * - K for the type of entity to be accumulated * - Integer for the cumulated number in each bucket */ - protected final SortedMap map = new TreeMap(); + protected final SortedMap map = new TreeMap<>(); /** Total count. */ protected int totalCount = 0; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Histogram object, with no pre-defined range of buckets. */ @@ -99,10 +97,14 @@ public Histogram () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // bucketSet // //-----------// + /** + * Report the set of buckets. + * + * @return histogram buckets + */ public Set bucketSet () { return map.keySet(); @@ -111,6 +113,9 @@ public Set bucketSet () //-------// // clear // //-------// + /** + * Empty the histogram. + */ public void clear () { map.clear(); @@ -120,6 +125,11 @@ public void clear () //------------// // dataString // //------------// + /** + * Report histogram content as a string. + * + * @return content as string + */ public String dataString () { StringBuilder sb = new StringBuilder("["); @@ -144,6 +154,11 @@ public String dataString () //----------// // entrySet // //----------// + /** + * Report histogram content as an entry set. + * + * @return entry set + */ public Set> entrySet () { return map.entrySet(); @@ -152,6 +167,11 @@ public Set> entrySet () //-------------// // firstBucket // //-------------// + /** + * Report the first key in histogram range. + * + * @return first key + */ public K firstBucket () { if (map.isEmpty()) { @@ -165,7 +185,7 @@ public K firstBucket () // getCount // //----------// /** - * Report the count of specified bucket + * Report the cumulated count at specified bucket. * * @param bucket the bucket of interest * @return the bucket count (zero for any empty bucket) @@ -193,7 +213,7 @@ public int getCount (K bucket) */ public List> getDoublePeaks (int minCount) { - final List> peaks = new ArrayList>(); + final List> peaks = new ArrayList<>(); K start = null; K stop = null; K best = null; @@ -215,7 +235,7 @@ public List> getDoublePeaks (int minCount) } } else if (isAbove) { // Above -> Below peaks.add( - new PeakEntry( + new PeakEntry<>( createDoublePeak(start, best, stop, minCount), (double) bestCount / totalCount)); stop = start = best = null; @@ -228,7 +248,7 @@ public List> getDoublePeaks (int minCount) // Last range if (isAbove) { peaks.add( - new PeakEntry( + new PeakEntry<>( createDoublePeak(start, best, stop, minCount), (double) bestCount / totalCount)); } @@ -249,7 +269,7 @@ public List> getDoublePeaks (int minCount) */ public List> getLocalMaxima () { - final List> maxima = new ArrayList>(); + final List> maxima = new ArrayList<>(); K prevKey = null; int prevValue = 0; boolean growing = false; @@ -264,7 +284,7 @@ public List> getLocalMaxima () } else { if (growing) { // End of a local max - maxima.add(new MaxEntry(prevKey, prevValue / (double) totalCount)); + maxima.add(new MaxEntry<>(prevKey, prevValue / (double) totalCount)); } growing = false; @@ -382,7 +402,6 @@ public PeakEntry getPeak (double quorumRatio, return peak; } - // // //----------// // // getPeaks // // //----------// @@ -401,7 +420,7 @@ public PeakEntry getPeak (double quorumRatio, // boolean absolute, // boolean sorted) // { - // final List> peaks = new ArrayList>(); + // final List> peaks = new ArrayList<>(); // K start = null; // K stop = null; // K best = null; @@ -482,6 +501,12 @@ public int getTotalCount () //---------------// // increaseCount // //---------------// + /** + * Increase bucket with provided value. + * + * @param bucket bucket to increase + * @param delta added value + */ public void increaseCount (K bucket, int delta) { @@ -499,6 +524,11 @@ public void increaseCount (K bucket, //------------// // lastBucket // //------------// + /** + * Report the last key in histogram range. + * + * @return last key + */ public K lastBucket () { if (map.isEmpty()) { @@ -511,6 +541,11 @@ public K lastBucket () //-------// // print // //-------// + /** + * Print content to provided stream. + * + * @param stream output + */ public void print (PrintStream stream) { stream.println(dataString()); @@ -554,6 +589,11 @@ public String toString () //--------// // values // //--------// + /** + * Report the collection of buckets counts. + * + * @return buckets counts + */ public Collection values () { return map.values(); @@ -614,8 +654,9 @@ private double preciseKey (K prev, double prevCount = getCount(prev); double nextCount = getCount(next); - return ((prev.doubleValue() * (nextCount - count)) - + (next.doubleValue() * (count - prevCount))) / (nextCount - prevCount); + return ((prev.doubleValue() * (nextCount - count)) + (next.doubleValue() * (count + - prevCount))) + / (nextCount - prevCount); } //---------// @@ -636,27 +677,39 @@ private K prevKey (K key) return null; } - //~ Inner Interfaces --------------------------------------------------------------------------- //------------// // HistoEntry // //------------// + /** + * @param specific type for x + */ public static interface HistoEntry { - //~ Methods -------------------------------------------------------------------------------- + /** + * Report the x value for highest count in bucket + * + * @return x value for best count + */ K getBest (); + /** + * Report the highest count + * + * @return the highest count in the bucket + */ double getValue (); } - //~ Inner Classes ------------------------------------------------------------------------------ //------------// // DoublePeak // //------------// + /** + * A peak of double values. + */ public static class DoublePeak extends Peak { - //~ Constructors --------------------------------------------------------------------------- private DoublePeak (double first, double best, @@ -671,19 +724,25 @@ private DoublePeak (double first, //----------// /** * A counted maximum value. + * + * @param precise type */ public static class MaxEntry implements HistoEntry { - //~ Instance fields ------------------------------------------------------------------------ - /** Key at local maximum */ + /** Key at local maximum. */ private K key; - /** Related count (normalized by total histogram count) */ + /** Related count. (normalized by total histogram count) */ private final double value; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a MaxEntry object + * + * @param key x value + * @param value y value + */ public MaxEntry (K key, double value) { @@ -691,7 +750,11 @@ public MaxEntry (K key, this.value = value; } - //~ Methods -------------------------------------------------------------------------------- + /** + * Report x value + * + * @return x + */ @Override public K getBest () { @@ -699,7 +762,9 @@ public K getBest () } /** - * @return the key + * Report x value + * + * @return the x key */ public K getKey () { @@ -707,17 +772,24 @@ public K getKey () } /** - * @return the value + * Set x value + * + * @param key x value */ - @Override - public double getValue () + public void setKey (K key) { - return value; + this.key = key; } - public void setKey (K key) + /** + * Report y value + * + * @return the y value + */ + @Override + public double getValue () { - this.key = key; + return value; } @Override @@ -732,10 +804,11 @@ public String toString () //------// /** * We are interested in the triplet: first, best, second. + * + * @param precise x type */ public static class Peak { - //~ Instance fields ------------------------------------------------------------------------ /** Key at beginning of range. */ public final K first; @@ -746,7 +819,13 @@ public static class Peak /** Key at end of range. */ public final K second; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a Peak object. + * + * @param first x at start + * @param best x at best y + * @param second x at stop + */ public Peak (K first, K best, K second) @@ -756,7 +835,6 @@ public Peak (K first, this.second = second; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { @@ -773,11 +851,12 @@ public String toString () //-----------// /** * A counted peak. + * + * @param number type */ public static class PeakEntry implements HistoEntry { - //~ Instance fields ------------------------------------------------------------------------ /** The peak data */ private final Peak key; @@ -785,7 +864,12 @@ public static class PeakEntry /** Count at best value (normalized by total histogram count) */ private final double value; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a PeakEntry. + * + * @param key x value + * @param value y value + */ public PeakEntry (Peak key, double value) { @@ -793,7 +877,11 @@ public PeakEntry (Peak key, this.value = value; } - //~ Methods -------------------------------------------------------------------------------- + /** + * Report best bucket in peak range. + * + * @return best bucket + */ @Override public K getBest () { diff --git a/src/main/org/audiveris/omr/math/InjectionSolver.java b/src/main/org/audiveris/omr/math/InjectionSolver.java index 3cdfefa44..56cd31cdf 100644 --- a/src/main/org/audiveris/omr/math/InjectionSolver.java +++ b/src/main/org/audiveris/omr/math/InjectionSolver.java @@ -35,7 +35,6 @@ */ public class InjectionSolver { - //~ Instance fields ---------------------------------------------------------------------------- /** Size of domain. */ private final int domainSize; @@ -58,7 +57,6 @@ public class InjectionSolver /** Configuration being worked upon. */ private final int[] config; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new instance of InjectionSolver * @@ -80,7 +78,6 @@ public InjectionSolver (int domainSize, config = new int[domainSize]; } - //~ Methods ------------------------------------------------------------------------------------ //-------// // solve // //-------// @@ -152,13 +149,11 @@ private void inspect (final int id, } } - //~ Inner Interfaces --------------------------------------------------------------------------- /** * Interface {@code Distance} provides the measurement for individual mapping costs. */ public static interface Distance { - //~ Methods -------------------------------------------------------------------------------- /** * Report the distance when mapping element 'id' of domain to element 'ir' of diff --git a/src/main/org/audiveris/omr/math/IntegerFunction.java b/src/main/org/audiveris/omr/math/IntegerFunction.java index 43828b5c7..abc789bca 100644 --- a/src/main/org/audiveris/omr/math/IntegerFunction.java +++ b/src/main/org/audiveris/omr/math/IntegerFunction.java @@ -44,11 +44,9 @@ */ public class IntegerFunction { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(IntegerFunction.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Minimum x value. */ protected final int xMin; @@ -58,7 +56,6 @@ public class IntegerFunction /** Array of y value for each x. */ private final int[] values; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates an instance of {@code IntegerFunction}. * @@ -73,7 +70,6 @@ public IntegerFunction (int xMin, values = new int[xMax - xMin + 1]; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // addValue // //----------// @@ -234,7 +230,7 @@ public List getLocalMaxima (int x1, x1 = Math.max(x1, xMin); x2 = Math.min(x2, xMax); - final List maxima = new ArrayList(); + final List maxima = new ArrayList<>(); Integer prevX = null; int prevY = 0; boolean growing = false; @@ -259,17 +255,15 @@ public List getLocalMaxima (int x1, } // Sort by decreasing y values - Collections.sort( - maxima, - new Comparator() - { - @Override - public int compare (Integer x1, - Integer x2) - { - return Integer.compare(getValue(x2), getValue(x1)); // Reverse - } - }); + Collections.sort(maxima, new Comparator() + { + @Override + public int compare (Integer x1, + Integer x2) + { + return Integer.compare(getValue(x2), getValue(x1)); // Reverse + } + }); return maxima; } @@ -424,7 +418,7 @@ public void print (PrintStream stream) for (int x = xMin + 1; x <= xMax; x++) { int der = getDerivative(x); - stream.format(" %d:%d/%+d\n", x, getValue(x), der); + stream.format(" %d:%d/%+d%n", x, getValue(x), der); } stream.println("]"); diff --git a/src/main/org/audiveris/omr/math/Line.java b/src/main/org/audiveris/omr/math/Line.java index 15af74c95..224b17807 100644 --- a/src/main/org/audiveris/omr/math/Line.java +++ b/src/main/org/audiveris/omr/math/Line.java @@ -31,7 +31,6 @@ */ public interface Line { - //~ Methods ------------------------------------------------------------------------------------ /** * Compute the orthogonal distance between the line and the provided point. @@ -175,14 +174,12 @@ void includePoint (double x, */ double yAtXExt (double x); - //~ Inner Classes ------------------------------------------------------------------------------ /** * Specific exception raised when trying to invert a non-invertible line. */ static class NonInvertibleLineException extends RuntimeException { - //~ Constructors --------------------------------------------------------------------------- NonInvertibleLineException (String message) { @@ -196,7 +193,6 @@ static class NonInvertibleLineException static class UndefinedLineException extends RuntimeException { - //~ Constructors --------------------------------------------------------------------------- UndefinedLineException (String message) { diff --git a/src/main/org/audiveris/omr/math/LineUtil.java b/src/main/org/audiveris/omr/math/LineUtil.java index 5299f176b..fc2127177 100644 --- a/src/main/org/audiveris/omr/math/LineUtil.java +++ b/src/main/org/audiveris/omr/math/LineUtil.java @@ -31,7 +31,6 @@ */ public abstract class LineUtil { - //~ Methods ------------------------------------------------------------------------------------ //----------// // bisector // @@ -270,7 +269,7 @@ public static Point2D.Double intersectionAtX (Point2D p1, Point2D p2, double x) { - return intersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, 0, x, 1000); + return intersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, 0, x, 1_000); } //-----------------// @@ -287,7 +286,7 @@ public static Point2D.Double intersectionAtX (Point2D p1, public static Point2D.Double intersectionAtX (Line2D line, double x) { - return intersection(line.getX1(), line.getY1(), line.getX2(), line.getY2(), x, 0, x, 1000); + return intersection(line.getX1(), line.getY1(), line.getX2(), line.getY2(), x, 0, x, 1_000); } //-----------------// @@ -306,15 +305,8 @@ public static Point2D.Double intersectionAtX (Point2D p1, double slope, double x) { - return intersection( - p1.getX(), - p1.getY(), - p1.getX() + 1000, - p1.getY() + (1000 * slope), - x, - 0, - x, - 1000); + return intersection(p1.getX(), p1.getY(), p1.getX() + 1_000, p1.getY() + (1_000 * slope), x, + 0, x, 1_000); } //-----------------// @@ -333,7 +325,7 @@ public static Point2D.Double intersectionAtY (Point2D p1, Point2D p2, double y) { - return intersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), 0, y, 1000, y); + return intersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), 0, y, 1_000, y); } //-----------------// @@ -352,15 +344,9 @@ public static Point2D.Double intersectionAtY (Point2D p1, double invertedSlope, double y) { - return intersection( - p1.getX(), - p1.getY(), - p1.getX() + (1000 * invertedSlope), - p1.getY() + 1000, - 0, - y, - 1000, - y); + return intersection(p1.getX(), p1.getY(), p1.getX() + (1_000 * invertedSlope), p1.getY() + + 1_000, 0, y, + 1_000, y); } //-----------------// @@ -377,7 +363,7 @@ public static Point2D.Double intersectionAtY (Point2D p1, public static Point2D.Double intersectionAtY (Line2D line, double y) { - return intersection(line.getX1(), line.getY1(), line.getX2(), line.getY2(), 0, y, 1000, y); + return intersection(line.getX1(), line.getY1(), line.getX2(), line.getY2(), 0, y, 1_000, y); } //----------// @@ -437,7 +423,7 @@ public static double xAtY (Point2D p1, Point2D p2, double y) { - return intersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), 0, y, 1000, y).x; + return intersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), 0, y, 1_000, y).x; } //------// @@ -454,7 +440,7 @@ public static double xAtY (Point2D p1, public static double xAtY (Line2D line, double y) { - return intersection(line.getX1(), line.getY1(), line.getX2(), line.getY2(), 0, y, 1000, y).x; + return intersection(line.getX1(), line.getY1(), line.getX2(), line.getY2(), 0, y, 1_000, y).x; } //------// @@ -473,15 +459,8 @@ public static double yAtX (Point2D p1, double slope, double x) { - return intersection( - p1.getX(), - p1.getY(), - p1.getX() + 1000, - p1.getY() + (1000 * slope), - x, - 0, - x, - 1000).y; + return intersection(p1.getX(), p1.getY(), p1.getX() + 1_000, p1.getY() + (1_000 * slope), x, + 0, x, 1_000).y; } //------// @@ -498,7 +477,7 @@ public static double yAtX (Point2D p1, public static double yAtX (Line2D line, double x) { - return intersection(line.getX1(), line.getY1(), line.getX2(), line.getY2(), x, 0, x, 1000).y; + return intersection(line.getX1(), line.getY1(), line.getX2(), line.getY2(), x, 0, x, 1_000).y; } //------// @@ -517,6 +496,10 @@ public static double yAtX (Point2D p1, Point2D p2, double x) { - return intersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, 0, x, 1000).y; + return intersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, 0, x, 1_000).y; + } + + private LineUtil () + { } } diff --git a/src/main/org/audiveris/omr/math/NaturalSpline.java b/src/main/org/audiveris/omr/math/NaturalSpline.java index 898b05597..92ece4689 100644 --- a/src/main/org/audiveris/omr/math/NaturalSpline.java +++ b/src/main/org/audiveris/omr/math/NaturalSpline.java @@ -46,33 +46,30 @@ *

                            * Internally the spline is composed of a sequence of curves, one curve between two consecutive * knots. Each curve is a bezier curve defined by the 2 related knots separated by 2 control - * points.

                            + * points. *

                            * At each knot, continuity in ensured up to the second derivative. - * The second derivative is set to zero at first and last knots of the whole spline.

                            + * The second derivative is set to zero at first and last knots of the whole spline. *

                            * Degenerated cases: When the sequence of knots contains only 3 or 2 points, the spline degenerates * to a quadratic or a straight line respectively. If less than two points are provided, the spline - * cannot be created.

                            + * cannot be created. *

                            * Cf - * http://www.cse.unsw.edu.au/~lambert/splines/

                            + * http://www.cse.unsw.edu.au/~lambert/splines/ * * @author Hervé Bitteur */ public class NaturalSpline extends GeoPath { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(NaturalSpline.class); - //~ Instance fields ---------------------------------------------------------------------------- private Point2D first; // Cached for faster access. Really useful??? private Point2D last; // Cached for faster access. Really useful??? - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new NaturalSpline object from a sequence of connected shapes. * @@ -85,103 +82,6 @@ private NaturalSpline (Shape... curves) } } - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // interpolate // - //-------------// - /** - * Create the natural spline that interpolates the provided knots - * - * @param points the provided points - * @return the resulting spline curve - */ - public static NaturalSpline interpolate (Collection points) - { - Objects.requireNonNull(points, "NaturalSpline cannot interpolate null arrays"); - - double[] xx = new double[points.size()]; - double[] yy = new double[points.size()]; - - int i = -1; - - for (Point2D pt : points) { - xx[++i] = pt.getX(); - yy[i] = pt.getY(); - } - - return interpolate(xx, yy); - } - - //-------------// - // interpolate // - //-------------// - /** - * Create the natural spline that interpolates the provided knots - * - * @param xx the abscissae of the provided points - * @param yy the ordinates of the provided points - * @return the resulting spline curve - */ - public static NaturalSpline interpolate (double[] xx, - double[] yy) - { - // Check parameters - Objects.requireNonNull(xx, "NaturalSpline cannot interpolate null arrays"); - Objects.requireNonNull(yy, "NaturalSpline cannot interpolate null arrays"); - - if (xx.length != yy.length) { - throw new IllegalArgumentException( - "NaturalSpline interpolation needs consistent coordinates"); - } - - // Number of segments - final int n = xx.length - 1; - - if (n < 1) { - throw new IllegalArgumentException( - "NaturalSpline interpolation needs at least 2 points"); - } - - if (n == 1) { - // Use a Line - return new NaturalSpline(new Line2D.Double(xx[0], yy[0], xx[1], yy[1])); - } else if (n == 2) { - // Use a Quadratic (TODO: check this formula...) - // double t = (xx[1] - xx[0]) / (xx[2] - xx[0]); - // double u = 1 - t; - // double cpx = (xx[1] - (u * u * xx[0]) - (t * t * xx[2])) / 2 * t * u; - // double cpy = (yy[1] - (u * u * yy[0]) - (t * t * yy[2])) / 2 * t * u; - return new NaturalSpline( - new QuadCurve2D.Double( - xx[0], - yy[0], - (2 * xx[1]) - ((xx[0] + xx[2]) / 2), - (2 * yy[1]) - ((yy[0] + yy[2]) / 2), - xx[2], - yy[2])); - } else { - // Use a sequence of cubics - double[] dx = getCubicDerivatives(xx); - double[] dy = getCubicDerivatives(yy); - Shape[] curves = new Shape[n]; - - for (int i = 0; i < n; i++) { - // Build each segment curve - curves[i] = new CubicCurve2D.Double( - xx[i], - yy[i], - xx[i] + (dx[i] / 3), - yy[i] + (dy[i] / 3), - xx[i + 1] - (dx[i + 1] / 3), - yy[i + 1] - (dy[i + 1] / 3), - xx[i + 1], - yy[i + 1]); - } - - return new NaturalSpline(curves); - } - } - //---------------// // getFirstPoint // //---------------// @@ -263,6 +163,12 @@ public void render (Graphics2D g, //------// // xAtY // //------// + /** + * Report x abscissa value at provided y ordinate. + * + * @param y provided ordinate + * @return x + */ public int xAtY (int y) { return (int) Math.rint(xAtY((double) y)); @@ -305,7 +211,8 @@ public double xDerivativeAtY (double y) double cpx2 = coords[2]; return ((-3 * p1.x * u * u) + (3 * cpx1 * ((u * u) - (2 * u * t))) - + (3 * cpx2 * ((2 * t * u) - (t * t))) + (3 * p2.x * t * t)) / deltaY; + + (3 * cpx2 * ((2 * t * u) - (t * t))) + + (3 * p2.x * t * t)) / deltaY; } default: @@ -316,6 +223,12 @@ public double xDerivativeAtY (double y) //------// // yAtX // //------// + /** + * Report y ordinate at provided x abscissa. + * + * @param x provided abscissa + * @return y + */ public int yAtX (int x) { return (int) Math.rint(yAtX((double) x)); @@ -358,7 +271,8 @@ public double yDerivativeAtX (double x) double cpy2 = buffer[3]; return ((-3 * p1.y * u * u) + (3 * cpy1 * ((u * u) - (2 * u * t))) - + (3 * cpy2 * ((2 * t * u) - (t * t))) + (3 * p2.y * t * t)) / deltaX; + + (3 * cpy2 * ((2 * t * u) - (t * t))) + + (3 * p2.y * t * t)) / deltaX; } default: @@ -366,6 +280,102 @@ public double yDerivativeAtX (double x) } } + //-------------// + // interpolate // + //-------------// + /** + * Create the natural spline that interpolates the provided knots + * + * @param points the provided points + * @return the resulting spline curve + */ + public static NaturalSpline interpolate (Collection points) + { + Objects.requireNonNull(points, "NaturalSpline cannot interpolate null arrays"); + + double[] xx = new double[points.size()]; + double[] yy = new double[points.size()]; + + int i = -1; + + for (Point2D pt : points) { + xx[++i] = pt.getX(); + yy[i] = pt.getY(); + } + + return interpolate(xx, yy); + } + + //-------------// + // interpolate // + //-------------// + /** + * Create the natural spline that interpolates the provided knots + * + * @param xx the abscissae of the provided points + * @param yy the ordinates of the provided points + * @return the resulting spline curve + */ + public static NaturalSpline interpolate (double[] xx, + double[] yy) + { + // Check parameters + Objects.requireNonNull(xx, "NaturalSpline cannot interpolate null arrays"); + Objects.requireNonNull(yy, "NaturalSpline cannot interpolate null arrays"); + + if (xx.length != yy.length) { + throw new IllegalArgumentException( + "NaturalSpline interpolation needs consistent coordinates"); + } + + // Number of segments + final int n = xx.length - 1; + + if (n < 1) { + throw new IllegalArgumentException( + "NaturalSpline interpolation needs at least 2 points"); + } + + if (n == 1) { + // Use a Line + return new NaturalSpline(new Line2D.Double(xx[0], yy[0], xx[1], yy[1])); + } else if (n == 2) { + // Use a Quadratic (TODO: check this formula...) + // double t = (xx[1] - xx[0]) / (xx[2] - xx[0]); + // double u = 1 - t; + // double cpx = (xx[1] - (u * u * xx[0]) - (t * t * xx[2])) / 2 * t * u; + // double cpy = (yy[1] - (u * u * yy[0]) - (t * t * yy[2])) / 2 * t * u; + return new NaturalSpline( + new QuadCurve2D.Double( + xx[0], + yy[0], + (2 * xx[1]) - ((xx[0] + xx[2]) / 2), + (2 * yy[1]) - ((yy[0] + yy[2]) / 2), + xx[2], + yy[2])); + } else { + // Use a sequence of cubics + double[] dx = getCubicDerivatives(xx); + double[] dy = getCubicDerivatives(yy); + Shape[] curves = new Shape[n]; + + for (int i = 0; i < n; i++) { + // Build each segment curve + curves[i] = new CubicCurve2D.Double( + xx[i], + yy[i], + xx[i] + (dx[i] / 3), + yy[i] + (dy[i] / 3), + xx[i + 1] - (dx[i + 1] / 3), + yy[i + 1] - (dy[i + 1] / 3), + xx[i + 1], + yy[i + 1]); + } + + return new NaturalSpline(curves); + } + } + //---------------------// // getCubicDerivatives // //---------------------// @@ -394,6 +404,7 @@ private static double[] getCubicDerivatives (double[] z) * | 1 4 1| | . | |3(z[n] - z[n-2])| * [ 1 2] [D[n]] [3(z[n] - z[n-1])] * + *

                            * by using row operations to convert the matrix to upper triangular * and then back substitution. */ diff --git a/src/main/org/audiveris/omr/math/NeuralNetwork.java b/src/main/org/audiveris/omr/math/NeuralNetwork.java index 7d7812c71..4322fbb55 100644 --- a/src/main/org/audiveris/omr/math/NeuralNetwork.java +++ b/src/main/org/audiveris/omr/math/NeuralNetwork.java @@ -64,16 +64,12 @@ @XmlRootElement(name = "neural-network") public class NeuralNetwork { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - NeuralNetwork.class); + private static final Logger logger = LoggerFactory.getLogger(NeuralNetwork.class); - /** Un/marshalling context for use with JAXB */ + /** Un/marshalling context for use with JAXB. */ private static volatile JAXBContext jaxbContext; - //~ Instance fields ---------------------------------------------------------------------------- - // /** Size of input layer. */ @XmlAttribute(name = "input-size") private final int inputSize; @@ -116,7 +112,6 @@ public class NeuralNetwork /** To trigger training stop. */ private transient volatile boolean stopping = false; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a neural network, with specified number of cells in each * layer, and default values. @@ -125,8 +120,8 @@ public class NeuralNetwork * @param hiddenSize number of cells in hidden layer * @param outputSize number of cells in output layer * @param amplitude amplitude (less than or = 1.0) for initial random values - * @param inputLabels array of labels for input cells, or null - * @param outputLabels array of labels for output cells, or null + * @param inputLabels array of labels for input cells, perhaps empty + * @param outputLabels array of labels for output cells, perhaps empty */ public NeuralNetwork (int inputSize, int hiddenSize, @@ -151,17 +146,17 @@ public NeuralNetwork (int inputSize, // Labels for input, if any this.inputLabels = new StringArray(inputLabels); - if ((inputLabels != null) && (inputLabels.length != inputSize)) { + if (inputLabels.length != inputSize) { throw new IllegalArgumentException( - "Inconsistent input labels " + inputLabels + " vs " + inputSize); + "Inconsistent input labels size " + inputLabels.length + " vs " + inputSize); } // Labels for output, if any this.outputLabels = new StringArray(outputLabels); - if ((outputLabels != null) && (outputLabels.length != outputSize)) { + if (outputLabels.length != outputSize) { throw new IllegalArgumentException( - "Inconsistent output labels " + outputLabels + " vs " + outputSize); + "Inconsistent output labels size " + outputLabels.length + " vs " + outputSize); } logger.debug("Network created"); @@ -175,8 +170,8 @@ public NeuralNetwork (int inputSize, * @param hiddenSize number of cells in hidden layer * @param outputSize number of cells in output layer * @param amplitude amplitude (less than or = 1.0) for initial random values - * @param inputLabels array of labels for input cells, or null - * @param outputLabels array of labels for output cells, or null + * @param inputLabels array of labels for input cells, perhaps empty + * @param outputLabels array of labels for output cells, perhaps empty * @param learningRate learning rate factor * @param momentum momentum from last adjustment * @param epochs number of epochs in training @@ -209,8 +204,6 @@ private NeuralNetwork () outputLabels = null; } - //~ Methods ------------------------------------------------------------------------------------ - // //--------// // backup // //--------// @@ -328,12 +321,14 @@ public int getOutputSize () * Marshal the NeuralNetwork to its XML file * * @param os the XML output stream, which is not closed by this method - * @throws JAXBException if a XML serialization error occurred + * @throws JAXBException if a XML serialization error occurred * @throws XMLStreamException if there are any problems writing to the stream - * @throws IOException if something goes wrong during IO operations + * @throws IOException if something goes wrong during IO operations */ public void marshal (OutputStream os) - throws JAXBException, XMLStreamException, IOException + throws JAXBException, + XMLStreamException, + IOException { Jaxb.marshal(this, os, getJaxbContext()); logger.debug("Network marshalled"); @@ -358,9 +353,9 @@ public void restore (Backup backup) // Make sure backup is compatible with this neural network if ((backup.hiddenWeights.length != hiddenSize) - || (backup.hiddenWeights[0].length != (inputSize + 1)) - || (backup.outputWeights.length != outputSize) - || (backup.outputWeights[0].length != (hiddenSize + 1))) { + || (backup.hiddenWeights[0].length != (inputSize + 1)) + || (backup.outputWeights.length != outputSize) + || (backup.outputWeights[0].length != (hiddenSize + 1))) { throw new IllegalArgumentException("Incompatible backup"); } @@ -381,7 +376,6 @@ public void restore (Backup backup) * @param hiddens provided buffer for hidden values, or null * @param outputs preallocated array for the computed output values, or null if not already * allocated - * * @return the computed output values */ public double[] run (double[] inputs, @@ -465,6 +459,9 @@ public void setMomentum (double momentum) //------// // stop // //------// + /** + * + */ public void stop () { stopping = true; @@ -542,15 +539,15 @@ public void train (double[][] inputs, // Update the output weights for (int io = outputSize - 1; io >= 0; io--) { for (int ih = hiddenSize - 1; ih >= 0; ih--) { - double dw = (learningRate * outputGrads[io] * hiddens[ih]) - + (momentum * outputDeltas[io][ih + 1]); + double dw = (learningRate * outputGrads[io] * hiddens[ih]) + (momentum + * outputDeltas[io][ih + + 1]); outputWeights[io][ih + 1] += dw; outputDeltas[io][ih + 1] = dw; } // Bias - double dw = (learningRate * outputGrads[io]) - + (momentum * outputDeltas[io][0]); + double dw = (learningRate * outputGrads[io]) + (momentum * outputDeltas[io][0]); outputWeights[io][0] += dw; outputDeltas[io][0] = dw; } @@ -558,15 +555,15 @@ public void train (double[][] inputs, // Update the hidden weights for (int ih = hiddenSize - 1; ih >= 0; ih--) { for (int i = inputSize - 1; i >= 0; i--) { - double dw = (learningRate * hiddenGrads[ih] * inputs[ip][i]) - + (momentum * hiddenDeltas[ih][i + 1]); + double dw = (learningRate * hiddenGrads[ih] * inputs[ip][i]) + (momentum + * hiddenDeltas[ih][i + + 1]); hiddenWeights[ih][i + 1] += dw; hiddenDeltas[ih][i + 1] = dw; } // Bias - double dw = (learningRate * hiddenGrads[ih]) - + (momentum * hiddenDeltas[ih][0]); + double dw = (learningRate * hiddenGrads[ih]) + (momentum * hiddenDeltas[ih][0]); hiddenWeights[ih][0] += dw; hiddenDeltas[ih][0] = dw; } @@ -601,105 +598,11 @@ public void train (double[][] inputs, } final long dur = System.currentTimeMillis() - startTime; - logger.info( - String.format( - "Duration %,d seconds, %d iterations on %d patterns", - dur / 1000, - epochs, - patterns)); + logger.info(String.format("Duration %,d seconds, %d iterations on %d patterns", dur / 1_000, + epochs, patterns)); stopping = false; } - //-----------// - // unmarshal // - //-----------// - /** - * Unmarshal the provided XML stream to allocate the corresponding NeuralNetwork. - * - * @param in the input stream that contains the network definition in XML format. - * The stream is not closed by this method - * - * @return the allocated network. - * @exception JAXBException raised when unmarshalling goes wrong - */ - public static NeuralNetwork unmarshal (InputStream in) - throws JAXBException - { - Unmarshaller um = getJaxbContext().createUnmarshaller(); - NeuralNetwork nn = (NeuralNetwork) um.unmarshal(in); - logger.debug("Network unmarshalled"); - - return nn; - } - - //-------------// - // cloneMatrix // - //-------------// - /** - * Create a clone of the provided matrix. - * - * @param matrix the matrix to clone - * @return the clone - */ - private static double[][] cloneMatrix (double[][] matrix) - { - final int rowNb = matrix.length; - final int colNb = matrix[0].length; - - double[][] clone = new double[rowNb][]; - - for (int row = rowNb - 1; row >= 0; row--) { - clone[row] = new double[colNb]; - System.arraycopy(matrix[row], 0, clone[row], 0, colNb); - } - - return clone; - } - - //--------------// - // createMatrix // - //--------------// - /** - * Create and initialize a matrix, with random values. - * Random values are between -amplitude and +amplitude - * - * @param rowNb number of rows - * @param colNb number of columns - * - * @return the properly initialized matrix - */ - private static double[][] createMatrix (int rowNb, - int colNb, - double amplitude) - { - double[][] matrix = new double[rowNb][]; - - for (int row = rowNb - 1; row >= 0; row--) { - double[] vector = new double[colNb]; - matrix[row] = vector; - - for (int col = colNb - 1; col >= 0; col--) { - vector[col] = amplitude * (1.0 - (2 * Math.random())); - } - } - - return matrix; - } - - //----------------// - // getJaxbContext // - //----------------// - private static JAXBContext getJaxbContext () - throws JAXBException - { - // Lazy creation - if (jaxbContext == null) { - jaxbContext = JAXBContext.newInstance(NeuralNetwork.class); - } - - return jaxbContext; - } - //------------// // dumpMatrix // //------------// @@ -796,8 +699,94 @@ private double sigmoidDif (double val) return val * (1 - val); } - //~ Inner Classes ------------------------------------------------------------------------------ - // + //-----------// + // unmarshal // + //-----------// + /** + * Unmarshal the provided XML stream to allocate the corresponding NeuralNetwork. + * + * @param in the input stream that contains the network definition in XML format. + * The stream is not closed by this method + * @return the allocated network. + * @exception JAXBException raised when unmarshalling goes wrong + */ + public static NeuralNetwork unmarshal (InputStream in) + throws JAXBException + { + Unmarshaller um = getJaxbContext().createUnmarshaller(); + NeuralNetwork nn = (NeuralNetwork) um.unmarshal(in); + logger.debug("Network unmarshalled"); + + return nn; + } + + //-------------// + // cloneMatrix // + //-------------// + /** + * Create a clone of the provided matrix. + * + * @param matrix the matrix to clone + * @return the clone + */ + private static double[][] cloneMatrix (double[][] matrix) + { + final int rowNb = matrix.length; + final int colNb = matrix[0].length; + + double[][] clone = new double[rowNb][]; + + for (int row = rowNb - 1; row >= 0; row--) { + clone[row] = new double[colNb]; + System.arraycopy(matrix[row], 0, clone[row], 0, colNb); + } + + return clone; + } + + //--------------// + // createMatrix // + //--------------// + /** + * Create and initialize a matrix, with random values. + * Random values are between -amplitude and +amplitude + * + * @param rowNb number of rows + * @param colNb number of columns + * @return the properly initialized matrix + */ + private static double[][] createMatrix (int rowNb, + int colNb, + double amplitude) + { + double[][] matrix = new double[rowNb][]; + + for (int row = rowNb - 1; row >= 0; row--) { + double[] vector = new double[colNb]; + matrix[row] = vector; + + for (int col = colNb - 1; col >= 0; col--) { + vector[col] = amplitude * (1.0 - (2 * Math.random())); + } + } + + return matrix; + } + + //----------------// + // getJaxbContext // + //----------------// + private static JAXBContext getJaxbContext () + throws JAXBException + { + // Lazy creation + if (jaxbContext == null) { + jaxbContext = JAXBContext.newInstance(NeuralNetwork.class); + } + + return jaxbContext; + } + //--------// // Backup // //--------// @@ -810,13 +799,11 @@ private double sigmoidDif (double val) */ public static class Backup { - //~ Instance fields ------------------------------------------------------------------------ private double[][] hiddenWeights; private double[][] outputWeights; - //~ Constructors --------------------------------------------------------------------------- // Private constructor private Backup (double[][] hiddenWeights, double[][] outputWeights) @@ -831,17 +818,15 @@ private Backup (double[][] hiddenWeights, //-------------// private static class StringArray { - //~ Instance fields ------------------------------------------------------------------------ @XmlValue String[] strings; - //~ Constructors --------------------------------------------------------------------------- - public StringArray () + StringArray () { } - public StringArray (String[] strings) + StringArray (String[] strings) { this.strings = strings; } diff --git a/src/main/org/audiveris/omr/math/Parabola.java b/src/main/org/audiveris/omr/math/Parabola.java index 40ba95b60..7aef430f2 100644 --- a/src/main/org/audiveris/omr/math/Parabola.java +++ b/src/main/org/audiveris/omr/math/Parabola.java @@ -25,18 +25,15 @@ import java.util.List; /** - * Class {@code Parabola} handles a parabola curve whose parameters (a,b,c) are such:. - *

                            - * y = a*x^2 + b*x + c
                            - * 
                            - * + * Class {@code Parabola} handles a parabola curve whose parameters (a,b,c) are such + * that: y = a * x ^ 2 + b * x + c. + *

                            * See http://mathforum.org/library/drmath/view/72047.html * * @author Hervé Bitteur */ public class Parabola { - //~ Instance fields ---------------------------------------------------------------------------- private double a; @@ -46,7 +43,6 @@ public class Parabola private final double dist; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Parabola object. * @@ -70,7 +66,11 @@ public Parabola (List points) dist = computeDistance(xx, yy); } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Report the mean distance from fitting points to parabola. + * + * @return mean distance to parabola + */ public double getMeanDistance () { return dist; @@ -124,12 +124,12 @@ private void fit (double[] xx, } double den = ((s00 * s20 * s40) - (s10 * s10 * s40) - (s00 * s30 * s30) - + (2 * s10 * s20 * s30)) - (s20 * s20 * s20); + + (2 * s10 * s20 * s30)) - (s20 * s20 * s20); a = (((s01 * s10 * s30) - (s11 * s00 * s30) - (s01 * s20 * s20) + (s11 * s10 * s20) - + (s21 * s00 * s20)) - (s21 * s10 * s10)) / den; + + (s21 * s00 * s20)) - (s21 * s10 * s10)) / den; b = (((s11 * s00 * s40) - (s01 * s10 * s40) + (s01 * s20 * s30)) - (s21 * s00 * s30) - - (s11 * s20 * s20) + (s21 * s10 * s20)) / den; + - (s11 * s20 * s20) + (s21 * s10 * s20)) / den; c = (((s01 * s20 * s40) - (s11 * s10 * s40) - (s01 * s30 * s30) + (s11 * s20 * s30) - + (s21 * s10 * s30)) - (s21 * s20 * s20)) / den; + + (s21 * s10 * s30)) - (s21 * s20 * s20)) / den; } } diff --git a/src/main/org/audiveris/omr/math/PointUtil.java b/src/main/org/audiveris/omr/math/PointUtil.java index b79f6606d..e37bb89a7 100644 --- a/src/main/org/audiveris/omr/math/PointUtil.java +++ b/src/main/org/audiveris/omr/math/PointUtil.java @@ -35,7 +35,6 @@ */ public abstract class PointUtil { - //~ Static fields/initializers ----------------------------------------------------------------- /** To compare points on abscissa. */ public static final Comparator byAbscissa = new Comparator() @@ -59,7 +58,13 @@ public int compare (Point p1, } }; - //~ Methods ------------------------------------------------------------------------------------ + /** + * Not meant to be instantiated. + */ + private PointUtil () + { + } + //----------// // addition // //----------// @@ -243,6 +248,12 @@ public static Point2D times (Point2D p, //----------// // toString // //----------// + /** + * A toString() that can cope with null instance. + * + * @param p point instance + * @return string value + */ public static String toString (Point p) { if (p == null) { @@ -254,4 +265,5 @@ public static String toString (Point p) return sb.toString(); } + } diff --git a/src/main/org/audiveris/omr/math/PointsCollector.java b/src/main/org/audiveris/omr/math/PointsCollector.java index 58e1ff817..344027fe8 100644 --- a/src/main/org/audiveris/omr/math/PointsCollector.java +++ b/src/main/org/audiveris/omr/math/PointsCollector.java @@ -27,10 +27,11 @@ /** * Class {@code PointsCollector} is meant to cumulate points coordinates, perhaps * within a provided absolute region of interest. + * + * @author Hervé Bitteur */ public class PointsCollector { - //~ Instance fields ---------------------------------------------------------------------------- /** The absolute region of interest, if any. */ private Rectangle roi; @@ -44,7 +45,6 @@ public class PointsCollector /** The ordinates. */ private int[] yy; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new PointsCollector object, with absolute roi area taken as capacity. * @@ -69,7 +69,6 @@ public PointsCollector (Rectangle roi, yy = new int[capacity]; } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // ensureCapacity // //----------------// diff --git a/src/main/org/audiveris/omr/math/Polynomial.java b/src/main/org/audiveris/omr/math/Polynomial.java index 9b3181e8f..ced716853 100644 --- a/src/main/org/audiveris/omr/math/Polynomial.java +++ b/src/main/org/audiveris/omr/math/Polynomial.java @@ -30,15 +30,16 @@ */ public class Polynomial { - //~ Instance fields ---------------------------------------------------------------------------- - /** The degree of polynomial */ + /** Epsilon value meant for equality testing: {@value}. */ + private static final double EPSILON = 1E-5; + + /** The degree of polynomial. */ protected int degree; - /** Polynomial coefficient vector, from low to high order */ + /** Polynomial coefficient vector, from low to high order. */ protected double[] coefficients; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Polynomial object (actually just a monomial). * Example new Polynomial(3,2) = 3x^2 @@ -58,44 +59,6 @@ public Polynomial (double c, this.degree = degree(); } - //~ Methods ------------------------------------------------------------------------------------ - //------// - // main // - //------// - // test client - public static void main (String[] args) - { - Polynomial zero = new Polynomial(0, 0); - - Polynomial p1 = new Polynomial(4, 3); // 4x^3 - Polynomial p2 = new Polynomial(3, 2); // 3x^2 - Polynomial p3 = new Polynomial(1, 0); // 1 - Polynomial p4 = new Polynomial(2, 1); // 2x - Polynomial p = p1.plus(p2).plus(p3).plus(p4); // 4x^3 + 3x^2 + 2x + 1 - - Polynomial q1 = new Polynomial(3, 2); // 3x^2 - Polynomial q2 = new Polynomial(5, 0); // 5 - Polynomial q = q1.plus(q2); // 3x^2 + 5 - - Polynomial r = p.plus(q); - Polynomial s = p.times(q); - Polynomial t = p.compose(q); - - System.out.println("zero(x) = " + zero); - System.out.println("p(x) = " + p); - System.out.println("q(x) = " + q); - System.out.println("p(x) + q(x) = " + r); - System.out.println("p(x) * q(x) = " + s); - System.out.println("p(q(x)) = " + t); - System.out.println("0 - p(x) = " + zero.minus(p)); - System.out.println("p(3) = " + p.evaluate(3)); - System.out.println("p'(x) = " + p.derivative()); - System.out.println("p''(x) = " + p.derivative().derivative()); - System.out.println("p'''(x) = " + p.derivative().derivative().derivative()); - System.out.println( - "p''''(x) = " + p.derivative().derivative().derivative().derivative()); - } - //--------// // degree // //--------// @@ -176,7 +139,7 @@ public boolean eq (Polynomial that) } for (int i = degree; i >= 0; i--) { - if (coefficients[i] != that.coefficients[i]) { + if (Math.abs(coefficients[i] - that.coefficients[i]) > EPSILON) { return false; } } @@ -322,24 +285,65 @@ public String toString () return coefficients[1] + "x + " + coefficients[0]; } - String s = coefficients[degree] + "x^" + degree; + StringBuilder sb = new StringBuilder(coefficients[degree] + "x^" + degree); for (int i = degree - 1; i >= 0; i--) { if (coefficients[i] == 0) { continue; } else if (coefficients[i] > 0) { - s = s + " + " + (coefficients[i]); + sb.append(" + ").append(coefficients[i]); } else if (coefficients[i] < 0) { - s = s + " - " + (-coefficients[i]); + sb.append(" - ").append(-coefficients[i]); } if (i == 1) { - s += "x"; + sb.append("x"); } else if (i > 1) { - s = s + "x^" + i; + sb.append("x^").append(i); } } - return s; + return sb.toString(); } + // + // //------// + // // main // + // //------// + // /** + // * A main entry, just meant for a few tests + // * + // * @param args not used + // */ + // public static void main (String[] args) + // { + // Polynomial zero = new Polynomial(0, 0); + // + // Polynomial p1 = new Polynomial(4, 3); // 4x^3 + // Polynomial p2 = new Polynomial(3, 2); // 3x^2 + // Polynomial p3 = new Polynomial(1, 0); // 1 + // Polynomial p4 = new Polynomial(2, 1); // 2x + // Polynomial p = p1.plus(p2).plus(p3).plus(p4); // 4x^3 + 3x^2 + 2x + 1 + // + // Polynomial q1 = new Polynomial(3, 2); // 3x^2 + // Polynomial q2 = new Polynomial(5, 0); // 5 + // Polynomial q = q1.plus(q2); // 3x^2 + 5 + // + // Polynomial r = p.plus(q); + // Polynomial s = p.times(q); + // Polynomial t = p.compose(q); + // + // System.out.println("zero(x) = " + zero); + // System.out.println("p(x) = " + p); + // System.out.println("q(x) = " + q); + // System.out.println("p(x) + q(x) = " + r); + // System.out.println("p(x) * q(x) = " + s); + // System.out.println("p(q(x)) = " + t); + // System.out.println("0 - p(x) = " + zero.minus(p)); + // System.out.println("p(3) = " + p.evaluate(3)); + // System.out.println("p'(x) = " + p.derivative()); + // System.out.println("p''(x) = " + p.derivative().derivative()); + // System.out.println("p'''(x) = " + p.derivative().derivative().derivative()); + // System.out.println( + // "p''''(x) = " + p.derivative().derivative().derivative().derivative()); + // } } diff --git a/src/main/org/audiveris/omr/math/PoorManAlgebra.java b/src/main/org/audiveris/omr/math/PoorManAlgebra.java index a4fd2d8ad..3b51ec9e3 100644 --- a/src/main/org/audiveris/omr/math/PoorManAlgebra.java +++ b/src/main/org/audiveris/omr/math/PoorManAlgebra.java @@ -1,607 +1,597 @@ -//------------------------------------------------------------------------------------------------// -// // -// P o o r M a n A l g e b r a // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.math; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -/** - * Class {@code PoorManAlgebra} is a "poor man" implementation for a small number of - * classes of Nd4j and DeepLearning4j. - *

                            - * They are meant for temporary use in Audiveris 5.x where we want to remove dependency on these - * classes (and their need for a 64-bit architecture). - * The class methods are limited to the strict minimum. - * - * @author Hervé Bitteur - */ -public abstract class PoorManAlgebra -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(PoorManAlgebra.class); - - //~ Inner Interfaces --------------------------------------------------------------------------- - //----------// - // INDArray // - //----------// - public static interface INDArray - { - //~ Methods -------------------------------------------------------------------------------- - - /** - * in place addition of two NDArrays - * - * @param other the second ndarray to add - * @return the result of the addition - */ - INDArray addi (INDArray other); - - /** - * Returns the number of columns in this matrix (throws exception if not 2d) - * - * @return the number of columns in this matrix - */ - int columns (); - - /** - * In place division of a row vector - * - * @param rowVector the row vector used for division - * @return the result of the division - */ - INDArray diviRowVector (INDArray rowVector); - - /** - * Return the item at the linear index i - * - * @param i the index of the item to getScalar - * @return the item at index j - */ - double getDouble (int i); - - /** - * Returns the specified row. - * Throws an exception if its not a matrix - * - * @param i the row to getScalar - * @return the specified row - */ - INDArray getRow (int i); - - /** - * Returns the overall mean of this INDArray - * - * @param dimension the dimension to getScalar the mean along - * @return the mean along the specified dimension of this INDArray - */ - INDArray mean (int dimension); - - /** - * Returns the number of rows in this matrix (throws exception if not 2d) - * - * @return the number of rows in this matrix - */ - int rows (); - - /** - * Standard deviation of an INDArray along a dimension - * - * @param dimension the dimension to getScalar the std along - * @return the standard deviation along a particular dimension - */ - INDArray std (int dimension); - - /** - * In place subtraction of a row vector - * - * @param rowVector the row vector to subtract - * @return the result of the subtraction - */ - INDArray subiRowVector (INDArray rowVector); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //---------// - // DataSet // - //---------// - public static class DataSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final INDArray features; - - private final INDArray labels; - - //~ Constructors --------------------------------------------------------------------------- - public DataSet (INDArray features, - INDArray labels, - INDArray featuresMask, // Unused - INDArray labelsMask) // Unused - - { - this.features = features; - this.labels = labels; - } - - //~ Methods -------------------------------------------------------------------------------- - public INDArray getFeatures () - { - return features; - } - - public INDArray getLabels () - { - return labels; - } - } - - //------// - // Nd4j // - //------// - public static class Nd4j - { - //~ Static fields/initializers ------------------------------------------------------------- - - public static double EPS_THRESHOLD = 1e-5; - - //~ Methods -------------------------------------------------------------------------------- - /** - * Create an INDArray based on the given data layout - * - * @param data the data to use - * @return an INDArray with the given data layout - */ - public static INDArray create (double[][] data) - { - return new Matrix(data); - } - - /** - * Create an INDArray based on the given data layout - * - * @param data the data to use - * @return an INDArray with the given data layout - */ - public static INDArray create (double[] data) - { - return new Vector(data); - } - - /** - * Read in an INDArray from a data input stream - * - * @param dis the data input stream to read from - * @return the INDArray - * @throws IOException - */ - public static INDArray read (DataInputStream dis) - throws IOException - { - throw new UnsupportedOperationException("PoorManAlgebra.Nd4j.read() not supported."); - } - - /** - * Create a scalar nd array with the specified value and offset - * - * @param value the value of the scalar - * @return the scalar nd array - */ - public static INDArray scalar (double value) - { - return new Scalar(value); - } - - /** - * Write an INDArray to the specified output stream - * - * @param arr the array to write - * @param dataOutputStream the data output stream to write to - * @throws IOException - */ - public static void write (INDArray arr, - DataOutputStream dataOutputStream) - throws IOException - { - throw new UnsupportedOperationException("PoorManAlgebra.Nd4j.write() not supported."); - } - } - - private static class Matrix - implements INDArray - { - //~ Instance fields ------------------------------------------------------------------------ - - private final double[][] data; - - //~ Constructors --------------------------------------------------------------------------- - public Matrix (double[][] data) - { - final int rowNb = data.length; - final int colNb = data[0].length; - - this.data = new double[rowNb][]; - - for (int ir = 0; ir < rowNb; ir++) { - final double[] vector = data[ir]; - final double[] thisVector = new double[colNb]; - System.arraycopy(vector, 0, thisVector, 0, colNb); - this.data[ir] = thisVector; - } - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public INDArray addi (INDArray other) - { - if (other instanceof Scalar) { - final double val = ((Scalar) other).getDouble(0); - final int rowNb = rows(); - final int colNb = columns(); - - for (int ir = 0; ir < rowNb; ir++) { - final double[] vector = data[ir]; - - for (int ic = 0; ic < colNb; ic++) { - vector[ic] += val; - } - } - - return this; - } else { - throw new UnsupportedOperationException("Not supported yet."); - } - } - - @Override - public int columns () - { - return data[0].length; - } - - @Override - public INDArray diviRowVector (INDArray rowVector) - { - final int rowNb = rows(); - final int colNb = columns(); - - for (int ic = 0; ic < colNb; ic++) { - double val = rowVector.getDouble(ic); - - for (int ir = 0; ir < rowNb; ir++) { - data[ir][ic] /= val; - } - } - - return this; - } - - @Override - public double getDouble (int i) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public INDArray getRow (int i) - { - return Nd4j.create(data[i]); - } - - @Override - public INDArray mean (int unused) - { - final int rowNb = rows(); - final int colNb = columns(); - final double[] results = new double[colNb]; - - for (int ic = 0; ic < colNb; ic++) { - double s = 0; - - for (int ir = 0; ir < rowNb; ir++) { - s += data[ir][ic]; - } - - results[ic] = s / rowNb; - } - - return Nd4j.create(results); - } - - @Override - public int rows () - { - return data.length; - } - - @Override - public INDArray std (int unused) - { - final int colNb = columns(); - final int rowNb = rows(); - final double[] results = new double[colNb]; - - for (int ic = 0; ic < colNb; ic++) { - double s = 0; - double s2 = 0d; - - for (int ir = 0; ir < rowNb; ir++) { - double val = data[ir][ic]; - s += val; - s2 += (val * val); - } - - double biasedVariance = Math.max(0, (s2 - ((s * s) / rowNb)) / rowNb); - double variance = (rowNb * biasedVariance) / (rowNb - 1); - results[ic] = Math.sqrt(variance); - } - - return Nd4j.create(results); - } - - @Override - public INDArray subiRowVector (INDArray rowVector) - { - final int colNb = columns(); - final int rowNb = rows(); - - for (int ic = 0; ic < colNb; ic++) { - double val = rowVector.getDouble(ic); - - for (int ir = 0; ir < rowNb; ir++) { - data[ir][ic] -= val; - } - } - - return this; - } - - @Override - public String toString () - { - final int rowNb = rows(); - final StringBuilder sb = new StringBuilder("["); - - for (int ir = 0; ir < rowNb; ir++) { - INDArray row = getRow(ir); - sb.append("\n ").append(row); - } - - sb.append("\n]"); - - return sb.toString(); - } - } - - private static class Scalar - implements INDArray - { - //~ Instance fields ------------------------------------------------------------------------ - - private double data; - - //~ Constructors --------------------------------------------------------------------------- - public Scalar (double data) - { - this.data = data; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public INDArray addi (INDArray other) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public int columns () - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public INDArray diviRowVector (INDArray rowVector) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public double getDouble (int i) - { - return data; - } - - @Override - public INDArray getRow (int i) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public INDArray mean (int dimension) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public int rows () - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public INDArray std (int dimension) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public INDArray subiRowVector (INDArray rowVector) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public String toString () - { - - final StringBuilder sb = new StringBuilder("{"); - - sb.append(format(data)); - - sb.append("}"); - - return sb.toString(); - } - } - - private static String format (double val) - { - return String.format("%.2f", val); - } - - private static class Vector - implements INDArray - { - //~ Instance fields ------------------------------------------------------------------------ - - private final double[] data; - - //~ Constructors --------------------------------------------------------------------------- - public Vector (double[] data) - { - this.data = new double[data.length]; - System.arraycopy(data, 0, this.data, 0, data.length); - } - - // Meant for JAXB - private Vector () - { - data = null; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public INDArray addi (INDArray other) - { - if (other instanceof Scalar) { - final int colNb = data.length; - final double val = ((Scalar) other).getDouble(0); - - for (int ic = 0; ic < colNb; ic++) { - data[ic] += val; - } - - return this; - } else { - throw new UnsupportedOperationException("Not supported yet."); - } - } - - @Override - public int columns () - { - return data.length; - } - - @Override - public INDArray diviRowVector (INDArray rowVector) - { - final int colNb = data.length; - - for (int ic = 0; ic < colNb; ic++) { - double val = rowVector.getDouble(ic); - - data[ic] /= val; - } - - return this; - } - - @Override - public double getDouble (int i) - { - return data[i]; - } - - @Override - public INDArray getRow (int i) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public INDArray mean (int dimension) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public int rows () - { - return 1; - } - - @Override - public INDArray std (int dimension) - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public INDArray subiRowVector (INDArray rowVector) - { - final int colNb = columns(); - - for (int ic = 0; ic < colNb; ic++) { - double val = rowVector.getDouble(ic); - - data[ic] -= val; - } - - return this; - } - - @Override - public String toString () - { - final StringBuilder sb = new StringBuilder("["); - final int colNb = columns(); - - for (int ic = 0; ic < colNb; ic++) { - if (ic > 0) { - sb.append(", "); - } - - sb.append(format(data[ic])); - } - - sb.append("]"); - - return sb.toString(); - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// P o o r M a n A l g e b r a // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.math; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Class {@code PoorManAlgebra} is a "poor man" implementation for a small number of + * classes of Nd4j and DeepLearning4j. + *

                            + * They are meant for temporary use in Audiveris 5.x where we want to remove dependency on these + * classes (and their need for a 64-bit architecture). + * The class methods are limited to the strict minimum. + * + * @author Hervé Bitteur + */ +public abstract class PoorManAlgebra +{ + + private static final Logger logger = LoggerFactory.getLogger(PoorManAlgebra.class); + + private PoorManAlgebra () + { + } + + private static String format (double val) + { + return String.format("%.2f", val); + } + + //----------// + // INDArray // + //----------// + public static interface INDArray + { + + /** + * in place addition of two NDArrays + * + * @param other the second ndarray to add + * @return the result of the addition + */ + INDArray addi (INDArray other); + + /** + * Returns the number of columns in this matrix (throws exception if not 2d) + * + * @return the number of columns in this matrix + */ + int columns (); + + /** + * In place division of a row vector + * + * @param rowVector the row vector used for division + * @return the result of the division + */ + INDArray diviRowVector (INDArray rowVector); + + /** + * Return the item at the linear index i + * + * @param i the index of the item to getScalar + * @return the item at index j + */ + double getDouble (int i); + + /** + * Returns the specified row. + * Throws an exception if its not a matrix + * + * @param i the row to getScalar + * @return the specified row + */ + INDArray getRow (int i); + + /** + * Returns the overall mean of this INDArray + * + * @param dimension the dimension to getScalar the mean along + * @return the mean along the specified dimension of this INDArray + */ + INDArray mean (int dimension); + + /** + * Returns the number of rows in this matrix (throws exception if not 2d) + * + * @return the number of rows in this matrix + */ + int rows (); + + /** + * Standard deviation of an INDArray along a dimension + * + * @param dimension the dimension to getScalar the std along + * @return the standard deviation along a particular dimension + */ + INDArray std (int dimension); + + /** + * In place subtraction of a row vector + * + * @param rowVector the row vector to subtract + * @return the result of the subtraction + */ + INDArray subiRowVector (INDArray rowVector); + } + + //---------// + // DataSet // + //---------// + public static class DataSet + { + + private final INDArray features; + + private final INDArray labels; + + public DataSet (INDArray features, + INDArray labels, + INDArray featuresMask, // Unused + INDArray labelsMask) // Unused + + { + this.features = features; + this.labels = labels; + } + + public INDArray getFeatures () + { + return features; + } + + public INDArray getLabels () + { + return labels; + } + } + + //------// + // Nd4j // + //------// + public static class Nd4j + { + + public static final double EPS_THRESHOLD = 1e-5; + + /** + * Create an INDArray based on the given data layout + * + * @param data the data to use + * @return an INDArray with the given data layout + */ + public static INDArray create (double[][] data) + { + return new Matrix(data); + } + + /** + * Create an INDArray based on the given data layout + * + * @param data the data to use + * @return an INDArray with the given data layout + */ + public static INDArray create (double[] data) + { + return new Vector(data); + } + + /** + * Read in an INDArray from a data input stream + * + * @param dis the data input stream to read from + * @return the INDArray + * @throws IOException if anything goes wrong in Input + */ + public static INDArray read (DataInputStream dis) + throws IOException + { + throw new UnsupportedOperationException("PoorManAlgebra.Nd4j.read() not supported."); + } + + /** + * Create a scalar nd array with the specified value and offset + * + * @param value the value of the scalar + * @return the scalar nd array + */ + public static INDArray scalar (double value) + { + return new Scalar(value); + } + + /** + * Write an INDArray to the specified output stream + * + * @param arr the array to write + * @param dataOutputStream the data output stream to write to + * @throws IOException if anything goes wrong in Output + */ + public static void write (INDArray arr, + DataOutputStream dataOutputStream) + throws IOException + { + throw new UnsupportedOperationException("PoorManAlgebra.Nd4j.write() not supported."); + } + + private Nd4j () + { + } + } + + private static class Matrix + implements INDArray + { + + private final double[][] data; + + Matrix (double[][] data) + { + final int rowNb = data.length; + final int colNb = data[0].length; + + this.data = new double[rowNb][]; + + for (int ir = 0; ir < rowNb; ir++) { + final double[] vector = data[ir]; + final double[] thisVector = new double[colNb]; + System.arraycopy(vector, 0, thisVector, 0, colNb); + this.data[ir] = thisVector; + } + } + + @Override + public INDArray addi (INDArray other) + { + if (other instanceof Scalar) { + final double val = other.getDouble(0); + final int rowNb = rows(); + final int colNb = columns(); + + for (int ir = 0; ir < rowNb; ir++) { + final double[] vector = data[ir]; + + for (int ic = 0; ic < colNb; ic++) { + vector[ic] += val; + } + } + + return this; + } else { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + @Override + public int columns () + { + return data[0].length; + } + + @Override + public INDArray diviRowVector (INDArray rowVector) + { + final int rowNb = rows(); + final int colNb = columns(); + + for (int ic = 0; ic < colNb; ic++) { + double val = rowVector.getDouble(ic); + + for (int ir = 0; ir < rowNb; ir++) { + data[ir][ic] /= val; + } + } + + return this; + } + + @Override + public double getDouble (int i) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public INDArray getRow (int i) + { + return Nd4j.create(data[i]); + } + + @Override + public INDArray mean (int unused) + { + final int rowNb = rows(); + final int colNb = columns(); + final double[] results = new double[colNb]; + + for (int ic = 0; ic < colNb; ic++) { + double s = 0; + + for (int ir = 0; ir < rowNb; ir++) { + s += data[ir][ic]; + } + + results[ic] = s / rowNb; + } + + return Nd4j.create(results); + } + + @Override + public int rows () + { + return data.length; + } + + @Override + public INDArray std (int unused) + { + final int colNb = columns(); + final int rowNb = rows(); + final double[] results = new double[colNb]; + + for (int ic = 0; ic < colNb; ic++) { + double s = 0; + double s2 = 0d; + + for (int ir = 0; ir < rowNb; ir++) { + double val = data[ir][ic]; + s += val; + s2 += (val * val); + } + + double biasedVariance = Math.max(0, (s2 - ((s * s) / rowNb)) / rowNb); + double variance = (rowNb * biasedVariance) / (rowNb - 1); + results[ic] = Math.sqrt(variance); + } + + return Nd4j.create(results); + } + + @Override + public INDArray subiRowVector (INDArray rowVector) + { + final int colNb = columns(); + final int rowNb = rows(); + + for (int ic = 0; ic < colNb; ic++) { + double val = rowVector.getDouble(ic); + + for (int ir = 0; ir < rowNb; ir++) { + data[ir][ic] -= val; + } + } + + return this; + } + + @Override + public String toString () + { + final int rowNb = rows(); + final StringBuilder sb = new StringBuilder("["); + + for (int ir = 0; ir < rowNb; ir++) { + INDArray row = getRow(ir); + sb.append("\n ").append(row); + } + + sb.append("\n]"); + + return sb.toString(); + } + } + + private static class Scalar + implements INDArray + { + + private double data; + + Scalar (double data) + { + this.data = data; + } + + @Override + public INDArray addi (INDArray other) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int columns () + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public INDArray diviRowVector (INDArray rowVector) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public double getDouble (int i) + { + return data; + } + + @Override + public INDArray getRow (int i) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public INDArray mean (int dimension) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int rows () + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public INDArray std (int dimension) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public INDArray subiRowVector (INDArray rowVector) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("{"); + + sb.append(format(data)); + + sb.append("}"); + + return sb.toString(); + } + } + + private static class Vector + implements INDArray + { + + private final double[] data; + + Vector (double[] data) + { + this.data = new double[data.length]; + System.arraycopy(data, 0, this.data, 0, data.length); + } + + // Meant for JAXB + private Vector () + { + data = null; + } + + @Override + public INDArray addi (INDArray other) + { + if (other instanceof Scalar) { + final int colNb = data.length; + final double val = other.getDouble(0); + + for (int ic = 0; ic < colNb; ic++) { + data[ic] += val; + } + + return this; + } else { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + @Override + public int columns () + { + return data.length; + } + + @Override + public INDArray diviRowVector (INDArray rowVector) + { + final int colNb = data.length; + + for (int ic = 0; ic < colNb; ic++) { + double val = rowVector.getDouble(ic); + + data[ic] /= val; + } + + return this; + } + + @Override + public double getDouble (int i) + { + return data[i]; + } + + @Override + public INDArray getRow (int i) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public INDArray mean (int dimension) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int rows () + { + return 1; + } + + @Override + public INDArray std (int dimension) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public INDArray subiRowVector (INDArray rowVector) + { + final int colNb = columns(); + + for (int ic = 0; ic < colNb; ic++) { + double val = rowVector.getDouble(ic); + + data[ic] -= val; + } + + return this; + } + + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("["); + final int colNb = columns(); + + for (int ic = 0; ic < colNb; ic++) { + if (ic > 0) { + sb.append(", "); + } + + sb.append(format(data[ic])); + } + + sb.append("]"); + + return sb.toString(); + } + } + +} diff --git a/src/main/org/audiveris/omr/math/Population.java b/src/main/org/audiveris/omr/math/Population.java index 503595735..000d9bda2 100644 --- a/src/main/org/audiveris/omr/math/Population.java +++ b/src/main/org/audiveris/omr/math/Population.java @@ -36,7 +36,6 @@ @XmlRootElement(name = "population") public class Population { - //~ Static fields/initializers ----------------------------------------------------------------- /** * Variance is said "biased" when dividing by n, and said "unbiased" when dividing @@ -44,7 +43,6 @@ public class Population */ public static final boolean BIASED = false; - //~ Instance fields ---------------------------------------------------------------------------- /** Sum of measured values */ @XmlAttribute(name = "sum") private double s = 0d; @@ -57,7 +55,6 @@ public class Population @XmlAttribute(name = "count") private int n = 0; - //~ Constructors ------------------------------------------------------------------------------- /** * Construct a structure to cumulate the measured values. */ @@ -65,7 +62,6 @@ public Population () { } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // excludeValue // //--------------// @@ -112,7 +108,7 @@ public double getMeanValue () throw new RuntimeException("Population is empty"); } - return s / (double) n; + return s / n; } //----------------------// diff --git a/src/main/org/audiveris/omr/math/Projection.java b/src/main/org/audiveris/omr/math/Projection.java index 1abc80772..436c4ab8b 100644 --- a/src/main/org/audiveris/omr/math/Projection.java +++ b/src/main/org/audiveris/omr/math/Projection.java @@ -28,7 +28,6 @@ */ public interface Projection { - //~ Methods ------------------------------------------------------------------------------------ /** * Report a simplistic derivative value at 'pos' point @@ -83,7 +82,6 @@ public interface Projection void increment (int pos, int inc); - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Integer // //---------// @@ -93,11 +91,9 @@ void increment (int pos, public static class Integer extends Abstract { - //~ Instance fields ------------------------------------------------------------------------ private final int[] data; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Projection.Integer object. * @@ -111,7 +107,6 @@ public Integer (int start, data = new int[getLength()]; } - //~ Methods -------------------------------------------------------------------------------- @Override public final int getValue (int pos) { @@ -141,11 +136,9 @@ public final void increment (int pos, public static class Short extends Abstract { - //~ Instance fields ------------------------------------------------------------------------ private final short[] data; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Projection.Short object. * @@ -159,7 +152,6 @@ public Short (int start, data = new short[getLength()]; } - //~ Methods -------------------------------------------------------------------------------- @Override public final int getValue (int pos) { @@ -183,10 +175,12 @@ public final void increment (int pos, //----------// // Abstract // //----------// + /** + * Abstract implementation. + */ public abstract class Abstract implements Projection { - //~ Instance fields ------------------------------------------------------------------------ /** First point on projection axis. */ protected final int start; @@ -194,7 +188,12 @@ public abstract class Abstract /** Last point on projection axis. */ protected final int stop; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create an instance of Abstract class. + * + * @param start first value in range + * @param stop last value in range + */ protected Abstract (int start, int stop) { @@ -202,7 +201,6 @@ protected Abstract (int start, this.stop = stop; } - //~ Methods -------------------------------------------------------------------------------- @Override public final int getDerivative (int pos) { diff --git a/src/main/org/audiveris/omr/math/Range.java b/src/main/org/audiveris/omr/math/Range.java index 9795919de..9b08fea9a 100644 --- a/src/main/org/audiveris/omr/math/Range.java +++ b/src/main/org/audiveris/omr/math/Range.java @@ -35,7 +35,6 @@ @XmlRootElement(name = "range") public class Range { - //~ Static fields/initializers ----------------------------------------------------------------- /** To sort by increasing main. */ public static final Comparator byMain = new Comparator() @@ -48,7 +47,6 @@ public int compare (Range e1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** Key at beginning of range. */ @XmlAttribute public final int min; @@ -61,7 +59,6 @@ public int compare (Range e1, @XmlAttribute public final int max; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code Range} object. * @@ -86,10 +83,14 @@ private Range () this.max = 0; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getWidth // //----------// + /** + * Report the range width, (max - min + 1). + * + * @return range width + */ public int getWidth () { return max - min + 1; diff --git a/src/main/org/audiveris/omr/math/Rational.java b/src/main/org/audiveris/omr/math/Rational.java index 96034df38..83f3ea274 100644 --- a/src/main/org/audiveris/omr/math/Rational.java +++ b/src/main/org/audiveris/omr/math/Rational.java @@ -34,25 +34,24 @@ * Class {@code Rational} implements non-mutable rational numbers * (composed of a numerator and a denominator). *

                            - * Invariants:

                              + * Invariants: + *
                                *
                              1. The rational data is always kept in reduced form : gcd(num,den) == 1
                              2. *
                              3. The denominator value is always kept positive : den ≥ 1
                              4. *
                              *

                              - * It is (un)marshallable through JAXB.

                              + * It is (un)marshallable through JAXB. + *

                              * * @author Hervé Bitteur */ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "rational") -@XmlType(propOrder = { - "num", "den"} -) +@XmlType(propOrder = {"num", "den"}) public class Rational extends Number implements Comparable { - //~ Static fields/initializers ----------------------------------------------------------------- /** The zero rational instance. */ public static final Rational ZERO = new Rational(0, 1); @@ -66,7 +65,6 @@ public class Rational /** Max rational value. */ public static final Rational MAX_VALUE = new Rational(Integer.MAX_VALUE, 1); - //~ Instance fields ---------------------------------------------------------------------------- /** Final numerator value. */ @XmlAttribute public final int num; @@ -75,7 +73,6 @@ public class Rational @XmlAttribute public final int den; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a final Rational instance * @@ -112,51 +109,6 @@ private Rational () num = den = 1; } - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // decode // - //--------// - public static Rational decode (String str) - { - final int slash = str.indexOf('/'); - - if (slash == -1) { - return new Rational(Integer.decode(str), 1); - } - - final int num = Integer.decode(str.substring(0, slash)); - final int den = Integer.decode(str.substring(slash + 1)); - - return new Rational(num, den); - } - - //-----// - // gcd // - //-----// - public static Rational gcd (Rational a, - Rational b) - { - if (a.num == 0) { - return b; - } else { - return new Rational(1, GCD.lcm(a.den, b.den)); - } - } - - //-----// - // gcd // - //-----// - public static Rational gcd (Rational... vals) - { - Rational s = Rational.ZERO; - - for (Rational val : vals) { - s = gcd(s, val); - } - - return s; - } - //-----// // abs // //-----// @@ -174,7 +126,7 @@ public Rational abs () // compareTo // //-----------// /** - * Comparison + * Comparison for total ordering. * * @param that the other rational instance * @return -1, 0, 1 if this <, ==, > that respectively @@ -182,12 +134,16 @@ public Rational abs () @Override public int compareTo (Rational that) { + if (this == that) { + return 0; + } + int a = this.num * that.den; int b = this.den * that.num; // Detect overflow, using the fact that den's are always >= 1 - if ((Integer.signum(b) != Integer.signum(that.num)) - || (Integer.signum(a) != Integer.signum(this.num))) { + if ((Integer.signum(b) != Integer.signum(that.num)) || (Integer.signum(a) != Integer.signum( + this.num))) { BigInteger bigThisNum = BigInteger.valueOf(this.num); BigInteger bigThisDen = BigInteger.valueOf(this.den); BigInteger bigThatNum = BigInteger.valueOf(that.num); @@ -197,7 +153,7 @@ public int compareTo (Rational that) return A.compareTo(B); } else { - return Integer.signum(a - b); + return Integer.compare(a, b); } } @@ -428,14 +384,85 @@ public String toString () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------// + // decode // + //--------// + /** + * Decode provided string as a rational value. + * + * @param str input string, such as "3/4" or "5" (equivalent of "5/1") + * @return decoded rational value + * @throws NumberFormatException if input string is invalid + */ + public static Rational decode (String str) + { + final String[] tokens = str.split("\\s*/\\s*"); + + switch (tokens.length) { + case 2: { + int num = Integer.decode(tokens[0].trim()); + int den = Integer.decode(tokens[1].trim()); + + return new Rational(num, den); + } + case 1: { + int num = Integer.decode(tokens[0].trim()); + return new Rational(num, 1); + } + default: + throw new NumberFormatException(str); + } + } + + //-----// + // gcd // + //-----// + /** + * Compute the Greatest Common Divisor of two rational values. + * + * @param a a rational + * @param b another rational + * @return the GCD of a and b + */ + public static Rational gcd (Rational a, + Rational b) + { + if (a.num == 0) { + return b; + } else { + return new Rational(1, GCD.lcm(a.den, b.den)); + } + } + + //-----// + // gcd // + //-----// + /** + * Compute the Greatest Common Divisor of several rational values. + * + * @param vals rational values + * @return the GCD of all + */ + public static Rational gcd (Rational... vals) + { + Rational s = Rational.ZERO; + + for (Rational val : vals) { + s = gcd(s, val); + } + + return s; + } + //---------// // Adapter // //---------// + /** + * JAXB adapter to un/marshal a Rational object as "num/den". + */ public static class Adapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public String marshal (Rational val) diff --git a/src/main/org/audiveris/omr/math/ReversePathIterator.java b/src/main/org/audiveris/omr/math/ReversePathIterator.java index ddacf3f2d..264f3ec21 100644 --- a/src/main/org/audiveris/omr/math/ReversePathIterator.java +++ b/src/main/org/audiveris/omr/math/ReversePathIterator.java @@ -75,7 +75,6 @@ public class ReversePathIterator implements PathIterator { - //~ Instance fields ---------------------------------------------------------------------------- /** The winding rule. */ private final int windingRule; @@ -92,7 +91,6 @@ public class ReversePathIterator /** The index into the segment types during iteration. */ private int segmentIndex = 0; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an inverted path iterator from a standard one, keeping the winding rule. * @@ -230,7 +228,6 @@ public ReversePathIterator (PathIterator original, } } - //~ Methods ------------------------------------------------------------------------------------ /** * Returns the coordinates and type of the current path segment in * the iteration. @@ -303,6 +300,44 @@ public int currentSegment (float[] coords) return segmentType; } + /** + * Returns the winding rule for determining the interior of the + * path. This just returns the winding rule of the original path, + * which may or may not be what is wanted. + * + * @return the winding rule. + * @see #WIND_EVEN_ODD + * @see #WIND_NON_ZERO + */ + @Override + public int getWindingRule () + { + return windingRule; + } + + /** + * Tests if the iteration is complete. + * + * @return {@code true} if all the segments have + * been read; {@code false} otherwise. + */ + @Override + public boolean isDone () + { + return segmentIndex >= segmentTypes.length; + } + + /** + * Moves the iterator to the next segment of the path forwards + * along the primary direction of traversal as long as there are + * more points in that direction. + */ + @Override + public void next () + { + coordIndex += coordinatesForSegmentType(segmentTypes[segmentIndex++]); + } + /** * Get a reverse path iterator for a shape, keeping the shape's winding * rule. @@ -335,7 +370,7 @@ public static PathIterator getReversePathIterator (Shape shape, * winding rule. * * @param shape shape for which a reverse transformed path iterator is needed - * @param at the affine transform + * @param at the affine transform * @return reverse transformed path iterator */ public static PathIterator getReversePathIterator (Shape shape, @@ -350,7 +385,7 @@ public static PathIterator getReversePathIterator (Shape shape, * * @param shape shape for which a reverse transformed flattened path * iterator is needed - * @param at the affine transform + * @param at the affine transform * @param flatness flatness epsilon * @return reverse transformed flattened path iterator */ @@ -395,7 +430,7 @@ public static PathIterator getReversePathIterator (Shape shape, * * @param shape shape for which a reverse transformed path iterator is * needed - * @param at the affine transform + * @param at the affine transform * @param windingRule winding rule of newly created iterator * @return reverse transformed path iterator */ @@ -411,7 +446,7 @@ public static PathIterator getReversePathIterator (Shape shape, * * @param shape shape for which a reverse transformed flattened path * iterator is needed - * @param at the affine transform + * @param at the affine transform * @param flatness flatness epsilon * @param windingRule winding rule of newly created iterator * @return reverse transformed flattened path iterator @@ -424,44 +459,6 @@ public static PathIterator getReversePathIterator (Shape shape, return new ReversePathIterator(shape.getPathIterator(at, flatness), windingRule); } - /** - * Returns the winding rule for determining the interior of the - * path. This just returns the winding rule of the original path, - * which may or may not be what is wanted. - * - * @return the winding rule. - * @see #WIND_EVEN_ODD - * @see #WIND_NON_ZERO - */ - @Override - public int getWindingRule () - { - return windingRule; - } - - /** - * Tests if the iteration is complete. - * - * @return {@code true} if all the segments have - * been read; {@code false} otherwise. - */ - @Override - public boolean isDone () - { - return segmentIndex >= segmentTypes.length; - } - - /** - * Moves the iterator to the next segment of the path forwards - * along the primary direction of traversal as long as there are - * more points in that direction. - */ - @Override - public void next () - { - coordIndex += coordinatesForSegmentType(segmentTypes[segmentIndex++]); - } - /** * Get the number of coordinates needed for a segment type. * diff --git a/src/main/org/audiveris/omr/math/TableUtil.java b/src/main/org/audiveris/omr/math/TableUtil.java index d1cb6b422..213cfd304 100644 --- a/src/main/org/audiveris/omr/math/TableUtil.java +++ b/src/main/org/audiveris/omr/math/TableUtil.java @@ -36,7 +36,6 @@ */ public abstract class TableUtil { - //~ Methods ------------------------------------------------------------------------------------ /** * Print out a table of int values. @@ -125,7 +124,6 @@ public static void dump (String title, } } - // // /** // * Print out a PixelBuffer. // * @@ -267,6 +265,12 @@ public static String printAbscissae (int width, //-------// // store // //-------// + /** + * Store the provided table as a BufferedImage to disk. + * + * @param id a distinguished name + * @param table the table to store + */ public static void store (String id, short[][] table) { @@ -291,7 +295,7 @@ public static void store (String id, for (int y = 0; y < height; y++) { int val = table[x][y]; val = (val * 255) / max; - pix[0] = (int) val; + pix[0] = val; raster.setPixel(x, y, pix); } } @@ -314,8 +318,6 @@ private static int[][] toInt (double[][] doubles) } } - System.out.println("max = " + max); - int[][] ints = new int[width][height]; for (int y = 0; y < height; y++) { @@ -326,4 +328,8 @@ private static int[][] toInt (double[][] doubles) return ints; } + + private TableUtil () + { + } } diff --git a/src/main/org/audiveris/omr/moments/ARTMoments.java b/src/main/org/audiveris/omr/moments/ARTMoments.java index 1992c2a6f..946edd9f3 100644 --- a/src/main/org/audiveris/omr/moments/ARTMoments.java +++ b/src/main/org/audiveris/omr/moments/ARTMoments.java @@ -32,7 +32,6 @@ public interface ARTMoments extends OrthogonalMoments { - //~ Static fields/initializers ----------------------------------------------------------------- /** Number of angular indices. */ public static final int ANGULAR = 20; //16; //12; @@ -43,7 +42,6 @@ public interface ARTMoments /** Number of ART moments. */ public static final int MOMENT_COUNT = -1 + (ANGULAR * RADIAL); - //~ Methods ------------------------------------------------------------------------------------ /** * Report the argument value for provided phase and radius indices. * diff --git a/src/main/org/audiveris/omr/moments/AbstractExtractor.java b/src/main/org/audiveris/omr/moments/AbstractExtractor.java index e35eb2fd6..b9f33edf9 100644 --- a/src/main/org/audiveris/omr/moments/AbstractExtractor.java +++ b/src/main/org/audiveris/omr/moments/AbstractExtractor.java @@ -27,13 +27,11 @@ * Class {@code AbstractExtractor} provides the basis for moments extraction. * * @param actual descriptor type - * * @author Hervé Bitteur */ public abstract class AbstractExtractor> implements MomentsExtractor { - //~ Instance fields ---------------------------------------------------------------------------- /** Input abscissae. */ protected int[] xx; @@ -53,7 +51,6 @@ public abstract class AbstractExtractor> /** The target descriptor. */ protected D descriptor; - //~ Methods ------------------------------------------------------------------------------------ //---------// // extract // //---------// @@ -121,7 +118,7 @@ private Point2D findCenterOfMass () m01 += yy[i]; } - return new Point2D.Double((double) m10 / (double) mass, (double) m01 / (double) mass); + return new Point2D.Double(m10 / (double) mass, m01 / (double) mass); } //------------// diff --git a/src/main/org/audiveris/omr/moments/BasicARTExtractor.java b/src/main/org/audiveris/omr/moments/BasicARTExtractor.java index 7a776e685..4acff90a4 100644 --- a/src/main/org/audiveris/omr/moments/BasicARTExtractor.java +++ b/src/main/org/audiveris/omr/moments/BasicARTExtractor.java @@ -38,7 +38,6 @@ public class BasicARTExtractor extends AbstractExtractor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -55,7 +54,6 @@ public class BasicARTExtractor initLUT(); } - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BasicARTExtractor object and process the provided foreground points. */ @@ -63,7 +61,6 @@ public BasicARTExtractor () { } - //~ Methods ------------------------------------------------------------------------------------ @Override public void reconstruct (WritableRaster raster) { @@ -175,14 +172,12 @@ private static void initLUT () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/moments/BasicARTMoments.java b/src/main/org/audiveris/omr/moments/BasicARTMoments.java index dcbed08f1..06de1d38f 100644 --- a/src/main/org/audiveris/omr/moments/BasicARTMoments.java +++ b/src/main/org/audiveris/omr/moments/BasicARTMoments.java @@ -31,7 +31,6 @@ public class BasicARTMoments implements ARTMoments { - //~ Instance fields ---------------------------------------------------------------------------- /** Module values */ private final double[][] modules = new double[ANGULAR][RADIAL]; @@ -45,7 +44,6 @@ public class BasicARTMoments /** Real values */ private final double[][] reals = new double[ANGULAR][RADIAL]; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BasicARTMoments object. */ @@ -53,7 +51,6 @@ public BasicARTMoments () { } - //~ Methods ------------------------------------------------------------------------------------ //------------// // distanceTo // //------------// @@ -191,7 +188,7 @@ public String toString () sb.append(" "); } - sb.append(String.format("%3.0f", 1000 * getMoment(p, r))); + sb.append(String.format("%3.0f", 1_000 * getMoment(p, r))); } } } diff --git a/src/main/org/audiveris/omr/moments/BasicLUT.java b/src/main/org/audiveris/omr/moments/BasicLUT.java index 8e5f5b9cb..345fa06ed 100644 --- a/src/main/org/audiveris/omr/moments/BasicLUT.java +++ b/src/main/org/audiveris/omr/moments/BasicLUT.java @@ -26,10 +26,9 @@ * * @author Hervé Bitteur */ -public final class BasicLUT +public class BasicLUT implements LUT { - //~ Instance fields ---------------------------------------------------------------------------- /** LUT radius. */ private final int RADIUS; @@ -40,7 +39,6 @@ public final class BasicLUT /** The table of values for each integer (x,y) location. */ private final double[][] table; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BasicLUT object. * @@ -57,7 +55,6 @@ public BasicLUT (int radius) table = new double[SIZE][SIZE]; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // assign // //--------// diff --git a/src/main/org/audiveris/omr/moments/BasicLegendreExtractor.java b/src/main/org/audiveris/omr/moments/BasicLegendreExtractor.java index 084c3964f..93ead2ef0 100644 --- a/src/main/org/audiveris/omr/moments/BasicLegendreExtractor.java +++ b/src/main/org/audiveris/omr/moments/BasicLegendreExtractor.java @@ -34,7 +34,6 @@ public class BasicLegendreExtractor extends AbstractExtractor { - //~ Static fields/initializers ----------------------------------------------------------------- /** Legendre polynomials. */ private static final Polynomial[] P = generatePolynomials(); @@ -66,7 +65,6 @@ public class BasicLegendreExtractor ///checkOrthogonal(); } - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BasicLegendreExtractor object. */ @@ -74,7 +72,6 @@ public BasicLegendreExtractor () { } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // reconstruct // //-------------// @@ -194,64 +191,6 @@ protected void extractMoments () } } - //---------------------// - // generatePolynomials // - //---------------------// - /** - * Generate all Legendre polynomials, iteratively up to ORDER. - * - * @return the array of polynomials - */ - private static Polynomial[] generatePolynomials () - { - Polynomial[] Q = new Polynomial[ORDER + 1]; - - Q[0] = new Polynomial(1, 0); - Q[1] = new Polynomial(1, 1); - - for (int n = 2; n <= ORDER; n++) { - Q[n] = Q[1].times(Q[n - 1]).times((2 * n) - 1).minus(Q[n - 2].times(n - 1)).times( - 1d / n); - } - - if (false) { - for (int n = 0; n <= ORDER; n++) { - System.out.println("P[" + n + "] = " + Q[n]); - } - } - - return Q; - } - - //---------// - // initLUT // - //---------// - /** - * Compute, once for all, the lookup table values. - */ - private static void initLUT () - { - final LUT anyLut = luts[0][0]; // Just for template - final int lutSize = anyLut.getSize(); - final int lutRadius = anyLut.getRadius(); - - for (int x = 0; x < lutSize; x++) { - double tx = (x - lutRadius) / (double) lutRadius; - - for (int y = 0; y < lutSize; y++) { - double ty = (y - lutRadius) / (double) lutRadius; - - for (int m = 0; m <= ORDER; m++) { - double pmx = P[m].evaluate(tx); - - for (int n = 0; n <= (ORDER - m); n++) { - luts[m][n].assign(x, y, pmx * P[n].evaluate(ty)); - } - } - } - } - } - //------------------------// // extractMomentsDirectly // Not using LUT (so rather slow...) //------------------------// @@ -315,4 +254,62 @@ private void extractMomentsDirectly () // } // } // } + //---------------------// + // generatePolynomials // + //---------------------// + /** + * Generate all Legendre polynomials, iteratively up to ORDER. + * + * @return the array of polynomials + */ + private static Polynomial[] generatePolynomials () + { + Polynomial[] Q = new Polynomial[ORDER + 1]; + + Q[0] = new Polynomial(1, 0); + Q[1] = new Polynomial(1, 1); + + for (int n = 2; n <= ORDER; n++) { + Q[n] = Q[1].times(Q[n - 1]).times((2 * n) - 1).minus(Q[n - 2].times(n - 1)).times( + 1d / n); + } + + if (false) { + for (int n = 0; n <= ORDER; n++) { + System.out.println("P[" + n + "] = " + Q[n]); + } + } + + return Q; + } + + //---------// + // initLUT // + //---------// + /** + * Compute, once for all, the lookup table values. + */ + private static void initLUT () + { + final LUT anyLut = luts[0][0]; // Just for template + final int lutSize = anyLut.getSize(); + final int lutRadius = anyLut.getRadius(); + + for (int x = 0; x < lutSize; x++) { + double tx = (x - lutRadius) / (double) lutRadius; + + for (int y = 0; y < lutSize; y++) { + double ty = (y - lutRadius) / (double) lutRadius; + + for (int m = 0; m <= ORDER; m++) { + double pmx = P[m].evaluate(tx); + + for (int n = 0; n <= (ORDER - m); n++) { + luts[m][n].assign(x, y, pmx * P[n].evaluate(ty)); + } + } + } + } + } + } diff --git a/src/main/org/audiveris/omr/moments/BasicLegendreMoments.java b/src/main/org/audiveris/omr/moments/BasicLegendreMoments.java index efd2b6890..a96e8d0e3 100644 --- a/src/main/org/audiveris/omr/moments/BasicLegendreMoments.java +++ b/src/main/org/audiveris/omr/moments/BasicLegendreMoments.java @@ -32,12 +32,10 @@ public class BasicLegendreMoments implements LegendreMoments { - //~ Instance fields ---------------------------------------------------------------------------- /** Resulting moments. */ protected double[][] moments = new double[ORDER + 1][ORDER + 1]; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BasicLegendreMoments object. */ @@ -45,7 +43,6 @@ public BasicLegendreMoments () { } - //~ Methods ------------------------------------------------------------------------------------ //------------// // distanceTo // //------------// @@ -102,8 +99,7 @@ public String toString () sb.append(" "); } - sb.append( - String.format(Locale.US, "%04.0f", 1000 * getMoment(m, n))); + sb.append(String.format(Locale.US, "%04.0f", 1_000 * getMoment(m, n))); } } } diff --git a/src/main/org/audiveris/omr/moments/GeometricMoments.java b/src/main/org/audiveris/omr/moments/GeometricMoments.java index ee5c9a358..953f5044d 100644 --- a/src/main/org/audiveris/omr/moments/GeometricMoments.java +++ b/src/main/org/audiveris/omr/moments/GeometricMoments.java @@ -36,11 +36,12 @@ */ public class GeometricMoments { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GeometricMoments.class); - // Hu coefficients are optional + /** + * Hu coefficients are optional. + */ public static final boolean useHuCoefficients = false; /** Number of features handled: {@value} */ @@ -81,12 +82,9 @@ public class GeometricMoments "h7", // 18 }; - //~ Instance fields ---------------------------------------------------------------------------- - // /** The various moments, implemented as an array of double's. */ private final double[] k = new double[size]; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new GeometricMoments object. * @@ -125,7 +123,7 @@ public GeometricMoments (int[] xx, int yMax = Integer.MIN_VALUE; // Normalized GeometricMoments - double n00 = (double) dim / (double) (unit * unit); + double n00 = dim / (double) (unit * unit); double n01 = 0d; double n02 = 0d; double n03 = 0d; @@ -223,22 +221,24 @@ public GeometricMoments (int[] xx, k[i++] = ((n20 - n02) * (n20 - n02)) + (4 * n11 * n11); // k[i++] = ((n30 - (3 * n12)) * (n30 - (3 * n12))) - + ((n03 - (3 * n21)) * (n03 - (3 * n21))); + + ((n03 - (3 * n21)) * (n03 - (3 * n21))); // k[i++] = ((n30 + n12) * (n30 + n12)) + ((n03 + n21) * (n03 + n21)); // k[i++] = ((n30 - (3 * n12)) * (n30 + n12) * (((n30 + n12) * (n30 + n12)) - - (3 * (n21 + n03) * (n21 + n03)))) - + ((n03 - (3 * n21)) * (n03 + n21) * (((n03 + n21) * (n03 + n21)) - - (3 * (n12 + n30) * (n12 + n30)))); + - (3 * (n21 + n03) * (n21 + n03)))) + + ((n03 - (3 * n21)) * (n03 + n21) + * (((n03 + n21) * (n03 + n21)) + - (3 * (n12 + n30) * (n12 + n30)))); // k[i++] = ((n20 - n02) * (((n30 + n12) * (n30 + n12)) - ((n03 + n21) * (n03 + n21)))) - + (4 * n11 * (n30 + n12) * (n03 + n21)); + + (4 * n11 * (n30 + n12) * (n03 + n21)); // k[i++] = (((3 * n21) - n03) * (n30 + n12) * (((n30 + n12) * (n30 + n12)) - - (3 * (n21 + n03) * (n21 + n03)))) - - (((3 * n12) - n30) * (n03 + n21) * (((n03 + n21) * (n03 + n21)) - - (3 * (n12 + n30) * (n12 + n30)))); + - (3 * (n21 + n03) * (n21 + n03)))) + - (((3 * n12) - n30) + * (n03 + n21) * (((n03 + n21) * (n03 + n21)) + - (3 * (n12 + n30) * (n12 + n30)))); } } @@ -252,7 +252,6 @@ public GeometricMoments () { } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // getCentroid // //-------------// @@ -279,20 +278,6 @@ public double getHeight () return k[2]; } - //----------// - // getLabel // - //----------// - /** - * Report the label related to moment at specified index. - * - * @param index the moment index - * @return the related index - */ - public static String getLabel (int index) - { - return labels[index]; - } - //--------// // getN12 // //--------// @@ -388,4 +373,18 @@ public String toString () return sb.toString(); } + + //----------// + // getLabel // + //----------// + /** + * Report the label related to moment at specified index. + * + * @param index the moment index + * @return the related index + */ + public static String getLabel (int index) + { + return labels[index]; + } } diff --git a/src/main/org/audiveris/omr/moments/LUT.java b/src/main/org/audiveris/omr/moments/LUT.java index 3760c1ffe..c169afa90 100644 --- a/src/main/org/audiveris/omr/moments/LUT.java +++ b/src/main/org/audiveris/omr/moments/LUT.java @@ -28,7 +28,6 @@ */ public interface LUT { - //~ Methods ------------------------------------------------------------------------------------ /** * Set the value for integer coordinates (x,y). diff --git a/src/main/org/audiveris/omr/moments/LegendreMoments.java b/src/main/org/audiveris/omr/moments/LegendreMoments.java index d6f50d2a7..90e927176 100644 --- a/src/main/org/audiveris/omr/moments/LegendreMoments.java +++ b/src/main/org/audiveris/omr/moments/LegendreMoments.java @@ -29,7 +29,6 @@ public interface LegendreMoments extends OrthogonalMoments { - //~ Static fields/initializers ----------------------------------------------------------------- /** Chosen polynomial order. */ public static final int ORDER = 10; diff --git a/src/main/org/audiveris/omr/moments/MomentsExtractor.java b/src/main/org/audiveris/omr/moments/MomentsExtractor.java index 1904eb8cc..6cb01c5e0 100644 --- a/src/main/org/audiveris/omr/moments/MomentsExtractor.java +++ b/src/main/org/audiveris/omr/moments/MomentsExtractor.java @@ -28,12 +28,10 @@ * {@link OrthogonalMoments}. * * @param the descriptor type - * * @author Hervé Bitteur */ public interface MomentsExtractor> { - //~ Methods ------------------------------------------------------------------------------------ /** * Extract information from provided foreground points and save the results into diff --git a/src/main/org/audiveris/omr/moments/OrthogonalMoments.java b/src/main/org/audiveris/omr/moments/OrthogonalMoments.java index 0a990f08c..681e96d61 100644 --- a/src/main/org/audiveris/omr/moments/OrthogonalMoments.java +++ b/src/main/org/audiveris/omr/moments/OrthogonalMoments.java @@ -26,12 +26,10 @@ * orthogonal moments. * * @param the descriptor type - * * @author Hervé Bitteur */ public interface OrthogonalMoments> { - //~ Methods ------------------------------------------------------------------------------------ /** * Report the distance to another descriptor instance. @@ -70,5 +68,4 @@ void setMoment (int m, // */ // String getLabel (int m, // int n); - // } diff --git a/src/main/org/audiveris/omr/moments/QuantizedARTMoments.java b/src/main/org/audiveris/omr/moments/QuantizedARTMoments.java index 8d6bc2ab3..d1322a43b 100644 --- a/src/main/org/audiveris/omr/moments/QuantizedARTMoments.java +++ b/src/main/org/audiveris/omr/moments/QuantizedARTMoments.java @@ -34,36 +34,27 @@ public class QuantizedARTMoments implements ARTMoments { - //~ Static fields/initializers ----------------------------------------------------------------- // Quantization table (17 cells) private static final double[] quantTable = { - 0.000000000, 0.003585473, 0.007418411, - 0.011535520, 0.015982337, 0.020816302, - 0.026111312, 0.031964674, 0.038508176, - 0.045926586, 0.054490513, 0.064619488, - 0.077016351, 0.092998687, 0.115524524, - 0.154032694, 1.000000000 + 0.000000000, 0.003585473, 0.007418411, 0.011535520, 0.015982337, 0.020816302, 0.026111312, + 0.031964674, 0.038508176, 0.045926586, 0.054490513, + 0.064619488, 0.077016351, 0.092998687, 0.115524524, 0.154032694, 1.000000000 }; // Inverse quantization table (16 cells) private static final double[] iQuantTable = { - 0.001763817, 0.005468893, 0.009438835, - 0.013714449, 0.018346760, 0.023400748, - 0.028960940, 0.035140141, 0.042093649, - 0.050043696, 0.059324478, 0.070472849, - 0.084434761, 0.103127662, 0.131506859, - 0.192540857 + 0.001763817, 0.005468893, 0.009438835, 0.013714449, 0.018346760, 0.023400748, 0.028960940, + 0.035140141, 0.042093649, 0.050043696, 0.059324478, + 0.070472849, 0.084434761, 0.103127662, 0.131506859, 0.192540857 }; - //~ Instance fields ---------------------------------------------------------------------------- // double QuantizedARTMoments::QuantTable[17] = {0.000000000, 0.001898192, 0.003927394, 0.006107040, 0.008461237, 0.011020396, 0.013823636, 0.016922475, 0.020386682, 0.024314076, 0.028847919, 0.034210318, 0.040773364, 0.049234601, 0.061160045, 0.081546727, 1.0}; // double QuantizedARTMoments::IQuantTable[16] = {0.000933785, 0.002895296, 0.004997030, 0.007260591, 0.009712991, 0.012388631, 0.015332262, 0.018603605, 0.022284874, 0.026493722, 0.031407077, 0.037309157, 0.044700757, 0.054597000, 0.069621283, 0.101933409}; // /** Actual values */ private final short[][] values = new short[ANGULAR][RADIAL]; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new QuantizedARTMoments object. */ @@ -71,7 +62,6 @@ public QuantizedARTMoments () { } - //~ Methods ------------------------------------------------------------------------------------ //------------// // distanceTo // //------------// diff --git a/src/main/org/audiveris/omr/plugin/Plugin.java b/src/main/org/audiveris/omr/plugin/Plugin.java index 6cac551a0..5a0715fbc 100644 --- a/src/main/org/audiveris/omr/plugin/Plugin.java +++ b/src/main/org/audiveris/omr/plugin/Plugin.java @@ -67,12 +67,9 @@ @XmlRootElement(name = "plugin") public class Plugin { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - Plugin.class); + private static final Logger logger = LoggerFactory.getLogger(Plugin.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Id. */ @XmlAttribute(name = "id") private final String id; @@ -85,7 +82,6 @@ public class Plugin @XmlElement(name = "arg") private final List args; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code Plugin} object. * @@ -112,7 +108,6 @@ private Plugin () args = null; } - //~ Methods ------------------------------------------------------------------------------------ //-------// // check // //-------// @@ -183,6 +178,12 @@ public Task getTask (Book book) //-----------// // runPlugin // //-----------// + /** + * Run this plugin on the provided book. + * + * @param book provided book + * @return nothing + */ public Void runPlugin (Book book) { Path exportPath = retrieveExport(book); @@ -216,7 +217,8 @@ public Void runPlugin (Book book) // Wait to get exit value final int exitValue = process.waitFor(); logger.info("{} exit value: {}", getId(), exitValue); - } catch (Throwable ex) { + } catch (IOException | + InterruptedException ex) { logger.warn("Error launching {} {}" + this, ex.toString(), ex); } @@ -249,7 +251,7 @@ public String toString () //----------// private List buildCli (Path exportPath) { - List cli = new ArrayList(args.size()); + List cli = new ArrayList<>(args.size()); for (String arg : args) { if (arg.trim().equals("{}")) { @@ -308,14 +310,13 @@ private Path retrieveExport (Book book) } logger.warn("Cannot find file {}", exportPath); - } catch (Exception ex) { + } catch (IOException ex) { logger.warn("Error getting export file " + ex, ex); } return null; } - //~ Inner Classes ------------------------------------------------------------------------------ //------------// // PluginTask // //------------// @@ -326,17 +327,14 @@ private Path retrieveExport (Book book) private class PluginTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ private final Book book; - //~ Constructors --------------------------------------------------------------------------- - public PluginTask (Book book) + PluginTask (Book book) { this.book = book; } - //~ Methods -------------------------------------------------------------------------------- @Override @SuppressWarnings("unchecked") protected Void doInBackground () diff --git a/src/main/org/audiveris/omr/plugin/PluginAction.java b/src/main/org/audiveris/omr/plugin/PluginAction.java index 065b148a5..a29c5d41e 100644 --- a/src/main/org/audiveris/omr/plugin/PluginAction.java +++ b/src/main/org/audiveris/omr/plugin/PluginAction.java @@ -30,7 +30,6 @@ import java.awt.event.ActionEvent; import javax.swing.AbstractAction; - import static javax.swing.Action.SHORT_DESCRIPTION; /** @@ -42,28 +41,24 @@ class PluginAction extends AbstractAction { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(PluginAction.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The related plugin. */ private final Plugin plugin; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new PluginAction object. * * @param plugin the underlying scripting plugin */ - public PluginAction (Plugin plugin) + PluginAction (Plugin plugin) { super(plugin.getId()); this.plugin = plugin; putValue(SHORT_DESCRIPTION, plugin.getDescription()); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // actionPerformed // //-----------------// @@ -84,4 +79,11 @@ public Plugin getPlugin () { return plugin; } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/plugin/PluginsManager.java b/src/main/org/audiveris/omr/plugin/PluginsManager.java index 402bed810..5d5ffcae5 100644 --- a/src/main/org/audiveris/omr/plugin/PluginsManager.java +++ b/src/main/org/audiveris/omr/plugin/PluginsManager.java @@ -44,6 +44,7 @@ import javax.swing.JMenuItem; import javax.swing.event.MenuEvent; import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -62,23 +63,20 @@ */ public class PluginsManager { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(PluginsManager.class); + /** Persistent default plugin id. */ + public static final Param defaultPluginId = new Default(); + /** File name for plugins definitions: {@value}. */ private static final String PLUGINS_FILE_NAME = "plugins.xml"; - /** Singleton. */ - private static PluginsManager INSTANCE; + /** Un/marshalling context for use with JAXB. */ + private static volatile JAXBContext jaxbContext; - /** Persistent default plugin id. */ - public static final Param defaultPluginId = new Default(); - - //~ Instance fields ---------------------------------------------------------------------------- - // /** The concrete UI menu. */ private JMenu menu; @@ -88,7 +86,6 @@ public class PluginsManager /** The current default plugin. */ private Plugin defaultPlugin; - //~ Constructors ------------------------------------------------------------------------------- /** * Generates the menu to be inserted in the plugin menu hierarchy, * based on the plugins file discovered in Audiveris user config folder. @@ -106,7 +103,6 @@ private PluginsManager () } } - //~ Methods ------------------------------------------------------------------------------------ //------------------// // setDefaultPlugin // //------------------// @@ -126,19 +122,6 @@ public final void setDefaultPlugin (String pluginId) } } - //------------------// - // setDefaultPlugin // - //------------------// - /** - * Assign the default plugin. - * - * @param defaultPlugin the new default plugin - */ - public final void setDefaultPlugin (Plugin defaultPlugin) - { - this.defaultPlugin = defaultPlugin; - } - //------------------// // getDefaultPlugin // //------------------// @@ -152,21 +135,17 @@ public Plugin getDefaultPlugin () return defaultPlugin; } - //-------------// - // getInstance // - //-------------// + //------------------// + // setDefaultPlugin // + //------------------// /** - * Report the class singleton. + * Assign the default plugin. * - * @return the unique instance of this class + * @param defaultPlugin the new default plugin */ - public static synchronized PluginsManager getInstance () + public final void setDefaultPlugin (Plugin defaultPlugin) { - if (INSTANCE == null) { - INSTANCE = new PluginsManager(); - } - - return INSTANCE; + this.defaultPlugin = defaultPlugin; } //---------// @@ -206,7 +185,7 @@ public JMenu getMenu (JMenu menu) */ public Collection getPluginIds () { - List ids = new ArrayList(); + List ids = new ArrayList<>(); for (Plugin plugin : plugins) { ids.add(plugin.getId()); @@ -239,8 +218,7 @@ private List loadPlugins () if (Files.exists(pluginsPath)) { try { - JAXBContext jaxbContext = JAXBContext.newInstance(PluginsHolder.class); - Unmarshaller um = jaxbContext.createUnmarshaller(); + Unmarshaller um = getJaxbContext().createUnmarshaller(); PluginsHolder pluginsHolder = (PluginsHolder) um.unmarshal(pluginsPath.toFile()); for (Plugin plugin : pluginsHolder.list) { @@ -250,7 +228,7 @@ private List loadPlugins () logger.info("Loaded plugins from {}", pluginsPath); return pluginsHolder.list; // Normal exit - } catch (Throwable ex) { + } catch (JAXBException ex) { logger.warn("Error loading {}", pluginsPath, ex); } } else { @@ -260,14 +238,86 @@ private List loadPlugins () return Collections.EMPTY_LIST; } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of PluginsManager in the application. + * + * @return the instance + */ + public static PluginsManager getInstance () + { + return LazySingleton.INSTANCE; + } + + //----------------// + // getJaxbContext // + //----------------// + private static JAXBContext getJaxbContext () + throws JAXBException + { + // Lazy creation + if (jaxbContext == null) { + jaxbContext = JAXBContext.newInstance(PluginsHolder.class); + } + + return jaxbContext; + } + + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { + + static final PluginsManager INSTANCE = new PluginsManager(); + } + + //----------------// + // MyMenuListener // + //----------------// + /** + * Class {@code MyMenuListener} is triggered when menu is entered. + *

                              + * This is meant to enable menu items only when a sheet is selected, + * and to indicate the default plugin if any. + */ + private class MyMenuListener + extends AbstractMenuListener + { + + @Override + public void menuSelected (MenuEvent e) + { + boolean enabled = StubsController.getCurrentStub() != null; + + for (int i = 0; i < menu.getItemCount(); i++) { + JMenuItem item = menu.getItem(i); + + // Beware of separators (for which returned menuItem is null) + if (item != null) { + item.setEnabled(enabled); + + // Indicate which plugin is the default (if any) + Action action = item.getAction(); + + if (action instanceof PluginAction) { + Plugin plugin = ((PluginAction) action).getPlugin(); + item.setText( + plugin.getId() + ((plugin == defaultPlugin) ? " (default)" : "")); + } + } + } + } + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ Constant.String defaultPlugin = new Constant.String("", "Name of default plugin"); } @@ -278,7 +328,6 @@ private static final class Constants private static class Default extends Param { - //~ Methods -------------------------------------------------------------------------------- @Override public String getSpecific () @@ -317,45 +366,6 @@ public boolean setSpecific (String specific) } } - //----------------// - // MyMenuListener // - //----------------// - /** - * Class {@code MyMenuListener} is triggered when menu is entered. - *

                              - * This is meant to enable menu items only when a sheet is selected, - * and to indicate the default plugin if any. - */ - private class MyMenuListener - extends AbstractMenuListener - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public void menuSelected (MenuEvent e) - { - boolean enabled = StubsController.getCurrentStub() != null; - - for (int i = 0; i < menu.getItemCount(); i++) { - JMenuItem item = menu.getItem(i); - - // Beware of separators (for which returned menuItem is null) - if (item != null) { - item.setEnabled(enabled); - - // Indicate which plugin is the default (if any) - Action action = item.getAction(); - - if (action instanceof PluginAction) { - Plugin plugin = ((PluginAction) action).getPlugin(); - item.setText( - plugin.getId() + ((plugin == defaultPlugin) ? " (default)" : "")); - } - } - } - } - } - //---------------// // PluginsHolder // //---------------// @@ -366,13 +376,11 @@ public void menuSelected (MenuEvent e) @XmlRootElement(name = "plugins") private static class PluginsHolder { - //~ Instance fields ------------------------------------------------------------------------ /** List of plugins. */ @XmlElementRef - private List list = new ArrayList(); + private List list = new ArrayList<>(); - //~ Constructors --------------------------------------------------------------------------- /** No-arg constructor meant for JAXB. */ private PluginsHolder () { diff --git a/src/main/org/audiveris/omr/run/MarkedRun.java b/src/main/org/audiveris/omr/run/MarkedRun.java index 607a1119c..e9e2b6b8c 100644 --- a/src/main/org/audiveris/omr/run/MarkedRun.java +++ b/src/main/org/audiveris/omr/run/MarkedRun.java @@ -30,12 +30,10 @@ public class MarkedRun extends Run { - //~ Instance fields ---------------------------------------------------------------------------- /** Assigned mark. */ private int mark; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code MarkedRun} object. * @@ -63,12 +61,21 @@ public MarkedRun (int start, this.mark = mark; } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Report the assigned mark. + * + * @return mark + */ public int getMark () { return mark; } + /** + * Assign a mark. + * + * @param mark assigned mark + */ public void setMark (int mark) { this.mark = mark; diff --git a/src/main/org/audiveris/omr/run/Oriented.java b/src/main/org/audiveris/omr/run/Oriented.java index a9a71f2e6..7056dfd02 100644 --- a/src/main/org/audiveris/omr/run/Oriented.java +++ b/src/main/org/audiveris/omr/run/Oriented.java @@ -28,7 +28,6 @@ */ public interface Oriented { - //~ Methods ------------------------------------------------------------------------------------ /** * Report the orientation constant diff --git a/src/main/org/audiveris/omr/run/Run.java b/src/main/org/audiveris/omr/run/Run.java index 98b37e724..9484eac6f 100644 --- a/src/main/org/audiveris/omr/run/Run.java +++ b/src/main/org/audiveris/omr/run/Run.java @@ -35,7 +35,6 @@ @XmlAccessorType(XmlAccessType.NONE) public class Run { - //~ Instance fields ---------------------------------------------------------------------------- /** Abscissa (for horizontal) / ordinate (for vertical) of first pixel. */ @XmlAttribute @@ -45,7 +44,6 @@ public class Run @XmlAttribute protected int length; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code Run} instance. * @@ -76,7 +74,6 @@ private Run () this(0, 0); } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getLength // //-----------// @@ -90,6 +87,16 @@ public final int getLength () return length; } + /** + * (package private) method to set length value. + * + * @param length the length to set + */ + void setLength (int length) + { + this.length = length; + } + //----------// // getStart // //----------// @@ -104,6 +111,16 @@ public final int getStart () return start; } + /** + * (package private) method to set start value. + * + * @param start the start to set + */ + void setStart (int start) + { + this.start = start; + } + //---------// // getStop // //---------// @@ -176,23 +193,4 @@ public String toString () return sb.toString(); } - /** - * (package private) method to set length value. - * - * @param length the length to set - */ - void setLength (int length) - { - this.length = length; - } - - /** - * (package private) method to set start value. - * - * @param start the start to set - */ - void setStart (int start) - { - this.start = start; - } } diff --git a/src/main/org/audiveris/omr/run/RunBoard.java b/src/main/org/audiveris/omr/run/RunBoard.java index df7d35fae..2f23066c4 100644 --- a/src/main/org/audiveris/omr/run/RunBoard.java +++ b/src/main/org/audiveris/omr/run/RunBoard.java @@ -44,14 +44,12 @@ public class RunBoard extends Board { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(RunBoard.class); /** Events this entity is interested in */ private static final Class[] eventClasses = new Class[]{RunEvent.class}; - //~ Instance fields ---------------------------------------------------------------------------- /** Field for run length */ private final LIntegerField rLength = new LIntegerField( false, @@ -64,7 +62,6 @@ public class RunBoard "Start", "Pixel coordinate at start of run"); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a Run Board on the RunTable of a provided ag * @@ -87,9 +84,10 @@ public RunBoard (RunTable runTable, boolean selected) { super( - Board.RUN.name - + ((runTable.getOrientation() == Orientation.VERTICAL) ? " Vert" : " Hori"), - Board.RUN.position + ((runTable.getOrientation() == Orientation.VERTICAL) ? 100 : 0), + Board.RUN.name + ((runTable.getOrientation() == Orientation.VERTICAL) ? " Vert" + : " Hori"), + Board.RUN.position + ((runTable.getOrientation() == Orientation.VERTICAL) ? 100 + : 0), runTable.getRunService(), eventClasses, selected, @@ -99,7 +97,6 @@ public RunBoard (RunTable runTable, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // onEvent // //---------// diff --git a/src/main/org/audiveris/omr/run/RunService.java b/src/main/org/audiveris/omr/run/RunService.java index 28a5900eb..33e838fdd 100644 --- a/src/main/org/audiveris/omr/run/RunService.java +++ b/src/main/org/audiveris/omr/run/RunService.java @@ -44,21 +44,18 @@ public class RunService extends SelectionService implements EventSubscriber { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(RunService.class); /** Events allowed to be published on this run service. */ - public static final Class[] eventsWritten = new Class[]{RunEvent.class}; + private static final Class[] eventsWritten = new Class[]{RunEvent.class}; /** Events observed on location service. */ - public static final Class[] locEventsRead = new Class[]{LocationEvent.class}; + private static final Class[] locEventsRead = new Class[]{LocationEvent.class}; - //~ Instance fields ---------------------------------------------------------------------------- /** The underlying run table. */ private final RunTable table; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code RunService} object. * @@ -72,7 +69,6 @@ public RunService (String name, this.table = table; } - //~ Methods ------------------------------------------------------------------------------------ //--------------------// // cutLocationService // //--------------------// @@ -106,10 +102,7 @@ public void onEvent (LocationEvent locationEvent) } logger.debug("RunsTable {}: {}", getName(), locationEvent); - - if (locationEvent instanceof LocationEvent) { - handleLocationEvent(locationEvent); // Location => Run - } + handleLocationEvent(locationEvent); // Location => Run } catch (Exception ex) { logger.warn(getClass().getName() + " onEvent error", ex); } diff --git a/src/main/org/audiveris/omr/run/RunTable.java b/src/main/org/audiveris/omr/run/RunTable.java index d0012c72b..236467f2e 100644 --- a/src/main/org/audiveris/omr/run/RunTable.java +++ b/src/main/org/audiveris/omr/run/RunTable.java @@ -44,6 +44,7 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.image.BufferedImage; +import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; @@ -55,6 +56,7 @@ import java.util.Objects; import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAccessType; @@ -63,6 +65,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlValue; +import javax.xml.stream.XMLStreamException; +import org.audiveris.omr.util.Jaxb; /** * Class {@code RunTable} handles a rectangular assembly of oriented runs. @@ -80,6 +84,7 @@ *

                              * We can have these various kinds of sequence, where 'F' stands for the length of a foreground run * and 'B' for the length of a background run: + * *

                                * null    (for an empty sequence)
                                * []      (for an empty sequence as well)
                              @@ -96,14 +101,12 @@
                               public class RunTable
                                       implements Cloneable, PixelSource, Oriented
                               {
                              -    //~ Static fields/initializers -----------------------------------------------------------------
                               
                              -    private static final Logger logger = LoggerFactory.getLogger(
                              -            RunTable.class);
                              +    private static final Logger logger = LoggerFactory.getLogger(RunTable.class);
                               
                              -    private static JAXBContext jaxbContext;
                              +    /** Un/marshalling context for use with JAXB. */
                              +    private static volatile JAXBContext jaxbContext;
                               
                              -    //~ Instance fields ----------------------------------------------------------------------------
                                   // Persistent data
                                   //----------------
                                   /** Orientation, the same for this table and all contained runs. */
                              @@ -130,7 +133,6 @@ public class RunTable
                                   /** Cached total weight. */
                                   private Integer weight;
                               
                              -    //~ Constructors -------------------------------------------------------------------------------
                                   /**
                                    * Creates a new RunTable object.
                                    *
                              @@ -162,7 +164,6 @@ private RunTable ()
                                       this.sequences = null;
                                   }
                               
                              -    //~ Methods ------------------------------------------------------------------------------------
                                   //--------//
                                   // addRun //
                                   //--------//
                              @@ -332,6 +333,13 @@ public ARTMoments computeArtMoments (int left,
                                   //-----------------//
                                   // computeCentroid //
                                   //-----------------//
                              +    /**
                              +     * Compute the absolute centroid of this runtable, with provided offset.
                              +     *
                              +     * @param left abscissa offset
                              +     * @param top  ordinate offset
                              +     * @return absolute centroid
                              +     */
                                   public Point computeCentroid (int left,
                                                                 int top)
                                   {
                              @@ -951,27 +959,53 @@ public int getWeight ()
                                       return weight;
                                   }
                               
                              +    @Override
                              +    public Object clone ()
                              +            throws CloneNotSupportedException
                              +    {
                              +        return super.clone(); //To change body of generated methods, choose Tools | Templates.
                              +    }
                              +
                              +    //---------//
                              +    // marshal //
                              +    //---------//
                              +    /**
                              +     * Marshal this RunTable to the provided path.
                              +     *
                              +     * @param path target path
                              +     * @throws IOException        on IO error
                              +     * @throws JAXBException      on JAXB error
                              +     * @throws XMLStreamException on XML error
                              +     */
                              +    public void marshal (Path path)
                              +            throws IOException,
                              +                   JAXBException,
                              +                   XMLStreamException
                              +    {
                              +        Jaxb.marshal(this, path, getJaxbContext());
                              +    }
                              +
                                   //-----------//
                                   // unmarshal //
                                   //-----------//
                              +    /**
                              +     * Unmarshal a RunTable from a file.
                              +     *
                              +     * @param path path to file
                              +     * @return unmarshalled run table
                              +     */
                                   public static RunTable unmarshal (Path path)
                                   {
                                       logger.debug("RunTable unmarshalling {}", path);
                               
                              -        try {
                              -            InputStream is = Files.newInputStream(path, StandardOpenOption.READ);
                              -
                              -            if (jaxbContext == null) {
                              -                jaxbContext = JAXBContext.newInstance(RunTable.class);
                              -            }
                              -
                              -            Unmarshaller um = jaxbContext.createUnmarshaller();
                              +        try (InputStream is = Files.newInputStream(path, StandardOpenOption.READ)) {
                              +            Unmarshaller um = getJaxbContext().createUnmarshaller();
                                           RunTable runTable = (RunTable) um.unmarshal(is);
                              -            is.close();
                                           logger.debug("Unmarshalled {}", runTable);
                               
                                           return runTable;
                              -        } catch (Exception ex) {
                              +        } catch (IOException |
                              +                 JAXBException ex) {
                                           logger.warn("RunTable. Error unmarshalling " + path + " " + ex, ex);
                               
                                           return null;
                              @@ -1283,9 +1317,9 @@ public void render (Graphics2D g,
                               
                                       if (orientation == HORIZONTAL) {
                                           final int minSeq = (clip != null) ? Math.max(clip.y - offset.y, 0) : 0;
                              -            final int maxSeq = (clip != null)
                              -                    ? (Math.min(((clip.y + clip.height) - offset.y), height) - 1)
                              -                    : (height - 1);
                              +            final int maxSeq = (clip != null) ? (Math.min(
                              +                    ((clip.y + clip.height) - offset.y),
                              +                    height) - 1) : (height - 1);
                               
                                           for (int iSeq = minSeq; iSeq <= maxSeq; iSeq++) {
                                               for (Itr it = new Itr(iSeq); it.hasNext();) {
                              @@ -1295,9 +1329,8 @@ public void render (Graphics2D g,
                                           }
                                       } else {
                                           final int minSeq = (clip != null) ? Math.max(clip.x - offset.x, 0) : 0;
                              -            final int maxSeq = (clip != null)
                              -                    ? (Math.min((clip.x + clip.width) - offset.x, width) - 1) : (width
                              -                                                                                 - 1);
                              +            final int maxSeq = (clip != null) ? (Math.min((clip.x + clip.width) - offset.x, width)
                              +                                                         - 1) : (width - 1);
                               
                                           for (int iSeq = minSeq; iSeq <= maxSeq; iSeq++) {
                                               for (Itr it = new Itr(iSeq); it.hasNext();) {
                              @@ -1410,7 +1443,6 @@ public String toString ()
                                    * Trim this run table, to come up with the smallest bounding box.
                                    *
                                    * @param offset (output) resulting offset WRT initial run table
                              -     *
                                    * @return the resulting trimmed table
                                    */
                                   public RunTable trim (Point offset)
                              @@ -1528,6 +1560,20 @@ public void write (ByteProcessor buffer,
                                       }
                                   }
                               
                              +    //----------------//
                              +    // getJaxbContext //
                              +    //----------------//
                              +    private static JAXBContext getJaxbContext ()
                              +            throws JAXBException
                              +    {
                              +        // Lazy creation
                              +        if (jaxbContext == null) {
                              +            jaxbContext = JAXBContext.newInstance(RunTable.class);
                              +        }
                              +
                              +        return jaxbContext;
                              +    }
                              +
                                   //--------//
                                   // encode //
                                   //--------//
                              @@ -1659,7 +1705,6 @@ private void beforeMarshal (Marshaller m)
                                       }
                                   }
                               
                              -    //~ Inner Classes ------------------------------------------------------------------------------
                                   //-------------//
                                   // RunSequence //
                                   //-------------//
                              @@ -1670,22 +1715,19 @@ private void beforeMarshal (Marshaller m)
                                   @XmlRootElement(name = "runs")
                                   static class RunSequence
                                   {
                              -        //~ Instance fields ------------------------------------------------------------------------
                               
                                       @XmlValue
                                       private int[] rle;
                               
                              -        //~ Constructors ---------------------------------------------------------------------------
                              -        public RunSequence (int[] rle)
                              +        RunSequence (int[] rle)
                                       {
                                           this.rle = rle;
                                       }
                               
                              -        public RunSequence ()
                              +        RunSequence ()
                                       {
                                       }
                               
                              -        //~ Methods --------------------------------------------------------------------------------
                                       @Override
                                       public boolean equals (Object obj)
                                       {
                              @@ -1747,26 +1789,27 @@ public String toString ()
                                   private class Itr
                                           implements Iterator
                                   {
                              -        //~ Instance fields ------------------------------------------------------------------------
                               
                                       /** The index of sequence being iterated upon. */
                                       private final int index;
                               
                              -        /** Current position in sequence array.
                              +        /**
                              +         * Current position in sequence array.
                                        * Always on an even position, pointing to the length of Foreground to be returned by
                              -         * next() */
                              +         * next()
                              +         */
                                       private int cursor = 0;
                               
                                       /** Start location of foreground run to be returned by next(). */
                                       private int loc = 0;
                               
                              -        /** Reusable Run structure. This is just a buffer meant to optimize browsing.
                              +        /**
                              +         * Reusable Run structure. This is just a buffer meant to optimize browsing.
                                        * Beware, don't keep a pointer to this Run object, make a copy.
                                        */
                                       private final Run run = new Run(-1, -1);
                               
                              -        //~ Constructors ---------------------------------------------------------------------------
                              -        public Itr (int index)
                              +        Itr (int index)
                                       {
                                           this.index = index;
                               
                              @@ -1788,7 +1831,6 @@ public Itr (int index)
                                           }
                                       }
                               
                              -        //~ Methods --------------------------------------------------------------------------------
                                       /**
                                        * Returns true only if there is still a foreground run to return.
                                        *
                              diff --git a/src/main/org/audiveris/omr/run/RunTableFactory.java b/src/main/org/audiveris/omr/run/RunTableFactory.java
                              index cb1378019..7d23d924d 100644
                              --- a/src/main/org/audiveris/omr/run/RunTableFactory.java
                              +++ b/src/main/org/audiveris/omr/run/RunTableFactory.java
                              @@ -41,19 +41,15 @@
                                */
                               public class RunTableFactory
                               {
                              -    //~ Static fields/initializers -----------------------------------------------------------------
                               
                                   private static final Logger logger = LoggerFactory.getLogger(RunTableFactory.class);
                               
                              -    //~ Instance fields ----------------------------------------------------------------------------
                              -    //
                                   /** The desired orientation. */
                                   private final Orientation orientation;
                               
                                   /** The filter, if any, to be applied on run candidates. */
                                   private final Filter filter;
                               
                              -    //~ Constructors -------------------------------------------------------------------------------
                                   /**
                                    * Create an RunsTableFactory, with its specified orientation and no filtering.
                                    *
                              @@ -80,8 +76,6 @@ public RunTableFactory (Orientation orientation,
                                       this.filter = filter;
                                   }
                               
                              -    //~ Methods ------------------------------------------------------------------------------------
                              -    //
                                   // ------------//
                                   // createTable //
                                   // ------------//
                              @@ -99,7 +93,6 @@ public RunTable createTable (ByteProcessor source)
                                       return createTable(source, roi);
                                   }
                               
                              -    //
                                   // ------------//
                                   // createTable //
                                   // ------------//
                              @@ -117,78 +110,18 @@ public RunTable createTable (ByteProcessor source,
                                       RunsRetriever retriever = new RunsRetriever(
                                               orientation,
                                               orientation.isVertical() ? new VerticalAdapter(source, table, roi.getLocation())
                              -                        : new HorizontalAdapter(source, table, roi.getLocation()));
                              +                : new HorizontalAdapter(source, table, roi.getLocation()));
                                       retriever.retrieveRuns(roi);
                               
                                       return table;
                                   }
                               
                              -    //~ Inner Interfaces ---------------------------------------------------------------------------
                              -    //--------//
                              -    // Filter //
                              -    //--------//
                              -    /**
                              -     * This class is able to filter a run candidate.
                              -     */
                              -    public static interface Filter
                              -    {
                              -        //~ Methods --------------------------------------------------------------------------------
                              -
                              -        /**
                              -         * Perform the filter on the provided run candidate.
                              -         *
                              -         * @param x      abscissa at beginning of run candidate
                              -         * @param y      ordinate at beginning of run candidate
                              -         * @param length the length of the run candidate
                              -         * @return true if candidate is to be kept
                              -         */
                              -        boolean check (int x,
                              -                       int y,
                              -                       int length);
                              -    }
                              -
                              -    //~ Inner Classes ------------------------------------------------------------------------------
                              -    //--------------//
                              -    // LengthFilter //
                              -    //--------------//
                              -    /**
                              -     * A convenient run filter, that checks whether the run length is sufficient.
                              -     */
                              -    public static class LengthFilter
                              -            implements Filter
                              -    {
                              -        //~ Instance fields ------------------------------------------------------------------------
                              -
                              -        private final int minLength;
                              -
                              -        //~ Constructors ---------------------------------------------------------------------------
                              -        /**
                              -         * Creates a length-based filter.
                              -         *
                              -         * @param minLength the minimum acceptable run length (specified in pixels)
                              -         */
                              -        public LengthFilter (int minLength)
                              -        {
                              -            this.minLength = minLength;
                              -        }
                              -
                              -        //~ Methods --------------------------------------------------------------------------------
                              -        @Override
                              -        public boolean check (int x,
                              -                              int y,
                              -                              int length)
                              -        {
                              -            return length >= minLength;
                              -        }
                              -    }
                              -
                                   // ----------//
                                   // MyAdapter //
                                   // ----------//
                                   private abstract class MyAdapter
                                           implements RunsRetriever.Adapter
                                   {
                              -        //~ Instance fields ------------------------------------------------------------------------
                               
                                       /** The source to read runs of pixels from. */
                                       protected final ByteProcessor source;
                              @@ -199,17 +132,15 @@ private abstract class MyAdapter
                                       /** Table offset, if any, WRT source. */
                                       protected Point tableOffset;
                               
                              -        //~ Constructors ---------------------------------------------------------------------------
                              -        public MyAdapter (ByteProcessor source,
                              -                          RunTable table,
                              -                          Point tableOffset)
                              +        MyAdapter (ByteProcessor source,
                              +                   RunTable table,
                              +                   Point tableOffset)
                                       {
                                           this.source = source;
                                           this.table = table;
                                           this.tableOffset = tableOffset;
                                       }
                               
                              -        //~ Methods --------------------------------------------------------------------------------
                                       // --------//
                                       // foreRun //
                                       // --------//
                              @@ -270,16 +201,14 @@ protected abstract boolean checkFilter (int coord,
                                   private class HorizontalAdapter
                                           extends MyAdapter
                                   {
                              -        //~ Constructors ---------------------------------------------------------------------------
                               
                              -        public HorizontalAdapter (ByteProcessor source,
                              -                                  RunTable table,
                              -                                  Point tableOffset)
                              +        HorizontalAdapter (ByteProcessor source,
                              +                           RunTable table,
                              +                           Point tableOffset)
                                       {
                                           super(source, table, tableOffset);
                                       }
                               
                              -        //~ Methods --------------------------------------------------------------------------------
                                       @Override
                                       public void endPosition (int pos,
                                                                List runs)
                              @@ -312,16 +241,14 @@ protected boolean checkFilter (int coord,
                                   private class VerticalAdapter
                                           extends MyAdapter
                                   {
                              -        //~ Constructors ---------------------------------------------------------------------------
                               
                              -        public VerticalAdapter (ByteProcessor source,
                              -                                RunTable table,
                              -                                Point tableOffset)
                              +        VerticalAdapter (ByteProcessor source,
                              +                         RunTable table,
                              +                         Point tableOffset)
                                       {
                                           super(source, table, tableOffset);
                                       }
                               
                              -        //~ Methods --------------------------------------------------------------------------------
                                       @Override
                                       public void endPosition (int pos,
                                                                List runs)
                              @@ -344,4 +271,57 @@ protected boolean checkFilter (int coord,
                                           return filter.check(pos, coord - length, length);
                                       }
                                   }
                              +
                              +    //--------//
                              +    // Filter //
                              +    //--------//
                              +    /**
                              +     * This class is able to filter a run candidate.
                              +     */
                              +    public static interface Filter
                              +    {
                              +
                              +        /**
                              +         * Perform the filter on the provided run candidate.
                              +         *
                              +         * @param x      abscissa at beginning of run candidate
                              +         * @param y      ordinate at beginning of run candidate
                              +         * @param length the length of the run candidate
                              +         * @return true if candidate is to be kept
                              +         */
                              +        boolean check (int x,
                              +                       int y,
                              +                       int length);
                              +    }
                              +
                              +    //--------------//
                              +    // LengthFilter //
                              +    //--------------//
                              +    /**
                              +     * A convenient run filter, that checks whether the run length is sufficient.
                              +     */
                              +    public static class LengthFilter
                              +            implements Filter
                              +    {
                              +
                              +        private final int minLength;
                              +
                              +        /**
                              +         * Creates a length-based filter.
                              +         *
                              +         * @param minLength the minimum acceptable run length (specified in pixels)
                              +         */
                              +        public LengthFilter (int minLength)
                              +        {
                              +            this.minLength = minLength;
                              +        }
                              +
                              +        @Override
                              +        public boolean check (int x,
                              +                              int y,
                              +                              int length)
                              +        {
                              +            return length >= minLength;
                              +        }
                              +    }
                               }
                              diff --git a/src/main/org/audiveris/omr/run/RunTableView.java b/src/main/org/audiveris/omr/run/RunTableView.java
                              index 2c97a5640..932d24c0e 100644
                              --- a/src/main/org/audiveris/omr/run/RunTableView.java
                              +++ b/src/main/org/audiveris/omr/run/RunTableView.java
                              @@ -44,15 +44,12 @@
                               public class RunTableView
                                       extends RubberPanel
                               {
                              -    //~ Static fields/initializers -----------------------------------------------------------------
                               
                                   private static final Logger logger = LoggerFactory.getLogger(RunTableView.class);
                               
                              -    //~ Instance fields ----------------------------------------------------------------------------
                                   /** The underlying table of runs. */
                                   private final RunTable table;
                               
                              -    //~ Constructors -------------------------------------------------------------------------------
                                   /**
                                    * Creates a new {@code RunTableView} object.
                                    *
                              @@ -74,7 +71,6 @@ public RunTableView (String name,
                                       setBackground(Color.white);
                                   }
                               
                              -    //~ Methods ------------------------------------------------------------------------------------
                                   //--------//
                                   // render //
                                   //--------//
                              diff --git a/src/main/org/audiveris/omr/run/RunsRetriever.java b/src/main/org/audiveris/omr/run/RunsRetriever.java
                              index c78a37bfa..3ad2bf0c5 100644
                              --- a/src/main/org/audiveris/omr/run/RunsRetriever.java
                              +++ b/src/main/org/audiveris/omr/run/RunsRetriever.java
                              @@ -43,19 +43,15 @@
                                */
                               public class RunsRetriever
                               {
                              -    //~ Static fields/initializers -----------------------------------------------------------------
                               
                                   private static final Logger logger = LoggerFactory.getLogger(RunsRetriever.class);
                               
                              -    //~ Instance fields ----------------------------------------------------------------------------
                              -    //
                                   /** The orientation of desired runs */
                                   private final Orientation orientation;
                               
                                   /** The adapter for pixel access and call-backs at run level */
                                   private final Adapter adapter;
                               
                              -    //~ Constructors -------------------------------------------------------------------------------
                                   /**
                                    * Creates a new RunsRetriever object.
                                    *
                              @@ -70,8 +66,6 @@ public RunsRetriever (Orientation orientation,
                                       this.adapter = adapter;
                                   }
                               
                              -    //~ Methods ------------------------------------------------------------------------------------
                              -    //
                                   //--------------//
                                   // retrieveRuns //
                                   //--------------//
                              @@ -106,7 +100,7 @@ private void processPosition (int pos,
                                                                 int cMax)
                                   {
                                       /** Buffer of runs for current position. */
                              -        final List posRuns = new ArrayList();
                              +        final List posRuns = new ArrayList<>();
                               
                                       // Current run is FOREGROUND or BACKGROUND
                                       boolean isFore = false;
                              @@ -180,7 +174,7 @@ private void rowBasedRetrieval (int pMin,
                                           // Parallel (TODO: should use Java 7 fork/join someday...)
                                           try {
                                               // Browse one dimension
                              -                List> tasks = new ArrayList>(pMax - pMin + 1);
                              +                List> tasks = new ArrayList<>(pMax - pMin + 1);
                               
                                               for (int p = pMin; p <= pMax; p++) {
                                                   final int pp = p;
                              @@ -212,8 +206,6 @@ public Void call ()
                                       }
                                   }
                               
                              -    //~ Inner Interfaces ---------------------------------------------------------------------------
                              -    //
                                   //---------//
                                   // Adapter //
                                   //---------//
                              @@ -223,7 +215,6 @@ public Void call ()
                                   public static interface Adapter
                                           extends Concurrency
                                   {
                              -        //~ Methods --------------------------------------------------------------------------------
                               
                                       /**
                                        * Called at end of position.
                              @@ -251,7 +242,6 @@ boolean foreRun (int coord,
                                        *
                                        * @param coord x for horizontal runs, y for vertical runs
                                        * @param pos   y for horizontal runs, x for vertical runs
                              -         *
                                        * @return true if pixel is foreground, false otherwise
                                        */
                                       boolean isFore (int coord,
                              diff --git a/src/main/org/audiveris/omr/score/LogicalPart.java b/src/main/org/audiveris/omr/score/LogicalPart.java
                              index e2cce66fe..13b08a4bb 100644
                              --- a/src/main/org/audiveris/omr/score/LogicalPart.java
                              +++ b/src/main/org/audiveris/omr/score/LogicalPart.java
                              @@ -40,22 +40,18 @@
                                * It can be "instantiated" in one or several SystemInfo by a {@link org.audiveris.omr.sheet.Part}.
                                * 

                              * There is an intermediate LogicalPart instance at Page level, which abstracts the system parts at - * page level, and which is then used when abstracting the part information from pages to score.

                              + * page level, and which is then used when abstracting the part information from pages to score. * * @author Hervé Bitteur */ @XmlAccessorType(XmlAccessType.NONE) public class LogicalPart { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - LogicalPart.class); + private static final Logger logger = LoggerFactory.getLogger(LogicalPart.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -82,7 +78,6 @@ public class LogicalPart @XmlAttribute(name = "midi-program") private Integer midiProgram; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new instance of ScorePart * @@ -103,7 +98,6 @@ private LogicalPart () staffCount = 0; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // equals // //--------// @@ -124,9 +118,9 @@ public boolean equals (Object obj) return false; } - return Objects.deepEquals(midiProgram, that.midiProgram) - && Objects.deepEquals(name, that.name) - && Objects.deepEquals(abbreviation, that.abbreviation); + return Objects.deepEquals(midiProgram, that.midiProgram) && Objects.deepEquals( + name, + that.name) && Objects.deepEquals(abbreviation, that.abbreviation); } //-------// @@ -144,6 +138,19 @@ public final void setId (int id) } } + //-------// + // getId // + //-------// + /** + * Report the id of this part + * + * @return the part id + */ + public int getId () + { + return id; + } + //-----------------// // getAbbreviation // //-----------------// @@ -155,9 +162,25 @@ public String getAbbreviation () return abbreviation; } + //-----------------// + // setAbbreviation // + //-----------------// + /** + * @param abbreviation the abbreviation to set + */ + public void setAbbreviation (String abbreviation) + { + this.abbreviation = abbreviation; + } + //----------------// // getDefaultName // //----------------// + /** + * Report the default name for this part, based on staff count. + * + * @return inferred name + */ public String getDefaultName () { switch (staffCount) { @@ -175,6 +198,11 @@ public String getDefaultName () //-------------------// // getDefaultProgram // //-------------------// + /** + * Report the default midi program, based on staff count. + * + * @return inferred program + */ public Integer getDefaultProgram () { logger.debug("Part #{} count={}", getId(), staffCount); @@ -191,25 +219,30 @@ public Integer getDefaultProgram () } } - //-------// - // getId // - //-------// + //----------------// + // getMidiProgram // + //----------------// /** - * Report the id of this part + * Report part midi program number, if any. * - * @return the part id + * @return program number or null */ - public int getId () + public Integer getMidiProgram () { - return id; + return midiProgram; } //----------------// - // getMidiProgram // + // setMidiProgram // //----------------// - public Integer getMidiProgram () + /** + * Assign the part midi program. + * + * @param midiProgram the midi program number for this part + */ + public void setMidiProgram (Integer midiProgram) { - return midiProgram; + this.midiProgram = midiProgram; } //---------// @@ -225,6 +258,19 @@ public String getName () return name; } + //---------// + // setName // + //---------// + /** + * Assign a name to this part + * + * @param name the new part name + */ + public void setName (String name) + { + this.name = name; + } + //--------// // getPid // //--------// @@ -275,38 +321,6 @@ public boolean isMultiStaff () return staffCount > 1; } - //-----------------// - // setAbbreviation // - //-----------------// - /** - * @param abbreviation the abbreviation to set - */ - public void setAbbreviation (String abbreviation) - { - this.abbreviation = abbreviation; - } - - //----------------// - // setMidiProgram // - //----------------// - public void setMidiProgram (Integer midiProgram) - { - this.midiProgram = midiProgram; - } - - //---------// - // setName // - //---------// - /** - * Assign a name to this part - * - * @param name the new part name - */ - public void setName (String name) - { - this.name = name; - } - //-------------// // swapVoiceId // //-------------// @@ -356,14 +370,12 @@ public String toString () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ // Default Part names private final Constant.String defaultSingleStaffPartName = new Constant.String( diff --git a/src/main/org/audiveris/omr/score/Mark.java b/src/main/org/audiveris/omr/score/Mark.java index ee354ff61..7bc8c0fcd 100644 --- a/src/main/org/audiveris/omr/score/Mark.java +++ b/src/main/org/audiveris/omr/score/Mark.java @@ -35,21 +35,7 @@ */ public class Mark { - //~ Enumerations ------------------------------------------------------------------------------- - /** Position relative to an entity */ - public static enum Position - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Mark should be horizontally located before the entity */ - BEFORE, - /** Mark - * should be horizontally located after the entity */ - AFTER; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Containing system */ @Navigable(false) private final SystemInfo system; @@ -66,7 +52,6 @@ public static enum Position /** Additional data, perhaps depending on shape for example */ private final Object data; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new instance of Mark * @@ -89,7 +74,6 @@ public Mark (SystemInfo system, this.data = data; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// @@ -154,4 +138,13 @@ public SystemInfo getSystem () { return system; } + + /** Position relative to an entity. */ + public static enum Position + { + /** Mark should be horizontally located before the entity */ + BEFORE, + /** Mark should be horizontally located after the entity */ + AFTER; + } } diff --git a/src/main/org/audiveris/omr/score/MeasureFixer.java b/src/main/org/audiveris/omr/score/MeasureFixer.java index efb1f7d54..0e75aae13 100644 --- a/src/main/org/audiveris/omr/score/MeasureFixer.java +++ b/src/main/org/audiveris/omr/score/MeasureFixer.java @@ -38,7 +38,7 @@ *
                            1. Detect implicit measures (as pickup measures)
                            2. *
                            3. Detect first half repeat measures
                            4. *
                            5. Detect implicit measures (as second half repeats)
                            6. - *
                            7. Detect inside barlines (empty measures)
                            8. + *
                            9. Detect inside barlines (empty measures)
                            10. *
                            11. Detect cautionary measures (CKT changes at end of staff)
                            12. *
                            13. Assign final page-based measure IDs
                            14. *
                          @@ -47,11 +47,9 @@ */ public class MeasureFixer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(MeasureFixer.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Current stack of measures in current system. */ private MeasureStack stack; @@ -70,7 +68,6 @@ public class MeasureFixer /** The latest id assigned to a measure stack. (in the current system) */ private Integer lastId; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new MeasureFixer object. */ @@ -78,10 +75,14 @@ public MeasureFixer () { } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // process Page // //--------------// + /** + * Process the provided page. + * + * @param page the page to process + */ public void process (Page page) { logger.debug("{} Visiting {}", getClass().getSimpleName(), page); @@ -98,8 +99,15 @@ public void process (Page page) } //---------------// - // process Score // Currently not used + // process Score // //---------------// + /** + * Process the provided score. + *

                          + * Currently not used. + * + * @param score the score to process + */ public void process (Score score) { logger.debug("{} Visiting {}", getClass().getSimpleName(), score); @@ -162,7 +170,8 @@ private boolean isPickup (MeasureStack stack) final SystemInfo system = stack.getSystem(); return (system.getIndexInPage() == 0) && (stack == system.getFirstStack()) - && (stackTermination != null) && (stackTermination.compareTo(Rational.ZERO) < 0); + && (stackTermination != null) && (stackTermination.compareTo(Rational.ZERO) + < 0); } //-------------// @@ -192,7 +201,8 @@ private boolean isRealStart (MeasureStack stack) private boolean isSecondRepeatHalf () { // Check for partial first half - if ((prevStackTermination == null) || (prevStackTermination.compareTo(Rational.ZERO) >= 0)) { + if ((prevStackTermination == null) || (prevStackTermination.compareTo( + Rational.ZERO) >= 0)) { return false; } @@ -210,8 +220,8 @@ private boolean isSecondRepeatHalf () } // Check for an exact duration sum (TODO: is this too strict?) - return prevStackTermination.plus(stackTermination).abs() - .equals(prevStack.getExpectedDuration()); + return prevStackTermination.plus(stackTermination).abs().equals( + prevStack.getExpectedDuration()); } //----------------// @@ -247,8 +257,7 @@ private void process (SystemInfo system) if (stack != system.getLastStack()) { setId( (lastId != null) ? (lastId + 1) - : ((prevSystemLastId != null) - ? (prevSystemLastId + 1) : 1)); + : ((prevSystemLastId != null) ? (prevSystemLastId + 1) : 1)); } else { // This is just a cautionary stack at right end of system logger.debug("cautionary"); diff --git a/src/main/org/audiveris/omr/score/MidiAbstractions.java b/src/main/org/audiveris/omr/score/MidiAbstractions.java index cca9b7aa7..49d0efd1a 100644 --- a/src/main/org/audiveris/omr/score/MidiAbstractions.java +++ b/src/main/org/audiveris/omr/score/MidiAbstractions.java @@ -33,7 +33,6 @@ */ public abstract class MidiAbstractions { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(MidiAbstractions.class); @@ -297,10 +296,8 @@ public abstract class MidiAbstractions /** 127 */ "Applause", /** 128 */ - "Gunshot" - }; + "Gunshot"}; - //~ Constructors ------------------------------------------------------------------------------- /** * Not meant to be instantiated. */ @@ -308,7 +305,6 @@ private MidiAbstractions () { } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // getProgramName // //----------------// diff --git a/src/main/org/audiveris/omr/score/MusicXML.java b/src/main/org/audiveris/omr/score/MusicXML.java index e969aae7c..2249db656 100644 --- a/src/main/org/audiveris/omr/score/MusicXML.java +++ b/src/main/org/audiveris/omr/score/MusicXML.java @@ -30,21 +30,23 @@ import org.audiveris.proxymusic.AccidentalText; import org.audiveris.proxymusic.AccidentalValue; import org.audiveris.proxymusic.BarStyle; +import org.audiveris.proxymusic.BreathMark; import org.audiveris.proxymusic.DegreeTypeValue; import org.audiveris.proxymusic.Empty; import org.audiveris.proxymusic.EmptyPlacement; +import org.audiveris.proxymusic.HorizontalTurn; import org.audiveris.proxymusic.KindValue; import org.audiveris.proxymusic.ObjectFactory; +import org.audiveris.proxymusic.RightLeftMiddle; import org.audiveris.proxymusic.Step; import org.audiveris.proxymusic.StrongAccent; import org.audiveris.proxymusic.Syllabic; import org.audiveris.proxymusic.UpDown; +import org.audiveris.proxymusic.YesNo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.String; // Do not remove this line! - import javax.xml.bind.JAXBElement; /** @@ -54,18 +56,23 @@ */ public abstract class MusicXML { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(MusicXML.class); - /** Names of the various note types used in MusicXML */ + /** Names of the various note types used in MusicXML. */ private static final String[] noteTypeNames = new String[]{ - "256th", "128th", "64th", "32nd", "16th", - "eighth", "quarter", "half", "whole", "breve", - "long" - }; + "256th", + "128th", + "64th", + "32nd", + "16th", + "eighth", + "quarter", + "half", + "whole", + "breve", + "long"}; - //~ Constructors ------------------------------------------------------------------------------- /** * Not meant to be instantiated. */ @@ -73,7 +80,6 @@ private MusicXML () { } - //~ Methods ------------------------------------------------------------------------------------ //------------------// // accidentalTextOf // //------------------// @@ -106,12 +112,28 @@ public static AccidentalValue accidentalValueOf (Shape shape) /** * Report the MusicXML bar style for a recognized Barline style * - * @param style the Audiveris barline style + * @param style the Audiveris barline style + * @param location position of barline with respect to containing measure * @return the MusicXML bar style */ - public static BarStyle barStyleOf (PartBarline.Style style) + public static BarStyle barStyleOf (PartBarline.Style style, + RightLeftMiddle location) { try { + // Special trick for back-to-back config + if (style == PartBarline.Style.LIGHT_HEAVY_LIGHT) { + switch (location) { + case LEFT: + return BarStyle.HEAVY_LIGHT; + + case MIDDLE: + return BarStyle.LIGHT_LIGHT; // What else? + + case RIGHT: + return BarStyle.LIGHT_HEAVY; + } + } + return BarStyle.valueOf(style.name()); } catch (Exception ex) { throw new IllegalArgumentException("Unknown bar style " + style, ex); @@ -157,10 +179,15 @@ public static JAXBElement getArticulationObject (Shape shape) case STACCATISSIMO: return factory.createArticulationsStaccatissimo(ep); - /** TODO: implement related shapes - * case BREATH_MARK : - * case CAESURA : - */ + case BREATH_MARK: + + BreathMark breathMark = factory.createBreathMark(); + breathMark.setValue("comma"); + + return factory.createArticulationsBreathMark(breathMark); + + case CAESURA: + return factory.createArticulationsCaesura(ep); } logger.error("Unsupported ornament shape:{}", shape); @@ -306,9 +333,22 @@ public static JAXBElement getOrnamentObject (Shape shape) case TURN: return factory.createOrnamentsTurn(factory.createHorizontalTurn()); + + case TURN_INVERTED: + return factory.createOrnamentsInvertedTurn(factory.createHorizontalTurn()); + + case TURN_SLASH: { + HorizontalTurn horizontalTurn = factory.createHorizontalTurn(); + horizontalTurn.setSlash(YesNo.YES); + + return factory.createOrnamentsInvertedTurn(horizontalTurn); } - logger.error("Unsupported ornament shape:{}", shape); + case TURN_UP: + return factory.createOrnamentsVerticalTurn(factory.createEmptyTrillSound()); + } + + logger.error("Unsupported ornament shape: {}", shape); return null; } diff --git a/src/main/org/audiveris/omr/score/OpusExporter.java b/src/main/org/audiveris/omr/score/OpusExporter.java index e43beacf5..94d71f905 100644 --- a/src/main/org/audiveris/omr/score/OpusExporter.java +++ b/src/main/org/audiveris/omr/score/OpusExporter.java @@ -46,15 +46,12 @@ */ public class OpusExporter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(OpusExporter.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The related book. */ private final Book book; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code OpusExporter} object on a related book. * @@ -65,7 +62,6 @@ public OpusExporter (Book book) this.book = book; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // export // //--------// @@ -82,10 +78,10 @@ public void export (Path path, boolean signed) throws Exception { - final OutputStream os = new FileOutputStream(path.toString()); - export(os, signed, rootName); - os.close(); - logger.info("Opus {} exported to {}", rootName, path); + try (OutputStream os = new FileOutputStream(path.toString())) { + export(os, signed, rootName); + logger.info("Opus {} exported to {}", rootName, path); + } } //--------// @@ -127,9 +123,8 @@ public void export (OutputStream os, for (Score score : scores) { // Reference each score/movement in opus - String entryName = rootName - + (multi ? (".mvt" + score.getId()) : "") - + OMR.SCORE_EXTENSION; + String entryName = rootName + (multi ? (".mvt" + score.getId()) : "") + + OMR.SCORE_EXTENSION; org.audiveris.proxymusic.opus.Score oScore = opusFactory.createScore(); oScore.setHref(entryName); oScore.setNewPage(YesNo.YES); diff --git a/src/main/org/audiveris/omr/score/Page.java b/src/main/org/audiveris/omr/score/Page.java index b300f3766..6dc7cf774 100644 --- a/src/main/org/audiveris/omr/score/Page.java +++ b/src/main/org/audiveris/omr/score/Page.java @@ -29,10 +29,8 @@ import org.audiveris.omr.sheet.rhythm.MeasureStack; import org.audiveris.omr.sig.inter.AbstractChordInter; import org.audiveris.omr.sig.inter.SlurInter; - import static org.audiveris.omr.util.HorizontalSide.LEFT; import static org.audiveris.omr.util.HorizontalSide.RIGHT; - import org.audiveris.omr.util.Jaxb; import org.audiveris.omr.util.Navigable; @@ -66,13 +64,9 @@ @XmlAccessorType(XmlAccessType.NONE) public class Page { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - Page.class); + private static final Logger logger = LoggerFactory.getLogger(Page.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -121,7 +115,6 @@ public class Page /** Greatest duration divisor (in this page). */ private Integer durationDivisor; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Page object. * @@ -147,7 +140,6 @@ private Page () id = 0; } - //~ Methods ------------------------------------------------------------------------------------ //---------------------// // computeMeasureCount // //---------------------// @@ -195,8 +187,7 @@ public void connectOrphanSlurs (boolean checkTie) Part precPart = part.getPrecedingInPage(); if (precPart != null) { - List precOrphans = precPart.getSlurs( - SlurInter.isEndingOrphan); + List precOrphans = precPart.getSlurs(SlurInter.isEndingOrphan); // Links: Slur -> prevSlur Map links = part.getCrossSlurLinks(precPart); @@ -273,6 +264,23 @@ public Integer getDeltaMeasureId () return deltaMeasureId; } + //-------------------// + // setDeltaMeasureId // + //-------------------// + /** + * Assign the progression of measure IDs within this page. + * + * @param deltaMeasureId the deltaMeasureId to set + */ + public void setDeltaMeasureId (Integer deltaMeasureId) + { + this.deltaMeasureId = deltaMeasureId; + + SheetStub stub = sheet.getStub(); + PageRef pageRef = stub.getPageRefs().get(id - 1); + pageRef.setDeltaMeasureId(deltaMeasureId); + } + //--------------// // getDimension // //--------------// @@ -330,6 +338,17 @@ public Integer getFirstSystemId () return firstSystemId; } + //------------------// + // setFirstSystemId // + //------------------// + /** + * @param firstSystemId the firstSystemId to set + */ + public void setFirstSystemId (Integer firstSystemId) + { + this.firstSystemId = firstSystemId; + } + //---------------------// // getFollowingInScore // //---------------------// @@ -381,9 +400,26 @@ public Integer getLastSystemId () return lastSystemId; } + //-----------------// + // setLastSystemId // + //-----------------// + /** + * @param lastSystemId the lastSystemId to set + */ + public void setLastSystemId (Integer lastSystemId) + { + this.lastSystemId = lastSystemId; + } + //--------------------// // getLogicalPartById // //--------------------// + /** + * Report the LogicalPart that corresponds to the provided ID. + * + * @param id provided ID + * @return corresponding LogicalPart or null if not found + */ public LogicalPart getLogicalPartById (int id) { if (logicalParts != null) { @@ -410,6 +446,19 @@ public List getLogicalParts () return logicalParts; } + //-----------------// + // setLogicalParts // + //-----------------// + /** + * Assign a part list valid for the page. + * + * @param logicalParts the list of logical parts + */ + public void setLogicalParts (List logicalParts) + { + this.logicalParts = logicalParts; + } + //-----------------// // getMeasureCount // //-----------------// @@ -458,6 +507,19 @@ public Score getScore () return score; } + //----------// + // setScore // + //----------// + /** + * Assign the containing score. + * + * @param score the score to set + */ + public void setScore (Score score) + { + this.score = score; + } + //----------// // getSheet // //----------// @@ -482,7 +544,7 @@ public Sheet getSheet () */ public List getSystemPartsById (int id) { - List parts = new ArrayList(); + List parts = new ArrayList<>(); for (SystemInfo system : getSystems()) { for (Part part : system.getParts()) { @@ -510,6 +572,23 @@ public List getSystems () return systems; } + //------------// + // setSystems // + //------------// + /** + * Using IDs of first and last page systems if any, register the proper (sub-)list + * of systems. + * + * @param sheetSystems the sheet whole list of systems + */ + public void setSystems (List sheetSystems) + { + // Define proper indices + int first = (firstSystemId != null) ? (firstSystemId - 1) : 0; + int last = (lastSystemId != null) ? (lastSystemId - 1) : (sheetSystems.size() - 1); + systems = sheetSystems.subList(first, last + 1); + } + //----------------// // initTransients // //----------------// @@ -534,6 +613,17 @@ public boolean isMovementStart () return movementStart; } + //------------------// + // setMovementStart // + //------------------// + /** + * @param movementStart the movementStart to set + */ + public void setMovementStart (boolean movementStart) + { + this.movementStart = movementStart; + } + //----------------// // numberMeasures // //----------------// @@ -564,102 +654,12 @@ public void numberMeasures () //----------------------// // resetDurationDivisor // //----------------------// - public void resetDurationDivisor () - { - durationDivisor = null; - } - - //-------------------// - // setDeltaMeasureId // - //-------------------// - /** - * Assign the progression of measure IDs within this page. - * - * @param deltaMeasureId the deltaMeasureId to set - */ - public void setDeltaMeasureId (Integer deltaMeasureId) - { - this.deltaMeasureId = deltaMeasureId; - - SheetStub stub = sheet.getStub(); - PageRef pageRef = stub.getPageRefs().get(id - 1); - pageRef.setDeltaMeasureId(deltaMeasureId); - } - - //------------------// - // setFirstSystemId // - //------------------// - /** - * @param firstSystemId the firstSystemId to set - */ - public void setFirstSystemId (Integer firstSystemId) - { - this.firstSystemId = firstSystemId; - } - - //-----------------// - // setLastSystemId // - //-----------------// /** - * @param lastSystemId the lastSystemId to set + * Nullify the duration divisor (before a re-computation). */ - public void setLastSystemId (Integer lastSystemId) - { - this.lastSystemId = lastSystemId; - } - - //-----------------// - // setLogicalParts // - //-----------------// - /** - * Assign a part list valid for the page. - * - * @param logicalParts the list of logical parts - */ - public void setLogicalParts (List logicalParts) - { - this.logicalParts = logicalParts; - } - - //------------------// - // setMovementStart // - //------------------// - /** - * @param movementStart the movementStart to set - */ - public void setMovementStart (boolean movementStart) - { - this.movementStart = movementStart ? Boolean.TRUE : null; - } - - //----------// - // setScore // - //----------// - /** - * Assign the containing score. - * - * @param score the score to set - */ - public void setScore (Score score) - { - this.score = score; - } - - //------------// - // setSystems // - //------------// - /** - * Using IDs of first and last page systems if any, register the proper (sub-)list - * of systems. - * - * @param sheetSystems the sheet whole list of systems - */ - public void setSystems (List sheetSystems) + public void resetDurationDivisor () { - // Define proper indices - int first = (firstSystemId != null) ? (firstSystemId - 1) : 0; - int last = (lastSystemId != null) ? (lastSystemId - 1) : (sheetSystems.size() - 1); - systems = sheetSystems.subList(first, last + 1); + durationDivisor = null; } //------------------// @@ -683,7 +683,19 @@ public int simpleDurationOf (Rational value) @Override public String toString () { - return "{Page#" + sheet.getStub().getNumber() + "." + getId() + "}"; + final StringBuilder sb = new StringBuilder("{Page"); + + if (sheet != null && sheet.getStub() != null) { + sb.append('#').append(sheet.getStub().getNumber()); + } + + if (id != 0) { + sb.append('.').append(id); + } + + sb.append('}'); + + return sb.toString(); } //------------------------// @@ -699,16 +711,15 @@ public String toString () private int computeDurationDivisor () { try { - final SortedSet durations = new TreeSet(); + final SortedSet durations = new TreeSet<>(); // Collect duration values for each standard chord in this page for (SystemInfo system : getSystems()) { for (MeasureStack stack : system.getStacks()) { for (AbstractChordInter chord : stack.getStandardChords()) { try { - final Rational duration = chord.isWholeRest() - ? stack.getExpectedDuration() - : chord.getDuration(); + final Rational duration = chord.isWholeRest() ? stack + .getExpectedDuration() : chord.getDuration(); if (duration != null) { durations.add(duration); diff --git a/src/main/org/audiveris/omr/score/PageReduction.java b/src/main/org/audiveris/omr/score/PageReduction.java index bfa6d7661..c47411088 100644 --- a/src/main/org/audiveris/omr/score/PageReduction.java +++ b/src/main/org/audiveris/omr/score/PageReduction.java @@ -41,15 +41,12 @@ */ public class PageReduction { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(PageReduction.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related page. */ private final Page page; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new PageReduction object. * @@ -60,7 +57,6 @@ public PageReduction (Page page) this.page = page; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // reduce // //--------// @@ -96,10 +92,10 @@ public void reduce () private List> buildSequences (Page page) { // Build candidates (here, a candidate is a Part) - List> sequences = new ArrayList>(); + List> sequences = new ArrayList<>(); for (SystemInfo system : page.getSystems()) { - List parts = new ArrayList(); + List parts = new ArrayList<>(); for (Part systemPart : system.getParts()) { parts.add(new PartCandidate(systemPart)); @@ -121,7 +117,7 @@ private List> buildSequences (Page page) */ private void storeResults (List resultEntries) { - List logicalParts = new ArrayList(); + List logicalParts = new ArrayList<>(); for (ResultEntry entry : resultEntries) { LogicalPart logicalPart = entry.result; @@ -134,7 +130,6 @@ private void storeResults (List resultEntries) } } - //~ Inner Classes ------------------------------------------------------------------------------ //---------------// // PartCandidate // //---------------// @@ -144,17 +139,14 @@ private void storeResults (List resultEntries) private static class PartCandidate implements Candidate { - //~ Instance fields ------------------------------------------------------------------------ private final Part systemPart; - //~ Constructors --------------------------------------------------------------------------- - public PartCandidate (Part part) + PartCandidate (Part part) { this.systemPart = part; } - //~ Methods -------------------------------------------------------------------------------- @Override public String getAbbreviation () { diff --git a/src/main/org/audiveris/omr/score/PageRef.java b/src/main/org/audiveris/omr/score/PageRef.java index a40fc9675..bc31e0d3d 100644 --- a/src/main/org/audiveris/omr/score/PageRef.java +++ b/src/main/org/audiveris/omr/score/PageRef.java @@ -39,7 +39,6 @@ public class PageRef implements Comparable { - //~ Instance fields ---------------------------------------------------------------------------- // Persistent data //---------------- @@ -59,13 +58,12 @@ public class PageRef // private int sheetNumber; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PageRef} object. * * @param sheetNumber sheet number within book * @param id page id within sheet - * @param movementStart is page a movement start + * @param movementStart is page a movement start? * @param deltaMeasureId increase of measure IDs within the page, or null */ public PageRef (int sheetNumber, @@ -88,13 +86,13 @@ private PageRef () movementStart = false; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // compareTo // //-----------// @Override public int compareTo (PageRef that) { + // This is a total ordering if (this.sheetNumber != that.sheetNumber) { return Integer.compare(this.sheetNumber, that.sheetNumber); } @@ -123,6 +121,14 @@ public Integer getDeltaMeasureId () return deltaMeasureId; } + /** + * @param deltaMeasureId the deltaMeasureId to set + */ + public void setDeltaMeasureId (Integer deltaMeasureId) + { + this.deltaMeasureId = deltaMeasureId; + } + /** * @return the id */ @@ -163,14 +169,6 @@ public boolean isMovementStart () return movementStart; } - /** - * @param deltaMeasureId the deltaMeasureId to set - */ - public void setDeltaMeasureId (Integer deltaMeasureId) - { - this.deltaMeasureId = deltaMeasureId; - } - @Override public String toString () { diff --git a/src/main/org/audiveris/omr/score/PartConnection.java b/src/main/org/audiveris/omr/score/PartConnection.java index 619f806ed..84e59a96d 100644 --- a/src/main/org/audiveris/omr/score/PartConnection.java +++ b/src/main/org/audiveris/omr/score/PartConnection.java @@ -57,14 +57,11 @@ */ public class PartConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(PartConnection.class); - //~ Instance fields ---------------------------------------------------------------------------- private List resultEntries; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PartConnection} object. * @@ -75,7 +72,6 @@ public PartConnection (List> sequences) resultEntries = connect(sequences); } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // dumpResults // //-------------// @@ -134,7 +130,7 @@ private ResultEntry addResultEntry (int resultIndex, logger.debug("Created {} from {}", result, candidate); ResultEntry entry = new ResultEntry(); - List candidates = new ArrayList(); + List candidates = new ArrayList<>(); candidates.add(candidate); entry.result = result; entry.candidates = candidates; @@ -177,7 +173,7 @@ private int biIndex (List sequence) private List connect (List> sequences) { /** Resulting sequence of logical parts. */ - resultEntries = new ArrayList(); + resultEntries = new ArrayList<>(); // Two-staff entry found, if any ResultEntry biEntry = null; @@ -247,7 +243,8 @@ private void dispatch (List sequence, resultIndex += dir; logger.debug("resultIndex:{}", resultIndex); - if (((dir > 0) && (resultIndex >= results.size())) || ((dir < 0) && (resultIndex < 0))) { + if (((dir > 0) && (resultIndex >= results.size())) || ((dir < 0) + && (resultIndex < 0))) { logger.debug("No more entries available"); // Create a brand new logical part for this candidate @@ -294,7 +291,6 @@ private void renumberResults () } } - //~ Inner Interfaces --------------------------------------------------------------------------- //-----------// // Candidate // //-----------// @@ -310,40 +306,44 @@ private void renumberResults () */ public static interface Candidate { - //~ Methods -------------------------------------------------------------------------------- - /** Report the abbreviation, if any, that relates to this part + /** + * Report the abbreviation, if any, that relates to this part * * @return the abbreviation if any */ String getAbbreviation (); - /** Report the name of the part, if any + /** + * Report the name of the part, if any * * @return the part name if any */ String getName (); - /** Report the number of staves in the part + /** + * Report the number of staves in the part * * @return the number of staves */ int getStaffCount (); - /** Assign an id to the candidate (and recursively to its affiliates if any). + /** + * Assign an id to the candidate (and recursively to its affiliates if any). * * @param id the assigned id value */ void setId (int id); } - //~ Inner Classes ------------------------------------------------------------------------------ //-------------// // ResultEntry // //-------------// + /** + * Entry to gather results related to a single part. + */ public static class ResultEntry { - //~ Instance fields ------------------------------------------------------------------------ /** Resulting logical part. */ LogicalPart result; diff --git a/src/main/org/audiveris/omr/score/PartData.java b/src/main/org/audiveris/omr/score/PartData.java index 78b16aabd..1990e593b 100644 --- a/src/main/org/audiveris/omr/score/PartData.java +++ b/src/main/org/audiveris/omr/score/PartData.java @@ -1,59 +1,66 @@ -//------------------------------------------------------------------------------------------------// -// // -// P a r t D a t a // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.score; - -import javax.xml.bind.annotation.XmlAttribute; - -public class PartData -{ - - //~ Instance fields ------------------------------------------------------------------------ - /** Name of the part */ - @XmlAttribute - public final String name; - - /** Midi Instrument */ - @XmlAttribute - public final int program; - - //~ Constructors --------------------------------------------------------------------------- - public PartData (String name, - int program) - { - this.name = name; - this.program = program; - } - - private PartData () - { - name = null; - program = 0; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return "{name:" + name + " program:" + program + "}"; - } - -} +//------------------------------------------------------------------------------------------------// +// // +// P a r t D a t a // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.score; + +import javax.xml.bind.annotation.XmlAttribute; + +/** + * Class {@code PartData} gathers descriptive information at score level for a part. + * + * @author Hervé Bitteur + */ +public class PartData +{ + + /** Name of the part, such as found in left margin of a part. */ + @XmlAttribute + public final String name; + + /** Midi Instrument. */ + @XmlAttribute + public final int program; + + /** + * Creates a new {@code PartData} object. + * + * @param name part name + * @param program midi program number + */ + public PartData (String name, + int program) + { + this.name = name; + this.program = program; + } + + private PartData () + { + name = null; + program = 0; + } + + @Override + public String toString () + { + return "{name:" + name + " program:" + program + "}"; + } +} diff --git a/src/main/org/audiveris/omr/score/PartwiseBuilder.java b/src/main/org/audiveris/omr/score/PartwiseBuilder.java index 9a7cf771d..a51f5b510 100644 --- a/src/main/org/audiveris/omr/score/PartwiseBuilder.java +++ b/src/main/org/audiveris/omr/score/PartwiseBuilder.java @@ -37,6 +37,7 @@ import org.audiveris.omr.sheet.SheetStub; import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sheet.beam.BeamGroup; import org.audiveris.omr.sheet.rhythm.Measure; import org.audiveris.omr.sheet.rhythm.MeasureStack; import org.audiveris.omr.sheet.rhythm.Slot; @@ -48,6 +49,8 @@ import static org.audiveris.omr.sig.inter.AbstractNoteInter.QUARTER_DURATION; import org.audiveris.omr.sig.inter.AbstractTimeInter; import org.audiveris.omr.sig.inter.AlterInter; +import org.audiveris.omr.sig.inter.ArpeggiatoInter; +import org.audiveris.omr.sig.inter.ArticulationInter; import org.audiveris.omr.sig.inter.ClefInter; import org.audiveris.omr.sig.inter.DynamicsInter; import org.audiveris.omr.sig.inter.EndingInter; @@ -58,6 +61,7 @@ import org.audiveris.omr.sig.inter.KeyInter; import org.audiveris.omr.sig.inter.LyricItemInter; import org.audiveris.omr.sig.inter.MarkerInter; +import org.audiveris.omr.sig.inter.OrnamentInter; import org.audiveris.omr.sig.inter.PedalInter; import org.audiveris.omr.sig.inter.RestChordInter; import org.audiveris.omr.sig.inter.SentenceInter; @@ -67,7 +71,9 @@ import org.audiveris.omr.sig.inter.StemInter; import org.audiveris.omr.sig.inter.TupletInter; import org.audiveris.omr.sig.inter.WedgeInter; +import org.audiveris.omr.sig.relation.ChordArticulationRelation; import org.audiveris.omr.sig.relation.ChordDynamicsRelation; +import org.audiveris.omr.sig.relation.ChordOrnamentRelation; import org.audiveris.omr.sig.relation.ChordPedalRelation; import org.audiveris.omr.sig.relation.ChordSentenceRelation; import org.audiveris.omr.sig.relation.ChordSyllableRelation; @@ -87,6 +93,7 @@ import static org.audiveris.omr.util.VerticalSide.*; import org.audiveris.proxymusic.AboveBelow; import org.audiveris.proxymusic.Accidental; +import org.audiveris.proxymusic.Arpeggiate; import org.audiveris.proxymusic.Articulations; import org.audiveris.proxymusic.Attributes; import org.audiveris.proxymusic.Backup; @@ -124,6 +131,8 @@ import org.audiveris.proxymusic.Notations; import org.audiveris.proxymusic.Note; import org.audiveris.proxymusic.NoteType; +import org.audiveris.proxymusic.Notehead; +import org.audiveris.proxymusic.NoteheadValue; import org.audiveris.proxymusic.ObjectFactory; import org.audiveris.proxymusic.Ornaments; import org.audiveris.proxymusic.OverUnder; @@ -178,6 +187,7 @@ import java.awt.geom.CubicCurve2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; @@ -206,7 +216,6 @@ */ public class PartwiseBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -242,7 +251,6 @@ public Void call () /** Should be 6, but is 12 to cope with slurs not closed for lack of time slot. */ private static final int MAX_SLUR_NUMBER = 12; - //~ Instance fields ---------------------------------------------------------------------------- /** The ScorePartwise instance to be populated. */ private final ScorePartwise scorePartwise = new ScorePartwise(); @@ -259,15 +267,14 @@ public Void call () private final IsFirst isFirst = new IsFirst(); /** Map of Slur numbers, reset for every LogicalPart. */ - private final Map slurNumbers = new HashMap(); + private final Map slurNumbers = new HashMap<>(); /** Map of Tuplet numbers, reset for every Measure. */ - private final Map tupletNumbers = new HashMap(); + private final Map tupletNumbers = new HashMap<>(); /** Factory for ProxyMusic entities. */ private final ObjectFactory factory = new ObjectFactory(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new PartwiseBuilder object, on a related score instance. * @@ -276,7 +283,8 @@ public Void call () * @throws ExecutionException if a checked exception was thrown */ private PartwiseBuilder (Score score) - throws InterruptedException, ExecutionException + throws InterruptedException, + ExecutionException { // Make sure the JAXB context is ready loading.get(); @@ -284,109 +292,6 @@ private PartwiseBuilder (Score score) this.score = score; } - //~ Methods ------------------------------------------------------------------------------------ - //-------// - // build // - //-------// - /** - * Visit the whole score tree and build the corresponding ScorePartwise. - * - * @param score the score to export (cannot be null) - * @return the populated ScorePartwise - * @throws InterruptedException if the thread has been interrupted - * @throws ExecutionException if a checked exception was thrown - */ - public static ScorePartwise build (Score score) - throws InterruptedException, ExecutionException - { - Objects.requireNonNull(score, "Trying to export a null score"); - - final PartwiseBuilder builder = new PartwiseBuilder(score); - - builder.processScore(); - - return builder.scorePartwise; - } - - //---------// - // preload // - //---------// - /** - * Empty static method, just to trigger class elaboration (and thus JAXB context). - */ - public static void preload () - { - } - - //----------// - // areEqual // - //----------// - private static boolean areEqual (Time left, - Time right) - { - return (getNum(left).equals(getNum(right))) && (getDen(left).equals(getDen(right))); - } - - //----------// - // areEqual // - //----------// - private static boolean areEqual (Key left, - Key right) - { - return left.getFifths().equals(right.getFifths()); - } - - //----------// - // areEqual // - //----------// - /** - * Check whether the two Clef instances are equal. - * - * @param left one clef - * @param right another clef - * @return true if equal - */ - private static boolean areEqual (Clef left, - Clef right) - { - return Objects.equals(left.getNumber(), right.getNumber()) - && Objects.equals(left.getSign(), right.getSign()) - && Objects.equals(left.getLine(), right.getLine()) - && Objects.equals(left.getClefOctaveChange(), right.getClefOctaveChange()); - } - - //--------// - // getDen // A VERIFIER A VERIFIER A VERIFIER A VERIFIER A VERIFIER - //--------// - private static java.lang.String getDen (Time time) - { - for (JAXBElement elem : time.getTimeSignature()) { - if (elem.getName().getLocalPart().equals("beat-type")) { - return elem.getValue(); - } - } - - logger.error("No denominator found in {}", time); - - return ""; - } - - //--------// - // getNum // A VERIFIER A VERIFIER A VERIFIER A VERIFIER A VERIFIER - //--------// - private static java.lang.String getNum (Time time) - { - for (JAXBElement elem : time.getTimeSignature()) { - if (elem.getName().getLocalPart().equals("beats")) { - return elem.getValue(); - } - } - - logger.error("No numerator found in {}", time); - - return ""; - } - //---------// // addSlur // //---------// @@ -526,7 +431,8 @@ private ScorePartwise.Part createScorePart (LogicalPart logicalPart) PartName partName = factory.createPartName(); pmScorePart.setPartName(partName); partName.setValue( - (logicalPart.getName() != null) ? logicalPart.getName() : logicalPart.getDefaultName()); + (logicalPart.getName() != null) ? logicalPart.getName() + : logicalPart.getDefaultName()); // Score instrument Integer midiProgram = logicalPart.getMidiProgram(); @@ -640,8 +546,8 @@ private Key getCurrentKey () // Browse the current list of measures backwards within current part List measures = current.pmPart.getMeasure(); - for (ListIterator it = measures.listIterator(measures.size()); - it.hasPrevious();) { + for (ListIterator it = measures.listIterator( + measures.size()); it.hasPrevious();) { ScorePartwise.Part.Measure pmMeasure = it.previous(); for (Object obj : pmMeasure.getNoteOrBackupOrForward()) { @@ -659,33 +565,6 @@ private Key getCurrentKey () return null; // No key found } - //----------// - // getGrace // - //----------// - /** - * Report the grace chord, if any, which precedes the provided head-chord. - * - * @param chord the standard chord to check - * @return the linked grace chord if any - */ - private SmallChordInter getGrace (HeadChordInter chord) - { - final SIGraph sig = chord.getSig(); - - for (Inter interNote : chord.getNotes()) { - for (Relation rel : sig.getRelations(interNote, SlurHeadRelation.class)) { - SlurInter slur = (SlurInter) sig.getOppositeInter(interNote, rel); - HeadInter head = slur.getHead(HorizontalSide.LEFT); - - if ((head != null) && head.getShape().isSmall()) { - return (SmallChordInter) head.getChord(); - } - } - } - - return null; - } - //--------------// // getNotations // //--------------// @@ -804,7 +683,7 @@ private void insertForward (Rational delta, forward.setVoice("" + current.voice.getId()); current.pmMeasure.getNoteOrBackupOrForward().add(forward); - // Staff ? (only if more than one staff in logicalPart) + // Staff? (only if more than one staff in logicalPart) insertStaffId(forward, chord.getTopStaff()); } catch (Exception ex) { if (current.page.getDurationDivisor() != null) { @@ -833,7 +712,11 @@ private void insertStaffId (Object obj, try { Method method = classe.getMethod("setStaff", BigInteger.class); method.invoke(obj, new BigInteger("" + (1 + staff.getIndexInPart()))); - } catch (Exception ex) { + } catch (IllegalAccessException | + IllegalArgumentException | + NoSuchMethodException | + SecurityException | + InvocationTargetException ex) { ex.printStackTrace(); logger.error("Could not setStaff for element {}", classe); } @@ -855,8 +738,8 @@ private boolean isNewClef (Clef newClef) // Browse the current list of measures backwards List measures = current.pmPart.getMeasure(); - for (ListIterator mit = measures.listIterator(measures.size()); - mit.hasPrevious();) { + for (ListIterator mit = measures.listIterator( + measures.size()); mit.hasPrevious();) { ScorePartwise.Part.Measure pmMeasure = mit.previous(); // Look backwards in measure items, checking staff @@ -883,6 +766,76 @@ private boolean isNewClef (Clef newClef) return true; // Since no previous clef was found for the same staff } + //- All processing Methods --------------------------------------------------------------------- + //-------------------// + // processArpeggiato // + //-------------------// + /** + * Try to process an arpeggiato item. + * + * @param arpeggiate, perhaps null + */ + private void processArpeggiato (ArpeggiatoInter arpeggiate) + { + if (arpeggiate == null) { + return; + } + + try { + logger.debug("Visiting {}", arpeggiate); + + Arpeggiate pmArpeggiate = factory.createArpeggiate(); + + // relative-x + pmArpeggiate.setRelativeX( + toTenths(arpeggiate.getCenter().x - current.note.getCenterLeft().x)); + + // number ??? + // TODO + // + getNotations().getTiedOrSlurOrTuplet().add(pmArpeggiate); + } catch (Exception ex) { + logger.warn("Error visiting " + arpeggiate, ex); + } + } + + //---------------------// + // processArticulation // + //---------------------// + private void processArticulation (ArticulationInter articulation) + { + try { + logger.debug("Visiting {}", articulation); + + JAXBElement element = getArticulationObject(articulation.getShape()); + + // Staff? + Staff staff = current.note.getStaff(); + + // Placement + Class classe = element.getDeclaredType(); + + Method method = classe.getMethod("setPlacement", AboveBelow.class); + method.invoke( + element.getValue(), + (articulation.getCenter().y < current.note.getCenter().y) ? AboveBelow.ABOVE + : AboveBelow.BELOW); + + // Default-Y + method = classe.getMethod("setDefaultY", BigDecimal.class); + method.invoke(element.getValue(), yOf(articulation.getCenter(), staff)); + + // Include in Articulations + getArticulations().getAccentOrStrongAccentOrStaccato().add(element); + } catch (IllegalAccessException | + IllegalArgumentException | + NoSuchMethodException | + SecurityException | + InvocationTargetException ex) { + logger.warn("Error visiting " + articulation, ex); + } + } + //----------------// // processBarline // //----------------// @@ -910,8 +863,7 @@ private void processBarline (PartBarline partBarline, final EndingInter ending = partBarline.getEnding( (location == RightLeftMiddle.RIGHT) ? RIGHT : LEFT); final String endingValue = (ending != null) ? ending.getValue() : null; - String endingNumber = (ending != null) ? ending.getExportedNumber() - : null; + String endingNumber = (ending != null) ? ending.getExportedNumber() : null; if (endingNumber == null) { endingNumber = "99"; // Dummy integer value to mean: unknown @@ -925,13 +877,15 @@ private void processBarline (PartBarline partBarline, needed |= (partBarline == current.measure.getLeftPartBarline()); // On left side, with stuff (left repeat, left ending): needed |= ((location == RightLeftMiddle.LEFT) - && (stack.isRepeat(LEFT) || (ending != null))); + && (stack.isRepeat(LEFT) || (ending != null))); // Specific barline on middle location: needed |= (location == RightLeftMiddle.MIDDLE); // On right side, but with stuff (right repeat, right ending, fermata) or non regular: needed |= ((location == RightLeftMiddle.RIGHT) - && (stack.isRepeat(RIGHT) || (ending != null) || !fermatas.isEmpty() - || (style != PartBarline.Style.REGULAR))); + && (stack.isRepeat(RIGHT) + || (ending != null) + || !fermatas.isEmpty() + || (style != PartBarline.Style.REGULAR))); if (needed) { try { @@ -943,7 +897,7 @@ private void processBarline (PartBarline partBarline, : RightLeftMiddle.LEFT); BarStyleColor barStyleColor = factory.createBarStyleColor(); - barStyleColor.setValue(barStyleOf(style)); + barStyleColor.setValue(barStyleOf(style, location)); pmBarline.setBarStyle(barStyleColor); switch (location) { @@ -1104,42 +1058,40 @@ private void processDirection (SentenceInter sentence) String content = sentence.getValue(); - if (content != null) { - Direction direction = factory.createDirection(); - DirectionType directionType = factory.createDirectionType(); - FormattedText pmWords = factory.createFormattedText(); - Point location = sentence.getLocation(); + Direction direction = factory.createDirection(); + DirectionType directionType = factory.createDirectionType(); + FormattedText pmWords = factory.createFormattedText(); + Point location = sentence.getLocation(); - pmWords.setValue(content); + pmWords.setValue(content); - // Staff - Staff staff = current.note.getStaff(); - insertStaffId(direction, staff); + // Staff + Staff staff = current.note.getStaff(); + insertStaffId(direction, staff); - // Placement - direction.setPlacement( - (location.y < current.note.getCenter().y) ? AboveBelow.ABOVE : AboveBelow.BELOW); + // Placement + direction.setPlacement( + (location.y < current.note.getCenter().y) ? AboveBelow.ABOVE + : AboveBelow.BELOW); - // default-y - pmWords.setDefaultY(yOf(location, staff)); + // default-y + pmWords.setDefaultY(yOf(location, staff)); - // Font information - setFontInfo(pmWords, sentence); + // Font information + setFontInfo(pmWords, sentence); - // relative-x - pmWords.setRelativeX(toTenths(location.x - current.note.getCenterLeft().x)); + // relative-x + pmWords.setRelativeX(toTenths(location.x - current.note.getCenterLeft().x)); - // Everything is now OK - directionType.getWords().add(pmWords); - direction.getDirectionType().add(directionType); - current.pmMeasure.getNoteOrBackupOrForward().add(direction); - } + // Everything is now OK + directionType.getWords().add(pmWords); + direction.getDirectionType().add(directionType); + current.pmMeasure.getNoteOrBackupOrForward().add(direction); } catch (Exception ex) { logger.warn("Error visiting " + sentence, ex); } } - // // //------------------// // // processChordName // // //------------------// @@ -1261,7 +1213,7 @@ private void processDynamics (DynamicsInter dynamics) // Precise dynamic signature pmDynamics.getPOrPpOrPpp().add(getDynamicsObject(dynamics.getShape())); - // Staff ? + // Staff? Staff staff = current.note.getStaff(); insertStaffId(direction, staff); @@ -1335,9 +1287,9 @@ private void processFermata (FermataInter fermata, // Everything is now OK if (pmBarline != null) { - pmBarline.getFermata().add(pmFermata); // Add to note + pmBarline.getFermata().add(pmFermata); // Add to barline } else { - getNotations().getTiedOrSlurOrTuplet().add(pmFermata); // Add to barline + getNotations().getTiedOrSlurOrTuplet().add(pmFermata); // Add to note } } catch (Exception ex) { logger.warn("Error visiting " + fermata, ex); @@ -1524,7 +1476,7 @@ private void processMarker (MarkerInter marker) // // default-y // empty.setDefaultY(yOf(marker.getCenterLeft(), staff)); // - // Need also a Sound element (TODO: We don't do anything with sound!) + // Need also a Sound element Sound sound = factory.createSound(); direction.setSound(sound); sound.setDivisions(new BigDecimal(current.page.simpleDurationOf(QUARTER_DURATION))); @@ -1541,6 +1493,38 @@ private void processMarker (MarkerInter marker) directionType.getSegno().add(empty); break; + + case DA_CAPO: { + FormattedText text = new FormattedText(); + text.setValue("D.C."); + directionType.getWords().add(text); + sound.setDacapo(YesNo.YES); + } + + break; + + case DAL_SEGNO: { + // Example: + // + // + // D.S. al Fine + // + // + // + FormattedText text = new FormattedText(); + text.setValue("D.S."); + directionType.getWords().add(text); + + //TODO: we need to point back to id of measure where segno is located + ///sound.setDalsegno(measureId); // NO, not this measure, but the target measure! + } + + break; + + default: + logger.warn("Unknown marker shape: {}", marker.getShape()); + + return; } // Everything is now OK @@ -1585,7 +1569,7 @@ private void processMeasure (Measure measure) // Print? new MeasurePrint(measure).process(); - // Left/mid barline ? + // Left/mid barline? PartBarline mid = measure.getMidPartBarline(); if (mid != null) { @@ -1597,9 +1581,8 @@ private void processMeasure (Measure measure) // Divisions? if (isPageFirstMeasure) { try { - getAttributes() - .setDivisions( - new BigDecimal(current.page.simpleDurationOf(QUARTER_DURATION))); + getAttributes().setDivisions( + new BigDecimal(current.page.simpleDurationOf(QUARTER_DURATION))); } catch (Exception ex) { if (current.page.getDurationDivisor() == null) { logger.warn( @@ -1664,7 +1647,7 @@ private void processMeasure (Measure measure) for (Voice voice : measure.getVoices()) { current.voice = voice; - // Need a backup ? + // Need a backup? if (!timeCounter.equals(Rational.ZERO)) { insertBackup(timeCounter); timeCounter = Rational.ZERO; @@ -1683,13 +1666,12 @@ private void processMeasure (Measure measure) for (Slot slot : stack.getSlots()) { Voice.SlotVoice info = voice.getSlotInfo(slot); - if ((info != null) - && // Skip free slots + if ((info != null) && // Skip free slots (info.status == Voice.Status.BEGIN)) { AbstractChordInter chord = info.chord; clefIters.push(slot.getXOffset(), chord.getTopStaff()); - // Need a forward before this chord ? + // Need a forward before this chord? Rational timeOffset = chord.getTimeOffset(); if (timeCounter.compareTo(timeOffset) < 0) { @@ -1697,12 +1679,21 @@ private void processMeasure (Measure measure) timeCounter = timeOffset; } - // Grace note before this chord? + // Grace chord(s) before this chord? if (chord instanceof HeadChordInter) { - SmallChordInter small = getGrace((HeadChordInter) chord); + HeadChordInter headChord = (HeadChordInter) chord; + SmallChordInter small = headChord.getGraceChord(); if (small != null) { - processChord(small); + BeamGroup group = small.getBeamGroup(); + + if (group != null) { + for (AbstractChordInter ch : group.getChords()) { + processChord(ch); + } + } else { + processChord(small); + } } } @@ -1712,7 +1703,7 @@ private void processMeasure (Measure measure) } } - // // Need an ending forward ? + // // Need an ending forward? // if (!stack.isImplicit() && !stack.isFirstHalf()) { // Rational termination = voice.getTermination(); // @@ -1757,25 +1748,33 @@ private void processNote (AbstractNoteInter note) logger.debug("Visiting {}", note); final SIGraph sig = note.getSig(); - current.note = note; - + final Staff staff = note.getStaff(); final AbstractChordInter chord = note.getChord(); final boolean isFirstInChord = chord.getNotes().indexOf(note) == 0; + current.note = note; + current.pmNote = factory.createNote(); + // For first note in chord if (!current.measure.isDummy()) { if (isFirstInChord) { - // Chord events (direction, pedal, dynamics, TODO: others?) + // Chord events (direction, pedal, dynamics, articulation, ornament) for (Relation rel : sig.edgesOf(chord)) { + final Inter other = sig.getOppositeInter(chord, rel); + if (rel instanceof ChordSentenceRelation) { - processDirection((SentenceInter) sig.getOppositeInter(chord, rel)); + processDirection((SentenceInter) other); } else if (rel instanceof ChordPedalRelation) { - processPedal((PedalInter) sig.getOppositeInter(chord, rel)); + processPedal((PedalInter) other); } else if (rel instanceof ChordWedgeRelation) { HorizontalSide side = ((ChordWedgeRelation) rel).getSide(); - processWedge((WedgeInter) sig.getOppositeInter(chord, rel), side); + processWedge((WedgeInter) other, side); } else if (rel instanceof ChordDynamicsRelation) { - processDynamics((DynamicsInter) sig.getOppositeInter(chord, rel)); + processDynamics((DynamicsInter) other); + } else if (rel instanceof ChordArticulationRelation) { + processArticulation((ArticulationInter) other); + } else if (rel instanceof ChordOrnamentRelation) { + processOrnament((OrnamentInter) other); } } @@ -1786,18 +1785,16 @@ private void processNote (AbstractNoteInter note) // process(chord.getChordSymbol()); // } } - } - - current.pmNote = factory.createNote(); - Staff staff = note.getStaff(); + // Arpeggiato involves every headChord note + if (chord instanceof HeadChordInter) { + HeadChordInter headChord = (HeadChordInter) chord; + processArpeggiato(headChord.getArpeggiato()); + } + } - // Chord notation events for first note in chord + // Chord events for first note in chord if (isFirstInChord) { - // for (Notation node : chord.getNotations()) { - // ///node.accept(this); - // process(node); - // } if (sig != null) { for (Relation rel : sig.edgesOf(chord)) { if (rel instanceof FermataChordRelation) { @@ -1819,7 +1816,7 @@ private void processNote (AbstractNoteInter note) // } } - // Rest ? + // Rest? boolean isMeasureRest = false; if (note.getShape().isRest()) { @@ -1852,7 +1849,8 @@ private void processNote (AbstractNoteInter note) if (stem != null) { for (Relation rel : sig.getRelations(stem, FlagStemRelation.class)) { - if (Shape.SMALL_FLAG_SLASH == sig.getOppositeInter(stem, rel).getShape()) { + if (Shape.SMALL_FLAG_SLASH == sig.getOppositeInter(stem, rel) + .getShape()) { grace.setSlash(YesNo.YES); break; @@ -1870,7 +1868,7 @@ private void processNote (AbstractNoteInter note) HeadInter head = (HeadInter) note; Key key = current.keys.get(staff.getIndexInPart()); Integer fifths = (key != null) ? key.getFifths().intValue() : null; - int alter = head.getAlter(fifths); + int alter = head.getAlteration(fifths); if (alter != 0) { pitch.setAlter(new BigDecimal(alter)); @@ -1886,7 +1884,7 @@ private void processNote (AbstractNoteInter note) toTenths(noteLeft - current.measure.getAbscissa(LEFT, staff))); } - // Tuplet factor ? + // Tuplet factor? if (chord.getTupletFactor() != null) { TimeModification timeModification = factory.createTimeModification(); timeModification.setActualNotes( @@ -1907,7 +1905,8 @@ private void processNote (AbstractNoteInter note) if (isFirstInChord) { List embraced = tuplet.getChords(); - if ((embraced.get(0) == chord) || (embraced.get(embraced.size() - 1) == chord)) { + if ((embraced.get(0) == chord) || (embraced.get( + embraced.size() - 1) == chord)) { processTuplet(tuplet); } } @@ -1934,7 +1933,13 @@ private void processNote (AbstractNoteInter note) } // Voice - current.pmNote.setVoice("" + chord.getVoice().getId()); + Voice voice = chord.getVoice(); + + if (voice != null) { + current.pmNote.setVoice("" + voice.getId()); + } else { + logger.warn("No voice for {}", chord); + } // Type if (!current.measure.isDummy()) { @@ -1945,22 +1950,21 @@ private void processNote (AbstractNoteInter note) } } - // - // // For specific mirrored note - // if (note.getMirroredNote() != null) { - // int fbn = note.getChord().getFlagsNumber() + note.getChord().getBeams().size(); - // - // if ((fbn > 0) && (note.getShape() == NOTEHEAD_VOID)) { - // // Indicate that the head should not be filled - // // normal - // Notehead notehead = factory.createNotehead(); - // notehead.setFilled(YesNo.NO); - // notehead.setValue(NoteheadValue.NORMAL); - // current.pmNote.setNotehead(notehead); - // } - // } - // - // Stem ? + // For specific mirrored note + if (note.getMirror() != null) { + int fbn = note.getChord().getBeamsOrFlagsNumber(); + + if ((fbn > 0) && (note.getShape() == Shape.NOTEHEAD_VOID)) { + // Indicate that the head should not be filled + // normal + Notehead notehead = factory.createNotehead(); + notehead.setFilled(YesNo.NO); + notehead.setValue(NoteheadValue.NORMAL); + current.pmNote.setNotehead(notehead); + } + } + + // Stem? if (chord.getStem() != null) { Stem pmStem = factory.createStem(); Point tail = chord.getTailLocation(); @@ -1975,7 +1979,7 @@ private void processNote (AbstractNoteInter note) current.pmNote.setStem(pmStem); } - // Staff ? + // Staff? if (current.logicalPart.isMultiStaff()) { current.pmNote.setStaff(new BigInteger("" + (1 + staff.getIndexInPart()))); } @@ -1986,7 +1990,7 @@ private void processNote (AbstractNoteInter note) } if (!note.getShape().isRest()) { - // Accidental ? + // Accidental? HeadInter head = (HeadInter) note; AlterInter alter = head.getAccidental(); @@ -1996,7 +2000,7 @@ private void processNote (AbstractNoteInter note) current.pmNote.setAccidental(accidental); } - // Beams ? + // Beams? int beamCounter = 0; for (AbstractBeamInter beam : chord.getBeams()) { @@ -2031,7 +2035,7 @@ private void processNote (AbstractNoteInter note) processSlur((SlurInter) sig.getOppositeInter(note, rel)); } - // Lyrics ? + // Lyrics? for (Relation rel : sig.getRelations(chord, ChordSyllableRelation.class)) { processSyllable((LyricItemInter) sig.getOppositeInter(chord, rel)); } @@ -2047,6 +2051,36 @@ private void processNote (AbstractNoteInter note) current.endNote(); } + //-----------------// + // processOrnament // + //-----------------// + @SuppressWarnings("unchecked") + private void processOrnament (OrnamentInter ornament) + { + try { + logger.debug("Visiting {}", ornament); + + JAXBElement element = getOrnamentObject(ornament.getShape()); + + // Placement? + Class classe = element.getDeclaredType(); + Method method = classe.getMethod("setPlacement", AboveBelow.class); + method.invoke( + element.getValue(), + (ornament.getCenter().y < current.note.getCenter().y) ? AboveBelow.ABOVE + : AboveBelow.BELOW); + // Everything is OK + // Include in ornaments + getOrnaments().getTrillMarkOrTurnOrDelayedTurn().add(element); + } catch (IllegalAccessException | + IllegalArgumentException | + NoSuchMethodException | + SecurityException | + InvocationTargetException ex) { + logger.warn("Error visiting " + ornament, ex); + } + } + //-------------// // processPart // //-------------// @@ -2077,7 +2111,7 @@ private void processPartList () scorePartwise.setPartList(partList); // Allocate & initialize a ScorePart instance for each logical part - Map partMap = new LinkedHashMap(); + Map partMap = new LinkedHashMap<>(); for (LogicalPart p : score.getLogicalParts()) { ScorePartwise.Part pmPart = createScorePart(p); @@ -2119,7 +2153,7 @@ private void processPedal (PedalInter pedal) sound.setDamperPedal("no"); } - // Staff ? + // Staff? Staff staff = current.note.getStaff(); insertStaffId(direction, staff); @@ -2136,7 +2170,8 @@ private void processPedal (PedalInter pedal) // Placement direction.setPlacement( - (refPoint.y < current.note.getCenter().y) ? AboveBelow.ABOVE : AboveBelow.BELOW); + (refPoint.y < current.note.getCenter().y) ? AboveBelow.ABOVE + : AboveBelow.BELOW); // Everything is OK directionType.setPedal(pmPedal); @@ -2219,24 +2254,24 @@ private void processScore () // Assuming 300 DPI scaling.setMillimeters( new BigDecimal( - String.format("%.4f", (current.scale.getInterline() * 25.4 * 4) / 300))); + String.format( + "%.4f", + (current.scale.getInterline() * 25.4 * 4) / 300))); scaling.setTenths(new BigDecimal(40)); // [Defaults]/PageLayout (using first page) - if (firstPage.getDimension() != null) { - PageLayout pageLayout = factory.createPageLayout(); - defaults.setPageLayout(pageLayout); - pageLayout.setPageHeight(toTenths(firstPage.getDimension().height)); - pageLayout.setPageWidth(toTenths(firstPage.getDimension().width)); - - PageMargins pageMargins = factory.createPageMargins(); - pageMargins.setType(MarginType.BOTH); - pageMargins.setLeftMargin(pageHorizontalMargin); - pageMargins.setRightMargin(pageHorizontalMargin); - pageMargins.setTopMargin(pageVerticalMargin); - pageMargins.setBottomMargin(pageVerticalMargin); - pageLayout.getPageMargins().add(pageMargins); - } + PageLayout pageLayout = factory.createPageLayout(); + defaults.setPageLayout(pageLayout); + pageLayout.setPageHeight(toTenths(firstPage.getDimension().height)); + pageLayout.setPageWidth(toTenths(firstPage.getDimension().width)); + + PageMargins pageMargins = factory.createPageMargins(); + pageMargins.setType(MarginType.BOTH); + pageMargins.setLeftMargin(pageHorizontalMargin); + pageMargins.setRightMargin(pageHorizontalMargin); + pageMargins.setTopMargin(pageVerticalMargin); + pageMargins.setBottomMargin(pageVerticalMargin); + pageLayout.getPageMargins().add(pageMargins); } // [Defaults]/LyricFont @@ -2273,88 +2308,6 @@ private void processScore () } } - //- All Visiting Methods ----------------------------------------------------------------------- - // //--------------------// - // // process Arpeggiate // - // //--------------------// - // private void process (OldArpeggiate arpeggiate) - // { - // try { - // logger.debug("Visiting {}", arpeggiate); - // - // Arpeggiate pmArpeggiate = factory.createArpeggiate(); - // - // // relative-x - // pmArpeggiate.setRelativeX( - // toTenths(arpeggiate.getReferencePoint().x - current.note.getCenterLeft().x)); - // - // // number ??? - // // TODO - // // - // getNotations().getTiedOrSlurOrTuplet().add(pmArpeggiate); - // } catch (Exception ex) { - // logger.warn("Error visiting " + arpeggiate, ex); - // } - // } - // - // //----------------------// - // // process Articulation // - // //----------------------// - // private void process (OldArticulation articulation) - // { - // try { - // logger.debug("Visiting {}", articulation); - // - // JAXBElement element = getArticulationObject(articulation.getShape()); - // - // // Staff ? - // Staff staff = current.note.getStaff(); - // - // // Placement - // Class classe = element.getDeclaredType(); - // - // Method method = classe.getMethod("setPlacement", AboveBelow.class); - // method.invoke( - // element.getValue(), - // (articulation.getReferencePoint().y < current.note.getCenter().y) - // ? AboveBelow.ABOVE : AboveBelow.BELOW); - // - // // Default-Y - // method = classe.getMethod("setDefaultY", BigDecimal.class); - // method.invoke(element.getValue(), yOf(articulation.getReferencePoint(), staff)); - // - // // Include in Articulations - // getArticulations().getAccentOrStrongAccentOrStaccato().add(element); - // } catch (Exception ex) { - // logger.warn("Error visiting " + articulation, ex); - // } - // } - // - // //------------------// - // // process Ornament // - // //------------------// - // @SuppressWarnings("unchecked") - // private void process (OldOrnament ornament) - // { - // try { - // logger.debug("Visiting {}", ornament); - // - // JAXBElement element = getOrnamentObject(ornament.getShape()); - // - // // Placement? - // Class classe = element.getDeclaredType(); - // Method method = classe.getMethod("setPlacement", AboveBelow.class); - // method.invoke( - // element.getValue(), - // (ornament.getReferencePoint().y < current.note.getCenter().y) ? AboveBelow.ABOVE - // : AboveBelow.BELOW); - // // Everything is OK - // // Include in ornaments - // getOrnaments().getTrillMarkOrTurnOrDelayedTurn().add(element); - // } catch (Exception ex) { - // logger.warn("Error visiting " + ornament, ex); - // } - // } //-----------------// // processSentence // //-----------------// @@ -2393,26 +2346,24 @@ private void processSentence (SentenceInter sentence) typedText.setValue(sentence.getValue()); // Additional type information? - if (role != null) { - switch (role) { - case CreatorArranger: - typedText.setType("arranger"); + switch (role) { + case CreatorArranger: + typedText.setType("arranger"); - break; + break; - case CreatorComposer: - typedText.setType("composer"); + case CreatorComposer: + typedText.setType("composer"); - break; + break; - case CreatorLyricist: - typedText.setType("lyricist"); + case CreatorLyricist: + typedText.setType("lyricist"); - break; + break; - default: - break; - } + default: + break; } scorePartwise.getIdentification().getCreator().add(typedText); @@ -2646,7 +2597,7 @@ private void processTime (int num, // BeatType time.getTimeSignature().add(factory.createTimeBeatType("" + den)); - // Symbol ? + // Symbol? if (shape != null) { switch (shape) { case COMMON_TIME: @@ -2658,6 +2609,8 @@ private void processTime (int num, time.setSymbol(TimeSymbol.CUT); break; + + default: } } @@ -2732,11 +2685,11 @@ private void processWedge (WedgeInter wedge, // Spread pmWedge.setSpread(toTenths(wedge.getSpread(side))); - // Staff ? + // Staff? Staff staff = current.note.getStaff(); insertStaffId(direction, staff); - // Start or stop ? + // Start or stop? final Point2D refPoint; if (side == LEFT) { @@ -2780,8 +2733,6 @@ private void processWedge (WedgeInter wedge, } } - //- Utilities ---------------------------------------------------------------------------------- - // //-------------// // setFontInfo // //-------------// @@ -2845,28 +2796,66 @@ private BigDecimal yOf (Point2D point, return toTenths(staffTopY - point.getY()); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet + //-------// + // build // + //-------// + /** + * Visit the whole score tree and build the corresponding ScorePartwise. + * + * @param score the score to export (cannot be null) + * @return the populated ScorePartwise + * @throws InterruptedException if the thread has been interrupted + * @throws ExecutionException if a checked exception was thrown + */ + public static ScorePartwise build (Score score) + throws InterruptedException, + ExecutionException { - //~ Instance fields ------------------------------------------------------------------------ + Objects.requireNonNull(score, "Trying to export a null score"); - private final Constant.Integer pageHorizontalMargin = new Constant.Integer( - "tenths", - 80, - "Page horizontal margin"); + final PartwiseBuilder builder = new PartwiseBuilder(score); - private final Constant.Integer pageVerticalMargin = new Constant.Integer( - "tenths", - 80, - "Page vertical margin"); + builder.processScore(); - private final Constant.Boolean avoidTupletBrackets = new Constant.Boolean( - false, - "Should we avoid brackets for all tuplets"); + return builder.scorePartwise; + } + + //---------// + // preload // + //---------// + /** + * Empty static method, just to trigger class elaboration (and thus JAXB context). + */ + public static void preload () + { + } + + //----------// + // areEqual // + //----------// + private static boolean areEqual (Key left, + Key right) + { + return left.getFifths().equals(right.getFifths()); + } + + //----------// + // areEqual // + //----------// + /** + * Check whether the two Clef instances are equal. + * + * @param left one clef + * @param right another clef + * @return true if equal + */ + private static boolean areEqual (Clef left, + Clef right) + { + return Objects.equals(left.getNumber(), right.getNumber()) && Objects.equals( + left.getSign(), + right.getSign()) && Objects.equals(left.getLine(), right.getLine()) && Objects + .equals(left.getClefOctaveChange(), right.getClefOctaveChange()); } //---------------// @@ -2880,7 +2869,6 @@ private static final class Constants */ private class ClefIterators { - //~ Instance fields ------------------------------------------------------------------------ /** Containing measure. */ private final Measure measure; @@ -2891,29 +2879,28 @@ private class ClefIterators /** Per staff, iterator on Clefs sorted by abscissa. */ private final Map> iters; - //~ Constructors --------------------------------------------------------------------------- - public ClefIterators (Measure measure) + ClefIterators (Measure measure) { this.measure = measure; staves = measure.getPart().getStaves(); // Temporary map: staff -> staff's clefs - Map> map = new HashMap>(); + Map> map = new HashMap<>(); for (ClefInter clef : measure.getClefs()) { Staff staff = clef.getStaff(); List list = map.get(staff); if (list == null) { - map.put(staff, list = new ArrayList()); + map.put(staff, list = new ArrayList<>()); } list.add(clef); } // Populate iterators - iters = new LinkedHashMap>(); + iters = new LinkedHashMap<>(); for (Map.Entry> entry : map.entrySet()) { List list = entry.getValue(); // Already sorted by full center abscissa @@ -2921,7 +2908,6 @@ public ClefIterators (Measure measure) } } - //~ Methods -------------------------------------------------------------------------------- /** * Push as far as possible the relevant clefs iterators, according to the * current abscissa offset. @@ -2946,7 +2932,7 @@ public void push (Integer xOffset, final ClefInter clef = it.next(); if (measure.isDummy() /// || measure.isTemporary() - || (stack.getXOffset(clef.getCenter(), theStaff) <= xOffset)) { + || (stack.getXOffset(clef.getCenter(), theStaff) <= xOffset)) { // Consume this clef processClef(clef); } else { @@ -2967,124 +2953,6 @@ public void push (Integer xOffset, } } - //---------// - // Current // - //---------// - /** Keep references of all current entities. */ - private static class Current - { - //~ Instance fields ------------------------------------------------------------------------ - - // Score dependent - Work pmWork; - - // Part dependent - LogicalPart logicalPart; - - ScorePartwise.Part pmPart; - - // Page dependent - Page page; - - Scale scale; - - // System dependent - SystemInfo system; - - // Measure dependent - Measure measure; - - ScorePartwise.Part.Measure pmMeasure; - - final TreeMap keys = new TreeMap(); - - Voice voice; - - Attributes pmAttributes; - - // Note dependent - AbstractNoteInter note; - - Note pmNote; - - Notations pmNotations; - - //~ Methods -------------------------------------------------------------------------------- - // Cleanup at end of measure - void endMeasure () - { - measure = null; - pmMeasure = null; - voice = null; - pmAttributes = null; - - endVoice(); - } - - // Cleanup at end of note - void endNote () - { - note = null; - pmNote = null; - pmNotations = null; - } - - // Cleanup at end of voice - void endVoice () - { - voice = null; - pmAttributes = null; - - endNote(); - } - } - - //---------// - // IsFirst // - //---------// - /** Composite flag to help drive processing of any entity. */ - private static class IsFirst - { - //~ Instance fields ------------------------------------------------------------------------ - - /** We are writing the first part of the score */ - boolean part; - - /** We are writing the first page of the score */ - boolean page; - - /** We are writing the first system in the current page */ - boolean system; - - /** We are writing the first measure in current system (in current logicalPart) */ - boolean measure; - - //~ Methods -------------------------------------------------------------------------------- - @Override - public java.lang.String toString () - { - StringBuilder sb = new StringBuilder(); - - if (part) { - sb.append(" firstPart"); - } - - if (page) { - sb.append(" firstPage"); - } - - if (system) { - sb.append(" firstSystem"); - } - - if (measure) { - sb.append(" firstMeasure"); - } - - return sb.toString(); - } - } - //--------------// // MeasurePrint // //--------------// @@ -3093,7 +2961,6 @@ public java.lang.String toString () */ private class MeasurePrint { - //~ Instance fields ------------------------------------------------------------------------ private final Measure measure; @@ -3102,8 +2969,7 @@ private class MeasurePrint /** Needed to remove the element if not actually used. */ private boolean used = false; - //~ Constructors --------------------------------------------------------------------------- - public MeasurePrint (Measure measure) + MeasurePrint (Measure measure) { this.measure = measure; @@ -3113,7 +2979,6 @@ public MeasurePrint (Measure measure) current.pmMeasure.getNoteOrBackupOrForward().add(pmPrint); } - //~ Methods -------------------------------------------------------------------------------- public void process () { populatePrint(); @@ -3155,7 +3020,8 @@ private void populatePrint () systemMargins.setRightMargin( toTenths( current.page.getDimension().width - current.system.getLeft() - - current.system.getWidth()).subtract(pageHorizontalMargin)); + - current.system.getWidth()).subtract( + pageHorizontalMargin)); if (isFirst.system) { // TopSystemDistance @@ -3187,13 +3053,15 @@ private void populatePrint () if (staffIndexInSystem > 0) { Staff staffAbove = system.getStaves().get(staffIndexInSystem - 1); staffLayout.setStaffDistance( - toTenths(staff.getLeftY(TOP) - staffAbove.getLeftY(BOTTOM))); + toTenths( + staff.getLeftY(TOP) - staffAbove.getLeftY(BOTTOM))); getPrint().getStaffLayout().add(staffLayout); } } catch (Exception ex) { logger.warn( "Error exporting staff layout system#" + current.system.getId() - + " part#" + current.logicalPart.getId() + " staff#" + staff.getId(), + + " part#" + current.logicalPart.getId() + " staff#" + + staff.getId(), ex); } } @@ -3221,4 +3089,141 @@ private void populatePrint () } } } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Integer pageHorizontalMargin = new Constant.Integer( + "tenths", + 80, + "Page horizontal margin"); + + private final Constant.Integer pageVerticalMargin = new Constant.Integer( + "tenths", + 80, + "Page vertical margin"); + + private final Constant.Boolean avoidTupletBrackets = new Constant.Boolean( + false, + "Should we avoid brackets for all tuplets"); + } + + //---------// + // Current // + //---------// + /** Keep references of all current entities. */ + private static class Current + { + + // Score dependent + Work pmWork; + + // Part dependent + LogicalPart logicalPart; + + ScorePartwise.Part pmPart; + + // Page dependent + Page page; + + Scale scale; + + // System dependent + SystemInfo system; + + // Measure dependent + Measure measure; + + ScorePartwise.Part.Measure pmMeasure; + + final TreeMap keys = new TreeMap<>(); + + Voice voice; + + Attributes pmAttributes; + + // Note dependent + AbstractNoteInter note; + + Note pmNote; + + Notations pmNotations; + + // Cleanup at end of measure + void endMeasure () + { + measure = null; + pmMeasure = null; + voice = null; + pmAttributes = null; + + endVoice(); + } + + // Cleanup at end of note + void endNote () + { + note = null; + pmNote = null; + pmNotations = null; + } + + // Cleanup at end of voice + void endVoice () + { + voice = null; + pmAttributes = null; + + endNote(); + } + } + + //---------// + // IsFirst // + //---------// + /** Composite flag to help drive processing of any entity. */ + private static class IsFirst + { + + /** We are writing the first part of the score */ + boolean part; + + /** We are writing the first page of the score */ + boolean page; + + /** We are writing the first system in the current page */ + boolean system; + + /** We are writing the first measure in current system (in current logicalPart) */ + boolean measure; + + @Override + public java.lang.String toString () + { + StringBuilder sb = new StringBuilder(); + + if (part) { + sb.append(" firstPart"); + } + + if (page) { + sb.append(" firstPage"); + } + + if (system) { + sb.append(" firstSystem"); + } + + if (measure) { + sb.append(" firstMeasure"); + } + + return sb.toString(); + } + } + } diff --git a/src/main/org/audiveris/omr/score/Score.java b/src/main/org/audiveris/omr/score/Score.java index c32497a26..5600e3d51 100644 --- a/src/main/org/audiveris/omr/score/Score.java +++ b/src/main/org/audiveris/omr/score/Score.java @@ -53,22 +53,19 @@ @XmlRootElement(name = "score") public class Score { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - Score.class); + private static final Logger logger = LoggerFactory.getLogger(Score.class); - /** Number of lines in a staff */ + /** Number of lines in a staff. */ public static final int LINE_NB = 5; - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // - /** Score id, within containing book. + /** + * Score id, within containing book. * see {@link #getId()}. */ /** LogicalPart list for the whole score. */ @@ -77,7 +74,7 @@ public class Score /** Pages links. */ @XmlElement(name = "page") - private final List pageLinks = new ArrayList(); + private final List pageLinks = new ArrayList<>(); // Transient data //--------------- @@ -87,7 +84,7 @@ public class Score private Book book; /** Page references. */ - private ArrayList pageRefs = new ArrayList(); + private ArrayList pageRefs = new ArrayList<>(); /** Referenced pages. */ private ArrayList pages; @@ -96,12 +93,11 @@ public class Score private final Param> partsParam = new PartsParam(); /** Handling of tempo parameter. */ - private final Param tempoParam = new Param(); + private final Param tempoParam = new Param<>(); /** The specified sound volume, if any. */ private Integer volume; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a Score. */ @@ -110,49 +106,15 @@ public Score () tempoParam.setParent(Tempo.defaultTempo); } - //~ Methods ------------------------------------------------------------------------------------ - //------------------// - // getDefaultVolume // - //------------------// - /** - * Report default value for Midi volume. - * - * @return the default volume value - */ - public static int getDefaultVolume () - { - return constants.defaultVolume.getValue(); - } - - //-----------------// - // setDefaultTempo // - //-----------------// - /** - * Assign default value for Midi tempo. - * - * @param tempo the default tempo value - */ - public static void setDefaultTempo (int tempo) - { - constants.defaultTempo.setValue(tempo); - } - - //------------------// - // setDefaultVolume // - //------------------// - /** - * Assign default value for Midi volume. - * - * @param volume the default volume value - */ - public static void setDefaultVolume (int volume) - { - constants.defaultVolume.setValue(volume); - } - //------------// // addPageRef // //------------// + /** + * Add a PageRef. + * + * @param stubNumber id of containing sheet stub + * @param pageRef to add + */ public void addPageRef (int stubNumber, PageRef pageRef) { @@ -171,15 +133,9 @@ public void close () logger.info("Closing {}", this); } - //-------------// - // disposePage // - //-------------// - public void disposePage (Page page) - { - throw new RuntimeException("disposePage. Not implemented yet."); - } - /** + * Report the containing book for this score + * * @return the book */ public Book getBook () @@ -187,9 +143,27 @@ public Book getBook () return book; } + //---------// + // setBook // + //---------// + /** + * Assign the containing book. + * + * @param book the book to set + */ + public void setBook (Book book) + { + this.book = book; + } + //--------------// // getFirstPage // //--------------// + /** + * Report the first page in this score + * + * @return first page + */ public Page getFirstPage () { if (pageRefs.isEmpty()) { @@ -202,6 +176,11 @@ public Page getFirstPage () //-----------------// // getFirstPageRef // //-----------------// + /** + * Return the first PageRef in this score + * + * @return first pageRef + */ public PageRef getFirstPageRef () { if (pageRefs.isEmpty()) { @@ -235,7 +214,9 @@ public Page getFollowingPage (Page page) // getId // //-------// /** - * @return the id, if any + * Report the score ID, if any + * + * @return the id, or null */ @XmlAttribute public Integer getId () @@ -252,6 +233,11 @@ public Integer getId () //-------------// // getLastPage // //-------------// + /** + * Report the last page in this score. + * + * @return last page + */ public Page getLastPage () { if (pageRefs.isEmpty()) { @@ -264,6 +250,11 @@ public Page getLastPage () //----------------// // getLastPageRef // //----------------// + /** + * Report the last PageRef in this score. + * + * @return last pageRef + */ public PageRef getLastPageRef () { if (pageRefs.isEmpty()) { @@ -286,6 +277,19 @@ public List getLogicalParts () return logicalParts; } + //-----------------// + // setLogicalParts // + //-----------------// + /** + * Assign a part list valid for the whole score. + * + * @param logicalParts the list of logical parts + */ + public void setLogicalParts (List logicalParts) + { + this.logicalParts = logicalParts; + } + //--------------------// // getMeasureIdOffset // //--------------------// @@ -364,6 +368,12 @@ public int getPageIndex (Page page) //------------// // getPageRef // //------------// + /** + * Return the score pageRef for a specified sheet stub. + * + * @param sheetNumber sheet stub number + * @return the score page in this sheet, or null + */ public PageRef getPageRef (int sheetNumber) { for (PageRef pageRef : pageRefs) { @@ -378,6 +388,11 @@ public PageRef getPageRef (int sheetNumber) //-------------// // getPageRefs // //-------------// + /** + * Report the sequence of PageRef instances for this score. + * + * @return sequence of PageRef's + */ public List getPageRefs () { return Collections.unmodifiableList(pageRefs); @@ -394,7 +409,7 @@ public List getPageRefs () public List getPages () { if (pages == null) { - pages = new ArrayList(); + pages = new ArrayList<>(); // De-reference pageRefs for (PageRef ref : pageRefs) { @@ -408,6 +423,11 @@ public List getPages () //---------------// // getPartsParam // //---------------// + /** + * Report the sequence of parts parameters. + * + * @return sequence of parts parameters (name, midi program) + */ public Param> getPartsParam () { return partsParam; @@ -463,7 +483,7 @@ public Integer getSheetPageId (int sheetNumber) */ public List getStubs () { - final List pageStubs = new ArrayList(); + final List pageStubs = new ArrayList<>(); final List bookStubs = book.getStubs(); for (PageRef ref : pageRefs) { @@ -476,6 +496,11 @@ public List getStubs () //----------------// // getTempoParam // //---------------// + /** + * Report the tempo parameter. + * + * @return tempo information + */ public Param getTempoParam () { return tempoParam; @@ -499,6 +524,19 @@ public Integer getVolume () return volume; } + //-----------// + // setVolume // + //-----------// + /** + * Assign a volume value. + * + * @param volume the volume value to be assigned + */ + public void setVolume (Integer volume) + { + this.volume = volume; + } + //-----------// // hasVolume // //-----------// @@ -543,43 +581,6 @@ public boolean isMultiPage () return pageRefs.size() > 1; } - //---------// - // setBook // - //---------// - /** - * @param book the book to set - */ - public void setBook (Book book) - { - this.book = book; - } - - //-----------------// - // setLogicalParts // - //-----------------// - /** - * Assign a part list valid for the whole score. - * - * @param logicalParts the list of logical parts - */ - public void setLogicalParts (List logicalParts) - { - this.logicalParts = logicalParts; - } - - //-----------// - // setVolume // - //-----------// - /** - * Assign a volume value. - * - * @param volume the volume value to be assigned - */ - public void setVolume (Integer volume) - { - this.volume = volume; - } - //----------// // toString // //----------// @@ -649,64 +650,43 @@ private PageRef getPageRef (Page page) return null; } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet + //------------------// + // getDefaultVolume // + //------------------// + /** + * Report default value for Midi volume. + * + * @return the default volume value + */ + public static int getDefaultVolume () { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Integer defaultTempo = new Constant.Integer( - "QuartersPerMn", - 120, - "Default tempo, stated in number of quarters per minute"); - - private final Constant.Integer defaultVolume = new Constant.Integer( - "Volume", - 78, - "Default Volume in 0..127 range"); + return constants.defaultVolume.getValue(); } - //----------// - // PageLink // - //----------// - private static class PageLink + //------------------// + // setDefaultVolume // + //------------------// + /** + * Assign default value for Midi volume. + * + * @param volume the default volume value + */ + public static void setDefaultVolume (int volume) { - //~ Instance fields ------------------------------------------------------------------------ - - @XmlAttribute(name = "sheet-number") - public final int sheetNumber; - - @XmlAttribute(name = "sheet-page-id") - public final int sheetPageId; - - //~ Constructors --------------------------------------------------------------------------- - public PageLink (int sheetNumber, - int sheetPageId) - { - this.sheetNumber = sheetNumber; - this.sheetPageId = sheetPageId; - } - - private PageLink () - { - this.sheetNumber = 0; - this.sheetPageId = 0; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("PageLink{"); - sb.append("sheetNumber:").append(sheetNumber); - sb.append(" sheetPageId:").append(sheetPageId); - sb.append('}'); + constants.defaultVolume.setValue(volume); + } - return sb.toString(); - } + //-----------------// + // setDefaultTempo // + //-----------------// + /** + * Assign default value for Midi tempo. + * + * @param tempo the default tempo value + */ + public static void setDefaultTempo (int tempo) + { + constants.defaultTempo.setValue(tempo); } //------------// @@ -715,24 +695,20 @@ public String toString () private class PartsParam extends Param> { - //~ Methods -------------------------------------------------------------------------------- @Override public List getSpecific () { List list = getLogicalParts(); - if (list != null) { - List data = new ArrayList(); - + List data = new ArrayList<>(); for (LogicalPart logicalPart : list) { // Initial setting for part midi program - int prog = (logicalPart.getMidiProgram() != null) - ? logicalPart.getMidiProgram() : logicalPart.getDefaultProgram(); + int prog = (logicalPart.getMidiProgram() != null) ? logicalPart.getMidiProgram() + : logicalPart.getDefaultProgram(); data.add(new PartData(logicalPart.getName(), prog)); } - return data; } else { return null; @@ -764,4 +740,60 @@ public boolean setSpecific (List specific) return false; } } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Integer defaultTempo = new Constant.Integer( + "QuartersPerMn", + 120, + "Default tempo, stated in number of quarters per minute"); + + private final Constant.Integer defaultVolume = new Constant.Integer( + "Volume", + 78, + "Default Volume in 0..127 range"); + } + + //----------// + // PageLink // + //----------// + private static class PageLink + { + + @XmlAttribute(name = "sheet-number") + public final int sheetNumber; + + @XmlAttribute(name = "sheet-page-id") + public final int sheetPageId; + + PageLink (int sheetNumber, + int sheetPageId) + { + this.sheetNumber = sheetNumber; + this.sheetPageId = sheetPageId; + } + + private PageLink () + { + this.sheetNumber = 0; + this.sheetPageId = 0; + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("PageLink{"); + sb.append("sheetNumber:").append(sheetNumber); + sb.append(" sheetPageId:").append(sheetPageId); + sb.append('}'); + + return sb.toString(); + } + } + } diff --git a/src/main/org/audiveris/omr/score/ScoreExporter.java b/src/main/org/audiveris/omr/score/ScoreExporter.java index 0d806bb1f..2c0d7d893 100644 --- a/src/main/org/audiveris/omr/score/ScoreExporter.java +++ b/src/main/org/audiveris/omr/score/ScoreExporter.java @@ -45,15 +45,12 @@ */ public class ScoreExporter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ScoreExporter.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The related score. */ private final Score score; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new {@code ScoreExporter} object, on a related score instance. * @@ -68,7 +65,6 @@ public ScoreExporter (Score score) this.score = score; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // export // //--------// @@ -87,10 +83,10 @@ public void export (Path path, boolean compressed) throws Exception { - final OutputStream os = new FileOutputStream(path.toString()); - export(os, signed, scoreName, compressed); - os.close(); - logger.info("Score {} exported to {}", scoreName, path); + try (OutputStream os = new FileOutputStream(path.toString())) { + export(os, signed, scoreName, compressed); + logger.info("Score {} exported to {}", scoreName, path); + } } //--------// diff --git a/src/main/org/audiveris/omr/score/ScoreReduction.java b/src/main/org/audiveris/omr/score/ScoreReduction.java index 57b576b79..ef5486e4a 100644 --- a/src/main/org/audiveris/omr/score/ScoreReduction.java +++ b/src/main/org/audiveris/omr/score/ScoreReduction.java @@ -36,7 +36,8 @@ * Class {@code ScoreReduction} reduces the logical parts for a score, * based on the merge of Audiveris Page instances. *

                          - * Features not yet implemented:

                            + * Features not yet implemented: + *
                              *
                            • Connection of slurs between pages
                            • *
                            • In part-list, handling of part-group beside score-part
                            • *
                            @@ -45,15 +46,12 @@ */ public class ScoreReduction { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ScoreReduction.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related score. */ private final Score score; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ScoreReduction object. * @@ -64,7 +62,6 @@ public ScoreReduction (Score score) this.score = score; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // reduce // //--------// @@ -102,10 +99,10 @@ public int reduce () private List> buildSequences (List pages) { // Build candidates (here a candidate is a LogicalPart, with affiliated system parts) - List> sequences = new ArrayList>(); + List> sequences = new ArrayList<>(); for (Page page : pages) { - List candidates = new ArrayList(); + List candidates = new ArrayList<>(); List partList = page.getLogicalParts(); if (partList != null) { @@ -135,7 +132,7 @@ private List> buildSequences (List pages) */ private boolean storeResults (List resultEntries) { - List partList = new ArrayList(); + List partList = new ArrayList<>(); for (ResultEntry entry : resultEntries) { LogicalPart logicalPart = entry.result; @@ -151,7 +148,6 @@ private boolean storeResults (List resultEntries) return false; } - //~ Inner Classes ------------------------------------------------------------------------------ //----------------------// // LogicalPartCandidate // //----------------------// @@ -161,7 +157,6 @@ private boolean storeResults (List resultEntries) private static class LogicalPartCandidate implements Candidate { - //~ Instance fields ------------------------------------------------------------------------ private final LogicalPart logicalPart; @@ -169,17 +164,15 @@ private static class LogicalPartCandidate private final List systemParts; - //~ Constructors --------------------------------------------------------------------------- - public LogicalPartCandidate (LogicalPart logicalPart, - Page page, - List systemParts) + LogicalPartCandidate (LogicalPart logicalPart, + Page page, + List systemParts) { this.logicalPart = logicalPart; this.page = page; this.systemParts = systemParts; } - //~ Methods -------------------------------------------------------------------------------- @Override public String getAbbreviation () { diff --git a/src/main/org/audiveris/omr/score/Source.java b/src/main/org/audiveris/omr/score/Source.java index 5cd60ea04..47c4ee665 100644 --- a/src/main/org/audiveris/omr/score/Source.java +++ b/src/main/org/audiveris/omr/score/Source.java @@ -78,7 +78,6 @@ */ public class Source { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Source.class); @@ -86,7 +85,6 @@ public class Source private static final String SHEET_PREFIX = "sheet-"; - //~ Instance fields ---------------------------------------------------------------------------- /** Path to source image file, if any. */ private String file; @@ -97,58 +95,17 @@ public class Source private URI uri; /** Systems processed in each image sheet. */ - private final List sheets = new ArrayList(); - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // decode // - //--------// - public static Source decode (ScorePartwise scorePartwise) - { - Identification identification = scorePartwise.getIdentification(); - - if (identification == null) { - return null; - } - - Miscellaneous misc = identification.getMiscellaneous(); - - if (misc == null) { - return null; - } - - Source source = new Source(); - - for (MiscellaneousField field : misc.getMiscellaneousField()) { - String name = field.getName(); - String value = field.getValue().trim(); - - ///logger.info("miscellaneous-field name:{} value:'{}'", name, value); - if (name.startsWith(SOURCE_PREFIX)) { - String tail = name.substring(SOURCE_PREFIX.length()); - - if (tail.equals("file")) { - source.file = value; - } else if (tail.equals("uri")) { - source.uri = URI.create(value); - } else if (tail.equals("offset")) { - source.offset = Integer.decode(value); - } else if (tail.startsWith(SHEET_PREFIX)) { - String numStr = tail.substring(SHEET_PREFIX.length()); - int num = Integer.decode(numStr); - SheetSystems sheet = new SheetSystems(num); - source.sheets.add(sheet); - sheet.getSystems().addAll(parseInts(value)); - } - } - } - - return source; - } + private final List sheets = new ArrayList<>(); //------------// // encodePage // //------------// + /** + * Encode provided page in target ScorePartwise. + * + * @param page input + * @param scorePartwise output + */ public void encodePage (Page page, ScorePartwise scorePartwise) { @@ -184,6 +141,11 @@ public void encodePage (Page page, //-------------// // encodeScore // //-------------// + /** + * Encode score source, by filling the MusicXML Miscellaneous element. + * + * @param scorePartwise the ScorePartwise to encode. + */ public void encodeScore (ScorePartwise scorePartwise) { final ObjectFactory factory = new ObjectFactory(); @@ -220,52 +182,6 @@ public void encodeScore (ScorePartwise scorePartwise) } } - // - // //--------// - // // encode // - // //--------// - // public void encode (ScorePartwise scorePartwise) - // { - // final ObjectFactory factory = new ObjectFactory(); - // Identification identification = scorePartwise.getIdentification(); - // - // if (identification == null) { - // identification = factory.createIdentification(); - // scorePartwise.setIdentification(identification); - // } - // - // Miscellaneous misc = identification.getMiscellaneous(); - // - // if (misc == null) { - // misc = factory.createMiscellaneous(); - // identification.setMiscellaneous(misc); - // } - // - // MiscellaneousField field; - // - // if (file != null) { - // misc.getMiscellaneousField().add(field = factory.createMiscellaneousField()); - // field.setName(SOURCE_PREFIX + "file"); - // field.setValue(file); - // } else if (uri != null) { - // misc.getMiscellaneousField().add(field = factory.createMiscellaneousField()); - // field.setName(SOURCE_PREFIX + "uri"); - // field.setValue(uri.toString()); - // } - // - // if (offset != 0) { - // misc.getMiscellaneousField().add(field = factory.createMiscellaneousField()); - // field.setName(SOURCE_PREFIX + "offset"); - // field.setValue("" + offset); - // } - // - // for (SheetSystems sheet : sheets) { - // misc.getMiscellaneousField().add(field = factory.createMiscellaneousField()); - // field.setName(SOURCE_PREFIX + SHEET_PREFIX + sheet.sheetNumber); - // field.setValue(packInts(sheet.getSystems())); - // } - // } - // /** * @return the file */ @@ -275,43 +191,43 @@ public String getFile () } /** - * @return the offset + * @param file the file to set */ - public int getOffset () + public void setFile (String file) { - return offset; + this.file = file; } /** - * @return the sheetSystems + * @return the offset */ - public List getSheets () + public int getOffset () { - return sheets; + return offset; } /** - * @return the uri + * @param offset the offset to set */ - public URI getUri () + public void setOffset (int offset) { - return uri; + this.offset = offset; } /** - * @param file the file to set + * @return the sheetSystems */ - public void setFile (String file) + public List getSheets () { - this.file = file; + return sheets; } /** - * @param offset the offset to set + * @return the uri */ - public void setOffset (int offset) + public URI getUri () { - this.offset = offset; + return uri; } /** @@ -347,6 +263,58 @@ public String toString () return sb.toString(); } + //--------// + // decode // + //--------// + /** + * Decode the Source information from MusicXML Miscellaneous element. + * + * @param scorePartwise the ScorePartwise to process + * @return Source information or null if not found + */ + public static Source decode (ScorePartwise scorePartwise) + { + Identification identification = scorePartwise.getIdentification(); + + if (identification == null) { + return null; + } + + Miscellaneous misc = identification.getMiscellaneous(); + + if (misc == null) { + return null; + } + + Source source = new Source(); + + for (MiscellaneousField field : misc.getMiscellaneousField()) { + String name = field.getName(); + String value = field.getValue().trim(); + + ///logger.info("miscellaneous-field name:{} value:'{}'", name, value); + if (name.startsWith(SOURCE_PREFIX)) { + String tail = name.substring(SOURCE_PREFIX.length()); + + if (tail.equals("file")) { + source.file = value; + } else if (tail.equals("uri")) { + source.uri = URI.create(value); + } else if (tail.equals("offset")) { + source.offset = Integer.decode(value); + } else if (tail.startsWith(SHEET_PREFIX)) { + String numStr = tail.substring(SHEET_PREFIX.length()); + int num = Integer.decode(numStr); + SheetSystems sheet = new SheetSystems(num); + source.sheets.add(sheet); + sheet.getSystems().addAll(parseInts(value)); + } + } + } + + return source; + } + //----------// // packInts // //----------// @@ -382,7 +350,7 @@ private static String packInts (List ints) */ private static List parseInts (String str) { - final List intList = new ArrayList(); + final List intList = new ArrayList<>(); final String[] tokens = str.split("\\s+"); for (String token : tokens) { @@ -400,31 +368,34 @@ private static List parseInts (String str) return intList; } - //~ Inner Classes ------------------------------------------------------------------------------ //--------------// // SheetSystems // //--------------// /** - * Describes which systems were processed in this sheet. + * Describes which systems have been processed in this sheet. */ public static class SheetSystems { - //~ Instance fields ------------------------------------------------------------------------ + + /** Sequence of systems processed, starting from 1. */ + private final List systems = new ArrayList<>(); /** Sheet number within source file, starting from 1. */ final int sheetNumber; - /** Sequence of systems processed, starting from 1. */ - private final List systems = new ArrayList(); - - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a SheetSystems object. + * + * @param sheetNumber starting sheet number in file + */ public SheetSystems (int sheetNumber) { this.sheetNumber = sheetNumber; } - //~ Methods -------------------------------------------------------------------------------- /** + * Report the IDs of the processed systems + * * @return the systems */ public List getSystems () diff --git a/src/main/org/audiveris/omr/score/StaffPosition.java b/src/main/org/audiveris/omr/score/StaffPosition.java index 325082f27..8acb60993 100644 --- a/src/main/org/audiveris/omr/score/StaffPosition.java +++ b/src/main/org/audiveris/omr/score/StaffPosition.java @@ -24,6 +24,8 @@ /** * Relative vertical position with respect to the staves of the system or the part * at hand. + * + * @author Hervé Bitteur */ public enum StaffPosition { diff --git a/src/main/org/audiveris/omr/score/Tempo.java b/src/main/org/audiveris/omr/score/Tempo.java index 5443cdc9a..27d86c350 100644 --- a/src/main/org/audiveris/omr/score/Tempo.java +++ b/src/main/org/audiveris/omr/score/Tempo.java @@ -35,7 +35,6 @@ */ public abstract class Tempo { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -44,20 +43,17 @@ public abstract class Tempo /** Default parameter. */ public static final Param defaultTempo = new Default(); - //~ Constructors ------------------------------------------------------------------------------- /** Not meant to be instantiated. */ private Tempo () { } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer defaultTempo = new Constant.Integer( "QuartersPerMn", @@ -71,7 +67,6 @@ private static final class Constants private static class Default extends Param { - //~ Methods -------------------------------------------------------------------------------- @Override public Integer getSpecific () diff --git a/src/main/org/audiveris/omr/score/TimeRational.java b/src/main/org/audiveris/omr/score/TimeRational.java index 562d82eae..895216d6f 100644 --- a/src/main/org/audiveris/omr/score/TimeRational.java +++ b/src/main/org/audiveris/omr/score/TimeRational.java @@ -41,19 +41,17 @@ * meant to carry the actual rational members of a TimeSignature. *

                            * For example, (3/4) and (6/8) share the same rational value, but with different actual members. + * + * @author Hervé Bitteur */ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "time-rational") -@XmlType(propOrder = { - "num", "den"} -) +@XmlType(propOrder = {"num", "den"}) public class TimeRational { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TimeRational.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The actual numerator. */ @XmlAttribute public final int num; @@ -62,7 +60,6 @@ public class TimeRational @XmlAttribute public final int den; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TimeRational object. * @@ -82,32 +79,6 @@ private TimeRational () den = num = 0; } - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // decode // - //--------// - /** - * Decode a string expected to contain one TimeRational value, formatted as num / den. - * - * @param str the string to decode - * @return the TimeRational value if successful - */ - public static TimeRational decode (String str) - { - final String[] tokens = str.split("\\s*/\\s*"); - - if (tokens.length == 2) { - int num = Integer.decode(tokens[0].trim()); - int den = Integer.decode(tokens[1].trim()); - - return new TimeRational(num, den); - } else { - logger.warn("Illegal TimeRational value: ", str); - - return null; - } - } - //--------// // equals // //--------// @@ -126,6 +97,13 @@ public boolean equals (Object obj) //----------// // getValue // //----------// + /** + * Report the rational value of this time signature. + *

                            + * Rational value of (2,4) is 1/2 + * + * @return rational value + */ public Rational getValue () { return new Rational(num, den); @@ -144,6 +122,41 @@ public int hashCode () return hash; } + //----------// + // toString // + //----------// + @Override + public String toString () + { + return num + "/" + den; + } + + //--------// + // decode // + //--------// + /** + * Decode a string expected to contain one TimeRational value, + * formatted as "num / den". + * + * @param str the string to decode + * @return the TimeRational value if successful + */ + public static TimeRational decode (String str) + { + final String[] tokens = str.split("\\s*/\\s*"); + + switch (tokens.length) { + case 2: { + int num = Integer.decode(tokens[0].trim()); + int den = Integer.decode(tokens[1].trim()); + + return new TimeRational(num, den); + } + default: + throw new NumberFormatException(str); + } + } + //-------------// // parseValues // //-------------// @@ -155,9 +168,8 @@ public int hashCode () */ public static List parseValues (String str) { - final List list = new ArrayList(); + final List list = new ArrayList<>(); final String[] tokens = str.split("\\s*,\\s*"); - for (String token : tokens) { String trimmedToken = token.trim(); @@ -169,27 +181,18 @@ public static List parseValues (String str) } } } - return list; } - //----------// - // toString // - //----------// - @Override - public String toString () - { - return num + "/" + den; - } - - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Adapter // //---------// + /** + * JAXB adapter for a TimeRational. + */ public static class Adapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public String marshal (TimeRational val) diff --git a/src/main/org/audiveris/omr/score/TimeSignatureFixer.java b/src/main/org/audiveris/omr/score/TimeSignatureFixer.java index 64a179678..4a351bba6 100644 --- a/src/main/org/audiveris/omr/score/TimeSignatureFixer.java +++ b/src/main/org/audiveris/omr/score/TimeSignatureFixer.java @@ -47,11 +47,9 @@ */ public class TimeSignatureFixer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TimeSignatureFixer.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TimeSignatureFixer object. */ @@ -59,7 +57,6 @@ public TimeSignatureFixer () { } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// @@ -109,19 +106,16 @@ public void process (Page page) } } else { // Whole page without explicit time signature - checkTimeSigs( - firstSystem.getFirstStack(), - page.getLastSystem().getLastStack()); + checkTimeSigs(firstSystem.getFirstStack(), page.getLastSystem().getLastStack()); } } catch (Exception ex) { logger.warn("TimeSignatureFixer. Error processing " + page, ex); } } - // - // //---------------// + // //--------------------// // // visit MeasureStack // - // //---------------// + // //--------------------// // @Override // public boolean visit (MeasureStack measure) // { @@ -153,18 +147,16 @@ private void checkTimeSigs (MeasureStack startStack, final Map sigMap = retrieveBestSigs(startStack, stopStack); // Sort them by decreasing occurrences - List sigs = new ArrayList(sigMap.keySet()); - Collections.sort( - sigs, - new Comparator() - { - @Override - public int compare (TimeRational t1, - TimeRational t2) - { - return Integer.compare(sigMap.get(t2), sigMap.get(t1)); - } - }); + List sigs = new ArrayList<>(sigMap.keySet()); + Collections.sort(sigs, new Comparator() + { + @Override + public int compare (TimeRational t1, + TimeRational t2) + { + return Integer.compare(sigMap.get(t2), sigMap.get(t1)); + } + }); logger.debug( "Best inferred time sigs in [M#{},M#{}]: {}", startStack.getIdValue(), @@ -184,8 +176,7 @@ public int compare (TimeRational t1, // Loop on every staff in the vertical startStack for (Measure measure : startStack.getMeasures()) { for (Staff staff : measure.getPart().getStaves()) { - int staffIndexInPart = measure.getPart().getStaves().indexOf( - staff); + int staffIndexInPart = measure.getPart().getStaves().indexOf(staff); AbstractTimeInter time = measure.getTimeSignature(staffIndexInPart); if (time != null) { @@ -263,7 +254,7 @@ private Map retrieveBestSigs (MeasureStack startStack, MeasureStack stopStack) { // Retrieve the significant measure informations - final Map sigs = new LinkedHashMap(); + final Map sigs = new LinkedHashMap<>(); MeasureStack stack = startStack; // Loop on stack range diff --git a/src/main/org/audiveris/omr/score/TimeValue.java b/src/main/org/audiveris/omr/score/TimeValue.java index a770dcafe..5992d18e3 100644 --- a/src/main/org/audiveris/omr/score/TimeValue.java +++ b/src/main/org/audiveris/omr/score/TimeValue.java @@ -41,7 +41,6 @@ */ public class TimeValue { - //~ Instance fields ---------------------------------------------------------------------------- /** Specific specificShape if any: COMMON_TIME or CUT_TIME, otherwise null. */ public final Shape specificShape; @@ -49,7 +48,6 @@ public class TimeValue /** Time rational value. (6/8, 3/4, etc) */ public final TimeRational timeRational; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TimeValue} object. * @@ -68,7 +66,6 @@ public TimeValue (Shape specificShape, this.timeRational = timeRational; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // equals // //--------// diff --git a/src/main/org/audiveris/omr/score/ui/BookPdfOutput.java b/src/main/org/audiveris/omr/score/ui/BookPdfOutput.java index eac98ec02..909b950a6 100644 --- a/src/main/org/audiveris/omr/score/ui/BookPdfOutput.java +++ b/src/main/org/audiveris/omr/score/ui/BookPdfOutput.java @@ -30,6 +30,7 @@ import org.audiveris.omr.sheet.Sheet; import org.audiveris.omr.sheet.SheetStub; import org.audiveris.omr.sheet.ui.SheetResultPainter; +import org.audiveris.omr.step.Step; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,18 +51,15 @@ */ public class BookPdfOutput { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(BookPdfOutput.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The related book. */ private final Book book; /** The file to print to. */ private final File file; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SheetPdfOutput object. * @@ -75,7 +73,6 @@ public BookPdfOutput (Book book, this.file = file; } - //~ Methods ------------------------------------------------------------------------------------ /** * Write the PDF output for the provided sheet if any, otherwise for the whole book. * @@ -95,6 +92,12 @@ public void write (Sheet sheet) fos = new FileOutputStream(file); for (SheetStub stub : stubs) { + if (!stub.isDone(Step.GRID)) { + logger.info("{} has not reached GRID step yet, no printout.", stub); + + continue; + } + final int width = stub.getSheet().getWidth(); final int height = stub.getSheet().getHeight(); diff --git a/src/main/org/audiveris/omr/score/ui/BooleanPane.java b/src/main/org/audiveris/omr/score/ui/BooleanPane.java new file mode 100644 index 000000000..a7d127586 --- /dev/null +++ b/src/main/org/audiveris/omr/score/ui/BooleanPane.java @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------------------------// +// // +// B o o l e a n P a n e // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.score.ui; + +import com.jgoodies.forms.builder.PanelBuilder; +import com.jgoodies.forms.layout.CellConstraints; + +import org.audiveris.omr.util.param.Param; + +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.SwingConstants; + +/** + * A data pane for just one boolean. + * + * @author Hervé Bitteur + */ +public class BooleanPane + extends XactDataPane +{ + + /** Boolean box. */ + protected final JCheckBox bbox = new JCheckBox(); + + /** Label for the boolean. */ + protected final JLabel label; + + /** + * Creates a new {@code BooleanPane} object. + * + * @param title pane title string + * @param parent parent pane if any + * @param text data text + * @param tip data description + * @param model underlying data model (cannot be null) + */ + public BooleanPane (String title, + XactDataPane parent, + String text, + String tip, + Param model) + { + super(title, parent, model); + this.label = new JLabel(text, SwingConstants.RIGHT); + + if (tip != null) { + bbox.setToolTipText(tip); + } + } + + @Override + public int defineLayout (PanelBuilder builder, + CellConstraints cst, + int r) + { + r = super.defineLayout(builder, cst, r); + builder.add(label, cst.xyw(3, r, 1)); + builder.add(bbox, cst.xyw(7, r, 1)); + + return r + 2; + } + + @Override + public void setEnabled (boolean bool) + { + bbox.setEnabled(bool); + label.setEnabled(bool); + } + + @Override + protected void display (Boolean content) + { + bbox.setSelected(content); + } + + @Override + protected Boolean read () + { + return bbox.isSelected(); + } +} diff --git a/src/main/org/audiveris/omr/score/ui/EditorMenu.java b/src/main/org/audiveris/omr/score/ui/EditorMenu.java index 6e334db91..1829a8e8f 100644 --- a/src/main/org/audiveris/omr/score/ui/EditorMenu.java +++ b/src/main/org/audiveris/omr/score/ui/EditorMenu.java @@ -67,11 +67,9 @@ public class EditorMenu extends SheetPopupMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(EditorMenu.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Create the editor page menu. * @@ -83,7 +81,6 @@ public EditorMenu (Sheet sheet) defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // defineLayout // //--------------// @@ -134,14 +131,12 @@ private Slot getCurrentSlot (Point point) return null; } - //~ Inner Classes ------------------------------------------------------------------------------ //-------------// // MeasureMenu // //-------------// private class MeasureMenu extends LocationDependentMenu { - //~ Instance fields ------------------------------------------------------------------------ /** Selected measure. */ private MeasureStack stack; @@ -150,8 +145,7 @@ private class MeasureMenu private final MergeAction mergeAction = new MergeAction(); - //~ Constructors --------------------------------------------------------------------------- - public MeasureMenu () + MeasureMenu () { super("Measure"); add(new JMenuItem(new DumpAction())); @@ -159,7 +153,6 @@ public MeasureMenu () add(new JMenuItem(mergeAction)); } - //~ Methods -------------------------------------------------------------------------------- @Override public void updateUserLocation (Rectangle rect) { @@ -181,27 +174,31 @@ public void updateUserLocation (Rectangle rect) mergeAction.update(); } - //~ Inner Classes -------------------------------------------------------------------------- /** * Dump the current measure. */ private class DumpAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- - public DumpAction () + DumpAction () { putValue(NAME, "Dump voices"); putValue(SHORT_DESCRIPTION, "Dump the voices of the selected measure"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { stack.printVoices("\n"); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } /** @@ -210,15 +207,13 @@ public void actionPerformed (ActionEvent e) private class MergeAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- - public MergeAction () + MergeAction () { putValue(NAME, "Merge on right"); putValue(SHORT_DESCRIPTION, "Merge this measure stack with next one on right"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -229,7 +224,7 @@ public void actionPerformed (ActionEvent e) final List measures = stack.getMeasures(); // All the StaffBarline pieces - List toRemove = new ArrayList(); + List toRemove = new ArrayList<>(); for (int ip = 0; ip < parts.size(); ip++) { Measure measure = measures.get(ip); @@ -237,15 +232,24 @@ public void actionPerformed (ActionEvent e) toRemove.addAll(pb.getStaffBarlines()); } - sheet.getInterController() - .removeInters(toRemove, Option.VALIDATED, Option.UPDATE_MEASURES); + sheet.getInterController().removeInters( + toRemove, + Option.VALIDATED, + Option.UPDATE_MEASURES); + } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. } private void update () { setEnabled( - (stack != null) && (stack != stack.getSystem().getLastStack()) - && (sheet.getStub().getLatestStep().compareTo(Step.MEASURES) >= 0)); + (stack != null) && (stack != stack.getSystem().getLastStack()) && (sheet + .getStub().getLatestStep().compareTo(Step.MEASURES) >= 0)); } } @@ -255,21 +259,26 @@ private void update () private class RhythmAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- - public RhythmAction () + RhythmAction () { putValue(NAME, "Reprocess rhythm"); putValue(SHORT_DESCRIPTION, "Reprocess rhythm on the selected measure"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { sheet.getInterController().reprocessRhythm(stack); } + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + private void update () { if (stack == null) { @@ -288,20 +297,17 @@ private void update () private class SlotMenu extends LocationDependentMenu { - //~ Instance fields ------------------------------------------------------------------------ /** Selected slot. */ private Slot slot; - //~ Constructors --------------------------------------------------------------------------- - public SlotMenu () + SlotMenu () { super("Slot"); add(new JMenuItem(new DumpSlotChordsAction())); add(new JMenuItem(new DumpVoicesAction())); } - //~ Methods -------------------------------------------------------------------------------- @Override public void updateUserLocation (Rectangle rect) { @@ -320,27 +326,31 @@ public void updateUserLocation (Rectangle rect) } } - //~ Inner Classes -------------------------------------------------------------------------- /** * Dump the chords of the current slot */ private class DumpSlotChordsAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- - public DumpSlotChordsAction () + DumpSlotChordsAction () { putValue(NAME, "Dump chords"); putValue(SHORT_DESCRIPTION, "Dump the chords of the selected slot"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { logger.info(slot.toChordString()); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } /** @@ -349,20 +359,25 @@ public void actionPerformed (ActionEvent e) private class DumpVoicesAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- - public DumpVoicesAction () + DumpVoicesAction () { putValue(NAME, "Dump voices"); putValue(SHORT_DESCRIPTION, "Dump the voices of the selected slot"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { logger.info(slot.toVoiceString()); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } } @@ -372,22 +387,19 @@ public void actionPerformed (ActionEvent e) private class StaffMenu extends LocationDependentMenu { - //~ Instance fields ------------------------------------------------------------------------ private Staff staff; - //~ Constructors --------------------------------------------------------------------------- /** * Create the staff menu */ - public StaffMenu () + StaffMenu () { super("Staff"); add(new JMenuItem(new PlotAction())); add(new JMenuItem(new PlotHeaderAction())); } - //~ Methods -------------------------------------------------------------------------------- @Override public void updateUserLocation (Rectangle rect) { @@ -400,22 +412,19 @@ public void updateUserLocation (Rectangle rect) } } - //~ Inner Classes -------------------------------------------------------------------------- /** * Plot the x-axis projection of the current staff. */ private class PlotAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- - public PlotAction () + PlotAction () { putValue(NAME, "Staff projection"); putValue(SHORT_DESCRIPTION, "Display staff horizontal projection"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -425,6 +434,13 @@ public void actionPerformed (ActionEvent e) logger.warn("StaffProjector error " + ex, ex); } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } /** @@ -433,15 +449,13 @@ public void actionPerformed (ActionEvent e) private class PlotHeaderAction extends AbstractAction { - //~ Constructors ----------------------------------------------------------------------- - public PlotHeaderAction () + PlotHeaderAction () { putValue(NAME, "Header projection"); putValue(SHORT_DESCRIPTION, "Display staff header horizontal projection"); } - //~ Methods ---------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -454,6 +468,13 @@ public void actionPerformed (ActionEvent e) } } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } } } diff --git a/src/main/org/audiveris/omr/score/ui/IntegerPane.java b/src/main/org/audiveris/omr/score/ui/IntegerPane.java new file mode 100644 index 000000000..ebd46c3b0 --- /dev/null +++ b/src/main/org/audiveris/omr/score/ui/IntegerPane.java @@ -0,0 +1,94 @@ +//------------------------------------------------------------------------------------------------// +// // +// I n t e g e r P a n e // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.score.ui; + +import com.jgoodies.forms.builder.PanelBuilder; +import com.jgoodies.forms.layout.CellConstraints; + +import org.audiveris.omr.ui.field.LIntegerField; +import org.audiveris.omr.util.param.Param; + +/** + * A data pane with just one integer. + * + * @author Hervé Bitteur + */ +public class IntegerPane + extends XactDataPane +{ + + /** Data labeled field. */ + protected final LIntegerField data; + + /** + * Creates a new {@code IntegerPane} object. + * + * @param title pane title string + * @param parent parent pane if any + * @param text data text + * @param tip data description + * @param model underlying data model (cannot be null) + */ + public IntegerPane (String title, + XactDataPane parent, + String text, + String tip, + Param model) + { + super(title, parent, model); + data = new LIntegerField(true, text, tip); + } + + @Override + public int defineLayout (PanelBuilder builder, + CellConstraints cst, + int r) + { + r = super.defineLayout(builder, cst, r); + builder.add(data.getLabel(), cst.xyw(3, r, 1)); + builder.add(data.getField(), cst.xyw(7, r, 1)); + + return r + 2; + } + + @Override + public void setEnabled (boolean bool) + { + data.setEnabled(bool); + } + + @Override + protected void display (Integer val) + { + if (val != null) { + data.setValue(val); + } else { + data.setText(""); + } + } + + @Override + protected Integer read () + { + return data.getValue(); + } +} diff --git a/src/main/org/audiveris/omr/score/ui/ScopedPanel.java b/src/main/org/audiveris/omr/score/ui/ScopedPanel.java new file mode 100644 index 000000000..d4183d31e --- /dev/null +++ b/src/main/org/audiveris/omr/score/ui/ScopedPanel.java @@ -0,0 +1,124 @@ +//------------------------------------------------------------------------------------------------// +// // +// S c o p e d P a n e l // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.score.ui; + +import com.jgoodies.forms.builder.PanelBuilder; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; + +import org.audiveris.omr.ui.util.Panel; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class {@code ScopedPanel} is a panel corresponding to a given scope tab. + * + * @author Hervé Bitteur + */ +public class ScopedPanel + extends Panel +{ + + /** Standard column spec for 4 fields. */ + private static final String colSpec4 = "12dlu,1dlu,100dlu,1dlu,35dlu,1dlu,right:12dlu"; + + /** Collection of individual data panes. */ + private final List panes = new ArrayList<>(); + + /** + * Creates a new {@code ScopedPanel} object. + * + * @param name panel name + * @param panes contained data panes + */ + public ScopedPanel (String name, + List panes) + { + setName(name); + + for (XactDataPane pane : panes) { + if (pane != null) { + this.panes.add(pane); + } + } + + defineLayout(); + + for (XactDataPane pane : this.panes) { + // Pane is pre-selected if model has specific data + final boolean isSpecific = pane.model.isSpecific(); + pane.selBox.setSelected(isSpecific); + // Fill pane data + pane.actionPerformed(null); + } + } + + /** + * Define layout of the pane. + */ + public void defineLayout () + { + // Compute the total number of logical rows + int logicalRowCount = 0; + + for (XactDataPane pane : panes) { + logicalRowCount += pane.getLogicalRowCount(); + } + + FormLayout layout = new FormLayout(colSpec4, Panel.makeRows(logicalRowCount)); + PanelBuilder builder = new PanelBuilder(layout, this); + CellConstraints cst = new CellConstraints(); + int r = 1; + + for (XactDataPane pane : panes) { + r = pane.defineLayout(builder, cst, r); + } + } + + /** + * Report the contained pane of proper class. + * + * @param classe desired class + * @return the pane found or null + */ + public XactDataPane getPane (Class classe) + { + for (XactDataPane pane : panes) { + if (classe.isAssignableFrom(pane.getClass())) { + return pane; + } + } + + return null; + } + + /** + * Report the contained data panes. + * + * @return the sequence of data panes + */ + public List getPanes () + { + return panes; + } +} diff --git a/src/main/org/audiveris/omr/score/ui/ScoreParameters.java b/src/main/org/audiveris/omr/score/ui/ScoreParameters.java index a5f1f8236..ba1d3b89c 100644 --- a/src/main/org/audiveris/omr/score/ui/ScoreParameters.java +++ b/src/main/org/audiveris/omr/score/ui/ScoreParameters.java @@ -23,7 +23,6 @@ import com.jgoodies.forms.builder.PanelBuilder; import com.jgoodies.forms.layout.CellConstraints; -import com.jgoodies.forms.layout.FormLayout; import org.audiveris.omr.image.AdaptiveDescriptor; import org.audiveris.omr.image.FilterDescriptor; @@ -34,21 +33,18 @@ import org.audiveris.omr.sheet.ProcessingSwitches.Switch; import org.audiveris.omr.sheet.SheetStub; import org.audiveris.omr.text.Language; -import org.audiveris.omr.text.OCR.UnavailableOcrException; +import org.audiveris.omr.text.OcrUtil; import org.audiveris.omr.ui.field.SpinnerUtil; -import org.audiveris.omr.ui.util.Panel; import org.audiveris.omr.util.param.Param; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.text.ParseException; import java.util.ArrayList; import java.util.List; -import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; @@ -66,38 +62,40 @@ /** * Class {@code ScoreParameters} is a dialog that allows the user to easily manage the * most frequent parameters. - *

                            - * Score parameters dialog - *
                            - * *

                            * It addresses: *

                              - *
                            • Text language specification
                            • *
                            • Binarization parameters
                            • - * + *
                            • Text language specification
                            • + *
                            • Support for specific features
                            • + * *
                            - * + *
                            + * Score parameters dialog + *
                            *

                            * The dialog is organized as a scope-based tabbed pane with: *

                              - *
                            • a panel for the default scope,
                            • - *
                            • a panel for current book scope (provided that there is a selected book),
                            • - *
                            • and one panel for every sheet scope (provided that the book contains more than a + *
                            • A panel for the default scope,
                            • + *
                            • A panel for current book scope (provided that there is a selected book),
                            • + *
                            • And one panel for every sheet scope (provided that the book contains more than a * single sheet).
                            • *
                            - * - *

                            - * A panel is a vertical collection of panes, each pane being introduced by a check box and a label. - * With no specific information, the box is unchecked, the pane content is disabled. - * With specific information, the box is checked and the pane content is enabled. - *
                            Manually checking the box represents a selection and indicates the intention to modify the + * Panel interface: + *

                              + *
                            • A panel is a vertical collection of panes, each pane being introduced by a check box + * and a label. + *
                            • With no specific information, the box is unchecked, the pane content is disabled. + *
                            • With specific information, the box is checked and the pane content is enabled. + *
                            • + * Manually checking the box represents a selection and indicates the intention to modify the * pane content (and thus enables the pane fields). - *
                              Un-checking the box reverts the content to the value it had prior to the selection. - * + *
                            • + * Un-checking the box reverts the content to the value it had prior to the selection. + *
                            *

                            - * The selected modifications are performed only when the user presses the OK button. - * + * NOTA: The selected modifications are actually applied only when the user presses the OK button. *

                            * Score parameters dialog * @@ -106,14 +104,9 @@ public class ScoreParameters implements ChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ScoreParameters.class); - /** Standard column spec for 4 fields. */ - private static final String colSpec4 = "12dlu,1dlu,100dlu,1dlu,35dlu,1dlu,right:12dlu"; - - //~ Instance fields ---------------------------------------------------------------------------- /** The swing component of this panel. */ private final JTabbedPane component = new JTabbedPane(); @@ -121,9 +114,8 @@ public class ScoreParameters private final Book book; /** The panel dedicated to setting of defaults. */ - private final TabPanel defaultPanel; + private final ScopedPanel defaultPanel; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a ScoreParameters object. * @@ -140,13 +132,18 @@ public ScoreParameters (SheetStub stub) component.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); // Allocate all required panels (default / book? / sheets??) - final TabPanel bookPanel; - TabPanel sheetPanel = null; // Only for multi-sheet book + final ScopedPanel bookPanel; + ScopedPanel sheetPanel = null; // Only for multi-sheet book // Default panel - List defaultPanes = new ArrayList(); + List defaultPanes = new ArrayList<>(); defaultPanes.add(new FilterPane(null, FilterDescriptor.defaultFilter)); - defaultPanes.add(createTextPane(null, Language.ocrDefaultLanguages)); + + TextPane defaultTextPane = createTextPane(null, Language.ocrDefaultLanguages); + + if (defaultTextPane != null) { + defaultPanes.add(defaultTextPane); + } ProcessingSwitches defaultSwitches = ProcessingSwitches.getDefaultSwitches(); @@ -155,48 +152,56 @@ public ScoreParameters (SheetStub stub) defaultPanes.add(switchPane); } - defaultPanel = new TabPanel("Default settings", defaultPanes); + defaultPanel = new ScopedPanel("Default settings", defaultPanes); component.addTab("Default", null, defaultPanel, defaultPanel.getName()); // Book panel? if (book != null) { - List bookPanes = new ArrayList(); + List bookPanes = new ArrayList<>(); bookPanes.add( new FilterPane( (FilterPane) defaultPanel.getPane(FilterPane.class), book.getBinarizationFilter())); - bookPanes.add( - createTextPane( - (TextPane) defaultPanel.getPane(TextPane.class), - book.getOcrLanguages())); + + TextPane bookTextPane = createTextPane( + (TextPane) defaultPanel.getPane(TextPane.class), + book.getOcrLanguages()); + + if (bookTextPane != null) { + bookPanes.add(bookTextPane); + } for (Switch key : Switch.values()) { Param bp = book.getProcessingSwitches().getParam(key); - bookPanes.add(new SwitchPane(key, defaultPanel.getSwitchPane(key), bp)); + bookPanes.add(new SwitchPane(key, getPane(defaultPanel, key), bp)); } - bookPanel = new TabPanel("Book settings", bookPanes); + bookPanel = new ScopedPanel("Book settings", bookPanes); component.addTab(book.getRadix(), null, bookPanel, bookPanel.getName()); // Sheets panels? if (book.isMultiSheet()) { for (SheetStub s : book.getStubs()) { - List sheetPanes = new ArrayList(); + List sheetPanes = new ArrayList<>(); sheetPanes.add( new FilterPane( (FilterPane) bookPanel.getPane(FilterPane.class), s.getBinarizationFilter())); - sheetPanes.add( - createTextPane( - (TextPane) bookPanel.getPane(TextPane.class), - s.getOcrLanguages())); + + TextPane sheetTextPane = createTextPane( + (TextPane) bookPanel.getPane(TextPane.class), + s.getOcrLanguages()); + + if (sheetTextPane != null) { + sheetPanes.add(sheetTextPane); + } for (Switch key : Switch.values()) { Param bp = s.getProcessingSwitches().getParam(key); - sheetPanes.add(new SwitchPane(key, bookPanel.getSwitchPane(key), bp)); + sheetPanes.add(new SwitchPane(key, getPane(bookPanel, key), bp)); } - TabPanel panel = new TabPanel("Sheet settings", sheetPanes); + ScopedPanel panel = new ScopedPanel("Sheet settings", sheetPanes); String label = "S#" + s.getNumber(); if (s == stub) { @@ -214,10 +219,10 @@ public ScoreParameters (SheetStub stub) // Initially selected tab component.addChangeListener(this); component.setSelectedComponent( - (sheetPanel != null) ? sheetPanel : ((bookPanel != null) ? bookPanel : defaultPanel)); + (sheetPanel != null) ? sheetPanel + : ((bookPanel != null) ? bookPanel : defaultPanel)); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // commit // //--------// @@ -232,10 +237,10 @@ public boolean commit (SheetStub stub) try { // Commit all specific values, if any, to their model object for (int t = 0, tBreak = component.getTabCount(); t < tBreak; t++) { - final TabPanel panel = (TabPanel) component.getComponentAt(t); + final ScopedPanel panel = (ScopedPanel) component.getComponentAt(t); boolean modified = false; - for (Pane pane : panel.panes) { + for (XactDataPane pane : panel.getPanes()) { modified |= pane.commit(); } @@ -278,14 +283,14 @@ public JTabbedPane getComponent () public void stateChanged (ChangeEvent e) { // Refresh the new current panel - TabPanel panel = (TabPanel) component.getSelectedComponent(); + ScopedPanel panel = (ScopedPanel) component.getSelectedComponent(); PaneLoop: - for (Pane pane : panel.panes) { + for (XactDataPane pane : panel.getPanes()) { if (!pane.isSelected()) { // Use the first parent with any specific value - Pane highestPane = pane; - Pane p = pane.parent; + XactDataPane highestPane = pane; + XactDataPane p = pane.parent; while (p != null) { if (p.isSelected()) { @@ -318,242 +323,44 @@ public void stateChanged (ChangeEvent e) private TextPane createTextPane (TextPane parent, Param model) { - // Caution: The language pane needs Tesseract up & running - try { - return new TextPane(parent, model); - } catch (UnavailableOcrException ex) { - logger.info("No language pane for lack of OCR"); - } catch (Throwable ex) { - logger.warn("Error creating language pane", ex); - } - - return null; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-------------// - // BooleanPane // - //-------------// - /** - * A template for pane with just one boolean. - * Scope can be: default, book, sheet. - */ - private abstract static class BooleanPane - extends Pane - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Boolean box. */ - protected final JCheckBox bbox = new JCheckBox(); - - protected final JLabel label; - - //~ Constructors --------------------------------------------------------------------------- - public BooleanPane (String title, - Pane parent, - String text, - String tip, - Param model) - { - super(title, parent, model); - - this.label = new JLabel(text, SwingConstants.RIGHT); - - if (tip != null) { - bbox.setToolTipText(tip); + // The language pane needs Tesseract up & running + if (OcrUtil.getOcr().isAvailable()) { + try { + return new TextPane(parent, model); + } catch (Throwable ex) { + logger.warn("Error creating language pane", ex); } + } else { + logger.info("No language pane for lack of OCR."); } - //~ Methods -------------------------------------------------------------------------------- - @Override - public int defineLayout (PanelBuilder builder, - CellConstraints cst, - int r) - { - r = super.defineLayout(builder, cst, r); - - builder.add(label, cst.xyw(3, r, 1)); - builder.add(bbox, cst.xyw(7, r, 1)); - - return r + 2; - } - - @Override - public void setEnabled (boolean bool) - { - bbox.setEnabled(bool); - label.setEnabled(bool); - } - - @Override - protected void display (Boolean content) - { - bbox.setSelected(content); - } - - @Override - protected Boolean read () - { - return bbox.isSelected(); - } + return null; } - //------// - // Pane // - //------// + //---------// + // getPane // + //---------// /** - * A pane is able to host data, check data validity and apply the requested - * modifications. + * Retrieve a SwitchPane knowing its key. + * + * @param panel containing panel + * @param key desired key + * @return corresponding pane */ - private abstract static class Pane - implements ActionListener + private static SwitchPane getPane (ScopedPanel panel, + Switch key) { - //~ Instance fields ------------------------------------------------------------------------ - - /** Model parameter (cannot be null). */ - protected final Param model; - - /** Parent pane, if any. */ - protected final Pane parent; - - /** Box for selecting specific vs inherited data. */ - protected final JCheckBox selBox; - - /** Title for the pane. */ - protected final String title; - - /** Separator. */ - protected final JLabel separator; - - //~ Constructors --------------------------------------------------------------------------- - public Pane (String title, - Pane parent, - Param model) - { - this.parent = parent; - - if (model == null) { - throw new IllegalArgumentException("Null model for pane '" + title + "'"); - } - - this.model = model; - this.title = title; - - separator = new JLabel(title); - separator.setHorizontalAlignment(SwingConstants.LEFT); - separator.setEnabled(false); - - selBox = new JCheckBox(); - - selBox.addActionListener(this); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void actionPerformed (ActionEvent e) - { - // Pane (de)selection (programmatic or manual) - boolean sel = isSelected(); - setEnabled(sel); - separator.setEnabled(sel); - - final E value; - - if (e == null) { - value = model.getValue(); - } else if (!sel) { - value = (parent != null) ? parent.read() : model.getSourceValue(); - } else { - return; - } - - display(value); - } + for (XactDataPane pane : panel.getPanes()) { + if (pane instanceof SwitchPane) { + SwitchPane switchPane = (SwitchPane) pane; - /** - * Commit the modifications, for the items that are not handled by the - * ParametersTask, which means all actions related to default values. - */ - public boolean commit () - { - if (isSelected()) { - return model.setSpecific(read()); - } else { - return model.setSpecific(null); + if (switchPane.getKey() == key) { + return switchPane; + } } } - /** - * Build the related user interface - * - * @param builder the shared panel builder - * @param cst the cell constraints - * @param r initial row value - * @return final row value - */ - public int defineLayout (PanelBuilder builder, - CellConstraints cst, - int r) - { - // Draw the specific/inherit box + separating line - builder.add(selBox, cst.xyw(1, r, 1)); - - ///builder.addSeparator(title, cst.xyw(3, r, 5)); - builder.add(separator, cst.xyw(3, r, 5)); - r += 2; - - return r; - } - - /** - * Report the count of needed logical rows. - * Typically 2 (the label separator plus 1 line of data) - */ - public int getLogicalRowCount () - { - return 2; - } - - /** - * User has selected (and enabled) this pane - * - * @return true if selected - */ - public boolean isSelected () - { - return selBox.isSelected(); - } - - /** - * User selects (or deselects) this pane - * - * @param bool true for selection - */ - public void setSelected (boolean bool) - { - selBox.setSelected(bool); - } - - /** - * Write the parameter into the fields content - * - * @param content the data to display - */ - protected abstract void display (E content); - - /** - * Read the parameter as defined by the fields content. - * - * @return the pane parameter - */ - protected abstract E read (); - - /** - * Set the enabled flag for all data fields - * - * @param bool the flag value - */ - protected abstract void setEnabled (boolean bool); + return null; } //------------// @@ -561,16 +368,14 @@ public void setSelected (boolean bool) //------------// /** * Pane to define the pixel binarization parameters. - * Scope can be: default, score, page. + * Scope can be: default, book, sheet. */ private static class FilterPane - extends Pane + extends XactDataPane { - //~ Instance fields ------------------------------------------------------------------------ /** ComboBox for filter kind */ - private final JComboBox kindCombo = new JComboBox( - FilterKind.values()); + private final JComboBox kindCombo = new JComboBox<>(FilterKind.values()); private final JLabel kindLabel = new JLabel("Filter", SwingConstants.RIGHT); @@ -591,9 +396,8 @@ private static class FilterPane "Coefficient for standard deviation value", new SpinnerNumberModel(0.2, 0.2, 1.5, 0.1)); - //~ Constructors --------------------------------------------------------------------------- - public FilterPane (FilterPane parent, - Param model) + FilterPane (FilterPane parent, + Param model) { super("Binarization", parent, model); @@ -602,14 +406,12 @@ public FilterPane (FilterPane parent, kindCombo.addActionListener(this); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { if ((e != null) && (e.getSource() == kindCombo)) { - FilterDescriptor desc = (readKind() == FilterKind.GLOBAL) - ? GlobalDescriptor.getDefault() - : AdaptiveDescriptor.getDefault(); + FilterDescriptor desc = (readKind() == FilterKind.GLOBAL) ? GlobalDescriptor + .getDefault() : AdaptiveDescriptor.getDefault(); display(desc); } else { super.actionPerformed(e); @@ -689,8 +491,8 @@ protected FilterDescriptor read () { commitSpinners(); - return (readKind() == FilterKind.GLOBAL) - ? new GlobalDescriptor((int) globalData.spinner.getValue()) + return (readKind() == FilterKind.GLOBAL) ? new GlobalDescriptor( + (int) globalData.spinner.getValue()) : new AdaptiveDescriptor( (double) localDataMean.spinner.getValue(), (double) localDataDev.spinner.getValue()); @@ -742,16 +544,14 @@ private FilterKind readKind () */ private static class SpinData { - //~ Instance fields ------------------------------------------------------------------------ protected final JLabel label; protected final JSpinner spinner; - //~ Constructors --------------------------------------------------------------------------- - public SpinData (String label, - String tip, - SpinnerModel model) + SpinData (String label, + String tip, + SpinnerModel model) { this.label = new JLabel(label, SwingConstants.RIGHT); @@ -761,7 +561,6 @@ public SpinData (String label, spinner.setToolTipText(tip); } - //~ Methods -------------------------------------------------------------------------------- public int defineLayout (PanelBuilder builder, CellConstraints cst, int r) @@ -790,23 +589,24 @@ public void setVisible (boolean bool) //------------// // SwitchPane // //------------// + /** + * A pane for one boolean switch. + * Scope can be: default, book, sheet. + */ private static class SwitchPane extends BooleanPane { - //~ Instance fields ------------------------------------------------------------------------ - private final Switch key; + final Switch key; - //~ Constructors --------------------------------------------------------------------------- - public SwitchPane (Switch key, - Pane parent, - Param model) + SwitchPane (Switch key, + XactDataPane parent, + Param model) { super(key.getConstant().getDescription(), parent, "", null, model); this.key = key; } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -842,107 +642,10 @@ public int getLogicalRowCount () } } - //----------// - // TabPanel // - //----------// - /** - * A panel corresponding to a tab. - */ - private static final class TabPanel - extends Panel - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Collection of individual data panes. */ - private final List panes = new ArrayList(); - - //~ Constructors --------------------------------------------------------------------------- - public TabPanel (String name, - List panes) - { - setName(name); - - for (Pane pane : panes) { - if (pane != null) { - this.panes.add(pane); - } - } - - defineLayout(); - - for (Pane pane : this.panes) { - // Pane is pre-selected if model has specific data - final boolean isSpecific = pane.model.isSpecific(); - pane.selBox.setSelected(isSpecific); - - // Fill pane data - pane.actionPerformed(null); - } - } - - //~ Methods -------------------------------------------------------------------------------- - public void defineLayout () - { - // Compute the total number of logical rows - int logicalRowCount = 0; - - for (Pane pane : panes) { - logicalRowCount += pane.getLogicalRowCount(); - } - - FormLayout layout = new FormLayout(colSpec4, Panel.makeRows(logicalRowCount)); - PanelBuilder builder = new PanelBuilder(layout, this); - - CellConstraints cst = new CellConstraints(); - int r = 1; - - for (Pane pane : panes) { - r = pane.defineLayout(builder, cst, r); - } - } - - public Pane getPane (Class classe) - { - for (Pane pane : panes) { - if (classe.isAssignableFrom(pane.getClass())) { - return pane; - } - } - - return null; - } - - public SwitchPane getSwitchPane (Switch key) - { - for (Pane pane : panes) { - if (pane instanceof SwitchPane) { - SwitchPane switchPane = (SwitchPane) pane; - - if (switchPane.getKey() == key) { - return switchPane; - } - } - } - - return null; - } - } - - //----------// - // TextPane // - //----------// - /** - * Pane to set the dominant text language specification. - * Scope can be: default, book, sheet. - */ private static class TextPane - extends Pane + extends XactDataPane implements ListSelectionListener { - //~ Instance fields ------------------------------------------------------------------------ - - /** Underlying language list model. */ - final Language.ListModel listModel; /** List for choosing elements of language specification. */ private final JList langList; @@ -953,15 +656,17 @@ private static class TextPane /** Resulting visible specification. */ private final JLabel langSpec = new JLabel("", SwingConstants.RIGHT); - //~ Constructors --------------------------------------------------------------------------- - public TextPane (TextPane parent, - Param model) + /** Underlying language list model. */ + final Language.ListModel listModel; + + TextPane (TextPane parent, + Param model) { super("OCR language(s)", parent, model); listModel = new Language.ListModel(); - langList = new JList(listModel); + langList = new JList<>(listModel); langList.setLayoutOrientation(JList.VERTICAL); langList.setToolTipText("Dominant languages for textual items"); langList.setVisibleRowCount(5); @@ -972,7 +677,6 @@ public TextPane (TextPane parent, langSpec.setToolTipText("Resulting specification"); } - //~ Methods -------------------------------------------------------------------------------- @Override public int defineLayout (PanelBuilder builder, CellConstraints cst, @@ -1120,7 +824,7 @@ protected void setEnabled (boolean bool) // //~ Instance fields ------------------------------------------------------------------------ // // /** All score part panes */ -// private final List partPanels = new ArrayList(); +// private final List partPanels = new ArrayList<>(); // // //~ Constructors --------------------------------------------------------------------------- // public PartsPane (Score score) @@ -1179,7 +883,7 @@ protected void setEnabled (boolean bool) // @Override // protected List read () // { -// List data = new ArrayList(); +// List data = new ArrayList<>(); // // for (PartPanel partPanel : partPanels) { // data.add(partPanel.getData()); diff --git a/src/main/org/audiveris/omr/score/ui/SheetParameters.java b/src/main/org/audiveris/omr/score/ui/SheetParameters.java new file mode 100644 index 000000000..0ba265391 --- /dev/null +++ b/src/main/org/audiveris/omr/score/ui/SheetParameters.java @@ -0,0 +1,270 @@ +//------------------------------------------------------------------------------------------------// +// // +// S h e e t P a r a m e t e r s // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.score.ui; + +import com.jgoodies.forms.builder.PanelBuilder; +import com.jgoodies.forms.layout.CellConstraints; + +import org.audiveris.omr.sheet.Scale; +import org.audiveris.omr.sheet.Scale.Item; +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.util.param.Param; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; + +/** + * Class {@code SheetParameters} is a dialog that allows the user to manage sheet + * parameters (such as scaling data). + * + * @author Hervé Bitteur + */ +public class SheetParameters +{ + + private static final Logger logger = LoggerFactory.getLogger(SheetParameters.class); + + /** The swing component of this panel. */ + private final ScopedPanel scopedPanel; + + /** The related sheet. */ + private final Sheet sheet; + + /** Underlying scale structure. */ + private Scale scale; + + /** Map of scaling parameters. */ + private final EnumMap scalings = new EnumMap<>(Item.class); + + /** + * Creates a new {@code SheetParameters} object. + * + * @param sheet the underlying sheet + */ + public SheetParameters (Sheet sheet) + { + this.sheet = sheet; + scale = sheet.getScale(); + + // Populate scalings with current scale values + populateScalings(); + + final List sheetPanes = new ArrayList<>(); + + for (Item key : Item.values()) { + ScalingParam ip = scalings.get(key); + + if (ip == null) { + ip = new ScalingParam(key); + } + + sheetPanes.add(new ScalingPane(key, null, ip)); + } + + scopedPanel = new ScopedPanel("Sheet settings", sheetPanes); + + initialDisplay(); + } + + //--------// + // commit // + //--------// + /** + * Commit the user actions. + * + * @return true if committed, false otherwise + */ + public boolean commit () + { + try { + // Commit all specific values, if any, to their model object + boolean modified = false; + + for (XactDataPane pane : scopedPanel.getPanes()) { + modified |= pane.commit(); + } + + // Book/Sheet modifications + if (modified) { + sheet.getStub().setModified(true); + } + } catch (Exception ex) { + logger.warn("Could not commit sheet parameters", ex); + + return false; + } + + return true; + } + + //--------------// + // getComponent // + //--------------// + /** + * Report the UI component. + * + * @return the concrete component + */ + public ScopedPanel getComponent () + { + return scopedPanel; + } + + //----------------// + // initialDisplay // + //----------------// + private void initialDisplay () + { + for (XactDataPane pane : scopedPanel.getPanes()) { + pane.actionPerformed(null); + } + } + + //------------------// + // populateScalings // + //------------------// + private void populateScalings () + { + for (Item key : Item.values()) { + ScalingParam ip = new ScalingParam(key); + scalings.put(key, ip); + } + } + + //--------------// + // ScalingParam // + //--------------// + /** + * An integer {@code Param}, backed by Scale structure. + */ + private class ScalingParam + extends Param + { + + public final Item key; + + ScalingParam (Item key) + { + this.key = key; + } + + @Override + public Integer getSourceValue () + { + if (scale == null) { + return null; + } + + return scale.getItemValue(key); + } + + @Override + public Integer getValue () + { + if (isSpecific()) { + return getSpecific(); + } + + return getSourceValue(); + } + + @Override + public boolean setSpecific (Integer specific) + { + final Integer value = getValue(); + this.specific = specific; + + if ((specific != null) && !specific.equals(value)) { + if (scale == null) { + sheet.setScale(scale = new Scale()); + } + + scale.setItemValue(key, specific); + logger.info(key.getDescription() + " set to {}", specific); + + return true; + } + + return false; + } + } + + //-------------// + // ScalingPane // + //-------------// + /** + * Pane to define a scaling parameter (sheet scope only). + */ + private static class ScalingPane + extends IntegerPane + { + + final Scale.Item key; + + ScalingPane (Scale.Item key, + XactDataPane parent, + ScalingParam model) + { + super(key.getDescription(), parent, "", null, model); + this.key = key; + } + + @Override + public void actionPerformed (ActionEvent e) + { + if ((e != null) && (e.getSource() == data.getField())) { + display(read()); + } else { + super.actionPerformed(e); + } + } + + @Override + public int defineLayout (PanelBuilder builder, + CellConstraints cst, + int r) + { + // Draw the specific/inherit box + builder.add(selBox, cst.xyw(1, r, 1)); + builder.add(separator, cst.xyw(3, r, 3)); + builder.add(data.getField(), cst.xyw(7, r, 1)); + + return r + 2; + } + + public Scale.Item getKey () + { + return key; + } + + @Override + public int getLogicalRowCount () + { + return 1; + } + } +} diff --git a/src/main/org/audiveris/omr/score/ui/SheetPopupMenu.java b/src/main/org/audiveris/omr/score/ui/SheetPopupMenu.java index 91627a19e..2fff83e2e 100644 --- a/src/main/org/audiveris/omr/score/ui/SheetPopupMenu.java +++ b/src/main/org/audiveris/omr/score/ui/SheetPopupMenu.java @@ -40,18 +40,15 @@ */ public class SheetPopupMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SheetPopupMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The related sheet. */ protected final Sheet sheet; /** Concrete pop-up menu. */ protected final JPopupMenu popup = new JPopupMenu(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SheetPopupMenu} object. * @@ -62,7 +59,6 @@ public SheetPopupMenu (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // addMenu // //---------// diff --git a/src/main/org/audiveris/omr/score/ui/XactDataPane.java b/src/main/org/audiveris/omr/score/ui/XactDataPane.java new file mode 100644 index 000000000..f8e60920b --- /dev/null +++ b/src/main/org/audiveris/omr/score/ui/XactDataPane.java @@ -0,0 +1,195 @@ +//------------------------------------------------------------------------------------------------// +// // +// X a c t D a t a P a n e // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.score.ui; + +import com.jgoodies.forms.builder.PanelBuilder; +import com.jgoodies.forms.layout.CellConstraints; + +import org.audiveris.omr.util.param.Param; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.SwingConstants; + +/** + * A transactional data pane is able to host data, check data validity and apply + * the requested modifications on commit. + * + * @param specific data type + * @author Hervé Bitteur + */ +public abstract class XactDataPane + implements ActionListener +{ + + /** Model parameter (cannot be null). */ + protected final Param model; + + /** Parent pane, if any. */ + protected final XactDataPane parent; + + /** Box for selecting specific vs inherited data. */ + protected final JCheckBox selBox; + + /** Title for the pane. */ + protected final String title; + + /** Separator. */ + protected final JLabel separator; + + /** + * Creates a new {@code XactDataPane} object. + * + * @param title pane title + * @param parent parent pane if any + * @param model underlying model (cannot be null) + */ + public XactDataPane (String title, + XactDataPane parent, + Param model) + { + this.parent = parent; + + if (model == null) { + throw new IllegalArgumentException("Null model for pane '" + title + "'"); + } + + this.model = model; + this.title = title; + separator = new JLabel(title); + separator.setHorizontalAlignment(SwingConstants.LEFT); + separator.setEnabled(false); + selBox = new JCheckBox(); + selBox.addActionListener(this); + } + + @Override + public void actionPerformed (ActionEvent e) + { + // Pane (de)selection (programmatic or manual) + final boolean sel = isSelected(); + setEnabled(sel); + separator.setEnabled(sel); + + final E value; + + if (e == null) { + value = model.getValue(); + } else if (!sel) { + value = (parent != null) ? parent.read() : model.getSourceValue(); + } else { + return; + } + + display(value); + } + + /** + * Commit the modifications. + * + * @return true if underlying model has been modified + */ + public boolean commit () + { + if (isSelected()) { + return model.setSpecific(read()); + } else { + return model.setSpecific(null); + } + } + + /** + * Build the related user interface + * + * @param builder the shared panel builder + * @param cst the cell constraints + * @param r initial row value + * @return final row value + */ + public int defineLayout (PanelBuilder builder, + CellConstraints cst, + int r) + { + // Draw the specific/inherit box + separating line + builder.add(selBox, cst.xyw(1, r, 1)); + ///builder.addSeparator(title, cst.xyw(3, r, 5)); + builder.add(separator, cst.xyw(3, r, 5)); + r += 2; + + return r; + } + + /** + * Report the count of needed logical rows. + * Typically 2 (the label separator plus 1 line of data) + * + * @return count of layout logical rows + */ + public int getLogicalRowCount () + { + return 2; + } + + /** + * User has selected (and enabled) this pane + * + * @return true if selected + */ + public boolean isSelected () + { + return selBox.isSelected(); + } + + /** + * User selects (or deselects) this pane + * + * @param bool true for selection + */ + public void setSelected (boolean bool) + { + selBox.setSelected(bool); + } + + /** + * Write the parameter into the fields content + * + * @param content the data to display + */ + protected abstract void display (E content); + + /** + * Read the parameter as defined by the fields content. + * + * @return the pane parameter + */ + protected abstract E read (); + + /** + * Set the enabled flag for all data fields + * + * @param bool the flag value + */ + protected abstract void setEnabled (boolean bool); +} diff --git a/src/main/org/audiveris/omr/sheet/AliasPatterns.java b/src/main/org/audiveris/omr/sheet/AliasPatterns.java index f2e4b4d63..f0b37af95 100644 --- a/src/main/org/audiveris/omr/sheet/AliasPatterns.java +++ b/src/main/org/audiveris/omr/sheet/AliasPatterns.java @@ -56,7 +56,6 @@ @XmlRootElement(name = "alias-patterns") public class AliasPatterns { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -64,10 +63,8 @@ public class AliasPatterns private static final String ALIAS_PATTERNS_FILENAME = "alias-patterns.xml"; - //~ Instance fields ---------------------------------------------------------------------------- private final List patterns = loadAliasPatterns(); - //~ Methods ------------------------------------------------------------------------------------ //----------// // getAlias // //----------// @@ -94,14 +91,6 @@ public String getAlias (String name) return null; } - //------------------// - // useAliasPatterns // - //------------------// - public static boolean useAliasPatterns () - { - return constants.useAliasPatterns.isSet(); - } - //-------------------// // loadAliasPatterns // //-------------------// @@ -114,14 +103,12 @@ public static boolean useAliasPatterns () */ private List loadAliasPatterns () { - final List patternList = new ArrayList(); + final List patternList = new ArrayList<>(); if (useAliasPatterns()) { URI[] uris = new URI[]{ - WellKnowns.CONFIG_FOLDER.resolve(ALIAS_PATTERNS_FILENAME).toUri() - .normalize(), - UriUtil.toURI(WellKnowns.RES_URI, ALIAS_PATTERNS_FILENAME) - }; + WellKnowns.CONFIG_FOLDER.resolve(ALIAS_PATTERNS_FILENAME).toUri().normalize(), + UriUtil.toURI(WellKnowns.RES_URI, ALIAS_PATTERNS_FILENAME)}; for (int i = 0; i < uris.length; i++) { URI uri = uris[i]; @@ -130,20 +117,19 @@ private List loadAliasPatterns () URL url = uri.toURL(); // Retrieve the raw strings - JAXBContext jaxbContext = JAXBContext.newInstance(Strings.class); - InputStream input = url.openStream(); - Unmarshaller um = jaxbContext.createUnmarshaller(); - Strings strings = (Strings) um.unmarshal(input); - input.close(); - - List stringList = strings.list; - - // Compile strings into patterns - if (!stringList.isEmpty()) { - logger.info("Alias patterns: {}", stringList); - - for (String raw : stringList) { - patternList.add(Pattern.compile(raw)); + try (InputStream input = url.openStream()) { + JAXBContext jaxbContext = JAXBContext.newInstance(Strings.class); + Unmarshaller um = jaxbContext.createUnmarshaller(); + Strings strings = (Strings) um.unmarshal(input); + List stringList = strings.list; + + // Compile strings into patterns + if (!stringList.isEmpty()) { + logger.info("Alias patterns: {}", stringList); + + for (String raw : stringList) { + patternList.add(Pattern.compile(raw)); + } } } } catch (IOException ex) { @@ -160,14 +146,25 @@ private List loadAliasPatterns () return patternList; } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------------// + // useAliasPatterns // + //------------------// + /** + * Tell whether we should use patterns. + * + * @return true if so + */ + public static boolean useAliasPatterns () + { + return constants.useAliasPatterns.isSet(); + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean useAliasPatterns = new Constant.Boolean( true, @@ -181,13 +178,11 @@ private static final class Constants @XmlRootElement(name = "alias-patterns") private static class Strings { - //~ Instance fields ------------------------------------------------------------------------ /** List of patterns on input names. */ @XmlElement(name = "pattern") - private List list = new ArrayList(); + private List list = new ArrayList<>(); - //~ Constructors --------------------------------------------------------------------------- /** No-arg constructor meant for JAXB. */ private Strings () { diff --git a/src/main/org/audiveris/omr/sheet/BasicBook.java b/src/main/org/audiveris/omr/sheet/BasicBook.java deleted file mode 100644 index 2a0e7fadf..000000000 --- a/src/main/org/audiveris/omr/sheet/BasicBook.java +++ /dev/null @@ -1,2208 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// B a s i c B o o k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet; - -import org.audiveris.omr.OMR; -import org.audiveris.omr.ProgramId; -import org.audiveris.omr.WellKnowns; -import static org.audiveris.omr.classifier.Annotations.BOOK_ANNOTATIONS_SUFFIX; -import org.audiveris.omr.classifier.SampleRepository; -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.image.FilterDescriptor; -import org.audiveris.omr.image.FilterParam; -import org.audiveris.omr.image.ImageLoading; -import org.audiveris.omr.log.LogUtil; -import org.audiveris.omr.run.RunTable; -import org.audiveris.omr.score.OpusExporter; -import org.audiveris.omr.score.Page; -import org.audiveris.omr.score.PageRef; -import org.audiveris.omr.score.Score; -import org.audiveris.omr.score.ScoreExporter; -import org.audiveris.omr.score.ScoreReduction; -import org.audiveris.omr.score.ui.BookPdfOutput; -import static org.audiveris.omr.sheet.Sheet.INTERNALS_RADIX; -import org.audiveris.omr.sheet.rhythm.Voices; -import org.audiveris.omr.sheet.ui.BookBrowser; -import org.audiveris.omr.sheet.ui.StubsController; -import org.audiveris.omr.step.ProcessingCancellationException; -import org.audiveris.omr.step.Step; -import org.audiveris.omr.step.StepException; -import org.audiveris.omr.step.ui.StepMonitoring; -import org.audiveris.omr.text.Language; -import org.audiveris.omr.util.FileUtil; -import org.audiveris.omr.util.Jaxb; -import org.audiveris.omr.util.Memory; -import org.audiveris.omr.util.OmrExecutors; -import org.audiveris.omr.util.StopWatch; -import org.audiveris.omr.util.ZipFileSystem; -import org.audiveris.omr.util.param.Param; -import org.audiveris.omr.util.param.StringParam; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.image.BufferedImage; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.zip.ZipOutputStream; - -import javax.swing.JFrame; -import javax.swing.SwingUtilities; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.adapters.XmlAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - -/** - * Class {@code BasicBook} is a basic implementation of Book interface. - * - * @author Hervé Bitteur - */ -@XmlAccessorType(XmlAccessType.NONE) -@XmlRootElement(name = "book") -public class BasicBook - implements Book -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger( - Book.class); - - /** Un/marshalling context for use with JAXB. */ - private static volatile JAXBContext jaxbContext; - - //~ Instance fields ---------------------------------------------------------------------------- - // - // Persistent data - //---------------- - // - /** Related Audiveris version that last operated on this book. */ - @XmlAttribute(name = "software-version") - private String version; - - /** Related Audiveris build that last operated on this book. */ - @XmlAttribute(name = "software-build") - private String build; - - /** Sub books, if any. */ - @XmlElement(name = "sub-books") - private final List subBooks; - - /** Book alias, if any. */ - @XmlAttribute(name = "alias") - private String alias; - - /** Input path of the related image(s) file, if any. */ - @XmlAttribute(name = "path") - private final Path path; - - /** Sheet offset of image file with respect to full work, if any. */ - @XmlAttribute(name = "offset") - private Integer offset; - - /** Indicate if the book scores must be updated. */ - @XmlAttribute(name = "dirty") - @XmlJavaTypeAdapter(type = boolean.class, value = Jaxb.BooleanPositiveAdapter.class) - private boolean dirty = false; - - /** Handling of binarization filter parameter. */ - @XmlElement(name = "binarization") - @XmlJavaTypeAdapter(FilterParam.Adapter.class) - private FilterParam binarizationFilter; - - /** Handling of dominant language(s) for this book. */ - @XmlElement(name = "ocr-languages") - @XmlJavaTypeAdapter(StringParam.Adapter.class) - private StringParam ocrLanguages; - - /** Handling of processing switches for this book. */ - @XmlElement(name = "processing") - @XmlJavaTypeAdapter(ProcessingSwitches.Adapter.class) - private ProcessingSwitches switches; - - /** Sequence of all sheets stubs got from image file. */ - @XmlElement(name = "sheet") - private final List stubs = new ArrayList(); - - /** Logical scores for this book. */ - @XmlElement(name = "score") - private final List scores = new ArrayList(); - - // Transient data - //--------------- - // - /** Project file lock. */ - private final Lock lock = new ReentrantLock(); - - /** The related file radix (file name without extension). */ - private String radix; - - /** File path where the book is kept. */ - private Path bookPath; - - /** File path where the book is printed. */ - private Path printPath; - - /** File path (without extension) where the MusicXML output is stored. */ - private Path exportPathSansExt; - - /** Browser on this book. */ - private BookBrowser bookBrowser; - - /** Flag to indicate this book is being closed. */ - private volatile boolean closing; - - /** Set if the book itself has been modified. */ - private boolean modified = false; - - /** Book-level sample repository. */ - private SampleRepository repository; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Create a Book with a path to an input images file. - * - * @param path the input image path (which may contain several images) - */ - public BasicBook (Path path) - { - Objects.requireNonNull(path, "Trying to create a Book with null path"); - - this.path = path; - subBooks = null; - - initTransients(FileUtil.getNameSansExtension(path), null); - } - - /** - * Create a meta Book, to be later populated with sub-books. - *

                            - * NOTA: This meta-book feature is not yet in use. - * - * @param nameSansExt a name (sans extension) for this book - */ - public BasicBook (String nameSansExt) - { - Objects.requireNonNull(nameSansExt, "Trying to create a meta Book with null name"); - - path = null; - subBooks = new ArrayList(); - - initTransients(nameSansExt, null); - } - - /** - * No-arg constructor needed by JAXB. - */ - public BasicBook () - { - path = null; - subBooks = null; - } - - //~ Methods ------------------------------------------------------------------------------------ - //----------// - // annotate // - //----------// - /** - * {@inheritDoc} - *

                            - * Generate a whole zip file, in which each valid sheet is represented by a pair - * composed of sheet image (.png) and sheet annotations (.xml). - */ - @Override - public void annotate () - { - Path root = null; - - try { - final Path bookFolder = BookManager.getDefaultBookFolder(this); - final Path path = bookFolder.resolve(getRadix() + BOOK_ANNOTATIONS_SUFFIX); - root = ZipFileSystem.create(path); - - for (SheetStub stub : getValidStubs()) { - try { - LogUtil.start(stub); - - final Path sheetFolder = root.resolve(INTERNALS_RADIX + stub.getNumber()); - final Sheet sheet = stub.getSheet(); - sheet.annotate(sheetFolder); - } catch (Exception ex) { - logger.warn("Error annotating {} {}", stub, ex.toString(), ex); - } finally { - LogUtil.stopStub(); - } - } - - logger.info("Book annotated as {}", path); - } catch (Exception ex) { - logger.warn("Error annotating book {} {}", this, ex.toString(), ex); - } finally { - if (root != null) { - try { - root.getFileSystem().close(); - } catch (Exception ignored) { - } - } - } - } - - //-------// - // close // - //-------// - @Override - public void close () - { - setClosing(true); - - // Close contained stubs/sheets - if (OMR.gui != null) { - SwingUtilities.invokeLater( - new Runnable() - { - @Override - public void run () - { - try { - LogUtil.start(BasicBook.this); - - for (SheetStub stub : new ArrayList(stubs)) { - LogUtil.start(stub); - - // Close stub UI, if any - if (stub.getAssembly() != null) { - StubsController.getInstance().deleteAssembly(stub); - stub.getAssembly().close(); - } - } - } finally { - LogUtil.stopBook(); - } - } - }); - } - - // Close browser if any - if (bookBrowser != null) { - bookBrowser.close(); - } - - // Remove from OMR instances - OMR.engine.removeBook(this); - - // Time for some cleanup... - Memory.gc(); - - logger.debug("Book closed."); - } - - //-----------------// - // closeFileSystem // - //-----------------// - /** - * Close the provided (book) file system. - * - * @param fileSystem the book file system - */ - public static void closeFileSystem (FileSystem fileSystem) - { - try { - fileSystem.close(); - - logger.info("Book file system closed."); - } catch (Exception ex) { - logger.warn("Could not close book file system " + ex, ex); - } - } - - //-----// - // ids // - //-----// - /** - * Build a string with just the IDs of the stub collection. - * - * @param stubs the collection of stub instances - * @return the string built - */ - public static String ids (List stubs) - { - if (stubs == null) { - return ""; - } - - StringBuilder sb = new StringBuilder(); - sb.append("["); - - for (SheetStub entity : stubs) { - sb.append("#").append(entity.getNumber()); - } - - sb.append("]"); - - return sb.toString(); - } - - //----------// - // loadBook // - //----------// - /** - * Load a book out of a provided book file. - * - * @param bookPath path to the (zipped) book file - * @return the loaded book if successful - */ - public static Book loadBook (Path bookPath) - { - StopWatch watch = new StopWatch("loadBook " + bookPath); - BasicBook book = null; - - try { - logger.info("Loading book {}", bookPath); - watch.start("book"); - - // Open book file - Path rootPath = ZipFileSystem.open(bookPath); - - // Load book internals (just the stubs) out of book.xml - Path internalsPath = rootPath.resolve(Book.BOOK_INTERNALS); - InputStream is = Files.newInputStream(internalsPath, StandardOpenOption.READ); - - Unmarshaller um = getJaxbContext().createUnmarshaller(); - book = (BasicBook) um.unmarshal(is); - book.getLock().lock(); - LogUtil.start(book); - - boolean ok = book.initTransients(null, bookPath); - - is.close(); // Close input stream - rootPath.getFileSystem().close(); // Close book file - - if (!ok) { - logger.info("Discarded {}", bookPath); - - return null; - } - - book.checkScore(); // TODO: remove ASAP - - return book; - } catch (Exception ex) { - logger.warn("Error loading book " + bookPath + " " + ex, ex); - - return null; - } finally { - if (constants.printWatch.isSet()) { - watch.print(); - } - - if (book != null) { - book.getLock().unlock(); - } - - LogUtil.stopBook(); - } - } - - //--------------// - // openBookFile // - //--------------// - /** - * Open the book file (supposed to already exist at location provided by - * '{@code bookPath}' parameter) for reading or writing. - *

                            - * When IO operations are finished, the book file must be closed via - * {@link #closeFileSystem(java.nio.file.FileSystem)} - * - * @param bookPath book path name - * @return the root path of the (zipped) book file system - */ - public static Path openBookFile (Path bookPath) - { - if (bookPath == null) { - throw new IllegalStateException("bookPath is null"); - } - - try { - logger.debug("Book file system opened"); - - FileSystem fileSystem = FileSystems.newFileSystem(bookPath, null); - - return fileSystem.getPath(fileSystem.getSeparator()); - } catch (FileNotFoundException ex) { - logger.warn("File not found: " + bookPath, ex); - } catch (IOException ex) { - logger.warn("Error reading book:" + bookPath, ex); - } - - return null; - } - - //-------------// - // createStubs // - //-------------// - @Override - public void createStubs (SortedSet sheetNumbers) - { - ImageLoading.Loader loader = ImageLoading.getLoader(path); - - if (loader != null) { - final int imageCount = loader.getImageCount(); - loader.dispose(); - logger.info("{} sheet{} in {}", imageCount, ((imageCount > 1) ? "s" : ""), path); - - if (sheetNumbers == null) { - sheetNumbers = new TreeSet(); - } - - if (sheetNumbers.isEmpty()) { - for (int i = 1; i <= imageCount; i++) { - sheetNumbers.add(i); - } - } - - for (int num : sheetNumbers) { - stubs.add(new BasicStub(this, num)); - } - } - } - - //-----------------// - // createStubsTabs // - //-----------------// - @Override - public void createStubsTabs (final Integer focus) - { - Runnable doRun = new Runnable() - { - @Override - public void run () - { - try { - LogUtil.start(BasicBook.this); - - final StubsController controller = StubsController.getInstance(); - - // Determine which stub should get the focus - SheetStub focusStub = null; - - if (focus != null) { - if ((focus > 0) && (focus <= stubs.size())) { - focusStub = stubs.get(focus - 1); - } else { - logger.warn("Illegal focus sheet id: {}", focus); - } - } - - if (focusStub == null) { - focusStub = getFirstValidStub(); // Focus on first valid stub, if any - - if (focusStub == null) { - logger.info("No valid sheet in {}", this); - } - } - - // Allocate one tab per stub, beginning by focusStub if any - Integer focusIndex = null; - - if (focusStub != null) { - controller.addAssembly(focusStub.getAssembly(), null); - focusIndex = controller.getIndex(focusStub); - - if (focusIndex == -1) { - focusIndex = null; // Safer - } - } - - for (SheetStub stub : stubs) { - if (focusIndex == null) { - controller.addAssembly(stub.getAssembly(), null); - } else if (stub != focusStub) { - if (stub.getNumber() < focusStub.getNumber()) { - controller.addAssembly( - stub.getAssembly(), - controller.getLastIndex()); - } else { - controller.addAssembly(stub.getAssembly(), null); - } - } - } - - controller.adjustStubTabs(BasicBook.this); - } finally { - LogUtil.stopBook(); - } - } - }; - - try { - SwingUtilities.invokeAndWait(doRun); - } catch (Exception ex) { - logger.warn("Error in createStubsTabs, {}", ex, ex); - } - } - - // - // //--------------// - // // deleteExport // - // //--------------// - // public void deleteExport () - // { - // // Determine the output path for the provided book: path/to/scores/Book - // Path bookPathSansExt = BookManager.getActualPath( - // getExportPathSansExt(), - // BookManager.getDefaultExportPathSansExt(this)); - // - // // One-sheet book: .mxl - // // One-sheet book: .mvt.mxl - // // One-sheet book: /... (perhaps some day: 1 directory per book) - // // - // // Multi-sheet book: -sheet#.mxl - // // Multi-sheet book: -sheet#.mvt.mxl - // final Path folder = isMultiSheet() ? bookPathSansExt : bookPathSansExt.getParent(); - // final Path bookName = bookPathSansExt.getFileName(); // bookname - // - // final String dirGlob = "glob:**/" + bookName + "{/**,}"; - // final String filGlob = "glob:**/" + bookName + "{/**,.*}"; - // final List paths = FileUtil.walkDown(folder, dirGlob, filGlob); - // - // if (!paths.isEmpty()) { - // BookManager.deletePaths(bookName + " deletion", paths); - // } else { - // logger.info("Nothing to delete"); - // } - // } - // - //--------// - // export // - //--------// - @Override - public void export () - { - // Make sure material is ready? - transcribe(); - - // path/to/scores/Book - final Path bookPathSansExt = BookManager.getActualPath( - getExportPathSansExt(), - BookManager.getDefaultExportPathSansExt(this)); - final boolean compressed = BookManager.useCompression(); - final String ext = compressed ? OMR.COMPRESSED_SCORE_EXTENSION : OMR.SCORE_EXTENSION; - final boolean sig = BookManager.useSignature(); - - // Export each movement score - String bookName = bookPathSansExt.getFileName().toString(); - final boolean multiMovements = scores.size() > 1; - - if (BookManager.useOpus()) { - // Export the book as one opus file - final Path opusPath = bookPathSansExt.resolveSibling(bookName + OMR.OPUS_EXTENSION); - - try { - new OpusExporter(this).export(opusPath, bookName, sig); - } catch (Exception ex) { - logger.warn("Could not export opus " + opusPath, ex); - } - } else { - // Export the book as one or several movement files - for (Score score : scores) { - final String scoreName = (!multiMovements) ? bookName - : (bookName + OMR.MOVEMENT_EXTENSION + score.getId()); - final Path scorePath = bookPathSansExt.resolveSibling(scoreName + ext); - - try { - new ScoreExporter(score).export(scorePath, scoreName, sig, compressed); - } catch (Exception ex) { - logger.warn("Could not export score " + scoreName, ex); - } - } - } - } - - //----------// - // getAlias // - //----------// - /** - * @return the alias - */ - @Override - public String getAlias () - { - return alias; - } - - //-----------------------// - // getBinarizationFilter // - //-----------------------// - @Override - public FilterParam getBinarizationFilter () - { - if (binarizationFilter == null) { - binarizationFilter = new FilterParam(); - binarizationFilter.setParent(FilterDescriptor.defaultFilter); - } - - return binarizationFilter; - } - - //-------------// - // getBookPath // - //-------------// - @Override - public Path getBookPath () - { - return bookPath; - } - - //-----------------// - // getBrowserFrame // - //-----------------// - @Override - public JFrame getBrowserFrame () - { - if (bookBrowser == null) { - // Build the BookBrowser on the score - bookBrowser = new BookBrowser(this); - } - - return bookBrowser.getFrame(); - } - - //----------------------// - // getExportPathSansExt // - //----------------------// - @Override - public Path getExportPathSansExt () - { - return exportPathSansExt; - } - - //-------------------// - // getFirstValidStub // - //-------------------// - @Override - public SheetStub getFirstValidStub () - { - for (SheetStub stub : stubs) { - if (stub.isValid()) { - return stub; - } - } - - return null; // No valid stub found! - } - - //--------------// - // getInputPath // - //--------------// - @Override - public Path getInputPath () - { - return path; - } - - //---------// - // getLock // - //---------// - @Override - public Lock getLock () - { - return lock; - } - - //-----------------// - // getOcrLanguages // - //-----------------// - @Override - public Param getOcrLanguages () - { - if (ocrLanguages == null) { - ocrLanguages = new StringParam(); - ocrLanguages.setParent(Language.ocrDefaultLanguages); - } - - return ocrLanguages; - } - - //-----------// - // getOffset // - //-----------// - @Override - public Integer getOffset () - { - return offset; - } - - //--------------// - // getPrintPath // - //--------------// - @Override - public Path getPrintPath () - { - return printPath; - } - - //-----------------------// - // getProcessingSwitches // - //-----------------------// - @Override - public ProcessingSwitches getProcessingSwitches () - { - if (switches == null) { - switches = new ProcessingSwitches(); - switches.setParent(ProcessingSwitches.getDefaultSwitches()); - } - - return switches; - } - - //----------// - // getRadix // - //----------// - @Override - public String getRadix () - { - return radix; - } - - //---------------------// - // getSampleRepository // - //---------------------// - @Override - public SampleRepository getSampleRepository () - { - SampleRepository repo = getSpecificSampleRepository(); - - if (repo != null) { - return repo; - } - - // No specific repository is possible, so use global - return SampleRepository.getGlobalInstance(); - } - - //----------// - // getScore // - //----------// - /** - * Report the score which contains the provided page. - * - * @param page provided page - * @return containing score (can it be null?) - */ - public Score getScore (Page page) - { - for (Score score : scores) { - int pageIndex = score.getPageIndex(page); - - if (pageIndex != -1) { - return score; - } - } - - return null; - } - - //-----------// - // getScores // - //-----------// - @Override - public List getScores () - { - return Collections.unmodifiableList(scores); - } - - //-----------------------------// - // getSpecificSampleRepository // - //-----------------------------// - @Override - public SampleRepository getSpecificSampleRepository () - { - if (repository == null) { - repository = SampleRepository.getInstance(this, true); - } - - return repository; - } - - //---------// - // getStub // - //---------// - @Override - public SheetStub getStub (int sheetId) - { - return stubs.get(sheetId - 1); - } - - //----------// - // getStubs // - //----------// - @Override - public List getStubs () - { - return Collections.unmodifiableList(stubs); - } - - //---------------// - // getValidStubs // - //---------------// - @Override - public List getValidStubs () - { - List valids = new ArrayList(); - - for (SheetStub stub : stubs) { - if (stub.isValid()) { - valids.add(stub); - } - } - - return valids; - } - - //------------------------// - // hasAllocatedRepository // - //------------------------// - @Override - public boolean hasAllocatedRepository () - { - return repository != null; - } - - //-----------------------// - // hasSpecificRepository // - //-----------------------// - @Override - public boolean hasSpecificRepository () - { - if (repository != null) { - return true; - } - - // Look for needed files - return SampleRepository.repositoryExists(this); - } - - //------------------// - // hideInvalidStubs // - //------------------// - @Override - public void hideInvalidStubs () - { - SwingUtilities.invokeLater( - new Runnable() - { - @Override - public void run () - { - final StubsController controller = StubsController.getInstance(); - - for (SheetStub stub : stubs) { - if (!stub.isValid()) { - controller.removeAssembly(stub); - } - } - } - }); - } - - //-------------// - // includeBook // - //-------------// - @Override - public void includeBook (Book book) - { - subBooks.add(book); - } - - //-----------// - // isClosing // - //-----------// - @Override - public boolean isClosing () - { - return closing; - } - - //---------// - // isDirty // - //---------// - @Override - public boolean isDirty () - { - return dirty; - } - - //------------// - // isModified // - //------------// - @Override - public boolean isModified () - { - if (modified) { - return true; // The book itself is modified - } - - if ((repository != null) && repository.isModified()) { - return true; // The book sample repository is modified - } - - for (SheetStub stub : stubs) { - if (stub.isModified()) { - return true; // This sheet is modified - } - } - - return false; - } - - //--------------// - // isMultiSheet // - //--------------// - @Override - public boolean isMultiSheet () - { - return stubs.size() > 1; - } - - //----------------// - // loadSheetImage // - //----------------// - @Override - public BufferedImage loadSheetImage (int id) - { - try { - final ImageLoading.Loader loader = ImageLoading.getLoader(path); - - if (loader == null) { - return null; - } - - BufferedImage img = loader.getImage(id); - logger.info("Loaded image {} {}x{} from {}", id, img.getWidth(), img.getHeight(), path); - - loader.dispose(); - - return img; - } catch (IOException ex) { - logger.warn("Error in book.loadSheetImage", ex); - - return null; - } - } - - //--------------// - // openBookFile // - //--------------// - /** - * Open the book file (supposed to already exist at location provided by - * '{@code bookPath}' member) for reading or writing. - *

                            - * When IO operations are finished, the book file must be closed via - * {@link #closeFileSystem(java.nio.file.FileSystem)} - * - * @return the root path of the (zipped) book file system - * @throws java.io.IOException if anything goes wrong - */ - public Path openBookFile () - throws IOException - { - return ZipFileSystem.open(bookPath); - } - - //-----------------// - // openSheetFolder // - //-----------------// - @Override - public Path openSheetFolder (int number) - throws IOException - { - Path root = openBookFile(); - - return root.resolve(INTERNALS_RADIX + number); - } - - //-------// - // print // - //-------// - @Override - public void print () - { - // Path to print file - final Path pdfPath = BookManager.getActualPath( - getPrintPath(), - BookManager.getDefaultPrintPath(this)); - - try { - new BookPdfOutput(BasicBook.this, pdfPath.toFile()).write(null); - setPrintPath(pdfPath); - } catch (Exception ex) { - logger.warn("Cannot write PDF to " + pdfPath, ex); - } - } - - //---------------// - // reachBookStep // - //---------------// - @Override - public boolean reachBookStep (final Step target, - final boolean force, - final Set sheetIds) - { - try { - final List concernedStubs = getConcernedStubs(sheetIds); - logger.debug("reachStep {} force:{} sheetIds:{}", target, force, sheetIds); - - if (!force) { - // Check against the least advanced step performed across all sheets concerned - Step least = getLeastStep(concernedStubs); - - if ((least != null) && (least.compareTo(target) >= 0)) { - return true; // Nothing to do - } - } - - // Launch the steps on each sheet - long startTime = System.currentTimeMillis(); - logger.info( - "Book reaching {}{} on sheets:{}", - target, - force ? " force" : "", - ids(concernedStubs)); - - try { - boolean someFailure = false; - StepMonitoring.notifyStart(); - - if (isMultiSheet() - && constants.processAllStubsInParallel.isSet() - && (OmrExecutors.defaultParallelism.getValue() == true)) { - // Process all stubs in parallel - List> tasks = new ArrayList>(); - - for (final SheetStub stub : concernedStubs) { - tasks.add( - new Callable() - { - @Override - public Boolean call () - throws StepException - { - LogUtil.start(stub); - - try { - boolean ok = stub.reachStep(target, force); - - if (ok && (OMR.gui == null)) { - stub.swapSheet(); // Save sheet & global book info to disk - } - - return ok; - } finally { - LogUtil.stopStub(); - } - } - }); - } - - try { - List> futures = OmrExecutors.getCachedLowExecutor() - .invokeAll(tasks); - - for (Future future : futures) { - try { - if (!future.get()) { - someFailure = true; - } - } catch (Exception ex) { - logger.warn("Future exception", ex); - someFailure = true; - } - } - - return !someFailure; - } catch (InterruptedException ex) { - logger.warn("Error in parallel reachBookStep", ex); - someFailure = true; - } - } else { - // Process one stub after the other - for (SheetStub stub : concernedStubs) { - LogUtil.start(stub); - - try { - if (stub.reachStep(target, force)) { - if (OMR.gui == null) { - stub.swapSheet(); // Save sheet & global book info to disk - } - } else { - someFailure = true; - } - } catch (Exception ex) { - // Exception (such as timeout) raised on stub - // Let processing continue for the other stubs - logger.warn("Error processing stub"); - someFailure = true; - } finally { - LogUtil.stopStub(); - } - } - } - - return !someFailure; - } finally { - LogUtil.stopStub(); - StepMonitoring.notifyStop(); - - long stopTime = System.currentTimeMillis(); - logger.debug("End of step set in {} ms.", (stopTime - startTime)); - } - } catch (ProcessingCancellationException pce) { - throw pce; - } catch (Exception ex) { - logger.warn("Error in performing " + target, ex); - } - - return false; - } - - //--------------// - // reduceScores // - //--------------// - @Override - public int reduceScores () - { - int modifs = 0; - - if (scores != null) { - for (Score score : scores) { - // (re) build the score logical parts - modifs += new ScoreReduction(score).reduce(); - - // Slurs and voices connection across pages in score - modifs += Voices.refineScore(score); - } - - if (modifs > 0) { - setModified(true); - logger.info("Scores built: {}", scores.size()); - } - - setDirty(false); - } - - return modifs; - } - - //------------// - // removeStub // - //------------// - @Override - public boolean removeStub (SheetStub stub) - { - return stubs.remove(stub); - } - - //-------// - // reset // - //-------// - @Override - public void reset () - { - for (SheetStub stub : getValidStubs()) { - stub.reset(); - } - - scores.clear(); - } - - //---------------// - // resetToBinary // - //---------------// - @Override - public void resetToBinary () - { - for (SheetStub stub : getValidStubs()) { - stub.resetToBinary(); - } - - scores.clear(); - } - - //--------// - // sample // - //--------// - @Override - public void sample () - { - for (SheetStub stub : getValidStubs()) { - Sheet sheet = stub.getSheet(); - sheet.sample(); - } - } - - //----------// - // setAlias // - //----------// - /** - * @param alias the alias to set - */ - @Override - public void setAlias (String alias) - { - this.alias = alias; - radix = alias; - } - - //------------// - // setClosing // - //------------// - @Override - public void setClosing (boolean closing) - { - this.closing = closing; - } - - //----------// - // setDirty // - //----------// - @Override - public void setDirty (boolean dirty) - { - this.dirty = dirty; - } - - //----------------------// - // setExportPathSansExt // - //----------------------// - @Override - public void setExportPathSansExt (Path exportPathSansExt) - { - this.exportPathSansExt = exportPathSansExt; - } - - //-------------// - // setModified // - //-------------// - @Override - public void setModified (boolean modified) - { - ///logger.info("{} setModified {}", this, modified); - this.modified = modified; - - if (OMR.gui != null) { - SwingUtilities.invokeLater( - new Runnable() - { - @Override - public void run () - { - final StubsController controller = StubsController.getInstance(); - final SheetStub stub = controller.getSelectedStub(); - - if ((stub != null) && (stub.getBook() == BasicBook.this)) { - controller.refresh(); - } - } - }); - } - } - - //-----------// - // setOffset // - //-----------// - @Override - public void setOffset (Integer offset) - { - this.offset = offset; - } - - //--------------// - // setPrintPath // - //--------------// - @Override - public void setPrintPath (Path printPath) - { - this.printPath = printPath; - } - - //-------// - // store // - //-------// - @Override - public void store (Path bookPath, - boolean withBackup) - { - Memory.gc(); // Launch garbage collection, to save on weak glyph references ... - - boolean diskWritten = false; // Has disk actually been written? - - // Backup existing book file? - if (withBackup && Files.exists(bookPath)) { - Path backup = FileUtil.backup(bookPath); - - if (backup != null) { - logger.info("Previous book file renamed as {}", backup); - } - } - - Path root = null; // Root of the zip file system - - try { - getLock().lock(); - checkRadixChange(bookPath); - logger.debug("Storing book..."); - - if ((this.bookPath == null) - || this.bookPath.toAbsolutePath().equals(bookPath.toAbsolutePath())) { - if (this.bookPath == null) { - root = ZipFileSystem.create(bookPath); - diskWritten = true; - } else { - root = ZipFileSystem.open(bookPath); - } - - if (modified) { - storeBookInfo(root); // Book info (book.xml) - diskWritten = true; - } - - // Contained sheets - for (SheetStub stub : stubs) { - if (stub.isModified()) { - final Path sheetFolder = root.resolve(INTERNALS_RADIX + stub.getNumber()); - stub.getSheet().store(sheetFolder, null); - diskWritten = true; - } - } - - // Separate repository - if ((repository != null) && repository.isModified()) { - repository.storeRepository(); - } - } else { - // (Store as): Switch from old to new book file - root = createBookFile(bookPath); - diskWritten = true; - - storeBookInfo(root); // Book info (book.xml) - - // Contained sheets - final Path oldRoot = openBookFile(this.bookPath); - - for (SheetStub stub : stubs) { - final Path oldSheetFolder = oldRoot.resolve(INTERNALS_RADIX + stub.getNumber()); - final Path sheetFolder = root.resolve(INTERNALS_RADIX + stub.getNumber()); - - // By default, copy existing sheet files - if (Files.exists(oldSheetFolder)) { - FileUtil.copyTree(oldSheetFolder, sheetFolder); - } - - // Update modified sheet files - if (stub.isModified()) { - stub.getSheet().store(sheetFolder, oldSheetFolder); - } - } - - oldRoot.getFileSystem().close(); // Close old book file - } - - this.bookPath = bookPath; - - BookManager.getInstance().getBookHistory().add(bookPath); // Insert in history - - if (diskWritten) { - logger.info("Book stored as {}", bookPath); - } - } catch (Throwable ex) { - logger.warn("Error storing " + this + " to " + bookPath + " ex:" + ex, ex); - } finally { - if (root != null) { - try { - root.getFileSystem().close(); - } catch (IOException ignored) { - } - } - - getLock().unlock(); - } - } - - //-------// - // store // - //-------// - @Override - public void store () - { - if (bookPath == null) { - logger.warn("Bookpath not defined"); - } else { - store(bookPath, false); - } - } - - //---------------// - // storeBookInfo // - //---------------// - @Override - public void storeBookInfo (Path root) - throws Exception - { - Path bookInternals = root.resolve(Book.BOOK_INTERNALS); - Files.deleteIfExists(bookInternals); - Jaxb.marshal(this, bookInternals, getJaxbContext()); - setModified(false); - logger.info("Stored {}", bookInternals); - } - - //---------------// - // swapAllSheets // - //---------------// - @Override - public void swapAllSheets () - { - if (isModified()) { - logger.info("{} storing", this); - store(); - } - - SheetStub currentStub = null; - - if (OMR.gui != null) { - currentStub = StubsController.getCurrentStub(); - } - - for (SheetStub stub : stubs) { - if (stub != currentStub) { - stub.swapSheet(); - } - } - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("Book{"); - sb.append(radix); - - if ((offset != null) && (offset > 0)) { - sb.append(" offset:").append(offset); - } - - sb.append("}"); - - return sb.toString(); - } - - //------------// - // transcribe // - //------------// - @Override - public boolean transcribe () - { - boolean ok = reachBookStep(Step.last(), false, null); - - reduceScores(); - - return ok; - } - - //--------------// - // updateScores // - //--------------// - /** - * {@inheritDoc} - *

                            - * The question is which scores should we update. - * Clearing all and rebuilding all is OK for pageRefs of all scores without loading sheets. - * But doing so, we lose logicalPart information of all scores, and to rebuild it we'll - * need to reload all valid sheets. - *

                            - * A better approach is to check the stub before and the stub after the current one. - * This may result in the addition or the removal of scores. - * - * @param currentStub the current stub - */ - @Override - public synchronized void updateScores (SheetStub currentStub) - { - if (scores.isEmpty()) { - // Easy: allocate scores based on all book stubs - createScores(); - } else { - try { - // Determine just the impacted pageRefs - final SortedSet impactedRefs = new TreeSet(); - final int stubNumber = currentStub.getNumber(); - - if (!currentStub.getPageRefs().isEmpty()) { - // Look in stub before current stub? - final PageRef firstPageRef = currentStub.getFirstPageRef(); - - if (!firstPageRef.isMovementStart()) { - final SheetStub prevStub = (stubNumber > 1) ? stubs.get(stubNumber - 2) : null; - - if (prevStub != null) { - final PageRef prevPageRef = prevStub.getLastPageRef(); - - if (prevPageRef != null) { - impactedRefs.addAll(getScore(prevPageRef).getPageRefs()); // NPE - } - } - } - - // Take pages of current stub - impactedRefs.addAll(currentStub.getPageRefs()); - - // Look in stub after current stub? - final SheetStub nextStub = (stubNumber < stubs.size()) ? stubs.get(stubNumber) - : null; - - if (nextStub != null) { - final PageRef nextPageRef = nextStub.getFirstPageRef(); - - if ((nextPageRef != null) && !nextPageRef.isMovementStart()) { - impactedRefs.addAll(getScore(nextPageRef).getPageRefs()); // NPE - } - } - } - - // Determine and remove the impacted scores - final List impactedScores = scoresOf(impactedRefs); - Integer scoreIndex = null; - - if (!impactedScores.isEmpty()) { - scoreIndex = scores.indexOf(impactedScores.get(0)); - } else { - for (Score score : scores) { - if (score.getFirstPageRef().getSheetNumber() > stubNumber) { - scoreIndex = scores.indexOf(score); - - break; - } - } - } - - if (scoreIndex == null) { - scoreIndex = scores.size(); - } - - logger.debug("Impacted pages:{} scores:{}", impactedRefs, impactedScores); - scores.removeAll(impactedScores); - - // Insert new score(s) to replace the impacted one(s)? - if (!currentStub.isValid()) { - impactedRefs.removeAll(currentStub.getPageRefs()); - } - - insertScores(currentStub, impactedRefs, scoreIndex); - } catch (Exception ex) { - // This seems to result from inconsistency between scores info and stubs info. - // Initial cause can be a sheet not marshalled (because of use by another process) - // followed by a reload of now non-consistent book.xml - - // Workaround: Clear all scores and rebuild them from stubs info - // (Doing so, we may lose logical-part informations) - logger.warn("Error updating scores " + ex, ex); - logger.warn("Rebuilding them from stubs info."); - scores.clear(); - createScores(); - } - } - } - - //-----------------------// - // areVersionsCompatible // - //-----------------------// - /** - * Check whether the program version can operate on the file version. - *

                            - * All version strings are expected to be formatted like "5.0" or "5.0.1" - * and we don't take the 3rd number, if any, into account for compatibility checking. - * - * @param programVersion version of software - * @param fileVersion version of book file - * @return true if OK - */ - private boolean areVersionsCompatible (String programVersion, - String fileVersion) - { - try { - logger.debug("Book file version: {}", fileVersion); - - final String[] programTokens = programVersion.split("\\."); - - if (programTokens.length < 2) { - throw new IllegalArgumentException("Illegal Audiveris version " + programVersion); - } - - final String[] fileTokens = fileVersion.split("\\."); - - if (fileTokens.length < 2) { - throw new IllegalArgumentException("Illegal Book file version " + fileVersion); - } - - for (int i = 0; i < 2; i++) { - if (Integer.decode(fileTokens[i]) < Integer.decode(programTokens[i])) { - return false; - } - } - - return true; - } catch (Throwable ex) { - logger.error("Error while checking versions " + ex, ex); - - return false; // Safer - } - } - - //---------------// - // beforeMarshal // - //---------------// - @SuppressWarnings("unused") - private void beforeMarshal (Marshaller m) - { - if ((binarizationFilter != null) && !binarizationFilter.isSpecific()) { - binarizationFilter = null; - } - - if ((ocrLanguages != null) && !ocrLanguages.isSpecific()) { - ocrLanguages = null; - } - - if ((switches != null) && switches.isEmpty()) { - switches = null; - } - } - - //------------// - // checkAlias // - //------------// - private static String checkAlias (Path path) - { - // Alias? - if (AliasPatterns.useAliasPatterns()) { - final String nameSansExt = FileUtil.getNameSansExtension(path); - - return BookManager.getInstance().getAlias(nameSansExt); - } - - return null; - } - - //------------------// - // checkRadixChange // - //------------------// - /** - * If the (new) book name does not match current one, update the book radix - * (and the title of first displayed sheet if any). - * - * @param bookPath new book target path - */ - private void checkRadixChange (Path bookPath) - { - // Are we changing the target name WRT the default name? - final String newRadix = FileUtil.avoidExtensions( - bookPath.getFileName(), - OMR.BOOK_EXTENSION).toString(); - - if (!newRadix.equals(radix)) { - // Update book radix - radix = newRadix; - - // We are really changing the radix, so nullify all other paths - exportPathSansExt = printPath = null; - - if (OMR.gui != null) { - // Update UI first sheet tab - SwingUtilities.invokeLater( - new Runnable() - { - @Override - public void run () - { - StubsController.getInstance().updateFirstStubTitle(BasicBook.this); - } - }); - } - } - } - - //------------// - // checkScore // Dirty hack, to be removed ASAP - //------------// - private void checkScore () - { - for (Score score : scores) { - PageRef ref = score.getFirstPageRef(); - - if (ref == null) { - logger.warn("Discarding invalid score data."); - scores.clear(); - - break; - } - } - } - - //----------------// - // createBookFile // - //----------------// - /** - * Create a new book file system dedicated to this book at the location provided - * by '{@code bookpath}' member. - * If such file already exists, it is deleted beforehand. - *

                            - * When IO operations are finished, the book file must be closed via - * {@link #closeFileSystem(java.nio.file.FileSystem)} - * - * @return the root path of the (zipped) book file system - */ - private static Path createBookFile (Path bookPath) - throws IOException - { - if (bookPath == null) { - throw new IllegalStateException("bookPath is null"); - } - - try { - Files.deleteIfExists(bookPath); - } catch (IOException ex) { - logger.warn("Error deleting book: " + bookPath, ex); - } - - // Make sure the containing folder exists - Files.createDirectories(bookPath.getParent()); - - // Make it a zip file - ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(bookPath.toFile())); - zos.close(); - - // Finally open the book file just created - return ZipFileSystem.open(bookPath); - } - - //----------------// - // getJaxbContext // - //----------------// - private static JAXBContext getJaxbContext () - throws JAXBException - { - // Lazy creation - if (jaxbContext == null) { - jaxbContext = JAXBContext.newInstance(BasicBook.class, RunTable.class); - } - - return jaxbContext; - } - - //--------------// - // createScores // - //--------------// - /** - * Create scores out of all book stubs. - */ - private void createScores () - { - Score score = null; - - // Group provided sheets pages into scores - for (SheetStub stub : stubs) { - // An invalid or not-yet-processed stub triggers a score break - if (stub.getPageRefs().isEmpty()) { - score = null; - } else { - for (PageRef pageRef : stub.getPageRefs()) { - if ((score == null) || pageRef.isMovementStart()) { - scores.add(score = new Score()); - score.setBook(this); - } - - score.addPageRef(stub.getNumber(), pageRef); - } - } - } - - logger.debug("Created scores:{}", scores); - } - - //-------------------// - // getConcernedStubs // - //-------------------// - private List getConcernedStubs (Set sheetIds) - { - List list = new ArrayList(); - - for (SheetStub stub : getValidStubs()) { - if ((sheetIds == null) || sheetIds.contains(stub.getNumber())) { - list.add(stub); - } - } - - return list; - } - - //--------------// - // getLeastStep // - //--------------// - /** - * Report the least advanced step reached among all provided stubs. - * - * @return the least step, null if any stub has not reached the first step (LOAD) - */ - private Step getLeastStep (List stubs) - { - Step least = Step.last(); - - for (SheetStub stub : stubs) { - Step latest = stub.getLatestStep(); - - if (latest == null) { - return null; // This sheet has not been processed at all - } - - if (latest.compareTo(least) < 0) { - least = latest; - } - } - - return least; - } - - //----------// - // getScore // - //----------// - /** - * Report the score if any that contains the provided PageRef. - * - * @param pageRef the provided page ref (sheet#, page#) - * @return the containing score or null if not found - */ - private Score getScore (PageRef pageRef) - { - for (Score score : scores) { - PageRef ref = score.getPageRef(pageRef.getSheetNumber()); - - if ((ref != null) && (ref.getId() == pageRef.getId())) { - return score; - } - } - - return null; - } - - //----------------// - // initTransients // - //----------------// - /** - * Initialize transient data. - * - * @param nameSansExt book name without extension, if any - * @param bookPath full path to book .omr file, if any - * @return true if OK - */ - private boolean initTransients (String nameSansExt, - Path bookPath) - { - if (binarizationFilter != null) { - binarizationFilter.setParent(FilterDescriptor.defaultFilter); - } - - if (ocrLanguages != null) { - ocrLanguages.setParent(Language.ocrDefaultLanguages); - } - - if (switches != null) { - switches.setParent(ProcessingSwitches.getDefaultSwitches()); - } - - if (alias == null) { - alias = checkAlias(getInputPath()); - - if (alias != null) { - nameSansExt = alias; - } - } - - if (nameSansExt != null) { - radix = nameSansExt; - } - - if (bookPath != null) { - this.bookPath = bookPath; - - if (nameSansExt == null) { - radix = FileUtil.getNameSansExtension(bookPath); - } - } - - if (build == null) { - build = WellKnowns.TOOL_BUILD; - } - - if (version == null) { - version = WellKnowns.TOOL_REF; - } else { - if (constants.checkBookVersion.isSet()) { - // Check compatibility between file version and program version - if (!areVersionsCompatible(ProgramId.PROGRAM_VERSION, version)) { - if (constants.resetOldBooks.isSet()) { - final String msg = bookPath + " version " + version; - logger.warn(msg); - - // Prompt user for resetting project sheets? - if ((OMR.gui == null) - || OMR.gui.displayConfirmation( - msg + "\nConfirm reset to binary?", - "Non compatible book version")) { - resetToBinary(); - logger.info("Book {} reset to binary.", radix); - version = WellKnowns.TOOL_REF; - build = WellKnowns.TOOL_BUILD; - - return true; - } - } else { - logger.info("Incompatible book version, but not reset."); - } - - return false; - } - } - } - - return true; - } - - //--------------// - // insertScores // - //--------------// - /** - * Insert scores out of provided sequence of PageRef's. - * - * @param currentStub stub being processed - * @param pageRefs sequence of pageRefs - * @param insertIndex insertion index in scores list - */ - private void insertScores (SheetStub currentStub, - SortedSet pageRefs, - int insertIndex) - { - Score score = null; - Integer stubNumber = null; - int index = insertIndex; - - for (PageRef ref : pageRefs) { - if (stubNumber == null) { - // Very first - score = null; - } else if (stubNumber < (ref.getSheetNumber() - 1)) { - // One or several stubs missing - score = null; - } - - if (ref.isMovementStart()) { - // Movement start - score = null; - } - - if (score == null) { - scores.add(index++, score = new Score()); - score.setBook(this); - } - - score.addPageRef(ref.getSheetNumber(), ref); - stubNumber = ref.getSheetNumber(); - } - - logger.debug("Inserted scores:{}", scores.subList(insertIndex, index)); - } - - //----------// - // scoresOf // - //----------// - /** - * Retrieve the list of scores that embrace the provided sequence of pageRefs. - * - * @param refs the provided pageRefs (sorted) - * @return the impacted scores - */ - private List scoresOf (SortedSet refs) - { - final List impacted = new ArrayList(); - - if (!refs.isEmpty()) { - final int firstNumber = refs.first().getSheetNumber(); - final int lastNumber = refs.last().getSheetNumber(); - - for (Score score : scores) { - if (score.getLastPageRef().getSheetNumber() < firstNumber) { - continue; - } - - if (score.getFirstPageRef().getSheetNumber() > lastNumber) { - break; - } - - List scoreRefs = new ArrayList(score.getPageRefs()); - scoreRefs.retainAll(refs); - - if (!scoreRefs.isEmpty()) { - impacted.add(score); - } - } - } - - return impacted; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //---------// - // Adapter // - //---------// - /** - * Meant for JAXB handling of Book interface. - */ - public static class Adapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public BasicBook marshal (Book s) - { - return (BasicBook) s; - } - - @Override - public Book unmarshal (BasicBook s) - { - return s; - } - } - - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print out the stop watch for book loading?"); - - private final Constant.Boolean processAllStubsInParallel = new Constant.Boolean( - false, - "Should we process all stubs of a book in parallel? (beware of many stubs)"); - - private final Constant.Boolean checkBookVersion = new Constant.Boolean( - true, - "Should we check version of loaded book files?"); - - private final Constant.Boolean resetOldBooks = new Constant.Boolean( - true, - "Should we reset to binary the too old book files?"); - } - - //------------------// - // OcrBookLanguages // - //------------------// - private static final class OcrBookLanguages - extends Param - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public boolean setSpecific (String specific) - { - if ((specific != null) && specific.isEmpty()) { - specific = null; - } - - return super.setSpecific(specific); - } - - //~ Inner Classes -------------------------------------------------------------------------- - /** - * JAXB adapter to mimic XmlValue. - */ - public static class Adapter - extends XmlAdapter - { - //~ Methods ---------------------------------------------------------------------------- - - @Override - public String marshal (OcrBookLanguages val) - throws Exception - { - if (val == null) { - return null; - } - - return val.getSpecific(); - } - - @Override - public OcrBookLanguages unmarshal (String str) - throws Exception - { - OcrBookLanguages ol = new OcrBookLanguages(); - ol.setSpecific(str); - - return ol; - } - } - } -} diff --git a/src/main/org/audiveris/omr/sheet/BasicSheet.java b/src/main/org/audiveris/omr/sheet/BasicSheet.java deleted file mode 100644 index 562330905..000000000 --- a/src/main/org/audiveris/omr/sheet/BasicSheet.java +++ /dev/null @@ -1,1386 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// B a s i c S h e e t // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet; - -import org.audiveris.omr.OMR; -import org.audiveris.omr.classifier.Annotations; -import org.audiveris.omr.classifier.AnnotationsBuilder; -import org.audiveris.omr.classifier.SampleRepository; -import org.audiveris.omr.classifier.SampleSheet; -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.GlyphIndex; -import org.audiveris.omr.glyph.GlyphsModel; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.glyph.dynamic.FilamentIndex; -import org.audiveris.omr.glyph.ui.GlyphsController; -import org.audiveris.omr.glyph.ui.SymbolsEditor; -import org.audiveris.omr.image.ImageFormatException; -import org.audiveris.omr.lag.LagManager; -import org.audiveris.omr.lag.Lags; -import org.audiveris.omr.run.RunTable; -import org.audiveris.omr.score.Page; -import org.audiveris.omr.score.PageRef; -import org.audiveris.omr.score.Score; -import org.audiveris.omr.score.ScoreExporter; -import org.audiveris.omr.score.ScoreReduction; -import org.audiveris.omr.score.ui.BookPdfOutput; -import org.audiveris.omr.sheet.ui.BinarizationBoard; -import org.audiveris.omr.sheet.ui.PictureView; -import org.audiveris.omr.sheet.ui.PixelBoard; -import org.audiveris.omr.sheet.ui.SheetTab; -import org.audiveris.omr.sheet.ui.StubsController; -import org.audiveris.omr.sig.InterIndex; -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.AbstractPitchedInter; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.ui.InterController; -import org.audiveris.omr.step.Step; -import org.audiveris.omr.step.StepException; -import org.audiveris.omr.ui.BoardsPane; -import org.audiveris.omr.ui.Colors; -import org.audiveris.omr.ui.ErrorsEditor; -import org.audiveris.omr.ui.selection.LocationEvent; -import org.audiveris.omr.ui.selection.PixelEvent; -import org.audiveris.omr.ui.selection.SelectionService; -import org.audiveris.omr.ui.util.ItemRenderer; -import org.audiveris.omr.ui.util.WeakItemRenderer; -import org.audiveris.omr.util.Dumping; -import org.audiveris.omr.util.FileUtil; -import org.audiveris.omr.util.Jaxb; -import org.audiveris.omr.util.Navigable; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import static java.nio.file.StandardOpenOption.CREATE; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.imageio.ImageIO; -import javax.swing.SwingUtilities; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.adapters.XmlAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - -/** - * Class {@code BasicSheet} is our implementation of {@link Sheet} interface. - *

                            - * The picture below represents the data model used for marshalling/unmarshalling a sheet to/from - * a sheet#n.xml file within a book .omr file - *

                            - * Most entities are represented here. Some Inter instances are listed only via their containing - * entity, such as tuplets in MeasureStack, slurs and lyrics in Part, ledgers and bars in Staff, - * graceChords and restChords in Measure, wholeChord in Voice. - *

                            - * Once an instance of BasicSheet has been unmarshalled, transient members of some entities need to - * be properly set. This is the purpose of the "afterReload()" methods which are called in a certain - * order as mentioned by the "(ar #n)" indications on these entities. - *

                            - * Sheet Binding - * - * @author Hervé Bitteur - */ -@XmlAccessorType(XmlAccessType.NONE) -@XmlRootElement(name = "sheet") -public class BasicSheet - implements Sheet -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger( - Sheet.class); - - /** Events that can be published on sheet location service. */ - private static final Class[] allowedEvents = new Class[]{ - LocationEvent.class, PixelEvent.class - }; - - /** Un/marshalling context for use with JAXB. */ - private static volatile JAXBContext jaxbContext; - - //~ Instance fields ---------------------------------------------------------------------------- - // - // Persistent data - //---------------- - // - /** Global id used to uniquely identify a persistent entity instance. */ - @XmlAttribute(name = "last-persistent-id") - @XmlJavaTypeAdapter(Jaxb.AtomicIntegerAdapter.class) - private final AtomicInteger lastPersistentId = new AtomicInteger(0); - - /** The related picture. */ - @XmlElement(name = "picture") - private Picture picture; - - /** Global scale for this sheet. */ - @XmlElement(name = "scale") - private Scale scale; - - /** Global skew. */ - @XmlElement(name = "skew") - private Skew skew; - - /** Corresponding page(s). A single sheet may relate to several pages. */ - @XmlElement(name = "page") - private final List pages = new ArrayList(); - - /** Global glyph index. - * See annotated get/set methods: {@link #getGlyphIndexContent()} - */ - private GlyphIndex glyphIndex; - - // Transient data - //--------------- - // - /** Corresponding sheet stub. */ - @Navigable(false) - private SheetStub stub; - - /** Inter index for all systems in this sheet. */ - private InterIndex interIndex; - - /** Staves. */ - private StaffManager staffManager; - - /** Systems management. */ - private SystemManager systemManager; - - /** Dictionary of sheet lags. */ - private LagManager lagManager; - - //-- UI ---------------------------------------------------------------------------------------- - // - /** Selections for this sheet. (SheetLocation, PixelLevel) */ - private SelectionService locationService; - - /** Registered item renderers, if any. */ - private Set itemRenderers; - - /** Related errors editor, if any. */ - private ErrorsEditor errorsEditor; - - /** Specific builder dealing with glyphs. */ - private volatile GlyphsController glyphsController; - - /** Specific UI manager dealing with inters. */ - private volatile InterController interController; - - /** Related symbols editor, if any. */ - private SymbolsEditor symbolsEditor; - - //-- resettable members ------------------------------------------------------------------------ - // - /** Global filaments index. */ - private FilamentIndex filamentIndex; - - /** Delta measurements. */ - private SheetDiff sheetDelta; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code BasicSheet} object with a binary table. - * - * @param stub the related sheet stub - * @param binaryTable the binary table, if any - */ - public BasicSheet (SheetStub stub, - RunTable binaryTable) - { - this(stub); - - if (binaryTable != null) { - setBinary(binaryTable); - } - } - - /** - * Create a new {@code Sheet} instance with an image. - * - * @param stub the related sheet stub - * @param image the already loaded image, if any - * @throws StepException if processing failed at this step - */ - public BasicSheet (SheetStub stub, - BufferedImage image) - throws StepException - { - this(stub); - - if (image != null) { - setImage(image); - } - } - - /** - * Create a new {@code Sheet} instance within a book. - * - * @param stub the related sheet stub - */ - private BasicSheet (SheetStub stub) - { - Objects.requireNonNull(stub, "Cannot create a sheet in a null stub"); - - glyphIndex = new GlyphIndex(); - - initTransients(stub); - - interIndex = new InterIndex(); - interIndex.initTransients(this); - } - - /** - * No-arg constructor needed for JAXB. - */ - private BasicSheet () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //------------------// - // getSheetFileName // - //------------------// - public static String getSheetFileName (int number) - { - return Sheet.INTERNALS_RADIX + number + ".xml"; - } - - //-----------------// - // addItemRenderer // - //-----------------// - @Override - public boolean addItemRenderer (ItemRenderer renderer) - { - if ((renderer != null) && (OMR.gui != null)) { - return itemRenderers.add(new WeakItemRenderer(renderer)); - - ///return itemRenderers.add(renderer); - } - - return false; - } - - //---------// - // addPage // - //---------// - @Override - public void addPage (Page page) - { - pages.add(page); - } - - //-------------// - // afterReload // - //-------------// - @Override - public void afterReload (SheetStub stub) - { - try { - // Predefined StaffHolder's are no longer useful - Staff.StaffHolder.clearStaffHolders(); - - // Complete sheet initialization - initTransients(stub); - - // Make sure hLag & vLag are available and their sections dispatched to relevant systems - if (stub.isDone(Step.GRID)) { - systemManager.dispatchHorizontalSections(); - systemManager.dispatchVerticalSections(); - } - - // Complete inters index - interIndex = new InterIndex(); - interIndex.initTransients(this); - - for (SystemInfo system : getSystems()) { - // Forward reload request down system hierarchy - system.afterReload(); - } - } catch (Exception ex) { - logger.warn("Error in " + getClass() + " afterReload() " + ex, ex); - } - } - - //----------// - // annotate // - //----------// - @Override - public void annotate () - { - try { - final Book book = stub.getBook(); - final Path bookFolder = BookManager.getDefaultBookFolder(book); - annotate(bookFolder); - } catch (Exception ex) { - logger.warn("Annotations failed {}", ex); - } - } - - //----------// - // annotate // - //----------// - @Override - public void annotate (Path sheetFolder) - { - OutputStream os = null; - - try { - // Sheet annotations - Path annPath = sheetFolder.resolve(getId() + Annotations.SHEET_ANNOTATIONS_SUFFIX); - new AnnotationsBuilder(this, annPath).processSheet(); - - // Sheet image - Path imgPath = sheetFolder.resolve(getId() + Annotations.SHEET_IMAGE_SUFFIX); - RunTable runTable = picture.getTable(Picture.TableKey.BINARY); - BufferedImage img = runTable.getBufferedImage(); - os = Files.newOutputStream(imgPath, CREATE); - ImageIO.write(img, Annotations.SHEET_IMAGE_FORMAT, os); - } catch (Exception ex) { - logger.warn("Error annotating {} {}", stub, ex.toString(), ex); - } finally { - if (os != null) { - try { - os.flush(); - os.close(); - } catch (Exception ignored) { - } - } - } - } - - //------------------// - // createBinaryView // - //------------------// - /** - * Create and display the binary view. - */ - public void createBinaryView () - { - if (!SwingUtilities.isEventDispatchThread()) { - try { - SwingUtilities.invokeAndWait( - new Runnable() - { - @Override - public void run () - { - createBinaryView(); - } - }); - } catch (Exception ex) { - logger.warn("invokeAndWait error", ex); - } - } else { - if (stub.getAssembly().getPane(SheetTab.BINARY_TAB.label) != null) { - return; - } - - locationService.subscribeStrongly(LocationEvent.class, picture); - - // Display sheet binary - PictureView pictureView = new PictureView(this); - stub.getAssembly().addViewTab( - SheetTab.BINARY_TAB, - pictureView, - new BoardsPane(new PixelBoard(this), new BinarizationBoard(this))); - } - } - - //-------------------// - // createPictureView // - //-------------------// - /** - * Create and display the picture view. - */ - public void createPictureView () - { - locationService.subscribeStrongly(LocationEvent.class, picture); - - // Display sheet picture - PictureView pictureView = new PictureView(this); - stub.getAssembly().addViewTab( - SheetTab.PICTURE_TAB, - pictureView, - new BoardsPane(new PixelBoard(this), new BinarizationBoard(this))); - } - - // - // //--------------// - // // deleteExport // - // //--------------// - // public void deleteExport () - // { - // final Book book = getBook(); - // - // if (!book.isMultiSheet()) { - // book.deleteExport(); // Simply delete the single-sheet book! - // } else { - // // path/to/scores/Book - // Path bookPathSansExt = BookManager.getActualPath( - // book.getExportPathSansExt(), - // BookManager.getDefaultExportPathSansExt(book)); - // - // // Determine the output path (sans extension) for the provided sheet - // final Path sheetPathSansExt = getSheetPathSansExt(bookPathSansExt); - // - // // Multi-sheet book: -sheet#.mvt.mxl - // // Multi-sheet book: -sheet#.mxl - // final Path folder = sheetPathSansExt.getParent(); - // final Path sheetName = sheetPathSansExt.getFileName(); // book-sheet#N - // - // final String dirGlob = "glob:**/" + sheetName + "{/**,}"; - // final String filGlob = "glob:**/" + sheetName + "{/**,.*}"; - // final List paths = FileUtil.walkDown(folder, dirGlob, filGlob); - // - // if (!paths.isEmpty()) { - // BookManager.deletePaths(sheetName + " deletion", paths); - // } - // } - // } - // - //----------------// - // displayDataTab // - //----------------// - @Override - public void displayDataTab () - { - try { - getSymbolsEditor(); - } catch (Throwable ex) { - logger.warn("Error in displayDataTab " + ex, ex); - } - } - - //-----------------// - // displayMainTabs // - //-----------------// - @Override - public void displayMainTabs () - { - if (stub.isDone(Step.GRID)) { - displayDataTab(); // Display DATA tab - } else if (stub.isDone(Step.BINARY)) { - createBinaryView(); // Display BINARY tab - } else { - createPictureView(); // Display Picture tab - } - - if (!stub.isValid()) { - StubsController.getInstance().markTab(stub, Colors.SHEET_INVALID); - } - } - - //-----------------// - // dumpSystemInfos // - //-----------------// - /** - * Utility method, to dump all sheet systems - */ - public void dumpSystemInfos () - { - System.out.println("--- SystemInfos ---"); - - int i = 0; - - for (SystemInfo system : getSystems()) { - new Dumping().dump(system, "#" + i++); - } - - System.out.println("--- SystemInfos end ---"); - } - - //--------// - // export // - //--------// - @Override - public void export (Path path) - { - if (pages.isEmpty()) { - return; - } - - final Book book = getBook(); - - try { - Path folder = path.getParent(); - - if (!Files.exists(folder)) { - Files.createDirectories(folder); - } - - final String ext = FileUtil.getExtension(path); - final String sheetName = FileUtil.getNameSansExtension(path.getFileName()); - final boolean compressed = (ext.equals(OMR.COMPRESSED_SCORE_EXTENSION)) ? true - : ((ext.equals(OMR.SCORE_EXTENSION)) ? false - : BookManager.useCompression()); - final boolean useSig = BookManager.useSignature(); - - int modifs = 0; // Count of modifications - - if (pages.size() > 1) { - // One file per page - for (PageRef pageRef : stub.getPageRefs()) { - final Score score = new Score(); - score.setBook(book); - score.addPageRef(stub.getNumber(), pageRef); - modifs += new ScoreReduction(score).reduce(); - - final int idx = pageRef.getId(); - final String scoreName = sheetName + OMR.MOVEMENT_EXTENSION + idx; - final Path scorePath = path.resolveSibling(scoreName + ext); - new ScoreExporter(score).export(scorePath, sheetName, useSig, compressed); - } - } else { - // Export the sheet single page as a score - final Score score = new Score(); - score.setBook(book); - score.addPageRef(stub.getNumber(), stub.getFirstPageRef()); - modifs += new ScoreReduction(score).reduce(); - - final String scoreName = sheetName; - final Path scorePath = path.resolveSibling(scoreName + ext); - new ScoreExporter(score).export(scorePath, scoreName, useSig, compressed); - } - - if (modifs > 0) { - book.setModified(true); - } - - // Remember the book export path in the book itself - book.setExportPathSansExt(folder.resolve(book.getRadix())); - } catch (Exception ex) { - logger.warn("Error exporting " + this + ", " + ex, ex); - } - } - - //-----------------// - // getErrorsEditor // - //-----------------// - @Override - public ErrorsEditor getErrorsEditor () - { - return errorsEditor; - } - - //------------------// - // getFilamentIndex // - //------------------// - @Override - public FilamentIndex getFilamentIndex () - { - if (filamentIndex == null) { - filamentIndex = new FilamentIndex(this); - } - - return filamentIndex; - } - - //---------------// - // getGlyphIndex // - //---------------// - @Override - public GlyphIndex getGlyphIndex () - { - if (glyphIndex == null) { - glyphIndex = new GlyphIndex(); - } - - return glyphIndex; - } - - //----------------------// - // getGlyphsController // - //----------------------// - @Override - public GlyphsController getGlyphsController () - { - if (glyphsController == null) { - createGlyphsController(); - } - - return glyphsController; - } - - //-----------// - // getHeight // - //-----------// - @Override - public int getHeight () - { - return picture.getHeight(); - } - - //-------// - // getId // - //-------// - @Override - public String getId () - { - if (stub != null) { - return stub.getId(); - } - - return null; - } - - //--------------------// - // getInterController // - //--------------------// - @Override - public InterController getInterController () - { - if (interController == null) { - interController = new InterController(this); - } - - return interController; - } - - //---------------// - // getInterIndex // - //---------------// - @Override - public InterIndex getInterIndex () - { - return interIndex; - } - - //--------------// - // getInterline // - //--------------// - @Override - public int getInterline () - { - return scale.getInterline(); - } - - //---------------// - // getLagManager // - //---------------// - @Override - public LagManager getLagManager () - { - return lagManager; - } - - //-------------// - // getLastPage // - //-------------// - /** - * Report the last page of the sheet, if any. - * - * @return the last page or null - */ - public Page getLastPage () - { - if (pages.isEmpty()) { - return null; - } - - return pages.get(pages.size() - 1); - } - - //--------------------// - // getLocationService // - //--------------------// - @Override - public SelectionService getLocationService () - { - return locationService; - } - - //----------// - // getPages // - //----------// - @Override - public List getPages () - { - return pages; - } - - //--------------------------// - // getPersistentIdGenerator // - //--------------------------// - @Override - public AtomicInteger getPersistentIdGenerator () - { - return lastPersistentId; - } - - //------------// - // getPicture // - //------------// - @Override - public Picture getPicture () - { - if (picture == null) { - BufferedImage img = getBook().loadSheetImage(stub.getNumber()); - - try { - setImage(img); - } catch (StepException ex) { - logger.warn("Error setting image id {}", stub.getNumber(), ex); - } - } - - return picture; - } - - //----------// - // getScale // - //----------// - @Override - public Scale getScale () - { - return scale; - } - - //----------// - // setImage // - //----------// - @Override - public final void setImage (BufferedImage image) - throws StepException - { - try { - picture = new Picture(this, image, locationService); - - if (OMR.gui != null) { - createPictureView(); - } - - done(Step.LOAD); - } catch (ImageFormatException ex) { - String msg = "Unsupported image format in file " + stub.getBook().getInputPath() - + "\n" + ex.getMessage(); - - if (OMR.gui != null) { - OMR.gui.displayWarning(msg); - } else { - logger.warn(msg); - } - - throw new StepException(ex); - } catch (Throwable ex) { - logger.warn("Error loading image", ex); - } - } - - //-----------// - // unmarshal // - //-----------// - /** - * Unmarshal the provided XML stream to allocate the corresponding sheet. - * - * @param in the input stream that contains the sheet in XML format. - * The stream is not closed by this method - * - * @return the allocated sheet. - * @exception JAXBException raised when unmarshalling goes wrong - */ - public static BasicSheet unmarshal (InputStream in) - throws JAXBException - { - Unmarshaller um = getJaxbContext().createUnmarshaller(); - - ///um.setListener(new Jaxb.UnmarshalLogger()); - BasicSheet sheet = (BasicSheet) um.unmarshal(in); - logger.debug("Sheet unmarshalled"); - - return sheet; - } - - //---------------// - // getSheetDelta // - //---------------// - @Override - public SheetDiff getSheetDelta () - { - return sheetDelta; - } - - //---------// - // getSkew // - //---------// - @Override - public Skew getSkew () - { - return skew; - } - - //-----------------// - // getStaffManager // - //-----------------// - @Override - public StaffManager getStaffManager () - { - return staffManager; - } - - //---------// - // getStub // - //---------// - @Override - public SheetStub getStub () - { - return stub; - } - - //------------------// - // getSymbolsEditor // - //------------------// - @Override - public SymbolsEditor getSymbolsEditor () - { - if (symbolsEditor == null) { - interController = new InterController(this); - symbolsEditor = new SymbolsEditor(this, getGlyphsController(), interController); - interController.setSymbolsEditor(symbolsEditor); - } - - return symbolsEditor; - } - - //------------------// - // getSystemManager // - //------------------// - @Override - public SystemManager getSystemManager () - { - return systemManager; - } - - //------------// - // getSystems // - //------------// - @Override - public List getSystems () - { - return systemManager.getSystems(); - } - - //----------// - // getWidth // - //----------// - @Override - public int getWidth () - { - return picture.getWidth(); - } - - //------------// - // hasPicture // - //------------// - @Override - public boolean hasPicture () - { - return picture != null; - } - - //-------// - // print // - //-------// - @Override - public void print (Path sheetPrintPath) - { - // Actually write the PDF - try { - Path parent = sheetPrintPath.getParent(); - - if (!Files.exists(parent)) { - Files.createDirectories(parent); - } - - new BookPdfOutput(getBook(), sheetPrintPath.toFile()).write(this); - logger.info("Sheet printed to {}", sheetPrintPath); - } catch (Exception ex) { - logger.warn("Cannot print sheet to " + sheetPrintPath + " " + ex, ex); - } - } - - //-------------// - // renderItems // - //-------------// - @Override - public void renderItems (Graphics2D g) - { - if (OMR.gui != null) { - for (ItemRenderer renderer : itemRenderers) { - renderer.renderItems(g); - } - } - } - - //--------// - // sample // - //--------// - @Override - public void sample () - { - final Book book = getBook(); - final SampleRepository repository = book.getSpecificSampleRepository(); - final SampleSheet sampleSheet = repository.findSampleSheet(this); - - for (SystemInfo system : getSystems()) { - SIGraph sig = system.getSig(); - - for (Inter inter : sig.vertexSet()) { - Shape shape = inter.getShape(); - Staff staff = inter.getStaff(); - Glyph glyph = inter.getGlyph(); - - if ((shape != null) && (staff != null) && (glyph != null)) { - Double pitch = (inter instanceof AbstractPitchedInter) - ? ((AbstractPitchedInter) inter).getPitch() : null; - repository.addSample( - inter.getShape(), - glyph, - staff.getSpecificInterline(), - sampleSheet, - pitch); - } - } - } - } - - //----------// - // setScale // - //----------// - @Override - public void setScale (Scale scale) - { - this.scale = scale; - } - - //---------------// - // setSheetDelta // - //---------------// - public void setSheetDelta (SheetDiff sheetDelta) - { - this.sheetDelta = sheetDelta; - } - - //---------// - // setSkew // - //---------// - @Override - public void setSkew (Skew skew) - { - this.skew = skew; - } - - //-------// - // store // - //-------// - @Override - public void store (Path sheetFolder, - Path oldSheetFolder) - { - // Picture internals, if any - if (picture != null) { - try { - // Make sure the folder exists for sheet internals - Files.createDirectories(sheetFolder); - - // Save picture tables - picture.store(sheetFolder, oldSheetFolder); - } catch (IOException ex) { - logger.warn("IOException on storing " + this, ex); - } - } - - // Sheet structure (sheet#n.xml) - try { - Path structurePath = sheetFolder.resolve(getSheetFileName(stub.getNumber())); - Files.deleteIfExists(structurePath); - Files.createDirectories(sheetFolder); - Jaxb.marshal(this, structurePath, getJaxbContext()); - stub.setModified(false); - logger.info("Stored {}", structurePath); - } catch (Exception ex) { - logger.warn("Error in saving sheet structure " + ex, ex); - } - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - return "Sheet{" + getId() + "}"; - } - - //-------// - // reset // - //-------// - /** - * Reinitialize the sheet members, according to step needs. - * - * @param step the starting step - */ - void reset (Step step) - { - switch (step) { - case LOAD: - picture = null; - - // Fall-through! - case BINARY: - case SCALE: - scale = null; - - // Fall-through! - case GRID: - pages.clear(); - stub.clearPageRefs(); - skew = null; - - lagManager.setLag(Lags.HLAG, null); - lagManager.setLag(Lags.VLAG, null); - - staffManager.reset(); - systemManager.reset(); - glyphsController = null; - symbolsEditor = null; - - default: - } - - // Clear errors and history for this step - if (OMR.gui != null) { - getErrorsEditor().clearStep(step); - - if (interController != null) { - SwingUtilities.invokeLater( - new Runnable() - { - // This part is run on swing thread - @Override - public void run () - { - interController.clearHistory(); - } - }); - - } - } - } - - //----------------// - // getJaxbContext // - //----------------// - private static JAXBContext getJaxbContext () - throws JAXBException - { - // Lazy creation - if (jaxbContext == null) { - jaxbContext = JAXBContext.newInstance(BasicSheet.class); - } - - return jaxbContext; - } - - //------------------------// - // createGlyphsController // - //------------------------// - private void createGlyphsController () - { - GlyphsModel model = new GlyphsModel(this, getGlyphIndex().getEntityService()); - glyphsController = new GlyphsController(model); - } - - //------// - // done // - //------// - /** - * Remember that the provided step has been completed on the sheet. - * - * @param step the provided step - */ - private void done (Step step) - { - ((BasicStub) stub).done(step); - } - - //---------// - // getBook // - //---------// - private Book getBook () - { - return stub.getBook(); - } - - //----------------------// - // getGlyphIndexContent // - //----------------------// - /** - * Mean for JAXB marshalling only. - * - * @return collection of glyphs from glyphIndex.weakIndex - */ - @SuppressWarnings("unchecked") - @XmlElement(name = "glyph-index") - @XmlJavaTypeAdapter(GlyphListAdapter.class) - private ArrayList getGlyphIndexContent () - { - if (glyphIndex == null) { - return null; - } - - return glyphIndex.getEntities(); - } - - //-----------// - // getNumber // - //-----------// - /** Sheet 1-based number within book. */ - @XmlAttribute(name = "number") - private int getNumber () - { - return stub.getNumber(); - } - - //---------------------// - // getSheetPathSansExt // - //---------------------// - /** - * Report the path (sans extension) to which the sheet will be written. - *

                              - *
                            • If this sheet is the only one in the containing book, we use:
                              - * <book-name>
                            • - *
                            • If the book contains several sheets, we use:
                              - * <book-name>-sheet#<N>
                            • - *
                            - * - * @param bookPathSansExt the non-null bookPath (without extension) - * @return the sheet path (without extension) - */ - private Path getSheetPathSansExt (Path bookPathSansExt) - { - // Determine the output path (sans extension) for the provided sheet - // path/to/scores/Book (for a single-sheet book) - // path/to/scores/Book-sheet#N (for a multi-sheet book) - final Book book = getBook(); - - if (!book.isMultiSheet()) { - return bookPathSansExt; - } else { - final Integer offset = book.getOffset(); - final int globalNumber = stub.getNumber() + ((offset != null) ? offset : 0); - - return Paths.get(bookPathSansExt + OMR.SHEET_SUFFIX + globalNumber); - } - } - - //----------------// - // initTransients // - //----------------// - /** - * Initialize needed transient members. - * (which by definition have not been set by the unmarshalling). - * - * @param stub the related stub - */ - private void initTransients (SheetStub stub) - { - logger.debug("BasicSheet#{} initTransients", stub.getNumber()); - - this.stub = stub; - - // Update UI information if so needed - if (OMR.gui != null) { - locationService = new SelectionService("locationService", allowedEvents); - errorsEditor = new ErrorsEditor(this); - itemRenderers = new LinkedHashSet(); - addItemRenderer(staffManager); - } - - if (picture != null) { - picture.initTransients(this); - } - - if (glyphIndex != null) { - glyphIndex.initTransients(this); - } - - for (Page page : pages) { - page.initTransients(this); - } - - if (systemManager == null) { - systemManager = new SystemManager(this); - } else { - systemManager.initTransients(this); - } - - // systemManager - List systems = new ArrayList(); - - for (Page page : pages) { - for (SystemInfo system : page.getSystems()) { - system.initTransients(this, page); - systems.add(system); - - List systemStaves = new ArrayList(); - - for (Part part : system.getParts()) { - part.setSystem(system); - - for (Staff staff : part.getStaves()) { - staff.setPart(part); - systemStaves.add(staff); - } - } - - system.setStaves(systemStaves); - } - } - - systemManager.setSystems(systems); - - staffManager = new StaffManager(this); - - lagManager = new LagManager(this); - } - - //-----------// - // setBinary // - //-----------// - private void setBinary (RunTable binaryTable) - { - try { - picture = new Picture(this, binaryTable); - - if (OMR.gui != null) { - createBinaryView(); - } - - done(Step.LOAD); - done(Step.BINARY); - } finally { - } - } - - //----------------------// - // setGlyphIndexContent // - //----------------------// - /** - * Meant for JAXB unmarshalling only. - * - * @param glyphs collection of glyphs to feed to the glyphIndex.weakIndex - */ - @SuppressWarnings("unchecked") - private void setGlyphIndexContent (ArrayList glyphs) - { - getGlyphIndex().setEntities(glyphs); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //---------// - // Adapter // - //---------// - /** - * Meant for JAXB handling of Sheet interface. - */ - public static class Adapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public BasicSheet marshal (Sheet s) - { - return (BasicSheet) s; - } - - @Override - public Sheet unmarshal (BasicSheet s) - { - return s; - } - } - - //-----------// - // GlyphList // For glyphIndex (un)marshalling - //-----------// - private static class GlyphList - { - //~ Instance fields ------------------------------------------------------------------------ - - @XmlElement(name = "glyph") - public ArrayList glyphs; - - //~ Constructors --------------------------------------------------------------------------- - public GlyphList () - { - } - - public GlyphList (ArrayList glyphs) - { - this.glyphs = glyphs; - } - } - - //------------------// - // GlyphListAdapter // For glyphIndex (un)marshalling - //------------------// - private static class GlyphListAdapter - extends XmlAdapter> - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public GlyphList marshal (ArrayList glyphs) - throws Exception - { - return new GlyphList(glyphs); - } - - @Override - public ArrayList unmarshal (GlyphList list) - throws Exception - { - return list.glyphs; - } - } -} diff --git a/src/main/org/audiveris/omr/sheet/BasicStub.java b/src/main/org/audiveris/omr/sheet/BasicStub.java deleted file mode 100644 index b7fae6cf1..000000000 --- a/src/main/org/audiveris/omr/sheet/BasicStub.java +++ /dev/null @@ -1,1068 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// B a s i c S t u b // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet; - -import org.audiveris.omr.Main; -import org.audiveris.omr.OMR; -import static org.audiveris.omr.WellKnowns.LINE_SEPARATOR; -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.image.FilterDescriptor; -import org.audiveris.omr.image.FilterParam; -import org.audiveris.omr.log.LogUtil; -import org.audiveris.omr.run.RunTable; -import org.audiveris.omr.score.PageRef; -import org.audiveris.omr.sheet.Picture.TableKey; -import static org.audiveris.omr.sheet.Sheet.INTERNALS_RADIX; -import org.audiveris.omr.sheet.ui.SheetAssembly; -import org.audiveris.omr.sheet.ui.StubsController; -import org.audiveris.omr.step.ProcessingCancellationException; -import org.audiveris.omr.step.Step; -import org.audiveris.omr.step.StepException; -import org.audiveris.omr.step.ui.StepMonitoring; -import org.audiveris.omr.ui.Colors; -import org.audiveris.omr.util.Jaxb; -import org.audiveris.omr.util.Memory; -import org.audiveris.omr.util.Navigable; -import org.audiveris.omr.util.OmrExecutors; -import org.audiveris.omr.util.StopWatch; -import org.audiveris.omr.util.ZipFileSystem; -import org.audiveris.omr.util.param.Param; -import org.audiveris.omr.util.param.StringParam; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.image.BufferedImage; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import javax.annotation.PostConstruct; -import javax.swing.SwingUtilities; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlList; -import javax.xml.bind.annotation.adapters.XmlAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - -/** - * Class {@code BasicStub} is the implementation of SheetStub. - * - * @author Hervé Bitteur - */ -@XmlAccessorType(XmlAccessType.NONE) -public class BasicStub - implements SheetStub -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger( - BasicStub.class); - - //~ Instance fields ---------------------------------------------------------------------------- - // - // Persistent data - //---------------- - // - /** Index of sheet, counted from 1, in the image file. */ - @XmlAttribute(name = "number") - private final int number; - - /** Indicate a sheet that contains no music. */ - @XmlAttribute - @XmlJavaTypeAdapter(type = boolean.class, value = Jaxb.BooleanPositiveAdapter.class) - private boolean invalid; - - /** Handling of binarization filter parameter. */ - @XmlElement(name = "binarization") - @XmlJavaTypeAdapter(FilterParam.Adapter.class) - private FilterParam binarizationFilter; - - /** Handling of dominant language(s) for this sheet. */ - @XmlElement(name = "ocr-languages") - @XmlJavaTypeAdapter(StringParam.Adapter.class) - private StringParam ocrLanguages; - - /** Handling of processing switches for this sheet. */ - @XmlElement(name = "processing") - @XmlJavaTypeAdapter(ProcessingSwitches.Adapter.class) - private ProcessingSwitches switches; - - /** All steps already performed on this sheet. */ - @XmlList - @XmlElement(name = "steps") - private final EnumSet doneSteps = EnumSet.noneOf(Step.class); - - /** Pages references. */ - @XmlElement(name = "page") - private final List pageRefs = new ArrayList(); - - // Transient data - //--------------- - // - /** Processing lock. */ - private final Lock lock = new ReentrantLock(); - - /** Containing book. */ - @Navigable(false) - private Book book; - - /** Full sheet material, if any. */ - private volatile BasicSheet sheet; - - /** The step being performed on the sheet. */ - private volatile Step currentStep; - - /** Has sheet been modified, WRT its book data. */ - private boolean modified = false; - - /** Related assembly instance, if any. */ - private SheetAssembly assembly; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SheetStub} object. - * - * @param book the containing book instance - * @param number the 1-based sheet number within the containing book - */ - public BasicStub (Book book, - int number) - { - this.number = number; - - initTransients(book); - } - - /** - * No-arg constructor meant for JAXB. - */ - private BasicStub () - { - this.number = 0; - } - - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // addPageRef // - //------------// - @Override - public void addPageRef (PageRef pageRef) - { - pageRefs.add(pageRef); - } - - //---------------// - // clearPageRefs // - //---------------// - @Override - public void clearPageRefs () - { - // Clear pageRefs in this stub - pageRefs.clear(); - - // Clear pageRefs in related book store if any - } - - //-------// - // close // - //-------// - @Override - public void close () - { - // If no stub is left, force book closing - if (!book.isClosing()) { - if (!book.getStubs().isEmpty()) { - logger.info("Sheet closed"); - } else { - book.close(); - } - } - } - - //-----------------// - // decideOnRemoval // - //-----------------// - @Override - public void decideOnRemoval (String msg, - boolean dummy) - throws StepException - { - if (dummy) { - invalidate(); - throw new StepException("Dummy decision"); - } - - logger.warn(msg.replaceAll(LINE_SEPARATOR, " ")); - - if (OMR.gui != null) { - StubsController.invokeSelect(this); - } - - if ((OMR.gui == null) - || (OMR.gui.displayConfirmation(msg + LINE_SEPARATOR + "OK for discarding this sheet?"))) { - invalidate(); - - if (book.isMultiSheet()) { - close(); - throw new StepException("Sheet removed"); - } else { - throw new StepException("Sheet ignored"); - } - } - } - - //------// - // done // - //------// - /** - * Remember that the provided step has been completed on the sheet. - * - * @param step the provided step - */ - public final void done (Step step) - { - doneSteps.add(step); - } - - //-------------// - // getAssembly // - //-------------// - @Override - public SheetAssembly getAssembly () - { - return assembly; - } - - //-----------------------// - // getBinarizationFilter // - //-----------------------// - @Override - public FilterParam getBinarizationFilter () - { - if (binarizationFilter == null) { - binarizationFilter = new FilterParam(); - binarizationFilter.setParent(FilterDescriptor.defaultFilter); - } - - return binarizationFilter; - } - - //---------// - // getBook // - //---------// - @Override - public Book getBook () - { - return book; - } - - //----------------// - // getCurrentStep // - //----------------// - @Override - public Step getCurrentStep () - { - return currentStep; - } - - //-----------------// - // getFirstPageRef // - //-----------------// - @Override - public PageRef getFirstPageRef () - { - if (pageRefs.isEmpty()) { - return null; - } - - return pageRefs.get(0); - } - - //-------// - // getId // - //-------// - @Override - public String getId () - { - if (book.isMultiSheet()) { - return book.getRadix() + "#" + number; - } else { - return book.getRadix(); - } - } - - //----------------// - // getLastPageRef // - //----------------// - @Override - public PageRef getLastPageRef () - { - if (pageRefs.isEmpty()) { - return null; - } - - return pageRefs.get(pageRefs.size() - 1); - } - - //---------------// - // getLatestStep // - //---------------// - @Override - public Step getLatestStep () - { - Step latest = null; - - for (Step step : Step.values()) { - if (isDone(step)) { - latest = step; - } - } - - return latest; - } - - //---------// - // getLock // - //---------// - @Override - public Lock getLock () - { - return lock; - } - - //--------// - // getNum // - //--------// - @Override - public String getNum () - { - if (book.isMultiSheet()) { - return "#" + number; - } - - return ""; - } - - //-----------// - // getNumber // - //-----------// - @Override - public int getNumber () - { - return number; - } - - //-----------------// - // getOcrLanguages // - //-----------------// - @Override - public Param getOcrLanguages () - { - if (ocrLanguages == null) { - ocrLanguages = new StringParam(); - ocrLanguages.setParent(book.getOcrLanguages()); - } - - return ocrLanguages; - } - - //-------------// - // getPageRefs // - //-------------// - /** - * @return the pageRefs - */ - @Override - public List getPageRefs () - { - return pageRefs; - } - - //-----------------------// - // getProcessingSwitches // - //-----------------------// - @Override - public ProcessingSwitches getProcessingSwitches () - { - if (switches == null) { - switches = new ProcessingSwitches(); - switches.setParent(book.getProcessingSwitches()); - } - - return switches; - } - - //----------// - // getSheet // - //----------// - @Override - public BasicSheet getSheet () - { - if (sheet == null) { - synchronized (this) { - // We have to recheck sheet, which may have just been allocated - if (sheet == null) { - if (SwingUtilities.isEventDispatchThread()) { - logger.warn("XXXX getSheet called on EDT XXXX"); - } - - // Actually load the sheet - if (!isDone(Step.LOAD)) { - // LOAD not yet performed: load from book image file - try { - sheet = new BasicSheet(this, (BufferedImage) null); - } catch (StepException ignored) { - logger.info("Could not load sheet for stub {}", this); - } - } else { - // LOAD already performed: load from book file - StopWatch watch = new StopWatch("Load Sheet " + this); - - try { - Path sheetFile = null; - watch.start("unmarshal"); - - // Open the book file system - try { - book.getLock().lock(); - sheetFile = book.openSheetFolder(number).resolve( - BasicSheet.getSheetFileName(number)); - - InputStream is = Files.newInputStream( - sheetFile, - StandardOpenOption.READ); - sheet = BasicSheet.unmarshal(is); - - // Close the stream as well as the book file system - is.close(); - sheetFile.getFileSystem().close(); - } finally { - book.getLock().unlock(); - } - - // Complete sheet reload - watch.start("afterReload"); - sheet.afterReload(this); - logger.info("Loaded {}", sheetFile); - } catch (Exception ex) { - logger.warn("Error in loading sheet structure " + ex, ex); - logger.info("Trying to restart from binary"); - resetToBinary(); - } finally { - if (constants.printWatch.isSet()) { - watch.print(); - } - } - } - } - } - } - - return sheet; - } - - //----------// - // hasSheet // - //----------// - @Override - public boolean hasSheet () - { - return sheet != null; - } - - //------------// - // invalidate // - //------------// - @Override - public void invalidate () - { - invalid = Boolean.TRUE; - - book.updateScores(this); - - pageRefs.clear(); - setModified(true); - - if (OMR.gui != null) { - StubsController.getInstance().markTab(this, Colors.SHEET_INVALID); - } - - logger.info("Sheet {} flagged as invalid.", getId()); - } - - //--------// - // isDone // - //--------// - @Override - public boolean isDone (Step step) - { - return doneSteps.contains(step); - } - - //------------// - // isModified // - //------------// - @Override - public boolean isModified () - { - return modified; - } - - //---------// - // isValid // - //---------// - @Override - public boolean isValid () - { - return !invalid; - } - - //-----------// - // reachStep // - //-----------// - /** - * {@inheritDoc} - *

                            - * Each needed step is performed sequentially, guarded by a timeout. - */ - @Override - public boolean reachStep (Step target, - boolean force) - { - final StubsController ctrl = (OMR.gui != null) ? StubsController.getInstance() : null; - final StopWatch watch = new StopWatch("reachStep " + target); - EnumSet neededSteps = null; - boolean ok = false; - getLock().lock(); // Wait for completion of early processing if any - logger.debug("reachStep got lock on {}", this); - - try { - final Step latestStep = getLatestStep(); - - if (force && (target.compareTo(latestStep) <= 0)) { - resetToBinary(); - } - - neededSteps = getNeededSteps(target); - - if (neededSteps.isEmpty()) { - return true; - } - - logger.debug("Sheet#{} scheduling {}", number, neededSteps); - StepMonitoring.notifyStart(); - - if (ctrl != null) { - ctrl.markTab(this, Colors.SHEET_BUSY); - } - - for (final Step step : neededSteps) { - watch.start(step.name()); - StepMonitoring.notifyMsg(step.toString()); - logger.debug("reachStep {} towards {}", step, target); - doOneStep(step); - } - - ok = true; - } catch (ProcessingCancellationException pce) { - throw pce; - } catch (StepException ignored) { - logger.info("StepException detected in " + neededSteps); - } catch (ExecutionException ex) { - // A StepException may have been wrapped into an ExecutionException - if (ex.getCause() instanceof StepException) { - logger.info("StepException cause detected in " + neededSteps); - } else { - logger.warn("Error in performing {} {}", neededSteps, ex.toString(), ex); - } - } catch (Exception ex) { - logger.warn("Error in performing {} {}", neededSteps, ex.toString(), ex); - } finally { - StepMonitoring.notifyStop(); - - if (constants.printWatch.isSet()) { - watch.print(); - } - - logger.debug("reachStep releasing lock on {}", this); - getLock().unlock(); - } - - if (ctrl != null) { - ctrl.markTab(this, ok ? Colors.SHEET_OK : Colors.SHEET_NOT_OK); - } - - return ok; - } - - //-------// - // reset // - //-------// - @Override - public void reset () - { - doReset(); - logger.info("Sheet#{} reset as valid.", number); - - if (OMR.gui != null) { - try { - Runnable runnable = new Runnable() - { - @Override - public void run () - { - StubsController.getInstance().reDisplay(BasicStub.this); - } - }; - - if (SwingUtilities.isEventDispatchThread()) { - runnable.run(); - } else { - SwingUtilities.invokeAndWait(runnable); - } - } catch (Throwable ex) { - logger.warn("Could not reset {}", ex.toString(), ex); - } - } - } - - //---------------// - // resetToBinary // - //---------------// - @Override - public void resetToBinary () - { - try { - // Avoid loading sheet just to reset to binary: - // If sheet is available, use its picture.getTable() - // Otherwise, load it directly from binary.xml on disk - RunTable binaryTable = null; - - if (hasSheet()) { - logger.debug("Getting BINARY from sheet"); - binaryTable = getSheet().getPicture().getTable(TableKey.BINARY); - } - - if (binaryTable == null) { - logger.debug("Loading BINARY from disk"); - binaryTable = new RunTableHolder(TableKey.BINARY).getData(this); - } - - doReset(); - sheet = new BasicSheet(this, binaryTable); - logger.info("Sheet#{} reset to BINARY.", number); - } catch (Throwable ex) { - logger.warn("Could not reset to BINARY {}", ex.toString(), ex); - reset(); - } - } - - //----------------// - // setCurrentStep // - //----------------// - public void setCurrentStep (Step step) - { - currentStep = step; - } - - //-------------// - // setModified // - //-------------// - @Override - public void setModified (boolean modified) - { - this.modified = modified; - - if (modified) { - book.setModified(true); - book.setDirty(true); - } - } - - //------------// - // storeSheet // - //------------// - @Override - public void storeSheet () - throws Exception - { - if (modified) { - book.getLock().lock(); - - Path bookPath = BookManager.getDefaultSavePath(book); - - try { - Path root = ZipFileSystem.open(bookPath); - book.storeBookInfo(root); // Book info (book.xml) - - Path sheetFolder = root.resolve(INTERNALS_RADIX + getNumber()); - sheet.store(sheetFolder, null); - root.getFileSystem().close(); - } finally { - book.getLock().unlock(); - } - } - } - - //-----------// - // swapSheet // - //-----------// - @Override - public void swapSheet () - { - try { - if (isModified()) { - logger.info("{} storing", this); - storeSheet(); - } - - if (sheet != null) { - logger.info("{} disposed", sheet); - sheet = null; - Memory.gc(); // Trigger a garbage collection... - } - - if (OMR.gui != null) { - SwingUtilities.invokeLater( - new Runnable() - { - @Override - public void run () - { - // Gray out the related tab - StubsController ctrl = StubsController.getInstance(); - ctrl.markTab(BasicStub.this, Colors.SHEET_NOT_LOADED); - - // Close stub UI, if any - if (assembly != null) { - assembly.reset(); - } - } - }); - } - } catch (Exception ex) { - logger.warn("Error swapping sheet", ex); - } - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - return "Stub#" + number; - } - - //------------// - // transcribe // - //------------// - @Override - public boolean transcribe () - { - return reachStep(Step.last(), false); - } - - //----------------// - // afterUnmarshal // - //----------------// - /** - * Called after all the properties (except IDREF) are unmarshalled - * for this object, but before this object is set to the parent object. - * All non-persistent members are null. - */ - @PostConstruct // Don't remove this method, invoked by JAXB through reflection - - private void afterUnmarshal (Unmarshaller um, - Object parent) - { - initTransients((Book) parent); - } - - //---------------// - // beforeMarshal // - //---------------// - @SuppressWarnings("unused") - private void beforeMarshal (Marshaller m) - { - if ((binarizationFilter != null) && !binarizationFilter.isSpecific()) { - binarizationFilter = null; - } - - if ((ocrLanguages != null) && !ocrLanguages.isSpecific()) { - ocrLanguages = null; - } - - if ((switches != null) && switches.isEmpty()) { - switches = null; - } - } - - //-----------// - // doOneStep // - //-----------// - /** - * Do just one specified step, synchronously, with display of related UI if any. - *

                            - * Step duration is guarded by a timeout, so that processing cannot get blocked infinitely. - * - * @param step the step to perform - * @throws Exception - */ - private void doOneStep (final Step step) - throws Exception - { - final int timeout = Main.getSheetStepTimeOut(); - Future future = null; - - try { - // Make sure sheet is available - if (!hasSheet()) { - getSheet(); - } - - // Implement a timeout for this step on the stub - future = OmrExecutors.getCachedLowExecutor().submit( - new Callable() - { - @Override - public Void call () - throws Exception - { - LogUtil.start(BasicStub.this); - - try { - setCurrentStep(step); - StepMonitoring.notifyStep(BasicStub.this, step); // Start monitoring - setModified(true); // At beginning of processing - sheet.reset(step); // Reset sheet relevant data - step.doit(sheet); // Standard processing on an existing sheet - done(step); // Full completion - } finally { - LogUtil.stopStub(); - } - - return null; - } - }); - - future.get(timeout, TimeUnit.SECONDS); - - // At end of each step, save sheet to disk? - if ((OMR.gui == null) && Main.saveSheetOnEveryStep()) { - logger.debug("calling storeSheet"); - storeSheet(); - } - } catch (TimeoutException tex) { - logger.warn("Timeout {} seconds for step {}", timeout, step, tex); - - // Signal the on-going step processing to stop (if possible) - if (future != null) { - future.cancel(true); - } - - throw new ProcessingCancellationException(tex); - } finally { - setCurrentStep(null); - StepMonitoring.notifyStep(this, step); // Stop monitoring - } - } - - //---------// - // doReset // - //---------// - private void doReset () - { - doneSteps.clear(); - pageRefs.clear(); - invalid = false; - sheet = null; - - if (assembly != null) { - assembly.reset(); - } - - setModified(true); - } - - //----------------// - // getNeededSteps // - //----------------// - private EnumSet getNeededSteps (Step target) - { - EnumSet neededSteps = EnumSet.noneOf(Step.class); - - // Add all needed steps - for (Step step : EnumSet.range(Step.first(), target)) { - if (!isDone(step)) { - neededSteps.add(step); - } - } - - return neededSteps; - } - - //----------------// - // initTransients // - //----------------// - /** - * Initialize needed transient members. - * (which by definition have not been set by the unmarshalling). - * - * @param book the containing book - */ - private void initTransients (Book book) - { - try { - LogUtil.start(book); - - logger.trace("{} initTransients", this); - this.book = book; - - if (binarizationFilter != null) { - binarizationFilter.setParent(book.getBinarizationFilter()); - } - - if (ocrLanguages != null) { - ocrLanguages.setParent(book.getOcrLanguages()); - } - - if (switches != null) { - switches.setParent(book.getProcessingSwitches()); - } - - if (OMR.gui != null) { - assembly = new SheetAssembly(this); - } - } finally { - LogUtil.stopBook(); - } - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //---------// - // Adapter // - //---------// - /** - * Meant for JAXB handling of SheetStub interface. - */ - public static class Adapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public BasicStub marshal (SheetStub s) - { - return (BasicStub) s; - } - - @Override - public SheetStub unmarshal (BasicStub s) - { - return s; - } - } - - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print out the stop watch for sheet loading"); - } - - //-------------------// - // OcrSheetLanguages // - //-------------------// - private static final class OcrSheetLanguages - extends Param - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public boolean setSpecific (String specific) - { - // Normalize - if ((specific != null) && specific.isEmpty()) { - specific = null; - } - - return super.setSpecific(specific); - } - - //~ Inner Classes -------------------------------------------------------------------------- - /** - * JAXB adapter to mimic XmlValue. - */ - public static class Adapter - extends XmlAdapter - { - //~ Methods ---------------------------------------------------------------------------- - - @Override - public String marshal (OcrSheetLanguages val) - throws Exception - { - if (val == null) { - return null; - } - - return val.getSpecific(); - } - - @Override - public OcrSheetLanguages unmarshal (String str) - throws Exception - { - OcrSheetLanguages ol = new OcrSheetLanguages(); - ol.setSpecific(str); - - return ol; - } - } - } -} diff --git a/src/main/org/audiveris/omr/sheet/Book.java b/src/main/org/audiveris/omr/sheet/Book.java index c5ecdef7c..801d8add7 100644 --- a/src/main/org/audiveris/omr/sheet/Book.java +++ b/src/main/org/audiveris/omr/sheet/Book.java @@ -21,26 +21,87 @@ // package org.audiveris.omr.sheet; +import org.audiveris.omr.OMR; +import org.audiveris.omr.ProgramId; +import org.audiveris.omr.WellKnowns; +import org.audiveris.omr.classifier.Annotations; import org.audiveris.omr.classifier.SampleRepository; +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.image.FilterDescriptor; import org.audiveris.omr.image.FilterParam; +import org.audiveris.omr.image.ImageLoading; +import org.audiveris.omr.log.LogUtil; +import org.audiveris.omr.run.RunTable; +import org.audiveris.omr.score.OpusExporter; import org.audiveris.omr.score.Page; +import org.audiveris.omr.score.PageRef; import org.audiveris.omr.score.Score; +import org.audiveris.omr.score.ScoreExporter; +import org.audiveris.omr.score.ScoreReduction; +import org.audiveris.omr.score.ui.BookPdfOutput; +import static org.audiveris.omr.sheet.Sheet.INTERNALS_RADIX; +import org.audiveris.omr.sheet.rhythm.Voices; +import org.audiveris.omr.sheet.ui.BookBrowser; +import org.audiveris.omr.sheet.ui.StubsController; +import org.audiveris.omr.step.ProcessingCancellationException; import org.audiveris.omr.step.Step; +import org.audiveris.omr.step.StepException; +import org.audiveris.omr.step.ui.StepMonitoring; +import org.audiveris.omr.text.Language; +import org.audiveris.omr.util.FileUtil; +import org.audiveris.omr.util.Jaxb; +import org.audiveris.omr.util.Memory; +import org.audiveris.omr.util.OmrExecutors; +import org.audiveris.omr.util.StopWatch; +import org.audiveris.omr.util.ZipFileSystem; import org.audiveris.omr.util.param.Param; +import org.audiveris.omr.util.param.StringParam; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.awt.image.BufferedImage; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.zip.ZipOutputStream; import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** - * Interface {@code Book} is the root class for handling a physical set of image input + * Class {@code Book} is the root class for handling a physical set of image input * files, resulting in one or several logical MusicXML scores. *

                            * A book instance generally corresponds to an input file containing one or several images, each @@ -56,7 +117,8 @@ * Methods are organized as follows: *

                            *
                            Administration
                            - *
                              + *
                              + *
                                *
                              • {@link #getInputPath}
                              • *
                              • {@link #getAlias}
                              • *
                              • {@link #setAlias}
                              • @@ -69,13 +131,18 @@ *
                              • {@link #isModified}
                              • *
                              • {@link #setModified}
                              • *
                              • {@link #close}
                              • + *
                              • {@link #closeFileSystem}
                              • *
                              • {@link #isClosing}
                              • *
                              • {@link #setClosing}
                              • *
                              • {@link #getLock}
                              • - *
                              + *
                            • {@link #openBookFile}
                            • + *
                            • {@link #openSheetFolder}
                            • + *
                            + *
                            * *
                            SheetStubs
                            - *
                              + *
                              + *
                                *
                              • {@link #createStubs}
                              • *
                              • {@link #createStubsTabs}
                              • *
                              • {@link #loadSheetImage}
                              • @@ -86,38 +153,49 @@ *
                              • {@link #getValidStubs}
                              • *
                              • {@link #removeStub}
                              • *
                              • {@link #hideInvalidStubs}
                              • + *
                              • {@link #ids}
                              • *
                              • {@link #swapAllSheets}
                              • - *
                              + *
                            + *
                            * *
                            Parameters
                            - *
                              + *
                              + *
                                *
                              • {@link #getBinarizationFilter}
                              • *
                              • {@link #getOcrLanguages}
                              • - *
                              + *
                            • {@link #getProcessingSwitches}
                            • + *
                            + *
                            * *
                            Transcription
                            - *
                              + *
                              + *
                                *
                              • {@link #reset}
                              • *
                              • {@link #resetToBinary}
                              • *
                              • {@link #transcribe}
                              • *
                              • {@link #reachBookStep}
                              • *
                              • {@link #updateScores}
                              • *
                              • {@link #reduceScores}
                              • + *
                              • {@link #getScore}
                              • *
                              • {@link #getScores}
                              • - *
                              + *
                            + *
                            * - *
                            Symbols
                            - *
                              - *
                            • {@link #annotate}
                            • + *
                              Samples
                              + *
                              + *
                                *
                              • {@link #getSampleRepository}
                              • *
                              • {@link #getSpecificSampleRepository}
                              • *
                              • {@link #hasAllocatedRepository}
                              • *
                              • {@link #hasSpecificRepository}
                              • *
                              • {@link #sample}
                              • - *
                              + *
                            • {@link #annotate}
                            • + *
                            + *
                            * *
                            Artifacts
                            - *
                              + *
                              + *
                                *
                              • {@link #getBrowserFrame}
                              • *
                              • {@link #getExportPathSansExt}
                              • *
                              • {@link #setExportPathSansExt}
                              • @@ -126,51 +204,374 @@ *
                              • {@link #setPrintPath}
                              • *
                              • {@link #print}
                              • *
                              • {@link #getBookPath}
                              • - *
                              • {@link #store}
                              • + *
                              • {@link #store()}
                              • + *
                              • {@link #store(java.nio.file.Path, boolean)}
                              • *
                              • {@link #storeBookInfo}
                              • - *
                              • {@link #openSheetFolder}
                              • - *
                              + *
                            • {@link #loadBook}
                            • + *
                            + *
                            *
                            - *

                            + * * Book detals UML * * @author Hervé Bitteur */ -@XmlJavaTypeAdapter(BasicBook.Adapter.class) -public interface Book +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = "book") +public class Book { - //~ Static fields/initializers ----------------------------------------------------------------- + + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(Book.class); /** File name for book internals in book file system: {@value}. */ - static final String BOOK_INTERNALS = "book.xml"; + public static final String BOOK_INTERNALS = "book.xml"; + + /** Un/marshalling context for use with JAXB. */ + private static volatile JAXBContext jaxbContext; + + // Persistent data + //---------------- + // + /** Related Audiveris version that last operated on this book. */ + @XmlAttribute(name = "software-version") + private String version; + + /** Related Audiveris build that last operated on this book. */ + @XmlAttribute(name = "software-build") + private String build; + + /** Sub books, if any. */ + @XmlElement(name = "sub-books") + private final List subBooks; + + /** Book alias, if any. */ + @XmlAttribute(name = "alias") + private String alias; + + /** Input path of the related image(s) file, if any. */ + @XmlAttribute(name = "path") + @XmlJavaTypeAdapter(Jaxb.PathAdapter.class) + private final Path path; + + /** Sheet offset of image file with respect to full work, if any. */ + @XmlAttribute(name = "offset") + private Integer offset; + + /** Indicate if the book scores must be updated. */ + @XmlAttribute(name = "dirty") + @XmlJavaTypeAdapter(type = boolean.class, value = Jaxb.BooleanPositiveAdapter.class) + private boolean dirty = false; + + /** Handling of binarization filter parameter. */ + @XmlElement(name = "binarization") + @XmlJavaTypeAdapter(FilterParam.Adapter.class) + private FilterParam binarizationFilter; + + /** Handling of dominant language(s) for this book. */ + @XmlElement(name = "ocr-languages") + @XmlJavaTypeAdapter(StringParam.Adapter.class) + private StringParam ocrLanguages; + + /** Handling of processing switches for this book. */ + @XmlElement(name = "processing") + @XmlJavaTypeAdapter(ProcessingSwitches.Adapter.class) + private ProcessingSwitches switches; + + /** Sequence of all sheets stubs got from image file. */ + @XmlElement(name = "sheet") + private final List stubs = new ArrayList<>(); + + /** Logical scores for this book. */ + @XmlElement(name = "score") + private final List scores = new ArrayList<>(); + + // Transient data + //--------------- + // + /** Project file lock. */ + private final Lock lock = new ReentrantLock(); + + /** The related file radix (file name without extension). */ + private String radix; + + /** File path where the book is kept. */ + private Path bookPath; + + /** File path where the book is printed. */ + private Path printPath; + + /** File path (without extension) where the MusicXML output is stored. */ + private Path exportPathSansExt; + + /** Browser on this book. */ + private BookBrowser bookBrowser; + + /** Flag to indicate this book is being closed. */ + private volatile boolean closing; + + /** Set if the book itself has been modified. */ + private boolean modified = false; + + /** Book-level sample repository. */ + private SampleRepository repository; + + /** + * Create a Book with a path to an input images file. + * + * @param path the input image path (which may contain several images) + */ + public Book (Path path) + { + Objects.requireNonNull(path, "Trying to create a Book with null path"); + + this.path = path; + subBooks = null; + + initTransients(FileUtil.getNameSansExtension(path), null); + } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Create a meta Book, to be later populated with sub-books. + *

                            + * NOTA: This meta-book feature is not yet in use. + * + * @param nameSansExt a name (sans extension) for this book + */ + public Book (String nameSansExt) + { + Objects.requireNonNull(nameSansExt, "Trying to create a meta Book with null name"); + + path = null; + subBooks = new ArrayList<>(); + + initTransients(nameSansExt, null); + } + + /** + * No-arg constructor needed by JAXB. + */ + public Book () + { + path = null; + subBooks = null; + } + + //----------// + // annotate // + //----------// /** * Write the book symbol annotations. + *

                            + * Generate a whole zip file, in which each valid sheet is represented by a pair + * composed of sheet image (.png) and sheet annotations (.xml). */ - void annotate (); + public void annotate () + { + Path root = null; + + try { + final Path bookFolder = BookManager.getDefaultBookFolder(this); + final Path path = bookFolder.resolve( + getRadix() + Annotations.BOOK_ANNOTATIONS_EXTENSION); + root = ZipFileSystem.create(path); + for (SheetStub stub : getValidStubs()) { + try { + LogUtil.start(stub); + + final Path sheetFolder = root.resolve(INTERNALS_RADIX + stub.getNumber()); + final Sheet sheet = stub.getSheet(); + sheet.annotate(sheetFolder); + } catch (Exception ex) { + logger.warn("Error annotating {} {}", stub, ex.toString(), ex); + } finally { + LogUtil.stopStub(); + } + } + + logger.info("Book annotated as {}", path); + } catch (IOException ex) { + logger.warn("Error annotating book {} {}", this, ex.toString(), ex); + } finally { + if (root != null) { + try { + root.getFileSystem().close(); + } catch (IOException ignored) { + } + } + } + } + + //-------// + // close // + //-------// /** * Delete this book instance, as well as its related resources. */ - void close (); + public void close () + { + setClosing(true); + + // Close contained stubs/sheets + if (OMR.gui != null) { + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run () + { + try { + LogUtil.start(Book.this); + + for (SheetStub stub : new ArrayList<>(stubs)) { + LogUtil.start(stub); + + // Close stub UI, if any + if (stub.getAssembly() != null) { + StubsController.getInstance().deleteAssembly(stub); + stub.getAssembly().close(); + } + } + } finally { + LogUtil.stopBook(); + } + } + }); + } + // Close browser if any + if (bookBrowser != null) { + bookBrowser.close(); + } + + // Remove from OMR instances + OMR.engine.removeBook(this); + + // Time for some cleanup... + Memory.gc(); + + logger.debug("Book closed."); + } + + //-------------// + // createStubs // + //-------------// /** * Create as many sheet stubs as there are images in the input image file. * A created stub is nearly empty, the related image will have to be loaded later. * * @param sheetNumbers set of sheet numbers (1-based) explicitly included, null for all */ - void createStubs (SortedSet sheetNumbers); + public void createStubs (SortedSet sheetNumbers) + { + ImageLoading.Loader loader = ImageLoading.getLoader(path); + + if (loader != null) { + final int imageCount = loader.getImageCount(); + loader.dispose(); + logger.info("{} sheet{} in {}", imageCount, ((imageCount > 1) ? "s" : ""), path); + + if (sheetNumbers == null) { + sheetNumbers = new TreeSet<>(); + } + if (sheetNumbers.isEmpty()) { + for (int i = 1; i <= imageCount; i++) { + sheetNumbers.add(i); + } + } + + for (int num : sheetNumbers) { + stubs.add(new SheetStub(this, num)); + } + } + } + + //-----------------// + // createStubsTabs // + //-----------------// /** * Insert stubs assemblies in UI tabbed pane. * GUI will focus on first valid stub, unless a stub number is provided. * * @param focus the stub number to focus upon, or null */ - void createStubsTabs (Integer focus); + public void createStubsTabs (final Integer focus) + { + Runnable doRun = new Runnable() + { + @Override + public void run () + { + try { + LogUtil.start(Book.this); + + final StubsController controller = StubsController.getInstance(); + + // Determine which stub should get the focus + SheetStub focusStub = null; + + if (focus != null) { + if ((focus > 0) && (focus <= stubs.size())) { + focusStub = stubs.get(focus - 1); + } else { + logger.warn("Illegal focus sheet id: {}", focus); + } + } + + if (focusStub == null) { + focusStub = getFirstValidStub(); // Focus on first valid stub, if any + + if (focusStub == null) { + logger.info("No valid sheet in {}", this); + } + } + + // Allocate one tab per stub, beginning by focusStub if any + Integer focusIndex = null; + + if (focusStub != null) { + controller.addAssembly(focusStub.getAssembly(), null); + focusIndex = controller.getIndex(focusStub); + + if (focusIndex == -1) { + focusIndex = null; // Safer + } + } + + for (SheetStub stub : stubs) { + if (focusIndex == null) { + controller.addAssembly(stub.getAssembly(), null); + } else if (stub != focusStub) { + if (stub.getNumber() < focusStub.getNumber()) { + controller.addAssembly( + stub.getAssembly(), + controller.getLastIndex()); + } else { + controller.addAssembly(stub.getAssembly(), null); + } + } + } + + controller.adjustStubTabs(Book.this); + } finally { + LogUtil.stopBook(); + } + } + }; + try { + SwingUtilities.invokeAndWait(doRun); + } catch (InterruptedException | + InvocationTargetException ex) { + logger.warn("Error in createStubsTabs, {}", ex, ex); + } + } + + //--------// + // export // + //--------// /** * Export this book scores using MusicXML format. *

                            @@ -184,213 +585,678 @@ public interface Book *

                          • If we use opus, everything goes into "BOOK.opus.mxl" as a single container file.
                          • *
                          */ - void export (); + public void export () + { + // Make sure material is ready? + transcribe(); + + // path/to/scores/Book + final Path bookPathSansExt = BookManager.getActualPath( + getExportPathSansExt(), + BookManager.getDefaultExportPathSansExt(this)); + final boolean compressed = BookManager.useCompression(); + final String ext = compressed ? OMR.COMPRESSED_SCORE_EXTENSION : OMR.SCORE_EXTENSION; + final boolean sig = BookManager.useSignature(); + + // Export each movement score + String bookName = bookPathSansExt.getFileName().toString(); + final boolean multiMovements = scores.size() > 1; + + if (BookManager.useOpus()) { + // Export the book as one opus file + final Path opusPath = bookPathSansExt.resolveSibling(bookName + OMR.OPUS_EXTENSION); + + try { + new OpusExporter(this).export(opusPath, bookName, sig); + } catch (Exception ex) { + logger.warn("Could not export opus " + opusPath, ex); + } + } else { + // Export the book as one or several movement files + for (Score score : scores) { + final String scoreName = (!multiMovements) ? bookName + : (bookName + OMR.MOVEMENT_EXTENSION + score.getId()); + final Path scorePath = bookPathSansExt.resolveSibling(scoreName + ext); + + try { + new ScoreExporter(score).export(scorePath, scoreName, sig, compressed); + } catch (Exception ex) { + logger.warn("Could not export score " + scoreName, ex); + } + } + } + } + //----------// + // getAlias // + //----------// /** * Report the book name alias if any. * * @return book alias or null */ - String getAlias (); + public String getAlias () + { + return alias; + } + + //----------// + // setAlias // + //----------// + /** + * Set the book alias + * + * @param alias the book alias + */ + public void setAlias (String alias) + { + this.alias = alias; + radix = alias; + } + //-----------------------// + // getBinarizationFilter // + //-----------------------// /** * Report the binarization filter defined at book level. * * @return the filter parameter */ - FilterParam getBinarizationFilter (); + public FilterParam getBinarizationFilter () + { + if (binarizationFilter == null) { + binarizationFilter = new FilterParam(); + binarizationFilter.setParent(FilterDescriptor.defaultFilter); + } + return binarizationFilter; + } + + //-------------// + // getBookPath // + //-------------// /** * Report where the book is kept. * - * @return the book path + * @return the path to book .omr file */ - Path getBookPath (); + public Path getBookPath () + { + return bookPath; + } + //-----------------// + // getBrowserFrame // + //-----------------// /** * Create a dedicated frame, where book hierarchy can be browsed interactively. * * @return the created frame */ - JFrame getBrowserFrame (); + public JFrame getBrowserFrame () + { + if (bookBrowser == null) { + // Build the BookBrowser on the score + bookBrowser = new BookBrowser(this); + } + + return bookBrowser.getFrame(); + } + //----------------------// + // getExportPathSansExt // + //----------------------// /** * Report the path (without extension) where book is to be exported. * * @return the book export path without extension, or null */ - Path getExportPathSansExt (); + public Path getExportPathSansExt () + { + return exportPathSansExt; + } + + //----------------------// + // setExportPathSansExt // + //----------------------// + /** + * Remember the path (without extension) where the book is to be exported. + * + * @param exportPathSansExt the book export path (without extension) + */ + public void setExportPathSansExt (Path exportPathSansExt) + { + this.exportPathSansExt = exportPathSansExt; + } + //-------------------// + // getFirstValidStub // + //-------------------// /** * Report the first non-discarded stub in this book * * @return the first non-discarded stub, or null */ - SheetStub getFirstValidStub (); + public SheetStub getFirstValidStub () + { + for (SheetStub stub : stubs) { + if (stub.isValid()) { + return stub; + } + } + + return null; // No valid stub found! + } + //--------------// + // getInputPath // + //--------------// /** - * Report the path name of the book image(s) input. + * Report the path to the book image(s) input. * * @return the image input path */ - Path getInputPath (); + public Path getInputPath () + { + return path; + } + //---------// + // getLock // + //---------// /** * Report the lock that protects book project file. * * @return book project lock */ - Lock getLock (); + public Lock getLock () + { + return lock; + } + //-----------------// + // getOcrLanguages // + //-----------------// /** * Report the OCR language(s) specification defined at book level, if any. * * @return the OCR language(s) spec */ - Param getOcrLanguages (); + public Param getOcrLanguages () + { + if (ocrLanguages == null) { + ocrLanguages = new StringParam(); + ocrLanguages.setParent(Language.ocrDefaultLanguages); + } + return ocrLanguages; + } + + //-----------// + // getOffset // + //-----------// /** * Report the offset of this book, with respect to a containing super-book. * * @return the offset (in terms of number of sheets) */ - Integer getOffset (); + public Integer getOffset () + { + return offset; + } + //-----------// + // setOffset // + //-----------// + /** + * Assign this book offset (WRT containing super-book) + * + * @param offset the offset to set + */ + public void setOffset (Integer offset) + { + this.offset = offset; + } + + //--------------// + // getPrintPath // + //--------------// /** * Report the path, if any, where book is to be printed. * * @return the print path, or null */ - Path getPrintPath (); + public Path getPrintPath () + { + return printPath; + } + + //--------------// + // setPrintPath // + //--------------// + /** + * Remember to which path book print data is to be written. + * + * @param printPath the print path + */ + public void setPrintPath (Path printPath) + { + this.printPath = printPath; + } + //-----------------------// + // getProcessingSwitches // + //-----------------------// /** - * Report the processing switches defined at bookk level, if any. + * Report the processing switches defined at book level, if any. * * @return the processing switches */ - ProcessingSwitches getProcessingSwitches (); + public ProcessingSwitches getProcessingSwitches () + { + if (switches == null) { + switches = new ProcessingSwitches(); + switches.setParent(ProcessingSwitches.getDefaultSwitches()); + } + + return switches; + } + //----------// + // getRadix // + //----------// /** * Report the radix of the file that corresponds to the book. * It is based on the simple file name of the book, with no path and no extension. * * @return the book input file radix */ - String getRadix (); + public String getRadix () + { + return radix; + } + //---------------------// + // getSampleRepository // + //---------------------// /** * Report the sample repository (specific or global) to populate for this book * * @return a specific book repository if possible, otherwise the global one */ - SampleRepository getSampleRepository (); + public SampleRepository getSampleRepository () + { + SampleRepository repo = getSpecificSampleRepository(); + + if (repo != null) { + return repo; + } + // No specific repository is possible, so use global + return SampleRepository.getGlobalInstance(); + } + + //----------// + // getScore // + //----------// /** * Report the score which contains the provided page. * * @param page provided page * @return containing score (can it be null?) */ - Score getScore (Page page); + public Score getScore (Page page) + { + for (Score score : scores) { + int pageIndex = score.getPageIndex(page); + + if (pageIndex != -1) { + return score; + } + } + + return null; + } + //-----------// + // getScores // + //-----------// /** * Report the scores (movements) detected in this book. * * @return the immutable list of scores */ - List getScores (); + public List getScores () + { + return Collections.unmodifiableList(scores); + } + //-----------------------------// + // getSpecificSampleRepository // + //-----------------------------// /** * Report (after allocation if needed) the book specific sample repository * * @return the repository instance with material for this book only, or null */ - SampleRepository getSpecificSampleRepository (); + public SampleRepository getSpecificSampleRepository () + { + if (repository == null) { + repository = SampleRepository.getInstance(this, true); + } + return repository; + } + + //---------// + // getStub // + //---------// /** * Report the sheet stub with provided id (counted from 1). * * @param sheetId the desired value for sheet id * @return the proper sheet stub, or null if not found */ - SheetStub getStub (int sheetId); + public SheetStub getStub (int sheetId) + { + return stubs.get(sheetId - 1); + } + //----------// + // getStubs // + //----------// /** * Report all the sheets stubs contained in this book. * * @return the immutable list of sheets stubs, list may be empty but is never null */ - List getStubs (); + public List getStubs () + { + return Collections.unmodifiableList(stubs); + } + //---------------// + // getValidStubs // + //---------------// /** * Report the non-discarded sheets stubs in this book. * * @return the immutable list of valid sheets stubs */ - List getValidStubs (); + public List getValidStubs () + { + List valids = new ArrayList<>(); + + for (SheetStub stub : stubs) { + if (stub.isValid()) { + valids.add(stub); + } + } + + return valids; + } + //------------------------// + // hasAllocatedRepository // + //------------------------// /** * Tell whether the book has allocated a dedicated sample repository. * * @return true if allocated */ - boolean hasAllocatedRepository (); + public boolean hasAllocatedRepository () + { + return repository != null; + } + //-----------------------// + // hasSpecificRepository // + //-----------------------// /** * Tell whether the book has an existing specific sample repository. * * @return true if specific repository exists */ - boolean hasSpecificRepository (); + public boolean hasSpecificRepository () + { + if (repository != null) { + return true; + } + // Look for needed files + return SampleRepository.repositoryExists(this); + } + + //------------------// + // hideInvalidStubs // + //------------------// /** * Hide stub assemblies of invalid sheets. */ - void hideInvalidStubs (); + public void hideInvalidStubs () + { + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run () + { + final StubsController controller = StubsController.getInstance(); + + for (SheetStub stub : stubs) { + if (!stub.isValid()) { + controller.removeAssembly(stub); + } + } + } + }); + } + //-------------// + // includeBook // + //-------------// /** * Include a (sub) book into this (super) book. * * @param book the sub book to include */ - void includeBook (Book book); + public void includeBook (Book book) + { + subBooks.add(book); + } + //-----------// + // isClosing // + //-----------// /** - * Report whether this book is closing + * Report whether this book is being closed. * * @return the closing flag */ - boolean isClosing (); + public boolean isClosing () + { + return closing; + } + + //------------// + // setClosing // + //------------// + /** + * Flag this book as closing. + * + * @param closing the closing to set + */ + public void setClosing (boolean closing) + { + this.closing = closing; + } + //---------// + // isDirty // + //---------// /** * Report whether the book scores need to be reduced. * * @return true if dirty */ - boolean isDirty (); + public boolean isDirty () + { + return dirty; + } + + //----------// + // setDirty // + //----------// + /** + * Set the dirty flag. + * + * @param dirty the new flag value + */ + public void setDirty (boolean dirty) + { + this.dirty = dirty; + } + //------------// + // isModified // + //------------// /** - * Report whether the book has been modified with respect to its book data. + * Report whether the book has been modified with respect to its persisted data. * * @return true if modified */ - boolean isModified (); + public boolean isModified () + { + if (modified) { + return true; // The book itself is modified + } + + if ((repository != null) && repository.isModified()) { + return true; // The book sample repository is modified + } + + for (SheetStub stub : stubs) { + if (stub.isModified()) { + return true; // This sheet is modified + } + } + + return false; + } + + //-------------// + // setModified // + //-------------// + /** + * Set the modified flag. + * + * @param modified the new flag value + */ + public void setModified (boolean modified) + { + this.modified = modified; + + if (OMR.gui != null) { + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run () + { + final StubsController controller = StubsController.getInstance(); + final SheetStub stub = controller.getSelectedStub(); + + if ((stub != null) && (stub.getBook() == Book.this)) { + controller.refresh(); + } + } + }); + } + } + + //------------// + // isUpgraded // + //------------// + /** + * Report whether the book has been upgraded with respect to its persisted data. + * + * @return true if upgraded + */ + public boolean isUpgraded () + { + for (SheetStub stub : stubs) { + if (stub.isUpgraded()) { + return true; // This sheet is upgraded + } + } + + return false; + } + //--------------// + // isMultiSheet // + //--------------// /** * Report whether this book contains several sheets. * * @return true for several sheets */ - boolean isMultiSheet (); + public boolean isMultiSheet () + { + return stubs.size() > 1; + } + //----------------// + // loadSheetImage // + //----------------// /** * Actually load the image that corresponds to the specified sheet id. * * @param id specified sheet id * @return the loaded sheet image */ - BufferedImage loadSheetImage (int id); + public BufferedImage loadSheetImage (int id) + { + try { + final ImageLoading.Loader loader = ImageLoading.getLoader(path); + if (loader == null) { + return null; + } + + BufferedImage img = loader.getImage(id); + logger.info("Loaded image {} {}x{} from {}", id, img.getWidth(), img.getHeight(), path); + + loader.dispose(); + + return img; + } catch (IOException ex) { + logger.warn("Error in book.loadSheetImage", ex); + + return null; + } + } + + //--------------// + // openBookFile // + //--------------// + /** + * Open the book file (supposed to already exist at location provided by + * '{@code bookPath}' member) for reading or writing. + *

                          + * When IO operations are finished, the book file must be closed via + * {@link #closeFileSystem(java.nio.file.FileSystem)} + * + * @return the root path of the (zipped) book file system + * @throws java.io.IOException if anything goes wrong + */ + public Path openBookFile () + throws IOException + { + return ZipFileSystem.open(bookPath); + } + + //-----------------// + // openSheetFolder // + //-----------------// /** * Open (in the book zipped file) the folder for provided sheet number * @@ -398,14 +1264,38 @@ public interface Book * @return the path to sheet folder * @throws IOException if anything goes wrong */ - Path openSheetFolder (int number) - throws IOException; + public Path openSheetFolder (int number) + throws IOException + { + Path root = openBookFile(); + + return root.resolve(INTERNALS_RADIX + number); + } + //-------// + // print // + //-------// /** * Print this book in PDF format. */ - void print (); + public void print () + { + // Path to print file + final Path pdfPath = BookManager.getActualPath( + getPrintPath(), + BookManager.getDefaultPrintPath(this)); + + try { + new BookPdfOutput(Book.this, pdfPath.toFile()).write(null); + setPrintPath(pdfPath); + } catch (Exception ex) { + logger.warn("Cannot print to {} {}", pdfPath, ex.toString(), ex); + } + } + //---------------// + // reachBookStep // + //---------------// /** * Reach a specific step (and all needed intermediate steps) on all valid sheets * of this book. @@ -415,17 +1305,161 @@ Path openSheetFolder (int number) * @param sheetIds IDs of selected valid sheets, or null for all valid sheets * @return true if OK on all sheet actions */ - boolean reachBookStep (Step target, - boolean force, - Set sheetIds); + public boolean reachBookStep (final Step target, + final boolean force, + final Set sheetIds) + { + try { + final List concernedStubs = getConcernedStubs(sheetIds); + logger.debug("reachStep {} force:{} sheetIds:{}", target, force, sheetIds); + + if (!force) { + // Check against the least advanced step performed across all sheets concerned + Step least = getLeastStep(concernedStubs); + + if ((least != null) && (least.compareTo(target) >= 0)) { + return true; // Nothing to do + } + } + + // Launch the steps on each sheet + long startTime = System.currentTimeMillis(); + logger.info( + "Book reaching {}{} on sheets:{}", + target, + force ? " force" : "", + ids(concernedStubs)); + + try { + boolean someFailure = false; + StepMonitoring.notifyStart(); + + if (isMultiSheet() && constants.processAllStubsInParallel.isSet() + && (OmrExecutors.defaultParallelism.getValue() == true)) { + // Process all stubs in parallel + List> tasks = new ArrayList<>(); + + for (final SheetStub stub : concernedStubs) { + tasks.add(new Callable() + { + @Override + public Boolean call () + throws StepException + { + LogUtil.start(stub); + + try { + boolean ok = stub.reachStep(target, force); + + if (ok && (OMR.gui == null)) { + stub.swapSheet(); // Save sheet & global book info to disk + } + + return ok; + } finally { + LogUtil.stopStub(); + } + } + }); + } + + try { + List> futures = OmrExecutors.getCachedLowExecutor() + .invokeAll(tasks); + + for (Future future : futures) { + try { + if (!future.get()) { + someFailure = true; + } + } catch (InterruptedException | + ExecutionException ex) { + logger.warn("Future exception", ex); + someFailure = true; + } + } + + return !someFailure; + } catch (InterruptedException ex) { + logger.warn("Error in parallel reachBookStep", ex); + someFailure = true; + } + } else { + // Process one stub after the other + for (SheetStub stub : concernedStubs) { + LogUtil.start(stub); + try { + if (stub.reachStep(target, force)) { + if (OMR.gui == null) { + stub.swapSheet(); // Save sheet & global book info to disk + } + } else { + someFailure = true; + } + } catch (Exception ex) { + // Exception (such as timeout) raised on stub + // Let processing continue for the other stubs + logger.warn("Error processing stub"); + someFailure = true; + } finally { + LogUtil.stopStub(); + } + } + } + + return !someFailure; + } finally { + LogUtil.stopStub(); + StepMonitoring.notifyStop(); + + long stopTime = System.currentTimeMillis(); + logger.debug("End of step set in {} ms.", (stopTime - startTime)); + } + } catch (ProcessingCancellationException pce) { + throw pce; + } catch (Exception ex) { + logger.warn("Error in performing " + target, ex); + } + + return false; + } + + //--------------// + // reduceScores // + //--------------// /** * Determine the logical parts of each score. * * @return the count of modifications done */ - int reduceScores (); + public int reduceScores () + { + int modifs = 0; + + if (scores != null) { + for (Score score : scores) { + // (re) build the score logical parts + modifs += new ScoreReduction(score).reduce(); + + // Slurs and voices connection across pages in score + modifs += Voices.refineScore(score); + } + if (modifs > 0) { + setModified(true); + logger.info("Scores built: {}", scores.size()); + } + + setDirty(false); + } + + return modifs; + } + + //------------// + // removeStub // + //------------// /** * Remove the specified sheet stub from the containing book. *

                          @@ -435,111 +1469,1016 @@ boolean reachBookStep (Step target, * @param stub the sheet stub to remove * @return true if actually removed */ - boolean removeStub (SheetStub stub); + public boolean removeStub (SheetStub stub) + { + return stubs.remove(stub); + } + //-------// + // reset // + //-------// /** * Reset all valid sheets of this book to their initial state. */ - void reset (); + public void reset () + { + for (SheetStub stub : getValidStubs()) { + stub.reset(); + } + scores.clear(); + } + + //---------------// + // resetToBinary // + //---------------// /** * Reset all valid sheets of this book to their BINARY step. */ - void resetToBinary (); + public void resetToBinary () + { + for (SheetStub stub : getValidStubs()) { + stub.resetToBinary(); + } + + scores.clear(); + } + //--------// + // sample // + //--------// /** * Write the book symbol samples into its sample repository. */ - void sample (); + public void sample () + { + for (SheetStub stub : getValidStubs()) { + Sheet sheet = stub.getSheet(); + sheet.sample(); + } + } + //-------// + // store // + //-------// /** - * Set the book alias + * Store book to disk. * - * @param alias the book alias + * @param bookPath target path for storing the book + * @param withBackup if true, rename beforehand any existing target as a backup + */ + public void store (Path bookPath, + boolean withBackup) + { + Memory.gc(); // Launch garbage collection, to save on weak glyph references ... + + boolean diskWritten = false; // Has disk actually been written? + + // Backup existing book file? + if (withBackup && Files.exists(bookPath)) { + Path backup = FileUtil.backup(bookPath); + + if (backup != null) { + logger.info("Previous book file renamed as {}", backup); + } + } + + Path root = null; // Root of the zip file system + + try { + getLock().lock(); + checkRadixChange(bookPath); + logger.debug("Storing book..."); + + if ((this.bookPath == null) || this.bookPath.toAbsolutePath().equals( + bookPath.toAbsolutePath())) { + if (this.bookPath == null) { + root = ZipFileSystem.create(bookPath); + diskWritten = true; + } else { + root = ZipFileSystem.open(bookPath); + } + + if (modified) { + storeBookInfo(root); // Book info (book.xml) + diskWritten = true; + } + + // Contained sheets + for (SheetStub stub : stubs) { + if (stub.isModified() || stub.isUpgraded()) { + final Path sheetFolder = root.resolve(INTERNALS_RADIX + stub.getNumber()); + stub.getSheet().store(sheetFolder, null); + diskWritten = true; + } + } + + // Separate repository + if ((repository != null) && repository.isModified()) { + repository.storeRepository(); + } + } else { + // (Store as): Switch from old to new book file + root = createBookFile(bookPath); + diskWritten = true; + + storeBookInfo(root); // Book info (book.xml) + + // Contained sheets + final Path oldRoot = openBookFile(this.bookPath); + + for (SheetStub stub : stubs) { + final Path oldSheetFolder = oldRoot.resolve(INTERNALS_RADIX + stub.getNumber()); + final Path sheetFolder = root.resolve(INTERNALS_RADIX + stub.getNumber()); + + // By default, copy existing sheet files + if (Files.exists(oldSheetFolder)) { + FileUtil.copyTree(oldSheetFolder, sheetFolder); + } + + // Update modified sheet files + if (stub.isModified() || stub.isUpgraded()) { + stub.getSheet().store(sheetFolder, oldSheetFolder); + } + } + + oldRoot.getFileSystem().close(); // Close old book file + } + + this.bookPath = bookPath; + + BookManager.getInstance().getBookHistory().add(bookPath); // Insert in history + + if (diskWritten) { + logger.info("Book stored as {}", bookPath); + } + } catch (Exception ex) { + logger.warn("Error storing " + this + " to " + bookPath + " ex:" + ex, ex); + } finally { + if (root != null) { + try { + root.getFileSystem().close(); + } catch (IOException ignored) { + } + } + + getLock().unlock(); + } + } + + //-------// + // store // + //-------// + /** + * Store book to disk, using its current book path. */ - void setAlias (String alias); + public void store () + { + if (bookPath == null) { + logger.warn("Bookpath not defined"); + } else { + store(bookPath, false); + } + } + //---------------// + // storeBookInfo // + //---------------// /** - * Flag this book as closing. + * Store the book information (global info + stub steps) into book file system. * - * @param closing the closing to set + * @param root root path of book file system + * @throws Exception if anything goes wrong */ - void setClosing (boolean closing); + public void storeBookInfo (Path root) + throws Exception + { + Path bookInternals = root.resolve(BOOK_INTERNALS); + Files.deleteIfExists(bookInternals); + Jaxb.marshal(this, bookInternals, getJaxbContext()); + setModified(false); + logger.info("Stored {}", bookInternals); + } + //---------------// + // swapAllSheets // + //---------------// /** - * Set the dirty flag. + * Swap all sheets, except the current one if any. + */ + public void swapAllSheets () + { + if (isModified() || isUpgraded()) { + logger.info("{} storing", this); + store(); + } + + SheetStub currentStub = null; + + if (OMR.gui != null) { + currentStub = StubsController.getCurrentStub(); + } + + for (SheetStub stub : stubs) { + if (stub != currentStub) { + stub.swapSheet(); + } + } + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("Book{"); + sb.append(radix); + + if ((offset != null) && (offset > 0)) { + sb.append(" offset:").append(offset); + } + + sb.append("}"); + + return sb.toString(); + } + + //------------// + // transcribe // + //------------// + /** + * Convenient method to perform all needed transcription steps on all valid sheets + * of this book and building the book score(s). * - * @param dirty the new flag value + * @return true if OK */ - void setDirty (boolean dirty); + public boolean transcribe () + { + boolean ok = reachBookStep(Step.last(), false, null); + + reduceScores(); + return ok; + } + + //--------------// + // updateScores // + //--------------// /** - * Remember the path (without extension) where the book is to be exported. + * Update the gathering of sheet pages into scores. + *

                          + * The question is which scores should we update. + * Clearing all and rebuilding all is OK for pageRefs of all scores without loading sheets. + * But doing so, we lose logicalPart information of all scores, and to rebuild it we'll + * need to reload all valid sheets. + *

                          + * A better approach is to check the stub before and the stub after the current one. + * This may result in the addition or the removal of scores. * - * @param exportPathSansExt the book export path (without extension) + * @param currentStub the current stub */ - void setExportPathSansExt (Path exportPathSansExt); + public synchronized void updateScores (SheetStub currentStub) + { + if (scores.isEmpty()) { + // Easy: allocate scores based on all book stubs + createScores(); + } else { + try { + // Determine just the impacted pageRefs + final SortedSet impactedRefs = new TreeSet<>(); + final int stubNumber = currentStub.getNumber(); + + if (!currentStub.getPageRefs().isEmpty()) { + // Look in stub before current stub? + final PageRef firstPageRef = currentStub.getFirstPageRef(); + + if (!firstPageRef.isMovementStart()) { + final SheetStub prevStub = (stubNumber > 1) ? stubs.get(stubNumber - 2) + : null; + + if (prevStub != null) { + final PageRef prevPageRef = prevStub.getLastPageRef(); + + if (prevPageRef != null) { + impactedRefs.addAll(getScore(prevPageRef).getPageRefs()); // NPE + } + } + } + + // Take pages of current stub + impactedRefs.addAll(currentStub.getPageRefs()); + + // Look in stub after current stub? + final SheetStub nextStub = (stubNumber < stubs.size()) ? stubs.get(stubNumber) + : null; + + if (nextStub != null) { + final PageRef nextPageRef = nextStub.getFirstPageRef(); + if ((nextPageRef != null) && !nextPageRef.isMovementStart()) { + impactedRefs.addAll(getScore(nextPageRef).getPageRefs()); // NPE + } + } + } + + // Determine and remove the impacted scores + final List impactedScores = scoresOf(impactedRefs); + Integer scoreIndex = null; + + if (!impactedScores.isEmpty()) { + scoreIndex = scores.indexOf(impactedScores.get(0)); + } else { + for (Score score : scores) { + if (score.getFirstPageRef().getSheetNumber() > stubNumber) { + scoreIndex = scores.indexOf(score); + + break; + } + } + } + + if (scoreIndex == null) { + scoreIndex = scores.size(); + } + + logger.debug("Impacted pages:{} scores:{}", impactedRefs, impactedScores); + scores.removeAll(impactedScores); + + // Insert new score(s) to replace the impacted one(s)? + if (!currentStub.isValid()) { + impactedRefs.removeAll(currentStub.getPageRefs()); + } + + insertScores(currentStub, impactedRefs, scoreIndex); + } catch (Exception ex) { + // This seems to result from inconsistency between scores info and stubs info. + // Initial cause can be a sheet not marshalled (because of use by another process) + // followed by a reload of now non-consistent book.xml + + // Workaround: Clear all scores and rebuild them from stubs info + // (Doing so, we may lose logical-part informations) + logger.warn("Error updating scores " + ex, ex); + logger.warn("Rebuilding them from stubs info."); + scores.clear(); + createScores(); + } + } + } + + //-----------------------// + // areVersionsCompatible // + //-----------------------// /** - * Set the modified flag. + * Check whether the program version can operate on the file version. + *

                          + * All version strings are expected to be formatted like "5.0" or "5.0.1" + * and we don't take the 3rd number, if any, into account for compatibility checking. * - * @param modified the new flag value + * @param programVersion version of software + * @param fileVersion version of book file + * @return true if OK */ - void setModified (boolean modified); + private boolean areVersionsCompatible (String programVersion, + String fileVersion) + { + try { + logger.debug("Book file version: {}", fileVersion); + + final String[] programTokens = programVersion.split("\\."); + + if (programTokens.length < 2) { + throw new IllegalArgumentException("Illegal Audiveris version " + programVersion); + } + + final String[] fileTokens = fileVersion.split("\\."); + + if (fileTokens.length < 2) { + throw new IllegalArgumentException("Illegal Book file version " + fileVersion); + } + + for (int i = 0; i < 2; i++) { + if (Integer.decode(fileTokens[i]) < Integer.decode(programTokens[i])) { + return false; + } + } + return true; + } catch (IllegalArgumentException ex) { + logger.error("Error while checking versions " + ex, ex); + + return false; // Safer + } + } + + //---------------// + // beforeMarshal // + //---------------// + @SuppressWarnings("unused") + private void beforeMarshal (Marshaller m) + { + if ((binarizationFilter != null) && !binarizationFilter.isSpecific()) { + binarizationFilter = null; + } + + if ((ocrLanguages != null) && !ocrLanguages.isSpecific()) { + ocrLanguages = null; + } + + if ((switches != null) && switches.isEmpty()) { + switches = null; + } + } + + //------------------// + // checkRadixChange // + //------------------// /** - * Assign this book offset (WRT containing super-book) + * If the (new) book name does not match current one, update the book radix + * (and the title of first displayed sheet if any). * - * @param offset the offset to set + * @param bookPath new book target path */ - void setOffset (Integer offset); + private void checkRadixChange (Path bookPath) + { + // Are we changing the target name WRT the default name? + final String newRadix = FileUtil.avoidExtensions(bookPath.getFileName(), OMR.BOOK_EXTENSION) + .toString(); + + if (!newRadix.equals(radix)) { + // Update book radix + radix = newRadix; + + // We are really changing the radix, so nullify all other paths + exportPathSansExt = printPath = null; + + if (OMR.gui != null) { + // Update UI first sheet tab + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run () + { + StubsController.getInstance().updateFirstStubTitle(Book.this); + } + }); + } + } + } + + //------------// + // checkScore // Dirty hack, to be removed ASAP + //------------// + private void checkScore () + { + for (Score score : scores) { + PageRef ref = score.getFirstPageRef(); + + if (ref == null) { + logger.warn("Discarding invalid score data."); + scores.clear(); + break; + } + } + } + + //--------------// + // createScores // + //--------------// /** - * Remember to which path book print data is to be written. + * Create scores out of all book stubs. + */ + private void createScores () + { + Score score = null; + + // Group provided sheets pages into scores + for (SheetStub stub : stubs) { + // An invalid or not-yet-processed stub triggers a score break + if (stub.getPageRefs().isEmpty()) { + score = null; + } else { + for (PageRef pageRef : stub.getPageRefs()) { + if ((score == null) || pageRef.isMovementStart()) { + scores.add(score = new Score()); + score.setBook(this); + } + + score.addPageRef(stub.getNumber(), pageRef); + } + } + } + + logger.debug("Created scores:{}", scores); + } + + //-------------------// + // getConcernedStubs // + //-------------------// + private List getConcernedStubs (Set sheetIds) + { + List list = new ArrayList<>(); + + for (SheetStub stub : getValidStubs()) { + if ((sheetIds == null) || sheetIds.contains(stub.getNumber())) { + list.add(stub); + } + } + + return list; + } + + //--------------// + // getLeastStep // + //--------------// + /** + * Report the least advanced step reached among all provided stubs. * - * @param printPath the print path + * @return the least step, null if any stub has not reached the first step (LOAD) */ - void setPrintPath (Path printPath); + private Step getLeastStep (List stubs) + { + Step least = Step.last(); + for (SheetStub stub : stubs) { + Step latest = stub.getLatestStep(); + + if (latest == null) { + return null; // This sheet has not been processed at all + } + + if (latest.compareTo(least) < 0) { + least = latest; + } + } + + return least; + } + + //----------// + // getScore // + //----------// /** - * Store book to disk. + * Report the score if any that contains the provided PageRef. * - * @param bookPath target path for storing the book - * @param withBackup if true, rename beforehand any existing target as a backup + * @param pageRef the provided page ref (sheet#, page#) + * @return the containing score or null if not found */ - void store (Path bookPath, - boolean withBackup); + private Score getScore (PageRef pageRef) + { + for (Score score : scores) { + PageRef ref = score.getPageRef(pageRef.getSheetNumber()); + + if ((ref != null) && (ref.getId() == pageRef.getId())) { + return score; + } + } + + return null; + } + //----------------// + // initTransients // + //----------------// /** - * Store book to disk, using its current book path. + * Initialize transient data. + * + * @param nameSansExt book name without extension, if any + * @param bookPath full path to book .omr file, if any + * @return true if OK */ - void store (); + private boolean initTransients (String nameSansExt, + Path bookPath) + { + if (binarizationFilter != null) { + binarizationFilter.setParent(FilterDescriptor.defaultFilter); + } + + if (ocrLanguages != null) { + ocrLanguages.setParent(Language.ocrDefaultLanguages); + } + + if (switches != null) { + switches.setParent(ProcessingSwitches.getDefaultSwitches()); + } + + if (alias == null) { + alias = checkAlias(getInputPath()); + + if (alias != null) { + nameSansExt = alias; + } + } + + if (nameSansExt != null) { + radix = nameSansExt; + } + + if (bookPath != null) { + this.bookPath = bookPath; + if (nameSansExt == null) { + radix = FileUtil.getNameSansExtension(bookPath); + } + } + + if (build == null) { + build = WellKnowns.TOOL_BUILD; + } + + if (version == null) { + version = WellKnowns.TOOL_REF; + } else { + if (constants.checkBookVersion.isSet()) { + // Check compatibility between file version and program version + if (!areVersionsCompatible(ProgramId.PROGRAM_VERSION, version)) { + if (constants.resetOldBooks.isSet()) { + final String msg = bookPath + " version " + version; + logger.warn(msg); + + // Prompt user for resetting project sheets? + if ((OMR.gui == null) || OMR.gui.displayConfirmation( + msg + "\nConfirm reset to binary?", + "Non compatible book version")) { + resetToBinary(); + logger.info("Book {} reset to binary.", radix); + version = WellKnowns.TOOL_REF; + build = WellKnowns.TOOL_BUILD; + + return true; + } + } else { + logger.info("Incompatible book version, but not reset."); + } + + return false; + } + } + } + + return true; + } + + //--------------// + // insertScores // + //--------------// /** - * Store the book information (global info + stub steps) into book file system. + * Insert scores out of provided sequence of PageRef's. * - * @param root root path of book file system - * @throws Exception if anything goes wrong + * @param currentStub stub being processed + * @param pageRefs sequence of pageRefs + * @param insertIndex insertion index in scores list + */ + private void insertScores (SheetStub currentStub, + SortedSet pageRefs, + int insertIndex) + { + Score score = null; + Integer stubNumber = null; + int index = insertIndex; + + for (PageRef ref : pageRefs) { + if (stubNumber == null) { + // Very first + score = null; + } else if (stubNumber < (ref.getSheetNumber() - 1)) { + // One or several stubs missing + score = null; + } + + if (ref.isMovementStart()) { + // Movement start + score = null; + } + + if (score == null) { + scores.add(index++, score = new Score()); + score.setBook(this); + } + + score.addPageRef(ref.getSheetNumber(), ref); + stubNumber = ref.getSheetNumber(); + } + + logger.debug("Inserted scores:{}", scores.subList(insertIndex, index)); + } + + //----------// + // scoresOf // + //----------// + /** + * Retrieve the list of scores that embrace the provided sequence of pageRefs. + * + * @param refs the provided pageRefs (sorted) + * @return the impacted scores */ - void storeBookInfo (Path root) - throws Exception; + private List scoresOf (SortedSet refs) + { + final List impacted = new ArrayList<>(); + + if (!refs.isEmpty()) { + final int firstNumber = refs.first().getSheetNumber(); + final int lastNumber = refs.last().getSheetNumber(); + + for (Score score : scores) { + if (score.getLastPageRef().getSheetNumber() < firstNumber) { + continue; + } + + if (score.getFirstPageRef().getSheetNumber() > lastNumber) { + break; + } + + List scoreRefs = new ArrayList<>(score.getPageRefs()); + scoreRefs.retainAll(refs); + + if (!scoreRefs.isEmpty()) { + impacted.add(score); + } + } + } + return impacted; + } + + //-----------------// + // closeFileSystem // + //-----------------// /** - * Swap all sheets, except the current one if any. + * Close the provided (book) file system. + * + * @param fileSystem the book file system */ - void swapAllSheets (); + public static void closeFileSystem (FileSystem fileSystem) + { + try { + fileSystem.close(); + logger.info("Book file system closed."); + } catch (IOException ex) { + logger.warn("Could not close book file system " + ex, ex); + } + } + + //-----// + // ids // + //-----// /** - * Convenient method to perform all needed transcription steps on all valid sheets - * of this book and building the book score(s). + * Build a string with just the IDs of the stub collection. * - * @return true if OK + * @param stubs the collection of stub instances + * @return the string built + */ + public static String ids (List stubs) + { + if (stubs == null) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + sb.append("["); + + for (SheetStub entity : stubs) { + sb.append("#").append(entity.getNumber()); + } + + sb.append("]"); + + return sb.toString(); + } + + //----------// + // loadBook // + //----------// + /** + * Load a book out of a provided book file. + * + * @param bookPath path to the (zipped) book file + * @return the loaded book if successful + */ + public static Book loadBook (Path bookPath) + { + StopWatch watch = new StopWatch("loadBook " + bookPath); + Book book = null; + + try { + logger.info("Loading book {}", bookPath); + watch.start("book"); + + // Open book file + Path rootPath = ZipFileSystem.open(bookPath); + + // Load book internals (just the stubs) out of book.xml + Path internalsPath = rootPath.resolve(BOOK_INTERNALS); + + try (InputStream is = Files.newInputStream(internalsPath, StandardOpenOption.READ)) { + JAXBContext ctx = getJaxbContext(); + Unmarshaller um = ctx.createUnmarshaller(); + ///Unmarshaller um = getJaxbContext().createUnmarshaller(); + book = (Book) um.unmarshal(is); + LogUtil.start(book); + book.getLock().lock(); + rootPath.getFileSystem().close(); // Close book file + + boolean ok = book.initTransients(null, bookPath); + + if (!ok) { + logger.info("Discarded {}", bookPath); + + return null; + } + + book.checkScore(); // TODO: remove ASAP + + return book; + } + } catch (IOException | + JAXBException ex) { + logger.warn("Error loading book " + bookPath + " " + ex, ex); + + return null; + } finally { + if (constants.printWatch.isSet()) { + watch.print(); + } + + if (book != null) { + book.getLock().unlock(); + } + + LogUtil.stopBook(); + } + } + + //--------------// + // openBookFile // + //--------------// + /** + * Open the book file (supposed to already exist at location provided by + * '{@code bookPath}' parameter) for reading or writing. + *

                          + * When IO operations are finished, the book file must be closed via + * {@link #closeFileSystem(java.nio.file.FileSystem)} + * + * @param bookPath book path name + * @return the root path of the (zipped) book file system */ - boolean transcribe (); + public static Path openBookFile (Path bookPath) + { + if (bookPath == null) { + throw new IllegalStateException("bookPath is null"); + } + + try { + logger.debug("Book file system opened"); + + FileSystem fileSystem = FileSystems.newFileSystem(bookPath, null); + + return fileSystem.getPath(fileSystem.getSeparator()); + } catch (FileNotFoundException ex) { + logger.warn("File not found: " + bookPath, ex); + } catch (IOException ex) { + logger.warn("Error reading book:" + bookPath, ex); + } + + return null; + } - /** Update the gathering of sheet pages into scores. + //------------// + // checkAlias // + //------------// + private static String checkAlias (Path path) + { + // Alias? + if (AliasPatterns.useAliasPatterns()) { + final String nameSansExt = FileUtil.getNameSansExtension(path); + + return BookManager.getInstance().getAlias(nameSansExt); + } + + return null; + } + + //----------------// + // createBookFile // + //----------------// + /** + * Create a new book file system dedicated to this book at the location provided + * by '{@code bookpath}' member. + * If such file already exists, it is deleted beforehand. + *

                          + * When IO operations are finished, the book file must be closed via + * {@link #closeFileSystem(java.nio.file.FileSystem)} * - * @param stub current stub + * @return the root path of the (zipped) book file system */ - void updateScores (SheetStub stub); + private static Path createBookFile (Path bookPath) + throws IOException + { + if (bookPath == null) { + throw new IllegalStateException("bookPath is null"); + } + + try { + Files.deleteIfExists(bookPath); + } catch (IOException ex) { + logger.warn("Error deleting book: " + bookPath, ex); + } + + // Make sure the containing folder exists + Files.createDirectories(bookPath.getParent()); + + // Make it a zip file + ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(bookPath.toFile())); + zos.close(); + + // Finally open the book file just created + return ZipFileSystem.open(bookPath); + } + + //----------------// + // getJaxbContext // + //----------------// + private static JAXBContext getJaxbContext () + throws JAXBException + { + // Lazy creation + if (jaxbContext == null) { + jaxbContext = JAXBContext.newInstance(Book.class, RunTable.class); + } + + return jaxbContext; + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print out the stop watch for book loading?"); + + private final Constant.Boolean processAllStubsInParallel = new Constant.Boolean( + false, + "Should we process all stubs of a book in parallel? (beware of many stubs)"); + + private final Constant.Boolean checkBookVersion = new Constant.Boolean( + true, + "Should we check version of loaded book files?"); + + private final Constant.Boolean resetOldBooks = new Constant.Boolean( + true, + "Should we reset to binary the too old book files?"); + } + + //------------------// + // OcrBookLanguages // + //------------------// + private static class OcrBookLanguages + extends Param + { + + @Override + public boolean setSpecific (String specific) + { + if ((specific != null) && specific.isEmpty()) { + specific = null; + } + + return super.setSpecific(specific); + } + + /** + * JAXB adapter to mimic XmlValue. + */ + public static class Adapter + extends XmlAdapter + { + + @Override + public String marshal (OcrBookLanguages val) + throws Exception + { + if (val == null) { + return null; + } + + return val.getSpecific(); + } + + @Override + public OcrBookLanguages unmarshal (String str) + throws Exception + { + OcrBookLanguages ol = new OcrBookLanguages(); + ol.setSpecific(str); + + return ol; + } + } + } } diff --git a/src/main/org/audiveris/omr/sheet/BookManager.java b/src/main/org/audiveris/omr/sheet/BookManager.java index 19640a24d..39f342337 100644 --- a/src/main/org/audiveris/omr/sheet/BookManager.java +++ b/src/main/org/audiveris/omr/sheet/BookManager.java @@ -51,12 +51,14 @@ *

                          * It handles where and how to handle inputs (images, books) and outputs (save, export, print, * sample). - *
                          - * Default input folder:

                            + *

                            + * Default input folder: + *

                              *
                            • (image) input: last image input folder used *
                            • (book) input: base *
                            - * Default output folder and file:
                              + * Default output folder and file: + *
                                *
                              • save: base/radix/radix.omr *
                              • export: base/radix/radix.mxl *
                              • print: base/radix/radix.pdf @@ -71,30 +73,29 @@ *

                                * For example, Mozart 40th symphony as available on IMSLP web site is made of one PDF file * containing 49 images (sheets). - * Its logical structure is a sequence of 4 movements:

                                  + * Its logical structure is a sequence of 4 movements: + *
                                    *
                                  1. Allegro Molto, starting on sheet #1
                                  2. *
                                  3. Andante, starting on sheet #19
                                  4. *
                                  5. Allegretto, starting on sheet #30
                                  6. *
                                  7. Allegro Assai, starting on sheet #33, system #2 (middle of the sheet)
                                  8. *
                                  - *

                                  * Assuming Opus is supported, the final result would be a single opus file: - *

                                  + * *
                                  - * Mozart_S40.opus.mxl (with each of the 4 movements included in this opus file)
                                  + *      Mozart_S40.opus.mxl (with each of the 4 movements included in this opus file)
                                    * 
                                  - *
                                  + * * Assuming Opus is NOT supported, the final result would be something like: - *
                                  + * *
                                  - * Mozart_S40/
                                  - * Mozart_S40/mvt1.mxl
                                  - * Mozart_S40/mvt2.mxl
                                  - * Mozart_S40/mvt3.mxl
                                  - * Mozart_S40/mvt4.mxl
                                  + *      Mozart_S40/
                                  + *      Mozart_S40/mvt1.mxl
                                  + *      Mozart_S40/mvt2.mxl
                                  + *      Mozart_S40/mvt3.mxl
                                  + *      Mozart_S40/mvt4.mxl
                                    * 
                                  - *
                                  - *

                                  + * * We could process all the 49 sheets in memory (although this is not practically feasible) with a * single book, discovering the 4 movements one after the other, and finally creating one MusicXML * Opus containing 4 {@link Score} instances, one for each movement. @@ -111,18 +112,13 @@ public class BookManager implements OmrEngine { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(BookManager.class); - /** The single instance of this class. */ - private static volatile BookManager INSTANCE; - - //~ Instance fields ---------------------------------------------------------------------------- /** All book instances. */ - private final List books = new ArrayList(); + private final List books = new ArrayList<>(); /** Alias patterns. */ private final AliasPatterns aliasPatterns = new AliasPatterns(); @@ -133,7 +129,6 @@ public class BookManager /** Book file history. (filled only when books are successfully loaded or saved) */ private PathHistory bookHistory; - //~ Constructors ------------------------------------------------------------------------------- /** * Private constructor for a singleton. */ @@ -141,7 +136,166 @@ private BookManager () { } - //~ Methods ------------------------------------------------------------------------------------ + //----------// + // getAlias // + //----------// + /** + * Try to retrieve an alias for the provided name, by applying registered patterns. + * + * @param name the full name provided + * @return the first alias found, or null if none + */ + public String getAlias (String name) + { + return aliasPatterns.getAlias(name); + } + + //-------------// + // getAllBooks // + //-------------// + @Override + public List getAllBooks () + { + return Collections.unmodifiableList(books); + } + + //----------// + // loadBook // + //----------// + @Override + public Book loadBook (Path bookPath) + { + Book book = Book.loadBook(bookPath); + + if (book != null) { + addBook(book); + + getBookHistory().add(bookPath); // Insert in book history + } + + return book; + } + + //-----------// + // loadInput // + //-----------// + @Override + public Book loadInput (Path path) + { + final Book book = new Book(path); + + // Alias? + if (AliasPatterns.useAliasPatterns()) { + final String nameSansExt = FileUtil.getNameSansExtension(path); + String alias = getAlias(nameSansExt); + + if (alias != null) { + book.setAlias(alias); + logger.info("Found alias: {} for {}", alias, nameSansExt); + } + } + + book.setModified(true); + book.setDirty(true); + addBook(book); + + getImageHistory().add(path); // Insert in input history + + return book; + } + + //------------// + // removeBook // + //------------// + /** + * Remove the provided book from the collection of Book instances. + * + * @param book the book to remove + * @return true if actually removed + */ + @Override + public synchronized boolean removeBook (Book book) + { + logger.debug("removeBook {}", book); + + return books.remove(book); + } + + //----------------// + // getBookHistory // + //----------------// + /** + * Get access to the list of previous books. + * + * @return the history set of book files + */ + public PathHistory getBookHistory () + { + if (bookHistory == null) { + bookHistory = new PathHistory( + "Book History", + constants.bookHistory, + null, + constants.historySize.getValue()); + } + + return bookHistory; + } + + //-----------------// + // getInputHistory // + //-----------------// + /** + * Get access to the list of previous inputs. + * + * @return the history set of input files + */ + public PathHistory getImageHistory () + { + if (imageHistory == null) { + imageHistory = new PathHistory( + "Input History", + constants.imageHistory, + constants.defaultImageFolder, + constants.historySize.getValue()); + } + + return imageHistory; + } + + //---------// + // addBook // + //---------// + /** + * Insert this new book in the set of book instances. + * + * @param book the book to insert + */ + private void addBook (Book book) + { + logger.debug("addBook {}", book); + + // + // // Remove duplicate if any + // for (Iterator it = books.iterator(); it.hasNext();) { + // Book b = it.next(); + // Path path = b.getInputPath(); + // + // if (path.equals(book.getInputPath())) { + // logger.debug("Removing duplicate {}", b); + // it.remove(); + // b.close(); + // + // break; + // } + // } + // + // Insert new book instance + synchronized (books) { + books.add(book); + } + } + //-------------// // deletePaths // //-------------// @@ -226,29 +380,6 @@ public static Path getActualPath (Path targetPath, } } - //----------// - // getAlias // - //----------// - /** - * Try to retrieve an alias for the provided name, by applying registered patterns. - * - * @param name the full name provided - * @return the first alias found, or null if none - */ - public String getAlias (String name) - { - return aliasPatterns.getAlias(name); - } - - //-------------// - // getAllBooks // - //-------------// - @Override - public List getAllBooks () - { - return Collections.unmodifiableList(books); - } - //---------------// // getBaseFolder // //---------------// @@ -296,91 +427,70 @@ public static Path getDefaultPrintPath (Book book) return book.getPrintPath(); } - return getDefaultBookFolder(book).resolve(book.getRadix() + OMR.PDF_EXTENSION); + return getDefaultBookFolder(book).resolve(book.getRadix() + OMR.PRINT_EXTENSION); } - //-------------// - // getInstance // - //-------------// + //--------------------// + // getExportExtension // + //--------------------// /** - * Report the single instance of this class. + * Report the extension to use for book export, depending on the use (or not) + * of opus and of compression. * - * @return the single instance + * @return the file extension to use. */ - public static BookManager getInstance () + public static String getExportExtension () { - if (INSTANCE == null) { - INSTANCE = new BookManager(); - } - - return INSTANCE; + return useOpus() ? OMR.OPUS_EXTENSION + : (useCompression() ? OMR.COMPRESSED_SCORE_EXTENSION : OMR.SCORE_EXTENSION); } //-------------// - // isMultiBook // + // getInstance // //-------------// /** - * Report whether we are currently handling more than one book. + * Report the single instance of BookManager in the application. * - * @return true if more than one book + * @return the instance */ - public static boolean isMultiBook () + public static BookManager getInstance () { - return getInstance().books.size() > 1; + return LazySingleton.INSTANCE; } - //----------// - // loadBook // - //----------// - @Override - public Book loadBook (Path bookPath) + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton { - Book book = BasicBook.loadBook(bookPath); - - if (book != null) { - addBook(book); - - getBookHistory().add(bookPath); // Insert in book history - } - - return book; - } - //---------// - // useOpus // - //---------// - public static boolean useOpus () - { - return constants.useOpus.isSet(); + static final BookManager INSTANCE = new BookManager(); } //--------------// // useSignature // //--------------// + /** + * Tell whether we should inject ProxyMusic signature. + * + * @return true if so + */ public static boolean useSignature () { return constants.defaultSigned.isSet(); } //----------------// - // getBookHistory // + // useCompression // //----------------// /** - * Get access to the list of previous books. + * Report whether we should use compression (to .MXL files) or not (to .XML files). * - * @return the history set of book files + * @return true for compression */ - public PathHistory getBookHistory () + public static boolean useCompression () { - if (bookHistory == null) { - bookHistory = new PathHistory( - "Book History", - constants.bookHistory, - null, - constants.historySize.getValue()); - } - - return bookHistory; + return constants.useCompression.getValue(); } //----------------------// @@ -466,77 +576,30 @@ public static Path getDefaultSavePath (Book book) return getDefaultBookFolder(book).resolve(book.getRadix() + OMR.BOOK_EXTENSION); } - //--------------------// - // getExportExtension // - //--------------------// - /** - * Report the extension to use for book export, depending on the use (or not) - * of opus and of compression. - * - * @return the file extension to use. - */ - public static String getExportExtension () - { - return useOpus() ? OMR.OPUS_EXTENSION - : (useCompression() ? OMR.COMPRESSED_SCORE_EXTENSION : OMR.SCORE_EXTENSION); - } - - //-----------// - // loadInput // - //-----------// - @Override - public Book loadInput (Path path) - { - final Book book = new BasicBook(path); - - // Alias? - if (AliasPatterns.useAliasPatterns()) { - final String nameSansExt = FileUtil.getNameSansExtension(path); - String alias = getAlias(nameSansExt); - - if (alias != null) { - book.setAlias(alias); - logger.info("Found alias: {} for {}", alias, nameSansExt); - } - } - - book.setModified(true); - book.setDirty(true); - addBook(book); - - getImageHistory().add(path); // Insert in input history - - return book; - } - - //------------// - // removeBook // - //------------// + //-------------// + // isMultiBook // + //-------------// /** - * Remove the provided book from the collection of Book instances. + * Report whether we are currently handling more than one book. * - * @param book the book to remove - * @return true if actually removed + * @return true if more than one book */ - @Override - public synchronized boolean removeBook (Book book) + public static boolean isMultiBook () { - logger.debug("removeBook {}", book); - - return books.remove(book); + return getInstance().books.size() > 1; } - //----------------// - // useCompression // - //----------------// + //---------// + // useOpus // + //---------// /** - * Report whether we should use compression (to .MXL files) or not (to .XML files). + * Tell whether Opus concept can be used * - * @return true for compression + * @return true if so */ - public static boolean useCompression () + public static boolean useOpus () { - return constants.useCompression.getValue(); + return constants.useOpus.isSet(); } //------------------------// @@ -553,68 +616,12 @@ public static boolean useSeparateBookFolders () return constants.useSeparateBookFolders.isSet(); } - //-----------------// - // getInputHistory // - //-----------------// - /** - * Get access to the list of previous inputs. - * - * @return the history set of input files - */ - public PathHistory getImageHistory () - { - if (imageHistory == null) { - imageHistory = new PathHistory( - "Input History", - constants.imageHistory, - constants.defaultImageFolder, - constants.historySize.getValue()); - } - - return imageHistory; - } - - //---------// - // addBook // - //---------// - /** - * Insert this new book in the set of book instances. - * - * @param book the book to insert - */ - private void addBook (Book book) - { - logger.debug("addBook {}", book); - - // - // // Remove duplicate if any - // for (Iterator it = books.iterator(); it.hasNext();) { - // Book b = it.next(); - // Path path = b.getInputPath(); - // - // if (path.equals(book.getInputPath())) { - // logger.debug("Removing duplicate {}", b); - // it.remove(); - // b.close(); - // - // break; - // } - // } - // - // Insert new book instance - synchronized (books) { - books.add(book); - } - } - - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean useOpus = new Constant.Boolean( false, @@ -648,10 +655,6 @@ private static final class Constants "", "History of books most recently loaded or saved"); - private final Constant.String scriptHistory = new Constant.String( - "", - "History of scripts most recently loaded or saved"); - private final Constant.Integer historySize = new Constant.Integer( "count", 10, diff --git a/src/main/org/audiveris/omr/sheet/DataHolder.java b/src/main/org/audiveris/omr/sheet/DataHolder.java index 83863fbe3..a0af22422 100644 --- a/src/main/org/audiveris/omr/sheet/DataHolder.java +++ b/src/main/org/audiveris/omr/sheet/DataHolder.java @@ -25,11 +25,13 @@ import org.slf4j.LoggerFactory; import java.io.InputStream; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -39,25 +41,22 @@ /** * Class {@code DataHolder} is a place holder for sheet internal data. *

                                  - * It handles:

                                    + * It handles: + *
                                      *
                                    • The data itself, if any.
                                    • *
                                    • A path to disk where data can be unmarshalled from.
                                    • *
                                    * * @param specific type for data handled - * * @author Hervé Bitteur */ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "holder") public class DataHolder { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - DataHolder.class); + private static final Logger logger = LoggerFactory.getLogger(DataHolder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Containing sheet. */ protected Sheet sheet; @@ -71,7 +70,6 @@ public class DataHolder @XmlAttribute(name = "path") private final String pathString; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code DataHolder} object. */ @@ -98,7 +96,6 @@ public DataHolder (Sheet sheet, this.pathString = pathString; } - //~ Methods ------------------------------------------------------------------------------------ /** * Return the handled data. * @@ -117,18 +114,20 @@ public T getData () Unmarshaller um = jaxbContext.createUnmarshaller(); // Open book file system - Path dataFile = book.openSheetFolder(sheet.getStub().getNumber()) - .resolve(pathString); + Path dataFile = book.openSheetFolder(sheet.getStub().getNumber()).resolve( + pathString); logger.debug("path: {}", dataFile); - InputStream is = Files.newInputStream(dataFile, StandardOpenOption.READ); - data = (T) um.unmarshal(is); - is.close(); + try (InputStream is = Files.newInputStream(dataFile, StandardOpenOption.READ)) { + data = (T) um.unmarshal(is); + } + logger.info("Loaded {}", dataFile); dataFile.getFileSystem().close(); // Close book file system } - } catch (Exception ex) { - logger.warn("Error unmarshalling from " + pathString, ex); + } catch (IOException | + JAXBException ex) { + logger.warn("Error unmarshalling from {}", pathString, ex); } finally { book.getLock().unlock(); } @@ -137,13 +136,23 @@ public T getData () return data; } - public boolean hasData () + /** + * Assign the data. + * + * @param data the data to be hold + */ + public void setData (T data) { - return data != null; + this.data = data; } - public void setData (T data) + /** + * Tell whether data is available. + * + * @return true if available + */ + public boolean hasData () { - this.data = data; + return data != null; } } diff --git a/src/main/org/audiveris/omr/sheet/DurationFactor.java b/src/main/org/audiveris/omr/sheet/DurationFactor.java index e693fd5fd..258c6e717 100644 --- a/src/main/org/audiveris/omr/sheet/DurationFactor.java +++ b/src/main/org/audiveris/omr/sheet/DurationFactor.java @@ -24,15 +24,18 @@ import org.audiveris.omr.math.Rational; /** - * Class {@code DurationFactor} handles a rational representation of - * duration modification + * Class {@code DurationFactor} handles a rational representation of duration + * modification, such as those triggered by a tuplet. + *

                                    + * Since in {@link Rational} super class, 'num' and 'den' are reduced by their GCD, in this class + * we add fields 'actualNum' and 'actualDen' which are not reduced. + * Therefore, DurationFactor 2/3 and 4/6 are not equal, even though their rational values are. * * @author Hervé Bitteur */ public class DurationFactor extends Rational { - //~ Instance fields ---------------------------------------------------------------------------- /** Actual numerator value, generally 2 or 4. */ public final int actualNum; @@ -40,7 +43,6 @@ public class DurationFactor /** Actual denominator value, generally 3 or 6. */ public final int actualDen; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new instance of DurationFactor * @@ -56,7 +58,38 @@ public DurationFactor (int num, actualDen = den; } - //~ Methods ------------------------------------------------------------------------------------ + //--------// + // equals // + //--------// + @Override + public boolean equals (Object obj) + { + if (obj instanceof DurationFactor) { + DurationFactor that = (DurationFactor) obj; + + if ((this.actualNum != that.actualNum) || (this.actualDen != that.actualDen)) { + return false; + } + + return super.equals(obj); + } + + return false; + } + + //----------// + // hashCode // + //----------// + @Override + public int hashCode () + { + int hash = 5; + hash = (97 * hash) + this.actualNum; + hash = (97 * hash) + this.actualDen; + + return hash; + } + //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/sheet/ExportPattern.java b/src/main/org/audiveris/omr/sheet/ExportPattern.java index 29134a278..c394b6956 100644 --- a/src/main/org/audiveris/omr/sheet/ExportPattern.java +++ b/src/main/org/audiveris/omr/sheet/ExportPattern.java @@ -38,7 +38,6 @@ */ public class ExportPattern { - //~ Static fields/initializers ----------------------------------------------------------------- private static final String OPUS = "opus"; @@ -58,11 +57,15 @@ public class ExportPattern private static final String doublePat = ".+" + "(" + opusPat + "|" + mvtPat + scorePat + ")"; - private static Pattern simplePattern; + private static volatile Pattern simplePattern; - private static Pattern doublePattern; + private static volatile Pattern doublePattern; + + /** Not meant to be instantiated. */ + private ExportPattern () + { + } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // getPathSansExt // //----------------// diff --git a/src/main/org/audiveris/omr/sheet/OldStaffBarline.java b/src/main/org/audiveris/omr/sheet/OldStaffBarline.java index 3a19a7f18..77cdb6280 100644 --- a/src/main/org/audiveris/omr/sheet/OldStaffBarline.java +++ b/src/main/org/audiveris/omr/sheet/OldStaffBarline.java @@ -1,54 +1,53 @@ -//------------------------------------------------------------------------------------------------// -// // -// O l d S t a f f B a r l i n e // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet; - -import org.audiveris.omr.sig.inter.BarlineInter; -import org.audiveris.omr.sig.inter.StaffBarlineInter; - -import java.util.ArrayList; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlIDREF; -import javax.xml.bind.annotation.XmlList; -import javax.xml.bind.annotation.XmlValue; - -/** - * Class {@code OldStaffBarline} is a temporary fix to keep compatibility with old - * .omr files. - *

                                    - * Replaced by {@link StaffBarlineInter}. - * - * @author Hervé Bitteur - */ -@Deprecated -@XmlAccessorType(XmlAccessType.NONE) -public class OldStaffBarline -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** Abscissa-ordered sequence of physical barlines. */ - @XmlList - @XmlIDREF - @XmlValue - public final ArrayList bars = new ArrayList(); -} +//------------------------------------------------------------------------------------------------// +// // +// O l d S t a f f B a r l i n e // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sheet; + +import org.audiveris.omr.sig.inter.BarlineInter; +import org.audiveris.omr.sig.inter.StaffBarlineInter; + +import java.util.ArrayList; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlIDREF; +import javax.xml.bind.annotation.XmlList; +import javax.xml.bind.annotation.XmlValue; + +/** + * Class {@code OldStaffBarline} is a temporary fix to keep compatibility with old + * .omr files. + *

                                    + * Replaced by {@link StaffBarlineInter}. + * + * @author Hervé Bitteur + */ +@Deprecated +@XmlAccessorType(XmlAccessType.NONE) +public class OldStaffBarline +{ + + /** Abscissa-ordered sequence of physical barlines. */ + @XmlList + @XmlIDREF + @XmlValue + public final ArrayList bars = new ArrayList<>(); +} diff --git a/src/main/org/audiveris/omr/sheet/PageCleaner.java b/src/main/org/audiveris/omr/sheet/PageCleaner.java index 483176c45..7e9217e36 100644 --- a/src/main/org/audiveris/omr/sheet/PageCleaner.java +++ b/src/main/org/audiveris/omr/sheet/PageCleaner.java @@ -73,13 +73,14 @@ * Class {@code PageCleaner} erases selected inter instances on the provided graphics * environment by painting them using white background color. *

                                    - * Painting uses various techniques:

                                      + * Painting uses various techniques: + *
                                        *
                                      • Default is to use inter shape to paint the shape symbol with a thicker * {@link #musicFont}.
                                      • *
                                      • For an area-based inter, area is filled exactly and area contour is drawn with * {@link #marginStroke}.
                                      • - *
                                      • For a glyph-based inter, all glyph runs are painted with no margin.
                                      • - *
                                      • For a line-based inter, the line is drawn with a thicker {@link #lineStroke}.
                                      • + *
                                      • For a glyph-based inter, all glyph runs are painted with no margin.
                                      • + *
                                      • For a line-based inter, the line is drawn with a thicker {@link #lineStroke}.
                                      • *
                                      * * @author Hervé Bitteur @@ -87,13 +88,11 @@ public abstract class PageCleaner extends AbstractInterVisitor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(PageCleaner.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Sheet buffer. */ protected final ByteProcessor buffer; @@ -118,7 +117,6 @@ public abstract class PageCleaner /** Slightly thicker stroke for lines. (endings, wedges, slurs) */ private final Stroke lineStroke; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PageCleaner} object. * @@ -167,7 +165,6 @@ public PageCleaner (ByteProcessor buffer, g.setColor(Color.WHITE); } - //~ Methods ------------------------------------------------------------------------------------ @Override public void visit (AbstractBeamInter inter) { @@ -238,8 +235,7 @@ public void visit (HeadInter head) { ShapeSymbol symbol = Symbols.getSymbol(head.getShape()); Glyph glyph = head.getGlyph(); - Point center = (glyph != null) ? glyph.getCenter() : GeoUtil.centerOf( - head.getBounds()); + Point center = (glyph != null) ? glyph.getCenter() : GeoUtil.centerOf(head.getBounds()); MusicFont font = head.getStaff().isSmall() ? smallHeadMusicFont : headMusicFont; symbol.paintSymbol(g, font, center, Alignment.AREA_CENTER); } @@ -254,8 +250,7 @@ public void visit (Inter inter) { ShapeSymbol symbol = Symbols.getSymbol(inter.getShape()); Glyph glyph = inter.getGlyph(); - Point center = (glyph != null) ? glyph.getCenter() : GeoUtil.centerOf( - inter.getBounds()); + Point center = (glyph != null) ? glyph.getCenter() : GeoUtil.centerOf(inter.getBounds()); boolean isSmall = (inter.getStaff() != null) && inter.getStaff().isSmall(); MusicFont font = isSmall ? smallMusicFont : musicFont; symbol.paintSymbol(g, font, center, Alignment.AREA_CENTER); @@ -459,14 +454,12 @@ private int dilated (int pointSize) return (int) Math.rint(pointSize * constants.dilationRatio.getValue()); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio dilationRatio = new Constant.Ratio( 1.1, diff --git a/src/main/org/audiveris/omr/sheet/Part.java b/src/main/org/audiveris/omr/sheet/Part.java index 9e61ce03c..979b28056 100644 --- a/src/main/org/audiveris/omr/sheet/Part.java +++ b/src/main/org/audiveris/omr/sheet/Part.java @@ -93,10 +93,8 @@ @XmlAccessorType(XmlAccessType.NONE) public class Part { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - Part.class); + private static final Logger logger = LoggerFactory.getLogger(Part.class); /** For comparing Part instances according to their id. */ public static final Comparator byId = new Comparator() @@ -109,8 +107,6 @@ public int compare (Part p1, } }; - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -133,7 +129,7 @@ public int compare (Part p1, /** Staves in this part. */ @XmlElement(name = "staff") - private final List staves = new ArrayList(); + private final List staves = new ArrayList<>(); /** Starting barline, if any. (the others are linked to measures) */ @XmlElement(name = "left-barline") @@ -141,7 +137,7 @@ public int compare (Part p1, /** Measures in this part. */ @XmlElement(name = "measure") - private final List measures = new ArrayList(); + private final List measures = new ArrayList<>(); /** Lyric lines in this part. To be kept sorted vertically. */ @XmlList @@ -162,7 +158,6 @@ public int compare (Part p1, @Navigable(false) private SystemInfo system; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new instance of {@code Part}. * @@ -180,14 +175,18 @@ private Part () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // addLyric // //----------// + /** + * Add a lyric line. + * + * @param lyric the lyric line to add + */ public void addLyric (LyricLineInter lyric) { if (lyrics == null) { - lyrics = new ArrayList(); + lyrics = new ArrayList<>(); } lyrics.add(lyric); @@ -196,6 +195,11 @@ public void addLyric (LyricLineInter lyric) //------------// // addmeasure // //------------// + /** + * Append a measure. + * + * @param measure the measure to append + */ public void addMeasure (Measure measure) { measures.add(measure); @@ -204,6 +208,12 @@ public void addMeasure (Measure measure) //------------// // addmeasure // //------------// + /** + * Insert a measure at specified index. + * + * @param index the specified index + * @param measure the measure to insert + */ public void addMeasure (int index, Measure measure) { @@ -213,10 +223,15 @@ public void addMeasure (int index, //---------// // addSlur // //---------// + /** + * Add a slur. + * + * @param slur the slur to add to this part + */ public void addSlur (SlurInter slur) { if (slurs == null) { - slurs = new ArrayList(); + slurs = new ArrayList<>(); } slurs.add(slur); @@ -225,6 +240,11 @@ public void addSlur (SlurInter slur) //----------// // addStaff // //----------// + /** + * Append a staff. + * + * @param staff the staff to append to this staff + */ public void addStaff (Staff staff) { staves.add(staff); @@ -234,6 +254,9 @@ public void addStaff (Staff staff) //-------------// // afterReload // //-------------// + /** + * To be called right after unmarshalling. + */ public void afterReload () { try { @@ -270,7 +293,7 @@ public void afterReload () *
                                    • The created inters (clef, key, time, rests) are not inserted in sig, only in the * (dummy) measures of the (dummy) part.
                                    • *
                                    • The created dummy part is not inserted in system list of parts.
                                    • - *
                                    • Similarly, the (dummy) measures of this dummy part are not inserted in + *
                                    • Similarly, the (dummy) measures of this dummy part are not inserted in * MeasureStack instances that play only with real measures.
                                    • *
                                    *

                                    @@ -358,7 +381,8 @@ public Part createDummyPart (int id) *

                                    * Important: Nothing is written in slurs yet, only in links map. *

                                    - * This method is called in two contexts:

                                      + * This method is called in two contexts: + *
                                        *
                                      1. Within a page: it processes slur connections between systems of the page. *
                                      2. Within a score: it processes slur connections between pages of a score. *
                                      @@ -372,7 +396,7 @@ public Map getCrossSlurLinks (Part precedingPart) Objects.requireNonNull(precedingPart, "Null part to connect Slurs with"); // Links: Slur -> prevSlur - Map links = new LinkedHashMap(); + Map links = new LinkedHashMap<>(); // Orphans slurs at the beginning of the current system part List orphans = getSlurs(SlurInter.isBeginningOrphan); @@ -471,6 +495,25 @@ public int getId () return id; } + //-------// + // setId // + //-------// + /** + * Set the part id. + * + * @param id the new id value + */ + public void setId (int id) + { + if (this.id != id) { + this.id = id; + + if (!isDummy()) { + getSystem().getSheet().getStub().setModified(true); + } + } + } + //----------------// // getLastMeasure // //----------------// @@ -512,9 +555,27 @@ public PartBarline getLeftPartBarline () return leftBarline; } + //--------------------// + // setLeftPartBarline // + //--------------------// + /** + * Set the PartBarline that starts the part. + * + * @param leftBarline the starting PartBarline + */ + public void setLeftPartBarline (PartBarline leftBarline) + { + this.leftBarline = leftBarline; + } + //----------------// // getLogicalPart // //----------------// + /** + * Report the LogicalPart this (physical) part implements. + * + * @return the logical part. + */ public LogicalPart getLogicalPart () { return system.getPage().getLogicalPartById(id); @@ -523,6 +584,11 @@ public LogicalPart getLogicalPart () //-----------// // getLyrics // //-----------// + /** + * Report the sequence of lyric lines in this part. + * + * @return list of lyrics, perhaps empty + */ public List getLyrics () { return (lyrics != null) ? Collections.unmodifiableList(lyrics) : Collections.EMPTY_LIST; @@ -583,6 +649,19 @@ public String getName () return (name != null) ? name.getValue() : null; } + //---------// + // setName // + //---------// + /** + * Assign a name to this part. + * + * @param name the name to set + */ + public void setName (SentenceInter name) + { + this.name = name; + } + //--------// // getPid // //--------// @@ -626,7 +705,7 @@ public Part getPrecedingInPage () */ public List getSlurs (Predicate predicate) { - List selectedSlurs = new ArrayList(); + List selectedSlurs = new ArrayList<>(); if (slurs != null) { for (SlurInter slur : slurs) { @@ -700,6 +779,11 @@ public StaffPosition getStaffPosition (Point2D point) //-----------// // getStaves // //-----------// + /** + * Report the sequence of staves in this part. + * + * @return list of staves + */ public List getStaves () { return staves; @@ -708,14 +792,37 @@ public List getStaves () //-----------// // getSystem // //-----------// + /** + * Report the containing system + * + * @return containing system + */ public SystemInfo getSystem () { return system; } + //-----------// + // setSystem // + //-----------// + /** + * Assign the containing system. + * + * @param system the containing system + */ + public void setSystem (SystemInfo system) + { + this.system = system; + } + //---------// // isDummy // //---------// + /** + * Tell whether this part is a dummy (that lives only for the duration of an export) + * + * @return true if so + */ public boolean isDummy () { return dummy; @@ -724,6 +831,9 @@ public boolean isDummy () //-----------------// // purgeContainers // //-----------------// + /** + * Update the internal collection of lyrics, by removing the deleted ones. + */ public void purgeContainers () { // Lyrics @@ -741,20 +851,30 @@ public void purgeContainers () //-------------// // removeLyric // //-------------// + /** + * Remove a lyric line. + * + * @param lyric the lyric line to remove + */ public void removeLyric (LyricLineInter lyric) { if (lyrics != null) { lyrics.remove(lyric); - } - if (lyrics.isEmpty()) { - lyrics = null; + if (lyrics.isEmpty()) { + lyrics = null; + } } } //---------------// // removeMeasure // //---------------// + /** + * Remove a measure. + * + * @param measure the measure to remove from this part + */ public void removeMeasure (Measure measure) { measures.remove(measure); @@ -787,60 +907,12 @@ public boolean removeSlur (SlurInter slur) //----------// // setDummy // //----------// - public void setDummy () - { - dummy = Boolean.TRUE; - } - - //-------// - // setId // - //-------// - /** - * Set the part id. - * - * @param id the new id value - */ - public void setId (int id) - { - if (this.id != id) { - this.id = id; - - if (!isDummy()) { - getSystem().getSheet().getStub().setModified(true); - } - } - } - - //--------------------// - // setLeftPartBarline // - //--------------------// - /** - * Set the PartBarline that starts the part. - * - * @param leftBarline the starting PartBarline - */ - public void setLeftPartBarline (PartBarline leftBarline) - { - this.leftBarline = leftBarline; - } - - //---------// - // setName // - //---------// /** - * @param name the name to set + * Assign this part as being dummy. */ - public void setName (SentenceInter name) - { - this.name = name; - } - - //-----------// - // setSystem // - //-----------// - public void setSystem (SystemInfo system) + public void setDummy () { - this.system = system; + dummy = Boolean.TRUE; } //----------------// diff --git a/src/main/org/audiveris/omr/sheet/PartBarline.java b/src/main/org/audiveris/omr/sheet/PartBarline.java index 6b428e7f7..8e612832a 100644 --- a/src/main/org/audiveris/omr/sheet/PartBarline.java +++ b/src/main/org/audiveris/omr/sheet/PartBarline.java @@ -60,33 +60,9 @@ @XmlRootElement(name = "part-barline") public class PartBarline { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(PartBarline.class); - //~ Enumerations ------------------------------------------------------------------------------- - /** - * Barline style. - * Identical to (or subset of) MusicXML BarStyle, to avoid strict dependency on MusicXML. - */ - public static enum Style - { - //~ Enumeration constant initializers ------------------------------------------------------ - - REGULAR, - DOTTED, - DASHED, - HEAVY, - LIGHT_LIGHT, - LIGHT_HEAVY, - HEAVY_LIGHT, - HEAVY_HEAVY, - TICK, - SHORT, - NONE; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** OLD underlying {@link StaffBarlineInter} instances. */ @Deprecated @XmlElement(name = "staff-barline") @@ -96,9 +72,8 @@ public static enum Style @XmlList @XmlIDREF @XmlElement(name = "staff-barlines") - private final List staffBarlines = new ArrayList(); + private final List staffBarlines = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PartBarline} object. */ @@ -106,10 +81,14 @@ public PartBarline () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // addStaffBarline // //-----------------// + /** + * Include a staff barline into this PartBarline. + * + * @param staffBarline the StaffBarlineInter to include + */ public void addStaffBarline (StaffBarlineInter staffBarline) { Objects.requireNonNull(staffBarline, "Trying to add a null StaffBarline"); @@ -120,6 +99,12 @@ public void addStaffBarline (StaffBarlineInter staffBarline) //----------// // contains // //----------// + /** + * Report whether this PartBarline contains the provided StaffBarline. + * + * @param staffBarline the StaffBarlineInter to check + * @return true if contained + */ public boolean contains (StaffBarlineInter staffBarline) { return staffBarlines.contains(staffBarline); @@ -151,14 +136,14 @@ public EndingInter getEnding (HorizontalSide side) */ public List getFermatas () { - Set fermatas = new LinkedHashSet(); + Set fermatas = new LinkedHashSet<>(); for (StaffBarlineInter sb : staffBarlines) { fermatas.addAll(sb.getFermatas()); } if (!fermatas.isEmpty()) { - List list = new ArrayList(fermatas); + List list = new ArrayList<>(fermatas); Collections.sort(list, Inters.byCenterOrdinate); return list; @@ -214,6 +199,13 @@ public int getRightX (Part part, //-----------------// // getStaffBarline // //-----------------// + /** + * Report the StaffBarline at crossing of part with provided staff. + * + * @param part containing part + * @param staff desired staff + * @return the StaffBarline found or null + */ public StaffBarlineInter getStaffBarline (Part part, Staff staff) { @@ -229,6 +221,11 @@ public StaffBarlineInter getStaffBarline (Part part, //------------------// // getStaffBarlines // //------------------// + /** + * Report the vertical sequence of StaffBarlineInter instances. + * + * @return list of StaffBarline instances + */ public List getStaffBarlines () { return Collections.unmodifiableList(staffBarlines); @@ -237,6 +234,11 @@ public List getStaffBarlines () //----------// // getStyle // //----------// + /** + * Report the (part) barline style. + * + * @return barline style, or null if no StaffBarline is contained. + */ public Style getStyle () { if (staffBarlines.isEmpty()) { @@ -249,6 +251,11 @@ public Style getStyle () //--------------// // isLeftRepeat // //--------------// + /** + * Report whether this is a left repeat barline. + * + * @return true if so + */ public boolean isLeftRepeat () { for (StaffBarlineInter sb : staffBarlines) { @@ -263,6 +270,11 @@ public boolean isLeftRepeat () //---------------// // isRightRepeat // //---------------// + /** + * Report whether this is a right repeat barline. + * + * @return true if so + */ public boolean isRightRepeat () { for (StaffBarlineInter sb : staffBarlines) { @@ -280,17 +292,21 @@ public boolean isRightRepeat () @Override public String toString () { - StringBuilder sb = new StringBuilder("{PartBarline"); - StaffBarlineInter first = staffBarlines.get(0); - StaffBarlineInter last = staffBarlines.get(staffBarlines.size() - 1); + final StringBuilder sb = new StringBuilder("{PartBarline"); - Style style = first.getStyle(); - sb.append(" ").append(style); + if (!staffBarlines.isEmpty()) { + StaffBarlineInter first = staffBarlines.get(0); + StaffBarlineInter last = staffBarlines.get(staffBarlines.size() - 1); - Rectangle box = new Rectangle(first.getCenter()); - box.add(last.getCenter()); - sb.append(" ").append(PointUtil.toString(GeoUtil.centerOf(box))); - sb.append("}"); + Style style = first.getStyle(); + sb.append(' ').append(style); + + Rectangle box = new Rectangle(first.getCenter()); + box.add(last.getCenter()); + sb.append(' ').append(PointUtil.toString(GeoUtil.centerOf(box))); + } + + sb.append('}'); return sb.toString(); } @@ -303,6 +319,7 @@ public String toString () * * @return true if really upgraded */ + @Deprecated public boolean upgradeOldStuff () { if (oldStaffBarlines != null) { @@ -319,4 +336,28 @@ public boolean upgradeOldStuff () return false; } + + /** + * Barline style. + *

                                      + * Identical to (or subset of) MusicXML BarStyle, to avoid strict dependency on MusicXML with + * the addition of LIGHT_HEAVY_LIGHT to handle back-to-back configuration. + */ + public static enum Style + { + REGULAR, + DOTTED, + DASHED, + HEAVY, + LIGHT_LIGHT, + LIGHT_HEAVY, + HEAVY_LIGHT, + HEAVY_HEAVY, + TICK, + SHORT, + NONE, + + /** LIGHT_HEAVY_LIGHT is not part of MusicXML. */ + LIGHT_HEAVY_LIGHT; + } } diff --git a/src/main/org/audiveris/omr/sheet/Picture.java b/src/main/org/audiveris/omr/sheet/Picture.java index e3b5465f4..ce7555c5b 100644 --- a/src/main/org/audiveris/omr/sheet/Picture.java +++ b/src/main/org/audiveris/omr/sheet/Picture.java @@ -42,7 +42,6 @@ import org.audiveris.omr.ui.selection.MouseMovement; import org.audiveris.omr.ui.selection.PixelEvent; import org.audiveris.omr.ui.selection.SelectionService; -import org.audiveris.omr.util.Jaxb; import org.audiveris.omr.util.Navigable; import org.audiveris.omr.util.StopWatch; @@ -67,12 +66,13 @@ import java.util.concurrent.ConcurrentSkipListMap; import javax.media.jai.JAI; -import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.stream.XMLStreamException; /** * Class {@code Picture} starts from the original BufferedImage to provide all {@link @@ -89,11 +89,9 @@ * Any instance of this class is registered on the related Sheet location service, so that each time * a location event is received, the corresponding pixel gray value of the INITIAL sources is * published. - * *

                                      * TODO: When an alpha channel is involved, perform the alpha multiplication if the components are * not yet premultiplied. - * *

                                      Overview of transforms:
                                      * Image Transforms UML *

                                      @@ -106,45 +104,11 @@ public class Picture implements EventSubscriber { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(Picture.class); - //~ Enumerations ------------------------------------------------------------------------------- - /** - * The set of handled sources. - */ - public static enum SourceKey - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** The initial gray-level source. */ - INITIAL, - /** The binarized (black & white) source. */ - BINARY, - /** The Gaussian-filtered source. */ - GAUSSIAN, - /** The Median-filtered source. */ - MEDIAN, - /** The source with staff lines removed. */ - NO_STAFF; - } - - /** - * The set of handled tables. - */ - public static enum TableKey - { - //~ Enumeration constant initializers ------------------------------------------------------ - - BINARY, - HEAD_SPOTS; - } - - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -158,29 +122,28 @@ public static enum TableKey /** Map of all handled run tables. */ @XmlElement(name = "tables") - private final EnumMap tables = new EnumMap( - TableKey.class); + private final EnumMap tables = new EnumMap<>(TableKey.class); // Transient data //--------------- // /** Map of all handled sources. */ - private final ConcurrentSkipListMap> sources = new ConcurrentSkipListMap>(); + private final ConcurrentSkipListMap> sources + = new ConcurrentSkipListMap<>(); /** Related sheet. */ @Navigable(false) private Sheet sheet; + /** The initial (gray-level) image, if any. */ + private BufferedImage initialImage; + /** * Service object where gray level of pixel is to be written to when so asked for * by the onEvent() method. */ final SelectionService pixelService; - /** The initial (gray-level) image, if any. */ - private BufferedImage initialImage; - - //~ Constructors ------------------------------------------------------------------------------- /** * Build a picture instance from a binary table. * @@ -242,10 +205,14 @@ private Picture () logger.debug("Picture unmarshalled by JAXB"); } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // buildNoStaffTable // //-------------------// + /** + * Build the table without staff lines. + * + * @return the no-staff table + */ public RunTable buildNoStaffTable () { ByteProcessor source = getSource(SourceKey.NO_STAFF); @@ -260,6 +227,11 @@ public RunTable buildNoStaffTable () //---------------------------// // buildStaffLineGlyphsImage // //---------------------------// + /** + * Build an image containing just the removed staff lines. + * + * @return image of staff lines + */ public BufferedImage buildStaffLineGlyphsImage () { BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); @@ -285,7 +257,9 @@ public BufferedImage buildStaffLineGlyphsImage () return img; } - // For debug only + /** + * For debug only. + */ public void checkSources () { for (SourceKey key : SourceKey.values()) { @@ -296,6 +270,11 @@ public void checkSources () //---------------// // disposeSource // //---------------// + /** + * Dispose of the source related to the provided key. + * + * @param key provided key + */ public void disposeSource (SourceKey key) { // Nullify cached data, if needed @@ -306,21 +285,6 @@ public void disposeSource (SourceKey key) sources.remove(key); } - // - // //--------------// - // // disposeTable // - // //--------------// - // public void disposeTable (TableKey key) - // { - // RunTableHolder tableHolder = tables.get(key); - // - // if (tableHolder != null) { - // tableHolder.store(); - // tableHolder.setData(null); - // logger.debug("{} table disposed.", key); - // } - // } - // //---------------// // dumpRectangle // //---------------// @@ -392,6 +356,12 @@ public void dumpRectangle (SourceKey key, //------------------// // gaussianFiltered // //------------------// + /** + * Apply a Gaussian filter on the provided source. + * + * @param src provided source + * @return filtered buffer + */ public ByteProcessor gaussianFiltered (ByteProcessor src) { StopWatch watch = new StopWatch("Gaussian"); @@ -456,7 +426,8 @@ public BufferedImage getImage (Rectangle rect) //-----------------// // getInitialImage // //-----------------// - /** Report the initial (BufferedImage) image. + /** + * Report the initial (BufferedImage) image. * * @return the initial image */ @@ -468,7 +439,8 @@ public BufferedImage getInitialImage () //------------------// // getInitialSource // //------------------// - /** Report the initial source. + /** + * Report the initial source. * * @param img the initial image * @return the initial source @@ -580,7 +552,7 @@ public ByteProcessor getSource (SourceKey key) if (src != null) { // Store in cache - sources.put(key, new WeakReference(src)); + sources.put(key, new WeakReference<>(src)); logger.debug("{} source built as {}", key, src); } } @@ -660,6 +632,12 @@ public boolean hasTableReady (TableKey key) //----------------// // medianFiltered // //----------------// + /** + * Apply a median filter on the provided source buffer. + * + * @param src provided source + * @return filtered buffer + */ public ByteProcessor medianFiltered (ByteProcessor src) { StopWatch watch = new StopWatch("Median"); @@ -759,6 +737,9 @@ public final void setTable (TableKey key, tables.put(key, tableHolder); switch (key) { + default: + break; + case BINARY: disposeSource(SourceKey.BINARY); } @@ -767,6 +748,12 @@ public final void setTable (TableKey key, //-------// // store // //-------// + /** + * Store the picture tables + * + * @param sheetFolder target sheet folder + * @param oldSheetFolder optional source sheet folder (or null) + */ public void store (Path sheetFolder, Path oldSheetFolder) { @@ -792,10 +779,12 @@ public void store (Path sheetFolder, Files.deleteIfExists(tablepath); RunTable table = holder.getData(sheet.getStub()); - Jaxb.marshal(table, tablepath, JAXBContext.newInstance(RunTable.class)); + table.marshal(tablepath); holder.setModified(false); logger.info("Stored {}", tablepath); - } catch (Exception ex) { + } catch (IOException | + JAXBException | + XMLStreamException ex) { logger.warn("Error in picture.store " + ex, ex); } } @@ -811,20 +800,6 @@ public String toString () return getName(); } - //----------------// - // initTransients // - //----------------// - /** - * (package private) method to initialize needed transient members. - * (which by definition have not been set by the unmarshalling). - * - * @param sheet the containing sheet - */ - final void initTransients (Sheet sheet) - { - this.sheet = sheet; - } - //-------------------// // adjustImageFormat // //-------------------// @@ -898,12 +873,14 @@ private ByteProcessor buildNoStaffBuffer () if (glyph == null) { logger.warn("glyph is null for line " + line + " staff:" + staff); - } else if (glyph.getRunTable() == null) { - logger.warn("glyph runtable is null"); + } else { + if (glyph.getRunTable() == null) { + logger.warn("glyph runtable is null"); + } else { + glyph.getRunTable().render(g, glyph.getTopLeft()); + linesErased = true; + } } - - linesErased = true; - glyph.getRunTable().render(g, glyph.getTopLeft()); } } } @@ -969,14 +946,52 @@ private ByteProcessor getStrongRef (SourceKey key) return null; } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------------// + // initTransients // + //----------------// + /** + * (package private) method to initialize needed transient members. + * (which by definition have not been set by the unmarshalling). + * + * @param sheet the containing sheet + */ + final void initTransients (Sheet sheet) + { + this.sheet = sheet; + } + + /** + * The set of handled sources. + */ + public static enum SourceKey + { + /** The initial gray-level source. */ + INITIAL, + /** The binarized (black & white) source. */ + BINARY, + /** The Gaussian-filtered source. */ + GAUSSIAN, + /** The Median-filtered source. */ + MEDIAN, + /** The source with staff lines removed. */ + NO_STAFF; + } + + /** + * The set of handled tables. + */ + public static enum TableKey + { + BINARY, + HEAD_SPOTS; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/sheet/ProcessingSwitches.java b/src/main/org/audiveris/omr/sheet/ProcessingSwitches.java index 5b5159a7d..574b3481e 100644 --- a/src/main/org/audiveris/omr/sheet/ProcessingSwitches.java +++ b/src/main/org/audiveris/omr/sheet/ProcessingSwitches.java @@ -1,318 +1,306 @@ -//------------------------------------------------------------------------------------------------// -// // -// S w i t c h e s P a r a m // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.util.param.BooleanParam; -import org.audiveris.omr.util.param.ConstantBasedParam; -import org.audiveris.omr.util.param.Param; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlValue; -import javax.xml.bind.annotation.adapters.XmlAdapter; - -/** - * Class {@code ProcessingSwitches} handles a set of named processing switches. - * - * @author Hervé Bitteur - */ -public class ProcessingSwitches -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger(ProcessingSwitches.class); - - /** Default switches values. */ - private static ProcessingSwitches defaultSwitches; - - //~ Enumerations ------------------------------------------------------------------------------- - /** Enumerated names, based on defined constants. */ - public static enum Switch - { - //~ Enumeration constant initializers ------------------------------------------------------ - - articulations(constants.articulations), - chordNames(constants.chordNames), - fingerings(constants.fingerings), - frets(constants.frets), - pluckings(constants.pluckings), - lyrics(constants.lyrics), - lyricsAboveStaff(constants.lyricsAboveStaff), - smallBlackHeads(constants.smallBlackHeads), - smallVoidHeads(constants.smallVoidHeads), - smallWholeHeads(constants.smallWholeHeads); - - //~ Instance fields ------------------------------------------------------------------------ - /** Underlying boolean constant. */ - Constant.Boolean constant; - - //~ Constructors --------------------------------------------------------------------------- - Switch (Constant.Boolean constant) - { - this.constant = constant; - } - - //~ Methods -------------------------------------------------------------------------------- - public Constant.Boolean getConstant () - { - return constant; - } - } - - //~ Instance fields ---------------------------------------------------------------------------- - /** Parent switches, if any. */ - private ProcessingSwitches parent; - - /** Map of switches. */ - protected final EnumMap> map = new EnumMap>( - Switch.class); - - //~ Methods ------------------------------------------------------------------------------------ - public static ProcessingSwitches getDefaultSwitches () - { - // Workaround for elaboration circularity - if (defaultSwitches == null) { - constants.initialize(); - defaultSwitches = new DefaultSwitches(); - } - - return defaultSwitches; - } - - public Param getParam (Switch key) - { - return map.get(key); - } - - public Boolean getSpecific (Switch key) - { - Param param = getParam(key); - - if (param == null) { - return null; - } - - return param.getSpecific(); - } - - public Boolean getValue (Switch key) - { - Param param = getParam(key); - - if (param == null) { - return null; - } - - return param.getValue(); - } - - public boolean isEmpty () - { - for (Entry> entry : map.entrySet()) { - if (entry.getValue().isSpecific()) { - return false; - } - } - - return true; - } - - public void setParent (ProcessingSwitches parent) - { - this.parent = parent; - - // Populate the map - for (Switch key : Switch.values()) { - Param param = getParam(key); - - if (param == null) { - param = new BooleanParam(); - param.setParent(parent.getParam(key)); - map.put(key, param); - } - } - } - - public void setSpecific (Switch key, - Boolean specific) - { - if (specific == null) { - map.remove(key); - } else { - Param param = getParam(key); - - if (param == null) { - map.put(key, param = new BooleanParam()); - - if (parent != null) { - param.setParent(parent.getParam(key)); - } - } - - param.setSpecific(specific); - } - } - - //~ Inner Classes ------------------------------------------------------------------------------ - public static class Adapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public MyEntries marshal (ProcessingSwitches switches) - throws Exception - { - if (switches == null) { - return null; - } - - final MyEntries myList = new MyEntries(); - - for (Map.Entry> entry : switches.map.entrySet()) { - MyEntry myEntry = new MyEntry(); - myEntry.key = entry.getKey(); - myEntry.value = entry.getValue().getSpecific(); - - if (myEntry.value != null) { - myList.entries.add(myEntry); - } - } - - return myList; - } - - @Override - public ProcessingSwitches unmarshal (MyEntries value) - throws Exception - { - ProcessingSwitches switches = new ProcessingSwitches(); - - for (MyEntry entry : value.entries) { - BooleanParam b = new BooleanParam(); - - if (entry.value != null) { - b.setSpecific(entry.value); - switches.map.put(entry.key, b); - } - } - - return switches; - } - - //~ Inner Classes -------------------------------------------------------------------------- - public static final class MyEntries - { - //~ Instance fields -------------------------------------------------------------------- - - @XmlElement(name = "switch") - List entries = new ArrayList(); - } - - public static final class MyEntry - { - //~ Instance fields -------------------------------------------------------------------- - - @XmlAttribute(name = "key") - public Switch key; - - @XmlValue - public Boolean value; - } - } - - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean articulations = new Constant.Boolean( - true, - "Support for articulations"); - - private final Constant.Boolean chordNames = new Constant.Boolean( - false, - "Support for chord names"); - - private final Constant.Boolean fingerings = new Constant.Boolean( - false, - "Support for fingering digits"); - - private final Constant.Boolean frets = new Constant.Boolean( - false, - "Support for frets roman digits (I, II, IV...)"); - - private final Constant.Boolean pluckings = new Constant.Boolean( - false, - "Support for plucking (p, i, m, a)"); - - private final Constant.Boolean lyrics = new Constant.Boolean(true, "Support for lyrics"); - - private final Constant.Boolean lyricsAboveStaff = new Constant.Boolean( - false, - "Support for lyrics even located above staff"); - - private final Constant.Boolean smallBlackHeads = new Constant.Boolean( - false, - "Support for small black note heads"); - - private final Constant.Boolean smallVoidHeads = new Constant.Boolean( - false, - "Support for small void note heads"); - - private final Constant.Boolean smallWholeHeads = new Constant.Boolean( - false, - "Support for small whole note heads"); - } - - //-----------------// - // DefaultSwitches // - //-----------------// - private static class DefaultSwitches - extends ProcessingSwitches - { - //~ Constructors --------------------------------------------------------------------------- - - public DefaultSwitches () - { - for (Switch key : Switch.values()) { - map.put(key, new ConstantBasedParam(key.getConstant())); - } - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// P r o c e s s i n g S w i t c h e s // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sheet; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.util.param.BooleanParam; +import org.audiveris.omr.util.param.ConstantBasedParam; +import org.audiveris.omr.util.param.Param; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlValue; +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * Class {@code ProcessingSwitches} handles a set of named processing switches. + * + * @author Hervé Bitteur + */ +public class ProcessingSwitches +{ + + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(ProcessingSwitches.class); + + /** Default switches values. */ + private static volatile ProcessingSwitches defaultSwitches; + + /** Map of switches. */ + protected final EnumMap> map = new EnumMap<>(Switch.class); + + /** Parent switches, if any. */ + private ProcessingSwitches parent; + + /** + * Report the parameter for the provided key. + * + * @param key provided key + * @return related parameter + */ + public Param getParam (Switch key) + { + return map.get(key); + } + + /** + * Report the current value for the provided key. + * + * @param key provided key + * @return current value, perhaps null + */ + public Boolean getValue (Switch key) + { + Param param = getParam(key); + + if (param == null) { + return null; + } + + return param.getValue(); + } + + /** + * Report whether this object provided no specific information. + * + * @return true if empty + */ + public boolean isEmpty () + { + for (Entry> entry : map.entrySet()) { + if (entry.getValue().isSpecific()) { + return false; + } + } + + return true; + } + + /** + * Assign the parent of this object. + * + * @param parent the parent to assign + */ + public void setParent (ProcessingSwitches parent) + { + this.parent = parent; + + // Populate the map + for (Switch key : Switch.values()) { + Param param = getParam(key); + + if (param == null) { + param = new BooleanParam(); + param.setParent(parent.getParam(key)); + map.put(key, param); + } + } + } + + /** + * Report the top level switches, which provide default values. + * + * @return top default switches. + */ + public static ProcessingSwitches getDefaultSwitches () + { + // Workaround for elaboration circularity + if (defaultSwitches == null) { + constants.initialize(); + defaultSwitches = new DefaultSwitches(); + } + + return defaultSwitches; + } + + /** Enumerated names, based on defined constants. */ + public static enum Switch + { + articulations(constants.articulations), + chordNames(constants.chordNames), + fingerings(constants.fingerings), + frets(constants.frets), + pluckings(constants.pluckings), + lyrics(constants.lyrics), + lyricsAboveStaff(constants.lyricsAboveStaff), + smallBlackHeads(constants.smallBlackHeads), + smallVoidHeads(constants.smallVoidHeads), + smallWholeHeads(constants.smallWholeHeads); + + /** Underlying boolean constant. */ + Constant.Boolean constant; + + Switch (Constant.Boolean constant) + { + this.constant = constant; + } + + public Constant.Boolean getConstant () + { + return constant; + } + } + + /** + * JAXB adapter for ProcessingSwitches type. + */ + public static class Adapter + extends XmlAdapter + { + + @Override + public MyEntries marshal (ProcessingSwitches switches) + throws Exception + { + if (switches == null) { + return null; + } + + final MyEntries myList = new MyEntries(); + + for (Map.Entry> entry : switches.map.entrySet()) { + MyEntry myEntry = new MyEntry(); + myEntry.key = entry.getKey(); + myEntry.value = entry.getValue().getSpecific(); + + if (myEntry.value != null) { + myList.entries.add(myEntry); + } + } + + return myList; + } + + @Override + public ProcessingSwitches unmarshal (MyEntries value) + throws Exception + { + ProcessingSwitches switches = new ProcessingSwitches(); + + for (MyEntry entry : value.entries) { + BooleanParam b = new BooleanParam(); + + if (entry.value != null) { + b.setSpecific(entry.value); + switches.map.put(entry.key, b); + } + } + + return switches; + } + + /** + * Flat list of entries. + */ + public static class MyEntries + { + + @XmlElement(name = "switch") + List entries = new ArrayList<>(); + } + + /** + * Plain entry: key / value. + */ + public static class MyEntry + { + + @XmlAttribute(name = "key") + public Switch key; + + @XmlValue + public Boolean value; + } + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean articulations = new Constant.Boolean( + true, + "Support for articulations"); + + private final Constant.Boolean chordNames = new Constant.Boolean( + false, + "Support for chord names"); + + private final Constant.Boolean fingerings = new Constant.Boolean( + false, + "Support for fingering digits"); + + private final Constant.Boolean frets = new Constant.Boolean( + false, + "Support for frets roman digits (I, II, IV...)"); + + private final Constant.Boolean pluckings = new Constant.Boolean( + false, + "Support for plucking (p, i, m, a)"); + + private final Constant.Boolean lyrics = new Constant.Boolean(true, "Support for lyrics"); + + private final Constant.Boolean lyricsAboveStaff = new Constant.Boolean( + false, + "Support for lyrics even located above staff"); + + private final Constant.Boolean smallBlackHeads = new Constant.Boolean( + false, + "Support for small black note heads"); + + private final Constant.Boolean smallVoidHeads = new Constant.Boolean( + false, + "Support for small void note heads"); + + private final Constant.Boolean smallWholeHeads = new Constant.Boolean( + false, + "Support for small whole note heads"); + } + + //-----------------// + // DefaultSwitches // + //-----------------// + private static class DefaultSwitches + extends ProcessingSwitches + { + + DefaultSwitches () + { + for (Switch key : Switch.values()) { + map.put(key, new ConstantBasedParam(key.getConstant())); + } + } + } +} diff --git a/src/main/org/audiveris/omr/sheet/RunTableHolder.java b/src/main/org/audiveris/omr/sheet/RunTableHolder.java index b03bbb7f9..f2dd1396a 100644 --- a/src/main/org/audiveris/omr/sheet/RunTableHolder.java +++ b/src/main/org/audiveris/omr/sheet/RunTableHolder.java @@ -27,13 +27,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.InputStream; -import java.nio.file.Files; +import java.io.IOException; import java.nio.file.Path; -import java.nio.file.StandardOpenOption; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; @@ -47,12 +42,9 @@ @XmlAccessorType(value = XmlAccessType.NONE) public class RunTableHolder { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - RunTableHolder.class); + private static final Logger logger = LoggerFactory.getLogger(RunTableHolder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Direct access to data, if any. */ private RunTable data; @@ -63,7 +55,6 @@ public class RunTableHolder /** To avoid useless marshalling to disk. */ private boolean modified = false; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code RunTableHolder} object. * @@ -80,7 +71,6 @@ private RunTableHolder () pathString = null; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// @@ -97,24 +87,17 @@ public RunTable getData (SheetStub stub) stub.getBook().getLock().lock(); if (data == null) { - JAXBContext jaxbContext = JAXBContext.newInstance(RunTable.class); - Unmarshaller um = jaxbContext.createUnmarshaller(); - // Open book file system - Path dataFile = stub.getBook().openSheetFolder(stub.getNumber()) - .resolve(pathString); - logger.debug("path: {}", dataFile); - - InputStream is = Files.newInputStream(dataFile, StandardOpenOption.READ); - data = (RunTable) um.unmarshal(is); - is.close(); - + Path dataFolder = stub.getBook().openSheetFolder(stub.getNumber()); + Path dataFile = dataFolder.resolve(pathString); + logger.debug("path to file: {}", dataFile); + data = RunTable.unmarshal(dataFile); dataFile.getFileSystem().close(); // Close book file system modified = false; logger.debug("Loaded {}", dataFile); } - } catch (Exception ex) { - logger.warn("Error unmarshalling from " + pathString, ex); + } catch (IOException ex) { + logger.warn("Error unmarshalling from {}", pathString, ex); } finally { stub.getBook().getLock().unlock(); } @@ -126,6 +109,11 @@ public RunTable getData (SheetStub stub) //---------// // hasData // //---------// + /** + * Tell whether run table data is present. + * + * @return true if loaded + */ public boolean hasData () { return data != null; @@ -134,14 +122,38 @@ public boolean hasData () //------------// // isModified // //------------// + /** + * Tell whether it has been modified. + * + * @return true if modified + */ public boolean isModified () { return modified; } + //-------------// + // setModified // + //-------------// + /** + * Assign modified indicator. + * + * @param bool new value for modified + */ + public void setModified (boolean bool) + { + modified = bool; + } + //---------// // setData // //---------// + /** + * Assign the table data (table just created) and modified flag. + * + * @param data the table data + * @param modified modified flag + */ public void setData (RunTable data, boolean modified) { @@ -149,11 +161,21 @@ public void setData (RunTable data, setModified(modified); } - //-------------// - // setModified // - //-------------// - public void setModified (boolean bool) + //----------// + // toString // + //----------// + @Override + public String toString () { - modified = bool; + final StringBuilder sb = new StringBuilder("RunTableHolder{"); + + if (pathString != null) { + sb.append(pathString); + } + + sb.append('}'); + + return sb.toString(); } + } diff --git a/src/main/org/audiveris/omr/sheet/Scale.java b/src/main/org/audiveris/omr/sheet/Scale.java index 8fc219c56..bc67110dc 100644 --- a/src/main/org/audiveris/omr/sheet/Scale.java +++ b/src/main/org/audiveris/omr/sheet/Scale.java @@ -42,21 +42,24 @@ * distance between two staff lines (center to center). *

                                      * Primary informations: This data is always detected, otherwise the current page is detected as not - * being a music page.

                                        + * being a music page. + *
                                          *
                                        • Staff interline: min, main, max. (Vertical distance measured from line center to line * center)
                                        • *
                                        • Staff line thickness (fore): min, main, max.
                                        • *
                                        *

                                        * Secondary informations: This data is always made available, either based on detected value or - * derived from other information.

                                          + * derived from other information. + *
                                            *
                                          • Beam thickness: main. A second peak in the histogram of vertical foreground runs * signals the presence of beams. * Otherwise it is computed as a ratio of main background length between staff lines.
                                          • *
                                          • Stem thickness: main, max. These values are computed during STEM_SEEDS step.
                                          • *
                                          *

                                          - * Optional informations: This data may exist or not, according to the sheet at hand.

                                            + * Optional informations: This data may exist or not, according to the sheet at hand. + *
                                              *
                                            • Black head: Typical width and height measured for black heads.
                                            • *
                                            • Music font: Precise point size determined for music font rendering of heads.
                                            • *
                                            • Small staff scale: @@ -70,15 +73,15 @@ * There are two different measurements, pixels and fractions: *
                                              *
                                              pixel
                                              - *
                                              This is simply an absolute number of pixels, so generally an integer.
                                              + *
                                              This is simply an absolute number of pixels, so generally an integer.
                                              *
                                              (interline) Fraction
                                              - *
                                              This is a number (or fraction) of interlines. + *
                                              This is a number (or fraction) of interlines. * Typical unit value for interline is around 20 pixels.
                                              *
                                              (interline) AreaFraction
                                              - *
                                              This is a number (or fraction) of square interlines, meant to measure glyph area or weight. + *
                                              This is a number (or fraction) of square interlines, meant to measure glyph area or weight. * Typical unit value for interline area is around 400 pixels.
                                              - *
                                              LineFraction
                                              - *
                                              This is a number (or fraction) of line thickness. + *
                                              LineFraction
                                              + *
                                              This is a number (or fraction) of line thickness. * Typical unit value for line is around 4 pixels.
                                              *
                                              * @@ -88,25 +91,12 @@ @XmlRootElement(name = "scale") public class Scale { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Scale.class); - //~ Enumerations ------------------------------------------------------------------------------- - public enum Size - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Standard staff. */ - LARGE, - /** Small staff. */ - SMALL; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Interline scale. */ @XmlElement(name = "interline") - private final InterlineScale interlineScale; + private InterlineScale interlineScale; /** Line thickness scale. */ @XmlElement(name = "line") @@ -132,9 +122,8 @@ public enum Size @XmlElement(name = "small-staff") private Scale smallScale; - //~ Constructors ------------------------------------------------------------------------------- /** - * Create a scale entity, meant for a whole sheet. + * Create a Scale object, meant for a whole sheet. * * @param interlineScale scale of (large) interline * @param lineScale scale of line thickness @@ -152,13 +141,13 @@ public Scale (InterlineScale interlineScale, this.smallScale = smallScale; } - /** No-arg constructor, needed by JAXB. */ - private Scale () + /** + * Create an empty Scale object. + */ + public Scale () { - this.interlineScale = null; } - //~ Methods ------------------------------------------------------------------------------------ //---------------------// // getBeamMeanDistance // //---------------------// @@ -169,9 +158,26 @@ private Scale () */ public Double getBeamMeanDistance () { + if (beamScale == null) { + return null; + } + return beamScale.getDistanceMean(); } + //--------------// + // getBeamScale // + //--------------// + /** + * Report the beam scale. + * + * @return the beam scale + */ + public BeamScale getBeamScale () + { + return beamScale; + } + //----------------------// // getBeamSigmaDistance // //----------------------// @@ -182,6 +188,10 @@ public Double getBeamMeanDistance () */ public Double getBeamSigmaDistance () { + if (beamScale == null) { + return null; + } + return beamScale.getDistanceSigma(); } @@ -191,11 +201,13 @@ public Double getBeamSigmaDistance () /** * Report the main thickness for beams. * - * @return main beam thickness + * @return main beam thickness, perhaps null */ - public int getBeamThickness () + public Integer getBeamThickness () { - Objects.requireNonNull(beamScale, "This scale instance has no beam information"); + if (beamScale == null) { + return null; + } return beamScale.getMain(); } @@ -213,56 +225,51 @@ public BlackHeadScale getBlackHeadScale () return blackHeadScale; } - //---------// - // getFore // - //---------// + //-------------------// + // setBlackHeadScale // + //-------------------// /** - * Report the line thickness this scale is based upon. + * Remember black head scale. * - * @return the number of black pixels in a staff line + * @param blackHeadScale the blackHeadScale to set */ - public int getFore () + public void setBlackHeadScale (BlackHeadScale blackHeadScale) { - return lineScale.main; + this.blackHeadScale = blackHeadScale; } - //--------------// - // getInterline // - //--------------// + //---------// + // getFore // + //---------// /** - * Report the main interline value this scale is based upon. + * Report the line thickness this scale is based upon. * - * @return the number of pixels (black + white) from one line to the other. + * @return the number of black pixels in a staff line, perhaps null */ - public int getInterline () + public Integer getFore () { - return interlineScale.main; + if (lineScale == null) { + return null; + } + + return lineScale.main; } //--------------// // getInterline // //--------------// /** - * Report the main interline value within the small or large family + * Report the main interline value this scale is based upon. * - * @param size LARGE or SMALL - * @return the smaller interline value + * @return the number of pixels (black + white) from one line to the other, perhaps null */ - public int getInterline (Size size) + public Integer getInterline () { - switch (size) { - default: - case LARGE: - return interlineScale.main; - - case SMALL: - - if (smallScale != null) { - return smallScale.getInterline(); - } else { - throw new IllegalArgumentException("No SMALL interline value"); - } + if (interlineScale == null) { + return null; } + + return interlineScale.main; } //-------------------// @@ -278,32 +285,6 @@ public InterlineScale getInterlineScale () return interlineScale; } - //-------------------// - // getInterlineScale // - //-------------------// - /** - * Report the large or small scale, according to boolean value. - * - * @param size LARGE or SMALL - * @return the desired interlineScale, perhaps null if no small exists - */ - public InterlineScale getInterlineScale (Size size) - { - switch (size) { - default: - case LARGE: - return interlineScale; - - case SMALL: - - if (smallScale != null) { - return smallScale.interlineScale; - } else { - return null; - } - } - } - //-------------------// // getInterlineScale // //-------------------// @@ -326,6 +307,38 @@ public InterlineScale getInterlineScale (int interline) throw new IllegalArgumentException("No interline scale for provided value " + interline); } + //--------------// + // getItemValue // + //--------------// + /** + * Report the value of a specific item. + * + * @param item desired item + * @return item value, perhaps null + */ + public Integer getItemValue (Item item) + { + switch (item) { + case line: + return getFore(); + + case interline: + return getInterline(); + + case smallInterline: + return getSmallInterline(); + + case beam: + return getBeamThickness(); + + case stem: + return getStemThickness(); + + default: + throw new IllegalArgumentException("No value defined for scaling item " + item); + } + } + //------------------------// // getLargeInterlineScale // //------------------------// @@ -339,6 +352,19 @@ public InterlineScale getLargeInterlineScale () return interlineScale; } + //--------------// + // getLineScale // + //--------------// + /** + * Report the line scale. + * + * @return the lineScale + */ + public LineScale getLineScale () + { + return lineScale; + } + //------------// // getMaxFore // //------------// @@ -451,6 +477,19 @@ public MusicFontScale getMusicFontScale () return musicFontScale; } + //-------------------// + // setMusicFontScale // + //-------------------// + /** + * Remember music font scale. + * + * @param musicFontScale the musicFontScale to set + */ + public void setMusicFontScale (MusicFontScale musicFontScale) + { + this.musicFontScale = musicFontScale; + } + //-------------------// // getSmallInterline // //-------------------// @@ -496,6 +535,42 @@ public Scale getSmallScale () return smallScale; } + /** + * Set small scale information. + * + * @param smallScale the smallScale to set + */ + public void setSmallScale (Scale smallScale) + { + this.smallScale = smallScale; + } + + //--------------// + // getStemScale // + //--------------// + /** + * Report the stem scale. + * + * @return the stem scale + */ + public StemScale getStemScale () + { + return stemScale; + } + + //--------------// + // setStemScale // + //--------------// + /** + * Remember stem scaling information. + * + * @param stemScale stem scaling + */ + public void setStemScale (StemScale stemScale) + { + this.stemScale = stemScale; + } + //------------------// // getStemThickness // //------------------// @@ -504,8 +579,12 @@ public Scale getSmallScale () * * @return the most frequent stem thickness */ - public int getStemThickness () + public Integer getStemThickness () { + if (stemScale == null) { + return null; + } + return stemScale.getMain(); } @@ -584,51 +663,38 @@ public void setBeamDistance (double meanValue, beamScale.setDistance(meanValue, standardDeviation); } - //-------------------// - // setBlackHeadScale // - //-------------------// + //--------------// + // setItemValue // + //--------------// /** - * Remember black head scale. + * Assign a value to a specific item. * - * @param blackHeadScale the blackHeadScale to set + * @param item desired item + * @param v new value + * @return the modified scale object */ - public void setBlackHeadScale (BlackHeadScale blackHeadScale) + public Object setItemValue (Item item, + int v) { - this.blackHeadScale = blackHeadScale; - } + switch (item) { + case line: + return lineScale = new LineScale(v, v, v); - //-------------------// - // setMusicFontScale // - //-------------------// - /** - * Remember music font scale. - * - * @param musicFontScale the musicFontScale to set - */ - public void setMusicFontScale (MusicFontScale musicFontScale) - { - this.musicFontScale = musicFontScale; - } + case interline: + return interlineScale = new InterlineScale(v, v, v); - /** - * @param smallScale the smallScale to set - */ - public void setSmallScale (Scale smallScale) - { - this.smallScale = smallScale; - } + case smallInterline: + return smallScale = new Scale(new InterlineScale(v, v, v), null, null, null); - //--------------// - // setStemScale // - //--------------// - /** - * Remember stem scaling information. - * - * @param stemScale stem scaling - */ - public void setStemScale (StemScale stemScale) - { - this.stemScale = stemScale; + case beam: + return beamScale = new BeamScale(v, false); + + case stem: + return stemScale = new StemScale(v, v); + + default: + throw new IllegalArgumentException("No value defined for scaling item " + item); + } } //----------// @@ -705,7 +771,7 @@ public double toPixelsDouble (Fraction frac) */ public double toPixelsDouble (LineFraction lineFrac) { - return (double) lineScale.main * lineFrac.getValue(); + return lineScale.main * lineFrac.getValue(); } //----------// @@ -714,18 +780,29 @@ public double toPixelsDouble (LineFraction lineFrac) @Override public String toString () { - return toString(/* full: */true); + return toString(true); } //----------// // toString // //----------// + /** + * An extensible description of scale. + * + * @param full true for full description + * @return scale description + */ public String toString (boolean full) { StringBuilder sb = new StringBuilder("Scale{"); - sb.append("line").append(lineScale); - sb.append(" interline").append(interlineScale); + if (lineScale != null) { + sb.append(lineScale); + } + + if (interlineScale != null) { + sb.append(" ").append(interlineScale); + } if (beamScale != null) { sb.append(" ").append(beamScale); @@ -768,7 +845,35 @@ private double fracToPixels (double val) return interlineScale.main * val; } - //~ Inner Classes ------------------------------------------------------------------------------ + /** + * Scale information kind. + */ + public static enum Item + { + line("Line thickness"), + interline("Interline"), + smallInterline("Small interline"), + beam("Beam thickness"), + stem("Stem thickness"); + + private final String description; + + Item (String description) + { + this.description = description; + } + + /** + * Report item description + * + * @return description + */ + public String getDescription () + { + return description; + } + } + //--------------// // AreaFraction // //--------------// @@ -779,7 +884,6 @@ private double fracToPixels (double val) public static class AreaFraction extends Constant.Double { - //~ Constructors --------------------------------------------------------------------------- /** * Specific constructor, where 'unit' and 'name' are assigned later. @@ -813,7 +917,6 @@ public AreaFraction (double defaultValue, @XmlAccessorType(XmlAccessType.NONE) public static class BeamScale { - //~ Instance fields ------------------------------------------------------------------------ /** Most frequent beam thickness. */ @XmlAttribute(name = "main-thickness") @@ -833,7 +936,6 @@ public static class BeamScale @XmlJavaTypeAdapter(Jaxb.Double1Adapter.class) private Double distanceSigma; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new {@code BeamScale} object. * @@ -856,7 +958,6 @@ private BeamScale () this.extra = null; } - //~ Methods -------------------------------------------------------------------------------- /** * Report the average distance (center to center) between beams of the same group. * @@ -940,7 +1041,6 @@ public String toString () @XmlAccessorType(XmlAccessType.NONE) public static class BlackHeadScale { - //~ Instance fields ------------------------------------------------------------------------ @XmlAttribute(name = "mean-width") @XmlJavaTypeAdapter(type = double.class, value = Jaxb.Double1Adapter.class) @@ -958,7 +1058,6 @@ public static class BlackHeadScale @XmlJavaTypeAdapter(type = double.class, value = Jaxb.Double1Adapter.class) final double heightStd; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new {@code BlackHeadScale} object. * @@ -989,7 +1088,6 @@ private BlackHeadScale () this.heightStd = 0; } - //~ Methods -------------------------------------------------------------------------------- /** * @return the heightMean */ @@ -1048,15 +1146,16 @@ public String toString () public static class Fraction extends Constant.Double { - //~ Static fields/initializers ------------------------------------------------------------- + /** + * + */ public static final Fraction ZERO = new Fraction(0, "zero"); static { ZERO.setUnitAndName(Scale.class.getName(), "ZERO"); } - //~ Constructors --------------------------------------------------------------------------- /** * Specific constructor, where 'unit' and 'name' are assigned later. * @@ -1086,13 +1185,24 @@ private Fraction () public static class InterlineScale extends Range { - //~ Constructors --------------------------------------------------------------------------- + /** + * Create an InterlineScale object. + * + * @param range the underlying range + */ public InterlineScale (Range range) { this(range.min, range.main, range.max); } + /** + * Create an InterlineScale object. + * + * @param min minimum value + * @param main most frequent value + * @param max maximum value + */ public InterlineScale (int min, int main, int max) @@ -1106,7 +1216,6 @@ protected InterlineScale () this(0, 0, 0); } - //~ Methods -------------------------------------------------------------------------------- /** * Compute the interline fraction that corresponds to the given number of pixels. * @@ -1119,32 +1228,6 @@ public double pixelsToFrac (double pixels) return pixels / main; } - public static int toPixels (int interline, - Fraction frac) - { - return (int) Math.rint(toPixelsDouble(interline, frac)); - } - - /** - * Compute the squared-normalized number of pixels, according to the provided - * interline. - * - * @param interline provided interline value - * @param areaFrac a measure based on interline (1 = one interline square) - * @return the actual squared number of pixels with the current scale - */ - public static int toPixels (int interline, - AreaFraction areaFrac) - { - return (int) Math.rint(interline * interline * areaFrac.getValue()); - } - - public static double toPixelsDouble (int interline, - Fraction frac) - { - return interline * frac.getValue(); - } - /** * Compute the squared-normalized number of pixels. * @@ -1180,6 +1263,52 @@ public double toPixelsDouble (Fraction frac) { return toPixelsDouble(main, frac); } + + /** + * Compute in pixels the provided Fraction, under the provided interline. + * + * @param interline the actual interline value + * @param frac the interline-based specification + * @return the resulting integer value in pixels + */ + public static int toPixels (int interline, + Fraction frac) + { + return (int) Math.rint(toPixelsDouble(interline, frac)); + } + + /** + * Compute the squared-normalized number of pixels, according to the provided + * interline. + * + * @param interline provided interline value + * @param areaFrac a measure based on interline (1 = one interline square) + * @return the actual squared number of pixels with the current scale + */ + public static int toPixels (int interline, + AreaFraction areaFrac) + { + return (int) Math.rint(interline * interline * areaFrac.getValue()); + } + + /** + * Compute in pixels the provided Fraction, under the provided interline. + * + * @param interline the actual interline value + * @param frac the interline-based specification + * @return the resulting double value in pixels + */ + public static double toPixelsDouble (int interline, + Fraction frac) + { + return interline * frac.getValue(); + } + + @Override + public String toString () + { + return "interline" + super.toString(); + } } //--------------// @@ -1193,7 +1322,6 @@ public double toPixelsDouble (Fraction frac) public static class LineFraction extends Constant.Double { - //~ Constructors --------------------------------------------------------------------------- /** * Specific constructor, where 'unit' and 'name' are assigned later. @@ -1224,13 +1352,24 @@ private LineFraction () public static class LineScale extends Range { - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a LineScale object. + * + * @param range the defining range + */ public LineScale (Range range) { this(range.min, range.main, range.max); } + /** + * Create a LineScale object. + * + * @param min minimum value + * @param main most frequent value + * @param max maximum value + */ public LineScale (int min, int main, int max) @@ -1244,7 +1383,6 @@ protected LineScale () this(0, 0, 0); } - //~ Methods -------------------------------------------------------------------------------- /** * Compute the number of pixels that corresponds to the fraction of line * thickness provided, according to the scale. @@ -1267,7 +1405,13 @@ public int toPixels (LineFraction lineFrac) */ public double toPixelsDouble (LineFraction lineFrac) { - return (double) main * lineFrac.getValue(); + return main * lineFrac.getValue(); + } + + @Override + public String toString () + { + return "line" + super.toString(); } } @@ -1282,7 +1426,6 @@ public double toPixelsDouble (LineFraction lineFrac) @XmlAccessorType(XmlAccessType.NONE) public static class MusicFontScale { - //~ Instance fields ------------------------------------------------------------------------ /** Font name. */ @XmlAttribute(name = "name") @@ -1292,7 +1435,6 @@ public static class MusicFontScale @XmlAttribute(name = "point-size") final int pointSize; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new {@code MusicFontScale} object. * @@ -1313,7 +1455,6 @@ private MusicFontScale () this.pointSize = 0; } - //~ Methods -------------------------------------------------------------------------------- /** * @return the name */ @@ -1356,7 +1497,6 @@ public String toString () @XmlAccessorType(XmlAccessType.NONE) public static class StemScale { - //~ Instance fields ------------------------------------------------------------------------ /** Most frequent stem thickness. */ @XmlAttribute(name = "main-thickness") @@ -1366,7 +1506,6 @@ public static class StemScale @XmlAttribute(name = "max-thickness") private final int max; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new {@code StemScale} object. * @@ -1389,7 +1528,6 @@ private StemScale () this.max = 0; } - //~ Methods -------------------------------------------------------------------------------- /** * Report the most frequent stem thickness * diff --git a/src/main/org/audiveris/omr/sheet/ScaleBuilder.java b/src/main/org/audiveris/omr/sheet/ScaleBuilder.java index 92d4ef963..647f005a9 100644 --- a/src/main/org/audiveris/omr/sheet/ScaleBuilder.java +++ b/src/main/org/audiveris/omr/sheet/ScaleBuilder.java @@ -25,6 +25,7 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; import org.audiveris.omr.math.HiLoPeakFinder; +import org.audiveris.omr.math.HiLoPeakFinder.Quorum; import org.audiveris.omr.math.IntegerFunction; import org.audiveris.omr.math.Range; import org.audiveris.omr.run.Run; @@ -53,37 +54,34 @@ *

                                              * A second black peak usually gives the average beam thickness. * And similarly, a second combo peak may indicate a series of staves with a different interline - * than the main series.

                                              + * than the main series. *

                                              - * Internally, additional validity checks are performed:

                                                + * Internally, additional validity checks are performed: + *
                                                  *
                                                1. If we cannot retrieve black peak, we decide that the sheet does not contain significant * lines.
                                                2. *
                                                3. If we cannot retrieve combo peak, we decide that the sheet does not contain regularly spaced * staff lines.
                                                4. *
                                                5. Method {@link #checkResolution} looks at combo peak. - * If the main interline value is below a certain threshold (see constants.minResolution), then we + * If the main interline value is below a certain threshold (see constants.minInterline), then we * suspect that the picture is not a music sheet (it may rather be an image, a page of text, etc). *
                                                6. *
                                                *

                                                * If we have doubts about the page at hand and if this page is part of a multi-page score, we * propose to simply discard this sheet. In batch, the page is discarded without asking for - * confirmation.

                                                + * confirmation. * * @see Scale - * * @author Hervé Bitteur */ public class ScaleBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(ScaleBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Related sheet. */ @Navigable(false) private final Sheet sheet; @@ -106,7 +104,6 @@ public class ScaleBuilder /** Main beam thickness found, if any. */ private Integer beamKey; - //~ Constructors ------------------------------------------------------------------------------- /** * Constructor to enable scale computation on a given sheet. * @@ -117,7 +114,6 @@ public ScaleBuilder (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // displayChart // //--------------// @@ -128,7 +124,7 @@ public void displayChart () { if (histoKeeper == null) { try { - retrieveScale(); + doRetrieveScale(true); // Dummy retrieval } catch (StepException ignored) { } } @@ -142,36 +138,16 @@ public void displayChart () // retrieveScale // //---------------// /** - * Retrieve the global scale counts by processing the provided picture runs, - * make decisions about the validity of current picture as a music page and store - * the results as a {@link Scale} instance in the related sheet. + * Retrieve scale information on sheet. * - * @return scale data for the sheet - * @throws StepException if processing must stop for this sheet. + * @return scale information + * @throws StepException raised if some mandatory information cannot be retrieved. + * For example if no staves are found. */ public Scale retrieveScale () throws StepException { - binary = sheet.getPicture().getTable(Picture.TableKey.BINARY); - histoKeeper = new HistoKeeper(); - - histoKeeper.buildBlacks(); - histoKeeper.retrieveLinePeak(); // -> blackPeak (or StepException thrown) - - histoKeeper.buildCombos(); - histoKeeper.retrieveInterlinePeaks(); // -> comboPeak (or StepException thrown), comboPeak2? - - histoKeeper.retrieveBeamKey(); // -> beamKey? - - // Check we have acceptable resolution. If not, throw StepException - checkResolution(); - - // Here, we keep going on with scale data - InterlineScale smallInterlineScale = computeSmallInterline(); - Scale smallScale = (smallInterlineScale == null) ? null - : new Scale(smallInterlineScale, null, null, null); - - return new Scale(computeInterline(), new LineScale(blackPeak), computeBeam(), smallScale); + return doRetrieveScale(false); } //-----------------// @@ -190,19 +166,30 @@ private void checkResolution () if (interline == 0) { sheet.getStub().decideOnRemoval( - sheet.getId() + LINE_SEPARATOR + "Interline value is zero." + LINE_SEPARATOR - + "This sheet does not seem to contain staff lines.", + sheet.getId() + LINE_SEPARATOR + + "Interline value is zero." + + LINE_SEPARATOR + + "This sheet does not seem to contain staff lines.", false); - } else if (interline < constants.minResolution.getValue()) { + } else if (interline < constants.minInterline.getValue()) { sheet.getStub().decideOnRemoval( - sheet.getId() + LINE_SEPARATOR + "With an interline value of " + interline - + " pixels," + LINE_SEPARATOR + "either this sheet contains no staves," - + LINE_SEPARATOR + "or the picture resolution is too low (try 300 DPI).", + sheet.getId() + LINE_SEPARATOR + + "With an interline value of " + + interline + + " pixels," + + LINE_SEPARATOR + + "either this sheet contains no staves," + + LINE_SEPARATOR + + "or the picture resolution is too low (try 300 DPI).", false); } else if (interline > constants.maxInterline.getValue()) { sheet.getStub().decideOnRemoval( - sheet.getId() + LINE_SEPARATOR + "Too large interline value: " + interline - + " pixels" + LINE_SEPARATOR + "This sheet does not seem to contain staff lines.", + sheet.getId() + LINE_SEPARATOR + + "Too large interline value: " + + interline + + " pixels" + + LINE_SEPARATOR + + "This sheet does not seem to contain staff lines.", false); } } @@ -212,28 +199,42 @@ private void checkResolution () //-------------// /** * Report the beam scale information for the sheet. - * We use the retrieved beam key if any, otherwise we extrapolate a probable beam height as a - * ratio of main white length. + *

                                                + * We try to retrieve a beam key in black histogram, otherwise we extrapolate a probable beam + * height based on minimum and maximum height values. * * @return the beam scale */ private BeamScale computeBeam () { - if (beamKey != null) { - return new BeamScale(beamKey, false); + // Scale data? + if (comboPeak == null) { + logger.warn("No global scale information available"); + + return null; } - if (comboPeak != null) { - final int guess = (int) Math.rint( - constants.beamAsWhiteRatio.getValue() * getMaxWhite()); - logger.info("No beam key found, guessed value {}", guess); + // Beam peak? + final int largerInterline = getLargerInterline(); + final int minHeight = Math.max( + blackPeak.max, + (int) Math.rint(constants.minBeamFraction.getValue() * largerInterline)); + final int maxHeight = Math.max( + largerInterline - blackPeak.main, + (int) Math.rint(constants.maxBeamFraction.getValue() * largerInterline)); + + beamKey = histoKeeper.retrieveBeamKey(minHeight, maxHeight); - return new BeamScale(guess, true); + if (beamKey != null) { + return new BeamScale(beamKey, false); } - logger.warn("No global scale information available"); + // Beam extrapolation from height possible range + final int guess = (int) Math.rint( + minHeight + ((maxHeight - minHeight) * constants.beamRangeRatio.getValue())); + logger.info("No beam key found, guessed value: {}", guess); - return null; + return new BeamScale(guess, true); } //------------------// @@ -264,84 +265,99 @@ private InterlineScale computeSmallInterline () } } - //-------------// - // getMaxWhite // - //-------------// + //-----------------// + // doRetrieveScale // + //-----------------// /** - * Return the maximum white gap between (larger) staff lines. + * Retrieve the global scale counts by processing the provided picture runs, + * make decisions about the validity of current picture as a music page and store + * the results as a {@link Scale} instance in the related sheet. * - * @return (larger) white gap + * @param dummy true for dummy retrieval (just for the chart) + * @return scale data for the sheet + * @throws StepException if processing must stop for this sheet. */ - private int getMaxWhite () + private Scale doRetrieveScale (boolean dummy) + throws StepException { - int maxCombo = comboPeak.max; + binary = sheet.getPicture().getTable(Picture.TableKey.BINARY); + histoKeeper = new HistoKeeper(); - if (comboPeak2 != null) { - maxCombo = Math.max(maxCombo, comboPeak2.max); - } + histoKeeper.buildBlacks(); + histoKeeper.retrieveLinePeak(); // -> blackPeak (or StepException thrown) - return maxCombo - blackPeak.min; - } + histoKeeper.buildCombos(); + histoKeeper.retrieveInterlinePeaks(); // -> comboPeak (or StepException thrown), comboPeak2? - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ + if (dummy) { + computeBeam(); // Just for the chart - private final Constant.Integer minResolution = new Constant.Integer( - "Pixels", - 11, - "Minimum resolution, expressed as number of pixels per interline"); + return null; + } - private final Constant.Integer maxInterline = new Constant.Integer( - "Pixels", - 100, - "Maximum interline value (in pixels)"); + // Check we have acceptable resolution. If not, throw StepException + checkResolution(); + + // Here, we keep going on with scale data + InterlineScale smallInterlineScale = computeSmallInterline(); + Scale smallScale = (smallInterlineScale == null) ? null + : new Scale(smallInterlineScale, null, null, null); - private final Constant.Ratio minCountRatio = new Constant.Ratio( - 0.1, - "Ratio of total runs for peak acceptance"); + // Respect user-assigned scale info, if any + final Scale scl = sheet.getScale(); + final Scale scale; - private final Constant.Ratio minGainRatio = new Constant.Ratio( - 0.03, - "Minimum ratio of peak runs for peak extension"); + if (scl != null) { + scale = new Scale( + (scl.getInterlineScale() != null) ? scl.getInterlineScale() + : computeInterline(), + (scl.getLineScale() != null) ? scl.getLineScale() : new LineScale(blackPeak), + (scl.getBeamScale() != null) ? scl.getBeamScale() : computeBeam(), + (scl.getSmallScale() != null) ? scl.getSmallScale() : smallScale); - private final Constant.Ratio minDerivativeRatio = new Constant.Ratio( - 0.025, - "Ratio of total runs for derivative acceptance"); + if (scl.getStemScale() != null) { + scale.setStemScale(scl.getStemScale()); + } + } else { + scale = new Scale( + computeInterline(), + new LineScale(blackPeak), + computeBeam(), + smallScale); + } - private final Constant.Ratio minBeamFraction = new Constant.Ratio( - 0.25, - "Minimum ratio between beam thickness and interline"); + return scale; + } - private final Constant.Ratio maxSecondRatio = new Constant.Ratio( - 2.0, - "Maximum ratio between second and first combined peak"); + //--------------------// + // getLargerInterline // + //--------------------// + /** + * Report the larger of comboPeak.main (and comboPeak2.main if any) + * + * @return (larger) main interline + */ + private int getLargerInterline () + { + int mainCombo = comboPeak.main; - private final Constant.Ratio beamAsWhiteRatio = new Constant.Ratio( - 0.75, - "Default beam height defined as ratio of background peak"); + if (comboPeak2 != null) { + mainCombo = Math.max(mainCombo, comboPeak2.main); + } - private final Constant.Ratio minBlackRatio = new Constant.Ratio( - 0.001, - "Minimum ratio of foreground pixels in image"); + return mainCombo; } //-------------// // HistoKeeper // //-------------// /** - * This class builds the precise vertical foreground and background run lengths, - * it retrieves the various peaks and is able to display a chart on the related - * populations. + * This class handles the histograms of vertical lengths for black runs (foreground) + * and combo runs (black + white and white + black). + * It retrieves the various peaks and is able to display a chart on the related populations. */ private class HistoKeeper { - //~ Instance fields ------------------------------------------------------------------------ // Upper bounds for run lengths (assuming sheet height >= staff height) final int maxBlack; @@ -356,9 +372,10 @@ private class HistoKeeper final HiLoPeakFinder comboFinder; - //~ Constructors --------------------------------------------------------------------------- - public HistoKeeper () + HistoKeeper () { + // We assume at least one staff in sheet, hence some maximum values for relevant white + // and black runs. maxBlack = binary.getHeight() / 16; maxWhite = binary.getHeight() / 4; logger.debug( @@ -375,7 +392,6 @@ public HistoKeeper () comboFinder = new HiLoPeakFinder("combo", comboFunction); } - //~ Methods -------------------------------------------------------------------------------- //-------------// // buildBlacks // //-------------// @@ -404,6 +420,12 @@ public void buildBlacks () //-------------// /** * Populate the combo histogram. + *

                                                + * A combo is the total length of blackRun + next whiteRun or whiteRun + next blackRun. + *

                                                + * NOTA: Roughly, all counts are doubled by the way we count the combos. + * This has no real impact, because area (integral) which is used as the base for quorum + * is also doubled. */ public void buildCombos () { @@ -454,23 +476,33 @@ public void buildCombos () * Take most frequent black local max for which key (beam thickness) is larger than a * minimum fraction of interline and smaller than main white gap between (large) staff * lines. + * + * @param minHeight start value for height range + * @param maxHeight stop value for height range */ - public void retrieveBeamKey () + public Integer retrieveBeamKey (int minHeight, + int maxHeight) { - double minBeamFraction = constants.minBeamFraction.getValue(); - int minHeight = Math.max( - blackPeak.max, - (int) Math.rint(minBeamFraction * comboPeak.main)); - int maxHeight = getMaxWhite(); List localMaxima = blackFunction.getLocalMaxima(minHeight, maxHeight); + // Quorum on height histo + final int totalArea = blackFunction.getArea(); + final double ratio = constants.minBeamCountRatio.getValue(); + final int quorum = (int) Math.rint(totalArea * ratio); + logger.info("Beam minHeight:{} maxHeight:{} quorum:{}", minHeight, maxHeight, quorum); + blackFinder.setQuorum(new Quorum(quorum, minHeight, maxHeight)); + for (int local : localMaxima) { if ((local >= minHeight) && (local <= maxHeight)) { - beamKey = local; + if (blackFunction.getValue(local) >= quorum) { + return local; + } break; } } + + return null; } //------------------------// @@ -489,7 +521,6 @@ public void retrieveInterlinePeaks () final int area = comboFunction.getArea(); final List comboPeaks = comboFinder.findPeaks( 1, - null, (int) Math.rint(area * constants.minDerivativeRatio.getValue()), constants.minGainRatio.getValue()); @@ -546,7 +577,6 @@ public void retrieveLinePeak () final int area = blackFunction.getArea(); final List blackPeaks = blackFinder.findPeaks( 1, - (int) Math.rint(area * constants.minCountRatio.getValue()), (int) Math.rint(area * constants.minDerivativeRatio.getValue()), constants.minGainRatio.getValue()); @@ -581,7 +611,7 @@ public void writePlot () xMax = Math.max(xMax, comboPeak2.max); } - xMax = (xMax * 5) / 4; // Add some margin + xMax = Math.min(sheet.getWidth() - 1, (xMax * 5) / 2); // Add some margin Scale scale = sheet.getScale(); String xLabel = "Lengths - " + ((scale != null) ? scale.toString(false) : "NO_SCALE"); @@ -622,9 +652,11 @@ private void checkBlack () if (blackRatio < constants.minBlackRatio.getValue()) { sheet.getStub().decideOnRemoval( - sheet.getId() + LINE_SEPARATOR + "Too few black pixels: " - + String.format("%.4f%%", 100 * blackRatio) + LINE_SEPARATOR - + "This sheet is almost blank.", + sheet.getId() + LINE_SEPARATOR + + "Too few black pixels: " + + String.format("%.4f%%", 100 * blackRatio) + + LINE_SEPARATOR + + "This sheet is almost blank.", false); } } @@ -652,4 +684,54 @@ private int getBlackCount () return total; } } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Integer minInterline = new Constant.Integer( + "Pixels", + 11, + "Minimum interline value (in pixels)"); + + private final Constant.Integer maxInterline = new Constant.Integer( + "Pixels", + 100, + "Maximum interline value (in pixels)"); + + private final Constant.Ratio minGainRatio = new Constant.Ratio( + 0.03, + "Minimum ratio of peak runs for peak extension"); + + private final Constant.Ratio minDerivativeRatio = new Constant.Ratio( + 0.025, + "Ratio of total runs for derivative acceptance"); + + private final Constant.Ratio maxSecondRatio = new Constant.Ratio( + 2.0, + "Maximum ratio between second and first combined peak"); + + private final Constant.Ratio minBeamFraction = new Constant.Ratio( + 0.275, + "Minimum ratio between beam thickness and interline"); + + private final Constant.Ratio maxBeamFraction = new Constant.Ratio( + 0.9, + "Maximum ratio between beam thickness and interline"); + + private final Constant.Ratio minBeamCountRatio = new Constant.Ratio( + 0.02, + "Ratio of total runs for beam peak acceptance"); + + private final Constant.Ratio beamRangeRatio = new Constant.Ratio( + 0.5, + "Ratio of beam range for extrapolation"); + + private final Constant.Ratio minBlackRatio = new Constant.Ratio( + 0.001, + "Minimum ratio of foreground pixels in image"); + } } diff --git a/src/main/org/audiveris/omr/sheet/Sheet.java b/src/main/org/audiveris/omr/sheet/Sheet.java index e0a55f9cd..a0e58b23f 100644 --- a/src/main/org/audiveris/omr/sheet/Sheet.java +++ b/src/main/org/audiveris/omr/sheet/Sheet.java @@ -21,64 +21,141 @@ // package org.audiveris.omr.sheet; +import org.audiveris.omr.OMR; +import org.audiveris.omr.classifier.Annotations; +import org.audiveris.omr.classifier.AnnotationsBuilder; +import org.audiveris.omr.classifier.SampleRepository; +import org.audiveris.omr.classifier.SampleSheet; +import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.GlyphIndex; +import org.audiveris.omr.glyph.GlyphsModel; +import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.glyph.dynamic.FilamentIndex; import org.audiveris.omr.glyph.ui.GlyphsController; import org.audiveris.omr.glyph.ui.SymbolsEditor; +import org.audiveris.omr.image.ImageFormatException; import org.audiveris.omr.lag.LagManager; +import org.audiveris.omr.lag.Lags; +import org.audiveris.omr.run.RunTable; import org.audiveris.omr.score.Page; +import org.audiveris.omr.score.PageRef; +import org.audiveris.omr.score.Score; +import org.audiveris.omr.score.ScoreExporter; +import org.audiveris.omr.score.ScoreReduction; +import org.audiveris.omr.score.ui.BookPdfOutput; +import org.audiveris.omr.sheet.ui.BinarizationBoard; +import org.audiveris.omr.sheet.ui.PictureView; +import org.audiveris.omr.sheet.ui.PixelBoard; +import org.audiveris.omr.sheet.ui.SheetAssembly; +import org.audiveris.omr.sheet.ui.SheetTab; +import org.audiveris.omr.sheet.ui.StubsController; import org.audiveris.omr.sig.InterIndex; +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.AbstractPitchedInter; +import org.audiveris.omr.sig.inter.Inter; import org.audiveris.omr.sig.ui.InterController; +import org.audiveris.omr.step.Step; import org.audiveris.omr.step.StepException; +import org.audiveris.omr.ui.BoardsPane; +import org.audiveris.omr.ui.Colors; import org.audiveris.omr.ui.ErrorsEditor; +import org.audiveris.omr.ui.selection.LocationEvent; +import org.audiveris.omr.ui.selection.PixelEvent; import org.audiveris.omr.ui.selection.SelectionService; import org.audiveris.omr.ui.util.ItemRenderer; +import org.audiveris.omr.ui.util.WeakItemRenderer; +import org.audiveris.omr.util.Dumping; +import org.audiveris.omr.util.FileUtil; +import org.audiveris.omr.util.Jaxb; +import org.audiveris.omr.util.Navigable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Files; import java.nio.file.Path; +import static java.nio.file.StandardOpenOption.CREATE; +import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import javax.imageio.ImageIO; +import javax.swing.SwingUtilities; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.util.IndentingXMLStreamWriter; /** - * Interface {@code Sheet} corresponds to one image in a book image file. + * Class {@code Sheet} corresponds to one image in a book image file. *

                                                * If a movement break occurs in the middle of a sheet, this sheet will contain at least two pages, * but in most cases there is exactly one {@link Page} instance per Sheet instance. - * + *

                                                * Methods are organized as follows: *

                                                *
                                                Administration
                                                - *
                                                  + *
                                                  + *
                                                    *
                                                  • {@link #getId}
                                                  • *
                                                  • {@link #getStub}
                                                  • + *
                                                  • {@link #getSheetFileName}
                                                  • + *
                                                  • {@link #store}
                                                  • + *
                                                  • {@link #unmarshal}
                                                  • *
                                                  • {@link #afterReload}
                                                  • + *
                                                  • {@link #reset}
                                                  • *
                                                  • {@link #getLagManager}
                                                  • *
                                                  • {@link #getFilamentIndex}
                                                  • *
                                                  • {@link #getGlyphIndex}
                                                  • *
                                                  • {@link #getInterIndex}
                                                  • *
                                                  • {@link #getPersistentIdGenerator}
                                                  • - *
                                                  - * + *
                                                + *
                                                *
                                                Pages, Systems and Staves
                                                - *
                                                  + *
                                                  + *
                                                    *
                                                  • {@link #addPage}
                                                  • *
                                                  • {@link #getPages}
                                                  • + *
                                                  • {@link #getLastPage}
                                                  • *
                                                  • {@link #getStaffManager}
                                                  • *
                                                  • {@link #getSystemManager}
                                                  • *
                                                  • {@link #getSystems}
                                                  • - *
                                                  - * - *
                                                  Symbols
                                                  - *
                                                    - *
                                                  • {@link #annotate}
                                                  • + *
                                                  • {@link #dumpSystemInfos}
                                                  • + *
                                                  + *
                                                  + *
                                                  Samples
                                                  + *
                                                  + *
                                                    + *
                                                  • {@link #annotate()}
                                                  • + *
                                                  • {@link #annotate(java.nio.file.Path)}
                                                  • *
                                                  • {@link #sample}
                                                  • - *
                                                  - * + *
                                                + *
                                                *
                                                Artifacts
                                                - *
                                                  + *
                                                  + *
                                                    *
                                                  • {@link #setImage}
                                                  • *
                                                  • {@link #hasPicture}
                                                  • *
                                                  • {@link #getPicture}
                                                  • @@ -89,15 +166,18 @@ *
                                                  • {@link #getInterline}
                                                  • *
                                                  • {@link #setSkew}
                                                  • *
                                                  • {@link #getSkew}
                                                  • - *
                                                  • {@link #store}
                                                  • *
                                                  • {@link #print}
                                                  • *
                                                  • {@link #export}
                                                  • + *
                                                  • {@link #setSheetDelta}
                                                  • *
                                                  • {@link #getSheetDelta}
                                                  • - *
                                                  - * + *
                                                + *
                                                *
                                                UI
                                                - *
                                                  + *
                                                  + *
                                                    *
                                                  • {@link #getSymbolsEditor}
                                                  • + *
                                                  • {@link #createBinaryView}
                                                  • + *
                                                  • {@link #createPictureView}
                                                  • *
                                                  • {@link #displayDataTab}
                                                  • *
                                                  • {@link #displayMainTabs}
                                                  • *
                                                  • {@link #getErrorsEditor}
                                                  • @@ -106,69 +186,426 @@ *
                                                  • {@link #getLocationService}
                                                  • *
                                                  • {@link #addItemRenderer}
                                                  • *
                                                  • {@link #renderItems}
                                                  • - *
                                                  + *
                                                + *
                                                *
                                                + * The picture below represents the data model used for marshalling/unmarshalling a sheet to/from + * a sheet#n.xml file within a book .omr file + *

                                                + * Most entities are represented here. Some Inter instances are listed only via their containing + * entity, such as tuplets in MeasureStack, slurs and lyrics in Part, ledgers and bars in Staff, + * graceChords and restChords in Measure, wholeChord in Voice. + *

                                                + * Once an instance of Sheet has been unmarshalled, transient members of some entities need to + * be properly set. This is the purpose of the "afterReload()" methods which are called in a certain + * order as mentioned by the "(ar #n)" indications on these entities. + *

                                                + * Sheet Binding * * @author Hervé Bitteur */ -@XmlJavaTypeAdapter(BasicSheet.Adapter.class) -public interface Sheet +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = "sheet") +public class Sheet { - //~ Static fields/initializers ----------------------------------------------------------------- + + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(Sheet.class); /** The radix used for folder of this sheet internals. */ - static final String INTERNALS_RADIX = "sheet#"; + public static final String INTERNALS_RADIX = "sheet#"; + + /** Events that can be published on sheet location service. */ + private static final Class[] allowedEvents = new Class[]{ + LocationEvent.class, + PixelEvent.class}; + + /** Un/marshalling context for use with JAXB. */ + private static volatile JAXBContext jaxbContext; + + // Persistent data + //---------------- + // + /** Global id used to uniquely identify a persistent entity instance. */ + @XmlAttribute(name = "last-persistent-id") + @XmlJavaTypeAdapter(Jaxb.AtomicIntegerAdapter.class) + private final AtomicInteger lastPersistentId = new AtomicInteger(0); + + /** The related picture. */ + @XmlElement(name = "picture") + private Picture picture; + + /** Global scale for this sheet. */ + @XmlElement(name = "scale") + private Scale scale; + + /** Global skew. */ + @XmlElement(name = "skew") + private Skew skew; + + /** Corresponding page(s). A single sheet may relate to several pages. */ + @XmlElement(name = "page") + private final List pages = new ArrayList<>(); + + /** + * Global glyph index. + * See annotated get/set methods: {@link #getGlyphIndexContent()} + */ + private GlyphIndex glyphIndex; + + // Transient data + //--------------- + // + /** Corresponding sheet stub. */ + @Navigable(false) + private SheetStub stub; + + /** Inter index for all systems in this sheet. */ + private InterIndex interIndex; + + /** Staves. */ + private StaffManager staffManager; + + /** Systems management. */ + private SystemManager systemManager; + + /** Dictionary of sheet lags. */ + private LagManager lagManager; + + //-- UI ---------------------------------------------------------------------------------------- + // + /** Selections for this sheet. (SheetLocation, PixelLevel) */ + private SelectionService locationService; + + /** Registered item renderers, if any. */ + private Set itemRenderers; + + /** Related errors editor, if any. */ + private ErrorsEditor errorsEditor; + + /** Specific builder dealing with glyphs. */ + private volatile GlyphsController glyphsController; + + /** Specific UI manager dealing with inters. */ + private volatile InterController interController; + + /** Related symbols editor, if any. */ + private SymbolsEditor symbolsEditor; + + //-- resettable members ------------------------------------------------------------------------ + // + /** Global filaments index. */ + private FilamentIndex filamentIndex; + + /** Delta measurements. */ + private SheetDiff sheetDelta; + + /** + * Creates a new {@code Sheet} object with a binary table. + * + * @param stub the related sheet stub + * @param binaryTable the binary table, if any + */ + public Sheet (SheetStub stub, + RunTable binaryTable) + { + this(stub); + + if (binaryTable != null) { + setBinary(binaryTable); + } + } + + /** + * Create a new {@code Sheet} instance with an image. + * + * @param stub the related sheet stub + * @param image the already loaded image, if any + * @throws StepException if processing failed at this step + */ + public Sheet (SheetStub stub, + BufferedImage image) + throws StepException + { + this(stub); + + if (image != null) { + setImage(image); + } + } + + /** + * Create a new {@code Sheet} instance within a book. + * + * @param stub the related sheet stub + */ + private Sheet (SheetStub stub) + { + Objects.requireNonNull(stub, "Cannot create a sheet in a null stub"); + + glyphIndex = new GlyphIndex(); + + initTransients(stub); + + interIndex = new InterIndex(); + interIndex.initTransients(this); + } + + /** + * No-arg constructor needed for JAXB. + */ + private Sheet () + { + } - //~ Methods ------------------------------------------------------------------------------------ + //-----------------// + // addItemRenderer // + //-----------------// /** * In non batch mode, register a class instance to render items on top of UI views. * * @param renderer an item renderer * @return true if renderer was added, false in batch */ - boolean addItemRenderer (ItemRenderer renderer); + public boolean addItemRenderer (ItemRenderer renderer) + { + if ((renderer != null) && (OMR.gui != null)) { + return itemRenderers.add(new WeakItemRenderer(renderer)); + + ///return itemRenderers.add(renderer); + } + return false; + } + + //---------// + // addPage // + //---------// /** * Add a related page to this sheet. * * @param page the detected page */ - void addPage (Page page); + public void addPage (Page page) + { + pages.add(page); + } + //-------------// + // afterReload // + //-------------// /** * Complete sheet initialization, after reload. * * @param stub the sheet stub */ - void afterReload (SheetStub stub); + public void afterReload (SheetStub stub) + { + try { + // Predefined StaffHolder's are no longer useful + Staff.StaffHolder.clearStaffHolders(); + + // Complete sheet initialization + initTransients(stub); + // Make sure hLag & vLag are available and their sections dispatched to relevant systems + if (stub.isDone(Step.GRID)) { + systemManager.dispatchHorizontalSections(); + systemManager.dispatchVerticalSections(); + } + + // Complete inters index + interIndex = new InterIndex(); + interIndex.initTransients(this); + + for (SystemInfo system : getSystems()) { + // Forward reload request down system hierarchy + system.afterReload(); + } + } catch (Exception ex) { + logger.warn("Error in " + getClass() + " afterReload() " + ex, ex); + } + } + + //----------// + // annotate // + //----------// + /** + * Save sheet symbols annotations. + */ + public void annotate () + { + try { + final Book book = stub.getBook(); + final Path bookFolder = BookManager.getDefaultBookFolder(book); + annotate(bookFolder); + } catch (Exception ex) { + logger.warn("Annotations failed {}", ex); + } + } + + //----------// + // annotate // + //----------// /** * Save sheet symbols annotations into the provided folder. * * @param sheetFolder target folder (perhaps in a zip file system) */ - void annotate (Path sheetFolder); + public void annotate (Path sheetFolder) + { + OutputStream os = null; + + try { + // Sheet annotations + Path annPath = sheetFolder.resolve(getId() + Annotations.SHEET_ANNOTATIONS_EXTENSION); + new AnnotationsBuilder(this, annPath).processSheet(); + // Sheet image + Path imgPath = sheetFolder.resolve(getId() + Annotations.SHEET_IMAGE_EXTENSION); + RunTable runTable = picture.getTable(Picture.TableKey.BINARY); + BufferedImage img = runTable.getBufferedImage(); + os = Files.newOutputStream(imgPath, CREATE); + ImageIO.write(img, Annotations.SHEET_IMAGE_FORMAT, os); + } catch (IOException | + JAXBException ex) { + logger.warn("Error annotating {} {}", stub, ex.toString(), ex); + } finally { + if (os != null) { + try { + os.flush(); + os.close(); + } catch (IOException ignored) { + } + } + } + } + + //------------------// + // createBinaryView // + //------------------// /** - * Save sheet symbols annotations. + * Create and display the binary view. */ - void annotate (); + public void createBinaryView () + { + if (!SwingUtilities.isEventDispatchThread()) { + try { + SwingUtilities.invokeAndWait(new Runnable() + { + @Override + public void run () + { + createBinaryView(); + } + }); + } catch (InterruptedException | + InvocationTargetException ex) { + logger.warn("invokeAndWait error", ex); + } + } else { + final SheetAssembly assembly = stub.getAssembly(); + final SheetTab tab = SheetTab.BINARY_TAB; + + if (assembly.getPane(tab.label) == null) { + locationService.subscribeStrongly(LocationEvent.class, picture); + // Display sheet binary + PictureView pictureView = new PictureView(this); + assembly.addViewTab( + tab, + pictureView, + new BoardsPane(new PixelBoard(this), new BinarizationBoard(this))); + } else { + assembly.selectViewTab(tab); + } + } + } + + //-------------------// + // createPictureView // + //-------------------// + /** + * Create and display the picture view. + */ + public void createPictureView () + { + locationService.subscribeStrongly(LocationEvent.class, picture); + + // Display sheet picture + PictureView pictureView = new PictureView(this); + stub.getAssembly().addViewTab( + SheetTab.PICTURE_TAB, + pictureView, + new BoardsPane(new PixelBoard(this), new BinarizationBoard(this))); + } + + //----------------// + // displayDataTab // + //----------------// /** * Display the DATA_TAB. */ - void displayDataTab (); + public void displayDataTab () + { + try { + getSymbolsEditor(); + stub.getAssembly().selectViewTab(SheetTab.DATA_TAB); + } catch (Throwable ex) { + logger.warn("Error in displayDataTab " + ex, ex); + } + } + //-----------------// + // displayMainTabs // + //-----------------// /** * Display the main tabs related to this sheet. */ - void displayMainTabs (); + public void displayMainTabs () + { + if (stub.isDone(Step.GRID)) { + displayDataTab(); // Display DATA tab + } else if (stub.isDone(Step.BINARY)) { + createBinaryView(); // Display BINARY tab + } else { + createPictureView(); // Display Picture tab + } + + if (!stub.isValid()) { + StubsController.getInstance().markTab(stub, Colors.SHEET_INVALID); + } + } + + //-----------------// + // dumpSystemInfos // + //-----------------// + /** + * Utility method, to dump all sheet systems + */ + public void dumpSystemInfos () + { + System.out.println("--- SystemInfos ---"); + + int i = 0; + + for (SystemInfo system : getSystems()) { + new Dumping().dump(system, "#" + i++); + } + System.out.println("--- SystemInfos end ---"); + } + + //--------// + // export // + //--------// /** * Export a single sheet in MusicXML. *

                                                * The output is structured differently according to whether the sheet contains one or several - * pages.

                                                  + * pages. + *
                                                    *
                                                  • A single-page sheet results in one score output.
                                                  • *
                                                  • A multi-page sheet results in one opus output (if useOpus is set) or a series of scores * (is useOpus is not set).
                                                  • @@ -176,225 +613,1003 @@ public interface Sheet * * @param path sheet export path */ - void export (Path path); + public void export (Path path) + { + if (pages.isEmpty()) { + return; + } + + final Book book = getBook(); + + try { + Path folder = path.getParent(); + if (!Files.exists(folder)) { + Files.createDirectories(folder); + } + + final String ext = FileUtil.getExtension(path); + final String sheetName = FileUtil.getNameSansExtension(path.getFileName()); + final boolean compressed = (ext.equals(OMR.COMPRESSED_SCORE_EXTENSION)) ? true + : ((ext.equals(OMR.SCORE_EXTENSION)) ? false : BookManager.useCompression()); + final boolean useSig = BookManager.useSignature(); + + int modifs = 0; // Count of modifications + + if (pages.size() > 1) { + // One file per page + for (PageRef pageRef : stub.getPageRefs()) { + final Score score = new Score(); + score.setBook(book); + score.addPageRef(stub.getNumber(), pageRef); + modifs += new ScoreReduction(score).reduce(); + + final int idx = pageRef.getId(); + final String scoreName = sheetName + OMR.MOVEMENT_EXTENSION + idx; + final Path scorePath = path.resolveSibling(scoreName + ext); + new ScoreExporter(score).export(scorePath, sheetName, useSig, compressed); + } + } else { + // Export the sheet single page as a score + final Score score = new Score(); + score.setBook(book); + score.addPageRef(stub.getNumber(), stub.getFirstPageRef()); + modifs += new ScoreReduction(score).reduce(); + + final String scoreName = sheetName; + final Path scorePath = path.resolveSibling(scoreName + ext); + new ScoreExporter(score).export(scorePath, scoreName, useSig, compressed); + } + + if (modifs > 0) { + book.setModified(true); + } + + // Remember the book export path in the book itself + book.setExportPathSansExt(folder.resolve(book.getRadix())); + } catch (Exception ex) { + logger.warn("Error exporting " + this + ", " + ex, ex); + } + } + + //-----------------// + // getErrorsEditor // + //-----------------// /** - * In non batch mode, report the editor dealing with detected errors in this sheet + * In non batch mode, report the editor dealing with detected errors in this sheet. * * @return the errors editor, or null */ - ErrorsEditor getErrorsEditor (); + public ErrorsEditor getErrorsEditor () + { + return errorsEditor; + } + //------------------// + // getFilamentIndex // + //------------------// /** - * Report the global index for filaments of this sheet, or null + * Report the global index for filaments of this sheet, or null. * * @return the index for filaments, perhaps null */ - FilamentIndex getFilamentIndex (); + public FilamentIndex getFilamentIndex () + { + if (filamentIndex == null) { + filamentIndex = new FilamentIndex(this); + } + + return filamentIndex; + } + //---------------// + // getGlyphIndex // + //---------------// /** - * Report the global nest for glyphs of this sheet, or null + * Report the global index for glyphs of this sheet, or null. * * @return the nest for glyphs, perhaps null */ - GlyphIndex getGlyphIndex (); + public GlyphIndex getGlyphIndex () + { + if (glyphIndex == null) { + glyphIndex = new GlyphIndex(); + } + return glyphIndex; + } + + //---------------------// + // getGlyphsController // + //---------------------// /** * In non batch mode, report the UI module for symbol assignment in this sheet * * @return the glyphs controller */ - GlyphsController getGlyphsController (); + public GlyphsController getGlyphsController () + { + if (glyphsController == null) { + createGlyphsController(); + } + + return glyphsController; + } + //-----------// + // getHeight // + //-----------// /** * Report the picture height in pixels * * @return the picture height */ - int getHeight (); + public int getHeight () + { + return picture.getHeight(); + } + //-------// + // getId // + //-------// /** * Report the distinguished name for this sheet. * * @return sheet name */ - String getId (); + public String getId () + { + if (stub != null) { + return stub.getId(); + } + return null; + } + + //--------------------// + // getInterController // + //--------------------// /** * In non batch mode, report the UI module for inter management in this sheet * * @return the inter controller */ - InterController getInterController (); + public InterController getInterController () + { + if (interController == null) { + interController = new InterController(this); + } + + return interController; + } + //---------------// + // getInterIndex // + //---------------// /** - * Access to the Inter index for this sheet + * Report the global index for inters in this sheet * - * @return the sheet Inter's index + * @return the sheet Inter index */ - InterIndex getInterIndex (); + public InterIndex getInterIndex () + { + return interIndex; + } + //--------------// + // getInterline // + //--------------// /** * Convenient method to report the key scaling information of the sheet * * @return the scale interline value */ - int getInterline (); + public int getInterline () + { + return scale.getInterline(); + } + //---------------// + // getLagManager // + //---------------// /** * Access to the lag manager for this sheet * * @return the lag Manager */ - LagManager getLagManager (); + public LagManager getLagManager () + { + return lagManager; + } + + //-------------// + // getLastPage // + //-------------// + /** + * Report the last page of the sheet, if any. + * + * @return the last page or null + */ + public Page getLastPage () + { + if (pages.isEmpty()) { + return null; + } + + return pages.get(pages.size() - 1); + } + //--------------------// + // getLocationService // + //--------------------// /** * In non batch mode, give access to sheet location service. * * @return the selection service dedicated to location in sheet (null in batch mode) */ - SelectionService getLocationService (); + public SelectionService getLocationService () + { + return locationService; + } + //----------// + // getPages // + //----------// /** - * Report the collections of pages found in this sheet (generally just one). + * Report the sequence of pages found in this sheet (generally just one). * * @return the list of page(s) */ - List getPages (); + public List getPages () + { + return pages; + } + //--------------------------// + // getPersistentIdGenerator // + //--------------------------// /** * Access to the generator of persistent IDs for this sheet. * * @return the ID generator */ - AtomicInteger getPersistentIdGenerator (); + public AtomicInteger getPersistentIdGenerator () + { + return lastPersistentId; + } + //------------// + // getPicture // + //------------// /** * Report the picture of this sheet, that provides sources and tables. * * @return the related picture */ - Picture getPicture (); + public Picture getPicture () + { + if (picture == null) { + BufferedImage img = getBook().loadSheetImage(stub.getNumber()); + try { + setImage(img); + } catch (StepException ex) { + logger.warn("Error setting image id {}", stub.getNumber(), ex); + } + } + + return picture; + } + + //----------// + // getScale // + //----------// /** * Report the computed scale of this sheet. * This drives several processing thresholds. * * @return the sheet scale */ - Scale getScale (); + public Scale getScale () + { + return scale; + } + + //----------// + // setScale // + //----------// + /** + * Remember scale information to this sheet + * + * @param scale the computed sheet global scale + */ + public void setScale (Scale scale) + { + this.scale = scale; + } + + //----------// + // setImage // + //----------// + /** + * Assign the related image to this sheet + * + * @param image the loaded image + * @throws StepException if processing failed at this step + */ + public final void setImage (BufferedImage image) + throws StepException + { + try { + picture = new Picture(this, image, locationService); + + if (OMR.gui != null) { + createPictureView(); + } + + done(Step.LOAD); + } catch (ImageFormatException ex) { + String msg = "Unsupported image format in file " + stub.getBook().getInputPath() + + "\n" + + ex.getMessage(); + + if (OMR.gui != null) { + OMR.gui.displayWarning(msg); + } else { + logger.warn(msg); + } + throw new StepException(ex); + } catch (Throwable ex) { + logger.warn("Error loading image", ex); + } + } + + //---------------// + // getSheetDelta // + //---------------// /** * Report the measured difference between entities and pixels. * * @return the sheetDelta */ - SheetDiff getSheetDelta (); + public SheetDiff getSheetDelta () + { + return sheetDelta; + } + //---------------// + // setSheetDelta // + //---------------// + /** + * Remember the sheet delta in sheet + * + * @param sheetDelta difference between input (pixels) and output (recognized entities) + */ + public void setSheetDelta (SheetDiff sheetDelta) + { + this.sheetDelta = sheetDelta; + } + + //---------// + // getSkew // + //---------// /** * Report the skew information for this sheet. * * @return the skew information */ - Skew getSkew (); + public Skew getSkew () + { + return skew; + } + //---------// + // setSkew // + //---------// + /** + * Link skew information to this sheet + * + * @param skew the skew information + */ + public void setSkew (Skew skew) + { + this.skew = skew; + } + + //-----------------// + // getStaffManager // + //-----------------// /** * Access to the staff manager for this sheet * * @return the staff Manager */ - StaffManager getStaffManager (); + public StaffManager getStaffManager () + { + return staffManager; + } + //---------// + // getStub // + //---------// /** * Report the related sheet stub. * * @return the related stub (non null) */ - SheetStub getStub (); + public SheetStub getStub () + { + return stub; + } + //------------------// + // getSymbolsEditor // + //------------------// /** * In non batch mode, report the editor dealing with symbols recognition in this sheet * * @return the symbols editor, or null */ - SymbolsEditor getSymbolsEditor (); + public SymbolsEditor getSymbolsEditor () + { + if (symbolsEditor == null) { + interController = new InterController(this); + symbolsEditor = new SymbolsEditor(this, getGlyphsController(), interController); + interController.setSymbolsEditor(symbolsEditor); + } + return symbolsEditor; + } + + //------------------// + // getSystemManager // + //------------------// /** * Access to the system manager for this sheet * * @return the SystemManager instance */ - SystemManager getSystemManager (); + public SystemManager getSystemManager () + { + return systemManager; + } + //------------// + // getSystems // + //------------// /** * Convenient way to get an unmodifiable view on sheet systems. * * @return a view on systems list */ - List getSystems (); + public List getSystems () + { + return systemManager.getSystems(); + } + //----------// + // getWidth // + //----------// /** * Report the picture width in pixels * * @return the picture width */ - int getWidth (); + public int getWidth () + { + return picture.getWidth(); + } + //------------// + // hasPicture // + //------------// /** * Report whether the Picture instance exists in sheet. * * @return true if so */ - boolean hasPicture (); + public boolean hasPicture () + { + return picture != null; + } + //-------// + // print // + //-------// /** * Print the sheet physical appearance using PDF format. * * @param sheetPrintPath path of sheet print file */ - void print (Path sheetPrintPath); + public void print (Path sheetPrintPath) + { + // Actually write the PDF + try { + Path parent = sheetPrintPath.getParent(); + + if (!Files.exists(parent)) { + Files.createDirectories(parent); + } + + new BookPdfOutput(getBook(), sheetPrintPath.toFile()).write(this); + logger.info("Sheet printed to {}", sheetPrintPath); + } catch (Exception ex) { + logger.warn("Cannot print sheet to " + sheetPrintPath + " " + ex, ex); + } + } + //-------------// + // renderItems // + //-------------// /** * In non batch mode, apply the registered item renderings on the provided graphics. * * @param g the graphics context */ - void renderItems (Graphics2D g); + public void renderItems (Graphics2D g) + { + if (OMR.gui != null) { + for (ItemRenderer renderer : itemRenderers) { + renderer.renderItems(g); + } + } + } + //--------// + // sample // + //--------// /** * Save sheet samples into book repository. */ - void sample (); + public void sample () + { + final Book book = getBook(); + final SampleRepository repository = book.getSpecificSampleRepository(); + final SampleSheet sampleSheet = repository.findSampleSheet(this); + for (SystemInfo system : getSystems()) { + SIGraph sig = system.getSig(); + + for (Inter inter : sig.vertexSet()) { + Shape shape = inter.getShape(); + Staff staff = inter.getStaff(); + Glyph glyph = inter.getGlyph(); + + if ((shape != null) && (staff != null) && (glyph != null)) { + Double pitch = (inter instanceof AbstractPitchedInter) + ? ((AbstractPitchedInter) inter).getPitch() : null; + repository.addSample( + inter.getShape(), + glyph, + staff.getSpecificInterline(), + sampleSheet, + pitch); + } else { + logger.debug( + "No sample for {} shape:{} staff:{} glyph:{}", + inter, + shape, + staff, + glyph); + } + } + } + } + + //-------// + // store // + //-------// /** - * Assign the related image to this sheet + * Store sheet internals into book file system. * - * @param image the loaded image - * @throws StepException if processing failed at this step + * @param sheetFolder path of sheet folder in (new) book file + * @param oldSheetFolder path of sheet folder in old book file, if any */ - void setImage (BufferedImage image) - throws StepException; + public void store (Path sheetFolder, + Path oldSheetFolder) + { + // Picture internals, if any + if (picture != null) { + try { + // Make sure the folder exists for sheet internals + Files.createDirectories(sheetFolder); + + // Save picture tables + picture.store(sheetFolder, oldSheetFolder); + } catch (IOException ex) { + logger.warn("IOException on storing " + this, ex); + } + } + + // Sheet structure (sheet#n.xml) + try { + Path structurePath = sheetFolder.resolve(getSheetFileName(stub.getNumber())); + Files.deleteIfExists(structurePath); + Files.createDirectories(sheetFolder); + + try (OutputStream os = Files.newOutputStream(structurePath, CREATE);) { + Marshaller m = getJaxbContext().createMarshaller(); + XMLStreamWriter writer = new IndentingXMLStreamWriter( + XMLOutputFactory.newInstance().createXMLStreamWriter(os, "UTF-8")); + + if (constants.useMarshalLogger.isSet()) { + m.setListener(new Jaxb.MarshalLogger()); + } + + m.marshal(this, writer); + os.flush(); + } + stub.setModified(false); + stub.setUpgraded(false); + logger.info("Stored {}", structurePath); + } catch (IOException | + JAXBException | + XMLStreamException ex) { + logger.warn("Error in saving sheet structure " + ex, ex); + } + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("Sheet{"); + + if (getId() != null) { + sb.append(getId()); + } + + sb.append('}'); + + return sb.toString(); + } + + //------------------------// + // createGlyphsController // + //------------------------// + private void createGlyphsController () + { + GlyphsModel model = new GlyphsModel(this, getGlyphIndex().getEntityService()); + glyphsController = new GlyphsController(model); + } + + //------// + // done // + //------// /** - * Remember scale information to this sheet + * Remember that the provided step has been completed on the sheet. * - * @param scale the computed sheet global scale + * @param step the provided step */ - void setScale (Scale scale); + private void done (Step step) + { + stub.done(step); + } + + //---------// + // getBook // + //---------// + private Book getBook () + { + return stub.getBook(); + } + //----------------------// + // getGlyphIndexContent // Needed for JAXB + //----------------------// /** - * Link skew information to this sheet + * Mean for JAXB marshalling only. * - * @param skew the skew information + * @return collection of glyphs from glyphIndex.weakIndex */ - void setSkew (Skew skew); + @SuppressWarnings("unchecked") + @XmlElement(name = "glyph-index") + @XmlJavaTypeAdapter(GlyphListAdapter.class) + private ArrayList getGlyphIndexContent () + { + if (glyphIndex == null) { + return null; + } + return glyphIndex.getEntities(); + } + + //----------------------// + // setGlyphIndexContent // Needed for JAXB + //----------------------// /** - * Store sheet internals into book file system. + * Meant for JAXB unmarshalling only. * - * @param sheetFolder path of sheet folder in (new) book file - * @param oldSheetFolder path of sheet folder in old book file, if any + * @param glyphs collection of glyphs to feed to the glyphIndex.weakIndex + */ + @SuppressWarnings("unchecked") + private void setGlyphIndexContent (ArrayList glyphs) + { + getGlyphIndex().setEntities(glyphs); + } + + //-----------// + // getNumber // Needed for JAXB + //-----------// + /** + * Sheet 1-based number within book. + */ + @XmlAttribute(name = "number") + private int getNumber () + { + return stub.getNumber(); + } + + //----------------// + // initTransients // + //----------------// + /** + * Initialize needed transient members. + * (which by definition have not been set by the unmarshalling). + * + * @param stub the related stub */ - void store (Path sheetFolder, - Path oldSheetFolder); + private void initTransients (SheetStub stub) + { + logger.debug("Sheet#{} initTransients", stub.getNumber()); + + this.stub = stub; + + // Update UI information if so needed + if (OMR.gui != null) { + locationService = new SelectionService("locationService", allowedEvents); + errorsEditor = new ErrorsEditor(this); + itemRenderers = new LinkedHashSet<>(); + addItemRenderer(staffManager); + } + + if (picture != null) { + picture.initTransients(this); + } + + if (glyphIndex != null) { + glyphIndex.initTransients(this); + } + + for (Page page : pages) { + page.initTransients(this); + } + + if (systemManager == null) { + systemManager = new SystemManager(this); + } else { + systemManager.initTransients(this); + } + + // systemManager + List systems = new ArrayList<>(); + + for (Page page : pages) { + for (SystemInfo system : page.getSystems()) { + system.initTransients(this, page); + systems.add(system); + + List systemStaves = new ArrayList<>(); + + for (Part part : system.getParts()) { + part.setSystem(system); + + for (Staff staff : part.getStaves()) { + staff.setPart(part); + systemStaves.add(staff); + } + } + + system.setStaves(systemStaves); + } + } + + systemManager.setSystems(systems); + + staffManager = new StaffManager(this); + + lagManager = new LagManager(this); + } + + //-----------// + // setBinary // + //-----------// + private void setBinary (RunTable binaryTable) + { + try { + picture = new Picture(this, binaryTable); + + if (OMR.gui != null) { + createBinaryView(); + } + + done(Step.LOAD); + done(Step.BINARY); + } finally { + } + } + + //-------// + // reset // + //-------// + /** + * Reinitialize the sheet members, according to step needs. + * + * @param step the starting step + */ + void reset (Step step) + { + switch (step) { + default: + break; + + case LOAD: + picture = null; + + // Fall-through! + case BINARY: + scale = null; + + // Fall-through! + case SCALE: + case GRID: + pages.clear(); + stub.clearPageRefs(); + skew = null; + + lagManager.setLag(Lags.HLAG, null); + lagManager.setLag(Lags.VLAG, null); + + staffManager.reset(); + systemManager.reset(); + glyphsController = null; + symbolsEditor = null; + } + + // Clear errors and history for this step + if (OMR.gui != null) { + getErrorsEditor().clearStep(step); + + if (interController != null) { + SwingUtilities.invokeLater(new Runnable() + { + // This part is run on swing thread + @Override + public void run () + { + interController.clearHistory(); + } + }); + } + } + } + + //------------------// + // getSheetFileName // + //------------------// + /** + * Report the file name of a sheet in the .omr zip file system. + * + * @param number sheet number (counted from 1) within the containing book + * @return the sheet file name + */ + public static String getSheetFileName (int number) + { + return Sheet.INTERNALS_RADIX + number + ".xml"; + } + + //-----------// + // unmarshal // + //-----------// + /** + * Unmarshal the provided XML stream to allocate the corresponding sheet. + * + * @param in the input stream that contains the sheet in XML format. + * The stream is not closed by this method + * @return the allocated sheet. + * @exception JAXBException raised when unmarshalling goes wrong + */ + public static Sheet unmarshal (InputStream in) + throws JAXBException + { + Unmarshaller um = getJaxbContext().createUnmarshaller(); + + if (constants.useUnmarshalLogger.isSet()) { + um.setListener(new Jaxb.UnmarshalLogger()); + } + + Sheet sheet = (Sheet) um.unmarshal(in); + logger.debug("Sheet unmarshalled"); + + return sheet; + } + + //----------------// + // getJaxbContext // + //----------------// + private static JAXBContext getJaxbContext () + throws JAXBException + { + // Lazy creation + if (jaxbContext == null) { + jaxbContext = JAXBContext.newInstance(Sheet.class); + } + + return jaxbContext; + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean useMarshalLogger = new Constant.Boolean( + false, + "Should we log every sheet marshalling?"); + + private final Constant.Boolean useUnmarshalLogger = new Constant.Boolean( + false, + "Should we log every sheet unmarshalling?"); + } + + //-----------// + // GlyphList // For glyphIndex (un)marshalling + //-----------// + private static class GlyphList + { + + @XmlElement(name = "glyph") + public ArrayList glyphs; + + GlyphList () + { + } + + GlyphList (ArrayList glyphs) + { + this.glyphs = glyphs; + } + + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("GlyphList{"); + + if (glyphs != null) { + sb.append("size:").append(glyphs.size()); + } + + sb.append('}'); + + return sb.toString(); + } + } + + //------------------// + // GlyphListAdapter // For glyphIndex (un)marshalling + //------------------// + private static class GlyphListAdapter + extends XmlAdapter> + { + + @Override + public GlyphList marshal (ArrayList glyphs) + throws Exception + { + return new GlyphList(glyphs); + } + + @Override + public ArrayList unmarshal (GlyphList list) + throws Exception + { + return list.glyphs; + } + } } diff --git a/src/main/org/audiveris/omr/sheet/SheetDiff.java b/src/main/org/audiveris/omr/sheet/SheetDiff.java index 8e6ab5d57..2c2ca8c78 100644 --- a/src/main/org/audiveris/omr/sheet/SheetDiff.java +++ b/src/main/org/audiveris/omr/sheet/SheetDiff.java @@ -62,32 +62,11 @@ */ public class SheetDiff { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(SheetDiff.class); - //~ Enumerations ------------------------------------------------------------------------------- - public static enum DiffKind - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** - * Non recognized entities. - * Input data not found in output. */ - NEGATIVES, - /** - * Recognized entities. - * Intersection of input and output. */ - POSITIVES, - /** - * False recognized entities. - * Output data not found in input. */ - FALSE_POSITIVES; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** The related sheet. */ @Navigable(false) private final Sheet sheet; @@ -98,13 +77,16 @@ public static enum DiffKind /** Cached number of foreground pixels in input image. */ private Integer inputCount; - //~ Constructors ------------------------------------------------------------------------------- + /** + * Create a {@code SheetDiff} object. + * + * @param sheet related sheet + */ public SheetDiff (Sheet sheet) { this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // computeDiff // //-------------// @@ -210,9 +192,9 @@ public void computeRatios () watch.start("xor"); - BufferedImage xor = ImageUtil.invert(ImageUtil.xor(input, output)); - + ///BufferedImage xor = ImageUtil.invert(ImageUtil.xor(input, output)); ///ImageUtil.saveOnDisk(xor, sheet.getPage().getId() + ".XOR"); + // for (DiffKind kind : DiffKind.values()) { watch.start(kind.toString()); @@ -400,25 +382,6 @@ private BufferedImage getOutput () return output; } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print out the stop watch?"); - - private final Constant.Integer binaryThreshold = new Constant.Integer( - "gray level", - 127, - "Global threshold to binarize delta results"); - } - //--------// // MyView // //--------// @@ -426,9 +389,8 @@ private class MyView extends ImageView implements PropertyChangeListener { - //~ Constructors --------------------------------------------------------------------------- - public MyView (ByteProcessor filtered) + MyView (ByteProcessor filtered) { super(filtered.getBufferedImage()); setModelSize(new Dimension(sheet.getWidth(), sheet.getHeight())); @@ -437,11 +399,10 @@ public MyView (ByteProcessor filtered) setLocationService(sheet.getLocationService()); // Listen to all view parameters - ViewParameters.getInstance() - .addPropertyChangeListener(new WeakPropertyChangeListener(this)); + ViewParameters.getInstance().addPropertyChangeListener( + new WeakPropertyChangeListener(this)); } - //~ Methods -------------------------------------------------------------------------------- //----------------// // propertyChange // //----------------// @@ -451,4 +412,43 @@ public void propertyChange (PropertyChangeEvent evt) repaint(); } } + + /** + * + */ + public static enum DiffKind + { + /** + * Non recognized entities. + * Input data not found in output. + */ + NEGATIVES, + /** + * Recognized entities. + * Intersection of input and output. + */ + POSITIVES, + /** + * False recognized entities. + * Output data not found in input. + */ + FALSE_POSITIVES + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print out the stop watch?"); + + private final Constant.Integer binaryThreshold = new Constant.Integer( + "gray level", + 127, + "Global threshold to binarize delta results"); + } } diff --git a/src/main/org/audiveris/omr/sheet/SheetStub.java b/src/main/org/audiveris/omr/sheet/SheetStub.java index c80926f07..14253d572 100644 --- a/src/main/org/audiveris/omr/sheet/SheetStub.java +++ b/src/main/org/audiveris/omr/sheet/SheetStub.java @@ -21,27 +21,78 @@ // package org.audiveris.omr.sheet; +import org.audiveris.omr.Main; +import org.audiveris.omr.OMR; +import static org.audiveris.omr.WellKnowns.LINE_SEPARATOR; +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.image.FilterDescriptor; import org.audiveris.omr.image.FilterParam; +import org.audiveris.omr.log.LogUtil; +import org.audiveris.omr.run.RunTable; import org.audiveris.omr.score.PageRef; +import org.audiveris.omr.sheet.Picture.TableKey; +import static org.audiveris.omr.sheet.Sheet.INTERNALS_RADIX; import org.audiveris.omr.sheet.ui.SheetAssembly; +import org.audiveris.omr.sheet.ui.StubsController; +import org.audiveris.omr.step.ProcessingCancellationException; import org.audiveris.omr.step.Step; import org.audiveris.omr.step.StepException; +import org.audiveris.omr.step.ui.StepMonitoring; +import org.audiveris.omr.ui.Colors; +import org.audiveris.omr.util.Jaxb; +import org.audiveris.omr.util.Memory; +import org.audiveris.omr.util.Navigable; +import org.audiveris.omr.util.OmrExecutors; +import org.audiveris.omr.util.StopWatch; +import org.audiveris.omr.util.ZipFileSystem; import org.audiveris.omr.util.param.Param; +import org.audiveris.omr.util.param.StringParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import javax.annotation.PostConstruct; +import javax.swing.SwingUtilities; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlList; +import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * Class {@code SheetStub} represents a placeholder in a {@link Book} to decouple the * Book instance from the actual {@link Sheet} instances and avoid loading all of them * in memory. - * + *

                                                    * Methods are organized as follows: *

                                                    *
                                                    Administration
                                                    - *
                                                      + *
                                                      + *
                                                        *
                                                      • {@link #getId}
                                                      • *
                                                      • {@link #getBook}
                                                      • *
                                                      • {@link #getNum}
                                                      • @@ -50,76 +101,195 @@ *
                                                      • {@link #getSheet}
                                                      • *
                                                      • {@link #swapSheet}
                                                      • *
                                                      • {@link #decideOnRemoval}
                                                      • - *
                                                      • {@link #isModified}
                                                      • *
                                                      • {@link #setModified}
                                                      • + *
                                                      • {@link #isModified}
                                                      • *
                                                      • {@link #close}
                                                      • *
                                                      • {@link #getLock}
                                                      • - *
                                                      - * + *
                                                    • {@link #storeSheet}
                                                    • + *
                                                    + *
                                                    *
                                                    Pages
                                                    - *
                                                      + *
                                                      + *
                                                        *
                                                      • {@link #addPageRef}
                                                      • *
                                                      • {@link #clearPageRefs}
                                                      • *
                                                      • {@link #getFirstPageRef}
                                                      • *
                                                      • {@link #getLastPageRef}
                                                      • *
                                                      • {@link #getPageRefs}
                                                      • - *
                                                      - * + *
                                                    + *
                                                    *
                                                    Parameters
                                                    - *
                                                      + *
                                                      + *
                                                        *
                                                      • {@link #getBinarizationFilter}
                                                      • *
                                                      • {@link #getOcrLanguages}
                                                      • *
                                                      • {@link #getProcessingSwitches}
                                                      • - *
                                                      - * + *
                                                    + *
                                                    *
                                                    Transcription
                                                    - *
                                                      + *
                                                      + *
                                                        *
                                                      • {@link #reset}
                                                      • *
                                                      • {@link #resetToBinary}
                                                      • *
                                                      • {@link #reachStep}
                                                      • *
                                                      • {@link #getCurrentStep}
                                                      • *
                                                      • {@link #getLatestStep}
                                                      • *
                                                      • {@link #transcribe}
                                                      • + *
                                                      • {@link #done}
                                                      • *
                                                      • {@link #isDone}
                                                      • - *
                                                      • {@link #isValid}
                                                      • *
                                                      • {@link #invalidate}
                                                      • - *
                                                      - * - *
                                                      Artifacts
                                                      - *
                                                        - *
                                                      • {@link #storeSheet}
                                                      • - *
                                                      - * + *
                                                    • {@link #isValid}
                                                    • + *
                                                    + *
                                                    *
                                                    UI
                                                    - *
                                                      + *
                                                      + *
                                                        *
                                                      • {@link #getAssembly}
                                                      • - *
                                                      + *
                                                    + *
                                                    *
                                                    * * @author Hervé Bitteur */ -@XmlJavaTypeAdapter(BasicStub.Adapter.class) -public interface SheetStub +@XmlAccessorType(XmlAccessType.NONE) +public class SheetStub { - //~ Methods ------------------------------------------------------------------------------------ + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(SheetStub.class); + + // Persistent data + //---------------- + // + /** Index of sheet, counted from 1, in the image file. */ + @XmlAttribute(name = "number") + private final int number; + + /** Indicate a sheet that contains no music. */ + @XmlAttribute + @XmlJavaTypeAdapter(type = boolean.class, value = Jaxb.BooleanPositiveAdapter.class) + private boolean invalid; + + /** Handling of binarization filter parameter. */ + @XmlElement(name = "binarization") + @XmlJavaTypeAdapter(FilterParam.Adapter.class) + private FilterParam binarizationFilter; + + /** Handling of dominant language(s) for this sheet. */ + @XmlElement(name = "ocr-languages") + @XmlJavaTypeAdapter(StringParam.Adapter.class) + private StringParam ocrLanguages; + + /** Handling of processing switches for this sheet. */ + @XmlElement(name = "processing") + @XmlJavaTypeAdapter(ProcessingSwitches.Adapter.class) + private ProcessingSwitches switches; + + /** All steps already performed on this sheet. */ + @XmlList + @XmlElement(name = "steps") + private final EnumSet doneSteps = EnumSet.noneOf(Step.class); + + /** Pages references. */ + @XmlElement(name = "page") + private final List pageRefs = new ArrayList<>(); + + // Transient data + //--------------- + // + /** Processing lock. */ + private final Lock lock = new ReentrantLock(); + + /** Containing book. */ + @Navigable(false) + private Book book; + + /** Full sheet material, if any. */ + private volatile Sheet sheet; + + /** The step being performed on the sheet. */ + private volatile Step currentStep; + + /** Has this sheet been modified, WRT its persisted data. */ + private volatile boolean modified = false; + + /** Has this sheet been upgraded, WRT its persisted data. */ + private volatile boolean upgraded = false; + + /** Related assembly instance, if any. */ + private SheetAssembly assembly; + + /** + * Creates a new {@code SheetStub} object. + * + * @param book the containing book instance + * @param number the 1-based sheet number within the containing book + */ + public SheetStub (Book book, + int number) + { + this.number = number; + + initTransients(book); + } + + /** + * No-arg constructor meant for JAXB. + */ + private SheetStub () + { + this.number = 0; + } + + //------------// + // addPageRef // + //------------// /** * Add a page reference to this stub. * * @param pageRef the page reference */ - void addPageRef (PageRef pageRef); + public void addPageRef (PageRef pageRef) + { + pageRefs.add(pageRef); + } + //---------------// + // clearPageRefs // + //---------------// /** * Empty the collection of page references. */ - void clearPageRefs (); + public void clearPageRefs () + { + // Clear pageRefs in this stub + pageRefs.clear(); + + // Clear pageRefs in related book store if any + } + //-------// + // close // + //-------// /** * Close this stub, and remove it from the containing book. */ - void close (); + public void close () + { + // If no stub is left, force book closing + if (!book.isClosing()) { + if (!book.getStubs().isEmpty()) { + logger.info("Sheet closed"); + } else { + book.close(); + } + } + } + //-----------------// + // decideOnRemoval // + //-----------------// /** * An abnormal situation has been found, as detailed in provided message, * now how should we proceed?, depending on batch mode or user answer. @@ -128,195 +298,966 @@ public interface SheetStub * @param dummy true for a dummy (positive) decision * @throws StepException thrown when processing must stop */ - void decideOnRemoval (String msg, - boolean dummy) - throws StepException; + public void decideOnRemoval (String msg, + boolean dummy) + throws StepException + { + if (dummy) { + invalidate(); + throw new StepException("Dummy decision"); + } + + logger.warn(msg.replaceAll(LINE_SEPARATOR, " ")); + + if (OMR.gui != null) { + StubsController.invokeSelect(this); + } + if ((OMR.gui == null) || (OMR.gui.displayConfirmation( + msg + LINE_SEPARATOR + "OK for discarding this sheet?"))) { + invalidate(); + + if (book.isMultiSheet()) { + close(); + throw new StepException("Sheet removed"); + } else { + throw new StepException("Sheet ignored"); + } + } + } + + //------// + // done // + //------// + /** + * Remember that the provided step has been completed on the sheet. + * + * @param step the provided step + */ + public final void done (Step step) + { + doneSteps.add(step); + } + + //-------------// + // getAssembly // + //-------------// /** * In non batch mode, report the related SheetAssembly for GUI * * @return the stub UI assembly, or null in batch mode */ - SheetAssembly getAssembly (); + public SheetAssembly getAssembly () + { + return assembly; + } + //-----------------------// + // getBinarizationFilter // + //-----------------------// /** * Report the binarization filter defined at sheet level. * * @return the filter parameter */ - FilterParam getBinarizationFilter (); + public FilterParam getBinarizationFilter () + { + if (binarizationFilter == null) { + binarizationFilter = new FilterParam(); + binarizationFilter.setParent(FilterDescriptor.defaultFilter); + } + + return binarizationFilter; + } + //---------// + // getBook // + //---------// /** * Report the containing book. * * @return containing book */ - Book getBook (); + public Book getBook () + { + return book; + } + //----------------// + // getCurrentStep // + //----------------// /** * Report the step being processed, if any. * * @return the current step or null */ - Step getCurrentStep (); + public Step getCurrentStep () + { + return currentStep; + } + + //----------------// + // setCurrentStep // + //----------------// + /** + * Assign the step being performed. + * + * @param step the current step + */ + public void setCurrentStep (Step step) + { + currentStep = step; + } + //-----------------// + // getFirstPageRef // + //-----------------// /** * Report the first page ref in stub * * @return first page ref or null */ - PageRef getFirstPageRef (); + public PageRef getFirstPageRef () + { + if (pageRefs.isEmpty()) { + return null; + } + + return pageRefs.get(0); + } + //-------// + // getId // + //-------// /** * Report the distinguished name for this sheet stub. * * @return sheet (stub) name */ - String getId (); + public String getId () + { + if (book.isMultiSheet()) { + return book.getRadix() + "#" + number; + } else { + return book.getRadix(); + } + } + //----------------// + // getLastPageRef // + //----------------// /** * Report the last page ref in stub * * @return last page ref or null */ - PageRef getLastPageRef (); + public PageRef getLastPageRef () + { + if (pageRefs.isEmpty()) { + return null; + } + + return pageRefs.get(pageRefs.size() - 1); + } + //---------------// + // getLatestStep // + //---------------// /** * Report the latest step done so far on this sheet. * * @return the latest step done, or null */ - Step getLatestStep (); + public Step getLatestStep () + { + Step latest = null; + + for (Step step : Step.values()) { + if (isDone(step)) { + latest = step; + } + } + + return latest; + } + //---------// + // getLock // + //---------// /** * Report the lock that protects stub processing. * * @return stub processing lock */ - Lock getLock (); + public Lock getLock () + { + return lock; + } + //--------// + // getNum // + //--------// /** * Report the number string for this sheet in containing book * * @return "#n" for a multi-sheet book, "" otherwise */ - String getNum (); + public String getNum () + { + if (book.isMultiSheet()) { + return "#" + number; + } + return ""; + } + + //-----------// + // getNumber // + //-----------// /** * Report the number for this sheet in containing book * * @return the sheet index number (1-based) in containing book */ - int getNumber (); + public int getNumber () + { + return number; + } + //-----------------// + // getOcrLanguages // + //-----------------// /** * Report the OCR language(s) specification defined at sheet level if any. * * @return the OCR language(s) spec */ - Param getOcrLanguages (); + public Param getOcrLanguages () + { + if (ocrLanguages == null) { + ocrLanguages = new StringParam(); + ocrLanguages.setParent(book.getOcrLanguages()); + } + + return ocrLanguages; + } + //-------------// + // getPageRefs // + //-------------// /** * Report the stub sequence of page references. * * @return the page ref 's */ - List getPageRefs (); + public List getPageRefs () + { + return pageRefs; + } + //-----------------------// + // getProcessingSwitches // + //-----------------------// /** * Report the processing switches defined at sheet level if any. * * @return sheet switches */ - ProcessingSwitches getProcessingSwitches (); + public ProcessingSwitches getProcessingSwitches () + { + if (switches == null) { + switches = new ProcessingSwitches(); + switches.setParent(book.getProcessingSwitches()); + } + + return switches; + } + //----------// + // getSheet // + //----------// /** * Make sure the sheet material is in memory. * * @return the sheet ready to use */ - Sheet getSheet (); + public Sheet getSheet () + { + Sheet sh = this.sheet; + if (sh == null) { + synchronized (this) { + sh = this.sheet; + // We have to recheck sheet, which may have just been allocated + if (sh == null) { + if (SwingUtilities.isEventDispatchThread()) { + logger.warn("XXXX getSheet called on EDT XXXX"); + } + + // Actually load the sheet + if (!isDone(Step.LOAD)) { + // LOAD not yet performed: load from book image file + try { + this.sheet = sh = new Sheet(this, (BufferedImage) null); + } catch (StepException ignored) { + logger.info("Could not load sheet for stub {}", this); + } + } else { + // LOAD already performed: load from book file + StopWatch watch = new StopWatch("Load Sheet " + this); + + try { + Path sheetFile = null; + watch.start("unmarshal"); + // Open the book file system + try { + book.getLock().lock(); + sheetFile = book.openSheetFolder(number).resolve( + Sheet.getSheetFileName(number)); + + try (InputStream is = Files.newInputStream( + sheetFile, + StandardOpenOption.READ)) { + this.sheet = sh = Sheet.unmarshal(is); + } + + sheetFile.getFileSystem().close(); + } finally { + book.getLock().unlock(); + } + + // Complete sheet reload + watch.start("afterReload"); + sh.afterReload(this); + logger.info("Loaded {}", sheetFile); + } catch (IOException | + JAXBException ex) { + logger.warn("Error in loading sheet structure " + ex, ex); + logger.info("Trying to restart from binary"); + resetToBinary(); + } finally { + if (constants.printWatch.isSet()) { + watch.print(); + } + } + } + } + } + } + + return sh; + } + + //----------// + // hasSheet // + //----------// /** * Report whether the stub has a sheet in memory * * @return true if sheet is present in memory */ - boolean hasSheet (); + public boolean hasSheet () + { + return sheet != null; + } + //------------// + // invalidate // + //------------// /** * Flag a stub as invalid (containing no music). */ - void invalidate (); + public void invalidate () + { + invalid = Boolean.TRUE; + book.updateScores(this); + + pageRefs.clear(); + setModified(true); + + if (OMR.gui != null) { + StubsController.getInstance().markTab(this, Colors.SHEET_INVALID); + } + + logger.info("Sheet {} flagged as invalid.", getId()); + } + + //--------// + // isDone // + //--------// /** * Report whether the specified step has been performed on this sheet * * @param step the step to check * @return true if already performed */ - boolean isDone (Step step); + public boolean isDone (Step step) + { + return doneSteps.contains(step); + } + //------------// + // isModified // + //------------// /** - * Has the sheet been modified with respect to its book data?. + * Has the sheet been modified with respect to its persisted data?. * * @return true if modified */ - boolean isModified (); + public boolean isModified () + { + return modified; + } + //-------------// + // setModified // + //-------------// + /** + * Set the modified flag. + * + * @param modified the new flag value + */ + public void setModified (boolean modified) + { + this.modified = modified; + + if (modified) { + book.setModified(true); + book.setDirty(true); + } + } + + //------------// + // isUpgraded // + //------------// + /** + * Has the sheet been upgraded with respect to its persisted data?. + * + * @return true if upgraded + */ + public boolean isUpgraded () + { + return upgraded; + } + + //-------------// + // setUpgraded // + //-------------// + /** + * Set the upgraded flag. + * + * @param upgraded the new flag value + */ + public void setUpgraded (boolean upgraded) + { + this.upgraded = upgraded; + + if (OMR.gui != null) { + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run () + { + final StubsController controller = StubsController.getInstance(); + final SheetStub stub = controller.getSelectedStub(); + + if ((stub == SheetStub.this)) { + controller.refresh(); + } + } + }); + } + } + + //---------// + // isValid // + //---------// /** * Report whether this sheet is valid music. * * @return true if valid, false if invalid */ - boolean isValid (); + public boolean isValid () + { + return !invalid; + } + //-----------// + // reachStep // + //-----------// /** * Make sure the provided step has been reached on this sheet stub. + *

                                                    + * Each needed step is performed sequentially, guarded by a timeout. * - * @param step the step to check - * @param force if true and step already reached, stub is reset and processed until step + * @param target the step to check + * @param force if true and step already reached, stub is reset and processed until step * @return true if OK */ - boolean reachStep (Step step, - boolean force); + public boolean reachStep (Step target, + boolean force) + { + final StubsController ctrl = (OMR.gui != null) ? StubsController.getInstance() : null; + final StopWatch watch = new StopWatch("reachStep " + target); + EnumSet neededSteps = null; + boolean ok = false; + getLock().lock(); // Wait for completion of early processing if any + logger.debug("reachStep got lock on {}", this); + + try { + final Step latestStep = getLatestStep(); + + if (force && (target.compareTo(latestStep) <= 0)) { + resetToBinary(); + } + + neededSteps = getNeededSteps(target); + if (neededSteps.isEmpty()) { + return true; + } + + logger.debug("Sheet#{} scheduling {}", number, neededSteps); + StepMonitoring.notifyStart(); + + if (ctrl != null) { + ctrl.markTab(this, Colors.SHEET_BUSY); + } + + for (final Step step : neededSteps) { + watch.start(step.name()); + StepMonitoring.notifyMsg(step.toString()); + logger.debug("reachStep {} towards {}", step, target); + doOneStep(step); + } + + ok = true; + } catch (ProcessingCancellationException pce) { + throw pce; + } catch (StepException ignored) { + logger.info("StepException detected in " + neededSteps); + } catch (ExecutionException ex) { + // A StepException may have been wrapped into an ExecutionException + if (ex.getCause() instanceof StepException) { + logger.info("StepException cause detected in " + neededSteps); + } else { + logger.warn("Error in performing {} {}", neededSteps, ex.toString(), ex); + } + } catch (Exception ex) { + logger.warn("Error in performing {} {}", neededSteps, ex.toString(), ex); + } finally { + StepMonitoring.notifyStop(); + + if (constants.printWatch.isSet()) { + watch.print(); + } + + logger.debug("reachStep releasing lock on {}", this); + getLock().unlock(); + } + + if (ctrl != null) { + ctrl.markTab(this, ok ? Colors.SHEET_OK : Colors.SHEET_NOT_OK); + } + + return ok; + } + + //-------// + // reset // + //-------// /** * Reset this stub to its initial state (that is valid and non-processed). */ - void reset (); + public void reset () + { + doReset(); + logger.info("Sheet#{} reset as valid.", number); + + if (OMR.gui != null) { + try { + Runnable runnable = new Runnable() + { + @Override + public void run () + { + StubsController.getInstance().reDisplay(SheetStub.this); + } + }; + if (SwingUtilities.isEventDispatchThread()) { + runnable.run(); + } else { + SwingUtilities.invokeAndWait(runnable); + } + } catch (InterruptedException | + InvocationTargetException ex) { + logger.warn("Could not reset {}", ex.toString(), ex); + } + } + } + + //---------------// + // resetToBinary // + //---------------// /** * Reset this stub to its BINARY step. */ - void resetToBinary (); + public void resetToBinary () + { + try { + // Avoid loading sheet just to reset to binary: + // If sheet is available, use its picture.getTable() + // Otherwise, load it directly from binary.xml on disk + RunTable binaryTable = null; - /** - * Set the modified flag. - * - * @param modified the new flag value - */ - void setModified (boolean modified); + if (hasSheet()) { + logger.debug("Getting BINARY from sheet"); + binaryTable = getSheet().getPicture().getTable(TableKey.BINARY); + } + + if (binaryTable == null) { + logger.debug("Loading BINARY from disk"); + binaryTable = new RunTableHolder(TableKey.BINARY).getData(this); + } + + doReset(); + sheet = new Sheet(this, binaryTable); + logger.info("Sheet#{} reset to BINARY.", number); + } catch (Throwable ex) { + logger.warn("Could not reset to BINARY {}", ex.toString(), ex); + reset(); + } + } + //------------// + // storeSheet // + //------------// /** * Store sheet material into book. * * @throws Exception if storing fails */ - void storeSheet () - throws Exception; + public void storeSheet () + throws Exception + { + if (modified) { + final Lock lock = book.getLock(); + lock.lock(); + + try { + Path bookPath = BookManager.getDefaultSavePath(book); + Path root = ZipFileSystem.open(bookPath); + book.storeBookInfo(root); // Book info (book.xml) + + Path sheetFolder = root.resolve(INTERNALS_RADIX + getNumber()); + sheet.store(sheetFolder, null); + root.getFileSystem().close(); + } finally { + lock.unlock(); + } + } + } + //-----------// + // swapSheet // + //-----------// /** * Swap sheet material. * If modified and not discarded, sheet material will be stored before being disposed of. */ - void swapSheet (); + public void swapSheet () + { + try { + if (isModified()) { + logger.info("{} storing", this); + storeSheet(); + } + if (sheet != null) { + logger.info("{} disposed", sheet); + sheet = null; + Memory.gc(); // Trigger a garbage collection... + } + + if (OMR.gui != null) { + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run () + { + // Gray out the related tab + StubsController ctrl = StubsController.getInstance(); + ctrl.markTab(SheetStub.this, Colors.SHEET_NOT_LOADED); + + // Close stub UI, if any + if (assembly != null) { + assembly.reset(); + } + } + }); + } + } catch (Exception ex) { + logger.warn("Error swapping sheet", ex); + } + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + return "Stub#" + number; + } + + //------------// + // transcribe // + //------------// /** * Convenient method to reach last step on this stub. * Defined as reachStep(Step.last(), false); * * @return true if OK */ - boolean transcribe (); + public boolean transcribe () + { + return reachStep(Step.last(), false); + } + + //----------------// + // afterUnmarshal // + //----------------// + /** + * Called after all the properties (except IDREF) are unmarshalled + * for this object, but before this object is set to the parent object. + * All non-persistent members are null. + */ + @PostConstruct // Don't remove this method, invoked by JAXB through reflection + + private void afterUnmarshal (Unmarshaller um, + Object parent) + { + initTransients((Book) parent); + } + + //---------------// + // beforeMarshal // + //---------------// + @SuppressWarnings("unused") + private void beforeMarshal (Marshaller m) + { + if ((binarizationFilter != null) && !binarizationFilter.isSpecific()) { + binarizationFilter = null; + } + + if ((ocrLanguages != null) && !ocrLanguages.isSpecific()) { + ocrLanguages = null; + } + + if ((switches != null) && switches.isEmpty()) { + switches = null; + } + } + + //-----------// + // doOneStep // + //-----------// + /** + * Do just one specified step, synchronously, with display of related UI if any. + *

                                                    + * Step duration is guarded by a timeout, so that processing cannot get blocked infinitely. + * + * @param step the step to perform + * @throws Exception + */ + private void doOneStep (final Step step) + throws Exception + { + final int timeout = Main.getSheetStepTimeOut(); + Future future = null; + + try { + // Make sure sheet is available + if (!hasSheet()) { + getSheet(); + } + + // Implement a timeout for this step on the stub + future = OmrExecutors.getCachedLowExecutor().submit(new Callable() + { + @Override + public Void call () + throws Exception + { + LogUtil.start(SheetStub.this); + + try { + setCurrentStep(step); + StepMonitoring.notifyStep(SheetStub.this, step); // Start monitoring + setModified(true); // At beginning of processing + sheet.reset(step); // Reset sheet relevant data + step.doit(sheet); // Standard processing on an existing sheet + done(step); // Full completion + } finally { + LogUtil.stopStub(); + } + + return null; + } + }); + + future.get(timeout, TimeUnit.SECONDS); + + // At end of each step, save sheet to disk? + if ((OMR.gui == null) && Main.getCli().isSave()) { + logger.debug("calling storeSheet"); + storeSheet(); + } + } catch (TimeoutException tex) { + logger.warn("Timeout {} seconds for step {}", timeout, step, tex); + + // Signal the on-going step processing to stop (if possible) + if (future != null) { + future.cancel(true); + } + + throw new ProcessingCancellationException(tex); + } finally { + setCurrentStep(null); + StepMonitoring.notifyStep(this, step); // Stop monitoring + } + } + + //---------// + // doReset // + //---------// + private void doReset () + { + doneSteps.clear(); + pageRefs.clear(); + invalid = false; + sheet = null; + + if (assembly != null) { + assembly.reset(); + } + + setModified(true); + } + + //----------------// + // getNeededSteps // + //----------------// + private EnumSet getNeededSteps (Step target) + { + EnumSet neededSteps = EnumSet.noneOf(Step.class); + + // Add all needed steps + for (Step step : EnumSet.range(Step.first(), target)) { + if (!isDone(step)) { + neededSteps.add(step); + } + } + + return neededSteps; + } + + //----------------// + // initTransients // + //----------------// + /** + * Initialize needed transient members. + * (which by definition have not been set by the unmarshalling). + * + * @param book the containing book + */ + private void initTransients (Book book) + { + try { + LogUtil.start(book); + + logger.trace("{} initTransients", this); + this.book = book; + + if (binarizationFilter != null) { + binarizationFilter.setParent(book.getBinarizationFilter()); + } + + if (ocrLanguages != null) { + ocrLanguages.setParent(book.getOcrLanguages()); + } + + if (switches != null) { + switches.setParent(book.getProcessingSwitches()); + } + + if (OMR.gui != null) { + assembly = new SheetAssembly(this); + } + } finally { + LogUtil.stopBook(); + } + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print out the stop watch for sheet loading"); + } + + //-------------------// + // OcrSheetLanguages // + //-------------------// + private static class OcrSheetLanguages + extends Param + { + + @Override + public boolean setSpecific (String specific) + { + // Normalize + if ((specific != null) && specific.isEmpty()) { + specific = null; + } + + return super.setSpecific(specific); + } + + /** + * JAXB adapter to mimic XmlValue. + */ + public static class Adapter + extends XmlAdapter + { + + @Override + public String marshal (OcrSheetLanguages val) + throws Exception + { + if (val == null) { + return null; + } + + return val.getSpecific(); + } + + @Override + public OcrSheetLanguages unmarshal (String str) + throws Exception + { + OcrSheetLanguages ol = new OcrSheetLanguages(); + ol.setSpecific(str); + + return ol; + } + } + } } diff --git a/src/main/org/audiveris/omr/sheet/Skew.java b/src/main/org/audiveris/omr/sheet/Skew.java index ad03bda1f..7855a36e9 100644 --- a/src/main/org/audiveris/omr/sheet/Skew.java +++ b/src/main/org/audiveris/omr/sheet/Skew.java @@ -42,13 +42,9 @@ */ public class Skew { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - Skew.class); + private static final Logger logger = LoggerFactory.getLogger(Skew.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -72,7 +68,6 @@ public class Skew /** Height of de-skewed sheet. */ private double deskewedHeight; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Skew object. * @@ -99,7 +94,6 @@ public Skew () this.deskewedHeight = 0; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // deskewed // //----------// diff --git a/src/main/org/audiveris/omr/sheet/Staff.java b/src/main/org/audiveris/omr/sheet/Staff.java index cd8166182..e382a5d7a 100644 --- a/src/main/org/audiveris/omr/sheet/Staff.java +++ b/src/main/org/audiveris/omr/sheet/Staff.java @@ -103,12 +103,10 @@ public class Staff implements AttachmentHolder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - Staff.class); + private static final Logger logger = LoggerFactory.getLogger(Staff.class); /** To sort by staff id. */ public static final Comparator byId = new Comparator() @@ -132,12 +130,13 @@ public int compare (Staff o1, } }; - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // - /** Staff id. (counted globally from 1 within the sheet) */ + /** + * Staff id. (counted globally from 1 within the sheet) + * The StringIntegerAdapter allows to use this id as an XML ID. + */ @XmlAttribute @XmlJavaTypeAdapter(type = int.class, value = Jaxb.StringIntegerAdapter.class) private final int id; @@ -173,7 +172,7 @@ public int compare (Staff o1, @XmlList @XmlIDREF @XmlElement(name = "barlines") - private List barlines = new ArrayList(); + private List barlines = new ArrayList<>(); /** Ledgers nearby, organized by position index WRT staff. Temporary for persistency */ @XmlElementWrapper(name = "ledgers") @@ -190,14 +189,13 @@ public int compare (Staff o1, //--------------- // /** Ledgers nearby, organized in a map. */ - private final TreeMap> ledgerMap = new TreeMap>(); + private final TreeMap> ledgerMap = new TreeMap<>(); /** To flag a dummy staff. */ private boolean dummy; /** Side barlines, if any. */ - private final Map sideBars = new EnumMap( - HorizontalSide.class); + private final Map sideBars = new EnumMap<>(HorizontalSide.class); /** * Area around the staff. @@ -232,7 +230,6 @@ public int compare (Staff o1, /** Potential attachments. */ private final AttachmentHolder attachments = new BasicAttachmentHolder(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create info about a staff, with its contained staff lines. * @@ -275,66 +272,6 @@ private Staff (int id) this.lines = null; } - //~ Methods ------------------------------------------------------------------------------------ - //-------------------// - // getCompetingClefs // - //-------------------// - /** - * Report the competing clef candidates active at provided abscissa. - * - * @param x provided abscissa - * @return the collection of competing clefs - */ - public List getCompetingClefs (int x) - { - // Look for clef on left side in staff (together with its competing clefs) - SIGraph sig = getSystem().getSig(); - List staffClefs = sig.inters(this, ClefInter.class); - Collections.sort(staffClefs, Inters.byAbscissa); - - Inter lastClef = null; - - for (Inter inter : staffClefs) { - int xClef = inter.getBounds().x; - - if (xClef < x) { - lastClef = inter; - } - } - - if (lastClef == null) { - return Collections.emptyList(); - } - - // Pick up this clef together with all competing clefs - Set excs = sig.getExclusions(lastClef); - List clefs = new ArrayList(); - clefs.add((ClefInter) lastClef); - - for (Relation rel : excs) { - Inter inter = Graphs.getOppositeVertex(sig, rel, lastClef); - - if (inter instanceof ClefInter) { - ClefInter clef = (ClefInter) inter; - - if ((clef.getStaff() == this) && !clefs.contains(clef)) { - clefs.add(clef); - } - } - } - - return clefs; - } - - //----------------------// - // getDefiningPointSize // - //----------------------// - public static Scale.Fraction getDefiningPointSize () - { - return constants.definingPointSize; - } - - // //---------------// // addAttachment // //---------------// @@ -377,7 +314,7 @@ public void addLedger (LedgerInter ledger, List ledgerSet = ledgerMap.get(index); if (ledgerSet == null) { - ledgerSet = new ArrayList(); + ledgerSet = new ArrayList<>(); ledgerMap.put(index, ledgerSet); } @@ -412,7 +349,7 @@ public void addLedger (LedgerInter ledger) public void addNote (AbstractNoteInter note) { if (notes == null) { - notes = new LinkedHashSet(); + notes = new LinkedHashSet<>(); } notes.add(note); @@ -421,6 +358,9 @@ public void addNote (AbstractNoteInter note) //-------------// // afterReload // //-------------// + /** + * To be called right after unmarshalling. + */ public void afterReload () { try { @@ -552,6 +492,21 @@ public Area getArea () return area; } + //---------// + // setArea // + //---------// + /** + * Assign staff area. + * + * @param area the underlying area + */ + public void setArea (Area area) + { + this.area = area; + + ///addAttachment("staff-area-" + id, area); + } + //---------------// // getAreaBounds // //---------------// @@ -585,9 +540,29 @@ public List getBarlines () return Collections.unmodifiableList(barlines); } + //-------------// + // setBarlines // + //-------------// + /** + * Assign the sequence of barlines. + * + * @param barlines the barlines to set + */ + public void setBarlines (List barlines) + { + this.barlines = barlines; + retrieveSideBars(); + } + //-------------// // getBestClef // //-------------// + /** + * Report the best clef that applies at provided abscissa. + * + * @param x provided abscissa + * @return best clef found or null + */ public ClefInter getBestClef (int x) { List clefs = getCompetingClefs(x); @@ -674,6 +649,20 @@ public Integer getClefStop () return null; } + //-------------// + // setClefStop // + //-------------// + /** + * Assign the ending abscissa of staff header clef. + * + * @param clefStop the clefStop to set + */ + public void setClefStop (int clefStop) + { + header.clefRange.setStop(clefStop); + header.clefRange.valid = true; + } + //------------------// // getClosestLedger // //------------------// @@ -707,7 +696,6 @@ public IndexedLedger getClosestLedger (Point2D point) searchBox = new Rectangle2D.Double(point.getX(), bottom, 0, point.getY() - bottom + 1); } - //searchBox.grow(interline, interline); searchBox.setRect( searchBox.getX() - specificInterline, searchBox.getY() - specificInterline, @@ -715,7 +703,7 @@ public IndexedLedger getClosestLedger (Point2D point) searchBox.getHeight() + (2 * specificInterline)); // Browse all staff ledgers - Set foundLedgers = new LinkedHashSet(); + Set foundLedgers = new LinkedHashSet<>(); for (Map.Entry> entry : ledgerMap.entrySet()) { for (LedgerInter ledger : entry.getValue()) { @@ -766,6 +754,56 @@ public LineInfo getClosestLine (Point2D point) return lines.get(idx); } + //-------------------// + // getCompetingClefs // + //-------------------// + /** + * Report the competing clef candidates active at provided abscissa. + * + * @param x provided abscissa + * @return the collection of competing clefs + */ + public List getCompetingClefs (int x) + { + // Look for clef on left side in staff (together with its competing clefs) + SIGraph sig = getSystem().getSig(); + List staffClefs = sig.inters(this, ClefInter.class); + Collections.sort(staffClefs, Inters.byAbscissa); + + Inter lastClef = null; + + for (Inter inter : staffClefs) { + int xClef = inter.getBounds().x; + + if (xClef < x) { + lastClef = inter; + } + } + + if (lastClef == null) { + return Collections.emptyList(); + } + + // Pick up this clef together with all competing clefs + Set excs = sig.getExclusions(lastClef); + List clefs = new ArrayList<>(); + clefs.add((ClefInter) lastClef); + + for (Relation rel : excs) { + Inter inter = Graphs.getOppositeVertex(sig, rel, lastClef); + + if (inter instanceof ClefInter) { + ClefInter clef = (ClefInter) inter; + + if ((clef.getStaff() == this) && !clefs.contains(clef)) { + clefs.add(clef); + } + } + } + + return clefs; + } + //----------------// // getEndingSlope // //----------------// @@ -779,7 +817,7 @@ public LineInfo getClosestLine (Point2D point) */ public double getEndingSlope (HorizontalSide side) { - List slopes = new ArrayList(lines.size()); + List slopes = new ArrayList<>(lines.size()); for (LineInfo l : lines) { StaffFilament line = (StaffFilament) l; @@ -797,51 +835,6 @@ public double getEndingSlope (HorizontalSide side) return sum / (slopes.size() - 2); } - //--------------------// - // getLedgerLineIndex // - //--------------------// - /** - * Compute staff-based line index, based on provided pitch position - * - * @param pitchPosition the provided pitch position - * @return the computed line index - */ - public static int getLedgerLineIndex (double pitchPosition) - { - if (pitchPosition > 0) { - return (int) Math.rint(pitchPosition / 2) - 2; - } else { - return (int) Math.rint(pitchPosition / 2) + 2; - } - } - - //------------------------// - // getLedgerPitchPosition // - //------------------------// - /** - * Report the pitch position of a ledger WRT the related staff. - *

                                                    - * TODO: This implementation assumes a 5-line staff. - * But can we have ledgers on a staff with more (of less) than 5 lines? - * - * @param lineIndex the ledger line index - * @return the ledger pitch position - */ - public static int getLedgerPitchPosition (int lineIndex) - { - // // Safer, for the time being... - // if (getStaff() - // .getLines() - // .size() != 5) { - // throw new RuntimeException("Only 5-line staves are supported"); - // } - if (lineIndex > 0) { - return 4 + (2 * lineIndex); - } else { - return -4 + (2 * lineIndex); - } - } - //--------------// // getFirstLine // //--------------// @@ -879,6 +872,19 @@ public StaffHeader getHeader () return header; } + //-----------// + // setHeader // + //-----------// + /** + * Assign staff header information. + * + * @param header the StaffHeader information + */ + public void setHeader (StaffHeader header) + { + this.header = header; + } + //----------------// // getHeaderStart // //----------------// @@ -905,6 +911,19 @@ public int getHeaderStop () return header.stop; } + //---------------// + // setHeaderStop // + //---------------// + /** + * Refine the abscissa of StaffHeader break. + * + * @param headerStop the refined StaffHeader end value + */ + public void setHeaderStop (int headerStop) + { + header.stop = headerStop; + } + //-----------// // getHeight // //-----------// @@ -988,6 +1007,20 @@ public Integer getKeyStop () return null; } + //------------// + // setKeyStop // + //------------// + /** + * Assign the ending abscissa of staff header key. + * + * @param keyStop the keyStop to set + */ + public void setKeyStop (Integer keyStop) + { + header.keyRange.setStop(keyStop); + header.keyRange.valid = true; + } + //-------------// // getLastLine // //-------------// @@ -1024,6 +1057,11 @@ public Integer getLedgerIndex (LedgerInter ledger) //--------------// // getLedgerMap // //--------------// + /** + * Report the map of ledgers for this staff, indexed by relative vertical position. + * + * @return map of ledgers + */ public SortedMap> getLedgerMap () { return ledgerMap; @@ -1193,6 +1231,19 @@ public Part getPart () return part; } + //---------// + // setPart // + //---------// + /** + * Assign the containing part. + * + * @param part the part to set + */ + public void setPart (Part part) + { + this.part = part; + } + //----------------// // getSideBarline // //----------------// @@ -1240,7 +1291,7 @@ public List getStaffBarlines () for (Inter inter : sig.inters(StaffBarlineInter.class)) { if (inter.getStaff() == this) { if (found == null) { - found = new ArrayList(); + found = new ArrayList<>(); } found.add((StaffBarlineInter) inter); @@ -1265,6 +1316,19 @@ public SystemInfo getSystem () return system; } + //-----------// + // setSystem // + //-----------// + /** + * Assign the containing system. + * + * @param system the system to set + */ + public void setSystem (SystemInfo system) + { + this.system = system; + } + //--------------// // getTimeStart // //--------------// @@ -1305,9 +1369,28 @@ public Integer getTimeStop () return null; } + //-------------// + // setTimeStop // + //-------------// + /** + * Assign the ending abscissa of the staff header time signature. + * + * @param timeStop the timeStop to set + */ + public void setTimeStop (Integer timeStop) + { + header.timeRange.setStop(timeStop); + header.timeRange.valid = true; + } + //---------// // isDummy // //---------// + /** + * Report whether this staff is a dummy staff (created temporarily for export). + * + * @return true for dummy staff + */ public boolean isDummy () { return dummy; @@ -1398,8 +1481,8 @@ public int removeAttachments (String prefix) public boolean removeBarline (BarlineInter barline) { // Purge sideBars if needed - for (Iterator> it = sideBars.entrySet().iterator(); - it.hasNext();) { + for (Iterator> it = sideBars.entrySet().iterator(); it + .hasNext();) { Entry entry = it.next(); if (entry.getValue() == barline) { @@ -1524,6 +1607,11 @@ public void renderAttachments (Graphics2D g) //-----------// // replicate // //-----------// + /** + * Build a "replicate" of this staff, to be used as a dummy staff. + * + * @return the dummy replicate + */ public Staff replicate () { Staff replicate = new Staff(0, left, right, specificInterline, null); @@ -1550,93 +1638,15 @@ public void setAbscissa (HorizontalSide side, } } - //---------// - // setArea // - //---------// - public void setArea (Area area) - { - this.area = area; - - ///addAttachment("staff-area-" + id, area); - } - - //-------------// - // setBarlines // - //-------------// - /** - * @param barlines the barlines to set - */ - public void setBarlines (List barlines) - { - this.barlines = barlines; - retrieveSideBars(); - } - - //-------------// - // setClefStop // - //-------------// - /** - * @param clefStop the clefStop to set - */ - public void setClefStop (int clefStop) - { - header.clefRange.setStop(clefStop); - header.clefRange.valid = true; - } - //----------// // setDummy // //----------// - public void setDummy () - { - dummy = true; - } - - //-----------// - // setHeader // - //-----------// - /** - * @param header the StaffHeader information - */ - public void setHeader (StaffHeader header) - { - this.header = header; - } - - //---------------// - // setHeaderStop // - //---------------// - /** - * Refine the abscissa of StaffHeader break. - * - * @param headerStop the refined StaffHeader end value - */ - public void setHeaderStop (int headerStop) - { - header.stop = headerStop; - } - - //------------// - // setKeyStop // - //------------// /** - * @param keyStop the keyStop to set + * Flag this staff as a dummy one (meant for MusicXML export of missing part). */ - public void setKeyStop (Integer keyStop) - { - header.keyRange.setStop(keyStop); - header.keyRange.valid = true; - } - - //---------// - // setPart // - //---------// - /** - * @param part the part to set - */ - public void setPart (Part part) + public void setDummy () { - this.part = part; + dummy = true; } //----------// @@ -1664,37 +1674,6 @@ public void setSmall () isSmall = true; } - //-----------// - // setSystem // - //-----------// - /** - * @param system the system to set - */ - public void setSystem (SystemInfo system) - { - this.system = system; - } - - //-------------// - // setTimeStop // - //-------------// - /** - * @param timeStop the timeStop to set - */ - public void setTimeStop (Integer timeStop) - { - header.timeRange.setStop(timeStop); - header.timeRange.valid = true; - } - - //--------------------// - // showDefiningPoints // - //--------------------// - public static Boolean showDefiningPoints () - { - return constants.showDefiningPoints.isSet(); - } - //---------------// // simplifyLines // //---------------// @@ -1713,7 +1692,7 @@ public List simplifyLines (Sheet sheet) } final GlyphIndex glyphIndex = sheet.getGlyphIndex(); - List copies = new ArrayList(lines); + List copies = new ArrayList<>(lines); lines.clear(); for (LineInfo line : copies) { @@ -1798,7 +1777,7 @@ private void beforeMarshal (Marshaller m) { if (!ledgerMap.isEmpty()) { // Populate ledgersValue from ledgerMap - ledgersValue = new ArrayList(); + ledgersValue = new ArrayList<>(); for (Entry> entry : ledgerMap.entrySet()) { ledgersValue.add(new LedgersEntry(entry.getKey(), entry.getValue())); @@ -1831,14 +1810,86 @@ private void retrieveSideBars () logger.debug("Staff#{} sideBars:{}", id, sideBars); } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------------------// + // getDefiningPointSize // + //----------------------// + /** + * Report the diameter to draw each staff defining point. + * + * @return circle diameter to draw a point + */ + public static Scale.Fraction getDefiningPointSize () + { + return constants.definingPointSize; + } + + //--------------------// + // getLedgerLineIndex // + //--------------------// + /** + * Compute staff-based line index, based on provided pitch position + * + * @param pitchPosition the provided pitch position + * @return the computed line index + */ + public static int getLedgerLineIndex (double pitchPosition) + { + if (pitchPosition > 0) { + return (int) Math.rint(pitchPosition / 2) - 2; + } else { + return (int) Math.rint(pitchPosition / 2) + 2; + } + } + + //------------------------// + // getLedgerPitchPosition // + //------------------------// + /** + * Report the pitch position of a ledger WRT the related staff. + *

                                                    + * TODO: This implementation assumes a 5-line staff. + * But can we have ledgers on a staff with more (of less) than 5 lines? + * + * @param lineIndex the ledger line index + * @return the ledger pitch position + */ + public static int getLedgerPitchPosition (int lineIndex) + { + // // Safer, for the time being... + // if (getStaff() + // .getLines() + // .size() != 5) { + // throw new RuntimeException("Only 5-line staves are supported"); + // } + if (lineIndex > 0) { + return 4 + (2 * lineIndex); + } else { + return -4 + (2 * lineIndex); + } + } + + //--------------------// + // showDefiningPoints // + //--------------------// + /** + * Tell whether the defining points should be drawn. + * + * @return true if so + */ + public static Boolean showDefiningPoints () + { + return constants.showDefiningPoints.isSet(); + } + //---------// // Adapter // //---------// + /** + * TODO: Still useful? + */ public static class Adapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public Integer marshal (Staff staff) @@ -1864,7 +1915,6 @@ public Staff unmarshal (Integer id) */ public static class IndexedLedger { - //~ Instance fields ------------------------------------------------------------------------ /** The ledger. */ public final LedgerInter ledger; @@ -1872,7 +1922,12 @@ public static class IndexedLedger /** Staff-based line index. (-1, -2, ... above, +1, +2, ... below) */ public final int index; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create an IndexedLedger object + * + * @param ledger the ledger + * @param index relative ledger position in staff + */ public IndexedLedger (LedgerInter ledger, int index) { @@ -1891,12 +1946,10 @@ public IndexedLedger (LedgerInter ledger, public static class StaffHolder extends Staff { - //~ Static fields/initializers ------------------------------------------------------------- /** Predefined place holders. */ - private static ConcurrentHashMap holders = new ConcurrentHashMap(); + private static ConcurrentHashMap holders = new ConcurrentHashMap<>(); - //~ Constructors --------------------------------------------------------------------------- /** * Create a place holder * @@ -1907,7 +1960,6 @@ public StaffHolder (int id) super(id); } - //~ Methods -------------------------------------------------------------------------------- /** * Check provided inter, to replace staff holder with proper staff instance. * @@ -1953,10 +2005,9 @@ public static StaffHolder getStaffHolder (int id) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean showDefiningPoints = new Constant.Boolean( false, @@ -1976,7 +2027,6 @@ private static final class Constants */ private static class LedgersEntry { - //~ Instance fields ------------------------------------------------------------------------ @XmlAttribute private final int index; @@ -1986,19 +2036,33 @@ private static class LedgersEntry @XmlValue private final List ledgers; - //~ Constructors --------------------------------------------------------------------------- // Needed for JAXB - public LedgersEntry () + LedgersEntry () { this.index = 0; this.ledgers = null; } - public LedgersEntry (int index, - List ledgers) + LedgersEntry (int index, + List ledgers) { this.index = index; this.ledgers = ledgers; } + + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("LedgersEntry{"); + sb.append("index:").append(index); + + if (ledgers != null) { + sb.append(" ledgers:").append(ledgers.size()); + } + + sb.append('}'); + + return sb.toString(); + } } } diff --git a/src/main/org/audiveris/omr/sheet/StaffLine.java b/src/main/org/audiveris/omr/sheet/StaffLine.java index e703c6e77..c49b6ba0c 100644 --- a/src/main/org/audiveris/omr/sheet/StaffLine.java +++ b/src/main/org/audiveris/omr/sheet/StaffLine.java @@ -56,18 +56,15 @@ public class StaffLine implements LineInfo { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - StaffLine.class); + private static final Logger logger = LoggerFactory.getLogger(StaffLine.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // - /** Absolute defining points (including start & stop points). */ + /** Absolute defining points (including start and stop points). */ @XmlElement(name = "point") + @XmlJavaTypeAdapter(Jaxb.Point2DAdapter.class) protected final List points; /** Mean line thickness. */ @@ -89,7 +86,6 @@ public class StaffLine /** Bounding box. */ protected Rectangle bounds; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StaffLine} object. * @@ -112,7 +108,6 @@ private StaffLine () this.thickness = 0; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getBounds // //-----------// @@ -161,6 +156,19 @@ public Glyph getGlyph () return glyph; } + //----------// + // setGlyph // + //----------// + /** + * Assign the underlying glyph for the whole staff line + * + * @param glyph the staff line glyph + */ + public void setGlyph (Glyph glyph) + { + this.glyph = glyph; + } + //-----------// // getSpline // //-----------// @@ -195,11 +203,20 @@ public void renderLine (Graphics2D g, } //----------// - // setGlyph // + // toString // //----------// - public void setGlyph (Glyph glyph) + @Override + public String toString () { - this.glyph = glyph; + final StringBuilder sb = new StringBuilder("StaffLine{"); + + if (points != null) { + sb.append("points:").append(points.size()); + } + + sb.append('}'); + + return sb.toString(); } //-----// @@ -230,7 +247,6 @@ public double yAt (double x) } } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Adapter // //---------// @@ -240,7 +256,6 @@ public double yAt (double x) public static class Adapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public StaffLine marshal (LineInfo lineInfo) diff --git a/src/main/org/audiveris/omr/sheet/StaffManager.java b/src/main/org/audiveris/omr/sheet/StaffManager.java index 892ad7679..ed39cca5b 100644 --- a/src/main/org/audiveris/omr/sheet/StaffManager.java +++ b/src/main/org/audiveris/omr/sheet/StaffManager.java @@ -60,6 +60,7 @@ *

                                                    * It must be able to correctly handle the sequence of staves even in complex * configurations like the following one (referred to as "layout order"): + * *

                                                      * +-------+
                                                      * |   1   |
                                                    @@ -78,22 +79,18 @@
                                                     public class StaffManager
                                                             implements ItemRenderer
                                                     {
                                                    -    //~ Static fields/initializers -----------------------------------------------------------------
                                                     
                                                         private static final Constants constants = new Constants();
                                                     
                                                         private static final Logger logger = LoggerFactory.getLogger(StaffManager.class);
                                                     
                                                    -    //~ Instance fields ----------------------------------------------------------------------------
                                                    -    //
                                                         /** The related sheet. */
                                                         @Navigable(false)
                                                         private final Sheet sheet;
                                                     
                                                         /** The sequence of staves, ordered by layout position. */
                                                    -    private final List staves = new ArrayList();
                                                    +    private final List staves = new ArrayList<>();
                                                     
                                                    -    //~ Constructors -------------------------------------------------------------------------------
                                                         /**
                                                          * Creates a new StaffManager object.
                                                          *
                                                    @@ -108,124 +105,6 @@ public StaffManager (Sheet sheet)
                                                             }
                                                         }
                                                     
                                                    -    //~ Methods ------------------------------------------------------------------------------------
                                                    -    //-----------------//
                                                    -    // getClosestStaff //
                                                    -    //-----------------//
                                                    -    /**
                                                    -     * Report the closest staff from the provided point, among the specified staves.
                                                    -     *
                                                    -     * @param point     the provided point
                                                    -     * @param theStaves the list of staves to browse
                                                    -     * @return the nearest staff, or null if none found
                                                    -     */
                                                    -    public static Staff getClosestStaff (Point2D point,
                                                    -                                         List theStaves)
                                                    -    {
                                                    -        // All staves whose area contains the provided point
                                                    -        final List found = getStavesOf(point, theStaves);
                                                    -
                                                    -        switch (found.size()) {
                                                    -        case 0:
                                                    -            return null;
                                                    -
                                                    -        case 1:
                                                    -            return found.get(0);
                                                    -
                                                    -        default:
                                                    -
                                                    -            Staff bestStaff = null;
                                                    -            double bestDist = Double.MAX_VALUE;
                                                    -
                                                    -            for (Staff staff : found) {
                                                    -                double dist = staff.distanceTo(point);
                                                    -
                                                    -                if (dist < bestDist) {
                                                    -                    bestDist = dist;
                                                    -                    bestStaff = staff;
                                                    -                }
                                                    -            }
                                                    -
                                                    -            return bestStaff;
                                                    -        }
                                                    -    }
                                                    -
                                                    -    //-------------//
                                                    -    // getCoreArea //
                                                    -    //-------------//
                                                    -    /**
                                                    -     * Compute a staff core area limited to staff lines (staff height and staff width)
                                                    -     * with additional margins in horizontal and vertical directions.
                                                    -     *
                                                    -     * @param staff   the staff to process
                                                    -     * @param hMargin margin added on left and right of staff (in pixels)
                                                    -     * @param vMargin margin added on top and bottom of staff (in pixels)
                                                    -     * @return the staff core area
                                                    -     */
                                                    -    public static Area getCoreArea (Staff staff,
                                                    -                                    int hMargin,
                                                    -                                    int vMargin)
                                                    -    {
                                                    -        GeoPath path = new GeoPath();
                                                    -
                                                    -        {
                                                    -            // Top limit
                                                    -            NaturalSpline spline = staff.getFirstLine().getSpline();
                                                    -            Point2D tl = spline.getFirstPoint();
                                                    -            path.moveTo(tl.getX() - hMargin, tl.getY() - vMargin);
                                                    -
                                                    -            AffineTransform at = AffineTransform.getTranslateInstance(0, -vMargin);
                                                    -            path.append(spline.getPathIterator(at), true);
                                                    -
                                                    -            Point2D tr = spline.getLastPoint();
                                                    -            path.lineTo(tr.getX() + hMargin, tr.getY() - vMargin);
                                                    -        }
                                                    -
                                                    -        {
                                                    -            // Bottom limit
                                                    -            NaturalSpline spline = staff.getLastLine().getSpline();
                                                    -            Point2D br = spline.getLastPoint();
                                                    -            path.lineTo(br.getX() + hMargin, br.getY() + vMargin);
                                                    -
                                                    -            AffineTransform at = AffineTransform.getTranslateInstance(0, +vMargin);
                                                    -            path.append(new ReversePathIterator(spline.getPathIterator(at)), true);
                                                    -
                                                    -            Point2D bl = spline.getFirstPoint();
                                                    -            path.lineTo(bl.getX() - hMargin, bl.getY() + vMargin);
                                                    -        }
                                                    -
                                                    -        path.closePath();
                                                    -
                                                    -        return new Area(path);
                                                    -    }
                                                    -
                                                    -    //-------------//
                                                    -    // getStavesOf //
                                                    -    //-------------//
                                                    -    /**
                                                    -     * Report the staves whose area contains the provided point.
                                                    -     *
                                                    -     * @param point     the provided pixel point
                                                    -     * @param theStaves the list of staves to check
                                                    -     * @return the containing staves
                                                    -     */
                                                    -    public static List getStavesOf (Point2D point,
                                                    -                                           List theStaves)
                                                    -    {
                                                    -        List found = new ArrayList();
                                                    -
                                                    -        for (Staff staff : theStaves) {
                                                    -            Area area = staff.getArea();
                                                    -
                                                    -            if ((area != null) && area.contains(point)) {
                                                    -                found.add(staff);
                                                    -            }
                                                    -        }
                                                    -
                                                    -        return found;
                                                    -    }
                                                    -
                                                    -    //
                                                         //----------//
                                                         // addStaff //
                                                         //----------//
                                                    @@ -256,14 +135,13 @@ public void computeStaffArea (Staff staff)
                                                             final int sheetHeight = sheet.getHeight();
                                                     
                                                             final List aboves = vertNeighbors(staff, TOP);
                                                    -        final PathIterator north = aboves.isEmpty()
                                                    -                ? new GeoPath(new Line2D.Double(0, 0, sheetWidth, 0)).getPathIterator(
                                                    -                        null) : getGlobalLine(aboves, BOTTOM);
                                                    +        final PathIterator north = aboves.isEmpty() ? new GeoPath(
                                                    +                new Line2D.Double(0, 0, sheetWidth, 0)).getPathIterator(null)
                                                    +                : getGlobalLine(aboves, BOTTOM);
                                                     
                                                             final List belows = vertNeighbors(staff, BOTTOM);
                                                    -        final PathIterator south = belows.isEmpty()
                                                    -                ? new GeoPath(
                                                    -                        new Line2D.Double(0, sheetHeight, sheetWidth, sheetHeight)).getPathIterator(null)
                                                    +        final PathIterator south = belows.isEmpty() ? new GeoPath(
                                                    +                new Line2D.Double(0, sheetHeight, sheetWidth, sheetHeight)).getPathIterator(null)
                                                                     : getGlobalLine(belows, TOP);
                                                     
                                                             // Define sheet-wide area
                                                    @@ -365,6 +243,12 @@ public PathIterator getGlobalLine (List staffList,
                                                         //------------//
                                                         // getIndexOf //
                                                         //------------//
                                                    +    /**
                                                    +     * Report the index in sheet of the provided staff.
                                                    +     *
                                                    +     * @param staff the provided staff
                                                    +     * @return index in sheet
                                                    +     */
                                                         public int getIndexOf (Staff staff)
                                                         {
                                                             return staves.indexOf(staff);
                                                    @@ -389,6 +273,12 @@ public List getRange (Staff first,
                                                         //----------//
                                                         // getStaff //
                                                         //----------//
                                                    +    /**
                                                    +     * Report the staff at provided index
                                                    +     *
                                                    +     * @param index the provided index
                                                    +     * @return the corresponding staff
                                                    +     */
                                                         public Staff getStaff (int index)
                                                         {
                                                             return staves.get(index);
                                                    @@ -468,6 +358,7 @@ public Staff getStrictStaffAt (Point2D point)
                                                          * horizontal side of the current one.
                                                          * 

                                                    * On the layout example: + * *

                                                          * +-------+
                                                          * |   1   |
                                                    @@ -479,10 +370,10 @@ public Staff getStrictStaffAt (Point2D point)
                                                          * |   7   |
                                                          * |   8   |
                                                          * +-------+
                                                    -     * - neighborOf(1, RIGHT) == null
                                                    -     * - neighborOf(3, RIGHT) == 5
                                                    -     * - neighborOf(6, LEFT) == 4
                                                    -     * - neighborOf(7, LEFT) == null
                                                    +     * - horiNeighbor(1, RIGHT) == null
                                                    +     * - horiNeighbor(3, RIGHT) == 5
                                                    +     * - horiNeighbor(6, LEFT) == 4
                                                    +     * - horiNeighbor(7, LEFT) == null
                                                          * 
                                                    * * @param current current staff @@ -567,6 +458,7 @@ public void reset () * side of the current staff. *

                                                    * On the layout example: + * *

                                                          * +-------+
                                                          * |   1   |
                                                    @@ -578,16 +470,16 @@ public void reset ()
                                                          * |   7   |
                                                          * |   8   |
                                                          * +-------+
                                                    -     * - neighborsOf(1, TOP)    == []
                                                    -     * - neighborsOf(1, BOTTOM) == [2]
                                                    -     * - neighborsOf(2, BOTTOM) == [3,5]
                                                    -     * - neighborsOf(5, TOP)    == [2]
                                                    -     * - neighborsOf(6, TOP)    == [3,5] (not just 5)
                                                    -     * - neighborsOf(3, BOTTOM) == [4,6] (not just 4)
                                                    -     * - neighborsOf(4, BOTTOM) == [7]
                                                    -     * - neighborsOf(7, TOP)    == [4,6]
                                                    -     * - neighborsOf(7, BOTTOM) == [8]
                                                    -     * - neighborsOf(8, BOTTOM) == []
                                                    +     * - vertNeighbors(1, TOP) == []
                                                    +     * - vertNeighbors(1, BOTTOM) == [2]
                                                    +     * - vertNeighbors(2, BOTTOM) == [3,5]
                                                    +     * - vertNeighbors(5, TOP) == [2]
                                                    +     * - vertNeighbors(6, TOP) == [3,5] (not just 5)
                                                    +     * - vertNeighbors(3, BOTTOM) == [4,6] (not just 4)
                                                    +     * - vertNeighbors(4, BOTTOM) == [7]
                                                    +     * - vertNeighbors(7, TOP) == [4,6]
                                                    +     * - vertNeighbors(7, BOTTOM) == [8]
                                                    +     * - vertNeighbors(8, BOTTOM) == []
                                                          * 
                                                    * * @param current current staff @@ -597,7 +489,7 @@ public void reset () public List vertNeighbors (Staff current, VerticalSide side) { - final List neighbors = new ArrayList(); + final List neighbors = new ArrayList<>(); final int idx = getIndexOf(current); final int dir = (side == TOP) ? (-1) : 1; final int iBreak = (side == TOP) ? (-1) : staves.size(); @@ -638,15 +530,126 @@ public List vertNeighbors (Staff current, return neighbors; } - //~ Inner Classes ------------------------------------------------------------------------------ - // + //-----------------// + // getClosestStaff // + //-----------------// + /** + * Report the closest staff from the provided point, among the specified staves. + * + * @param point the provided point + * @param theStaves the list of staves to browse + * @return the nearest staff, or null if none found + */ + public static Staff getClosestStaff (Point2D point, + List theStaves) + { + // All staves whose area contains the provided point + final List found = getStavesOf(point, theStaves); + + switch (found.size()) { + case 0: + return null; + + case 1: + return found.get(0); + + default: + + Staff bestStaff = null; + double bestDist = Double.MAX_VALUE; + + for (Staff staff : found) { + double dist = staff.distanceTo(point); + + if (dist < bestDist) { + bestDist = dist; + bestStaff = staff; + } + } + + return bestStaff; + } + } + + //-------------// + // getCoreArea // + //-------------// + /** + * Compute a staff core area limited to staff lines (staff height and staff width) + * with additional margins in horizontal and vertical directions. + * + * @param staff the staff to process + * @param hMargin margin added on left and right of staff (in pixels) + * @param vMargin margin added on top and bottom of staff (in pixels) + * @return the staff core area + */ + public static Area getCoreArea (Staff staff, + int hMargin, + int vMargin) + { + GeoPath path = new GeoPath(); + + { + // Top limit + NaturalSpline spline = staff.getFirstLine().getSpline(); + Point2D tl = spline.getFirstPoint(); + path.moveTo(tl.getX() - hMargin, tl.getY() - vMargin); + + AffineTransform at = AffineTransform.getTranslateInstance(0, -vMargin); + path.append(spline.getPathIterator(at), true); + + Point2D tr = spline.getLastPoint(); + path.lineTo(tr.getX() + hMargin, tr.getY() - vMargin); + } + + { + // Bottom limit + NaturalSpline spline = staff.getLastLine().getSpline(); + Point2D br = spline.getLastPoint(); + path.lineTo(br.getX() + hMargin, br.getY() + vMargin); + + AffineTransform at = AffineTransform.getTranslateInstance(0, +vMargin); + path.append(new ReversePathIterator(spline.getPathIterator(at)), true); + + Point2D bl = spline.getFirstPoint(); + path.lineTo(bl.getX() - hMargin, bl.getY() + vMargin); + } + + path.closePath(); + + return new Area(path); + } + + //-------------// + // getStavesOf // + //-------------// + /** + * Report the staves whose area contains the provided point. + * + * @param point the provided pixel point + * @param theStaves the list of staves to check + * @return the containing staves + */ + public static List getStavesOf (Point2D point, + List theStaves) + { + List found = new ArrayList<>(); + for (Staff staff : theStaves) { + Area area = staff.getArea(); + + if ((area != null) && area.contains(point)) { + found.add(staff); + } + } + return found; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean showStaffLines = new Constant.Boolean( true, diff --git a/src/main/org/audiveris/omr/sheet/SystemInfo.java b/src/main/org/audiveris/omr/sheet/SystemInfo.java index b717a19b5..275d0595f 100644 --- a/src/main/org/audiveris/omr/sheet/SystemInfo.java +++ b/src/main/org/audiveris/omr/sheet/SystemInfo.java @@ -21,7 +21,6 @@ // package org.audiveris.omr.sheet; -import org.audiveris.omr.glyph.BasicGlyph; import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.GlyphGroup; import org.audiveris.omr.glyph.GlyphIndex; @@ -89,10 +88,8 @@ public class SystemInfo implements Comparable { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - SystemInfo.class); + private static final Logger logger = LoggerFactory.getLogger(SystemInfo.class); /** To sort by system id. */ public static final Comparator byId = new Comparator() @@ -105,8 +102,6 @@ public int compare (SystemInfo o1, } }; - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -121,23 +116,24 @@ public int compare (SystemInfo o1, /** Horizontal sequence of measure stacks in this system. */ @XmlElement(name = "stack") - private final List stacks = new ArrayList(); + private final List stacks = new ArrayList<>(); /** Vertical sequence of real parts in this system (no dummy parts included). */ @XmlElement(name = "part") - private final List parts = new ArrayList(); + private final List parts = new ArrayList<>(); /** PartGroups in this system. */ @XmlElement(name = "part-group") - private final List partGroups = new ArrayList(); + private final List partGroups = new ArrayList<>(); - /** Collection of stand-alone glyphs in this system. + /** + * Collection of stand-alone glyphs in this system. * This should be limited to glyphs not referenced elsewhere, to avoid garbage collection. */ @XmlList @XmlIDREF @XmlElement(name = "free-glyphs") - private Set freeGlyphs; + private Set freeGlyphs; /** * Symbol Interpretation Graph for this system. @@ -154,16 +150,16 @@ public int compare (SystemInfo o1, private Sheet sheet; /** Real staves of this system (no dummy staves included). */ - private List staves = new ArrayList(); + private List staves = new ArrayList<>(); /** Assigned page, if any. */ private Page page; /** Horizontal sections. */ - private final List
                                                    hSections = new ArrayList
                                                    (); + private final List
                                                    hSections = new ArrayList<>(); /** Vertical sections. */ - private final List
                                                    vSections = new ArrayList
                                                    (); + private final List
                                                    vSections = new ArrayList<>(); /** Area that encloses all items related to this system. */ private Area area; @@ -189,7 +185,6 @@ public int compare (SystemInfo o1, /** Width of the system. */ private int width = -1; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a SystemInfo entity, to register the provided parameters. * @@ -218,7 +213,6 @@ private SystemInfo () this.id = 0; } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // addFreeGlyph // //--------------// @@ -230,10 +224,10 @@ private SystemInfo () public void addFreeGlyph (Glyph glyph) { if (freeGlyphs == null) { - freeGlyphs = new LinkedHashSet(); + freeGlyphs = new LinkedHashSet<>(); } - freeGlyphs.add((BasicGlyph) glyph); + freeGlyphs.add(glyph); } //---------// @@ -280,6 +274,9 @@ public void addStack (int index, //-------------// // afterReload // //-------------// + /** + * To be called right after unmarshalling. + */ public void afterReload () { try { @@ -311,7 +308,7 @@ public void afterReload () } if (upgraded) { - sheet.getStub().setModified(true); + sheet.getStub().setUpgraded(true); } } @@ -358,7 +355,24 @@ public void clearFreeGlyphs () @Override public int compareTo (SystemInfo that) { - return Integer.compare(id, that.id); + return Integer.compare(id, that.id); // This is a total ordering + } + + //--------// + // equals // + //--------// + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + + if (obj instanceof SystemInfo) { + return compareTo((SystemInfo) obj) == 0; + } + + return false; } //----------------// @@ -397,9 +411,28 @@ public Area getArea () return area; } + //---------// + // setArea // + //---------// + /** + * Assign the system area. + * + * @param area the underlying system area + */ + public void setArea (Area area) + { + this.area = area; + } + //------------// // getAreaEnd // //------------// + /** + * Report area side abscissa. + * + * @param side desired side + * @return abscissa value + */ public int getAreaEnd (HorizontalSide side) { if (side == LEFT) { @@ -552,7 +585,7 @@ public SystemInfo getFollowingInPage () */ public List getGroupedGlyphs (GlyphGroup group) { - List found = new ArrayList(); + List found = new ArrayList<>(); if (freeGlyphs != null) { for (Glyph glyph : freeGlyphs) { @@ -596,6 +629,11 @@ public int getId () //----------------// // getIndexInPage // //----------------// + /** + * Report 0-based index of this system within containing page. + * + * @return index in page + */ public int getIndexInPage () { return getPage().getSystems().indexOf(this); @@ -733,8 +771,8 @@ public NotePosition getNoteStaffAt (Point point) if (otherPos.getLedger() != null) { // Delta pitch from closest reference ledger double otherDp = Math.abs( - otherPos.getPitchPosition() - - Staff.getLedgerPitchPosition(otherPos.getLedger().index)); + otherPos.getPitchPosition() - Staff.getLedgerPitchPosition( + otherPos.getLedger().index)); if (otherDp < dp) { logger.debug(" otherPos: {}", pos); @@ -760,6 +798,19 @@ public Page getPage () return page; } + //---------// + // setPage // + //---------// + /** + * Assign the containing page. + * + * @param page the containing page + */ + public void setPage (Page page) + { + this.page = page; + } + //------------------// // getPartAtOrAbove // //------------------// @@ -941,9 +992,8 @@ public MeasureStack getStackAt (Point2D point) for (MeasureStack stack : stacks) { final Measure measure = stack.getMeasureAt(staff); - if ((measure != null) - && (x >= measure.getAbscissa(LEFT, staff)) - && (x <= measure.getAbscissa(RIGHT, staff))) { + if ((measure != null) && (x >= measure.getAbscissa(LEFT, staff)) && (x <= measure + .getAbscissa(RIGHT, staff))) { return stack; } } @@ -965,6 +1015,13 @@ public List getStacks () //-------------------// // getStaffAtOrAbove // //-------------------// + /** + * Report the staff (within this system) which either embraces or is above the + * provided point. + * + * @param point provided point + * @return staff here or above (within system), null otherwise + */ public Staff getStaffAtOrAbove (Point2D point) { final Staff closest = getClosestStaff(point); @@ -993,6 +1050,13 @@ public Staff getStaffAtOrAbove (Point2D point) //-------------------// // getStaffAtOrBelow // //-------------------// + /** + * Report the staff (within this system) which either embraces or is below the + * provided point. + * + * @param point provided point + * @return staff here or below (within system), null otherwise + */ public Staff getStaffAtOrBelow (Point2D point) { final Staff closest = getClosestStaff(point); @@ -1054,6 +1118,23 @@ public List getStaves () return staves; } + //-----------// + // setStaves // + //-----------// + /** + * @param staves the range of staves + */ + public final void setStaves (List staves) + { + this.staves = staves; + + for (Staff staff : staves) { + staff.setSystem(this); + } + + updateCoordinates(); + } + //-----------------// // getStavesAround // //-----------------// @@ -1145,17 +1226,42 @@ public int getWidth () return width; } + //----------// + // hashCode // + //----------// + @Override + public int hashCode () + { + int hash = 7; + hash = (67 * hash) + this.id; + + return hash; + } + //------------// // isIndented // //------------// /** - * @return the indented + * Report whether this system is indented, WRT other systems in sheet. + * + * @return true if indented */ public boolean isIndented () { return indented; } + //-------------// + // setIndented // + //-------------// + /** + * @param indented the indented to set + */ + public void setIndented (boolean indented) + { + this.indented = indented; + } + //--------------// // isMultiStaff // //--------------// @@ -1219,7 +1325,7 @@ public void registerGlyphs (List parts, public void removeFreeGlyph (Glyph glyph) { if (freeGlyphs != null) { - freeGlyphs.remove((BasicGlyph) glyph); + freeGlyphs.remove(glyph); if (freeGlyphs.isEmpty()) { freeGlyphs = null; @@ -1238,7 +1344,7 @@ public void removeFreeGlyph (Glyph glyph) public void removeGroupedGlyphs (GlyphGroup group) { if (freeGlyphs != null) { - for (Iterator it = freeGlyphs.iterator(); it.hasNext();) { + for (Iterator it = freeGlyphs.iterator(); it.hasNext();) { final Glyph glyph = it.next(); final EnumSet glyphGroups = glyph.getGroups(); @@ -1268,17 +1374,15 @@ public void removeStack (MeasureStack stack) stacks.remove(stack); } - //---------// - // setArea // - //---------// - public void setArea (Area area) - { - this.area = area; - } - //------------// // setAreaEnd // //------------// + /** + * Set the abscissa value of the area side + * + * @param side desired side + * @param x side abscissa value + */ public void setAreaEnd (HorizontalSide side, int x) { @@ -1289,37 +1393,12 @@ public void setAreaEnd (HorizontalSide side, } } - //-------------// - // setIndented // - //-------------// - /** - * @param indented the indented to set - */ - public void setIndented (boolean indented) - { - this.indented = indented ? Boolean.TRUE : null; - } - - //-----------// - // setStaves // - //-----------// - /** - * @param staves the range of staves - */ - public final void setStaves (List staves) - { - this.staves = staves; - - for (Staff staff : staves) { - staff.setSystem(this); - } - - updateCoordinates(); - } - //-------------------// // updateCoordinates // //-------------------// + /** + * + */ public final void updateCoordinates () { try { @@ -1350,42 +1429,6 @@ public final void updateCoordinates () } } - //---------// - // setPage // - //---------// - public void setPage (Page page) - { - this.page = page; - } - - //----------// - // toString // - //----------// - /** - * Convenient method, to build a string with just the IDs of the system collection. - * - * @param systems the collection of systems - * @return the string built - */ - public static String toString (Collection systems) - { - if (systems == null) { - return ""; - } - - StringBuilder sb = new StringBuilder(); - sb.append(" systems["); - - for (SystemInfo system : systems) { - sb.append("#").append(system.getId()); - } - - sb.append("]"); - - return sb.toString(); - } - - // // //-------------// // // swapVoiceId // // //-------------// @@ -1467,10 +1510,37 @@ public boolean yOverlaps (SystemInfo that) //----------------// // initTransients // //----------------// - void initTransients (BasicSheet sheet, + void initTransients (Sheet sheet, Page page) { this.sheet = sheet; this.page = page; } + + //----------// + // toString // + //----------// + /** + * Convenient method, to build a string with just the IDs of the system collection. + * + * @param systems the collection of systems + * @return the string built + */ + public static String toString (Collection systems) + { + if (systems == null) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + sb.append(" systems["); + + for (SystemInfo system : systems) { + sb.append("#").append(system.getId()); + } + + sb.append("]"); + + return sb.toString(); + } } diff --git a/src/main/org/audiveris/omr/sheet/SystemManager.java b/src/main/org/audiveris/omr/sheet/SystemManager.java index 88ed8566e..66805028f 100644 --- a/src/main/org/audiveris/omr/sheet/SystemManager.java +++ b/src/main/org/audiveris/omr/sheet/SystemManager.java @@ -75,21 +75,18 @@ */ public class SystemManager { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(SystemManager.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ @Navigable(false) private Sheet sheet; /** Sheet retrieved systems. */ - private final List systems = new ArrayList(); + private final List systems = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SystemManager object. * @@ -108,7 +105,6 @@ private SystemManager () this.sheet = null; } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // computeSystemArea // //-------------------// @@ -133,22 +129,20 @@ public void computeSystemArea (SystemInfo system) // Vertical abscissae on system left & right final SystemInfo leftNeighbor = horizontalNeighbor(system, LEFT); - final int left = (leftNeighbor != null) - ? ((leftNeighbor.getRight() + system.getLeft()) / 2) : 0; + final int left = (leftNeighbor != null) ? ((leftNeighbor.getRight() + system.getLeft()) / 2) + : 0; system.setAreaEnd(LEFT, left); final SystemInfo rightNeighbor = horizontalNeighbor(system, RIGHT); - final int right = (rightNeighbor != null) - ? ((system.getRight() + rightNeighbor.getLeft()) / 2) : sheetWidth; + final int right = (rightNeighbor != null) ? ((system.getRight() + rightNeighbor.getLeft()) + / 2) : sheetWidth; system.setAreaEnd(RIGHT, right); - PathIterator north = aboves.isEmpty() - ? new GeoPath(new Line2D.Double(left, 0, right, 0)).getPathIterator( - null) : getGlobalLine(aboves, BOTTOM); + PathIterator north = aboves.isEmpty() ? new GeoPath(new Line2D.Double(left, 0, right, 0)) + .getPathIterator(null) : getGlobalLine(aboves, BOTTOM); - PathIterator south = belows.isEmpty() - ? new GeoPath( - new Line2D.Double(left, sheetHeight, right, sheetHeight)).getPathIterator(null) + PathIterator south = belows.isEmpty() ? new GeoPath( + new Line2D.Double(left, sheetHeight, right, sheetHeight)).getPathIterator(null) : getGlobalLine(belows, TOP); // Define sheet-wide area @@ -183,7 +177,7 @@ public List containingSystems (Rectangle2D rect, if (found != null) { found.clear(); } else { - found = new ArrayList(); + found = new ArrayList<>(); } for (SystemInfo system : systems) { @@ -211,7 +205,7 @@ public void dispatchHorizontalSections () } // Now dispatch the lag sections among relevant systems - List relevants = new ArrayList(); + List relevants = new ArrayList<>(); for (Section section : sheet.getLagManager().getLag(Lags.HLAG).getEntities()) { getSystemsOf(section.getCentroid(), relevants); @@ -237,7 +231,7 @@ public void dispatchVerticalSections () } // Now dispatch the lag sections among relevant systems - List relevants = new ArrayList(); + List relevants = new ArrayList<>(); for (Section section : sheet.getLagManager().getLag(Lags.VLAG).getEntities()) { getSystemsOf(section.getCentroid(), relevants); @@ -300,6 +294,22 @@ public List getSystems () return Collections.unmodifiableList(systems); } + //------------// + // setSystems // + //------------// + /** + * Assign the whole sequence of systems + * + * @param systems the (new) systems + */ + public void setSystems (Collection systems) + { + if (this.systems != systems) { + this.systems.clear(); + this.systems.addAll(systems); + } + } + //--------------// // getSystemsOf // //--------------// @@ -344,7 +354,7 @@ public List getSystemsOf (Point2D point, if (found != null) { found.clear(); } else { - found = new ArrayList(); + found = new ArrayList<>(); } for (SystemInfo system : systems) { @@ -374,7 +384,7 @@ public List getSystemsOf (Rectangle2D rect, if (found != null) { found.clear(); } else { - found = new ArrayList(); + found = new ArrayList<>(); } for (SystemInfo system : systems) { @@ -502,22 +512,6 @@ public void reset () systems.clear(); } - //------------// - // setSystems // - //------------// - /** - * Assign the whole sequence of systems - * - * @param systems the (new) systems - */ - public void setSystems (Collection systems) - { - if (this.systems != systems) { - this.systems.clear(); - this.systems.addAll(systems); - } - } - //-------------------// // verticalNeighbors // //-------------------// @@ -532,7 +526,7 @@ public void setSystems (Collection systems) public List verticalNeighbors (SystemInfo current, VerticalSide side) { - final List neighbors = new ArrayList(); + final List neighbors = new ArrayList<>(); final int idx = systems.indexOf(current); final int dir = (side == TOP) ? (-1) : 1; final int iBreak = (side == TOP) ? (-1) : systems.size(); @@ -573,14 +567,6 @@ public List verticalNeighbors (SystemInfo current, return neighbors; } - //----------------// - // initTransients // - //----------------// - void initTransients (Sheet sheet) - { - this.sheet = sheet; - } - //---------------// // allocatePages // //---------------// @@ -687,7 +673,7 @@ private PathIterator getGlobalLine (List list, return null; } - List staffList = new ArrayList(); + List staffList = new ArrayList<>(); for (SystemInfo system : list) { staffList.add((side == TOP) ? system.getFirstStaff() : system.getLastStaff()); @@ -746,14 +732,20 @@ private void reportResults () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------------// + // initTransients // + //----------------// + void initTransients (Sheet sheet) + { + this.sheet = sheet; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction minShift = new Scale.Fraction( 3.0, diff --git a/src/main/org/audiveris/omr/sheet/beam/BeamGroup.java b/src/main/org/audiveris/omr/sheet/beam/BeamGroup.java index 7b3d9c2b0..8af3bc31a 100644 --- a/src/main/org/audiveris/omr/sheet/beam/BeamGroup.java +++ b/src/main/org/audiveris/omr/sheet/beam/BeamGroup.java @@ -80,15 +80,11 @@ public class BeamGroup implements Vip { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - BeamGroup.class); + private static final Logger logger = LoggerFactory.getLogger(BeamGroup.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -105,7 +101,7 @@ public class BeamGroup @XmlList @XmlIDREF @XmlValue - private final LinkedHashSet beams = new LinkedHashSet(); + private final LinkedHashSet beams = new LinkedHashSet<>(); // Transient data //--------------- @@ -120,7 +116,6 @@ public class BeamGroup /** Same voice for all chords in this beam group. */ private Voice voice; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new instance of BeamGroup. * @@ -143,40 +138,6 @@ private BeamGroup () this.id = 0; } - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // includeBeam // - //-------------// - /** - * Manually include (or re-include) a beam into the measure BeamGroup structure. - * - * @param beam beam to include - * @param measure containing measure - */ - public static void includeBeam (AbstractBeamInter beam, - Measure measure) - { - final Set beamStems = beam.getStems(); - - // Look for a compatible group (via a common stem) - for (BeamGroup group : measure.getBeamGroups()) { - for (AbstractBeamInter b : group.getBeams()) { - Set s = b.getStems(); - s.retainAll(beamStems); - - if (!s.isEmpty()) { - assignGroup(group, beam); - - return; // Found a hosting group - } - } - } - - // Not found, create a new one - BeamGroup group = new BeamGroup(measure); - assignGroup(group, beam); - } - //---------// // addBeam // //---------// @@ -201,6 +162,11 @@ public void addBeam (AbstractBeamInter beam) //-------------// // afterReload // //-------------// + /** + * To be called right after unmarshalling. + * + * @param measure the containing measure + */ public void afterReload (Measure measure) { try { @@ -274,6 +240,31 @@ public Set getBeams () return beams; } + //-----------// + // getChords // + //-----------// + /** + * Report the x-ordered collection of chords that are grouped by this beam group. + * + * @return the (perhaps empty) collection of 'beamed' chords. + */ + public List getChords () + { + List chords = new ArrayList<>(); + + for (AbstractBeamInter beam : getBeams()) { + for (AbstractChordInter chord : beam.getChords()) { + if (!chords.contains(chord)) { + chords.add(chord); + } + } + } + + Collections.sort(chords, Inters.byAbscissa); + + return chords; + } + //-------------// // getDuration // //-------------// @@ -348,11 +339,54 @@ public AbstractChordInter getLastChord () //----------// // getVoice // //----------// + /** + * Report the assigned voice. + * + * @return beam group voice + */ public Voice getVoice () { return voice; } + //----------// + // setVoice // + //----------// + /** + * Assign a voice to this beam group, and to the related entities. + * + * @param voice the voice to assign + */ + public void setVoice (Voice voice) + { + // Already done? + if (this.voice == null) { + this.voice = voice; + + // Forward this information to the beamed chords + // Including the interleaved rests if any + AbstractChordInter prevChord = null; + + for (AbstractChordInter chord : getChords()) { + if (prevChord != null) { + // Here we must check for interleaved rest + AbstractNoteInter rest = measure.lookupRest(prevChord, chord); + + if (rest != null) { + rest.getChord().setVoice(voice); + } + } + + chord.setVoice(voice); + prevChord = chord; + } + } else if (voice == null) { + this.voice = null; + } else if (!this.voice.equals(voice)) { + logger.warn("Reassigning voice from " + this.voice + " to " + voice + " in " + this); + } + } + //--------------// // isMultiStaff // //--------------// @@ -375,59 +409,13 @@ public boolean isVip () return vip; } - //----------// - // populate // - //----------// - /** - * Populate all the BeamGroup instances for a given measure. - * - * @param measure the containing measure - * @param checkGroupSplit true for check on group split - */ - public static void populate (Measure measure, - boolean checkGroupSplit) + //--------// + // setVip // + //--------// + @Override + public void setVip (boolean vip) { - measure.clearBeamGroups(); - - // Retrieve beams in this measure - Set beams = new LinkedHashSet(); - - for (AbstractChordInter chord : measure.getHeadChords()) { - beams.addAll(chord.getBeams()); - } - - // Reset group info in each beam - for (AbstractBeamInter beam : beams) { - beam.setGroup(null); - } - - // Build beam groups for this measure stack - for (AbstractBeamInter beam : beams) { - if (beam.getGroup() == null) { - BeamGroup group = new BeamGroup(measure); - assignGroup(group, beam); - logger.debug("{}", group); - } - } - - if (checkGroupSplit) { - // In case something goes wrong, use an upper limit to loop - int loopNb = constants.maxSplitLoops.getValue(); - - while (checkBeamGroups(measure)) { - if (--loopNb < 0) { - logger.warn("Loop detected in BeamGroup split in {}", measure); - - break; - } - } - } - - // Detect groups that are linked to more than one staff - for (BeamGroup group : measure.getBeamGroups()) { - group.countStaves(); - logger.debug(" {}", group); - } + this.vip = vip; } //------------// @@ -455,6 +443,9 @@ public void removeBeam (AbstractBeamInter beam) //-------------// // resetTiming // //-------------// + /** + * + */ public void resetTiming () { voice = null; @@ -471,53 +462,6 @@ public void setMeasure (Measure measure) this.measure = measure; } - //--------// - // setVip // - //--------// - @Override - public void setVip (boolean vip) - { - this.vip = vip; - } - - //----------// - // setVoice // - //----------// - /** - * Assign a voice to this beam group, and to the related entities. - * - * @param voice the voice to assign - */ - public void setVoice (Voice voice) - { - // Already done? - if (this.voice == null) { - this.voice = voice; - - // Forward this information to the beamed chords - // Including the interleaved rests if any - AbstractChordInter prevChord = null; - - for (AbstractChordInter chord : getChords()) { - if (prevChord != null) { - // Here we must check for interleaved rest - AbstractNoteInter rest = measure.lookupRest(prevChord, chord); - - if (rest != null) { - rest.getChord().setVoice(voice); - } - } - - chord.setVoice(voice); - prevChord = chord; - } - } else if (voice == null) { - this.voice = null; - } else if (!this.voice.equals(voice)) { - logger.warn("Reassigning voice from " + this.voice + " to " + voice + " in " + this); - } - } - //----------// // toString // //----------// @@ -538,72 +482,6 @@ public String toString () return sb.toString(); } - //----------------// - // determineGroup // - //----------------// - /** - * Recursively determine BeamGroup for the provided beam, as well as all other beams - * connected within the same group. - * - * @param beam the beam seed - * @param measure the containing measure - */ - private static void assignGroup (BeamGroup group, - AbstractBeamInter beam) - { - group.addBeam(beam); - beam.setGroup(group); - - for (AbstractChordInter chord : beam.getChords()) { - for (AbstractBeamInter b : chord.getBeams()) { - if (b.getGroup() == null) { - assignGroup(group, b); - } - } - } - } - - //-----------------// - // checkBeamGroups // - //-----------------// - /** - * Check all the BeamGroup instances of the given measure, to find the first - * split if any to perform. - * - * @param measure the given measure - * @return the first split parameters, or null if everything is OK - */ - private static boolean checkBeamGroups (Measure measure) - { - for (BeamGroup group : measure.getBeamGroups()) { - AbstractChordInter alienChord = group.checkForSplit(); - - if (alienChord != null) { - group.split(alienChord); - - return true; - } - } - - return false; - } - - /** - * Find proper unique ID for a new group. - * - * @return proper ID - */ - private static int getNextGroupId (Measure measure) - { - int max = 0; - - for (BeamGroup group : measure.getBeamGroups()) { - max = Math.max(max, group.getId()); - } - - return ++max; - } - //---------------// // checkForSplit // //---------------// @@ -624,19 +502,20 @@ private AbstractChordInter checkForSplit () final Point tail = chord.getTailLocation(); // Get the collection of questionable beams WRT chord - List questionableBeams = new ArrayList(); + List questionableBeams = new ArrayList<>(); for (AbstractBeamInter beam : beams) { // Skip beam hooks // Skip beams attached to this chord // Skip beams with no abscissa overlap WRT this chord - if (!beam.isHook() - && !beam.getChords().contains(chord) - && (GeoUtil.xOverlap(beam.getBounds(), chordBox) > 0)) { + if (!beam.isHook() && !beam.getChords().contains(chord) && (GeoUtil.xOverlap( + beam.getBounds(), + chordBox) > 0)) { // Check vertical gap int lineY = (int) Math.rint(LineUtil.yAtX(beam.getMedian(), tail.x)); - int yOverlap = Math.min(lineY, chordBox.y + chordBox.height) - - Math.max(lineY, chordBox.y); + int yOverlap = Math.min(lineY, chordBox.y + chordBox.height) - Math.max( + lineY, + chordBox.y); if (yOverlap < 0) { questionableBeams.add(beam); @@ -650,26 +529,23 @@ private AbstractChordInter checkForSplit () // Sort these questionable beams vertically, at chord stem abscissa, // according to distance from chord tail. - Collections.sort( - questionableBeams, - new Comparator() - { - @Override - public int compare (AbstractBeamInter b1, - AbstractBeamInter b2) - { - final double y1 = LineUtil.yAtX(b1.getMedian(), tail.x); - double tailDy1 = Math.abs(y1 - tail.y); - final double y2 = LineUtil.yAtX(b2.getMedian(), tail.x); - double tailDy2 = Math.abs(y2 - tail.y); - - return Double.compare(tailDy1, tailDy2); - } - }); + Collections.sort(questionableBeams, new Comparator() + { + @Override + public int compare (AbstractBeamInter b1, + AbstractBeamInter b2) + { + final double y1 = LineUtil.yAtX(b1.getMedian(), tail.x); + double tailDy1 = Math.abs(y1 - tail.y); + final double y2 = LineUtil.yAtX(b2.getMedian(), tail.x); + double tailDy2 = Math.abs(y2 - tail.y); + + return Double.compare(tailDy1, tailDy2); + } + }); AbstractBeamInter nearestBeam = questionableBeams.get(0); - int lineY = (int) Math.rint( - LineUtil.yAtX(nearestBeam.getMedian(), tail.x)); + int lineY = (int) Math.rint(LineUtil.yAtX(nearestBeam.getMedian(), tail.x)); int tailDy = Math.abs(lineY - tail.y); double normedDy = scale.pixelsToFrac(tailDy); @@ -698,7 +574,7 @@ public int compare (AbstractBeamInter b1, */ private void countStaves () { - Set staves = new LinkedHashSet(); + Set staves = new LinkedHashSet<>(); for (AbstractBeamInter beam : beams) { SIGraph sig = beam.getSig(); @@ -718,56 +594,160 @@ private void countStaves () } } - //-----------// - // getChords // - //-----------// + //-------// + // split // + //-------// + private void split (AbstractChordInter alienChord) + { + new Splitter(alienChord).process(); + } + + //-------------// + // includeBeam // + //-------------// /** - * Report the x-ordered collection of chords that are grouped by this beam group. + * Manually include (or re-include) a beam into the measure BeamGroup structure. * - * @return the (perhaps empty) collection of 'beamed' chords. + * @param beam beam to include + * @param measure containing measure */ - private List getChords () + public static void includeBeam (AbstractBeamInter beam, + Measure measure) { - List chords = new ArrayList(); + final Set beamStems = beam.getStems(); - for (AbstractBeamInter beam : getBeams()) { - for (AbstractChordInter chord : beam.getChords()) { - if (!chords.contains(chord)) { - chords.add(chord); + // Look for a compatible group (via a common stem) + for (BeamGroup group : measure.getBeamGroups()) { + for (AbstractBeamInter b : group.getBeams()) { + Set s = b.getStems(); + s.retainAll(beamStems); + + if (!s.isEmpty()) { + assignGroup(group, beam); + + return; // Found a hosting group } } } - Collections.sort(chords, Inters.byAbscissa); + // Not found, create a new one + BeamGroup group = new BeamGroup(measure); + assignGroup(group, beam); + } - return chords; + //----------// + // populate // + //----------// + /** + * Populate all the BeamGroup instances for a given measure. + * + * @param measure the containing measure + * @param checkGroupSplit true for check on group split + */ + public static void populate (Measure measure, + boolean checkGroupSplit) + { + measure.clearBeamGroups(); + // Retrieve beams in this measure + Set beams = new LinkedHashSet<>(); + for (AbstractChordInter chord : measure.getHeadChords()) { + beams.addAll(chord.getBeams()); + } + // Reset group info in each beam + for (AbstractBeamInter beam : beams) { + beam.setGroup(null); + } + // Build beam groups for this measure stack + for (AbstractBeamInter beam : beams) { + if (beam.getGroup() == null) { + BeamGroup group = new BeamGroup(measure); + assignGroup(group, beam); + logger.debug("{}", group); + } + } + if (checkGroupSplit) { + // In case something goes wrong, use an upper limit to loop + int loopNb = constants.maxSplitLoops.getValue(); + + while (checkBeamGroups(measure)) { + if (--loopNb < 0) { + logger.warn("Loop detected in BeamGroup split in {}", measure); + + break; + } + } + } + // Detect groups that are linked to more than one staff + for (BeamGroup group : measure.getBeamGroups()) { + group.countStaves(); + logger.debug(" {}", group); + } } - //-------// - // split // - //-------// - private void split (AbstractChordInter alienChord) + //----------------// + // determineGroup // + //----------------// + /** + * Recursively determine BeamGroup for the provided beam, as well as all other beams + * connected within the same group. + * + * @param beam the beam seed + * @param measure the containing measure + */ + private static void assignGroup (BeamGroup group, + AbstractBeamInter beam) { - new Splitter(alienChord).process(); + group.addBeam(beam); + beam.setGroup(group); + + for (AbstractChordInter chord : beam.getChords()) { + for (AbstractBeamInter b : chord.getBeams()) { + if (b.getGroup() == null) { + assignGroup(group, b); + } + } + } } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet + //-----------------// + // checkBeamGroups // + //-----------------// + /** + * Check all the BeamGroup instances of the given measure, to find the first + * split if any to perform. + * + * @param measure the given measure + * @return the first split parameters, or null if everything is OK + */ + private static boolean checkBeamGroups (Measure measure) { - //~ Instance fields ------------------------------------------------------------------------ + for (BeamGroup group : measure.getBeamGroups()) { + AbstractChordInter alienChord = group.checkForSplit(); - private final Constant.Integer maxSplitLoops = new Constant.Integer( - "loops", - 10, - "Maximum number of loops allowed for splitting beam groups"); + if (alienChord != null) { + group.split(alienChord); - private final Scale.Fraction maxChordDy = new Scale.Fraction( - 0.5, - "Maximum vertical gap between a chord and a beam"); + return true; + } + } + + return false; + } + + /** + * Find proper unique ID for a new group. + * + * @return proper ID + */ + private static int getNextGroupId (Measure measure) + { + int max = 0; + + for (BeamGroup group : measure.getBeamGroups()) { + max = Math.max(max, group.getId()); + } + + return ++max; } //----------// @@ -779,13 +759,14 @@ private static final class Constants */ private class Splitter { - //~ Instance fields ------------------------------------------------------------------------ /** Chord detected as belonging to a (new) alien group. */ private final AbstractChordInter alienChord; - /** Beams that belong to new alien group. - * (Initially populated with all beams (except beam hooks) attached to alienChord) */ + /** + * Beams that belong to new alien group. + * (Initially populated with all beams (except beam hooks) attached to alienChord) + */ private Set alienBeams; /** The new alien group. */ @@ -794,18 +775,16 @@ private class Splitter /** The chord that embraces both (old) group and (new) alien group. */ private HeadChordInter pivotChord; - //~ Constructors --------------------------------------------------------------------------- /** * Create a splitter for this BeamGroup, triggered by alienChord * * @param alienChord a detected chord that should belong to a separate group */ - public Splitter (AbstractChordInter alienChord) + Splitter (AbstractChordInter alienChord) { this.alienChord = alienChord; } - //~ Methods -------------------------------------------------------------------------------- //---------// // process // //---------// @@ -814,7 +793,7 @@ public Splitter (AbstractChordInter alienChord) *

                                                    * Some beams of this group instance are moved to a new separate BeamGroup instance. * The two instances are articulated around a pivot chord, common to both groups. - * + *

                                                    */ public void process () { @@ -846,7 +825,7 @@ private BeamGroup createAlienGroup () // Check all former beams: any beam linked to the detected alienChord should be // moved to the alienGroup. // (This cannot apply to beam hooks, they will be processed later) - alienBeams = new LinkedHashSet(alienChord.getBeams()); + alienBeams = new LinkedHashSet<>(alienChord.getBeams()); // Now apply the move for (AbstractBeamInter beam : alienBeams) { @@ -885,7 +864,7 @@ private void dispatchAllBeams () { List pivotBeams = pivotChord.getBeams(); AllLoop: - for (AbstractBeamInter beam : new ArrayList(beams)) { + for (AbstractBeamInter beam : new ArrayList<>(beams)) { // If beam is attached to pivotChord, skip it if (pivotBeams.contains(beam)) { continue; @@ -1074,6 +1053,23 @@ private void splitChord () measure.getStack().addInter(shortChord); } } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Integer maxSplitLoops = new Constant.Integer( + "loops", + 10, + "Maximum number of loops allowed for splitting beam groups"); + + private final Scale.Fraction maxChordDy = new Scale.Fraction( + 0.5, + "Maximum vertical gap between a chord and a beam"); + } } // // //-----------------// diff --git a/src/main/org/audiveris/omr/sheet/beam/BeamItem.java b/src/main/org/audiveris/omr/sheet/beam/BeamItem.java index 55405fcba..983c10513 100644 --- a/src/main/org/audiveris/omr/sheet/beam/BeamItem.java +++ b/src/main/org/audiveris/omr/sheet/beam/BeamItem.java @@ -32,11 +32,15 @@ /** * Class {@code BeamItem} represents one beam candidate, using a very simple * parallelogram definition. + * + * @author Hervé Bitteur */ public class BeamItem implements Vip { - //~ Instance fields ---------------------------------------------------------------------------- + + /** VIP flag. */ + private boolean vip; /** The median line of the beam item. */ final Line2D median; @@ -44,10 +48,6 @@ public class BeamItem /** The constant height of the item. */ final double height; - /** VIP flag. */ - private boolean vip; - - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BeamItem object. * @@ -61,16 +61,24 @@ public BeamItem (Line2D median, this.height = height; } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // getBeltArea // //-------------// + /** + * Build the belt area around this beam item. + * + * @param coreArea black area + * @param dx horizontal margin + * @param topDy vertical margin above + * @param bottomDy vertical margin below + * @return belt area around the beam item + */ public Area getBeltArea (Area coreArea, int dx, int topDy, int bottomDy) { - final double shiftY = (bottomDy - topDy) / 2; + final double shiftY = (bottomDy - topDy) / 2.0; final double beltHeight = height + topDy + bottomDy; Point2D p1 = LineUtil.intersectionAtX(median, median.getX1() - dx); @@ -88,6 +96,11 @@ public Area getBeltArea (Area coreArea, //-------------// // getCoreArea // //-------------// + /** + * Report the theoretical black area for the beam item + * + * @return parallelogram for black pixels + */ public Area getCoreArea () { return AreaUtil.horizontalParallelogram(median.getP1(), median.getP2(), height); diff --git a/src/main/org/audiveris/omr/sheet/beam/BeamLine.java b/src/main/org/audiveris/omr/sheet/beam/BeamLine.java index 59870d460..875f71ed8 100644 --- a/src/main/org/audiveris/omr/sheet/beam/BeamLine.java +++ b/src/main/org/audiveris/omr/sheet/beam/BeamLine.java @@ -36,10 +36,12 @@ public class BeamLine implements Vip { - //~ Instance fields ---------------------------------------------------------------------------- /** Items that compose the line, from left to right. */ - private final List items = new ArrayList(); + private final List items = new ArrayList<>(); + + /** VIP flag. */ + private boolean vip; /** The median line from left item to right item. */ final Line2D median; @@ -47,10 +49,6 @@ public class BeamLine /** The constant height of the line. */ final double height; - /** VIP flag. */ - private boolean vip; - - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BeamLine object. * @@ -64,7 +62,6 @@ public BeamLine (Line2D median, this.height = height; } - //~ Methods ------------------------------------------------------------------------------------ /** * @return the items */ diff --git a/src/main/org/audiveris/omr/sheet/beam/BeamStructure.java b/src/main/org/audiveris/omr/sheet/beam/BeamStructure.java index 653c8fc1e..56c693f15 100644 --- a/src/main/org/audiveris/omr/sheet/beam/BeamStructure.java +++ b/src/main/org/audiveris/omr/sheet/beam/BeamStructure.java @@ -60,16 +60,16 @@ * Class {@code BeamStructure} handles one or several {@link BeamLine} instances, * all retrieved from a single glyph. * This is a private working companion of {@link BeamsBuilder}. + * + * @author Hervé Bitteur */ public class BeamStructure implements Vip { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - BeamStructure.class); + private static final Logger logger = LoggerFactory.getLogger(BeamStructure.class); /** Comparator on abscissae. */ public static final Comparator byAbscissa = new Comparator() @@ -82,7 +82,6 @@ public int compare (BeamStructure b1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying glyph. */ private final Glyph glyph; @@ -99,12 +98,11 @@ public int compare (BeamStructure b1, private final ItemParameters params; /** Sequence of lines retrieved for the same glyph, from top to bottom. */ - private final List lines = new ArrayList(); + private final List lines = new ArrayList<>(); /** VIP flag. */ private boolean vip; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BeamItems object. * @@ -122,7 +120,6 @@ public BeamStructure (Glyph glyph, center = glyph.getCentroid(); } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // adjustSides // //-------------// @@ -272,7 +269,7 @@ public Double computeLines () } // Check straightness - List allLines = new ArrayList(); + List allLines = new ArrayList<>(); allLines.addAll(topLines); allLines.addAll(bottomLines); @@ -536,10 +533,12 @@ private void completeBorderLines (double yDir, double xMid = (other.getX1() + other.getX2()) / 2; double yMid = (other.getY1() + other.getY2()) / 2; double height = yMid - LineUtil.yAtX(base, xMid); - Point2D p1 = (base.getX1() < other.getX1()) - ? new Point2D.Double(base.getX1(), base.getY1() + height) : other.getP1(); - Point2D p2 = (base.getX2() > other.getX2()) - ? new Point2D.Double(base.getX2(), base.getY2() + height) : other.getP2(); + Point2D p1 = (base.getX1() < other.getX1()) ? new Point2D.Double( + base.getX1(), + base.getY1() + height) : other.getP1(); + Point2D p2 = (base.getX2() > other.getX2()) ? new Point2D.Double( + base.getX2(), + base.getY2() + height) : other.getP2(); double x = (p1.getX() + p2.getX()) / 2; double y = LineUtil.yAtX(p1, p2, x); double offset = y - LineUtil.yAtX(center, globalSlope, x); @@ -631,7 +630,7 @@ private List getBorderLines (Glyph glyph, } // All sections are vertical, retrieve their border (top or bottom) - List sectionBorders = new ArrayList(); + List sectionBorders = new ArrayList<>(); for (Section section : getGlyphSections()) { final Rectangle sectionBox = section.getBounds(); @@ -664,12 +663,12 @@ private List getBorderLines (Glyph glyph, border.setOffset(dy); } - Collections.sort(sectionBorders); // By vertical offset WRT ref line + Collections.sort(sectionBorders, SectionBorder.byOrdinateOffset); // Retrieve groups of offset values, roughly separated by beam height // Each group will correspond to a separate beam line final double delta = params.typicalHeight * constants.maxBorderJitter.getValue(); - final List borderLines = new ArrayList(); + final List borderLines = new ArrayList<>(); Barycenter dys = new Barycenter(); BasicLine currentLine = null; @@ -719,7 +718,7 @@ private List

                                                    getGlyphSections () private SortedMap getLinesMap (double globalSlope, List topLines) { - SortedMap map = new TreeMap(); + SortedMap map = new TreeMap<>(); // Use refined value of global slope and flag each line WRT reference line for (BasicLine l : topLines) { @@ -855,14 +854,12 @@ private double slopeOfLongest (List lines) return bestLine.getSlope(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Double maxSectionSlopeGap = new Constant.Double( "tangent", @@ -881,9 +878,18 @@ private static final class Constants * Gathers info about a border line (top or bottom) of a section. */ private static class SectionBorder - implements Comparable { - //~ Static fields/initializers ------------------------------------------------------------- + + static Comparator byOrdinateOffset = new Comparator() + { + @Override + public int compare (SectionBorder o1, + SectionBorder o2) + { + // Sort by increasing ordinate offset WRT glyph reference line + return Double.compare(o1.dy, o2.dy); + } + }; static Comparator byReverseLength = new Comparator() { @@ -895,29 +901,19 @@ public int compare (SectionBorder o1, } }; - //~ Instance fields ------------------------------------------------------------------------ final Section section; // Underlying section final BasicLine line; // Border line (top or bottom) double dy; // Ordinate offset WRT glyph reference line - //~ Constructors --------------------------------------------------------------------------- - public SectionBorder (Section section, - BasicLine line) + SectionBorder (Section section, + BasicLine line) { this.section = section; this.line = line; } - //~ Methods -------------------------------------------------------------------------------- - @Override - public int compareTo (SectionBorder that) - { - // Sort by increasing ordinate - return Double.compare(this.dy, that.dy); - } - public void setOffset (double dy) { this.dy = dy; diff --git a/src/main/org/audiveris/omr/sheet/beam/BeamsBuilder.java b/src/main/org/audiveris/omr/sheet/beam/BeamsBuilder.java index 1e6d6c124..3e60c5a7e 100644 --- a/src/main/org/audiveris/omr/sheet/beam/BeamsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/beam/BeamsBuilder.java @@ -25,12 +25,11 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.glyph.BasicGlyph; import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.GlyphGroup; import org.audiveris.omr.glyph.Glyphs; import org.audiveris.omr.glyph.Grades; import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.glyph.GlyphGroup; import org.audiveris.omr.image.AreaMask; import org.audiveris.omr.lag.Lag; import org.audiveris.omr.math.AreaUtil; @@ -39,9 +38,7 @@ import org.audiveris.omr.math.LineUtil; import org.audiveris.omr.math.Population; import org.audiveris.omr.run.Orientation; - import static org.audiveris.omr.run.Orientation.VERTICAL; - import org.audiveris.omr.run.RunTable; import org.audiveris.omr.run.RunTableFactory; import org.audiveris.omr.sheet.Picture; @@ -64,15 +61,11 @@ import org.audiveris.omr.util.Corner; import org.audiveris.omr.util.Dumping; import org.audiveris.omr.util.HorizontalSide; - import static org.audiveris.omr.util.HorizontalSide.*; - import org.audiveris.omr.util.Navigable; import org.audiveris.omr.util.Predicate; import org.audiveris.omr.util.VerticalSide; - import static org.audiveris.omr.util.VerticalSide.*; - import org.audiveris.omr.util.WrappedInteger; import org.slf4j.Logger; @@ -106,13 +99,11 @@ */ public class BeamsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(BeamsBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated system. */ @Navigable(false) private final SystemInfo system; @@ -137,7 +128,7 @@ public class BeamsBuilder private final SIGraph sig; /** Spots already recognized as beams. */ - private final List assignedSpots = new ArrayList(); + private final List assignedSpots = new ArrayList<>(); /** Remaining beam spots candidates, sorted by abscissa. */ private List sortedBeamSpots; @@ -154,7 +145,6 @@ public class BeamsBuilder /** Lag of glyph sections. */ private final Lag spotLag; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BeamsBuilder object. * @@ -175,13 +165,11 @@ public BeamsBuilder (SystemInfo system, params = new Parameters(sheet.getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // buildBeams // //------------// /** * Find possible interpretations of beams among system spots. - * */ public void buildBeams () { @@ -234,32 +222,6 @@ public void buildCueBeams (List spots) } } - //----------------// - // isSideInSystem // - //----------------// - /** - * Check whether the (beam) side designated by provided point and height values - * lies fully in the current system - * - * @param pt side point on median - * @param height beam height - * @return true if side is fully within current system, false otherwise - */ - boolean isSideInSystem (Point2D pt, - double height) - { - Area area = system.getArea(); - - // Check top and bottom points of the beam side - for (int dir : new int[]{-1, +1}) { - if (!area.contains(pt.getX(), pt.getY() + (dir * (height / 2)))) { - return false; - } - } - - return true; - } - //-------------// // browseHooks // //-------------// @@ -577,11 +539,10 @@ private Impacts computeImpacts (BeamItem item, double beltRatio = (double) belt.value / beltCount; int width = (int) Math.rint(item.median.getX2() - item.median.getX1() + 1); - if ((width < minWidthLow) - || (item.height < itemParams.minHeightLow) - || (item.height > itemParams.maxHeightHigh) - || (coreRatio < params.minCoreBlackRatio) - || (beltRatio > params.maxBeltBlackRatio)) { + if ((width < minWidthLow) || (item.height < itemParams.minHeightLow) + || (item.height > itemParams.maxHeightHigh) + || (coreRatio < params.minCoreBlackRatio) + || (beltRatio > params.maxBeltBlackRatio)) { if (item.isVip() || logger.isDebugEnabled()) { logger.info( "Rejected {} width:{} height:{} %core:{} %belt:{}", @@ -597,9 +558,9 @@ private Impacts computeImpacts (BeamItem item, double widthImpact = (width - minWidthLow) / (minWidthHigh - minWidthLow); double minHeightImpact = (item.height - itemParams.minHeightLow) / (itemParams.typicalHeight - - itemParams.minHeightLow); - double maxHeightImpact = (itemParams.maxHeightHigh - item.height) / (itemParams.maxHeightHigh - - itemParams.typicalHeight); + - itemParams.minHeightLow); + double maxHeightImpact = (itemParams.maxHeightHigh - item.height) + / (itemParams.maxHeightHigh - itemParams.typicalHeight); double coreImpact = (coreRatio - params.minCoreBlackRatio) / (1 - params.minCoreBlackRatio); double beltImpact = 1 - (beltRatio / params.maxBeltBlackRatio); @@ -728,7 +689,7 @@ private void createBeams () private List createSmallBeamInters (BeamStructure structure, double distImpact) { - final List beams = new ArrayList(); + final List beams = new ArrayList<>(); final List lines = structure.getLines(); for (BeamLine line : lines) { @@ -777,7 +738,7 @@ private void extendBeams () rawSystemBeams = sig.inters(AbstractBeamInter.class); // Extend each orphan beam as much as possible - for (Inter inter : new ArrayList(rawSystemBeams)) { + for (Inter inter : new ArrayList<>(rawSystemBeams)) { if (inter.isRemoved()) { continue; } @@ -843,10 +804,7 @@ private boolean extendInParallel (AbstractBeamInter beam, final double height = beam.getHeight(); final double slope = LineUtil.getSlope(median); - Area luArea = AreaUtil.horizontalParallelogram( - median.getP1(), - median.getP2(), - 3 * height); + Area luArea = AreaUtil.horizontalParallelogram(median.getP1(), median.getP2(), 3 * height); beam.addAttachment("=", luArea); List others = Inters.intersectedInters(rawSystemBeams, GeoOrder.NONE, luArea); @@ -892,7 +850,8 @@ private boolean extendInParallel (AbstractBeamInter beam, } // Check the other beam can really extend the current beam - final Point2D otherEndPt = (side == LEFT) ? otherMedian.getP1() : otherMedian.getP2(); + final Point2D otherEndPt = (side == LEFT) ? otherMedian.getP1() + : otherMedian.getP2(); double extDx = (side == LEFT) ? (endPt.getX() - otherEndPt.getX()) : (otherEndPt.getX() - endPt.getX()); @@ -948,9 +907,8 @@ private boolean extendToBeam (AbstractBeamInter beam, final Line2D beamMedian = beam.getMedian(); final Line2D otherMedian = other.getMedian(); - double gap = (beamMedian.getX1() < otherMedian.getX1()) - ? (otherMedian.getX1() - beamMedian.getX2()) - : (beamMedian.getX1() - otherMedian.getX2()); + double gap = (beamMedian.getX1() < otherMedian.getX1()) ? (otherMedian.getX1() - beamMedian + .getX2()) : (beamMedian.getX1() - otherMedian.getX2()); if (gap >= params.minBeamsGapX) { Area middleArea = middleArea(beam, other); @@ -1013,8 +971,8 @@ private boolean extendToPoint (AbstractBeamInter beam, // Check we have a concrete extension Point2D endPt = (side == LEFT) ? median.getP1() : median.getP2(); - double extDx = (side == LEFT) ? (endPt.getX() - extPt.getX()) : (extPt.getX() - - endPt.getX()); + double extDx = (side == LEFT) ? (endPt.getX() - extPt.getX()) + : (extPt.getX() - endPt.getX()); // (to cope with rounding we use 1 instead of 0) if (extDx <= 1) { @@ -1099,7 +1057,7 @@ private boolean extendToSpot (AbstractBeamInter beam, final int dx = (maxDx == null) ? params.maxExtensionToSpot : Math.min(params.maxExtensionToSpot, maxDx); final Area luArea = sideAreaOf("O", beam, side, 0, dx, 0); - final List spots = new ArrayList( + final List spots = new ArrayList<>( Glyphs.intersectedGlyphs(sortedBeamSpots, luArea)); Collections.sort(spots, Glyphs.byAbscissa); @@ -1147,8 +1105,7 @@ private boolean extendToStem (AbstractBeamInter beam, : Math.min(params.maxExtensionToStem, maxDx); final int dy = params.maxStemBeamGapY; final Area luArea = sideAreaOf("|", beam, side, dy, dx, 0); - List seeds = new ArrayList( - Glyphs.intersectedGlyphs(sortedSystemSeeds, luArea)); + List seeds = new ArrayList<>(Glyphs.intersectedGlyphs(sortedSystemSeeds, luArea)); Collections.sort(seeds, Glyphs.byAbscissa); // We should remove seeds already 'embraced' by the beam @@ -1184,12 +1141,11 @@ private boolean extendToStem (AbstractBeamInter beam, */ private List getCueAggregates () { - List aggregates = new ArrayList(); + List aggregates = new ArrayList<>(); // We look for collections of good cue black heads + stem, close enough // to be able to be connected by a cue beam. - List smallBlacks = sig.inters( - new Predicate() + List smallBlacks = sig.inters(new Predicate() { @Override public boolean check (Inter inter) @@ -1293,7 +1249,7 @@ private AbstractBeamInter getSideBeam (AbstractBeamInter beam, Line2D otherMedian = other.getMedian(); if ((Math.abs(LineUtil.getSlope(otherMedian) - slope) > params.maxBeamSlopeGap) - || (otherMedian.ptLineDist(endPt) > params.maxBeamsGapY)) { + || (otherMedian.ptLineDist(endPt) > params.maxBeamsGapY)) { it.remove(); } } @@ -1301,28 +1257,26 @@ private AbstractBeamInter getSideBeam (AbstractBeamInter beam, // Keep just the closest one to current beam (abscissa-wise) if (others.size() > 1) { final double endX = endPt.getX(); - Collections.sort( - others, - new Comparator() - { - @Override - public int compare (Inter o1, - Inter o2) - { - AbstractBeamInter b1 = (AbstractBeamInter) o1; - AbstractBeamInter b2 = (AbstractBeamInter) o2; - - if (side == LEFT) { - return Double.compare( - endX - b1.getMedian().getX2(), - endX - b2.getMedian().getX2()); - } else { - return Double.compare( - b1.getMedian().getX1() - endX, - b2.getMedian().getX1() - endX); - } - } - }); + Collections.sort(others, new Comparator() + { + @Override + public int compare (Inter o1, + Inter o2) + { + AbstractBeamInter b1 = (AbstractBeamInter) o1; + AbstractBeamInter b2 = (AbstractBeamInter) o2; + + if (side == LEFT) { + return Double.compare( + endX - b1.getMedian().getX2(), + endX - b2.getMedian().getX2()); + } else { + return Double.compare( + b1.getMedian().getX1() - endX, + b2.getMedian().getX1() - endX); + } + } + }); } if (!others.isEmpty()) { @@ -1381,14 +1335,14 @@ private BeamInter mergeOf (AbstractBeamInter one, final Line2D twoMedian = two.getMedian(); // Mean dist - double distImpact = (((Impacts) one.getImpacts()).getDistImpact() - + ((Impacts) two.getImpacts()).getDistImpact()) / 2; + double distImpact = (((Impacts) one.getImpacts()).getDistImpact() + ((Impacts) two + .getImpacts()).getDistImpact()) / 2; // Height double oneWidth = oneMedian.getX2() - oneMedian.getX1(); double twoWidth = twoMedian.getX2() - twoMedian.getX1(); double height = ((one.getHeight() * oneWidth) + (two.getHeight() * twoWidth)) / (oneWidth - + twoWidth); + + twoWidth); // Median & width final Line2D median; @@ -1435,7 +1389,7 @@ private Area middleArea (AbstractBeamInter one, double oneWidth = oneMedian.getX2() - oneMedian.getX1(); double twoWidth = twoMedian.getX2() - twoMedian.getX1(); double height = ((one.getHeight() * oneWidth) + (two.getHeight() * twoWidth)) / (oneWidth - + twoWidth); + + twoWidth); // Median final Line2D median; @@ -1529,7 +1483,7 @@ private Glyph retrieveGlyph (AbstractBeamInter beam) for (int dx = 0; dx < box.width; dx++) { p.x = box.x + dx; - if (p.x < filterWidth && p.y < filterHeight) { + if ((p.x < filterWidth) && (p.y < filterHeight)) { final int val = pixelFilter.get(p.x, p.y); if ((val == 0) && beam.contains(p)) { @@ -1543,8 +1497,7 @@ private Glyph retrieveGlyph (AbstractBeamInter beam) RunTable runTable = new RunTableFactory(VERTICAL).createTable(buf); // Glyph - Glyph glyph = sheet.getGlyphIndex().registerOriginal( - new BasicGlyph(box.x, box.y, runTable)); + Glyph glyph = sheet.getGlyphIndex().registerOriginal(new Glyph(box.x, box.y, runTable)); if (glyph.getWeight() == 0) { logger.warn("No pixels for {}", beam); @@ -1581,8 +1534,7 @@ private Area sideAreaOf (String kind, final Point2D intPt = LineUtil.intersectionAtX(median, intX); final double extX = (side == LEFT) ? (median.getX1() - extDx) : (median.getX2() + extDx); final Point2D extPt = LineUtil.intersectionAtX(median, extX); - Area area = (side == LEFT) - ? AreaUtil.horizontalParallelogram(extPt, intPt, height) + Area area = (side == LEFT) ? AreaUtil.horizontalParallelogram(extPt, intPt, height) : AreaUtil.horizontalParallelogram(intPt, extPt, height); if (kind != null) { @@ -1606,14 +1558,253 @@ private Inter stemOf (Inter head) return null; } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------------// + // isSideInSystem // + //----------------// + /** + * Check whether the (beam) side designated by provided point and height values + * lies fully in the current system + * + * @param pt side point on median + * @param height beam height + * @return true if side is fully within current system, false otherwise + */ + boolean isSideInSystem (Point2D pt, + double height) + { + Area area = system.getArea(); + + // Check top and bottom points of the beam side + for (int dir : new int[]{-1, +1}) { + if (!area.contains(pt.getX(), pt.getY() + (dir * (height / 2)))) { + return false; + } + } + + return true; + } + + //--------------// + // CueAggregate // + //--------------// + /** + * Describes an aggregate of cue notes. + * We assume that within a cue aggregate, the layout is rather simple: + * - all stems have the same direction. + */ + private class CueAggregate + { + + /** Unique Id. (in page) */ + private String id = ""; + + /** Bounds of the aggregate. */ + private Rectangle bounds; + + /** Sequence of cue heads. */ + private final List heads = new ArrayList<>(); + + /** Sequence of stems. (parallel to heads list) */ + private final List stems = new ArrayList<>(); + + /** Global stem direction. (up:-1, down:+1, mixed/unknown:0) */ + private int globalDir = 0; + + public void add (Inter head, + Inter stem) + { + // Head/Stem box + Rectangle hsBox = head.getBounds().union(stem.getBounds()); + + if (bounds == null) { + bounds = hsBox; + } else { + bounds.add(hsBox); + } + + heads.add(head); + stems.add(stem); + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + sb.append("{"); + sb.append(id); + + if (bounds != null) { + sb.append(" bounds:").append(bounds); + } + + for (int i = 0; i < heads.size(); i++) { + sb.append(" ").append(heads.get(i)).append("+").append(stems.get(i)); + } + + sb.append("}"); + + return sb.toString(); + } + + /** + * (Try to) connect the provided stem to compatible beams. + * + * @param stem the provided stem + * @param allBeams the collection of beams identified + */ + private void connectStemToBeams (Inter stem, + List allBeams, + Inter head) + { + // Which head corner? + if (globalDir == 0) { + return; + } + final Corner corner; + int headX = GeoUtil.centerOf(head.getBounds()).x; + int stemX = GeoUtil.centerOf(stem.getBounds()).x; + if (headX <= stemX) { + corner = (globalDir < 0) ? Corner.TOP_LEFT : Corner.BOTTOM_LEFT; + } else { + corner = (globalDir < 0) ? Corner.TOP_RIGHT : Corner.BOTTOM_RIGHT; + } + // Limit to beams that cross stem vertical line + List beams = new ArrayList<>(); + Rectangle fatStemBox = stem.getBounds(); + fatStemBox.grow(params.cueBoxDx, 0); + fatStemBox.y = bounds.y; + fatStemBox.height = bounds.height; + for (Inter beam : allBeams) { + if (fatStemBox.intersects(beam.getBounds())) { + beams.add(beam); + } + } + new StemsBuilder(system).linkCueBeams(head, corner, stem, beams); + } + + /** + * Retrieve cue glyph instances out of an aggregate snapshot. + * + * @return the list of glyph instances found + */ + private List getCueGlyphs () + { + // Expand aggregate bounds using global direction + Rectangle box = new Rectangle(bounds); + box.grow(params.cueBoxDx, 0); + + if (globalDir != 0) { + box.y += (globalDir * params.cueBoxDy); + } else { + box.grow(0, params.cueBoxDy); + } + + // Take a small *COPY* of binary image and apply morphology + Picture picture = sheet.getPicture(); + ByteProcessor whole = picture.getSource(Picture.SourceKey.BINARY); + ByteProcessor buf = new ByteProcessor(box.width, box.height); + + for (int y = 0; y < box.height; y++) { + for (int x = 0; x < box.width; x++) { + int val = whole.get(box.x + x, box.y + y); + buf.set(x, y, val); + } + } + + double beam = params.cueBeamRatio * sheet.getScale().getBeamThickness(); + + return new SpotsBuilder(sheet).buildSpots(buf, box.getLocation(), beam, id); + } + + /** + * Retrieve the global stem direction in the aggregate. + * + * @return the global direction found, 0 otherwise + */ + private int getDirection () + { + Integer dir = null; + + for (int i = 0; i < heads.size(); i++) { + final Inter head = heads.get(i); + final int headY = GeoUtil.centerOf(head.getBounds()).y; + final Inter stem = stems.get(i); + final Rectangle stemBox = stem.getBounds(); + + // Consider relative position is reliable only if head center + // is found in upper quarter or lower quarter of stem height + final int quarter = (int) Math.rint(stemBox.height / 4.0); + + if (headY >= ((stemBox.y + stemBox.height) - quarter)) { + if (dir == null) { + dir = -1; + } else if (dir > 0) { + return 0; + } + } else if (headY <= (stemBox.y + quarter)) { + if (dir == null) { + dir = 1; + } else if (dir < 0) { + return 0; + } + } + } + + return (dir != null) ? dir : 0; + } + + private void identify (int index) + { + id = "S" + system.getId() + "A" + (index + 1); + } + + private void process (List spots) + { + // Determine stem direction in the aggregate + globalDir = getDirection(); + if (globalDir == 0) { + logger.info("Mixed or unknown direction in cue area {}", this); + + return; + } + // Retrieve candidate glyphs from spots + List glyphs = getCueGlyphs(); + // Retrieve beams from candidate glyphs + List beams = new ArrayList<>(); + for (Glyph glyph : glyphs) { + glyph = system.registerGlyph(glyph, GlyphGroup.BEAM_SPOT); + spots.add(glyph); + List glyphBeams = new ArrayList<>(); + final String failure = checkBeamGlyph(glyph, true, glyphBeams); + if (failure != null) { + if (glyph.isVip()) { + logger.info("VIP cue#{} {}", glyph.getId(), failure); + } + } else { + if (glyph.isVip()) { + logger.debug("{} -> {}", glyph.idString(), glyphBeams); + } + + beams.addAll(glyphBeams); + } + } + // Link stems & beams as possible + if (!beams.isEmpty()) { + for (int i = 0; i < heads.size(); i++) { + final Inter head = heads.get(i); + final Inter stem = stems.get(i); + connectStemToBeams(stem, beams, head); + } + } + } + } + //----------------// // ItemParameters // //----------------// /** Parameters that govern beam/hook items, sometime dependent on cue/standard. */ public static class ItemParameters { - //~ Instance fields ------------------------------------------------------------------------ final double minBeamWidthLow; @@ -1637,7 +1828,12 @@ public static class ItemParameters final int coreSectionWidth; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create an ItemParameters object + * + * @param scale global sheet scale + * @param ratio provided ratio (1.0 for standard beam, about 0.6 for cue beam) + */ public ItemParameters (Scale scale, double ratio) { @@ -1660,10 +1856,9 @@ public ItemParameters (Scale scale, //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ // Item parameters //---------------- @@ -1692,7 +1887,7 @@ private static final class Constants "Low minimum height for a beam or hook, specified as ratio of typical beam"); private final Constant.Ratio maxHeightRatioHigh = new Constant.Ratio( - 1.3, + 1.4, // 1.3, "High maximum height for a beam or hook, specified as ratio of typical beam"); private final Constant.Ratio cornerMarginRatio = new Constant.Ratio( @@ -1817,7 +2012,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int maxSideBeamDx; @@ -1867,13 +2061,12 @@ private static class Parameters final double cueBeamRatio; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Parameters object. * * @param scale the scaling factor */ - public Parameters (Scale scale) + Parameters (Scale scale) { maxSideBeamDx = scale.toPixels(constants.maxSideBeamDx); minBeamsGapX = scale.toPixels(constants.minBeamsGapX); @@ -1906,232 +2099,4 @@ public Parameters (Scale scale) } } - //--------------// - // CueAggregate // - //--------------// - /** - * Describes an aggregate of cue notes. - * We assume that within a cue aggregate, the layout is rather simple: - * - all stems have the same direction. - */ - private class CueAggregate - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Unique Id. (in page) */ - private String id = ""; - - /** Bounds of the aggregate. */ - private Rectangle bounds; - - /** Sequence of cue heads. */ - private final List heads = new ArrayList(); - - /** Sequence of stems. (parallel to heads list) */ - private final List stems = new ArrayList(); - - /** Global stem direction. (up:-1, down:+1, mixed/unknown:0) */ - private int globalDir = 0; - - //~ Methods -------------------------------------------------------------------------------- - public void add (Inter head, - Inter stem) - { - // Head/Stem box - Rectangle hsBox = head.getBounds().union(stem.getBounds()); - - if (bounds == null) { - bounds = hsBox; - } else { - bounds.add(hsBox); - } - - heads.add(head); - stems.add(stem); - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()); - sb.append("{"); - sb.append(id); - - if (bounds != null) { - sb.append(" bounds:").append(bounds); - } - - for (int i = 0; i < heads.size(); i++) { - sb.append(" ").append(heads.get(i)).append("+").append(stems.get(i)); - } - - sb.append("}"); - - return sb.toString(); - } - - /** - * (Try to) connect the provided stem to compatible beams. - * - * @param stem the provided stem - * @param allBeams the collection of beams identified - */ - private void connectStemToBeams (Inter stem, - List allBeams, - Inter head) - { - // Which head corner? - if (globalDir == 0) { - return; - } - - final Corner corner; - int headX = GeoUtil.centerOf(head.getBounds()).x; - int stemX = GeoUtil.centerOf(stem.getBounds()).x; - - if (headX <= stemX) { - corner = (globalDir < 0) ? Corner.TOP_LEFT : Corner.BOTTOM_LEFT; - } else { - corner = (globalDir < 0) ? Corner.TOP_RIGHT : Corner.BOTTOM_RIGHT; - } - - // Limit to beams that cross stem vertical line - List beams = new ArrayList(); - Rectangle fatStemBox = stem.getBounds(); - fatStemBox.grow(params.cueBoxDx, 0); - fatStemBox.y = bounds.y; - fatStemBox.height = bounds.height; - - for (Inter beam : allBeams) { - if (fatStemBox.intersects(beam.getBounds())) { - beams.add(beam); - } - } - - new StemsBuilder(system).linkCueBeams(head, corner, stem, beams); - } - - /** - * Retrieve cue glyph instances out of an aggregate snapshot. - * - * @return the list of glyph instances found - */ - private List getCueGlyphs () - { - // Expand aggregate bounds using global direction - Rectangle box = new Rectangle(bounds); - box.grow(params.cueBoxDx, 0); - - if (globalDir != 0) { - box.y += (globalDir * params.cueBoxDy); - } else { - box.grow(0, params.cueBoxDy); - } - - // Take a small *COPY* of binary image and apply morphology - Picture picture = sheet.getPicture(); - ByteProcessor whole = picture.getSource(Picture.SourceKey.BINARY); - ByteProcessor buf = new ByteProcessor(box.width, box.height); - - for (int y = 0; y < box.height; y++) { - for (int x = 0; x < box.width; x++) { - int val = whole.get(box.x + x, box.y + y); - buf.set(x, y, val); - } - } - - double beam = params.cueBeamRatio * sheet.getScale().getBeamThickness(); - - return new SpotsBuilder(sheet).buildSpots(buf, box.getLocation(), beam, id); - } - - /** - * Retrieve the global stem direction in the aggregate. - * - * @return the global direction found, 0 otherwise - */ - private int getDirection () - { - Integer dir = null; - - for (int i = 0; i < heads.size(); i++) { - final Inter head = heads.get(i); - final int headY = GeoUtil.centerOf(head.getBounds()).y; - final Inter stem = stems.get(i); - final Rectangle stemBox = stem.getBounds(); - - // Consider relative position is reliable only if head center - // is found in upper quarter or lower quarter of stem height - final int quarter = (int) Math.rint(stemBox.height / 4.0); - - if (headY >= ((stemBox.y + stemBox.height) - quarter)) { - if (dir == null) { - dir = -1; - } else if (dir > 0) { - return 0; - } - } else if (headY <= (stemBox.y + quarter)) { - if (dir == null) { - dir = 1; - } else if (dir < 0) { - return 0; - } - } - } - - return (dir != null) ? dir : 0; - } - - private void identify (int index) - { - id = "S" + system.getId() + "A" + (index + 1); - } - - private void process (List spots) - { - // Determine stem direction in the aggregate - globalDir = getDirection(); - - if (globalDir == 0) { - logger.info("Mixed or unknown direction in cue area {}", this); - - return; - } - - // Retrieve candidate glyphs from spots - List glyphs = getCueGlyphs(); - - // Retrieve beams from candidate glyphs - List beams = new ArrayList(); - - for (Glyph glyph : glyphs) { - glyph = system.registerGlyph(glyph, GlyphGroup.BEAM_SPOT); - spots.add(glyph); - - List glyphBeams = new ArrayList(); - final String failure = checkBeamGlyph(glyph, true, glyphBeams); - - if (failure != null) { - if (glyph.isVip()) { - logger.info("VIP cue#{} {}", glyph.getId(), failure); - } - } else { - if (glyph.isVip()) { - logger.debug("{} -> {}", glyph.idString(), glyphBeams); - } - - beams.addAll(glyphBeams); - } - } - - // Link stems & beams as possible - if (!beams.isEmpty()) { - for (int i = 0; i < heads.size(); i++) { - final Inter head = heads.get(i); - final Inter stem = stems.get(i); - connectStemToBeams(stem, beams, head); - } - } - } - } } diff --git a/src/main/org/audiveris/omr/sheet/beam/BeamsStep.java b/src/main/org/audiveris/omr/sheet/beam/BeamsStep.java index 3affb934b..c6b75846d 100644 --- a/src/main/org/audiveris/omr/sheet/beam/BeamsStep.java +++ b/src/main/org/audiveris/omr/sheet/beam/BeamsStep.java @@ -46,11 +46,9 @@ public class BeamsStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(BeamsStep.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BeamsStep object. */ @@ -58,7 +56,6 @@ public BeamsStep () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // doSystem // //----------// @@ -88,8 +85,9 @@ protected void doEpilog (Sheet sheet, if (distances.getCardinality() > 0) { logger.info("BeamDistance{{}}", distances); - sheet.getScale() - .setBeamDistance(distances.getMeanValue(), distances.getStandardDeviation()); + sheet.getScale().setBeamDistance( + distances.getMeanValue(), + distances.getStandardDeviation()); } // Dispose of BEAM_SPOT glyphs, a glyph may be split into several beams @@ -119,7 +117,7 @@ protected Context doProlog (Sheet sheet) new SpotsBuilder(sheet).buildSheetSpots(spotLag); // Allocate map to collect vertical distances between beams of the same group - Map distanceMap = new TreeMap(); + Map distanceMap = new TreeMap<>(); for (SystemInfo system : sheet.getSystems()) { distanceMap.put(system, new Population()); @@ -128,13 +126,14 @@ protected Context doProlog (Sheet sheet) return new Context(distanceMap, spotLag); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Context // //---------// + /** + * Context for step processing. + */ protected static class Context { - //~ Instance fields ------------------------------------------------------------------------ /** Beam group vertical distances, per system. */ public final Map distancemap; @@ -142,9 +141,14 @@ protected static class Context /** Lag of spot sections. */ public final Lag spotLag; - //~ Constructors --------------------------------------------------------------------------- - public Context (Map distanceMap, - Lag spotLag) + /** + * Create Context. + * + * @param distanceMap + * @param spotLag + */ + Context (Map distanceMap, + Lag spotLag) { this.distancemap = distanceMap; this.spotLag = spotLag; diff --git a/src/main/org/audiveris/omr/sheet/beam/BlackHeadSizer.java b/src/main/org/audiveris/omr/sheet/beam/BlackHeadSizer.java index b1182d31c..4d427dbaf 100644 --- a/src/main/org/audiveris/omr/sheet/beam/BlackHeadSizer.java +++ b/src/main/org/audiveris/omr/sheet/beam/BlackHeadSizer.java @@ -57,19 +57,16 @@ */ public class BlackHeadSizer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(BlackHeadSizer.class); - //~ Instance fields ---------------------------------------------------------------------------- private final Sheet sheet; /** Scale-dependent global constants. */ private final Parameters params; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code BlackHeadSizer} object. * @@ -81,7 +78,6 @@ public BlackHeadSizer (Sheet sheet) params = new Parameters(sheet.getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// @@ -107,8 +103,8 @@ public void process (List spots) // Filter the spots based on typical weight, width and height. // Then derive main width and main height. // For visual check, use group BLACK_HEAD_SPOT and BLACK_STACK_SPOT - final List singles = new ArrayList(); - final List stacks = new ArrayList(); + final List singles = new ArrayList<>(); + final List stacks = new ArrayList<>(); for (Glyph glyph : spots) { // First check on glyph @@ -167,11 +163,7 @@ private boolean checkSpot (Glyph glyph) final int height = glyph.getHeight(); - if ((height < params.minHeight) || (height > params.maxStackHeight)) { - return false; - } - - return true; + return !((height < params.minHeight) || (height > params.maxStackHeight)); } //----------------// @@ -256,14 +248,12 @@ private void measureSingles (List singles) logger.info("{}", musicFontScale); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction closingDiameter = new Scale.Fraction( 0.9, @@ -328,7 +318,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int minWidth; @@ -356,13 +345,12 @@ private static class Parameters final int singlesQuorum; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Parameters object. * * @param scale the scaling factor */ - public Parameters (Scale scale) + Parameters (Scale scale) { minWidth = scale.toPixels(constants.minWidth); maxWidth = scale.toPixels(constants.maxWidth); diff --git a/src/main/org/audiveris/omr/sheet/beam/CueBeamsStep.java b/src/main/org/audiveris/omr/sheet/beam/CueBeamsStep.java index 36b90038a..a6a0c096a 100644 --- a/src/main/org/audiveris/omr/sheet/beam/CueBeamsStep.java +++ b/src/main/org/audiveris/omr/sheet/beam/CueBeamsStep.java @@ -45,11 +45,9 @@ public class CueBeamsStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new CueBeamsStep object. */ @@ -57,7 +55,6 @@ public CueBeamsStep () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // doSystem // //----------// @@ -75,7 +72,7 @@ public void doSystem (SystemInfo system, @Override protected Context doProlog (Sheet sheet) { - List spots = new ArrayList(); + List spots = new ArrayList<>(); Lag spotLag = new BasicLag(Lags.SPOT_LAG, SpotsBuilder.SPOT_ORIENTATION); // Display on cue spot glyphs? @@ -87,23 +84,29 @@ protected Context doProlog (Sheet sheet) return new Context(spots, spotLag); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Context // //---------// + /** + * Context for step processing. + */ protected static class Context { - //~ Instance fields ------------------------------------------------------------------------ - - /** Spot glyphs. */ - private final List spots; /** Lag of spot sections. */ public final Lag spotLag; - //~ Constructors --------------------------------------------------------------------------- - public Context (List spots, - Lag spotLag) + /** Spot glyphs. */ + private final List spots; + + /** + * Create Context. + * + * @param spots + * @param spotLag + */ + Context (List spots, + Lag spotLag) { this.spots = spots; this.spotLag = spotLag; @@ -113,10 +116,9 @@ public Context (List spots, //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean displayCueBeamSpots = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/sheet/beam/SpotsBuilder.java b/src/main/org/audiveris/omr/sheet/beam/SpotsBuilder.java index 91b5f95fe..0ef13ef22 100644 --- a/src/main/org/audiveris/omr/sheet/beam/SpotsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/beam/SpotsBuilder.java @@ -28,8 +28,8 @@ import org.audiveris.omr.constant.ConstantSet; import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.GlyphFactory; -import org.audiveris.omr.glyph.GlyphIndex; import org.audiveris.omr.glyph.GlyphGroup; +import org.audiveris.omr.glyph.GlyphIndex; import org.audiveris.omr.image.ImageUtil; import org.audiveris.omr.image.MorphoProcessor; import org.audiveris.omr.image.StructureElement; @@ -69,7 +69,6 @@ */ public class SpotsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -78,12 +77,10 @@ public class SpotsBuilder /** Orientation chosen for spot runs. */ public static final Orientation SPOT_ORIENTATION = Orientation.VERTICAL; - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ @Navigable(false) private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SpotsBuilder object. * @@ -94,7 +91,6 @@ public SpotsBuilder (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // buildSheetSpots // //-----------------// @@ -117,7 +113,12 @@ public void buildSheetSpots (Lag spotLag) // Retrieve major spots watch.start("buildSpots"); - int beam = sheet.getScale().getBeamThickness(); + Integer beam = sheet.getScale().getBeamThickness(); + + if (beam == null) { + throw new RuntimeException("No scale information on beam thickness"); + } + List spots = buildSpots(buffer, null, beam, null); // Dispatch spots per system(s) @@ -247,7 +248,7 @@ private void dispatchSheetSpots (List spots) int count = 0; final GlyphIndex glyphIndex = sheet.getGlyphIndex(); - final List relevants = new ArrayList(); + final List relevants = new ArrayList<>(); final SystemManager systemManager = sheet.getSystemManager(); for (Glyph glyph : spots) { @@ -374,14 +375,12 @@ private void saveHeadRuns (ByteProcessor buffer) sheet.getPicture().setTable(Picture.TableKey.HEAD_SPOTS, runs, true); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean displayBeamSpots = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/sheet/beam/SpotsController.java b/src/main/org/audiveris/omr/sheet/beam/SpotsController.java index 4d47beebd..9e279d6f4 100644 --- a/src/main/org/audiveris/omr/sheet/beam/SpotsController.java +++ b/src/main/org/audiveris/omr/sheet/beam/SpotsController.java @@ -52,11 +52,9 @@ */ public class SpotsController { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SpotsController.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ private final Sheet sheet; @@ -73,7 +71,6 @@ public class SpotsController /** User display. */ private MyView view; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SpotsController object. * @@ -90,7 +87,6 @@ public SpotsController (Sheet sheet, this.spotLag = spotLag; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // refresh // //---------// @@ -101,7 +97,7 @@ public void refresh () { if (view == null) { displayFrame(); - } else if (view != null) { + } else { view.repaint(); } } @@ -123,16 +119,14 @@ private void displayFrame () new SectionBoard(spotLag, true))); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // MyView // //--------// - private final class MyView + private class MyView extends NestView { - //~ Constructors --------------------------------------------------------------------------- - public MyView (GlyphIndex glyphIndex) + MyView (GlyphIndex glyphIndex) { super(glyphIndex.getEntityService(), Arrays.asList(spotLag), sheet); @@ -141,7 +135,6 @@ public MyView (GlyphIndex glyphIndex) setName("SpotsController-MyView"); } - //~ Methods -------------------------------------------------------------------------------- //--------// // render // //--------// diff --git a/src/main/org/audiveris/omr/sheet/curve/Arc.java b/src/main/org/audiveris/omr/sheet/curve/Arc.java index 03994df5d..e42c9499e 100644 --- a/src/main/org/audiveris/omr/sheet/curve/Arc.java +++ b/src/main/org/audiveris/omr/sheet/curve/Arc.java @@ -43,7 +43,6 @@ */ public class Arc { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Arc.class); @@ -69,9 +68,11 @@ public int compare (Arc a1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** Sequence of arc points so far. */ - protected final List points = new ArrayList(); + protected final List points = new ArrayList<>(); + + /** Related model, if any. */ + protected Model model; /** Junction point, if any, before points sequence. */ private Point firstJunction; @@ -82,13 +83,9 @@ public int compare (Arc a1, /** Shape found for this arc. */ private ArcShape shape; - /** Related model, if any. */ - protected Model model; - /** Already assigned to a curve?. */ private boolean assigned; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an arc with perhaps a firstJunction. * @@ -134,7 +131,6 @@ public Arc (Point firstJunction, this.model = model; } - //~ Methods ------------------------------------------------------------------------------------ //------------------// // checkOrientation // //------------------// @@ -214,6 +210,19 @@ public Model getModel () return model; } + //----------// + // setModel // + //----------// + /** + * Assign the arc model. + * + * @param model the model to set + */ + public void setModel (Model model) + { + this.model = model; + } + //-----------// // getPoints // //-----------// @@ -254,6 +263,19 @@ public ArcShape getShape () return shape; } + //----------// + // setShape // + //----------// + /** + * Assign the arc shape. + * + * @param shape the shape to set + */ + public void setShape (ArcShape shape) + { + this.shape = shape; + } + //------------// // getXLength // //------------// @@ -278,6 +300,19 @@ public boolean isAssigned () return assigned; } + //-------------// + // setAssigned // + //-------------// + /** + * Set the assigned fleg. + * + * @param assigned the assigned flag to set + */ + public void setAssigned (boolean assigned) + { + this.assigned = assigned; + } + //---------// // reverse // //---------// @@ -300,17 +335,6 @@ public void reverse () } } - //-------------// - // setAssigned // - //-------------// - /** - * @param assigned the assigned flag to set - */ - public void setAssigned (boolean assigned) - { - this.assigned = assigned; - } - //-------------// // setJunction // //-------------// @@ -330,28 +354,6 @@ public void setJunction (Point junction, } } - //----------// - // setModel // - //----------// - /** - * @param model the model to set - */ - public void setModel (Model model) - { - this.model = model; - } - - //----------// - // setShape // - //----------// - /** - * @param shape the shape to set - */ - public void setShape (ArcShape shape) - { - this.shape = shape; - } - //----------// // toString // //----------// @@ -370,6 +372,11 @@ public String toString () //-----------// // internals // //-----------// + /** + * Report a string description of class internals + * + * @return string description of internals + */ protected String internals () { StringBuilder sb = new StringBuilder(); diff --git a/src/main/org/audiveris/omr/sheet/curve/ArcRetriever.java b/src/main/org/audiveris/omr/sheet/curve/ArcRetriever.java index 24abf658f..c33f7c6e2 100644 --- a/src/main/org/audiveris/omr/sheet/curve/ArcRetriever.java +++ b/src/main/org/audiveris/omr/sheet/curve/ArcRetriever.java @@ -64,43 +64,24 @@ * If a skeleton sequence of points share the same (long) vertical run, only two junction points are * set, one at the beginning and one at the end. *
                                                    - * - scanImage()              // Scan the whole image for arc starts
                                                    - *   + scanJunction()         // Scan all arcs leaving a junction point
                                                    - *   |   + scanArc()
                                                    - *   + scanArc()              // Scan one arc
                                                    - *       + walkAlong()        // Walk till arc end (forward or backward)
                                                    - *       |   + move()         // Move just one pixel
                                                    - *       + determineShape()   // Determine the global arc shape
                                                    - *       + storeShape()       // Store arc shape in its ending pixels
                                                    + * -scanImage() // Scan the whole image for arc starts
                                                    + *         + scanJunction() // Scan all arcs leaving a junction point
                                                    + *         | +scanArc() + scanArc() // Scan one arc
                                                    + *                 + walkAlong() // Walk till arc end (forward or backward)
                                                    + *         | +move() // Move just one pixel
                                                    + *                 + determineShape() // Determine the global arc shape
                                                    + *                 + storeShape() // Store arc shape in its ending pixels
                                                      * 
                                                    * * @author Hervé Bitteur */ public class ArcRetriever { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(ArcRetriever.class); - //~ Enumerations ------------------------------------------------------------------------------- - /** - * Status for current move along arc. - */ - private static enum Status - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** One more point on arc. */ - CONTINUE, - /** Arrived at a new junction point. */ - SWITCH, - /** No more move possible. */ - END; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** The related sheet. */ @Navigable(false) private final Sheet sheet; @@ -130,7 +111,6 @@ private static enum Status /** Are we in a long run part?. */ boolean longRunPart; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates an ArcRetriever object * @@ -146,7 +126,6 @@ public ArcRetriever (Curves curves) params = new Parameters(sheet.getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // scanImage // //-----------// @@ -333,8 +312,9 @@ private boolean isStaffArc (Arc arc) minDy = min(minDy, dist); } - return (maxDist < params.minStaffLineDistance) - && ((maxDy - minDy) < params.minStaffLineDistance); + return (maxDist < params.minStaffLineDistance) && ((maxDy + - minDy) + < params.minStaffLineDistance); } //------// @@ -511,29 +491,6 @@ private void scanJunction (int x, } } - //-------// - // sinSq // - //-------// - /** Sin**2 of angle between (p0,p1) & (p0,p2). */ - private static double sinSq (int x0, - int y0, - int x1, - int y1, - int x2, - int y2) - { - x1 -= x0; - y1 -= y0; - x2 -= x0; - y2 -= y0; - - double vect = (x1 * y2) - (x2 * y1); - double l1Sq = (x1 * x1) + (y1 * y1); - double l2Sq = (x2 * x2) + (y2 * y2); - - return (vect * vect) / (l1Sq * l2Sq); - } - //------------// // storeShape // //------------// @@ -613,14 +570,48 @@ private void walkAlong (Arc arc, } } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------// + // sinSq // + //-------// + /** Sin**2 of angle between (p0,p1) & (p0,p2). */ + private static double sinSq (int x0, + int y0, + int x1, + int y1, + int x2, + int y2) + { + x1 -= x0; + y1 -= y0; + x2 -= x0; + y2 -= y0; + + double vect = (x1 * y2) - (x2 * y1); + double l1Sq = (x1 * x1) + (y1 * y1); + double l2Sq = (x2 * x2) + (y2 * y2); + + return (vect * vect) / (l1Sq * l2Sq); + } + + /** + * Status for current move along arc. + */ + private static enum Status + { + /** One more point on arc. */ + CONTINUE, + /** Arrived at a new junction point. */ + SWITCH, + /** No more move possible. */ + END; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Double maxAlpha = new Constant.Double( "degree", @@ -665,7 +656,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int arcMinQuorum; @@ -683,13 +673,12 @@ private static class Parameters final int maxRunLength; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Parameters object. * * @param scale the scaling factor */ - public Parameters (Scale scale) + Parameters (Scale scale) { double maxSin = sin(toRadians(constants.maxAlpha.getValue())); diff --git a/src/main/org/audiveris/omr/sheet/curve/ArcShape.java b/src/main/org/audiveris/omr/sheet/curve/ArcShape.java index 513eb0016..dad333159 100644 --- a/src/main/org/audiveris/omr/sheet/curve/ArcShape.java +++ b/src/main/org/audiveris/omr/sheet/curve/ArcShape.java @@ -23,6 +23,8 @@ /** * Shape detected for arc. + * + * @author Hervé Bitteur */ public enum ArcShape { @@ -67,11 +69,21 @@ public enum ArcShape this.forWedge = forWedge; } + /** + * Tell whether this arc is relevant for slur retrieval. + * + * @return true if so + */ public boolean isSlurRelevant () { return forSlur; } + /** + * Tell whether this arc is relevant for wedge retrieval. + * + * @return true if so + */ public boolean isWedgeRelevant () { return forWedge; diff --git a/src/main/org/audiveris/omr/sheet/curve/ArcView.java b/src/main/org/audiveris/omr/sheet/curve/ArcView.java index 1e6dd8520..eece13702 100644 --- a/src/main/org/audiveris/omr/sheet/curve/ArcView.java +++ b/src/main/org/audiveris/omr/sheet/curve/ArcView.java @@ -33,7 +33,6 @@ */ public class ArcView { - //~ Instance fields ---------------------------------------------------------------------------- /** True arc underneath. */ private final Arc arc; @@ -42,7 +41,6 @@ public class ArcView private List points; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ArcView object. * @@ -56,27 +54,48 @@ public ArcView (Arc arc, this.reversed = reversed; } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Report the underlying arc. + * + * @return the arc + */ public Arc getArc () { return arc; } + /** + * Report the ending point at specified end. + * + * @param reverse desired direction + * @return ending point + */ public Point getEnd (boolean reverse) { return arc.getEnd(reverse ^ reversed); } + /** + * Report junction, if any, at specified end. + * + * @param reverse desired direction + * @return junction or null + */ public Point getJunction (boolean reverse) { return arc.getJunction(reverse ^ reversed); } + /** + * Report the sequence of points. + * + * @return the current sequence of defining points + */ public List getPoints () { if (points == null) { if (reversed) { - points = new ArrayList(arc.getPoints()); + points = new ArrayList<>(arc.getPoints()); Collections.reverse(points); } else { points = arc.getPoints(); diff --git a/src/main/org/audiveris/omr/sheet/curve/ChordSplitter.java b/src/main/org/audiveris/omr/sheet/curve/ChordSplitter.java index e5bde9748..270c37d8b 100644 --- a/src/main/org/audiveris/omr/sheet/curve/ChordSplitter.java +++ b/src/main/org/audiveris/omr/sheet/curve/ChordSplitter.java @@ -90,14 +90,11 @@ */ public class ChordSplitter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - ChordSplitter.class); + private static final Logger logger = LoggerFactory.getLogger(ChordSplitter.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The large chord to be split. */ private final HeadChordInter chord; @@ -123,7 +120,6 @@ public class ChordSplitter private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ChordSplitter} object. * @@ -146,10 +142,12 @@ public ChordSplitter (HeadChordInter chord, minSubStemLength = sheet.getScale().toPixels(constants.minSubStemLength); } - //~ Methods ------------------------------------------------------------------------------------ //-------// // split // //-------// + /** + * Perform the chord split. + */ public void split () { if (chord.isVip()) { @@ -201,10 +199,10 @@ public void split () private void getAllPartitions () { // Collection of tied heads - final List tiedHeads = new ArrayList(); + final List tiedHeads = new ArrayList<>(); // Detect the partitions of consistent heads in this chord - allPartitions = new ArrayList(); + allPartitions = new ArrayList<>(); for (List slurList : origins.values()) { Partition partition = new Partition(); @@ -244,7 +242,7 @@ private void getAllPartitions () */ private Map> getSubStems () { - final Map> stemMap = new LinkedHashMap>(); + final Map> stemMap = new LinkedHashMap<>(); int iFirst = 0; // Index of first partition pending int iLastAddressed = -1; // Index of last addressed partition final int rootHeight = rootStem.getBounds().height; @@ -252,9 +250,8 @@ private Map> getSubStems () ((stemDir > 0) ? rootStem.getTop() : rootStem.getBottom()).getY()); for (int iLast = 0, iMax = allPartitions.size() - 1; iLast <= iMax; iLast++) { - final int yStop = (iLast != iMax) - ? (allPartitions.get(iLast + 1).first().getCenter().y - stemDir) - : chord.getTailLocation().y; + final int yStop = (iLast != iMax) ? (allPartitions.get(iLast + 1).first().getCenter().y + - stemDir) : chord.getTailLocation().y; final int height = stemDir * (yStop - yStart + 1); // Extract a sub-stem only if height is significant and smaller than root stem height @@ -300,7 +297,7 @@ private Map> getSubStems () */ private void injectStandardHeads (Collection tiedHeads) { - List stdHeads = new ArrayList(); + List stdHeads = new ArrayList<>(); for (Inter inter : chord.getNotes()) { HeadInter head = (HeadInter) inter; @@ -379,14 +376,12 @@ private void processStem (StemInter stem, } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction minSubStemLength = new Scale.Fraction( 2.5, @@ -403,14 +398,12 @@ private static class Partition extends TreeSet implements Comparable { - //~ Constructors --------------------------------------------------------------------------- - public Partition () + Partition () { super(HeadChordInter.headComparator); } - //~ Methods -------------------------------------------------------------------------------- @Override public int compareTo (Partition that) { @@ -428,5 +421,11 @@ public int distanceTo (int y) return Math.max(first - y, y - last); } + + @Override + public Object clone () + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } } diff --git a/src/main/org/audiveris/omr/sheet/curve/CircleModel.java b/src/main/org/audiveris/omr/sheet/curve/CircleModel.java index 564bbfb7b..5d9a55ac0 100644 --- a/src/main/org/audiveris/omr/sheet/curve/CircleModel.java +++ b/src/main/org/audiveris/omr/sheet/curve/CircleModel.java @@ -39,12 +39,10 @@ public class CircleModel implements Model { - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying circle. */ private final Circle circle; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new CircleModel object. * @@ -79,28 +77,6 @@ public CircleModel (Circle circle) this.circle = circle; } - //~ Methods ------------------------------------------------------------------------------------ - /** - * Factory method to try to create a valuable circle model. - * - * @param first first point - * @param middle a point rather in the middle - * @param last last point - * @return the CircleModel instance if OK, null otherwise - */ - public static CircleModel create (Point2D first, - Point2D middle, - Point2D last) - { - CircleModel model = new CircleModel(first, middle, last); - - if (model.circle.getRadius().isInfinite()) { - return null; - } else { - return model; - } - } - @Override public int above () { @@ -153,6 +129,12 @@ public double getDistance () return circle.getDistance(); } + @Override + public void setDistance (double dist) + { + circle.setDistance(dist); + } + @Override public Point2D getEndVector (boolean reverse) { @@ -174,9 +156,24 @@ public void reverse () circle.reverse(); } - @Override - public void setDistance (double dist) + /** + * Factory method to try to create a valuable circle model. + * + * @param first first point + * @param middle a point rather in the middle + * @param last last point + * @return the CircleModel instance if OK, null otherwise + */ + public static CircleModel create (Point2D first, + Point2D middle, + Point2D last) { - circle.setDistance(dist); + CircleModel model = new CircleModel(first, middle, last); + + if (model.circle.getRadius().isInfinite()) { + return null; + } else { + return model; + } } } diff --git a/src/main/org/audiveris/omr/sheet/curve/ClumpPruner.java b/src/main/org/audiveris/omr/sheet/curve/ClumpPruner.java index 047581085..038f7ff8b 100644 --- a/src/main/org/audiveris/omr/sheet/curve/ClumpPruner.java +++ b/src/main/org/audiveris/omr/sheet/curve/ClumpPruner.java @@ -61,18 +61,15 @@ */ public class ClumpPruner { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ClumpLinker.class); - //~ Instance fields ---------------------------------------------------------------------------- @Navigable(false) private final Sheet sheet; /** The engine which selects the best link pair for a given slur. */ private final SlurLinker slurLinker; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SlursLinker object. * @@ -85,7 +82,6 @@ public ClumpPruner (Sheet sheet) slurLinker = new SlurLinker(sheet); } - //~ Methods ------------------------------------------------------------------------------------ //-------// // prune // //-------// @@ -100,7 +96,7 @@ public SlurInter prune (Set clump) { // Compute lookup areas for each slur in clump // If we cannot compute areas for a slur, we simply discard the slur - Map> areas = new LinkedHashMap>(); + Map> areas = new LinkedHashMap<>(); for (Iterator it = clump.iterator(); it.hasNext();) { SlurInter slur = (SlurInter) it.next(); @@ -147,8 +143,8 @@ public SlurInter prune (Set clump) final HeadInter leftMirror = (HeadInter) leftHead.getMirror(); final HeadInter rightMirror = (HeadInter) rightHead.getMirror(); - if ((leftHead.getIntegerPitch() == rightHead.getIntegerPitch()) - && (leftHead.getStaff() == rightHead.getStaff())) { + if ((leftHead.getIntegerPitch() == rightHead.getIntegerPitch()) && (leftHead + .getStaff() == rightHead.getStaff())) { final SlurInter slur = selected.slur; // Check there is no other chords in between @@ -190,8 +186,7 @@ public SlurInter prune (Set clump) private Map getBounds (Set clump, Map> areas) { - Map bounds = new EnumMap( - HorizontalSide.class); + Map bounds = new EnumMap<>(HorizontalSide.class); for (HorizontalSide side : HorizontalSide.values()) { // Take union of areas for this side @@ -230,79 +225,6 @@ private void switchMirrorHead (SlurEntry entry, link.partner = mirror; } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // SlurEntry // - //-----------// - /** - * Class {@code SlurEntry} handles link data for a slur. - */ - private static class SlurEntry - { - //~ Static fields/initializers ------------------------------------------------------------- - - public static Comparator byWeightedDist = new Comparator() - { - @Override - public int compare (SlurEntry s1, - SlurEntry s2) - { - return Double.compare(s1.weightedDist(), s2.weightedDist()); - } - }; - - //~ Instance fields ------------------------------------------------------------------------ - /** The slur concerned. */ - public final SlurInter slur; - - /** The two best head links (left and right). */ - public final Map links; - - //~ Constructors --------------------------------------------------------------------------- - public SlurEntry (SlurInter slur, - Map links) - { - this.slur = slur; - this.links = links; - } - - //~ Methods -------------------------------------------------------------------------------- - /** - * Compute the mean euclidian-distance of this entry - * - * @return means euclidian distance - */ - public double meanEuclidianDist () - { - double dist = 0; - int n = 0; - - for (HorizontalSide side : HorizontalSide.values()) { - SlurHeadLink link = links.get(side); - - if (link != null) { - dist += ((SlurHeadRelation) link.relation).getEuclidean(); - n++; - } - } - - return dist / n; - } - - /** - * Temper raw euclidean distance with the slur length. - * - * @return a more valuable measurement of link quality - */ - public double weightedDist () - { - final double dist = meanEuclidianDist(); - final int nbPoints = slur.getInfo().getPoints().size(); - - return dist / nbPoints; - } - } - //-------------// // ClumpLinker // //-------------// @@ -311,7 +233,6 @@ public double weightedDist () */ private class ClumpLinker { - //~ Instance fields ------------------------------------------------------------------------ private final SystemInfo system; @@ -323,14 +244,12 @@ private class ClumpLinker private final List sysChords; /** Head-chords found around clump. */ - private final Map> chords = new EnumMap>( - HorizontalSide.class); - - //~ Constructors --------------------------------------------------------------------------- - public ClumpLinker (SystemInfo system, - Set clump, - Map bounds, - List sysChords) + private final Map> chords = new EnumMap<>(HorizontalSide.class); + + ClumpLinker (SystemInfo system, + Set clump, + Map bounds, + List sysChords) { this.system = system; this.clump = clump; @@ -341,7 +260,6 @@ public ClumpLinker (SystemInfo system, filterChords(bounds); } - //~ Methods -------------------------------------------------------------------------------- //--------// // doLink // //--------// @@ -372,13 +290,13 @@ public void doLink (SlurEntry entry) /** * Select the best slur in clump (within current system). * - * @param the lookup areas for each candidate + * @param areas the lookup areas for each candidate * @return the entry for best slur and its best links */ public SlurEntry selectSlur (Map> areas) { // Determine the pair of best links for every slur candidate - List entries = new ArrayList(); + List entries = new ArrayList<>(); for (Inter inter : clump) { SlurInter slur = (SlurInter) inter; @@ -438,7 +356,7 @@ private void filterChords (Map bounds) */ private List getNonOrphans (List entries) { - List nonOrphans = new ArrayList(); + List nonOrphans = new ArrayList<>(); EntryLoop: for (SlurEntry entry : entries) { for (HorizontalSide side : HorizontalSide.values()) { @@ -487,25 +405,24 @@ private SlurEntry selectAmong (List entries) SlurHeadLink bestRight = null; for (SlurEntry entry : entries) { + final SlurHeadLink left = entry.links.get(LEFT); + final SlurHeadLink right = entry.links.get(RIGHT); + if (bestEntry == null) { bestEntry = entry; bestDist = entry.weightedDist(); + bestLeft = left; + bestRight = right; logger.debug(" {} euclide:{}", entry, bestDist); } else { // Check whether embraced chords are still the same - SlurHeadLink left = entry.links.get(LEFT); - - if ((bestLeft != null) - && (left != null) - && (left.getChord() != bestLeft.getChord())) { + if ((bestLeft != null) && (left != null) && (left.getChord() != bestLeft + .getChord())) { break; } - SlurHeadLink right = entry.links.get(RIGHT); - - if ((bestRight != null) - && (right != null) - && (right.getChord() != bestRight.getChord())) { + if ((bestRight != null) && (right != null) && (right.getChord() != bestRight + .getChord())) { break; } @@ -523,4 +440,72 @@ private SlurEntry selectAmong (List entries) return bestEntry; } } + + //-----------// + // SlurEntry // + //-----------// + /** + * Class {@code SlurEntry} handles link data for a slur. + */ + private static class SlurEntry + { + + public static Comparator byWeightedDist = new Comparator() + { + @Override + public int compare (SlurEntry s1, + SlurEntry s2) + { + return Double.compare(s1.weightedDist(), s2.weightedDist()); + } + }; + + /** The slur concerned. */ + public final SlurInter slur; + + /** The two best head links (left and right). */ + public final Map links; + + SlurEntry (SlurInter slur, + Map links) + { + this.slur = slur; + this.links = links; + } + + /** + * Compute the mean euclidian-distance of this entry + * + * @return means euclidian distance + */ + public double meanEuclidianDist () + { + double dist = 0; + int n = 0; + + for (HorizontalSide side : HorizontalSide.values()) { + SlurHeadLink link = links.get(side); + + if (link != null) { + dist += ((SlurHeadRelation) link.relation).getEuclidean(); + n++; + } + } + + return dist / n; + } + + /** + * Temper raw euclidean distance with the slur length. + * + * @return a more valuable measurement of link quality + */ + public double weightedDist () + { + final double dist = meanEuclidianDist(); + final int nbPoints = slur.getInfo().getPoints().size(); + + return dist / nbPoints; + } + } } diff --git a/src/main/org/audiveris/omr/sheet/curve/Curve.java b/src/main/org/audiveris/omr/sheet/curve/Curve.java index f6db31bcc..8ceaffe01 100644 --- a/src/main/org/audiveris/omr/sheet/curve/Curve.java +++ b/src/main/org/audiveris/omr/sheet/curve/Curve.java @@ -21,7 +21,6 @@ // package org.audiveris.omr.sheet.curve; -import org.audiveris.omr.glyph.BasicGlyph; import org.audiveris.omr.glyph.Glyph; import static org.audiveris.omr.run.Orientation.VERTICAL; import org.audiveris.omr.run.Run; @@ -58,7 +57,6 @@ public abstract class Curve extends Arc implements AttachmentHolder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Curve.class); @@ -84,29 +82,27 @@ public int compare (Curve c1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** Unique id. (within containing sheet) */ protected final int id; - /** Arcs that compose this curve. */ - private final Set parts = new LinkedHashSet(); - /** Area for extension on first side. */ protected Area firstExtArea; /** Area for extension on last side. */ protected Area lastExtArea; - /** Potential attachments, lazily allocated. */ - private AttachmentHolder attachments; - /** Underlying glyph made of relevant runs. (generally thicker than the curve line) */ protected Glyph glyph; /** Bounds of points that compose the curve line. */ protected Rectangle bounds; - //~ Constructors ------------------------------------------------------------------------------- + /** Arcs that compose this curve. */ + private final Set parts = new LinkedHashSet<>(); + + /** Potential attachments, lazily allocated. */ + private AttachmentHolder attachments; + /** * Creates a new Curve object. * @@ -129,29 +125,6 @@ public Curve (int id, this.parts.addAll(parts); } - //~ Methods ------------------------------------------------------------------------------------ - //-----------------------// - // getAbscissaComparator // - //-----------------------// - /** - * Report a comparator on abscissa - * - * @param reverse which side is concerned - * @return the comparator ready for use - */ - public static Comparator getAbscissaComparator (final boolean reverse) - { - return new Comparator() - { - @Override - public int compare (Curve a1, - Curve a2) - { - return Integer.compare(a1.getEnd(reverse).x, a2.getEnd(reverse).x); - } - }; - } - //---------------// // addAttachment // //---------------// @@ -195,7 +168,7 @@ public void assign () public List getAllPoints (ArcView arcView, boolean reverse) { - List pts = new ArrayList(); + List pts = new ArrayList<>(); if (reverse) { pts.addAll(arcView.getPoints()); @@ -282,6 +255,11 @@ public Map getAttachments () //-----------// // getBounds // //-----------// + /** + * Report the bounding box for this curve. + * + * @return bounding box + */ public Rectangle getBounds () { if (bounds == null) { @@ -321,6 +299,12 @@ public Rectangle getBounds () //------------// // getExtArea // //------------// + /** + * Report the extension lookup area on provided side. + * + * @param reverse desired direction + * @return lookup area + */ public Area getExtArea (boolean reverse) { if (reverse) { @@ -343,6 +327,19 @@ public Glyph getGlyph () return glyph; } + //----------// + // setGlyph // + //----------// + /** + * Assign the underlying glyph + * + * @param glyph the underlying glyph (with relevant runs only) + */ + public void setGlyph (Glyph glyph) + { + this.glyph = glyph; + } + //-------// // getId // //-------// @@ -447,9 +444,12 @@ public Glyph retrieveGlyph (Sheet sheet, final Point point = points.get(index); // Point of curve final Run run = sheetTable.getRunAt(point.x, point.y); // Containing run - if (isCloseToCurve(point.x, run.getStart(), maxRunDistance, index) - && ((run.getLength() <= 1) - || isCloseToCurve(point.x, run.getStop(), maxRunDistance, index))) { + if (isCloseToCurve(point.x, run.getStart(), maxRunDistance, index) && ((run + .getLength() <= 1) || isCloseToCurve( + point.x, + run.getStop(), + maxRunDistance, + index))) { curveTable.addRun(point.x - fatBox.x, run.getStart() - fatBox.y, run.getLength()); } } @@ -457,7 +457,7 @@ public Glyph retrieveGlyph (Sheet sheet, // Build glyph (TODO: table a bit too high, should be trimmed?) if (curveTable.getSize() > 0) { Glyph curveGlyph = sheet.getGlyphIndex().registerOriginal( - new BasicGlyph(fatBox.x, fatBox.y, curveTable)); + new Glyph(fatBox.x, fatBox.y, curveTable)); setGlyph(curveGlyph); logger.debug("{} -> {}", this, curveGlyph); @@ -488,51 +488,6 @@ public void setExtArea (Area area, } } - //----------// - // setGlyph // - //----------// - /** - * Assign the underlying glyph - * - * @param glyph the underlying glyph (with relevant runs only) - */ - public void setGlyph (Glyph glyph) - { - this.glyph = glyph; - } - - // - // //----------// - // // toString // - // //----------// - // @Override - // public String toString () - // { - // StringBuilder sb = new StringBuilder(); - // sb.append(getClass().getSimpleName()); - // - // boolean first = true; - // - // for (Arc arc : parts) { - // if (first) { - // first = false; - // } else { - // Point j = arc.getJunction(true); - // - // if (j != null) { - // sb.append(" <").append(j.x).append(",").append(j.y).append(">"); - // } - // } - // - // sb.append(" ").append(arc); - // } - // - // sb.append(internals()); - // - // sb.append("}"); - // - // return sb.toString(); - // } //-----------// // internals // //-----------// @@ -603,11 +558,11 @@ private boolean isCloseToCurve (int x, private Point junctionOf (Arc a1, Arc a2) { - List s1 = new ArrayList(); + List s1 = new ArrayList<>(); s1.add(a1.getJunction(true)); s1.add(a1.getJunction(false)); - List s2 = new ArrayList(); + List s2 = new ArrayList<>(); s2.add(a2.getJunction(true)); s2.add(a2.getJunction(false)); s1.retainAll(s2); @@ -618,4 +573,26 @@ private Point junctionOf (Arc a1, return null; } } + + //-----------------------// + // getAbscissaComparator // + //-----------------------// + /** + * Report a comparator on abscissa + * + * @param reverse which side is concerned + * @return the comparator ready for use + */ + public static Comparator getAbscissaComparator (final boolean reverse) + { + return new Comparator() + { + @Override + public int compare (Curve a1, + Curve a2) + { + return Integer.compare(a1.getEnd(reverse).x, a2.getEnd(reverse).x); + } + }; + } } diff --git a/src/main/org/audiveris/omr/sheet/curve/CurveGap.java b/src/main/org/audiveris/omr/sheet/curve/CurveGap.java index d9e59bdb6..c56a2f8b9 100644 --- a/src/main/org/audiveris/omr/sheet/curve/CurveGap.java +++ b/src/main/org/audiveris/omr/sheet/curve/CurveGap.java @@ -40,14 +40,12 @@ */ public abstract class CurveGap { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(CurveGap.class); /** Pixels added on both sides of connection line to get some thickness. */ protected static final int MARGIN = 1; - //~ Instance fields ---------------------------------------------------------------------------- /** End point of curve. */ protected final Point p1; @@ -60,7 +58,6 @@ public abstract class CurveGap /** Gap vector to check empty locations. */ protected int[] vector; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new CurveGap object. * @@ -74,29 +71,6 @@ private CurveGap (Point p1, this.p2 = p2; } - //~ Methods ------------------------------------------------------------------------------------ - /** - * Factory method to create the CurveGap instance with proper orientation. - * - * @param p1 curve end point - * @param p2 arc end point - * @return either a Horizontal or a Vertical gap instance according to relative position of - * p1 and p2. - */ - public static CurveGap create (Point p1, - Point p2) - { - // Determine if line is rather horizontal or vertical - final int dx = Math.abs(p2.x - p1.x); - final int dy = Math.abs(p2.y - p1.y); - - if (dx >= dy) { - return new Horizontal(p1, p2); - } else { - return new Vertical(p1, p2); - } - } - /** * Compute the gap vector based on foreground pixels found in gap area. * @@ -120,6 +94,11 @@ public int[] computeVector (ByteProcessor buf) return vector; } + /** + * Report the lookup area. + * + * @return lookup area + */ public Area getArea () { return area; @@ -171,14 +150,34 @@ public int getLargestGap () protected abstract void populateVector (int x, int y); - //~ Inner Classes ------------------------------------------------------------------------------ + /** + * Factory method to create the CurveGap instance with proper orientation. + * + * @param p1 curve end point + * @param p2 arc end point + * @return either a Horizontal or a Vertical gap instance according to relative position of + * p1 and p2. + */ + public static CurveGap create (Point p1, + Point p2) + { + // Determine if line is rather horizontal or vertical + final int dx = Math.abs(p2.x - p1.x); + final int dy = Math.abs(p2.y - p1.y); + + if (dx >= dy) { + return new Horizontal(p1, p2); + } else { + return new Vertical(p1, p2); + } + } + /** * For rather horizontal gaps. */ public static class Horizontal extends CurveGap { - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new CurveGap.Horizontal object. @@ -194,7 +193,6 @@ public Horizontal (Point p1, area = computeArea(); } - //~ Methods -------------------------------------------------------------------------------- @Override protected final void populateVector (int x, int y) @@ -226,7 +224,6 @@ private Area computeArea () public static class Vertical extends CurveGap { - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new CurveGap.Vertical object. @@ -242,7 +239,6 @@ public Vertical (Point p1, area = computeArea(); } - //~ Methods -------------------------------------------------------------------------------- @Override protected final void populateVector (int x, int y) diff --git a/src/main/org/audiveris/omr/sheet/curve/Curves.java b/src/main/org/audiveris/omr/sheet/curve/Curves.java index fc3429614..1bf6b3708 100644 --- a/src/main/org/audiveris/omr/sheet/curve/Curves.java +++ b/src/main/org/audiveris/omr/sheet/curve/Curves.java @@ -57,7 +57,6 @@ */ public class Curves { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -65,7 +64,6 @@ public class Curves private static final List breakPoints = getBreakPoints(); - //~ Instance fields ---------------------------------------------------------------------------- /** The related sheet. */ @Navigable(false) private final Sheet sheet; @@ -77,15 +75,14 @@ public class Curves private final Skeleton skeleton; /** Line segments found. */ - private final List segments = new ArrayList(); + private final List segments = new ArrayList<>(); /** Registered item renderers, if any. */ - private final Set itemRenderers = new LinkedHashSet(); + private final Set itemRenderers = new LinkedHashSet<>(); /** Builder for slurs (also used to evaluate arcs). */ private SlursBuilder slursBuilder; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Curves object. * @@ -110,7 +107,6 @@ public Curves (Sheet sheet) } } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // buildCurves // //-------------// @@ -196,6 +192,11 @@ public List getSegments () //----------// // getSheet // //----------// + /** + * Report the containing sheet + * + * @return the containing sheet + */ public Sheet getSheet () { return sheet; @@ -215,6 +216,11 @@ public Skeleton getSkeleton () //-----------------// // getSlursBuilder // //-----------------// + /** + * Report the SlursBuilder companion. + * + * @return SlursBuilder instance + */ public SlursBuilder getSlursBuilder () { return slursBuilder; @@ -243,7 +249,7 @@ public void selectPoint (Point point) */ private static List getBreakPoints () { - List points = new ArrayList(); + List points = new ArrayList<>(); try { String str = constants.breakPointCoordinates.getValue(); @@ -267,28 +273,6 @@ private static List getBreakPoints () return points; } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean displayCurves = new Constant.Boolean( - false, - "Should we display the view on curves?"); - - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print out the stop watch?"); - - private final Constant.String breakPointCoordinates = new Constant.String( - "", - "(Debug) Comma-separated coordinates of curve break points if any"); - } - //--------// // MyView // //--------// @@ -298,14 +282,12 @@ private static final class Constants private class MyView extends ImageView { - //~ Constructors --------------------------------------------------------------------------- - public MyView (BufferedImage image) + MyView (BufferedImage image) { super(image); } - //~ Methods -------------------------------------------------------------------------------- @Override protected void renderItems (Graphics2D g) { @@ -318,4 +300,24 @@ protected void renderItems (Graphics2D g) } } } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean displayCurves = new Constant.Boolean( + false, + "Should we display the view on curves?"); + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print out the stop watch?"); + + private final Constant.String breakPointCoordinates = new Constant.String( + "", + "(Debug) Comma-separated coordinates of curve break points if any"); + } } diff --git a/src/main/org/audiveris/omr/sheet/curve/CurvesBuilder.java b/src/main/org/audiveris/omr/sheet/curve/CurvesBuilder.java index 74b911be4..688914ff3 100644 --- a/src/main/org/audiveris/omr/sheet/curve/CurvesBuilder.java +++ b/src/main/org/audiveris/omr/sheet/curve/CurvesBuilder.java @@ -72,13 +72,22 @@ public abstract class CurvesBuilder implements ItemRenderer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(CurvesBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- + /** To sort Extension instances by decreasing grade. */ + private static final Comparator byReverseGrade = new Comparator() + { + @Override + public int compare (Extension e1, + Extension e2) + { + return Double.compare(e2.getGrade(), e1.getGrade()); + } + }; + /** The related sheet. */ @Navigable(false) protected final Sheet sheet; @@ -92,36 +101,27 @@ public abstract class CurvesBuilder /** Image skeleton. */ protected final Skeleton skeleton; - /** Binary image (with staff lines). */ - private final ByteProcessor binaryBuf; - - /** Scale-dependent parameters. */ - private final Parameters params; - /** For unique curve IDs. (per page and per type of curve: slur or segment) */ protected int globalId = 0; /** (Current) orientation for walking along a curve. */ protected boolean reverse; - /** To sort Extension instances by decreasing grade. */ - private final Comparator byReverseGrade = new Comparator() - { - @Override - public int compare (Extension e1, - Extension e2) - { - return Double.compare(e2.getGrade(), e1.getGrade()); - } - }; - /** (Debug) tells whether an arc is being debugged. */ protected boolean debugArc; // Debug, to be removed ASAP. + /** + * + */ protected int maxClumpSize = 0; - //~ Constructors ------------------------------------------------------------------------------- + /** Binary image (with staff lines). */ + private final ByteProcessor binaryBuf; + + /** Scale-dependent parameters. */ + private final Parameters params; + /** * Creates a new SequencesBuilder object. * @@ -138,7 +138,6 @@ public CurvesBuilder (Curves curves) params = new Parameters(sheet.getScale()); } - //~ Methods ------------------------------------------------------------------------------------ /** * Try to append one arc to an existing curve and thus create a new curve. * @@ -222,7 +221,7 @@ protected Curve createCurve (Curve curve, points = curve.getAllPoints(arcView, reverse); } - Set parts = new LinkedHashSet(curve.getParts()); + Set parts = new LinkedHashSet<>(curve.getParts()); parts.add(arcView.getArc()); return createInstance(firstJunction, lastJunction, points, model, parts); @@ -253,13 +252,6 @@ protected abstract Curve createInstance (Point firstJunction, protected abstract void createInter (Curve curve, Set inters); - /** - * Additional filtering if any on the provided clump. - * - * @param clump the collection of slur candidates to be pruned down to one slur - */ - protected abstract void pruneClump (Set clump); - /** * Report the number of points at beginning of arc tested for connection. * @@ -275,6 +267,13 @@ protected abstract void createInter (Curve curve, */ protected abstract Point2D getEndVector (Curve curve); + /** + * Additional filtering if any on the provided clump. + * + * @param clump the collection of slur candidates to be pruned down to one slur + */ + protected abstract void pruneClump (Set clump); + /** * Among the clump of curves built from a common trunk on 'reverse' side, weed out * some of them. @@ -299,7 +298,7 @@ protected double arcDistance (Model model, ArcView arcView) { // First, determine the collection of points to measure (junction + first arc points) - List points = new ArrayList(); + List points = new ArrayList<>(); Point junction = arcView.getJunction(!reverse); if (junction != null) { @@ -337,8 +336,8 @@ protected void buildCurve (Arc arc) } // Try to extend the trunk as much as possible, on right end then on left end. - final Set leftClump = new LinkedHashSet(); - final Set rightClump = new LinkedHashSet(); + final Set leftClump = new LinkedHashSet<>(); + final Set rightClump = new LinkedHashSet<>(); for (boolean rev : new boolean[]{false, true}) { Set clump = rev ? leftClump : rightClump; @@ -350,7 +349,7 @@ protected void buildCurve (Arc arc) } // Combine candidates from both sides - Set clump = new LinkedHashSet(); + Set clump = new LinkedHashSet<>(); // Connect lefts & rights // Both endings must be OK, hence none of the side clumps is allowed to be empty @@ -390,7 +389,7 @@ protected Curve createCurve (Curve left, } // Build the list of points with no duplicates - List points = new ArrayList(left.getPoints()); + List points = new ArrayList<>(left.getPoints()); for (Point p : right.getPoints()) { if (!points.contains(p)) { @@ -399,7 +398,7 @@ protected Curve createCurve (Curve left, } // Build the set of parts - Set parts = new LinkedHashSet(left.getParts()); + Set parts = new LinkedHashSet<>(left.getParts()); parts.addAll(right.getParts()); return createInstance( @@ -456,12 +455,16 @@ protected Area defineExtArea (Curve curve) double dl1 = params.gapBoxDeltaIn; Point2D dlVect = new Point2D.Double(-dl1 * uv.getY(), dl1 * uv.getX()); path = new GeoPath( - new Line2D.Double(PointUtil.addition(ce, dlVect), PointUtil.subtraction(ce, dlVect))); + new Line2D.Double( + PointUtil.addition(ce, dlVect), + PointUtil.subtraction(ce, dlVect))); double dl2 = params.gapBoxDeltaOut; dlVect = new Point2D.Double(-dl2 * uv.getY(), dl2 * uv.getX()); path.append( - new Line2D.Double(PointUtil.subtraction(ce2, dlVect), PointUtil.addition(ce2, dlVect)), + new Line2D.Double( + PointUtil.subtraction(ce2, dlVect), + PointUtil.addition(ce2, dlVect)), true); // } path.closePath(); @@ -540,7 +543,7 @@ private Set erasedInters (SystemInfo system, Rectangle box, boolean crossable) { - Set found = new LinkedHashSet(); + Set found = new LinkedHashSet<>(); List inters = skeleton.getErasedInters(crossable).get(system); for (Inter inter : inters) { @@ -576,13 +579,13 @@ private void extend (Curve trunk, clump.add(trunk); // Extensions kept for clump - final Set candidates = new LinkedHashSet(); + final Set candidates = new LinkedHashSet<>(); // Map of pivot points reached so far - final Map pivots = new LinkedHashMap(); + final Map pivots = new LinkedHashMap<>(); // Extensions created in last pass - List rookies = new ArrayList(); + List rookies = new ArrayList<>(); Extension trunkExt = new Extension(trunk, trunk.getParts()); Point trunkPivot = trunk.getJunction(reverse); @@ -596,7 +599,7 @@ private void extend (Curve trunk, // We use a breadth-first strategy, to detect converging extensions ASAP do { final List actives = rookies; - rookies = new ArrayList(); + rookies = new ArrayList<>(); for (Extension ext : actives) { Point pivot = ext.curve.getJunction(reverse); @@ -742,7 +745,7 @@ private void filterReachableArcs (Curve curve, */ private Set findReachableArcs (Extension ext) { - final Set reachableArcs = new LinkedHashSet(); + final Set reachableArcs = new LinkedHashSet<>(); final Area area = defineExtArea(ext.curve); if (area != null) { @@ -905,7 +908,8 @@ private void register (Set inters) *

                                                    * The gap may be due to actual lack of black pixels but also to hidden items, some of which * being crossable (like bar line or stem) and some not (like note or beam). So the improved - * strategy for handling gaps is the following:

                                                      + * strategy for handling gaps is the following: + *
                                                        *
                                                      1. Using large gap window, check whether there is at least one reachable arc. If not, scan * is stopped.
                                                      2. *
                                                      3. If line between curve end and reachable arc would hit a non-crossable item, this @@ -967,10 +971,10 @@ private void scanPivot (Extension ext, final int lastDir = getDir(prevPoint, pivot); // Try to go past this pivot, keeping only the acceptable possibilities - List newCurves = new ArrayList(); + List newCurves = new ArrayList<>(); final Point np = new Point(); boolean sideJunctionMet = false; - List arcs = new ArrayList(); + List arcs = new ArrayList<>(); for (int dir : scans[lastDir]) { // If junction has already been met on side dir, stop here @@ -1029,14 +1033,63 @@ private void scanPivot (Extension ext, } } - //~ Inner Classes ------------------------------------------------------------------------------ + //-----------// + // Extension // + //-----------// + private class Extension + { + + /** Curve quality. */ + private Double grade; + + /** Curve as defined so far. */ + Curve curve; + + /** Arcs considered for curve immediate extension. */ + Set browsed; + + Extension (Curve curve, + Set browsed) + { + this.curve = curve; + this.browsed = new LinkedHashSet<>(browsed); // Copy is needed + } + + public double getGrade () + { + if (grade == null) { + GradeImpacts impacts = computeImpacts(curve, false); + + if (impacts != null) { + grade = impacts.getGrade(); + } else { + grade = 0.0; + } + } + + return grade; + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(); + sb.append("{Ext "); + sb.append(String.format("%.3f", getGrade())); + sb.append(curve); + ///sb.append(" browsed:").append(browsed); + sb.append('}'); + + return sb.toString(); + } + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction gapMaxLength = new Scale.Fraction( 0.25, @@ -1084,7 +1137,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final double gapMaxLength; @@ -1104,13 +1156,12 @@ private static class Parameters final int maxExtensionRookies; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Parameters object. * * @param scale the scaling factor */ - public Parameters (Scale scale) + Parameters (Scale scale) { gapMaxLength = scale.toPixels(constants.gapMaxLength); gapBoxLength = scale.toPixels(constants.gapBoxLength); @@ -1128,61 +1179,4 @@ public Parameters (Scale scale) } } - //-----------// - // Extension // - //-----------// - /** - * Meant to handle the process of extending a curve, by keeping track of arcs - * already browsed (regardless whether these arcs were actually kept or not). - */ - private class Extension - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Curve as defined so far. */ - Curve curve; - - /** Arcs considered for curve immediate extension. */ - Set browsed; - - /** Curve quality. */ - private Double grade; - - //~ Constructors --------------------------------------------------------------------------- - public Extension (Curve curve, - Set browsed) - { - this.curve = curve; - this.browsed = new LinkedHashSet(browsed); // Copy is needed - } - - //~ Methods -------------------------------------------------------------------------------- - public double getGrade () - { - if (grade == null) { - GradeImpacts impacts = computeImpacts(curve, false); - - if (impacts != null) { - grade = impacts.getGrade(); - } else { - grade = 0.0; - } - } - - return grade; - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(); - sb.append("{Ext "); - sb.append(String.format("%.3f", getGrade())); - sb.append(curve); - ///sb.append(" browsed:").append(browsed); - sb.append('}'); - - return sb.toString(); - } - } } diff --git a/src/main/org/audiveris/omr/sheet/curve/CurvesStep.java b/src/main/org/audiveris/omr/sheet/curve/CurvesStep.java index 46aba537e..ed23818c8 100644 --- a/src/main/org/audiveris/omr/sheet/curve/CurvesStep.java +++ b/src/main/org/audiveris/omr/sheet/curve/CurvesStep.java @@ -33,7 +33,6 @@ public class CurvesStep extends AbstractStep { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SlursStep object. @@ -42,7 +41,6 @@ public CurvesStep () { } - //~ Methods ------------------------------------------------------------------------------------ //------// // doit // //------// diff --git a/src/main/org/audiveris/omr/sheet/curve/EndingsBuilder.java b/src/main/org/audiveris/omr/sheet/curve/EndingsBuilder.java index 7074c481e..54c6c2585 100644 --- a/src/main/org/audiveris/omr/sheet/curve/EndingsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/curve/EndingsBuilder.java @@ -75,13 +75,11 @@ */ public class EndingsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(EndingsBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The related sheet. */ @Navigable(false) protected final Sheet sheet; @@ -92,7 +90,6 @@ public class EndingsBuilder /** Scale-dependent parameters. */ private final Parameters params; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new WedgesBuilder object. * @@ -105,7 +102,6 @@ public EndingsBuilder (Curves curves) params = new Parameters(sheet.getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // buildEndings // //--------------// @@ -139,7 +135,7 @@ private Glyph buildGlyph (SegmentInter segment, Filament leftLeg, Filament rightLeg) { - final List parts = new ArrayList(3); + final List parts = new ArrayList<>(3); parts.add(segment.getGlyph()); parts.add(leftLeg.toGlyph(null)); @@ -162,7 +158,7 @@ private void grabSentence (EndingInter ending) { Rectangle box = ending.getBounds(); SIGraph sig = ending.getSig(); - List found = new ArrayList(); + List found = new ArrayList<>(); List systemSentences = sig.inters(SentenceInter.class); for (Inter si : systemSentences) { @@ -243,11 +239,9 @@ private Filament lookupLeg (SegmentInfo seg, staff.getFirstLine().yAt(end.x) - end.y - (2 * params.legYMargin)); SystemInfo system = staff.getSystem(); - Set
                                                        sections = Sections.intersectedSections( - box, - system.getVerticalSections()); + Set
                                                        sections = Sections.intersectedSections(box, system.getVerticalSections()); Scale scale = sheet.getScale(); - FilamentFactory factory = new FilamentFactory( + FilamentFactory factory = new FilamentFactory<>( scale, sheet.getFilamentIndex(), VERTICAL, @@ -271,8 +265,9 @@ private Filament lookupLeg (SegmentInfo seg, for (Iterator it = filaments.iterator(); it.hasNext();) { StraightFilament fil = it.next(); - if ((fil.getLength(VERTICAL) < params.minLegLow) - || ((fil.getStartPoint().getY() - end.y) > params.maxLegYGap)) { + if ((fil.getLength(VERTICAL) < params.minLegLow) || ((fil.getStartPoint().getY() + - end.y) + > params.maxLegYGap)) { it.remove(); } } @@ -412,9 +407,7 @@ private void processSegment (SegmentInter segment) leftLeg.getStartPoint(), leftLeg.getStopPoint()); Line2D rightLine = (rightLeg == null) ? null - : new Line2D.Double( - rightLeg.getStartPoint(), - rightLeg.getStopPoint()); + : new Line2D.Double(rightLeg.getStartPoint(), rightLeg.getStopPoint()); EndingInter endingInter = new EndingInter( segment, line, @@ -440,14 +433,12 @@ private void processSegment (SegmentInter segment) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction minLengthLow = new Scale.Fraction( 6, @@ -457,9 +448,7 @@ private static final class Constants 10, "High minimum ending length"); - private final Scale.Fraction minLegLow = new Scale.Fraction( - 1.0, - "Low minimum leg length"); + private final Scale.Fraction minLegLow = new Scale.Fraction(1.0, "Low minimum leg length"); private final Scale.Fraction minLegHigh = new Scale.Fraction( 2.5, @@ -507,7 +496,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int minLengthLow; @@ -527,8 +515,7 @@ private static class Parameters final double maxSlope; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale) + Parameters (Scale scale) { minLengthLow = scale.toPixels(constants.minLengthLow); minLengthHigh = scale.toPixels(constants.minLengthHigh); diff --git a/src/main/org/audiveris/omr/sheet/curve/GlyphSlurInfo.java b/src/main/org/audiveris/omr/sheet/curve/GlyphSlurInfo.java index fcea99e0f..8bae82904 100644 --- a/src/main/org/audiveris/omr/sheet/curve/GlyphSlurInfo.java +++ b/src/main/org/audiveris/omr/sheet/curve/GlyphSlurInfo.java @@ -1,261 +1,260 @@ -//------------------------------------------------------------------------------------------------// -// // -// G l y p h S l u r I n f o // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet.curve; - -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.math.CubicUtil; -import org.audiveris.omr.math.LineUtil; -import org.audiveris.omr.math.PointUtil; -import org.audiveris.omr.run.Orientation; -import org.audiveris.omr.run.Run; -import org.audiveris.omr.run.RunTable; - -import java.awt.Point; -import java.awt.geom.CubicCurve2D; -import java.awt.geom.Line2D; -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * Class {@code GlyphSlurInfo} is a degenerated {@link SlurInfo}, meant for a manual - * slur defined by its underlying glyph. - * - * @author Hervé Bitteur - */ -public class GlyphSlurInfo - extends SlurInfo -{ - //~ Constructors ------------------------------------------------------------------------------- - - private GlyphSlurInfo (Glyph glyph, - List keyPoints) - { - super(0, null, null, keyPoints, null, Collections.EMPTY_LIST, 0); - this.glyph = glyph; - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Create proper GlyphSlurInfo for a slur defined by its glyph. - * The bezier curve is directly computed from the glyph. - * - * @param glyph the defining glyph - * @return the corresponding slur info - */ - public static GlyphSlurInfo create (Glyph glyph) - { - final List points = new KeyPointsBuilder(glyph).retrieveKeyPoints(); - final GlyphSlurInfo info = new GlyphSlurInfo(glyph, points); - - // Curve (nota: s1 and s2 are not control points, but intermediate points located on curve) - final Point s0 = points.get(0); - final Point s1 = points.get(1); - final Point s2 = points.get(2); - final Point s3 = points.get(3); - - info.curve = new CubicCurve2D.Double( - s0.x, - s0.y, - ((-(5 * s0.x) + (18 * s1.x)) - (9 * s2.x) + (2 * s3.x)) / 6, - ((-(5 * s0.y) + (18 * s1.y)) - (9 * s2.y) + (2 * s3.y)) / 6, - (((2 * s0.x) - (9 * s1.x) + (18 * s2.x)) - (5 * s3.x)) / 6, - (((2 * s0.y) - (9 * s1.y) + (18 * s2.y)) - (5 * s3.y)) / 6, - s3.x, - s3.y); - - // Above or below? - final int ccw = new Line2D.Double(s0, s1).relativeCCW(s3); - info.above = -ccw; - info.bisUnit = info.computeBisector(info.above > 0); - - return info; - } - - //----------// - // getCurve // - //----------// - @Override - public CubicCurve2D getCurve () - { - return curve; - } - - //--------------// - // getEndVector // - //--------------// - @Override - public Point2D getEndVector (boolean reverse) - { - // We use the control points to retrieve tangent vector - Point2D vector = reverse ? PointUtil.subtraction(curve.getP1(), curve.getCtrlP1()) - : PointUtil.subtraction(curve.getP2(), curve.getCtrlP2()); - - // Normalize - vector = PointUtil.times(vector, 1.0 / PointUtil.length(vector)); - - return vector; - } - - //-------------// - // getMidPoint // - //-------------// - @Override - public Point2D getMidPoint () - { - return CubicUtil.getMidPoint(curve); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //------------------// - // KeyPointsBuilder // - //------------------// - /** - * Class {@code KeyPointsBuilder} builds a slur from a provided glyph. - *

                                                        - * This class is meant for manual slur built on a selected glyph, by using exactly 4 key points - * evenly spaced along the glyph abscissa axis. - */ - private static class KeyPointsBuilder - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Glyph glyph; - - private final RunTable rt; - - //~ Constructors --------------------------------------------------------------------------- - /** - * Creates a new {@code KeyPointsBuilder} object. - * - * @param glyph the underlying glyph - */ - public KeyPointsBuilder (Glyph glyph) - { - this.glyph = glyph; - rt = glyph.getRunTable(); - } - - //~ Methods -------------------------------------------------------------------------------- - public List retrieveKeyPoints () - { - if (rt.getOrientation() != Orientation.VERTICAL) { - throw new IllegalArgumentException("Slur glyph runs must be vertical"); - } - - // Retrieve the 4 points WRT glyph bounds - final int width = glyph.getWidth(); - final List points = new ArrayList(4); - points.add(vectorAtX(0)); - points.add(vectorAtX((int) Math.rint(width / 3.0))); - points.add(vectorAtX((int) Math.rint((2 * width) / 3.0))); - points.add(vectorAtX(width - 1)); - - // Use sheet coordinates rather than glyph-based coordinates - final int dx = glyph.getLeft(); - final int dy = glyph.getTop(); - - for (Point point : points) { - point.translate(dx, dy); - } - - return points; - } - - private Point lookLeft (int x0) - { - for (int x = x0 - 1; x >= 0; x--) { - int y = yAtX(x); - - if (y != -1) { - return new Point(x, y); - } - } - - return null; - } - - private Point lookRight (int x0) - { - int size = rt.getSize(); - - for (int x = x0 + 1; x < size; x++) { - int y = yAtX(x); - - if (y != -1) { - return new Point(x, y); - } - } - - return null; - } - - /** - * Report the best offset for a given x offset. - *

                                                        - * If no run is found at x offset, columns on left and on right are searched, and y offset - * is then interpolated. - * - * @param x abscissa offset since glyph left side - * @return vector offset since glyph top left corner - */ - private Point vectorAtX (int x) - { - int y = yAtX(x); - - if (y != -1) { - return new Point(x, y); - } - - Point left = lookLeft(x); - Point right = lookRight(x); - - return PointUtil.rounded(LineUtil.intersectionAtX(new Line2D.Double(left, right), x)); - } - - /** - * Read the y offset for a given x offset. - * - * @param x given x offset - * @return -1 if there is no run at x, otherwise the middle of run - */ - private int yAtX (int x) - { - if (rt.isSequenceEmpty(x)) { - return -1; - } - - int yMin = Integer.MAX_VALUE; - int yMax = Integer.MIN_VALUE; - - for (Iterator it = rt.iterator(x); it.hasNext();) { - Run run = it.next(); - yMin = Math.min(yMin, run.getStart()); - yMax = Math.max(yMax, run.getStop()); - } - - return yMin + ((yMax - yMin) / 2); - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// G l y p h S l u r I n f o // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sheet.curve; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.math.CubicUtil; +import org.audiveris.omr.math.LineUtil; +import org.audiveris.omr.math.PointUtil; +import org.audiveris.omr.run.Orientation; +import org.audiveris.omr.run.Run; +import org.audiveris.omr.run.RunTable; + +import java.awt.Point; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Class {@code GlyphSlurInfo} is a degenerated {@link SlurInfo}, meant for a manual + * slur defined by its underlying glyph. + * + * @author Hervé Bitteur + */ +public class GlyphSlurInfo + extends SlurInfo +{ + + private GlyphSlurInfo (Glyph glyph, + List keyPoints) + { + super(0, null, null, keyPoints, null, Collections.EMPTY_LIST, 0); + this.glyph = glyph; + } + + //----------// + // getCurve // + //----------// + @Override + public CubicCurve2D getCurve () + { + return curve; + } + + //--------------// + // getEndVector // + //--------------// + @Override + public Point2D getEndVector (boolean reverse) + { + // We use the control points to retrieve tangent vector + Point2D vector = reverse ? PointUtil.subtraction(curve.getP1(), curve.getCtrlP1()) + : PointUtil.subtraction(curve.getP2(), curve.getCtrlP2()); + + // Normalize + vector = PointUtil.times(vector, 1.0 / PointUtil.length(vector)); + + return vector; + } + + //-------------// + // getMidPoint // + //-------------// + /** + * Report the middle point of the slur. + * + * @return middle point + */ + @Override + public Point2D getMidPoint () + { + return CubicUtil.getMidPoint(curve); + } + + /** + * Create proper GlyphSlurInfo for a slur defined by its glyph. + * The bezier curve is directly computed from the glyph. + * + * @param glyph the defining glyph + * @return the corresponding slur info + */ + public static GlyphSlurInfo create (Glyph glyph) + { + final List points = new KeyPointsBuilder(glyph).retrieveKeyPoints(); + final GlyphSlurInfo info = new GlyphSlurInfo(glyph, points); + + // Curve (nota: s1 and s2 are not control points, but intermediate points located on curve) + final Point s0 = points.get(0); + final Point s1 = points.get(1); + final Point s2 = points.get(2); + final Point s3 = points.get(3); + + info.curve = new CubicCurve2D.Double( + s0.x, + s0.y, + ((-(5 * s0.x) + (18 * s1.x)) - (9 * s2.x) + (2 * s3.x)) / 6.0, + ((-(5 * s0.y) + (18 * s1.y)) - (9 * s2.y) + (2 * s3.y)) / 6.0, + (((2 * s0.x) - (9 * s1.x) + (18 * s2.x)) - (5 * s3.x)) / 6.0, + (((2 * s0.y) - (9 * s1.y) + (18 * s2.y)) - (5 * s3.y)) / 6.0, + s3.x, + s3.y); + + // Above or below? + final int ccw = new Line2D.Double(s0, s1).relativeCCW(s3); + info.above = -ccw; + info.bisUnit = info.computeBisector(info.above > 0); + + return info; + } + + //------------------// + // KeyPointsBuilder // + //------------------// + /** + * Class {@code KeyPointsBuilder} builds a slur from a provided glyph. + *

                                                        + * This class is meant for manual slur built on a selected glyph, by using exactly 4 key points + * evenly spaced along the glyph abscissa axis. + */ + private static class KeyPointsBuilder + { + + private final Glyph glyph; + + private final RunTable rt; + + /** + * Creates a new {@code KeyPointsBuilder} object. + * + * @param glyph the underlying glyph + */ + KeyPointsBuilder (Glyph glyph) + { + this.glyph = glyph; + rt = glyph.getRunTable(); + } + + public List retrieveKeyPoints () + { + if (rt.getOrientation() != Orientation.VERTICAL) { + throw new IllegalArgumentException("Slur glyph runs must be vertical"); + } + + // Retrieve the 4 points WRT glyph bounds + final int width = glyph.getWidth(); + final List points = new ArrayList<>(4); + points.add(vectorAtX(0)); + points.add(vectorAtX((int) Math.rint(width / 3.0))); + points.add(vectorAtX((int) Math.rint((2 * width) / 3.0))); + points.add(vectorAtX(width - 1)); + + // Use sheet coordinates rather than glyph-based coordinates + final int dx = glyph.getLeft(); + final int dy = glyph.getTop(); + + for (Point point : points) { + point.translate(dx, dy); + } + + return points; + } + + private Point lookLeft (int x0) + { + for (int x = x0 - 1; x >= 0; x--) { + int y = yAtX(x); + + if (y != -1) { + return new Point(x, y); + } + } + + return null; + } + + private Point lookRight (int x0) + { + int size = rt.getSize(); + + for (int x = x0 + 1; x < size; x++) { + int y = yAtX(x); + + if (y != -1) { + return new Point(x, y); + } + } + + return null; + } + + /** + * Report the best offset for a given x offset. + *

                                                        + * If no run is found at x offset, columns on left and on right are searched, and y offset + * is then interpolated. + * + * @param x abscissa offset since glyph left side + * @return vector offset since glyph top left corner + */ + private Point vectorAtX (int x) + { + int y = yAtX(x); + + if (y != -1) { + return new Point(x, y); + } + + Point left = lookLeft(x); + Point right = lookRight(x); + + return PointUtil.rounded(LineUtil.intersectionAtX(new Line2D.Double(left, right), x)); + } + + /** + * Read the y offset for a given x offset. + * + * @param x given x offset + * @return -1 if there is no run at x, otherwise the middle of run + */ + private int yAtX (int x) + { + if (rt.isSequenceEmpty(x)) { + return -1; + } + + int yMin = Integer.MAX_VALUE; + int yMax = Integer.MIN_VALUE; + + for (Iterator it = rt.iterator(x); it.hasNext();) { + Run run = it.next(); + yMin = Math.min(yMin, run.getStart()); + yMax = Math.max(yMax, run.getStop()); + } + + return yMin + ((yMax - yMin) / 2); + } + } +} diff --git a/src/main/org/audiveris/omr/sheet/curve/JunctionRetriever.java b/src/main/org/audiveris/omr/sheet/curve/JunctionRetriever.java index 6a7423f24..a8aab1c02 100644 --- a/src/main/org/audiveris/omr/sheet/curve/JunctionRetriever.java +++ b/src/main/org/audiveris/omr/sheet/curve/JunctionRetriever.java @@ -41,18 +41,15 @@ */ public class JunctionRetriever { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(JunctionRetriever.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Skeleton buffer. */ private final ByteProcessor buf; /** Vicinity of current pixel. */ private final Vicinity vicinity = new Vicinity(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new JunctionRetriever object. * @@ -63,7 +60,6 @@ public JunctionRetriever (Skeleton skeleton) buf = skeleton.buf; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // scanImage // //-----------// @@ -77,7 +73,7 @@ public void scanImage () int pix = buf.get(x, y); if ((pix == FOREGROUND) // Basic pixel, not yet processed - || isJunction(pix)) { // Junction, perhaps not the best + || isJunction(pix)) { // Junction, perhaps not the best checkJunction(x, y); } } @@ -203,7 +199,6 @@ private int vicinityOf (int x, return vicinity.getCount(); } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // Vicinity // //----------// @@ -212,7 +207,6 @@ private int vicinityOf (int x, */ private static class Vicinity { - //~ Instance fields ------------------------------------------------------------------------ int verts; // Number of neighbors vertically connected @@ -220,7 +214,6 @@ private static class Vicinity int diags; // Number of neighbors diagonally connected - //~ Methods -------------------------------------------------------------------------------- public int getCount () { return verts + horis + diags; diff --git a/src/main/org/audiveris/omr/sheet/curve/LineModel.java b/src/main/org/audiveris/omr/sheet/curve/LineModel.java index 2266b7543..62d351fc1 100644 --- a/src/main/org/audiveris/omr/sheet/curve/LineModel.java +++ b/src/main/org/audiveris/omr/sheet/curve/LineModel.java @@ -38,12 +38,10 @@ public class LineModel implements Model { - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying line. */ private final BasicLine line; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LineModel object. * @@ -54,7 +52,6 @@ public LineModel (List points) line = new BasicLine(points); } - //~ Methods ------------------------------------------------------------------------------------ @Override public int above () { @@ -110,6 +107,12 @@ public double getDistance () return line.getMeanDistance(); } + @Override + public void setDistance (double dist) + { + // void? + } + @Override public Point2D getEndVector (boolean reverse) { @@ -136,9 +139,4 @@ public void reverse () throw new UnsupportedOperationException("Not supported yet."); } - @Override - public void setDistance (double dist) - { - // void? - } } diff --git a/src/main/org/audiveris/omr/sheet/curve/Model.java b/src/main/org/audiveris/omr/sheet/curve/Model.java index 02bec6e75..f083cb38d 100644 --- a/src/main/org/audiveris/omr/sheet/curve/Model.java +++ b/src/main/org/audiveris/omr/sheet/curve/Model.java @@ -38,7 +38,6 @@ */ public interface Model { - //~ Methods ------------------------------------------------------------------------------------ /** * Report the item shape as /--\ (above) or \--/ (below) or ---- (flat). diff --git a/src/main/org/audiveris/omr/sheet/curve/SegmentInfo.java b/src/main/org/audiveris/omr/sheet/curve/SegmentInfo.java index cfc9e990e..7c87f6f0b 100644 --- a/src/main/org/audiveris/omr/sheet/curve/SegmentInfo.java +++ b/src/main/org/audiveris/omr/sheet/curve/SegmentInfo.java @@ -33,7 +33,6 @@ public class SegmentInfo extends Curve { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SegmentInfo object. diff --git a/src/main/org/audiveris/omr/sheet/curve/SegmentsBuilder.java b/src/main/org/audiveris/omr/sheet/curve/SegmentsBuilder.java index 9e94502bd..37fd446e9 100644 --- a/src/main/org/audiveris/omr/sheet/curve/SegmentsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/curve/SegmentsBuilder.java @@ -62,7 +62,6 @@ public class SegmentsBuilder extends CurvesBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -70,14 +69,12 @@ public class SegmentsBuilder private static final Color SEGMENT = Color.CYAN; - //~ Instance fields ---------------------------------------------------------------------------- /** Scale-dependent parameters. */ private final Parameters params; /** All segments retrieved in sheet. */ private final List segments; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SegmentsBuilder object. * @@ -90,7 +87,6 @@ public SegmentsBuilder (Curves curves) segments = curves.getSegments(); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // buildSegments // //---------------// @@ -226,16 +222,13 @@ protected void createInter (Curve curve, needGlobalModel(segment); GradeImpacts impacts = computeImpacts(segment, true); + SegmentInter inter = new SegmentInter(segment, impacts); + Glyph glyph = segment.retrieveGlyph(sheet, params.maxRunDistance); - if (impacts != null) { - SegmentInter inter = new SegmentInter(segment, impacts); - Glyph glyph = segment.retrieveGlyph(sheet, params.maxRunDistance); - - if (glyph != null) { - inter.setGlyph(glyph); - inters.add(inter); - segments.add(inter); - } + if (glyph != null) { + inter.setGlyph(glyph); + inters.add(inter); + segments.add(inter); } } @@ -260,7 +253,7 @@ protected void pruneClump (Set clump) protected void weed (Set clump) { // Simply keep the one with longest X range. - List list = new ArrayList(clump); + List list = new ArrayList<>(clump); Collections.sort(list, Curve.byReverseXLength); clump.clear(); clump.add(list.get(0)); @@ -278,7 +271,7 @@ protected void weed (Set clump) private List getSeedArcs () { final StaffManager staffManager = sheet.getStaffManager(); - final Set set = new LinkedHashSet(); + final Set set = new LinkedHashSet<>(); ArcLoop: for (Arc arc : skeleton.arcsMap.values()) { @@ -316,7 +309,7 @@ private List getSeedArcs () set.add(arc); } - List list = new ArrayList(set); + List list = new ArrayList<>(set); Collections.sort(list, Arc.byReverseLength); return list; @@ -349,14 +342,12 @@ private void purgeDuplicates () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction arcMinSeedLength = new Scale.Fraction( 1.25, @@ -392,7 +383,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int arcMinSeedLength; @@ -406,8 +396,7 @@ private static class Parameters final double maxRunDistance; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale) + Parameters (Scale scale) { arcMinSeedLength = scale.toPixels(constants.arcMinSeedLength); maxWedgeSlope = Math.tan(Math.toRadians(constants.maxWedgeAngle.getValue())); diff --git a/src/main/org/audiveris/omr/sheet/curve/Skeleton.java b/src/main/org/audiveris/omr/sheet/curve/Skeleton.java index 807626b1e..3f5b27a5a 100644 --- a/src/main/org/audiveris/omr/sheet/curve/Skeleton.java +++ b/src/main/org/audiveris/omr/sheet/curve/Skeleton.java @@ -70,12 +70,17 @@ public class Skeleton implements ItemRenderer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(Skeleton.class); + private static final Color ARC_SLUR = Color.RED; + + private static final Color ARC_LINE = Color.BLUE; + + private static final Color ARC_LAMBDA = Color.LIGHT_GRAY; + /** Color for a foreground pixel discarded. */ static final int HIDDEN = 230; @@ -91,24 +96,23 @@ public class Skeleton /** Color for a foreground junction pixel already processed. */ static final int JUNCTION_PROCESSED = 90; - /** - * Headings. - *

                                                        -     * +-----+-----+-----+
                                                        -     * |     |     |     |
                                                        -     * |  7  |  8  |  1  |
                                                        -     * |     |     |     |
                                                        -     * +-----+-----+-----+
                                                        -     * |     |     |     |
                                                        -     * |  6  |  0  |  2  |
                                                        -     * |     |     |     |
                                                        -     * +-----+-----+-----+
                                                        -     * |     |     |     |
                                                        -     * |  5  |  4  |  3  |
                                                        -     * |     |     |     |
                                                        -     * +-----+-----+-----+
                                                        -     * 
                                                        - */ + // + // Headings: + // + // +-----+-----+-----+ + // | | | | + // | 7 | 8 | 1 | + // | | | | + // +-----+-----+-----+ + // | | | | + // | 6 | 0 | 2 | + // | | | | + // +-----+-----+-----+ + // | | | | + // | 5 | 4 | 3 | + // | | | | + // +-----+-----+-----+ + // /** Delta abscissa, per heading. 0 1 2. 3. 4 . 5 . 6 . 7. 8 */ static final int[] dxs = new int[]{0, 1, 1, 1, 0, -1, -1, -1, 0}; @@ -116,24 +120,22 @@ public class Skeleton static final int[] dys = new int[]{0, -1, 0, 1, 1, 1, 0, -1, -1}; /** Headings to scan, according to last heading. */ - static final int[][] scans = new int[][]{ - {2, 4, 6, 8, 1, 3, 5, 7}, // 0 - {2, 8, 1, 3, 7}, // 1 - {2, 4, 8, 1, 3}, // 2 - {2, 4, 1, 3, 5}, // 3 - {2, 4, 6, 3, 5}, // 4 - {4, 6, 3, 5, 7}, // 5 - {4, 6, 8, 5, 7}, // 6 - {6, 8, 1, 5, 7}, // 7 - {2, 6, 8, 1, 7} // 8 - }; + static final int[][] scans = new int[][]{{2, 4, 6, 8, 1, 3, 5, 7}, // 0 + {2, 8, 1, 3, 7}, // 1 + {2, 4, 8, 1, 3}, // 2 + {2, 4, 1, 3, 5}, // 3 + {2, 4, 6, 3, 5}, // 4 + {4, 6, 3, 5, 7}, // 5 + {4, 6, 8, 5, 7}, // 6 + {6, 8, 1, 5, 7}, // 7 + {2, 6, 8, 1, 7} // 8 +}; /** Map (Dx,Dy) -> Heading. */ - static final int[][] deltaToDir = new int[][]{ - {7, 6, 5}, // x:-1, y: -1, 0, +1 - {8, 0, 4}, // x: 0, y: -1, 0, +1 - {1, 2, 3} // x:+1, y: -1, 0, +1 - }; + static final int[][] deltaToDir = new int[][]{{7, 6, 5}, // x:-1, y: -1, 0, +1 + {8, 0, 4}, // x: 0, y: -1, 0, +1 + {1, 2, 3} // x:+1, y: -1, 0, +1 +}; /** Vertical headings: south & north. */ static final int[] vertDirs = new int[]{4, 8}; @@ -150,28 +152,21 @@ public class Skeleton /** All headings. */ static final int[] allDirs = new int[]{2, 4, 6, 8, 1, 3, 5, 7}; - private static final Color ARC_SLUR = Color.RED; - - private static final Color ARC_LINE = Color.BLUE; - - private static final Color ARC_LAMBDA = Color.LIGHT_GRAY; - - //~ Instance fields ---------------------------------------------------------------------------- - /** The related sheet. */ - @Navigable(false) - private final Sheet sheet; - /** The skeleton buffer. */ public ByteProcessor buf; /** Map of relevant arcs (end points → arc). */ - public final Map arcsMap = new LinkedHashMap(); + public final Map arcsMap = new LinkedHashMap<>(); /** Map of void arcs (pivot → arc(s)). */ - public final Map> voidArcsMap = new LinkedHashMap>(); + public final Map> voidArcsMap = new LinkedHashMap<>(); /** List of arcs end points, with no junction, ordered by abscissa. */ - public final List arcsEnds = new ArrayList(); + public final List arcsEnds = new ArrayList<>(); + + /** The related sheet. */ + @Navigable(false) + private final Sheet sheet; /** Map of non crossable erased inters. */ private Map> nonCrossables; @@ -182,7 +177,6 @@ public class Skeleton /** Map of erased (seed) glyphs. */ private Map> erasedSeeds; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Skeleton object. * @@ -193,7 +187,6 @@ public Skeleton (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // buildSkeleton // //---------------// @@ -203,7 +196,8 @@ public Skeleton (Sheet sheet) * Since this skeleton is meant for curves (slurs, wedges, endings) we can limit processing past * some reasonable distance from staves (both in vertical and horizontal directions). *

                                                        - * We must keep track of erased shapes at system level.

                                                          + * We must keep track of erased shapes at system level. + *
                                                            *
                                                          • Notes and beams cannot be crossed by a curve.
                                                          • *
                                                          • Bar lines, connections and stems can be crossed by a curve. * Perhaps another specific background value could be used?
                                                          • @@ -271,81 +265,6 @@ public BufferedImage buildSkeleton () return img; } - //--------// - // getDir // - //--------// - /** - * Report the precise heading that goes from point 'from' to point 'to'. - * - * @param from p1 - * @param to p2 - * @return heading p1 → p2 - */ - public static int getDir (Point from, - Point to) - { - int dx = to.x - from.x; - int dy = to.y - from.y; - - return deltaToDir[1 + dx][1 + dy]; - } - - //------------// - // isJunction // - //------------// - /** - * Tell whether the pixel value indicates a junction point. - * - * @param pix pixel gray value - * @return true if junction - */ - public static boolean isJunction (int pix) - { - return (pix >= JUNCTION) && (pix <= (JUNCTION + 10)); - } - - //---------------------// - // isJunctionProcessed // - //---------------------// - /** - * Tell whether the pixel value indicates a junction point already processed. - * - * @param pix pixel gray value - * @return true if junction already processed - */ - public static boolean isJunctionProcessed (int pix) - { - return pix == JUNCTION_PROCESSED; - } - - //-------------// - // isProcessed // - //-------------// - /** - * Tell whether the pixel value indicates an end point of an arc already processed. - * - * @param pix pixel gray value - * @return true if arc already processed - */ - public static boolean isProcessed (int pix) - { - return (pix >= PROCESSED) && (pix < (PROCESSED + 10)); - } - - //--------// - // isSide // - //--------// - /** - * Tell whether the provided heading is a side one (Horizontal or Vertical). - * - * @param dir provided heading - * @return true if horizontal or vertical - */ - public static boolean isSide (int dir) - { - return (dir % 2) == 0; - } - //------------// // addVoidArc // //------------// @@ -361,7 +280,7 @@ public void addVoidArc (Arc arc) List arcs = voidArcsMap.get(junctionPt); if (arcs == null) { - voidArcsMap.put(junctionPt, arcs = new ArrayList()); + voidArcsMap.put(junctionPt, arcs = new ArrayList<>()); } arcs.add(arc); @@ -423,6 +342,35 @@ public void setPixel (int x, buf.set(x, y, val); } + //----------// + // setColor // + //----------// + /** + * Paint the arc with a color that indicates its type of arc. + * + * @param arc the arc to paint + * @param g graphics context + */ + private void setColor (Arc arc, + Graphics2D g) + { + if (null == arc.getShape()) { + g.setColor(ARC_LAMBDA); + } else { + switch (arc.getShape()) { + case SLUR: + g.setColor(ARC_SLUR); + break; + case LINE: + g.setColor(ARC_LINE); + break; + default: + g.setColor(ARC_LAMBDA); + break; + } + } + } + //-----------------// // getErasedInters // //-----------------// @@ -448,35 +396,87 @@ Map> getErasedSeeds () return erasedSeeds; } - //----------// - // setColor // - //----------// + //--------// + // getDir // + //--------// /** - * Paint the arc with a color that indicates its type of arc. + * Report the precise heading that goes from point 'from' to point 'to'. * - * @param arc the arc to paint - * @param g graphics context + * @param from p1 + * @param to p2 + * @return heading p1 → p2 */ - private void setColor (Arc arc, - Graphics2D g) + public static int getDir (Point from, + Point to) { - if (arc.getShape() == ArcShape.SLUR) { - g.setColor(ARC_SLUR); - } else if (arc.getShape() == ArcShape.LINE) { - g.setColor(ARC_LINE); - } else { - g.setColor(ARC_LAMBDA); - } + int dx = to.x - from.x; + int dy = to.y - from.y; + + return deltaToDir[1 + dx][1 + dy]; + } + + //------------// + // isJunction // + //------------// + /** + * Tell whether the pixel value indicates a junction point. + * + * @param pix pixel gray value + * @return true if junction + */ + public static boolean isJunction (int pix) + { + return (pix >= JUNCTION) && (pix <= (JUNCTION + 10)); + } + + //---------------------// + // isJunctionProcessed // + //---------------------// + /** + * Tell whether the pixel value indicates a junction point already processed. + * + * @param pix pixel gray value + * @return true if junction already processed + */ + public static boolean isJunctionProcessed (int pix) + { + return pix == JUNCTION_PROCESSED; + } + + //-------------// + // isProcessed // + //-------------// + /** + * Tell whether the pixel value indicates an end point of an arc already processed. + * + * @param pix pixel gray value + * @return true if arc already processed + */ + public static boolean isProcessed (int pix) + { + return (pix >= PROCESSED) && (pix < (PROCESSED + 10)); + } + + //--------// + // isSide // + //--------// + /** + * Tell whether the provided heading is a side one (Horizontal or Vertical). + * + * @param dir provided heading + * @return true if horizontal or vertical + */ + public static boolean isSide (int dir) + { + return (dir % 2) == 0; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean keepSkeleton = new Constant.Boolean( false, @@ -514,7 +514,6 @@ private static final class Constants private static class CurvesCleaner extends PageCleaner { - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new CurvesEraser object. @@ -523,14 +522,13 @@ private static class CurvesCleaner * @param g graphics context on buffer * @param sheet related sheet */ - public CurvesCleaner (ByteProcessor buffer, - Graphics2D g, - Sheet sheet) + CurvesCleaner (ByteProcessor buffer, + Graphics2D g, + Sheet sheet) { super(buffer, g, sheet); } - //~ Methods -------------------------------------------------------------------------------- //---------------------// // eraseDistantRegions // //---------------------// @@ -586,11 +584,11 @@ public void eraseDistantRegions () */ public Map> eraseShapes (Collection shapes) { - final Map> erasedMap = new TreeMap>(); + final Map> erasedMap = new TreeMap<>(); for (SystemInfo system : sheet.getSystems()) { final SIGraph sig = system.getSig(); - final List erased = new ArrayList(); + final List erased = new ArrayList<>(); erasedMap.put(system, erased); for (Inter inter : sig.vertexSet()) { diff --git a/src/main/org/audiveris/omr/sheet/curve/SlurHeadLink.java b/src/main/org/audiveris/omr/sheet/curve/SlurHeadLink.java index bc046c4ae..befae416f 100644 --- a/src/main/org/audiveris/omr/sheet/curve/SlurHeadLink.java +++ b/src/main/org/audiveris/omr/sheet/curve/SlurHeadLink.java @@ -1,103 +1,108 @@ -//------------------------------------------------------------------------------------------------// -// // -// S l u r H e a d L i n k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet.curve; - -import org.audiveris.omr.sig.inter.AbstractChordInter; -import org.audiveris.omr.sig.inter.HeadChordInter; -import org.audiveris.omr.sig.inter.HeadInter; -import org.audiveris.omr.sig.relation.Link; -import org.audiveris.omr.sig.relation.SlurHeadRelation; -import org.audiveris.omr.util.HorizontalSide; - -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.Line2D; -import java.util.Comparator; - -/** - * Class {@code SlurHeadLink} formalizes a link between a slur end and a head - * in a chord nearby. - *

                                                            - * Rather than chord box center point, we use chord box middle vertical line. - * - * @author Hervé Bitteur - */ -public class SlurHeadLink - extends Link -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - public static Comparator byEuclidean = new Comparator() - { - @Override - public int compare (SlurHeadLink o1, - SlurHeadLink o2) - { - return Double.compare( - ((SlurHeadRelation) o1.relation).getEuclidean(), - ((SlurHeadRelation) o2.relation).getEuclidean()); - } - }; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SlurHeadLink} object. - * - * @param head the linked head - * @param rel precise relation instance - */ - public SlurHeadLink (HeadInter head, - SlurHeadRelation rel) - { - super(head, rel, true); - } - - //~ Methods ------------------------------------------------------------------------------------ - public static SlurHeadLink create (Point slurEnd, - HorizontalSide slurSide, - AbstractChordInter chord, - HeadInter head) - { - SlurHeadRelation rel = new SlurHeadRelation(slurSide); - - // Define middle vertical line of chord box - Rectangle box = chord.getBounds(); - Line2D vert = new Line2D.Double( - box.x + (box.width / 2), - box.y, - box.x + (box.width / 2), - box.y + box.height); - rel.setEuclidean(vert.ptSegDist(slurEnd)); - - return new SlurHeadLink(head, rel); - } - - /** - * Report the HeadChord which contains the linked head. - * - * @return the containing HeadChord - */ - public HeadChordInter getChord () - { - return (HeadChordInter) ((HeadInter) partner).getChord(); - } -} +//------------------------------------------------------------------------------------------------// +// // +// S l u r H e a d L i n k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sheet.curve; + +import org.audiveris.omr.sig.inter.AbstractChordInter; +import org.audiveris.omr.sig.inter.HeadChordInter; +import org.audiveris.omr.sig.inter.HeadInter; +import org.audiveris.omr.sig.relation.Link; +import org.audiveris.omr.sig.relation.SlurHeadRelation; +import org.audiveris.omr.util.HorizontalSide; + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.geom.Line2D; +import java.util.Comparator; + +/** + * Class {@code SlurHeadLink} formalizes a link between a slur end and a head + * in a chord nearby. + *

                                                            + * Rather than chord box center point, we use chord box middle vertical line. + * + * @author Hervé Bitteur + */ +public class SlurHeadLink + extends Link +{ + + /** + * To sort by increasing euclidian distance. + */ + public static final Comparator byEuclidean = new Comparator() + { + @Override + public int compare (SlurHeadLink o1, + SlurHeadLink o2) + { + return Double.compare(((SlurHeadRelation) o1.relation).getEuclidean(), + ((SlurHeadRelation) o2.relation).getEuclidean()); + } + }; + + /** + * Creates a new {@code SlurHeadLink} object. + * + * @param head the linked head + * @param rel precise relation instance + */ + public SlurHeadLink (HeadInter head, + SlurHeadRelation rel) + { + super(head, rel, true); + } + + /** + * Report the HeadChord which contains the linked head. + * + * @return the containing HeadChord + */ + public HeadChordInter getChord () + { + return ((HeadInter) partner).getChord(); + } + + /** + * Build proper SlurHeadRelation object. + * + * @param slurEnd slur ending point + * @param slurSide slur horizontal side + * @param chord head chord + * @param head head to be connected + * @return proper SlurHeadRelation instance, ready to be inserted in SIG + */ + public static SlurHeadLink create (Point slurEnd, + HorizontalSide slurSide, + AbstractChordInter chord, + HeadInter head) + { + SlurHeadRelation rel = new SlurHeadRelation(slurSide); + + // Define middle vertical line of chord box + Rectangle box = chord.getBounds(); + Line2D vert = new Line2D.Double(box.x + (box.width / 2), box.y, box.x + (box.width / 2), + box.y + box.height); + rel.setEuclidean(vert.ptSegDist(slurEnd)); + + return new SlurHeadLink(head, rel); + } +} diff --git a/src/main/org/audiveris/omr/sheet/curve/SlurInfo.java b/src/main/org/audiveris/omr/sheet/curve/SlurInfo.java index 544bacfcb..93d0bbd91 100644 --- a/src/main/org/audiveris/omr/sheet/curve/SlurInfo.java +++ b/src/main/org/audiveris/omr/sheet/curve/SlurInfo.java @@ -49,11 +49,9 @@ public class SlurInfo extends Curve { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SlurInfo.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Approximating first side model. */ protected Model firstModel; @@ -63,7 +61,8 @@ public class SlurInfo /** Number of points for side circles. */ protected final int sideLength; - /** Is the slur: above heads, below heads or flat. + /** + * Is the slur: above heads, below heads or flat. * 1 for above, -1 for below, 0 for flat */ protected int above; @@ -77,7 +76,6 @@ public class SlurInfo /** Global Bézier curve for the slur. */ protected CubicCurve2D curve; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SlurInfo object. * @@ -101,7 +99,6 @@ public SlurInfo (int id, this.sideLength = sideLength; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // isAbove // //---------// @@ -259,6 +256,11 @@ public Point2D getEndVector (boolean reverse) //-------------// // getMidPoint // //-------------// + /** + * Report slur middle point + * + * @return mid point + */ public Point2D getMidPoint () { return model.getMidPoint(); @@ -391,9 +393,15 @@ public void setModel (Model model) } } - //---------------// + //--------------// // setSideModel // - //---------------// + //--------------// + /** + * Assign model to provided side of the slur. + * + * @param model chosen model + * @param reverse desired direction + */ public void setSideModel (Model model, boolean reverse) { diff --git a/src/main/org/audiveris/omr/sheet/curve/SlurLinker.java b/src/main/org/audiveris/omr/sheet/curve/SlurLinker.java index a6f03a3d6..a6dc26b28 100644 --- a/src/main/org/audiveris/omr/sheet/curve/SlurLinker.java +++ b/src/main/org/audiveris/omr/sheet/curve/SlurLinker.java @@ -1,697 +1,687 @@ -//------------------------------------------------------------------------------------------------// -// // -// S l u r L i n k e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet.curve; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.math.GeoPath; -import org.audiveris.omr.math.GeoUtil; -import org.audiveris.omr.math.LineUtil; -import static org.audiveris.omr.math.LineUtil.bisector; -import static org.audiveris.omr.math.LineUtil.intersection; -import static org.audiveris.omr.math.LineUtil.intersectionAtX; -import org.audiveris.omr.math.PointUtil; -import static org.audiveris.omr.math.PointUtil.*; -import org.audiveris.omr.sheet.Part; -import org.audiveris.omr.sheet.Scale; -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sheet.rhythm.Measure; -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.AbstractChordInter; -import org.audiveris.omr.sig.inter.HeadInter; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.inter.SlurInter; -import org.audiveris.omr.sig.relation.BeamStemRelation; -import org.audiveris.omr.util.Dumping; -import org.audiveris.omr.util.HorizontalSide; -import static org.audiveris.omr.util.HorizontalSide.*; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.Area; -import java.awt.geom.Line2D; -import java.awt.geom.Point2D; -import static java.lang.Math.abs; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -/** - * Class {@code SlurLinker} retrieves the potential left and right links of a single slur - * with the most suitable heads found in slur side areas. - *

                                                              - *
                                                            • Rather horizontal slurs have specific side areas, select intersected chords, - * then select the closest head within those chords. - *
                                                            • Rather vertical slurs have specific side areas, select intersected chords and also - * check that heads centers are contained by side areas, then select the closest head. - * Head center must be on the same side (same half plane) of slur bisector than the slur end. - *
                                                            - * In both cases, head center location is checked with respect to slur concavity. - *

                                                            - * Areas image - * - * @author Hervé Bitteur - */ -public class SlurLinker -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger(SlurLinker.class); - - //~ Instance fields ---------------------------------------------------------------------------- - /** Scale-dependent parameters. */ - private final Parameters params; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SlurLinker} object. - * - * @param sheet the containing sheet - */ - public SlurLinker (Sheet sheet) - { - params = new Parameters(sheet.getScale()); - } - - //~ Methods ------------------------------------------------------------------------------------ - //----------------// - // defineAreaPair // - //----------------// - /** - * Define the pair of look-up areas for the slur, one on first end and - * another on last end. - *

                                                            - * These areas will be looked up for intersection with head-based chords (rather than heads). - * The strategy to define look-up areas differs between "horizontal" and "vertical" slurs. - *

                                                            - * Areas image - * - * @param slur the slur being processed - * @return the area on both slur sides - */ - public Map defineAreaPair (SlurInter slur) - { - final SlurInfo info = slur.getInfo(); - final Point first = info.getEnd(true); - final Point last = info.getEnd(false); - final int slurWidth = abs(last.x - first.x); - final int vDir = info.above(); - final int hDir = Integer.signum(last.x - first.x); - final Point2D mid = new Point2D.Double((first.x + last.x) / 2d, (first.y + last.y) / 2d); - final Point2D firstExt; - final Point2D lastExt; - final GeoPath firstPath; - final GeoPath lastPath; - final Line2D baseLine; - final Point2D firstBase; - final Point2D lastBase; - - // Qualify the slur as horizontal or vertical - if ((abs(LineUtil.getSlope(first, last)) <= params.slopeSeparator) - || (slurWidth >= params.wideSlurWidth)) { - // Horizontal: Use base parallel to slur - info.setHorizontal(true); - firstExt = extension(mid, first, params.coverageHExt); - lastExt = extension(mid, last, params.coverageHExt); - firstBase = new Point2D.Double( - firstExt.getX(), - firstExt.getY() + (vDir * params.coverageHDepth)); - lastBase = new Point2D.Double( - lastExt.getX(), - lastExt.getY() + (vDir * params.coverageHDepth)); - baseLine = new Line2D.Double(firstBase, lastBase); - - if (slurWidth > (2 * params.coverageHIn)) { - // Wide slur: separate first & last areas - Point2D firstIn = extension(mid, first, -params.coverageHIn); - firstPath = new GeoPath(new Line2D.Double(firstIn, firstExt)); - firstPath.append( - new Line2D.Double(firstBase, intersectionAtX(baseLine, firstIn.getX())), - true); - - Point2D lastIn = extension(mid, last, -params.coverageHIn); - lastPath = new GeoPath(new Line2D.Double(lastIn, lastExt)); - lastPath.append( - new Line2D.Double(lastBase, intersectionAtX(baseLine, lastIn.getX())), - true); - } else { - // Narrow slur: just one vertical separation - Point2D midBase = intersectionAtX(baseLine, mid.getX()); - firstPath = new GeoPath(new Line2D.Double(mid, firstExt)); - firstPath.append(new Line2D.Double(firstBase, midBase), true); - lastPath = new GeoPath(new Line2D.Double(mid, lastExt)); - lastPath.append(new Line2D.Double(lastBase, midBase), true); - } - } else { - // Vertical: Use slanted separation - info.setHorizontal(false); - firstExt = extension(mid, first, params.coverageVExt); - lastExt = extension(mid, last, params.coverageVExt); - - Point2D bisUnit = info.getBisUnit(); - double vDepth = (slurWidth <= params.maxSmallSlurWidth) ? params.coverageVDepthSmall - : params.coverageVDepth; - Point2D depth = new Point2D.Double(vDepth * bisUnit.getX(), vDepth * bisUnit.getY()); - firstBase = new Point2D.Double( - firstExt.getX() + depth.getX(), - firstExt.getY() + depth.getY()); - lastBase = new Point2D.Double( - lastExt.getX() + depth.getX(), - lastExt.getY() + depth.getY()); - baseLine = new Line2D.Double(firstBase, lastBase); - - if (first.distance(last) > (2 * params.coverageVIn)) { - // Tall slur, separate first & last areas - Point2D firstIn = extension(firstExt, first, params.coverageVIn); - Point2D firstBaseIn = new Point2D.Double( - firstIn.getX() + depth.getX(), - firstIn.getY() + depth.getY()); - firstPath = new GeoPath(new Line2D.Double(firstIn, firstExt)); - firstPath.append(new Line2D.Double(firstBase, firstBaseIn), true); - - Point2D lastIn = extension(lastExt, last, params.coverageVIn); - Point2D lastBaseIn = new Point2D.Double( - lastIn.getX() + depth.getX(), - lastIn.getY() + depth.getY()); - lastPath = new GeoPath(new Line2D.Double(lastIn, lastExt)); - lastPath.append(new Line2D.Double(lastBase, lastBaseIn), true); - } else { - // Small slur, just one slanted separation - firstPath = new GeoPath(new Line2D.Double(mid, firstExt)); - lastPath = new GeoPath(new Line2D.Double(mid, lastExt)); - - Line2D bisector = (vDir == hDir) ? bisector(first, last) : bisector(last, first); - Point2D baseInter = intersection(baseLine, bisector); - firstPath.append(new Line2D.Double(firstBase, baseInter), true); - lastPath.append(new Line2D.Double(lastBase, baseInter), true); - } - } - - firstPath.closePath(); - lastPath.closePath(); - - Map areaMap = new EnumMap(HorizontalSide.class); - Area firstArea = new Area(firstPath); - ///info.setArea(firstArea, true); - areaMap.put(LEFT, firstArea); - slur.addAttachment("F", firstArea); - - Area lastArea = new Area(lastPath); - ///info.setArea(lastArea, false); - areaMap.put(RIGHT, lastArea); - slur.addAttachment("L", lastArea); - - return areaMap; - } - - //----------------// - // lookupLinkPair // - //----------------// - /** - * Look precisely at content of lookup areas on left and right, and select best - * pair of links if any. - *

                                                              - *
                                                            • The same chord (including mirror) cannot belong to both areas.
                                                            • - *
                                                            • Left and right chords must differ enough in abscissa.
                                                            • - *
                                                            • To be really accepted, a chord candidate must contain a head suitable to be linked on - * proper slur side.
                                                            • - * - *
                                                            • Special heuristics for mirrored chords:
                                                                - *
                                                              • If the slur goes to another staff, select the mirror chord whose stem points towards - * the other staff.
                                                              • - *
                                                              • If the slur stays in its staff, select the mirror chord with same stem direction as - * the chord found at the other slur end. - * If there is no chord as the other slur end, select the mirror chord with the smallest - * number of beams.
                                                              • - *
                                                              - *
                                                            - * Possible orphan slurs are accepted for those close to staff side and rather horizontal. - * The other slurs must exhibit links on both sides. - * - * @param slur the slur candidate to check for links - * @param areas the lookup area on each slur side - * @param system the containing system - * @param chords the potential candidate chords on left and right side - * @return the pair of links if acceptable (only half-filled for orphan), null if not - */ - public Map lookupLinkPair (SlurInter slur, - Map areas, - SystemInfo system, - Map> chords) - { - // The pair to populate - final Map linkPair = new EnumMap( - HorizontalSide.class); - - // Slur target locations on each side - final Point leftTarget = getTargetPoint(slur, LEFT); - final Point rightTarget = getTargetPoint(slur, RIGHT); - - // Chords candidates on each side - Map lefts = lookup(slur, LEFT, areas.get(LEFT), chords.get(LEFT)); - Map rights = lookup(slur, RIGHT, areas.get(RIGHT), chords.get(RIGHT)); - - // The same chord cannot be linked to both slur ends - // Keep it only where it is closer to slur target - Set commons = new LinkedHashSet(); - commons.addAll(lefts.keySet()); - commons.retainAll(rights.keySet()); - - for (Inter common : commons) { - Rectangle chordBox = common.getBounds(); - Point chordCenter = GeoUtil.centerOf(chordBox); // TODO: choose a better ref point? - - if (chordCenter.distance(leftTarget) > chordCenter.distance(rightTarget)) { - lefts.remove(common); - } else { - rights.remove(common); - } - } - - // Reduce each side, except for couple [chord / mirrored chord] - Map mirrors = new EnumMap( - HorizontalSide.class); - - for (HorizontalSide side : HorizontalSide.values()) { - Map links = (side == LEFT) ? lefts : rights; - List list = new ArrayList(links.values()); - - if (!list.isEmpty()) { - Collections.sort(list, SlurHeadLink.byEuclidean); - - SlurHeadLink best = list.get(0); - - // Mirror? - Inter mirror = best.getChord().getMirror(); - - if ((mirror != null) && links.keySet().contains(mirror)) { - mirrors.put(side, best); // We have a conflict to solve - } else { - linkPair.put(side, best); // The best link is OK - } - } - } - - // Process mirrors conflicts if any - if (!mirrors.isEmpty()) { - for (Entry entry : mirrors.entrySet()) { - // This side of the slur - final HorizontalSide side = entry.getKey(); - final SlurHeadLink link = entry.getValue(); - final Map links = (side == LEFT) ? lefts : rights; - final SlurHeadLink mirrorLink = links.get(link.getChord().getMirror()); - final boolean linkOk; - - // The other side of the slur - final HorizontalSide otherSide = side.opposite(); - final SlurHeadLink otherLink = linkPair.get(otherSide); - - if (otherLink != null) { - // Compare with other side of the slur - final Staff otherStaff = otherLink.getChord().getTopStaff(); - final Staff staff = link.getChord().getTopStaff(); - final int dir = link.getChord().getStemDir(); - - if (staff != otherStaff) { - // Select mirror according to direction to other staff - linkOk = (dir * Staff.byId.compare(otherStaff, staff)) > 0; - } else { - // Select mirror with same stem dir as at other slur end - // (This may be called into question later when looking for ties) - int otherDir = otherLink.getChord().getStemDir(); - linkOk = dir == otherDir; - } - } else { - // No link found on other side (orphan? or slur truncated?) - // Not too stupid: select the mirror with less beams - final SIGraph sig = system.getSig(); - Inter stem = link.getChord().getStem(); - int nb = sig.getRelations(stem, BeamStemRelation.class).size(); - Inter mStem = mirrorLink.getChord().getStem(); - int mNb = sig.getRelations(mStem, BeamStemRelation.class).size(); - linkOk = nb <= mNb; - } - - linkPair.put(side, linkOk ? link : mirrorLink); - } - } - - // Check we don't have a chord on one slur side and its mirror on the other side - SlurHeadLink leftLink = linkPair.get(LEFT); - SlurHeadLink rightLink = linkPair.get(RIGHT); - - if ((leftLink != null) && (rightLink != null)) { - Inter leftMirror = leftLink.getChord().getMirror(); - - if ((leftMirror != null) && (leftMirror == rightLink.getChord())) { - logger.debug("{} chord and its mirror linked by the same slur!", slur); - - return null; - } - } - - // No link on left and on right? - if (linkPair.isEmpty()) { - return null; - } - - // One link is missing, check whether this slur candidate can be an orphan - for (HorizontalSide side : HorizontalSide.values()) { - if ((linkPair.get(side) == null) && !canBeOrphan(slur, side, system)) { - return null; // TODO: Too strict for manual usage - } - } - - return linkPair; - } - - //-------------// - // canBeOrphan // - //-------------// - /** - * Check whether the provided slur can be a legal orphan on the specified side. - * - * @param slur the slur to check - * @param side which side is orphaned - * @param system containing system - * @return true if legal - */ - private boolean canBeOrphan (SlurInter slur, - HorizontalSide side, - SystemInfo system) - { - final SlurInfo info = slur.getInfo(); - - // Check if slur is rather horizontal - if (abs(LineUtil.getSlope(info.getEnd(true), info.getEnd(false))) > params.maxOrphanSlope) { - logger.debug("{} too sloped orphan", slur); - - return false; - } - - // A left orphan must start in first measure. - // A right orphan must stop in last measure. - Point slurEnd = info.getEnd(side == LEFT); - Staff staff = system.getClosestStaff(slurEnd); - Part part = staff.getPart(); - Measure sideMeasure = (side == LEFT) ? part.getFirstMeasure() : part.getLastMeasure(); - Measure endMeasure = part.getMeasureAt(slurEnd); - - if (endMeasure != sideMeasure) { - logger.debug("{} orphan side not in part side measure", slur); - - return false; - } - - // Also, check horizontal gap to staff limit - int staffEnd = (side == LEFT) ? staff.getHeaderStop() : staff.getAbscissa(side); - - if (abs(slurEnd.x - staffEnd) > params.maxOrphanDx) { - logger.debug("{} too far orphan", slur); - - return false; - } - - return true; - } - - //----------------// - // getTargetPoint // - //----------------// - /** - * Report the precise target point for a head connection on desired side of a slur. - * - * @param slur the slur to process - * @param side the desired slur side - * @return the target connection point, slightly away from slur end - */ - private Point getTargetPoint (SlurInter slur, - HorizontalSide side) - { - final boolean rev = side == LEFT; - final SlurInfo info = slur.getInfo(); - final Point end = info.getEnd(rev); - final Point2D vector = info.getEndVector(rev); - final double ext = params.targetExtension; - - return PointUtil.rounded(PointUtil.addition(end, PointUtil.times(vector, ext))); - } - - //--------// - // lookup // - //--------// - /** - * Retrieve the best head embraced by the slur side. - * - * @param slur the provided slur - * @param side desired side - * @param area lookup area on slur side - * @param chords candidate chords on desired side - * @return the map of heads found, perhaps empty, with their data - */ - private Map lookup (SlurInter slur, - HorizontalSide side, - Area area, - List chords) - { - final Map found = new HashMap(); - final SlurInfo info = slur.getInfo(); - final Point end = info.getEnd(side == LEFT); - final Point target = getTargetPoint(slur, side); - final Point2D bisUnit = info.getBisUnit(); - - // Look for intersected chords - for (Inter chordInter : chords) { - AbstractChordInter chord = (AbstractChordInter) chordInter; - Rectangle chordBox = chord.getBounds(); - - if (area.intersects(chordBox)) { - // Check the chord contains at least one suitable head on desired slur side - HeadInter head = selectBestHead(slur, chord, end, target, bisUnit, area); - - if (head != null) { - found.put(chord, SlurHeadLink.create(target, side, chord, head)); - } - } - } - - return found; - } - - //----------------// - // selectBestHead // - //----------------// - /** - * Select the best note head in the selected head-based chord. - * We select the compatible note head which is closest to slur target end. - * - * @param chord the selected chord - * @param end the slur end point - * @param target the slur target point - * @param bisUnit the direction from slur middle to slur center (unit length) - * @param area target area - * @return the best note head or null - */ - private HeadInter selectBestHead (SlurInter slur, - AbstractChordInter chord, - Point end, - Point target, - Point2D bisUnit, - Area area) - { - final boolean horizontal = slur.getInfo().isHorizontal(); - final boolean above = slur.isAbove(); - - double bestDist = Double.MAX_VALUE; - HeadInter bestHead = null; - - for (Inter head : chord.getNotes()) { - Point center = head.getCenter(); - - if (!horizontal) { - // We require head center to be contained by lookup area - if (!area.contains(center)) { - continue; - } - } - - // Check head reference point WRT slur concavity - Rectangle bounds = head.getBounds(); - Point refPt = new Point(center.x, bounds.y + (above ? (bounds.height - 1) : 0)); - - if (dotProduct(subtraction(refPt, end), bisUnit) <= 0) { - continue; - } - - // Keep the closest head - final double dist = center.distanceSq(target); - - if (dist < bestDist) { - bestDist = dist; - bestHead = (HeadInter) head; - } - } - - return bestHead; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Scale.Fraction coverageHExt = new Scale.Fraction( - 1.25, - "Length of extension for horizontal slur coverage"); - - private final Scale.Fraction coverageHIn = new Scale.Fraction( - 0.5, - "Internal abscissa of horizontal slur coverage"); - - private final Scale.Fraction coverageHDepth = new Scale.Fraction( - 3.0, - "Vertical extension of horizontal slur coverage"); - - private final Scale.Fraction coverageVExt = new Scale.Fraction( - 2.0, - "Length of extension for vertical slur coverage"); - - private final Scale.Fraction coverageVIn = new Scale.Fraction( - 1.5, - "Internal abscissa of vertical slur coverage"); - - private final Scale.Fraction coverageVDepth = new Scale.Fraction( - 2.5, - "Vertical extension of vertical slur coverage"); - - private final Scale.Fraction coverageVDepthSmall = new Scale.Fraction( - 1.5, - "Vertical extension of small vertical slur coverage"); - - private final Scale.Fraction targetExtension = new Scale.Fraction( - 0.5, - "Extension length from slur end to slur target point"); - - private final Constant.Double slopeSeparator = new Constant.Double( - "tangent", - 0.5, - "Slope that separates vertical slurs from horizontal slurs"); - - private final Constant.Double maxOrphanSlope = new Constant.Double( - "tangent", - 0.5, - "Maximum slope for an orphan slur"); - - private final Scale.Fraction maxOrphanDx = new Scale.Fraction( - 6.0, - "Maximum dx to staff end for an orphan slur"); - - private final Scale.Fraction wideSlurWidth = new Scale.Fraction( - 6.0, - "Minimum width to be a wide slur"); - - private final Scale.Fraction maxSmallSlurWidth = new Scale.Fraction( - 1.5, - "Maximum width for a small slur"); - } - - //------------// - // Parameters // - //------------// - /** - * All pre-scaled constants. - */ - private static class Parameters - { - //~ Instance fields ------------------------------------------------------------------------ - - final int coverageHExt; - - final int coverageVExt; - - final int coverageHIn; - - final int coverageVIn; - - final int coverageHDepth; - - final int coverageVDepth; - - final int coverageVDepthSmall; - - final int targetExtension; - - final double slopeSeparator; - - final double maxOrphanSlope; - - final int maxOrphanDx; - - final int wideSlurWidth; - - final int maxSmallSlurWidth; - - //~ Constructors --------------------------------------------------------------------------- - /** - * Creates a new Parameters object. - * - * @param scale the scaling factor - */ - public Parameters (Scale scale) - { - coverageHExt = scale.toPixels(constants.coverageHExt); - coverageHIn = scale.toPixels(constants.coverageHIn); - coverageHDepth = scale.toPixels(constants.coverageHDepth); - coverageVExt = scale.toPixels(constants.coverageVExt); - coverageVIn = scale.toPixels(constants.coverageVIn); - coverageVDepth = scale.toPixels(constants.coverageVDepth); - coverageVDepthSmall = scale.toPixels(constants.coverageVDepthSmall); - targetExtension = scale.toPixels(constants.targetExtension); - slopeSeparator = constants.slopeSeparator.getValue(); - maxOrphanSlope = constants.maxOrphanSlope.getValue(); - maxOrphanDx = scale.toPixels(constants.maxOrphanDx); - wideSlurWidth = scale.toPixels(constants.wideSlurWidth); - maxSmallSlurWidth = scale.toPixels(constants.maxSmallSlurWidth); - - if (logger.isDebugEnabled()) { - new Dumping().dump(this); - } - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// S l u r L i n k e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sheet.curve; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.math.GeoPath; +import org.audiveris.omr.math.GeoUtil; +import org.audiveris.omr.math.LineUtil; +import static org.audiveris.omr.math.LineUtil.bisector; +import static org.audiveris.omr.math.LineUtil.intersection; +import static org.audiveris.omr.math.LineUtil.intersectionAtX; +import org.audiveris.omr.math.PointUtil; +import static org.audiveris.omr.math.PointUtil.*; +import org.audiveris.omr.sheet.Part; +import org.audiveris.omr.sheet.Scale; +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sheet.rhythm.Measure; +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.AbstractChordInter; +import org.audiveris.omr.sig.inter.HeadInter; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.inter.SlurInter; +import org.audiveris.omr.sig.relation.BeamStemRelation; +import org.audiveris.omr.util.Dumping; +import org.audiveris.omr.util.HorizontalSide; +import static org.audiveris.omr.util.HorizontalSide.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.geom.Area; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import static java.lang.Math.abs; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Class {@code SlurLinker} retrieves the potential left and right links of a single slur + * with the most suitable heads found in slur side areas. + *
                                                              + *
                                                            • Rather horizontal slurs have specific side areas, select intersected chords, + * then select the closest head within those chords. + *
                                                            • Rather vertical slurs have specific side areas, select intersected chords and also + * check that heads centers are contained by side areas, then select the closest head. + * Head center must be on the same side (same half plane) of slur bisector than the slur end. + *
                                                            + * In both cases, head center location is checked with respect to slur concavity. + *

                                                            + * Areas image + * + * @author Hervé Bitteur + */ +public class SlurLinker +{ + + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(SlurLinker.class); + + /** Scale-dependent parameters. */ + private final Parameters params; + + /** + * Creates a new {@code SlurLinker} object. + * + * @param sheet the containing sheet + */ + public SlurLinker (Sheet sheet) + { + params = new Parameters(sheet.getScale()); + } + + //----------------// + // defineAreaPair // + //----------------// + /** + * Define the pair of look-up areas for the slur, one on first end and + * another on last end. + *

                                                            + * These areas will be looked up for intersection with head-based chords (rather than heads). + * The strategy to define look-up areas differs between "horizontal" and "vertical" slurs. + *

                                                            + * Areas image + * + * @param slur the slur being processed + * @return the area on both slur sides + */ + public Map defineAreaPair (SlurInter slur) + { + final SlurInfo info = slur.getInfo(); + final Point first = info.getEnd(true); + final Point last = info.getEnd(false); + final int slurWidth = abs(last.x - first.x); + final int vDir = info.above(); + final int hDir = Integer.signum(last.x - first.x); + final Point2D mid = new Point2D.Double((first.x + last.x) / 2d, (first.y + last.y) / 2d); + final Point2D firstExt; + final Point2D lastExt; + final GeoPath firstPath; + final GeoPath lastPath; + final Line2D baseLine; + final Point2D firstBase; + final Point2D lastBase; + + // Qualify the slur as horizontal or vertical + if ((abs(LineUtil.getSlope(first, last)) <= params.slopeSeparator) + || (slurWidth >= params.wideSlurWidth)) { + // Horizontal: Use base parallel to slur + info.setHorizontal(true); + firstExt = extension(mid, first, params.coverageHExt); + lastExt = extension(mid, last, params.coverageHExt); + firstBase = new Point2D.Double( + firstExt.getX(), + firstExt.getY() + (vDir * params.coverageHDepth)); + lastBase = new Point2D.Double( + lastExt.getX(), + lastExt.getY() + (vDir * params.coverageHDepth)); + baseLine = new Line2D.Double(firstBase, lastBase); + + if (slurWidth > (2 * params.coverageHIn)) { + // Wide slur: separate first & last areas + Point2D firstIn = extension(mid, first, -params.coverageHIn); + firstPath = new GeoPath(new Line2D.Double(firstIn, firstExt)); + firstPath.append( + new Line2D.Double(firstBase, intersectionAtX(baseLine, firstIn.getX())), + true); + + Point2D lastIn = extension(mid, last, -params.coverageHIn); + lastPath = new GeoPath(new Line2D.Double(lastIn, lastExt)); + lastPath.append( + new Line2D.Double(lastBase, intersectionAtX(baseLine, lastIn.getX())), + true); + } else { + // Narrow slur: just one vertical separation + Point2D midBase = intersectionAtX(baseLine, mid.getX()); + firstPath = new GeoPath(new Line2D.Double(mid, firstExt)); + firstPath.append(new Line2D.Double(firstBase, midBase), true); + lastPath = new GeoPath(new Line2D.Double(mid, lastExt)); + lastPath.append(new Line2D.Double(lastBase, midBase), true); + } + } else { + // Vertical: Use slanted separation + info.setHorizontal(false); + firstExt = extension(mid, first, params.coverageVExt); + lastExt = extension(mid, last, params.coverageVExt); + + Point2D bisUnit = info.getBisUnit(); + double vDepth = (slurWidth <= params.maxSmallSlurWidth) ? params.coverageVDepthSmall + : params.coverageVDepth; + Point2D depth = new Point2D.Double(vDepth * bisUnit.getX(), vDepth * bisUnit.getY()); + firstBase = new Point2D.Double( + firstExt.getX() + depth.getX(), + firstExt.getY() + depth.getY()); + lastBase = new Point2D.Double( + lastExt.getX() + depth.getX(), + lastExt.getY() + depth.getY()); + baseLine = new Line2D.Double(firstBase, lastBase); + + if (first.distance(last) > (2 * params.coverageVIn)) { + // Tall slur, separate first & last areas + Point2D firstIn = extension(firstExt, first, params.coverageVIn); + Point2D firstBaseIn = new Point2D.Double( + firstIn.getX() + depth.getX(), + firstIn.getY() + depth.getY()); + firstPath = new GeoPath(new Line2D.Double(firstIn, firstExt)); + firstPath.append(new Line2D.Double(firstBase, firstBaseIn), true); + + Point2D lastIn = extension(lastExt, last, params.coverageVIn); + Point2D lastBaseIn = new Point2D.Double( + lastIn.getX() + depth.getX(), + lastIn.getY() + depth.getY()); + lastPath = new GeoPath(new Line2D.Double(lastIn, lastExt)); + lastPath.append(new Line2D.Double(lastBase, lastBaseIn), true); + } else { + // Small slur, just one slanted separation + firstPath = new GeoPath(new Line2D.Double(mid, firstExt)); + lastPath = new GeoPath(new Line2D.Double(mid, lastExt)); + + Line2D bisector = (vDir == hDir) ? bisector(first, last) : bisector(last, first); + Point2D baseInter = intersection(baseLine, bisector); + firstPath.append(new Line2D.Double(firstBase, baseInter), true); + lastPath.append(new Line2D.Double(lastBase, baseInter), true); + } + } + + firstPath.closePath(); + lastPath.closePath(); + + Map areaMap = new EnumMap<>(HorizontalSide.class); + Area firstArea = new Area(firstPath); + ///info.setArea(firstArea, true); + areaMap.put(LEFT, firstArea); + slur.addAttachment("F", firstArea); + + Area lastArea = new Area(lastPath); + ///info.setArea(lastArea, false); + areaMap.put(RIGHT, lastArea); + slur.addAttachment("L", lastArea); + + return areaMap; + } + + //----------------// + // lookupLinkPair // + //----------------// + /** + * Look precisely at content of lookup areas on left and right, and select best + * pair of links if any. + *

                                                              + *
                                                            • The same chord (including mirror) cannot belong to both areas.
                                                            • + *
                                                            • Left and right chords must differ enough in abscissa.
                                                            • + *
                                                            • To be really accepted, a chord candidate must contain a head suitable to be linked on + * proper slur side.
                                                            • + *
                                                            • Special heuristics for mirrored chords: + *
                                                                + *
                                                              • If the slur goes to another staff, select the mirror chord whose stem points towards + * the other staff.
                                                              • + *
                                                              • If the slur stays in its staff, select the mirror chord with same stem direction as + * the chord found at the other slur end. + * If there is no chord as the other slur end, select the mirror chord with the smallest + * number of beams.
                                                              • + *
                                                              + *
                                                            + * Possible orphan slurs are accepted for those close to staff side and rather horizontal. + * The other slurs must exhibit links on both sides. + * + * @param slur the slur candidate to check for links + * @param areas the lookup area on each slur side + * @param system the containing system + * @param chords the potential candidate chords on left and right side + * @return the pair of links if acceptable (only half-filled for orphan), null if not + */ + public Map lookupLinkPair (SlurInter slur, + Map areas, + SystemInfo system, + Map> chords) + { + // The pair to populate + final Map linkPair = new EnumMap<>(HorizontalSide.class); + + // Slur target locations on each side + final Point leftTarget = getTargetPoint(slur, LEFT); + final Point rightTarget = getTargetPoint(slur, RIGHT); + + // Chords candidates on each side + Map lefts = lookup(slur, LEFT, areas.get(LEFT), chords.get(LEFT)); + Map rights = lookup(slur, RIGHT, areas.get(RIGHT), chords.get(RIGHT)); + + // The same chord cannot be linked to both slur ends + // Keep it only where it is closer to slur target + Set commons = new LinkedHashSet<>(); + commons.addAll(lefts.keySet()); + commons.retainAll(rights.keySet()); + + for (Inter common : commons) { + Rectangle chordBox = common.getBounds(); + Point chordCenter = GeoUtil.centerOf(chordBox); // TODO: choose a better ref point? + + if (chordCenter.distance(leftTarget) > chordCenter.distance(rightTarget)) { + lefts.remove(common); + } else { + rights.remove(common); + } + } + + // Reduce each side, except for couple [chord / mirrored chord] + Map mirrors = new EnumMap<>(HorizontalSide.class); + + for (HorizontalSide side : HorizontalSide.values()) { + Map links = (side == LEFT) ? lefts : rights; + List list = new ArrayList<>(links.values()); + + if (!list.isEmpty()) { + Collections.sort(list, SlurHeadLink.byEuclidean); + + SlurHeadLink best = list.get(0); + + // Mirror? + Inter mirror = best.getChord().getMirror(); + + if ((mirror != null) && links.keySet().contains(mirror)) { + mirrors.put(side, best); // We have a conflict to solve + } else { + linkPair.put(side, best); // The best link is OK + } + } + } + + // Process mirrors conflicts if any + if (!mirrors.isEmpty()) { + for (Entry entry : mirrors.entrySet()) { + // This side of the slur + final HorizontalSide side = entry.getKey(); + final SlurHeadLink link = entry.getValue(); + final Map links = (side == LEFT) ? lefts : rights; + final SlurHeadLink mirrorLink = links.get(link.getChord().getMirror()); + final boolean linkOk; + + // The other side of the slur + final HorizontalSide otherSide = side.opposite(); + final SlurHeadLink otherLink = linkPair.get(otherSide); + + if (otherLink != null) { + // Compare with other side of the slur + final Staff otherStaff = otherLink.getChord().getTopStaff(); + final Staff staff = link.getChord().getTopStaff(); + final int dir = link.getChord().getStemDir(); + + if (staff != otherStaff) { + // Select mirror according to direction to other staff + linkOk = (dir * Staff.byId.compare(otherStaff, staff)) > 0; + } else { + // Select mirror with same stem dir as at other slur end + // (This may be called into question later when looking for ties) + int otherDir = otherLink.getChord().getStemDir(); + linkOk = dir == otherDir; + } + } else { + // No link found on other side (orphan? or slur truncated?) + // Not too stupid: select the mirror with less beams + final SIGraph sig = system.getSig(); + Inter stem = link.getChord().getStem(); + int nb = sig.getRelations(stem, BeamStemRelation.class).size(); + Inter mStem = mirrorLink.getChord().getStem(); + int mNb = sig.getRelations(mStem, BeamStemRelation.class).size(); + linkOk = nb <= mNb; + } + + linkPair.put(side, linkOk ? link : mirrorLink); + } + } + + // Check we don't have a chord on one slur side and its mirror on the other side + SlurHeadLink leftLink = linkPair.get(LEFT); + SlurHeadLink rightLink = linkPair.get(RIGHT); + + if ((leftLink != null) && (rightLink != null)) { + Inter leftMirror = leftLink.getChord().getMirror(); + + if ((leftMirror != null) && (leftMirror == rightLink.getChord())) { + logger.debug("{} chord and its mirror linked by the same slur!", slur); + + return null; + } + } + + // No link on left and on right? + if (linkPair.isEmpty()) { + return null; + } + + // One link is missing, check whether this slur candidate can be an orphan + for (HorizontalSide side : HorizontalSide.values()) { + if ((linkPair.get(side) == null) && !canBeOrphan(slur, side, system)) { + return null; // TODO: Too strict for manual usage + } + } + + return linkPair; + } + + //-------------// + // canBeOrphan // + //-------------// + /** + * Check whether the provided slur can be a legal orphan on the specified side. + * + * @param slur the slur to check + * @param side which side is orphaned + * @param system containing system + * @return true if legal + */ + private boolean canBeOrphan (SlurInter slur, + HorizontalSide side, + SystemInfo system) + { + final SlurInfo info = slur.getInfo(); + + // Check if slur is rather horizontal + if (abs(LineUtil.getSlope(info.getEnd(true), info.getEnd(false))) > params.maxOrphanSlope) { + logger.debug("{} too sloped orphan", slur); + + return false; + } + + // A left orphan must start in first measure. + // A right orphan must stop in last measure. + Point slurEnd = info.getEnd(side == LEFT); + Staff staff = system.getClosestStaff(slurEnd); + Part part = staff.getPart(); + Measure sideMeasure = (side == LEFT) ? part.getFirstMeasure() : part.getLastMeasure(); + Measure endMeasure = part.getMeasureAt(slurEnd); + + if (endMeasure != sideMeasure) { + logger.debug("{} orphan side not in part side measure", slur); + + return false; + } + + // Also, check horizontal gap to staff limit + int staffEnd = (side == LEFT) ? staff.getHeaderStop() : staff.getAbscissa(side); + + if (abs(slurEnd.x - staffEnd) > params.maxOrphanDx) { + logger.debug("{} too far orphan", slur); + + return false; + } + + return true; + } + + //----------------// + // getTargetPoint // + //----------------// + /** + * Report the precise target point for a head connection on desired side of a slur. + * + * @param slur the slur to process + * @param side the desired slur side + * @return the target connection point, slightly away from slur end + */ + private Point getTargetPoint (SlurInter slur, + HorizontalSide side) + { + final boolean rev = side == LEFT; + final SlurInfo info = slur.getInfo(); + final Point end = info.getEnd(rev); + final Point2D vector = info.getEndVector(rev); + final double ext = params.targetExtension; + + return PointUtil.rounded(PointUtil.addition(end, PointUtil.times(vector, ext))); + } + + //--------// + // lookup // + //--------// + /** + * Retrieve the best head embraced by the slur side. + * + * @param slur the provided slur + * @param side desired side + * @param area lookup area on slur side + * @param chords candidate chords on desired side + * @return the map of heads found, perhaps empty, with their data + */ + private Map lookup (SlurInter slur, + HorizontalSide side, + Area area, + List chords) + { + final Map found = new HashMap<>(); + final SlurInfo info = slur.getInfo(); + final Point end = info.getEnd(side == LEFT); + final Point target = getTargetPoint(slur, side); + final Point2D bisUnit = info.getBisUnit(); + + // Look for intersected chords + for (Inter chordInter : chords) { + AbstractChordInter chord = (AbstractChordInter) chordInter; + Rectangle chordBox = chord.getBounds(); + + if (area.intersects(chordBox)) { + // Check the chord contains at least one suitable head on desired slur side + HeadInter head = selectBestHead(slur, chord, end, target, bisUnit, area); + + if (head != null) { + found.put(chord, SlurHeadLink.create(target, side, chord, head)); + } + } + } + + return found; + } + + //----------------// + // selectBestHead // + //----------------// + /** + * Select the best note head in the selected head-based chord. + * We select the compatible note head which is closest to slur target end. + * + * @param chord the selected chord + * @param end the slur end point + * @param target the slur target point + * @param bisUnit the direction from slur middle to slur center (unit length) + * @param area target area + * @return the best note head or null + */ + private HeadInter selectBestHead (SlurInter slur, + AbstractChordInter chord, + Point end, + Point target, + Point2D bisUnit, + Area area) + { + final boolean horizontal = slur.getInfo().isHorizontal(); + final boolean above = slur.isAbove(); + + double bestDist = Double.MAX_VALUE; + HeadInter bestHead = null; + + for (Inter head : chord.getNotes()) { + Point center = head.getCenter(); + + if (!horizontal) { + // We require head center to be contained by lookup area + if (!area.contains(center)) { + continue; + } + } + + // Check head reference point WRT slur concavity + Rectangle bounds = head.getBounds(); + Point refPt = new Point(center.x, bounds.y + (above ? (bounds.height - 1) : 0)); + + if (dotProduct(subtraction(refPt, end), bisUnit) <= 0) { + continue; + } + + // Keep the closest head + final double dist = center.distanceSq(target); + + if (dist < bestDist) { + bestDist = dist; + bestHead = (HeadInter) head; + } + } + + return bestHead; + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Scale.Fraction coverageHExt = new Scale.Fraction( + 1.25, + "Length of extension for horizontal slur coverage"); + + private final Scale.Fraction coverageHIn = new Scale.Fraction( + 0.5, + "Internal abscissa of horizontal slur coverage"); + + private final Scale.Fraction coverageHDepth = new Scale.Fraction( + 3.0, + "Vertical extension of horizontal slur coverage"); + + private final Scale.Fraction coverageVExt = new Scale.Fraction( + 2.0, + "Length of extension for vertical slur coverage"); + + private final Scale.Fraction coverageVIn = new Scale.Fraction( + 1.5, + "Internal abscissa of vertical slur coverage"); + + private final Scale.Fraction coverageVDepth = new Scale.Fraction( + 2.5, + "Vertical extension of vertical slur coverage"); + + private final Scale.Fraction coverageVDepthSmall = new Scale.Fraction( + 1.5, + "Vertical extension of small vertical slur coverage"); + + private final Scale.Fraction targetExtension = new Scale.Fraction( + 0.5, + "Extension length from slur end to slur target point"); + + private final Constant.Double slopeSeparator = new Constant.Double( + "tangent", + 0.5, + "Slope that separates vertical slurs from horizontal slurs"); + + private final Constant.Double maxOrphanSlope = new Constant.Double( + "tangent", + 0.5, + "Maximum slope for an orphan slur"); + + private final Scale.Fraction maxOrphanDx = new Scale.Fraction( + 6.0, + "Maximum dx to staff end for an orphan slur"); + + private final Scale.Fraction wideSlurWidth = new Scale.Fraction( + 6.0, + "Minimum width to be a wide slur"); + + private final Scale.Fraction maxSmallSlurWidth = new Scale.Fraction( + 1.5, + "Maximum width for a small slur"); + } + + //------------// + // Parameters // + //------------// + /** + * All pre-scaled constants. + */ + private static class Parameters + { + + final int coverageHExt; + + final int coverageVExt; + + final int coverageHIn; + + final int coverageVIn; + + final int coverageHDepth; + + final int coverageVDepth; + + final int coverageVDepthSmall; + + final int targetExtension; + + final double slopeSeparator; + + final double maxOrphanSlope; + + final int maxOrphanDx; + + final int wideSlurWidth; + + final int maxSmallSlurWidth; + + /** + * Creates a new Parameters object. + * + * @param scale the scaling factor + */ + Parameters (Scale scale) + { + coverageHExt = scale.toPixels(constants.coverageHExt); + coverageHIn = scale.toPixels(constants.coverageHIn); + coverageHDepth = scale.toPixels(constants.coverageHDepth); + coverageVExt = scale.toPixels(constants.coverageVExt); + coverageVIn = scale.toPixels(constants.coverageVIn); + coverageVDepth = scale.toPixels(constants.coverageVDepth); + coverageVDepthSmall = scale.toPixels(constants.coverageVDepthSmall); + targetExtension = scale.toPixels(constants.targetExtension); + slopeSeparator = constants.slopeSeparator.getValue(); + maxOrphanSlope = constants.maxOrphanSlope.getValue(); + maxOrphanDx = scale.toPixels(constants.maxOrphanDx); + wideSlurWidth = scale.toPixels(constants.wideSlurWidth); + maxSmallSlurWidth = scale.toPixels(constants.maxSmallSlurWidth); + + if (logger.isDebugEnabled()) { + new Dumping().dump(this); + } + } + } +} diff --git a/src/main/org/audiveris/omr/sheet/curve/SlursBuilder.java b/src/main/org/audiveris/omr/sheet/curve/SlursBuilder.java index 52027c992..988244f49 100644 --- a/src/main/org/audiveris/omr/sheet/curve/SlursBuilder.java +++ b/src/main/org/audiveris/omr/sheet/curve/SlursBuilder.java @@ -80,7 +80,6 @@ public class SlursBuilder extends CurvesBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -92,7 +91,6 @@ public class SlursBuilder private static final Color SLUR_MODELS = new Color(255, 255, 0, 100); - //~ Instance fields ---------------------------------------------------------------------------- /** Scale-dependent parameters. */ private final Parameters params; @@ -100,15 +98,14 @@ public class SlursBuilder private final ClumpPruner clumpPruner; /** All slur informations created. */ - private final List pageInfos = new ArrayList(); + private final List pageInfos = new ArrayList<>(); /** All slur inters retrieved. */ - private final List pageSlurs = new ArrayList(); + private final List pageSlurs = new ArrayList<>(); /** Current maximum length for arcs to be tried. */ private Integer maxLength = null; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SlursBuilder object. * @@ -122,10 +119,12 @@ public SlursBuilder (Curves curves) params = new Parameters(sheet.getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // buildSlurs // //------------// + /** + * Build slurs for the system. + */ public void buildSlurs () { try { @@ -150,7 +149,7 @@ public void buildSlurs () } } - // Handle tie collisions on same chord, although ties are not fully known + // Handle tie collisions on same chord, although ties are not fully known // (because alterations & clefs have not been handled yet) handleTieCollisions(); @@ -202,7 +201,7 @@ public void renderItems (Graphics2D g) g.setColor(SLUR_CURVES); Stroke lineStroke = new BasicStroke( - (float) sheet.getScale().getFore(), + sheet.getScale().getFore(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); g.setStroke(lineStroke); @@ -382,7 +381,7 @@ protected SlurInter.Impacts computeImpacts (Curve curve, } double angleImpact = (params.maxArcAngleHigh - arcAngle) / (params.maxArcAngleHigh - - params.maxArcAngleLow); + - params.maxArcAngleLow); // Features below are relevant only for full slur evaluations if (!bothSides) { @@ -396,7 +395,7 @@ protected SlurInter.Impacts computeImpacts (Curve curve, midAngle += (2 * PI); } - midAngle = midAngle % PI; + midAngle %= PI; double fromVertical = min(abs(midAngle), abs(PI - midAngle)); @@ -406,8 +405,9 @@ protected SlurInter.Impacts computeImpacts (Curve curve, return null; } - double vertImpact = (fromVertical - params.minAngleFromVerticalLow) / (params.minAngleFromVerticalHigh - - params.minAngleFromVerticalLow); + double vertImpact = (fromVertical - params.minAngleFromVerticalLow) + / (params.minAngleFromVerticalHigh + - params.minAngleFromVerticalLow); List points = slur.getPoints(); Point p0 = points.get(0); @@ -424,7 +424,7 @@ protected SlurInter.Impacts computeImpacts (Curve curve, } double widthImpact = (width - params.minSlurWidthLow) / (params.minSlurWidthHigh - - params.minSlurWidthLow); + - params.minSlurWidthLow); // Slur high enough (bent enough) double height = Line2D.ptLineDist(p0.x, p0.y, p2.x, p2.y, p1.x, p1.y); @@ -436,7 +436,7 @@ protected SlurInter.Impacts computeImpacts (Curve curve, } double heightImpact = (height - params.minSlurHeightLow) / (params.minSlurHeightHigh - - params.minSlurHeightLow); + - params.minSlurHeightLow); return new SlurInter.Impacts( distImpact, @@ -492,8 +492,9 @@ protected Model computeModel (List points, final double r1 = rough.getRadius(); final double r2 = fitted.getRadius(); - if (Double.isInfinite(r1) - || ((abs(r1 - r2) / (max(r1, r2))) <= params.similarRadiusRatio)) { + if (Double.isInfinite(r1) || ((abs(r1 - r2) / (max( + r1, + r2))) <= params.similarRadiusRatio)) { circle = fitted; dist = circle.getDistance(); } else { @@ -551,9 +552,8 @@ protected void createInter (Curve seq, SlurInfo slur = (SlurInfo) seq; GradeImpacts impacts = computeImpacts(slur, true); - if ((impacts != null) - && (impacts.getGrade() >= SlurInter.getMinGrade()) - && (slur.getCurve() != null)) { + if ((impacts != null) && (impacts.getGrade() >= SlurInter.getMinGrade()) && (slur + .getCurve() != null)) { slur.retrieveGlyph(sheet, params.maxRunDistance); if (slur.getGlyph() != null) { @@ -609,7 +609,7 @@ protected void pruneClump (Set clump) protected void weed (Set clump) { // Compute grades - List inters = new ArrayList(); + List inters = new ArrayList<>(); for (Curve seq : clump) { SlurInfo slur = (SlurInfo) seq; @@ -722,7 +722,7 @@ private Rectangle getBounds (Set slurs) */ private List getSeedArcs () { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (Arc arc : skeleton.arcsMap.values()) { if (arc.getLength() >= params.arcMinSeedLength) { @@ -730,7 +730,7 @@ private List getSeedArcs () } } - List list = new ArrayList(set); + List list = new ArrayList<>(set); Collections.sort(list, Arc.byReverseLength); return list; @@ -809,7 +809,7 @@ private void handleTieCollisions () for (HorizontalSide slurSide : HorizontalSide.values()) { // Count ties for this chord on selected *slur* side - final Set ties = new LinkedHashSet(); + final Set ties = new LinkedHashSet<>(); for (Inter nInter : chord.getNotes()) { for (Relation rel : sig.getRelations(nInter, SlurHeadRelation.class)) { @@ -828,7 +828,7 @@ private void handleTieCollisions () if (ties.size() > 1) { HorizontalSide oppSide = slurSide.opposite(); Map> origins; - origins = new LinkedHashMap>(); + origins = new LinkedHashMap<>(); // Check whether the ties are linked to different chords for (SlurInter tie : ties) { @@ -841,7 +841,7 @@ private void handleTieCollisions () List list = origins.get(ch); if (list == null) { - origins.put(ch, list = new ArrayList()); + origins.put(ch, list = new ArrayList<>()); } list.add(tie); @@ -884,7 +884,7 @@ private void purgeIdenticalEndings (List inters) SlurInter slur = inters.get(i); Point end = slur.getInfo().getEnd(reverse); - List toDelete = new ArrayList(); + List toDelete = new ArrayList<>(); for (SlurInter otherSlur : inters.subList(i + 1, inters.size())) { Point otherEnd = otherSlur.getInfo().getEnd(reverse); @@ -954,7 +954,7 @@ private SlurInter purgeShortests (List inters) */ private void purgeStaffLines (List inters) { - List toDelete = new ArrayList(); + List toDelete = new ArrayList<>(); for (SlurInter inter : inters) { SlurInfo slur = inter.getInfo(); @@ -1071,14 +1071,12 @@ private void register (Set clump) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio similarRadiusRatio = new Constant.Ratio( 0.25, @@ -1186,7 +1184,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final double similarRadiusRatio; @@ -1234,13 +1231,12 @@ private static class Parameters final double maxRunDistance; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Parameters object. * * @param scale the scaling factor */ - public Parameters (Scale scale) + Parameters (Scale scale) { similarRadiusRatio = constants.similarRadiusRatio.getValue(); tangentLength = scale.toPixels(constants.tangentLength); diff --git a/src/main/org/audiveris/omr/sheet/curve/WedgesBuilder.java b/src/main/org/audiveris/omr/sheet/curve/WedgesBuilder.java index 97357ba7a..635e70786 100644 --- a/src/main/org/audiveris/omr/sheet/curve/WedgesBuilder.java +++ b/src/main/org/audiveris/omr/sheet/curve/WedgesBuilder.java @@ -55,13 +55,11 @@ */ public class WedgesBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(WedgesBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The related sheet. */ @Navigable(false) protected final Sheet sheet; @@ -72,7 +70,6 @@ public class WedgesBuilder /** Scale-dependent parameters. */ private final Parameters params; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new WedgesBuilder object. * @@ -85,7 +82,6 @@ public WedgesBuilder (Curves curves) params = new Parameters(sheet.getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // buildWedges // //-------------// @@ -246,14 +242,12 @@ private Rectangle getArea (SegmentInfo segment, return rect; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction closedMaxDx = new Scale.Fraction( 0.2, @@ -285,7 +279,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int closedMaxDx; @@ -297,8 +290,7 @@ private static class Parameters final double openMaxBias; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale) + Parameters (Scale scale) { closedMaxDx = scale.toPixels(constants.closedMaxDx); closedMaxDy = scale.toPixels(constants.closedMaxDy); @@ -311,11 +303,4 @@ public Parameters (Scale scale) } } } - - //-----------// - // WedgeInfo // - //-----------// - private class WedgeInfo - { - } } diff --git a/src/main/org/audiveris/omr/sheet/grid/BarAlignment.java b/src/main/org/audiveris/omr/sheet/grid/BarAlignment.java index 5ecf51895..621813686 100644 --- a/src/main/org/audiveris/omr/sheet/grid/BarAlignment.java +++ b/src/main/org/audiveris/omr/sheet/grid/BarAlignment.java @@ -21,7 +21,6 @@ // package org.audiveris.omr.sheet.grid; -import org.audiveris.omr.sig.BasicImpacts; import org.audiveris.omr.sig.GradeImpacts; import org.audiveris.omr.util.VerticalSide; @@ -37,7 +36,6 @@ public class BarAlignment implements Comparable { - //~ Instance fields ---------------------------------------------------------------------------- /** Bar peak in the upper staff. */ protected final StaffPeak topPeak; @@ -57,7 +55,6 @@ public class BarAlignment /** Alignment grade. */ protected double grade; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BarAlignment object. * @@ -82,44 +79,6 @@ public BarAlignment (StaffPeak topPeak, grade = impacts.getGrade(); } - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // bestOf // - //--------// - /** - * Report the best (connection or alignment) among the provided collection. - * - * @param alignments the collection to filter - * @param side which side of alignment to consider - * @return the best one, or null if collection is empty - */ - public static BarAlignment bestOf (Collection alignments, - VerticalSide side) - { - BarAlignment best = null; - double bestCtxGrade = 0; - - for (final BarAlignment align : alignments) { - final StaffPeak partner = (side == VerticalSide.TOP) ? align.topPeak : align.bottomPeak; - final double ctxGrade = align.getGrade() * partner.getImpacts().getGrade(); - - if (best == null) { - best = align; - bestCtxGrade = ctxGrade; - } else if (best instanceof BarConnection) { - if (align instanceof BarConnection && (ctxGrade > bestCtxGrade)) { - best = align; - bestCtxGrade = ctxGrade; - } - } else if (align instanceof BarConnection || (ctxGrade > bestCtxGrade)) { - best = align; - bestCtxGrade = ctxGrade; - } - } - - return best; - } - //-----------// // compareTo // //-----------// @@ -164,6 +123,11 @@ public boolean equals (Object obj) //----------// // getGrade // //----------// + /** + * Report quality of this alignment. + * + * @return alignment grade value + */ public double getGrade () { return grade; @@ -172,6 +136,11 @@ public double getGrade () //------------// // getImpacts // //------------// + /** + * Report evaluation details. + * + * @return the grade detailed impacts + */ public GradeImpacts getImpacts () { return impacts; @@ -180,6 +149,12 @@ public GradeImpacts getImpacts () //---------// // getPeak // //---------// + /** + * Report the peak on desired vertical side of this peak alignment. + * + * @param side desired vertical side + * @return top or bottom peak + */ public StaffPeak getPeak (VerticalSide side) { if (side == VerticalSide.TOP) { @@ -221,6 +196,11 @@ public String toString () //-----------// // internals // //-----------// + /** + * String description of internals. + * + * @return description of internals + */ protected String internals () { StringBuilder sb = new StringBuilder(); @@ -230,20 +210,63 @@ protected String internals () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------// + // bestOf // + //--------// + /** + * Report the best (connection or alignment) among the provided collection. + * + * @param alignments the collection to filter + * @param side which side of alignment to consider + * @return the best one, or null if collection is empty + */ + public static BarAlignment bestOf (Collection alignments, + VerticalSide side) + { + BarAlignment best = null; + double bestCtxGrade = 0; + + for (final BarAlignment align : alignments) { + final StaffPeak partner = (side == VerticalSide.TOP) ? align.topPeak : align.bottomPeak; + final double ctxGrade = align.getGrade() * partner.getImpacts().getGrade(); + + if (best == null) { + best = align; + bestCtxGrade = ctxGrade; + } else if (best instanceof BarConnection) { + if (align instanceof BarConnection && (ctxGrade > bestCtxGrade)) { + best = align; + bestCtxGrade = ctxGrade; + } + } else if (align instanceof BarConnection || (ctxGrade > bestCtxGrade)) { + best = align; + bestCtxGrade = ctxGrade; + } + } + + return best; + } + //---------// // Impacts // //---------// + /** + * Evaluation details. + */ public static class Impacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{"align", "dWidth"}; private static final double[] WEIGHTS = new double[]{2, 1}; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create Impacts. + * + * @param align alignment contribution + * @param dWidth width contribution + */ public Impacts (double align, double dWidth) { @@ -252,12 +275,21 @@ public Impacts (double align, setImpact(1, dWidth); } - //~ Methods -------------------------------------------------------------------------------- + /** + * Report contribution of alignment. + * + * @return impact of alignment + */ public double getAlignImpact () { return impacts[0]; } + /** + * Report contribution of width. + * + * @return impact of width + */ public double getWidthImpact () { return impacts[1]; diff --git a/src/main/org/audiveris/omr/sheet/grid/BarColumn.java b/src/main/org/audiveris/omr/sheet/grid/BarColumn.java index 99b0240f4..886351353 100644 --- a/src/main/org/audiveris/omr/sheet/grid/BarColumn.java +++ b/src/main/org/audiveris/omr/sheet/grid/BarColumn.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.TreeSet; /** @@ -52,11 +53,9 @@ */ public class BarColumn { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(BarColumn.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The sheet graph of peaks. */ private final PeakGraph peakGraph; @@ -72,7 +71,6 @@ public class BarColumn /** Mean width. */ private Double width; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code BarColumn} object. * @@ -87,10 +85,14 @@ public BarColumn (SystemInfo system, peaks = new StaffPeak[system.getStaves().size()]; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // addChain // //----------// + /** + * Include all the peaks of a chain into column. + * + * @param chain the provided chain of peaks + */ public void addChain (Chain chain) { for (StaffPeak peak : chain) { @@ -101,6 +103,11 @@ public void addChain (Chain chain) //---------// // addPeak // //---------// + /** + * Include a peak. + * + * @param peak the peak to include + */ public void addPeak (StaffPeak peak) { int idx = peak.getStaff().getId() - system.getFirstStaff().getId(); @@ -115,6 +122,12 @@ public void addPeak (StaffPeak peak) //------------// // canInclude // //------------// + /** + * Report whether chain would be compatible with this column. + * + * @param chain the chain to check for compatibility + * @return true if OK + */ public boolean canInclude (Chain chain) { // Make sure we have room for the provided chain candidate @@ -134,6 +147,11 @@ public boolean canInclude (Chain chain) //----------// // getPeaks // //----------// + /** + * Report the array of peaks. + * + * @return the peaks + */ public StaffPeak[] getPeaks () { return peaks; @@ -142,6 +160,11 @@ public StaffPeak[] getPeaks () //----------// // getWidth // //----------// + /** + * Report average barline width within the column. + * + * @return the mean width of column barlines + */ public double getWidth () { if (width == null) { @@ -164,6 +187,11 @@ public double getWidth () //---------// // getXDsk // //---------// + /** + * Report the average de-skewed abscissa. + * + * @return mean (de-skewed) abscissa on all column peaks. + */ public double getXDsk () { if (xDsk == null) { @@ -186,6 +214,11 @@ public double getXDsk () //--------// // isFull // //--------// + /** + * Tell whether the column has no void room. + * + * @return true if full + */ public boolean isFull () { return computeStatus(); @@ -256,7 +289,7 @@ public String toString () if (i > 0) { // Print link if any - BarAlignment link = (BarAlignment) peakGraph.getEdge(peaks[i - 1], peak); + BarAlignment link = peakGraph.getEdge(peaks[i - 1], peak); if (link == null) { sb.append(" X "); @@ -289,31 +322,43 @@ private boolean computeStatus () return nb == peaks.length; } - //~ Inner Classes ------------------------------------------------------------------------------ //-------// // Chain // //-------// /** - * A chain represent a vertical sequence of peaks linked by alignment or connection. + * A chain represents a vertical sequence of peaks linked by alignment or connection. */ public static class Chain extends TreeSet - implements Comparable { - //~ Constructors --------------------------------------------------------------------------- + /** To sort by (de-skewed) abscissa. */ + public static final Comparator byAbscissa = new Comparator() + { + @Override + public int compare (Chain c1, + Chain c2) + { + return Double.compare( + c1.first().getDeskewedAbscissa(), + c2.first().getDeskewedAbscissa()); + } + }; + + /** + * Create a Chain out of peaks. + * + * @param peaks the peaks that compose a chain + */ public Chain (Collection peaks) { addAll(peaks); } - //~ Methods -------------------------------------------------------------------------------- @Override - public int compareTo (Chain that) + public Object clone () { - return Double.compare( - this.first().getDeskewedAbscissa(), - that.first().getDeskewedAbscissa()); + return super.clone(); //To change body of generated methods, choose Tools | Templates. } } } diff --git a/src/main/org/audiveris/omr/sheet/grid/BarConnection.java b/src/main/org/audiveris/omr/sheet/grid/BarConnection.java index 41abc99e5..071abb162 100644 --- a/src/main/org/audiveris/omr/sheet/grid/BarConnection.java +++ b/src/main/org/audiveris/omr/sheet/grid/BarConnection.java @@ -22,7 +22,7 @@ package org.audiveris.omr.sheet.grid; import org.audiveris.omr.math.AreaUtil; -import org.audiveris.omr.sig.BasicImpacts; +import org.audiveris.omr.sig.GradeImpacts; import java.awt.geom.Area; import java.awt.geom.Line2D; @@ -38,7 +38,6 @@ public class BarConnection extends BarAlignment { - //~ Instance fields ---------------------------------------------------------------------------- /** Physical portion of the connection line, excluding portions within staves. */ private Area area; @@ -46,7 +45,6 @@ public class BarConnection /** Rather vertical median line. */ private Line2D median; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BarConnection object. * @@ -63,13 +61,34 @@ public BarConnection (BarAlignment align, align.bottomPeak, align.slope, align.dWidth, - new Impacts((BarAlignment.Impacts) align.getImpacts(), gapImpact, whiteImpact)); + new Impacts( + ((BarAlignment.Impacts) align.getImpacts()).getAlignImpact(), + ((BarAlignment.Impacts) align.getImpacts()).getWidthImpact(), + gapImpact, + whiteImpact)); + } + + //--------// + // equals // + //--------// + @Override + public boolean equals (Object obj) + { + if (obj instanceof BarConnection) { + return super.equals(obj); + } + + return false; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getArea // //---------// + /** + * Report the underlying area. + * + * @return connector area + */ public Area getArea () { if (area == null) { @@ -79,9 +98,14 @@ public Area getArea () return area; } - //-----------/ + //------------/ // getMedian // - //-----------/ + //------------/ + /** + * Report the defining line of this connection. + * + * @return connection line + */ public Line2D getMedian () { if (median == null) { @@ -96,25 +120,47 @@ public Line2D getMedian () //----------// // getWidth // //----------// + /** + * Report the average with of the connector. + * + * @return width in pixels + */ public double getWidth () { return (topPeak.getWidth() + bottomPeak.getWidth()) / 2d; } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------// + // hashCode // + //----------// + @Override + public int hashCode () + { + return super.hashCode(); + } + //---------// // Impacts // //---------// + /** + * Evaluation details as a connector. + */ public static class Impacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{"align", "dWidth", "gap", "white"}; private static final double[] WEIGHTS = new double[]{2, 1, 2, 2}; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create Impacts. + * + * @param align impact for alignment + * @param dWidth impact for width consistency + * @param gap impact for maximum vertical gap + * @param white impact for ratio of white + */ public Impacts (double align, double dWidth, double gap, @@ -126,12 +172,5 @@ public Impacts (double align, setImpact(2, gap); setImpact(3, white); } - - public Impacts (BarAlignment.Impacts alignImpacts, - double gap, - double white) - { - this(alignImpacts.getAlignImpact(), alignImpacts.getWidthImpact(), gap, white); - } } } diff --git a/src/main/org/audiveris/omr/sheet/grid/BarFilamentBuilder.java b/src/main/org/audiveris/omr/sheet/grid/BarFilamentBuilder.java index 2281b994c..b0b5dcf5e 100644 --- a/src/main/org/audiveris/omr/sheet/grid/BarFilamentBuilder.java +++ b/src/main/org/audiveris/omr/sheet/grid/BarFilamentBuilder.java @@ -38,7 +38,6 @@ */ public class BarFilamentBuilder { - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ @Navigable(false) @@ -47,7 +46,6 @@ public class BarFilamentBuilder /** Specific factory for peak-based filaments. */ private final BarFilamentFactory factory; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code BarFilamentBuilder} object. * @@ -60,7 +58,6 @@ public BarFilamentBuilder (Sheet sheet) factory = new BarFilamentFactory(sheet.getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // buildFilament // //---------------// @@ -82,7 +79,7 @@ public Filament buildFilament (StaffPeak peak, peakBox.grow(0, verticalExtension); final int xBreak = peakBox.x + peakBox.width; - final List
                                                            sections = new ArrayList
                                                            (); + final List
                                                            sections = new ArrayList<>(); final int maxSectionWidth = peak.getWidth(); // Width of this particular peak for (Section section : allSections) { diff --git a/src/main/org/audiveris/omr/sheet/grid/BarFilamentFactory.java b/src/main/org/audiveris/omr/sheet/grid/BarFilamentFactory.java index 678675155..6e1e51fad 100644 --- a/src/main/org/audiveris/omr/sheet/grid/BarFilamentFactory.java +++ b/src/main/org/audiveris/omr/sheet/grid/BarFilamentFactory.java @@ -57,20 +57,17 @@ */ public class BarFilamentFactory { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(BarFilamentFactory.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related scale. */ private final Scale scale; /** Scale-dependent constants. */ private final Parameters params; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code BarFilamentFactory} object. * @@ -82,7 +79,6 @@ public BarFilamentFactory (Scale scale) params = new Parameters(scale); } - //~ Methods ------------------------------------------------------------------------------------ //------------------// // buildBarFilament // //------------------// @@ -150,7 +146,7 @@ private void expandFilament (Filament fil, Rectangle core, Collection
                                                            source) { - final List
                                                            sections = new ArrayList
                                                            (source); + final List
                                                            sections = new ArrayList<>(source); sections.removeAll(fil.getMembers()); boolean expanding; @@ -216,14 +212,12 @@ private Filament populateCore (Collection
                                                            source, } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, @@ -244,16 +238,14 @@ private static final class Constants /** * Class {@code Parameters} gathers all scale-dependent parameters. */ - private class Parameters + private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ public int minCoreSectionLength; public int segmentLength; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale) + Parameters (Scale scale) { minCoreSectionLength = scale.toPixels(constants.minCoreSectionLength); segmentLength = scale.toPixels(constants.segmentLength); diff --git a/src/main/org/audiveris/omr/sheet/grid/BarsRetriever.java b/src/main/org/audiveris/omr/sheet/grid/BarsRetriever.java index 28713aba7..c9073a9b8 100644 --- a/src/main/org/audiveris/omr/sheet/grid/BarsRetriever.java +++ b/src/main/org/audiveris/omr/sheet/grid/BarsRetriever.java @@ -146,15 +146,11 @@ public class BarsRetriever implements ItemRenderer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - BarsRetriever.class); + private static final Logger logger = LoggerFactory.getLogger(BarsRetriever.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Related sheet. */ @Navigable(false) private final Sheet sheet; @@ -170,13 +166,13 @@ public class BarsRetriever private final StaffManager staffManager; /** Staff projectors. (sequence parallel to staves in sheet) */ - private final List projectors = new ArrayList(); + private final List projectors = new ArrayList<>(); /** Graph of all peaks. */ private final PeakGraph peakGraph; /** Columns of barlines, organized by system. */ - private final SortedMap> columnMap = new TreeMap>(); + private final SortedMap> columnMap = new TreeMap<>(); /** Constructor for brace compound. */ private final CompoundConstructor braceConstructor; @@ -193,7 +189,6 @@ public class BarsRetriever /** All sections suitable for a brace. */ private List
                                                            allBraceSections; - //~ Constructors ------------------------------------------------------------------------------- /** * Retrieve the bar lines of all staves. * @@ -208,22 +203,10 @@ public BarsRetriever (Sheet sheet) params = new Parameters(scale); // Specific constructors - braceConstructor = new CompoundConstructor() - { - @Override - public SectionCompound newInstance () - { - return new CurvedFilament(scale.getInterline(), params.braceSegmentLength); - } - }; - serifConstructor = new CompoundConstructor() - { - @Override - public SectionCompound newInstance () - { - return new StraightFilament(scale.getInterline()); - } - }; + braceConstructor = new CurvedFilament.Constructor( + scale.getInterline(), + params.braceSegmentLength); + serifConstructor = new StraightFilament.Constructor(scale.getInterline()); // Companions staffManager = sheet.getStaffManager(); @@ -232,7 +215,6 @@ public SectionCompound newInstance () peakGraph = new PeakGraph(sheet, projectors); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// @@ -421,7 +403,8 @@ private Filament buildBraceFilament (List portions) } // Right (bottom up) - for (ListIterator it = portions.listIterator(portions.size()); it.hasPrevious();) { + for (ListIterator it = portions.listIterator(portions.size()); it + .hasPrevious();) { StaffPeak peak = it.previous(); path.lineTo(peak.getStart() - params.braceLeftMargin, peak.getBottom() + 1); @@ -435,7 +418,7 @@ private Filament buildBraceFilament (List portions) final Area area = new Area(path); // Select sections that could be added to filaments - final List filaments = new ArrayList(); + final List filaments = new ArrayList<>(); final List
                                                            sections = getAreaSections(area, allBraceSections); for (StaffPeak peak : portions) { @@ -499,7 +482,7 @@ private void buildBraces () } if (bracePeak.isSet(BRACE_TOP)) { - List portions = new ArrayList(); + List portions = new ArrayList<>(); portions.add(bracePeak); // Look down for compatible brace portion(s): @@ -573,13 +556,13 @@ private void buildBraces () */ private void buildColumns () { - ConnectivityInspector inspector = new ConnectivityInspector( + ConnectivityInspector inspector = new ConnectivityInspector<>( peakGraph); List> sets = inspector.connectedSets(); logger.debug("sets: {}", sets.size()); // Process system per system (we have already purged cross-system links) - final SortedMap> chainMap = new TreeMap>(); + final SortedMap> chainMap = new TreeMap<>(); for (Set set : sets) { Chain chain = new Chain(set); @@ -587,15 +570,15 @@ private void buildColumns () List chainList = chainMap.get(system); if (chainList == null) { - chainMap.put(system, chainList = new ArrayList()); + chainMap.put(system, chainList = new ArrayList<>()); } chainList.add(chain); } - // Sort all chains within each system + // Sort all chains by deskewed abscissa within each system for (List chains : chainMap.values()) { - Collections.sort(chains); + Collections.sort(chains, Chain.byAbscissa); } // Try to aggregate chains into full-size columns @@ -603,7 +586,7 @@ private void buildColumns () List columns = columnMap.get(system); if (columns == null) { - columnMap.put(system, columns = new ArrayList()); + columnMap.put(system, columns = new ArrayList<>()); } final List chains = chainMap.get(system); @@ -676,22 +659,20 @@ private Filament buildSerifFilament (Staff staff, if (compounds.size() > 1) { // Sort filaments according to their distance from bar/roi vertex final Point vertex = new Point(roi.x, roi.y + ((side == TOP) ? (roi.height - 1) : 0)); - Collections.sort( - compounds, - new Comparator() - { - @Override - public int compare (SectionCompound g1, - SectionCompound g2) - { - double d1 = PointUtil.length( - GeoUtil.vectorOf(g1.getCentroid(), vertex)); - double d2 = PointUtil.length( - GeoUtil.vectorOf(g2.getCentroid(), vertex)); - - return Double.compare(d1, d2); - } - }); + Collections.sort(compounds, new Comparator() + { + @Override + public int compare (SectionCompound g1, + SectionCompound g2) + { + double d1 = PointUtil + .length(GeoUtil.vectorOf(g1.getCentroid(), vertex)); + double d2 = PointUtil + .length(GeoUtil.vectorOf(g2.getCentroid(), vertex)); + + return Double.compare(d1, d2); + } + }); // Pickup the first ones and stop as soon as minimum weight is reached int totalWeight = 0; @@ -755,7 +736,8 @@ private void createConnectionInters () sig.addVertex( connector = new BarConnectorInter( connection, - topPeak.isSet(THICK) ? Shape.THICK_CONNECTOR : Shape.THIN_CONNECTOR, + topPeak.isSet(THICK) ? Shape.THICK_CONNECTOR + : Shape.THIN_CONNECTOR, connection.getImpacts())); } @@ -803,7 +785,7 @@ private void createGroups () logger.debug("createGroups {}", system); final List allGroups = system.getPartGroups(); // All groups in this system - final Map activeGroups = new TreeMap(); // Active groups + final Map activeGroups = new TreeMap<>(); // Active groups for (Staff staff : system.getStaves()) { logger.debug(" Staff#{}", staff.getId()); @@ -948,11 +930,7 @@ private void createInters () } double x = (peak.getStart() + peak.getStop()) / 2d; - Line2D median = new Line2D.Double( - x, - peak.getTop(), - x, - peak.getBottom()); + Line2D median = new Line2D.Double(x, peak.getTop(), x, peak.getBottom()); final Glyph glyph = sheet.getGlyphIndex().registerOriginal( peak.getFilament().toGlyph(null)); @@ -1050,7 +1028,7 @@ private void createParts () final List allGroups = system.getPartGroups(); // All groups in this system // Look for "true" braced groups - TreeSet bracedGroups = new TreeSet(PartGroup.byFirstId); + TreeSet bracedGroups = new TreeSet<>(PartGroup.byFirstId); for (PartGroup pg : allGroups) { if (isTrueBraceGroup(pg)) { @@ -1107,7 +1085,7 @@ private void deleteRelatedColumns (SystemInfo system, final List columns = columnMap.get(system); if (columns != null) { - final Set columnsToRemove = new LinkedHashSet(); + final Set columnsToRemove = new LinkedHashSet<>(); for (StaffPeak peak : removed) { BarColumn column = peak.getColumn(); @@ -1192,7 +1170,8 @@ private void detectBracePortions () /** * Detect the peaks that correspond to top or bottom end of brackets. *

                                                            - * Such bracket end is characterized as follows:

                                                              + * Such bracket end is characterized as follows: + *
                                                                *
                                                              • It is located on left side of the start column.
                                                              • *
                                                              • It is a rather thick peak.
                                                              • *
                                                              • It sometimes (but not always) goes a bit beyond staff top or bottom line.
                                                              • @@ -1237,8 +1216,11 @@ private void detectBracketEnds () // Check for serif shape Filament serif; - if ((ext <= params.maxBracketExtension) - && (null != (serif = getSerif(staff, peak, rightPeak, side)))) { + if ((ext <= params.maxBracketExtension) && (null != (serif = getSerif( + staff, + peak, + rightPeak, + side)))) { logger.debug("Staff#{} {} bracket end", staff.getId(), side); peak.setBracketEnd(side, serif); @@ -1275,7 +1257,7 @@ private void detectBracketMiddles () for (BarAlignment align : aligns) { if (align instanceof BarConnection) { - next = ((BarConnection) align).bottomPeak; + next = align.bottomPeak; if (next.isBracketEnd(BOTTOM)) { continue PeakLoop; @@ -1330,8 +1312,8 @@ private void detectStartColumns () if (column.isFull()) { if (startColumn != null) { - double gap = (column.getXDsk() - (column.getWidth() / 2)) - - (startColumn.getXDsk() + (startColumn.getWidth() / 2)); + double gap = (column.getXDsk() - (column.getWidth() / 2)) - (startColumn + .getXDsk() + (startColumn.getWidth() / 2)); int maxGap = (i == 1) ? params.maxBraceBarGap : params.maxDoubleBarGap; if (gap > maxGap) { @@ -1418,7 +1400,7 @@ private void extendConnection (BarConnection connection) } // Build the column of peaks, recursively - final List list = new ArrayList(); + final List list = new ArrayList<>(); list.add(topPeak); for (int i = 0; i < list.size(); i++) { @@ -1473,7 +1455,7 @@ private List
                                                                getAreaSections (Area area, { final Rectangle areaBox = area.getBounds(); final int xBreak = areaBox.x + areaBox.width; - final List
                                                                sections = new ArrayList
                                                                (); + final List
                                                                sections = new ArrayList<>(); for (Section section : allSections) { final Rectangle sectionBox = section.getBounds(); @@ -1518,7 +1500,7 @@ private BracketKind getBracketKind (StaffPeak peak) private List getConnections (Staff staff, VerticalSide side) { - final List list = new ArrayList(); + final List list = new ArrayList<>(); final VerticalSide opposite = side.opposite(); final StaffProjector projector = projectorOf(staff); final int iStart = projector.getStartPeakIndex(); @@ -1560,8 +1542,8 @@ private List getConnections (Staff staff, */ private List> getGroups (List peaks) { - List> groups = new ArrayList>(); - List group = new ArrayList(); + List> groups = new ArrayList<>(); + List group = new ArrayList<>(); for (StaffPeak peak : peaks) { if (!group.isEmpty()) { @@ -1569,7 +1551,7 @@ private List> getGroups (List peaks) if (gap > params.maxDoubleBarGap) { if (group.size() > 1) { - groups.add(new ArrayList(group)); + groups.add(new ArrayList<>(group)); } group.clear(); @@ -1581,7 +1563,7 @@ private List> getGroups (List peaks) // Last group? if (group.size() > 1) { - groups.add(new ArrayList(group)); + groups.add(new ArrayList<>(group)); } return groups; @@ -1601,7 +1583,7 @@ private List> getGroups (List peaks) */ private List
                                                                getSectionsByWidth (int maxWidth) { - List
                                                                sections = new ArrayList
                                                                (); + List
                                                                sections = new ArrayList<>(); Lag hLag = sheet.getLagManager().getLag(Lags.HLAG); Lag vLag = sheet.getLagManager().getLag(Lags.VLAG); @@ -1741,7 +1723,7 @@ private void groupBarPeaks (List isolated, if (gap <= params.maxDoubleBarGap) { // We are in a group with previous peak if (group == null) { - groups.add(group = new ArrayList()); + groups.add(group = new ArrayList<>()); group.add(prevPeak); } @@ -1873,8 +1855,8 @@ private boolean isTrueBraceGroup (PartGroup pg) final Staff lastStaff = staffManager.getStaff(lastId - 1); return !isPartConnected(firstStaff, TOP) // Not connected above - && isPartConnected(firstStaff, BOTTOM) // Internally connected - && !isPartConnected(lastStaff, BOTTOM); // Not connected below + && isPartConnected(firstStaff, BOTTOM) // Internally connected + && !isPartConnected(lastStaff, BOTTOM); // Not connected below } //------------------// @@ -1982,8 +1964,8 @@ private StaffPeak lookForBracePeak (Staff staff, private void partitionWidths () { // Dispatch peaks into isolated peaks and groups of peaks - final List isolated = new ArrayList(); - final List> groups = new ArrayList>(); + final List isolated = new ArrayList<>(); + final List> groups = new ArrayList<>(); groupBarPeaks(isolated, groups); // Isolated peaks are considered thin @@ -2058,7 +2040,7 @@ private void purgeCClefs () final List peaks = projector.getPeaks(); final int staffStart = staff.getAbscissa(LEFT); int measureStart = staffStart; - final List tails = new ArrayList(); + final List tails = new ArrayList<>(); for (int i = 0; i < peaks.size(); i++) { final StaffPeak p1 = peaks.get(i); @@ -2068,21 +2050,16 @@ private void purgeCClefs () } // Look for a rather thick first peak - if (!p1.isStaffEnd(LEFT) - && !p1.isStaffEnd(RIGHT) - && !(p1.isBrace()) - && !p1.isBracket() - && (p1.getWidth() >= params.minPeak1WidthForCClef)) { + if (!p1.isStaffEnd(LEFT) && !p1.isStaffEnd(RIGHT) && !(p1.isBrace()) && !p1 + .isBracket() && (p1.getWidth() >= params.minPeak1WidthForCClef)) { // Check gap is larger than multi-bar gap but smaller than measure int gap = p1.getStart() - measureStart; // Gap is not relevant for first measure, thanks to !peak.isStaffEnd() test int minGap = (measureStart == staffStart) ? 0 : params.maxDoubleBarGap; - if ((gap > minGap) - && (gap < params.minMeasureWidth) - && !isConnected(p1, TOP) - && !isConnected(p1, BOTTOM)) { + if ((gap > minGap) && (gap < params.minMeasureWidth) && !isConnected(p1, TOP) + && !isConnected(p1, BOTTOM)) { if (logger.isDebugEnabled() || p1.isVip()) { logger.info("VIP perhaps a C-Clef peak1 at {}", p1); } else { @@ -2095,9 +2072,8 @@ private void purgeCClefs () int gap2 = p2.getStart() - p1.getStop() - 1; if ((p2.getWidth() <= params.maxPeak2WidthForCClef) - && (gap2 <= params.maxDoubleBarGap) - && !isConnected(p2, TOP) - && !isConnected(p2, BOTTOM)) { + && (gap2 <= params.maxDoubleBarGap) && !isConnected(p2, TOP) + && !isConnected(p2, BOTTOM)) { boolean cancelled = false; tails.clear(); @@ -2144,7 +2120,7 @@ private void purgeCClefs () t.set(CCLEF_TAIL); } - final List toRemove = new ArrayList(); + final List toRemove = new ArrayList<>(); toRemove.add(p1); toRemove.add(p2); toRemove.addAll(tails); @@ -2189,12 +2165,11 @@ private void purgeExtendingPeaks () } for (VerticalSide side : VerticalSide.values()) { - final Staff staff = (side == TOP) ? system.getFirstStaff() - : system.getLastStaff(); + final Staff staff = (side == TOP) ? system.getFirstStaff() : system.getLastStaff(); final StaffProjector projector = projectorOf(staff); final List peaks = projector.getPeaks(); final int iStart = projector.getStartPeakIndex(); - final Set toRemove = new LinkedHashSet(); + final Set toRemove = new LinkedHashSet<>(); for (int i = iStart + 1; i < peaks.size(); i++) { StaffPeak peak = peaks.get(i); @@ -2241,7 +2216,7 @@ private void purgeLeftOfBraces () final List peaks = projector.getPeaks(); final int iStart = projector.getStartPeakIndex(); - final Set toRemove = new LinkedHashSet(); + final Set toRemove = new LinkedHashSet<>(); for (int i = iStart - 1; i >= 0; i--) { final StaffPeak peak = peaks.get(i); @@ -2276,7 +2251,7 @@ private void purgeLeftPeaks () for (SystemInfo system : sheet.getSystems()) { for (Staff staff : system.getStaves()) { final StaffProjector projector = projectorOf(staff); - final Set toRemove = new LinkedHashSet(); + final Set toRemove = new LinkedHashSet<>(); final int xLeft = staff.getAbscissa(LEFT); for (StaffPeak peak : projector.getPeaks()) { @@ -2355,7 +2330,7 @@ private void purgeTooLeft () } final List peaks = projector.getPeaks(); - final Set toRemove = new LinkedHashSet(); + final Set toRemove = new LinkedHashSet<>(); StaffPeak prevPeak = peaks.get(iStart); for (int i = iStart - 1; i >= 0; i--) { @@ -2398,8 +2373,8 @@ private void purgeUnalignedBars () for (Staff staff : system.getStaves()) { final StaffProjector projector = projectorOf(staff); - for (StaffPeak peak : new ArrayList(projector.getPeaks())) { - if (peakGraph.edgesOf(peak).isEmpty()) { + for (StaffPeak peak : new ArrayList<>(projector.getPeaks())) { + if (peakGraph.containsVertex(peak) && peakGraph.edgesOf(peak).isEmpty()) { if (peak.isVip()) { logger.info("VIP unaligned {}", peak); } @@ -2419,7 +2394,7 @@ private void recordBars () for (SystemInfo system : sheet.getSystems()) { for (Staff staff : system.getStaves()) { // All bars - List bars = new ArrayList(); + List bars = new ArrayList<>(); for (StaffPeak peak : projectorOf(staff).getPeaks()) { Inter inter = peak.getInter(); @@ -2457,12 +2432,12 @@ private void replacePeak (StaffPeak oldPeak, StaffPeak newPeak) { // PeakGraph - for (BarAlignment edge : new ArrayList(peakGraph.incomingEdgesOf(oldPeak))) { + for (BarAlignment edge : new ArrayList<>(peakGraph.incomingEdgesOf(oldPeak))) { StaffPeak source = peakGraph.getEdgeSource(edge); peakGraph.addEdge(source, newPeak, edge); } - for (BarAlignment edge : new ArrayList(peakGraph.outgoingEdgesOf(oldPeak))) { + for (BarAlignment edge : new ArrayList<>(peakGraph.outgoingEdgesOf(oldPeak))) { StaffPeak target = peakGraph.getEdgeTarget(edge); peakGraph.addEdge(newPeak, target, edge); } @@ -2502,14 +2477,12 @@ private void verifyLinesRoot () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, @@ -2636,7 +2609,6 @@ private static final class Constants //------------// private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int largeSystemStaffCount; @@ -2690,13 +2662,12 @@ private static class Parameters final int maxColumnDx; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Parameters object. * * @param scale the scaling factor */ - public Parameters (Scale scale) + Parameters (Scale scale) { largeSystemStaffCount = constants.largeSystemStaffCount.getValue(); minNormedDeltaWidth = constants.minThinThickDelta.getValue(); diff --git a/src/main/org/audiveris/omr/sheet/grid/ClustersRetriever.java b/src/main/org/audiveris/omr/sheet/grid/ClustersRetriever.java index 7d463c4ba..19a0ae33a 100644 --- a/src/main/org/audiveris/omr/sheet/grid/ClustersRetriever.java +++ b/src/main/org/audiveris/omr/sheet/grid/ClustersRetriever.java @@ -26,9 +26,7 @@ import org.audiveris.omr.glyph.dynamic.Compounds; import org.audiveris.omr.math.GeoUtil; import org.audiveris.omr.run.Orientation; - import static org.audiveris.omr.run.Orientation.*; - import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sheet.Scale.InterlineScale; import org.audiveris.omr.sheet.Sheet; @@ -65,12 +63,10 @@ */ public class ClustersRetriever { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - ClustersRetriever.class); + private static final Logger logger = LoggerFactory.getLogger(ClustersRetriever.class); /** * For comparing Filament instances on their starting point. @@ -100,7 +96,6 @@ public int compare (StaffFilament f1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** Comparator on cluster ordinate. */ public Comparator byOrdinate = new Comparator() { @@ -165,7 +160,7 @@ public int compare (LineCluster c1, private final List filaments; /** Filaments discarded. */ - private final List discardedFilaments = new ArrayList(); + private final List discardedFilaments = new ArrayList<>(); /** Skew of the sheet */ private final Skew skew; @@ -186,9 +181,8 @@ public int compare (LineCluster c1, private int[] colX; /** Collection of clusters */ - private final List clusters = new ArrayList(); + private final List clusters = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ClustersRetriever object, for a given staff * interline. @@ -211,12 +205,11 @@ public ClustersRetriever (Sheet sheet, skew = sheet.getSkew(); pictureWidth = sheet.getWidth(); scale = sheet.getScale(); - colCombs = new TreeMap>(); + colCombs = new TreeMap<>(); params = new Parameters(scale, interlineScale); } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // buildInfo // //-----------// @@ -275,31 +268,6 @@ public int getInterline () return interlineScale.main; } - //-------------// - // renderItems // - //-------------// - /** - * Render the vertical combs of filaments - * - * @param g graphics context - */ - void renderItems (Graphics2D g) - { - Color oldColor = g.getColor(); - g.setColor(combColor); - - for (Entry> entry : colCombs.entrySet()) { - int col = entry.getKey(); - int x = colX[col]; - - for (FilamentComb comb : entry.getValue()) { - g.draw(new Line2D.Double(x, comb.getY(0), x, comb.getY(comb.getCount() - 1))); - } - } - - g.setColor(oldColor); - } - //-----------// // bestMatch // //-----------// @@ -438,8 +406,8 @@ private boolean checkCollision (LineCluster one, LineCluster two, int delta) { - final List oneLines = new ArrayList(one.getLines()); - final List twoLines = new ArrayList(two.getLines()); + final List oneLines = new ArrayList<>(one.getLines()); + final List twoLines = new ArrayList<>(two.getLines()); for (int i1 = 0; i1 < oneLines.size(); i1++) { final StaffFilament f1 = oneLines.get(i1); @@ -475,7 +443,7 @@ private boolean checkCollision (LineCluster one, private double computeAcceptableLength () { // Determine minimum true length for valid clusters - List lengths = new ArrayList(); + List lengths = new ArrayList<>(); for (LineCluster cluster : clusters) { lengths.add(cluster.getTrueLength()); @@ -686,10 +654,10 @@ private void expandCluster (LineCluster cluster, */ private void expandClusters () { - List startFils = new ArrayList(filaments); + List startFils = new ArrayList<>(filaments); Collections.sort(startFils, byStartAbscissa); - List stopFils = new ArrayList(startFils); + List stopFils = new ArrayList<>(startFils); Collections.sort(stopFils, byStopAbscissa); // Browse clusters, starting with the longest ones @@ -719,7 +687,7 @@ private void followCombsNetwork () Map combs = fil.getCombs(); // Sequence of lines around the filament, indexed by relative pos - Map lines = new TreeMap(); + Map lines = new TreeMap<>(); // Loop on all combs this filament is involved in for (FilamentComb comb : combs.values()) { @@ -851,7 +819,7 @@ private void mergeClusters () // Keep on working while we do have a candidate to check for merge CandidateLoop: while (true) { - Wrapper deltaPos = new Wrapper(null); + Wrapper deltaPos = new Wrapper<>(null); Rectangle candidateBox = candidate.getBounds(); candidateBox.grow(params.maxMergeDx, params.clusterYMargin); @@ -1035,7 +1003,7 @@ private void retrieveCombs () double samplingDx = (double) pictureWidth / (sampleCount + 1); for (int col = 1; col <= sampleCount; col++) { - final List colList = new ArrayList(); + final List colList = new ArrayList<>(); colCombs.put(col, colList); final int x = (int) Math.rint(samplingDx * col); @@ -1093,7 +1061,7 @@ private void retrieveCombs () */ private List retrieveFilamentsAtX (double x) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (StaffFilament fil : filaments) { if ((x >= fil.getStartPoint().getX()) && (x <= fil.getStopPoint().getX())) { @@ -1101,12 +1069,11 @@ private List retrieveFilamentsAtX (double x) } } - Collections.sort(list); + Collections.sort(list, FilY.byOrdinate); return list; } - // // //---------------------// // // retrievePopularSize // // //---------------------// @@ -1116,7 +1083,7 @@ private List retrieveFilamentsAtX (double x) // private void retrievePopularSize () // { // // Build histogram of combs lengths - // Histogram histo = new Histogram(); + // Histogram histo = new Histogram<>(); // // for (List list : colCombs.values()) { // for (FilamentComb comb : list) { @@ -1140,20 +1107,42 @@ private void trimClusters () Collections.sort(clusters, byOrdinate); // Trim clusters with too many lines - for (Iterator it = clusters.iterator(); it.hasNext();) { - LineCluster cluster = it.next(); + for (LineCluster cluster : clusters) { cluster.trim(popSize); } } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------// + // renderItems // + //-------------// + /** + * Render the vertical combs of filaments + * + * @param g graphics context + */ + void renderItems (Graphics2D g) + { + Color oldColor = g.getColor(); + g.setColor(combColor); + + for (Entry> entry : colCombs.entrySet()) { + int col = entry.getKey(); + int x = colX[col]; + + for (FilamentComb comb : entry.getValue()) { + g.draw(new Line2D.Double(x, comb.getY(0), x, comb.getY(comb.getCount() - 1))); + } + } + + g.setColor(oldColor); + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction samplingDx = new Scale.Fraction( 1, @@ -1208,29 +1197,29 @@ private static final class Constants * knowing their ordinate at a common abscissa value. */ private static class FilY - implements Comparable { - //~ Instance fields ------------------------------------------------------------------------ + + public static final Comparator byOrdinate = new Comparator() + { + @Override + public int compare (FilY f1, + FilY f2) + { + return Double.compare(f1.y, f2.y); + } + }; final StaffFilament filament; final double y; - //~ Constructors --------------------------------------------------------------------------- - public FilY (StaffFilament filament, - double y) + FilY (StaffFilament filament, + double y) { this.filament = filament; this.y = y; } - //~ Methods -------------------------------------------------------------------------------- - @Override - public int compareTo (FilY that) - { - return Double.compare(this.y, that.y); - } - @Override public String toString () { @@ -1247,7 +1236,6 @@ public String toString () */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int samplingDx; @@ -1267,15 +1255,14 @@ private static class Parameters final int combMaxMargin; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Parameters object. * * @param scale the sheet global scaling factor * @param interlineScale the scaling for these clusters */ - public Parameters (Scale scale, - InterlineScale interlineScale) + Parameters (Scale scale, + InterlineScale interlineScale) { samplingDx = scale.toPixels(constants.samplingDx); maxExpandDx = scale.toPixels(constants.maxExpandDx); diff --git a/src/main/org/audiveris/omr/sheet/grid/FilamentComb.java b/src/main/org/audiveris/omr/sheet/grid/FilamentComb.java index bdcd6f733..c2b7d2379 100644 --- a/src/main/org/audiveris/omr/sheet/grid/FilamentComb.java +++ b/src/main/org/audiveris/omr/sheet/grid/FilamentComb.java @@ -32,7 +32,6 @@ */ public class FilamentComb { - //~ Instance fields ---------------------------------------------------------------------------- /** Column index where sample was taken. */ private final int col; @@ -46,7 +45,6 @@ public class FilamentComb /** To save processing. */ private boolean processed = false; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new FilamentComb object. * @@ -56,11 +54,10 @@ public FilamentComb (int col) { this.col = col; - filaments = new ArrayList(); - ys = new ArrayList(); + filaments = new ArrayList<>(); + ys = new ArrayList<>(); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // append // //--------// @@ -94,6 +91,12 @@ public int getCount () //-------------// // getFilament // //-------------// + /** + * Report the filament at provided index in comb. + * + * @param index the provided index in comb + * @return the filament at this index + */ public StaffFilament getFilament (int index) { return filaments.get(index); @@ -102,6 +105,11 @@ public StaffFilament getFilament (int index) //--------------// // getFilaments // //--------------// + /** + * Report the vertical sequence of filaments in this comb. + * + * @return vertical sequence of filaments + */ public List getFilaments () { return filaments; @@ -110,6 +118,12 @@ public List getFilaments () //----------// // getIndex // //----------// + /** + * Report the index of provided filament (or its ancestor) in this comb. + * + * @param filament the provided filament + * @return the related index in comb of this filament (or its ancestor) + */ public int getIndex (StaffFilament filament) { StaffFilament ancestor = (StaffFilament) filament.getAncestor(); @@ -128,6 +142,12 @@ public int getIndex (StaffFilament filament) //------// // getY // //------// + /** + * Report ordinate at provided index. + * + * @param index the provided index in comb + * @return the corresponding ordinate value + */ public double getY (int index) { return ys.get(index); @@ -137,6 +157,8 @@ public double getY (int index) // isProcessed // //-------------// /** + * Tell whether this comb has been processed. + * * @return the processed */ public boolean isProcessed () @@ -148,6 +170,8 @@ public boolean isProcessed () // setProcessed // //--------------// /** + * Flag this comb as processed. + * * @param processed the processed to set */ public void setProcessed (boolean processed) diff --git a/src/main/org/audiveris/omr/sheet/grid/GridBuilder.java b/src/main/org/audiveris/omr/sheet/grid/GridBuilder.java index 242bd5b98..871698ab3 100644 --- a/src/main/org/audiveris/omr/sheet/grid/GridBuilder.java +++ b/src/main/org/audiveris/omr/sheet/grid/GridBuilder.java @@ -53,24 +53,21 @@ */ public class GridBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(GridBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- - /** Related sheet. */ - @Navigable(false) - private final Sheet sheet; - /** Companion in charge of staff lines. */ public final LinesRetriever linesRetriever; /** Companion in charge of bar lines. */ public final BarsRetriever barsRetriever; - //~ Constructors ------------------------------------------------------------------------------- + /** Related sheet. */ + @Navigable(false) + private final Sheet sheet; + /** * Retrieve the frames of all staff lines. * @@ -89,7 +86,6 @@ public GridBuilder (Sheet sheet) } } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // buildInfo // //-----------// @@ -114,9 +110,8 @@ public void buildInfo () sheet.getStub().getAssembly().addBoard(SheetTab.DATA_TAB, new InterBoard(sheet)); // Filament board - sheet.getStub().getAssembly().addBoard( - SheetTab.DATA_TAB, - new FilamentBoard(sheet.getFilamentIndex().getEntityService(), true)); + sheet.getStub().getAssembly().addBoard(SheetTab.DATA_TAB, new FilamentBoard(sheet + .getFilamentIndex().getEntityService(), true)); } // Retrieve the horizontal staff lines filaments with long sections @@ -210,25 +205,20 @@ private void buildAllLags () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print out the stop watch?"); + private final Constant.Boolean printWatch = new Constant.Boolean(false, + "Should we print out the stop watch?"); - private final Constant.Boolean buildDewarpedTarget = new Constant.Boolean( - false, - "Should we build a dewarped target?"); + private final Constant.Boolean buildDewarpedTarget = new Constant.Boolean(false, + "Should we build a dewarped target?"); - private final Constant.Boolean showGrid = new Constant.Boolean( - false, - "Should we show the details of grid?"); + private final Constant.Boolean showGrid = new Constant.Boolean(false, + "Should we show the details of grid?"); } } diff --git a/src/main/org/audiveris/omr/sheet/grid/GridStep.java b/src/main/org/audiveris/omr/sheet/grid/GridStep.java index f87cc4e63..0dc2e75d1 100644 --- a/src/main/org/audiveris/omr/sheet/grid/GridStep.java +++ b/src/main/org/audiveris/omr/sheet/grid/GridStep.java @@ -47,13 +47,11 @@ public class GridStep extends AbstractStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(GridStep.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new GridStep object. */ @@ -61,7 +59,6 @@ public GridStep () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // displayUI // //-----------// @@ -77,7 +74,8 @@ public void displayUI (Step step, new ScrollImageView( sheet, new ImageView( - sheet.getPicture().getSource(Picture.SourceKey.NO_STAFF).getBufferedImage())), + sheet.getPicture().getSource(Picture.SourceKey.NO_STAFF) + .getBufferedImage())), new BoardsPane(new PixelBoard(sheet))); } } @@ -101,14 +99,12 @@ public void doit (Sheet sheet) ///watch.print(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean displayNoStaff = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/sheet/grid/LineCluster.java b/src/main/org/audiveris/omr/sheet/grid/LineCluster.java index 5ab98a5ba..5d2287cc0 100644 --- a/src/main/org/audiveris/omr/sheet/grid/LineCluster.java +++ b/src/main/org/audiveris/omr/sheet/grid/LineCluster.java @@ -54,7 +54,6 @@ public class LineCluster implements Vip { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(LineCluster.class); @@ -70,7 +69,6 @@ public int compare (LineCluster c1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** Id for debug. */ private final String id; @@ -95,7 +93,6 @@ public int compare (LineCluster c1, /** For debugging. */ private boolean vip = false; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LineCluster object. * @@ -120,12 +117,11 @@ public LineCluster (Scale scale, id = "C" + seed.getId(); - lines = new TreeMap(); + lines = new TreeMap<>(); include(seed, 0); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // destroy // //---------// @@ -162,6 +158,11 @@ public LineCluster getAncestor () //-----------// // getBounds // //-----------// + /** + * Report the bounding box of the cluster + * + * @return bounds + */ public Rectangle getBounds () { if (contourBox == null) { @@ -203,6 +204,11 @@ public Point getCenter () //--------------// // getFirstLine // //--------------// + /** + * Report the first line in cluster + * + * @return the first line + */ public StaffFilament getFirstLine () { return lines.get(lines.firstKey()); @@ -212,6 +218,8 @@ public StaffFilament getFirstLine () // getId // //-------// /** + * Report the cluster ID. + * * @return the id */ public String getId () @@ -233,6 +241,11 @@ public int getInterline () //-------------// // getLastLine // //-------------// + /** + * Report the last line in cluster + * + * @return last line + */ public StaffFilament getLastLine () { return lines.get(lines.lastKey()); @@ -241,6 +254,11 @@ public StaffFilament getLastLine () //----------// // getLines // //----------// + /** + * Report the cluster lines. + * + * @return lines + */ public Collection getLines () { return lines.values(); @@ -274,8 +292,8 @@ public List getPointsAt (double x, double globalSlope) { // Populate points (and holes) - SortedMap points = new TreeMap(); - List holes = new ArrayList(); + SortedMap points = new TreeMap<>(); + List holes = new ArrayList<>(); for (Entry entry : lines.entrySet()) { int pos = entry.getKey(); @@ -300,9 +318,8 @@ public List getPointsAt (double x, for (int dir : new int[]{-1, 1}) { final StaffFilament otherLine = lines.get(pos + dir); - if ((otherLine != null) - && otherLine.isWithinRange(x) - && otherLine.isWithinRange(endX)) { + if ((otherLine != null) && otherLine.isWithinRange(x) && otherLine.isWithinRange( + endX)) { y = otherLine.yAt(x) + (line.yAt(endX) - otherLine.yAt(endX)); break; @@ -321,12 +338,17 @@ public List getPointsAt (double x, points.put(pos, (y != null) ? new Point2D.Double(x, y) : null); } - return new ArrayList(points.values()); + return new ArrayList<>(points.values()); } //---------// // getSize // //---------// + /** + * Report the cluster count of lines + * + * @return count of lines + */ public int getSize () { return lines.size(); @@ -335,9 +357,14 @@ public int getSize () //-----------// // getStarts // //-----------// + /** + * Report the sequence of lines starting point + * + * @return starts + */ public List getStarts () { - List points = new ArrayList(getSize()); + List points = new ArrayList<>(getSize()); for (StaffFilament line : lines.values()) { points.add(line.getStartPoint()); @@ -349,9 +376,14 @@ public List getStarts () //----------// // getStops // //----------// + /** + * Report the sequence of lines ending point + * + * @return stops + */ public List getStops () { - List points = new ArrayList(getSize()); + List points = new ArrayList<>(getSize()); for (StaffFilament line : lines.values()) { points.add(line.getStopPoint()); @@ -455,9 +487,24 @@ public boolean isVip () return vip; } + //--------// + // setVip // + //--------// + @Override + public void setVip (boolean vip) + { + this.vip = vip; + } + //-----------// // mergeWith // //-----------// + /** + * Include another cluster into this one. + * + * @param that the other cluster to include + * @param deltaPos shift between clusters vertical positions + */ public void mergeWith (LineCluster that, int deltaPos) { @@ -476,7 +523,7 @@ public void renumberLines () int firstPos = lines.firstKey(); if (firstPos != 0) { - SortedMap newLines = new TreeMap(); + SortedMap newLines = new TreeMap<>(); for (Entry entry : lines.entrySet()) { int pos = entry.getKey(); @@ -492,15 +539,6 @@ public void renumberLines () invalidateCache(); } - //--------// - // setVip // - //--------// - @Override - public void setVip (boolean vip) - { - this.vip = vip; - } - //----------// // toString // //----------// @@ -597,7 +635,7 @@ private void include (StaffFilament pivot, // Loop on all combs that involve this filament // Use a copy to avoid concurrent modification error - List combs = new ArrayList(pivot.getCombs().values()); + List combs = new ArrayList<>(pivot.getCombs().values()); for (FilamentComb comb : combs) { if (comb.isProcessed()) { diff --git a/src/main/org/audiveris/omr/sheet/grid/LineInfo.java b/src/main/org/audiveris/omr/sheet/grid/LineInfo.java index 382bb0146..a29ef190b 100644 --- a/src/main/org/audiveris/omr/sheet/grid/LineInfo.java +++ b/src/main/org/audiveris/omr/sheet/grid/LineInfo.java @@ -40,7 +40,6 @@ @XmlJavaTypeAdapter(StaffLine.Adapter.class) public interface LineInfo { - //~ Methods ------------------------------------------------------------------------------------ /** * Report the absolute contour rectangle diff --git a/src/main/org/audiveris/omr/sheet/grid/LinesRetriever.java b/src/main/org/audiveris/omr/sheet/grid/LinesRetriever.java index e5bf1bcb4..9e02a70f7 100644 --- a/src/main/org/audiveris/omr/sheet/grid/LinesRetriever.java +++ b/src/main/org/audiveris/omr/sheet/grid/LinesRetriever.java @@ -28,9 +28,9 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; import org.audiveris.omr.glyph.dynamic.Compounds; +import org.audiveris.omr.glyph.dynamic.CurvedFilament; import org.audiveris.omr.glyph.dynamic.Filament; import org.audiveris.omr.glyph.dynamic.FilamentFactory; -import org.audiveris.omr.glyph.dynamic.SectionCompound; import org.audiveris.omr.lag.JunctionRatioPolicy; import org.audiveris.omr.lag.Lag; import org.audiveris.omr.lag.Section; @@ -93,28 +93,25 @@ public class LinesRetriever implements ItemRenderer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(LinesRetriever.class); - //~ Instance fields ---------------------------------------------------------------------------- - // - /** related sheet */ + /** Related sheet. */ @Navigable(false) private final Sheet sheet; - /** Related scale */ + /** Related scale. */ private final Scale scale; /** Related staff manager. */ private final StaffManager staffManager; - /** Scale-dependent constants for horizontal stuff */ + /** Scale-dependent constants for horizontal stuff. */ private final Parameters params; - /** Lag of horizontal runs */ + /** Lag of horizontal runs. */ private Lag hLag; /** Long horizontal filaments found, non sorted. */ @@ -123,31 +120,30 @@ public class LinesRetriever /** Second collection of filaments. (for small staves) */ private List secondFilaments; - /** Sloped filaments */ + /** Sloped filaments. */ private List slopedFilaments; - /** Discarded filaments */ + /** Discarded filaments. */ private List discardedFilaments; - /** Global slope of the sheet */ + /** Global slope of the sheet. */ private double globalSlope; - /** Companion in charge of clusters of main interline */ + /** Companion in charge of clusters of main interline. */ private ClustersRetriever clustersRetriever; - /** Companion in charge of clusters of second interline, if any */ + /** Companion in charge of clusters of second interline, if any. */ private ClustersRetriever smallClustersRetriever; - /** Companion in charge of bar lines */ - final BarsRetriever barsRetriever; - - /** Too-short horizontal runs */ + /** Too-short horizontal runs. */ private RunTable shortHoriTable; /** Binary buffer. */ private ByteProcessor binaryBuffer; - //~ Constructors ------------------------------------------------------------------------------- + /** Companion in charge of bar lines. */ + final BarsRetriever barsRetriever; + /** * Retrieve the frames of all staff lines. * @@ -165,7 +161,6 @@ public LinesRetriever (Sheet sheet, params = new Parameters(scale); } - //~ Methods ------------------------------------------------------------------------------------ //--------------------// // buildHorizontalLag // //--------------------// @@ -194,16 +189,14 @@ public RunTable buildHorizontalLag () // Split horizontal runs into short & long tables shortHoriTable = new RunTable(HORIZONTAL, sheet.getWidth(), sheet.getHeight()); - RunTable longHoriTable = horiTable.purge( - new Predicate() + RunTable longHoriTable = horiTable.purge(new Predicate() { @Override public final boolean check (Run run) { return run.getLength() < params.minRunLength; } - }, - shortHoriTable); + }, shortHoriTable); if (runsViewer != null) { runsViewer.display("short-hori", shortHoriTable); @@ -238,6 +231,7 @@ public final boolean check (Run run) * sections within each abscissa sample. *

                                                                * Synopsis: + *
                                                                *

                                                                      *      + defineEndPoints()
                                                                      *      + includeDiscardedFilaments()
                                                                @@ -271,8 +265,8 @@ public void completeLines ()
                                                                             fillHoles();
                                                                 
                                                                             // Dispatch short sections into thick & thin ones
                                                                -            final List
                                                                thickSections = new ArrayList
                                                                (); - final List
                                                                thinSections = new ArrayList
                                                                (); + final List
                                                                thickSections = new ArrayList<>(); + final List
                                                                thinSections = new ArrayList<>(); watch.start("dispatchShortSections"); dispatchShortSections(thickSections, thinSections); @@ -356,7 +350,7 @@ public void renderItems (Graphics2D g) // Filament lines? if (constants.showHorizontalLines.isSet() && (filaments != null)) { - List allFils = new ArrayList(filaments); + List allFils = new ArrayList<>(filaments); if (secondFilaments != null) { allFils.addAll(secondFilaments); @@ -380,11 +374,19 @@ public void renderItems (Graphics2D g) Point2D p = filament.getStartPoint(); double der = filament.getSlopeAt(p.getX(), HORIZONTAL); g.draw( - new Line2D.Double(p.getX(), p.getY(), p.getX() - dx, p.getY() - (der * dx))); + new Line2D.Double( + p.getX(), + p.getY(), + p.getX() - dx, + p.getY() - (der * dx))); p = filament.getStopPoint(); der = filament.getSlopeAt(p.getX(), HORIZONTAL); g.draw( - new Line2D.Double(p.getX(), p.getY(), p.getX() + dx, p.getY() + (der * dx))); + new Line2D.Double( + p.getX(), + p.getY(), + p.getX() + dx, + p.getY() + (der * dx))); } } } @@ -402,10 +404,11 @@ public void renderItems (Graphics2D g) *
                                                                  *
                                                                1. First, retrieve long horizontal sections and merge them into filaments.
                                                                2. *
                                                                3. Second, detect series of filaments regularly spaced vertically and aggregate them into - * clusters of lines (as staff candidates).
                                                                4. + * clusters of lines (as staff candidates). *
                                                                *

                                                                * Synopsis: + *
                                                                *

                                                                      *      + filamentFactory.retrieveFilaments()
                                                                      *      + retrieveGlobalSlope()
                                                                @@ -426,7 +429,7 @@ public void retrieveLines ()
                                                                             watch.start("retrieveFilaments");
                                                                 
                                                                             // Create initial filaments
                                                                -            FilamentFactory factory = new FilamentFactory(
                                                                +            FilamentFactory factory = new FilamentFactory<>(
                                                                                     scale,
                                                                                     sheet.getFilamentIndex(),
                                                                                     Orientation.HORIZONTAL,
                                                                @@ -501,7 +504,7 @@ public void retrieveLines ()
                                                                     private void buildStaves ()
                                                                     {
                                                                         // Accumulate all clusters, and sort them by layout
                                                                -        List allClusters = new ArrayList();
                                                                +        List allClusters = new ArrayList<>();
                                                                         allClusters.addAll(clustersRetriever.getClusters());
                                                                 
                                                                         Integer smallInterline = null;
                                                                @@ -523,7 +526,7 @@ private void buildStaves ()
                                                                             logger.debug("{}", cluster);
                                                                 
                                                                             // Copy array of lines
                                                                -            List lines = new ArrayList(cluster.getLines());
                                                                +            List lines = new ArrayList<>(cluster.getLines());
                                                                 
                                                                             // Determine rough abscissa values for left & right sides
                                                                             double left = Integer.MAX_VALUE;
                                                                @@ -535,10 +538,10 @@ private void buildStaves ()
                                                                             }
                                                                 
                                                                             // Allocate Staff instance
                                                                -            List infos = new ArrayList(lines.size());
                                                                +            List infos = new ArrayList<>(lines.size());
                                                                 
                                                                             for (StaffFilament line : lines) {
                                                                -                infos.add((LineInfo) line);
                                                                +                infos.add(line);
                                                                             }
                                                                 
                                                                             Staff staff = new Staff(++staffId, left, right, cluster.getInterline(), infos);
                                                                @@ -621,17 +624,16 @@ private boolean canIncludeFilament (StaffFilament lineFilament,
                                                                         }
                                                                 
                                                                         // Check resulting thickness
                                                                -        double rThickness = Compounds.getThicknessAt(
                                                                -                xMid,
                                                                -                HORIZONTAL,
                                                                -                scale,
                                                                -                (SectionCompound) fil,
                                                                -                lineFilament);
                                                                +        double rThickness = Compounds.getThicknessAt(xMid, HORIZONTAL, scale, fil, lineFilament);
                                                                 
                                                                         if (rThickness > maxThickness) {
                                                                             if (logger.isDebugEnabled() || isVip) {
                                                                                 logger.info(
                                                                -                        String.format("%sRes thickness:%.1f vs %d", vips, rThickness, maxThickness));
                                                                +                        String.format(
                                                                +                                "%sRes thickness:%.1f vs %d",
                                                                +                                vips,
                                                                +                                rThickness,
                                                                +                                maxThickness));
                                                                             }
                                                                 
                                                                             return false;
                                                                @@ -707,17 +709,16 @@ private boolean canIncludeSection (StaffFilament filament,
                                                                         }
                                                                 
                                                                         // Check resulting thickness
                                                                -        double rThickness = Compounds.getThicknessAt(
                                                                -                xMid,
                                                                -                HORIZONTAL,
                                                                -                scale,
                                                                -                (Section) section,
                                                                -                filament);
                                                                +        double rThickness = Compounds.getThicknessAt(xMid, HORIZONTAL, scale, section, filament);
                                                                 
                                                                         if (rThickness > maxThickness) {
                                                                             if (logger.isDebugEnabled() || isVip) {
                                                                                 logger.info(
                                                                -                        String.format("%sRes thickness:%.1f vs %d", vips, rThickness, maxThickness));
                                                                +                        String.format(
                                                                +                                "%sRes thickness:%.1f vs %d",
                                                                +                                vips,
                                                                +                                rThickness,
                                                                +                                maxThickness));
                                                                             }
                                                                 
                                                                             return false;
                                                                @@ -742,8 +743,7 @@ private void defineEndPoints ()
                                                                         for (Staff staff : staffManager.getStaves()) {
                                                                             double meanDy = staff.getMeanInterline();
                                                                 
                                                                -            Map> endMap = new EnumMap>(
                                                                -                    HorizontalSide.class);
                                                                +            Map> endMap = new EnumMap<>(HorizontalSide.class);
                                                                 
                                                                             for (HorizontalSide side : HorizontalSide.values()) {
                                                                                 endMap.put(side, retrieveEndPoints(staff, meanDy, side));
                                                                @@ -797,7 +797,7 @@ private void fillHoles ()
                                                                             logger.debug("{}", staff);
                                                                 
                                                                             // Insert line intermediate points, if so needed
                                                                -            List fils = new ArrayList();
                                                                +            List fils = new ArrayList<>();
                                                                 
                                                                             for (LineInfo line : staff.getLines()) {
                                                                                 fils.add((StaffFilament) line);
                                                                @@ -815,7 +815,7 @@ private void fillHoles ()
                                                                     //----------------//
                                                                     private List
                                                                getAllStickers () { - List
                                                                list = new ArrayList
                                                                (hLag.getEntities()); + List
                                                                list = new ArrayList<>(hLag.getEntities()); // Remove any (hori) section that is already part of a staff line for (Staff staff : staffManager.getStaves()) { @@ -828,9 +828,9 @@ private List
                                                                getAllStickers () Collections.sort(list, Section.byFullPosition); // Build pos-based index - final SectionTally
                                                                tally = new SectionTally
                                                                (sheet.getHeight(), list); + final SectionTally
                                                                tally = new SectionTally<>(sheet.getHeight(), list); - Set
                                                                connected = new LinkedHashSet
                                                                (); + Set
                                                                connected = new LinkedHashSet<>(); // Detect sections with connections below for (int i = 0, iBreak = list.size(); i < iBreak; i++) { @@ -897,7 +897,7 @@ private List
                                                                getAllStickers () // Keep only sections that are 1-pixel high and have limited connection list.removeAll(connected); - List
                                                                stickers = new ArrayList
                                                                (); + List
                                                                stickers = new ArrayList<>(); for (Section section : list) { if (section.getRunCount() == 1) { @@ -916,7 +916,7 @@ private List
                                                                getAllStickers () */ private void includeDiscardedFilaments () { - List candidates = new ArrayList(); + List candidates = new ArrayList<>(); candidates.addAll(discardedFilaments); candidates.addAll(slopedFilaments); @@ -1023,7 +1023,7 @@ private void includeSections (List
                                                                sections) final double maxX = fil.getStopPoint().getX(); final int minY = lineBox.y; final int maxY = lineBox.y + lineBox.height; - final List
                                                                stickers = new ArrayList
                                                                (); + final List
                                                                stickers = new ArrayList<>(); for (int i = iMin; i <= iMax; i++) { Section section = sections.get(i); @@ -1079,7 +1079,7 @@ private void includeSections (List
                                                                sections) private void includeStickers () { final List
                                                                stickers = getAllStickers(); - final SectionTally
                                                                tally = new SectionTally
                                                                (sheet.getHeight(), stickers); + final SectionTally
                                                                tally = new SectionTally<>(sheet.getHeight(), stickers); for (Staff staff : staffManager.getStaves()) { int lineId = 0; @@ -1088,11 +1088,12 @@ private void includeStickers () lineId++; StaffFilament fil = (StaffFilament) l; - Set
                                                                toAdd = new LinkedHashSet
                                                                (); + Set
                                                                toAdd = new LinkedHashSet<>(); for (Section source : fil.getMembers()) { for (VerticalSide side : VerticalSide.values()) { - final Run predRun = (side == TOP) ? source.getFirstRun() : source.getLastRun(); + final Run predRun = (side == TOP) ? source.getFirstRun() + : source.getLastRun(); final int predStart = predRun.getStart(); final int predStop = predRun.getStop(); @@ -1143,7 +1144,7 @@ private void polishCurvatures () { for (Staff staff : staffManager.getStaves()) { for (LineInfo line : staff.getLines()) { - ((StaffFilament) line).polishCurvature(params.minRadius); + ((CurvedFilament) line).polishCurvature(params.minRadius); } } } @@ -1157,7 +1158,7 @@ private void polishCurvatures () private void purgeCurvedFilaments () throws StepException { - List toRemove = new ArrayList(); + List toRemove = new ArrayList<>(); for (StaffFilament fil : filaments) { Point2D start = fil.getStartPoint(); @@ -1191,7 +1192,7 @@ private void purgeCurvedFilaments () if (filaments.size() < 5) { sheet.getStub().decideOnRemoval( sheet.getId() + LINE_SEPARATOR + "Too few staff filaments: " + filaments.size() - + LINE_SEPARATOR + "This sheet does not seem to contain staff lines.", + + LINE_SEPARATOR + "This sheet does not seem to contain staff lines.", false); } } @@ -1213,11 +1214,9 @@ private void purgeCurvedFilaments () private List purgeSlopedFilaments () { final double sheetSlope = sheet.getSkew().getSlope(); - final double minShortSlope = (sheetSlope > 0) ? (-params.maxSlopeDiff / 2) - : sheetSlope; - final double maxShortSlope = (sheetSlope > 0) ? sheetSlope - : (params.maxSlopeDiff / 2); - final List toRemove = new ArrayList(); + final double minShortSlope = (sheetSlope > 0) ? (-params.maxSlopeDiff / 2) : sheetSlope; + final double maxShortSlope = (sheetSlope > 0) ? sheetSlope : (params.maxSlopeDiff / 2); + final List toRemove = new ArrayList<>(); for (StaffFilament fil : filaments) { if (fil.isVip()) { @@ -1392,14 +1391,9 @@ private double retrieveGlobalSlope () } } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio topRatioForSlope = new Constant.Ratio( 0.1, @@ -1471,12 +1465,6 @@ private static final class Constants 0.05, "Maximum connected pixels for a line sticker"); - // Constants for display - // --------------------- - Constant.Boolean displayRuns = new Constant.Boolean( - false, - "Should we display all images on runs?"); - private final Constant.Boolean showHorizontalLines = new Constant.Boolean( true, "Should we show the horizontal grid lines?"); @@ -1496,6 +1484,12 @@ private static final class Constants private final Constant.Boolean showCombs = new Constant.Boolean( false, "Should we show staff lines combs?"); + + // Constants for display + // --------------------- + Constant.Boolean displayRuns = new Constant.Boolean( + false, + "Should we display all images on runs?"); } //------------// @@ -1507,7 +1501,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ /** Minimum run length for horizontal lag */ final int minRunLength; @@ -1552,13 +1545,12 @@ private static class Parameters final int maxStickerConnectionLength; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Parameters object. * * @param scale the scaling factor */ - public Parameters (Scale scale) + Parameters (Scale scale) { // Special parameters maxStickerThickness = (int) Math.rint( diff --git a/src/main/org/audiveris/omr/sheet/grid/PartGroup.java b/src/main/org/audiveris/omr/sheet/grid/PartGroup.java index f8513c7be..2c20abba9 100644 --- a/src/main/org/audiveris/omr/sheet/grid/PartGroup.java +++ b/src/main/org/audiveris/omr/sheet/grid/PartGroup.java @@ -35,10 +35,9 @@ @XmlAccessorType(XmlAccessType.NONE) public class PartGroup { - //~ Static fields/initializers ----------------------------------------------------------------- /** To compare groups by their first staff ID. */ - public static Comparator byFirstId = new Comparator() + public static final Comparator byFirstId = new Comparator() { @Override public int compare (PartGroup pg1, @@ -48,18 +47,6 @@ public int compare (PartGroup pg1, } }; - //~ Enumerations ------------------------------------------------------------------------------- - public static enum Symbol - { - //~ Enumeration constant initializers ------------------------------------------------------ - - bracket, - brace, - square; - } - - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -91,7 +78,6 @@ public static enum Symbol @XmlAttribute private String abbreviation; - //~ Constructors ------------------------------------------------------------------------------- /** * Build a {@code PartGroup} object. * @@ -124,7 +110,6 @@ private PartGroup () this.firstStaffId = 0; } - //~ Methods ------------------------------------------------------------------------------------ /** * @return the abbreviation */ @@ -134,6 +119,18 @@ public String getAbbreviation () } /** + * Set group abbreviation. + * + * @param abbreviation the abbreviation to set + */ + public void setAbbreviation (String abbreviation) + { + this.abbreviation = abbreviation; + } + + /** + * Get group first staff id. + * * @return the firstStaffId */ public int getFirstStaffId () @@ -142,6 +139,8 @@ public int getFirstStaffId () } /** + * Get group last staff id. + * * @return the lastStaffId */ public int getLastStaffId () @@ -150,6 +149,18 @@ public int getLastStaffId () } /** + * Set group first staff id. + * + * @param lastStaffId ID of the lastStaff + */ + public void setLastStaffId (int lastStaffId) + { + this.lastStaffId = lastStaffId; + } + + /** + * Report group name, if any + * * @return the name */ public String getName () @@ -158,6 +169,18 @@ public String getName () } /** + * Assign group name. + * + * @param name the name to set + */ + public void setName (String name) + { + this.name = name; + } + + /** + * Report group number. + * * @return the number */ public int getNumber () @@ -166,6 +189,8 @@ public int getNumber () } /** + * Report the group defining symbol. + * * @return the symbol */ public Symbol getSymbol () @@ -174,7 +199,9 @@ public Symbol getSymbol () } /** - * @return the barline + * Tell whether this group is based on a barline connection. + * + * @return true if so */ public boolean isBarline () { @@ -191,30 +218,6 @@ public boolean isBrace () return symbol == Symbol.brace; } - /** - * @param abbreviation the abbreviation to set - */ - public void setAbbreviation (String abbreviation) - { - this.abbreviation = abbreviation; - } - - /** - * @param lastStaffId ID of the lastStaff - */ - public void setLastStaffId (int lastStaffId) - { - this.lastStaffId = lastStaffId; - } - - /** - * @param name the name to set - */ - public void setName (String name) - { - this.name = name; - } - //----------// // toString // //----------// @@ -247,4 +250,14 @@ public String toString () return sb.toString(); } + + /** + * Kind of symbol that defines the group of parts. + */ + public static enum Symbol + { + bracket, + brace, + square + } } diff --git a/src/main/org/audiveris/omr/sheet/grid/PeakGraph.java b/src/main/org/audiveris/omr/sheet/grid/PeakGraph.java index 1580ada9a..187846789 100644 --- a/src/main/org/audiveris/omr/sheet/grid/PeakGraph.java +++ b/src/main/org/audiveris/omr/sheet/grid/PeakGraph.java @@ -84,13 +84,11 @@ public class PeakGraph extends SimpleDirectedGraph { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(PeakGraph.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ @Navigable(false) private final Sheet sheet; @@ -107,7 +105,6 @@ public class PeakGraph /** Specific builder for peak-based filaments. */ private final BarFilamentBuilder filamentBuilder; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PeakGraph} object. * @@ -127,7 +124,6 @@ public PeakGraph (Sheet sheet, filamentBuilder = new BarFilamentBuilder(sheet); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // buildSystems // //--------------// @@ -271,6 +267,12 @@ public boolean checkBraceAlignment (StaffPeak topPeak, return Math.abs(dx) <= params.maxAlignmentBraceDx; } + @Override + public Object clone () + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-------------------// // areRightConnected // //-------------------// @@ -298,7 +300,6 @@ private boolean areRightConnected (Staff top, return false; } - // // //-------------// // // alignGroups // // //-------------// @@ -374,7 +375,7 @@ private void buildBarSticks () logger.debug("sections:{}", allSections.size()); for (StaffProjector projector : projectors) { - List toRemove = new ArrayList(); + List toRemove = new ArrayList<>(); for (StaffPeak peak : projector.getPeaks()) { // Build filament from proper slice of sections for this peak @@ -413,8 +414,7 @@ private void buildBarSticks () */ private BarConnection checkConnection (BarAlignment alignment) { - ByteProcessor pixelFilter = sheet.getPicture().getSource( - Picture.SourceKey.BINARY); + ByteProcessor pixelFilter = sheet.getPicture().getSource(Picture.SourceKey.BINARY); StaffPeak p1 = alignment.topPeak; StaffPeak p2 = alignment.bottomPeak; final boolean vip = p1.isVip() && p2.isVip(); @@ -435,7 +435,7 @@ private BarConnection checkConnection (BarAlignment alignment) } if ((data.gap <= params.maxConnectionGap) - && (data.whiteRatio <= params.maxConnectionWhiteRatio)) { + && (data.whiteRatio <= params.maxConnectionWhiteRatio)) { double gapImpact = 1 - (data.gap / (double) params.maxConnectionGap); double whiteImpact = 1 - (data.whiteRatio / params.maxConnectionWhiteRatio); BarConnection connection = new BarConnection(alignment, gapImpact, whiteImpact); @@ -485,8 +485,7 @@ private Map> checkForSplit (StaffPeak peak) logger.info("VIP running checkForSplit for {}", peak); } - final Map> map = new EnumMap>( - VerticalSide.class); + final Map> map = new EnumMap<>(VerticalSide.class); final List peakGroup = groupOf(Arrays.asList(peak)); if (peakGroup.size() > 1) { @@ -582,8 +581,7 @@ private StaffPeak createSubPeak (StaffPeak peak, HorizontalSide side) { final Staff staff = peak.getStaff(); - final StaffProjector projector = projectorOf(staff); - final List
                                                                allSections = new ArrayList
                                                                (peak.getFilament().getMembers()); + final List
                                                                allSections = new ArrayList<>(peak.getFilament().getMembers()); StaffPeak p = new StaffPeak( staff, @@ -618,7 +616,7 @@ private StaffPeak createSubPeak (StaffPeak peak, */ private List createSystems (Integer[] systemTops) { - final List newSystems = new ArrayList(); + final List newSystems = new ArrayList<>(); Integer staffTop = null; int systemId = 0; SystemInfo system = null; @@ -658,9 +656,9 @@ private List createSystems (Integer[] systemTops) private void detectCurvedPeaks () { for (StaffProjector projector : projectors) { - ///List toRemove = new ArrayList(); + ///List toRemove = new ArrayList<>(); for (StaffPeak peak : projector.getPeaks()) { - Filament fil = (Filament) peak.getFilament(); + Filament fil = peak.getFilament(); double curvature = fil.getMeanCurvature(); if (curvature < params.minBarCurvature) { @@ -695,7 +693,7 @@ private void detectCurvedPeaks () private List findAlignmentsAbove (StaffPeak peak, Staff staffAbove) { - List alignments = new ArrayList(); + List alignments = new ArrayList<>(); for (StaffPeak peakAbove : projectorOf(staffAbove).getPeaks()) { BarAlignment alignment = checkAlignment(peakAbove, peak, true, true); @@ -758,7 +756,7 @@ private void findAlignmentsAndConnectionsOf (StaffPeak peak) private List findAlignmentsBelow (StaffPeak peak, Staff staffBelow) { - List alignments = new ArrayList(); + List alignments = new ArrayList<>(); for (StaffPeak peakBelow : projectorOf(staffBelow).getPeaks()) { BarAlignment alignment = checkAlignment(peak, peakBelow, true, true); @@ -826,7 +824,7 @@ private void findBarPeaks () private void findConnections () { // Check among the alignments for peaks connected across staves - for (BarAlignment alignment : new ArrayList(edgeSet())) { + for (BarAlignment alignment : new ArrayList<>(edgeSet())) { // Look for concrete connection checkConnection(alignment); } @@ -849,9 +847,8 @@ private List getConnectedPeaks (StaffPeak peak, int degree = (side == TOP) ? inDegreeOf(peak) : outDegreeOf(peak); if (degree > 0) { - SortedSet others = new TreeSet(); - Set edges = (side == TOP) ? incomingEdgesOf(peak) : outgoingEdgesOf( - peak); + SortedSet others = new TreeSet<>(); + Set edges = (side == TOP) ? incomingEdgesOf(peak) : outgoingEdgesOf(peak); for (BarAlignment edge : edges) { if (edge instanceof BarConnection) { @@ -859,7 +856,7 @@ private List getConnectedPeaks (StaffPeak peak, } } - return new ArrayList(others); + return new ArrayList<>(others); } return Collections.emptyList(); @@ -870,7 +867,7 @@ private List getConnectedPeaks (StaffPeak peak, //----------------// private List getConnections () { - List list = new ArrayList(); + List list = new ArrayList<>(); for (BarAlignment align : edgeSet()) { if (align instanceof BarConnection) { @@ -894,7 +891,7 @@ private List getConnections () */ private List> getGroupsOf (StaffProjector projector) { - final List> groups = new ArrayList>(); + final List> groups = new ArrayList<>(); final List peaks = projector.getPeaks(); int ig = -1; // Index of start of group @@ -952,7 +949,7 @@ private int getMaxPeaksWidth () */ private Set getPeaksToSplit (Collection peaks) { - Set toSplit = new LinkedHashSet(); + Set toSplit = new LinkedHashSet<>(); for (StaffPeak peak : peaks) { Map> map = checkForSplit(peak); @@ -981,7 +978,7 @@ private Set getPeaksToSplit (Collection peaks) */ private List
                                                                getSectionsByWidth (int maxWidth) { - List
                                                                sections = new ArrayList
                                                                (); + List
                                                                sections = new ArrayList<>(); Lag hLag = sheet.getLagManager().getLag(Lags.HLAG); Lag vLag = sheet.getLagManager().getLag(Lags.VLAG); @@ -1032,8 +1029,9 @@ private Integer[] getSystemTops () final int xOffset = p2.getStart() - p2.getStaff().getAbscissa(LEFT); if (xOffset > params.maxFirstConnectionXOffset) { - if ((p2 == projectorOf(p2.getStaff()).getLastPeak()) - || !areRightConnected(p1.getStaff(), p2.getStaff())) { + if ((p2 == projectorOf(p2.getStaff()).getLastPeak()) || !areRightConnected( + p1.getStaff(), + p2.getStaff())) { continue; } } @@ -1142,13 +1140,13 @@ private void pruneGroupPair (List g1, StaffPeak p1 = g1.get(i); StaffPeak p2 = g2.get(i); - for (BarAlignment align : new ArrayList(outgoingEdgesOf(p1))) { + for (BarAlignment align : new ArrayList<>(outgoingEdgesOf(p1))) { if (getEdgeTarget(align) != p2) { removeEdge(align); } } - for (BarAlignment align : new ArrayList(incomingEdgesOf(p2))) { + for (BarAlignment align : new ArrayList<>(incomingEdgesOf(p2))) { if (getEdgeSource(align) != p1) { removeEdge(align); } @@ -1174,7 +1172,7 @@ private void pruneGroupPair (List g1, */ private void purgeAlignments () { - Set toRemove = new LinkedHashSet(); + Set toRemove = new LinkedHashSet<>(); for (StaffPeak peak : vertexSet()) { if (peak.isVip()) { @@ -1182,13 +1180,13 @@ private void purgeAlignments () } if (inDegreeOf(peak) > 1) { - List edges = new ArrayList(incomingEdgesOf(peak)); + List edges = new ArrayList<>(incomingEdgesOf(peak)); edges.remove(BarAlignment.bestOf(edges, TOP)); toRemove.addAll(edges); } if (outDegreeOf(peak) > 1) { - List edges = new ArrayList(outgoingEdgesOf(peak)); + List edges = new ArrayList<>(outgoingEdgesOf(peak)); edges.remove(BarAlignment.bestOf(edges, BOTTOM)); toRemove.addAll(edges); } @@ -1209,7 +1207,7 @@ private void purgeAlignments () */ private void purgeCrossAlignments () { - final Set toRemove = new LinkedHashSet(); + final Set toRemove = new LinkedHashSet<>(); for (BarAlignment alignment : edgeSet()) { final SystemInfo s1 = alignment.getPeak(TOP).getStaff().getSystem(); @@ -1242,8 +1240,8 @@ private void rectifyAlignments (List topGroup, for (int i = 0; i < topGroup.size(); i++) { StaffPeak top = topGroup.get(i); StaffPeak bottom = bottomGroup.get(i); - removeAllEdges(new ArrayList(outgoingEdgesOf(top))); - removeAllEdges(new ArrayList(incomingEdgesOf(bottom))); + removeAllEdges(new ArrayList<>(outgoingEdgesOf(top))); + removeAllEdges(new ArrayList<>(incomingEdgesOf(bottom))); addEdge(top, bottom, checkAlignment(top, bottom, false, false)); } } @@ -1259,10 +1257,10 @@ private void rectifyAlignments (List topGroup, private int splitMergedGroups () { int count = 0; - Set toSplit = getPeaksToSplit(new ArrayList(vertexSet())); + Set toSplit = getPeaksToSplit(new ArrayList<>(vertexSet())); while (!toSplit.isEmpty()) { - Set impacted = new LinkedHashSet(); + Set impacted = new LinkedHashSet<>(); for (StaffPeak peak : toSplit) { if (splitPeak(peak, impacted)) { @@ -1386,14 +1384,12 @@ private int totalWidth (List peaks) return total; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, @@ -1453,7 +1449,6 @@ private static final class Constants //------------// private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final double maxAlignmentSlope; @@ -1479,13 +1474,12 @@ private static class Parameters final int maxFirstConnectionXOffset; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new Parameters object. * * @param scale the scaling factor */ - public Parameters (Scale scale) + Parameters (Scale scale) { maxAlignmentSlope = constants.maxAlignmentSlope.getValue(); maxAlignmentDeltaWidth = scale.toPixels(constants.maxAlignmentDeltaWidth); diff --git a/src/main/org/audiveris/omr/sheet/grid/StaffFilament.java b/src/main/org/audiveris/omr/sheet/grid/StaffFilament.java index 691b6c478..477a7a5f8 100644 --- a/src/main/org/audiveris/omr/sheet/grid/StaffFilament.java +++ b/src/main/org/audiveris/omr/sheet/grid/StaffFilament.java @@ -48,19 +48,18 @@ * candidate staff line, thus a filament within a cluster. *

                                                                * It is a CurvedFilament augmented by combs and cluster information. + * + * @author Hervé Bitteur */ public class StaffFilament extends CurvedFilament implements LineInfo { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(StaffFilament.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Combs where this filament appears. map (column index -> comb) */ private SortedMap combs; @@ -70,7 +69,6 @@ public class StaffFilament /** Relative position in cluster. (relevant only if cluster is not null) */ private int clusterPos; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LineFilament object. * Nota: this constructor is needed for FilamentFactory which calls this @@ -83,8 +81,6 @@ public StaffFilament (int interline) super(interline, InterlineScale.toPixels(interline, constants.segmentLength)); } - //~ Methods ------------------------------------------------------------------------------------ - // //---------// // addComb // //---------// @@ -98,7 +94,7 @@ public void addComb (int column, FilamentComb comb) { if (combs == null) { - combs = new TreeMap(); + combs = new TreeMap<>(); } combs.put(column, comb); @@ -161,7 +157,8 @@ public void fillHoles (int pos, for (int i = 1; i <= insert; i++) { int x = (int) Math.rint(holeStart + (i * dx)); - Point2D pt = new Filler(x, pos, fils, virtualLength / 2).findInsertion(); + Point2D pt = new Filler(x, pos, fils, virtualLength / 2) + .findInsertion(); if (pt == null) { // Take default line point instead @@ -220,7 +217,7 @@ public SortedMap getCombs () if (combs != null) { return combs; } else { - return new TreeMap(); + return new TreeMap<>(); } } @@ -372,14 +369,12 @@ protected String internals () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction segmentLength = new Scale.Fraction( 4.0, @@ -402,7 +397,6 @@ private static final class Constants */ private static class Filler { - //~ Instance fields ------------------------------------------------------------------------ final int x; // Preferred abscissa for point insertion @@ -412,11 +406,10 @@ private static class Filler final int margin; // Margin on abscissa to lookup refs - //~ Constructors --------------------------------------------------------------------------- - public Filler (int x, - int pos, - List fils, - int margin) + Filler (int x, + int pos, + List fils, + int margin) { this.x = x; this.pos = pos; @@ -424,7 +417,6 @@ public Filler (int x, this.margin = margin; } - //~ Methods -------------------------------------------------------------------------------- //---------------// // findInsertion // //---------------// @@ -483,19 +475,16 @@ private Neighbor findNeighbor (List subfils, return null; } - //~ Inner Classes -------------------------------------------------------------------------- /** Convey a point together with its relative cluster position.. */ - private class Neighbor + private static class Neighbor { - //~ Instance fields -------------------------------------------------------------------- final int pos; final Point2D point; - //~ Constructors ----------------------------------------------------------------------- - public Neighbor (int pos, - Point2D point) + Neighbor (int pos, + Point2D point) { this.pos = pos; this.point = point; @@ -512,12 +501,17 @@ public Neighbor (int pos, private static class VirtualPoint extends Point2D.Double { - //~ Constructors --------------------------------------------------------------------------- - public VirtualPoint (double x, - double y) + VirtualPoint (double x, + double y) { super(x, y); } + + @Override + public Object clone () + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } } diff --git a/src/main/org/audiveris/omr/sheet/grid/StaffLineCleaner.java b/src/main/org/audiveris/omr/sheet/grid/StaffLineCleaner.java index 6535763bf..cb5151abc 100644 --- a/src/main/org/audiveris/omr/sheet/grid/StaffLineCleaner.java +++ b/src/main/org/audiveris/omr/sheet/grid/StaffLineCleaner.java @@ -23,6 +23,7 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.glyph.dynamic.SectionCompound; import org.audiveris.omr.lag.Lag; import org.audiveris.omr.lag.Lags; import org.audiveris.omr.sheet.Sheet; @@ -46,13 +47,11 @@ */ public class StaffLineCleaner { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(StaffLineCleaner.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ @Navigable(false) private final Sheet sheet; @@ -60,7 +59,6 @@ public class StaffLineCleaner /** Horizontal lag. */ private final Lag hLag; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StaffLineCleaner} object. * @@ -73,10 +71,12 @@ public StaffLineCleaner (Sheet sheet) hLag = sheet.getLagManager().getLag(Lags.HLAG); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// + /** + * Clean the staff lines by "removing" the line glyphs. + */ public void process () { StopWatch watch = new StopWatch("StaffLineCleaner"); @@ -89,7 +89,7 @@ public void process () // Remove staff line sections from hLag for (LineInfo line : originals) { - hLag.removeSections(((StaffFilament) line).getMembers()); + hLag.removeSections(((SectionCompound) line).getMembers()); } } @@ -105,14 +105,12 @@ public void process () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/sheet/grid/StaffPattern.java b/src/main/org/audiveris/omr/sheet/grid/StaffPattern.java index 7f54de1b5..7c30ce8c3 100644 --- a/src/main/org/audiveris/omr/sheet/grid/StaffPattern.java +++ b/src/main/org/audiveris/omr/sheet/grid/StaffPattern.java @@ -33,7 +33,6 @@ */ public class StaffPattern { - //~ Instance fields ---------------------------------------------------------------------------- /** The number of lines expected in staff. */ private final int count; @@ -47,7 +46,6 @@ public class StaffPattern /** The precise interline value. */ private final double interline; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StaffPattern} object. * @@ -67,7 +65,6 @@ public StaffPattern (int count, this.interline = interline; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // evaluate // //----------// diff --git a/src/main/org/audiveris/omr/sheet/grid/StaffPeak.java b/src/main/org/audiveris/omr/sheet/grid/StaffPeak.java index 8d7db0daa..a09814c8a 100644 --- a/src/main/org/audiveris/omr/sheet/grid/StaffPeak.java +++ b/src/main/org/audiveris/omr/sheet/grid/StaffPeak.java @@ -37,6 +37,7 @@ import java.awt.Rectangle; import java.awt.geom.Point2D; import java.util.EnumSet; +import java.util.Objects; /** * Class {@code StaffPeak} represents a peak in staff projection onto x-axis. @@ -48,46 +49,7 @@ public class StaffPeak implements Comparable { - //~ Enumerations ------------------------------------------------------------------------------- - /** - * All attributes flags that can be assigned to a StaffPeak instance. - */ - public static enum Attribute - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** This is a thin peak */ - THIN, - /** This is a thick peak */ - THICK, - /** This peak defines staff left end */ - STAFF_LEFT_END, - /** This peak defines staff right end */ - STAFF_RIGHT_END, - /** This peak is a top portion of a bracket */ - BRACKET_TOP, - /** This peak is a middle portion of a bracket */ - BRACKET_MIDDLE, - /** This peak is a bottom portion of a bracket */ - BRACKET_BOTTOM, - /** This peak is the thick one of a C-Clef */ - CCLEF_ONE, - /** This peak is the thin one of a C-Clef */ - CCLEF_TWO, - /** This peak is part of the tail of a C-Clef */ - CCLEF_TAIL, - /** This peak is a portion of a brace */ - BRACE, - /** This peak is a top portion of a brace */ - BRACE_TOP, - /** This peak is a middle portion of a brace */ - BRACE_MIDDLE, - /** This peak is a bottom portion of a brace */ - BRACE_BOTTOM; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Containing staff. */ protected final Staff staff; @@ -109,25 +71,24 @@ public static enum Attribute /** Underlying filament. */ protected Filament filament; - /** Top serif filament, if any. */ - private Filament topSerif; - - /** Bottom serif filament, if any. */ - private Filament bottomSerif; - /** Corresponding inter, if any. */ protected Inter inter; /** Attributes currently set. */ protected final EnumSet attrs = EnumSet.noneOf(Attribute.class); + /** Top serif filament, if any. */ + private Filament topSerif; + + /** Bottom serif filament, if any. */ + private Filament bottomSerif; + /** Evaluation, if any. */ private final GradeImpacts impacts; /** Containing column, if any. */ private BarColumn column; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StaffPeak} object. * @@ -153,15 +114,19 @@ public StaffPeak (Staff staff, this.impacts = impacts; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // compareTo // //-----------// @Override public int compareTo (StaffPeak that) { - if (this.getStaff() != that.getStaff()) { - return Staff.byId.compare(this.getStaff(), that.getStaff()); + if (this == that) { + return 0; + } + + // Total ordering, first by staff order, then by abscissa in staff + if (this.staff != that.staff) { + return Staff.byId.compare(this.staff, that.staff); } return Integer.compare(this.start, that.start); @@ -170,6 +135,11 @@ public int compareTo (StaffPeak that) //-----------------------// // computeDeskewedCenter // //-----------------------// + /** + * Compute the (de-skewed) peak center. + * + * @param skew global sheet skew + */ public void computeDeskewedCenter (Skew skew) { Point2D mid = new Point2D.Double((start + stop) / 2.0, (top + bottom) / 2.0); @@ -177,6 +147,20 @@ public void computeDeskewedCenter (Skew skew) dsk = skew.deskewed(mid); } + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + + if (obj instanceof StaffPeak) { + return compareTo((StaffPeak) obj) == 0; + } + + return false; + } + //-----------// // getBottom // //-----------// @@ -202,6 +186,11 @@ public Filament getBottomSerif () //-----------// // getBounds // //-----------// + /** + * Report the bounding box of the peak. + * + * @return bounds + */ public Rectangle getBounds () { return new Rectangle(start, top, getWidth(), bottom - top + 1); @@ -218,9 +207,27 @@ public BarColumn getColumn () return column; } + //-----------// + // setColumn // + //-----------// + /** + * Assign the containing column. + * + * @param column the column to set + */ + public void setColumn (BarColumn column) + { + this.column = column; + } + //---------------------// // getDeskewedAbscissa // //---------------------// + /** + * Report the abscissa of peak de-skewed center. + * + * @return (de-skewed) x + */ public double getDeskewedAbscissa () { return dsk.getX(); @@ -229,6 +236,11 @@ public double getDeskewedAbscissa () //-------------------// // getDeskewedCenter // //-------------------// + /** + * Report the de-skewed peak center + * + * @return de-skewed center + */ public Point2D getDeskewedCenter () { return dsk; @@ -237,15 +249,35 @@ public Point2D getDeskewedCenter () //-------------// // getFilament // //-------------// + /** + * Report the related filament, if any + * + * @return filament, perhaps null + */ public Filament getFilament () { return filament; } + //-------------// + // setFilament // + //-------------// + /** + * Assign a related filament. + * + * @param filament the related filament + */ + public void setFilament (Filament filament) + { + this.filament = filament; + } + //------------// // getImpacts // //------------// /** + * Report the peak evaluation, if any. + * * @return the impacts */ public GradeImpacts getImpacts () @@ -257,6 +289,8 @@ public GradeImpacts getImpacts () // getInter // //----------// /** + * Report the related inter, if any. + * * @return the inter */ public Inter getInter () @@ -264,9 +298,28 @@ public Inter getInter () return inter; } + //----------// + // setInter // + //----------// + /** + * Set the related Inter. + * + * @param inter the inter to set (instance of BraceInter or AbstractVerticalInter) + */ + public void setInter (Inter inter) + { + this.inter = inter; + } + //-------------// // getOrdinate // //-------------// + /** + * Report peak ordinate on desired vertical side. + * + * @param side the desired vertical side + * @return the end ordinate on given side + */ public int getOrdinate (VerticalSide side) { if (side == VerticalSide.TOP) { @@ -280,6 +333,8 @@ public int getOrdinate (VerticalSide side) // getStaff // //----------// /** + * Report the underlying staff. + * * @return the staff */ public Staff getStaff () @@ -291,6 +346,8 @@ public Staff getStaff () // getStart // //----------// /** + * Report the starting abscissa + * * @return the start */ public int getStart () @@ -302,6 +359,8 @@ public int getStart () // getStop // //---------// /** + * Report the ending abscissa + * * @return the stop */ public int getStop () @@ -313,6 +372,8 @@ public int getStop () // getTop // //--------// /** + * Return line ordinate at top of peak. + * * @return the top */ public int getTop () @@ -324,6 +385,8 @@ public int getTop () // getTopSerif // //-------------// /** + * Report the serif at top of peak, if any + * * @return the topSerif */ public Filament getTopSerif () @@ -334,11 +397,26 @@ public Filament getTopSerif () //----------// // getWidth // //----------// + /** + * Report peak width + * + * @return the width of peak + */ public int getWidth () { return stop - start + 1; } + @Override + public int hashCode () + { + int hash = 5; + hash = (71 * hash) + Objects.hashCode(staff); + hash = (71 * hash) + this.start; + + return hash; + } + //---------// // isBrace // //---------// @@ -407,35 +485,15 @@ public final boolean isSet (Attribute attr) return attrs.contains(attr); } - //-----// - // set // - //-----// - /** - * Set the provided attribute to this instance. - * - * @param attr provided attribute - */ - public final void set (Attribute attr) - { - attrs.add(attr); - } - - //-------// - // unset // - //-------// - /** - * Un-set the provided attribute to this instance. - * - * @param attr provided attribute - */ - public final void unset (Attribute attr) - { - attrs.remove(attr); - } - //------------// // isStaffEnd // //------------// + /** + * Tell whether this peak is a staff end on provided horizontal side. + * + * @param side desired horizontal side + * @return true if so + */ public boolean isStaffEnd (HorizontalSide side) { return isSet((side == LEFT) ? STAFF_LEFT_END : STAFF_RIGHT_END); @@ -444,6 +502,11 @@ public boolean isStaffEnd (HorizontalSide side) //-------// // isVip // //-------// + /** + * Tell whether it's a VIP object + * + * @return true if so + */ public boolean isVip () { return (filament != null) && filament.isVip(); @@ -452,6 +515,11 @@ public boolean isVip () //--------// // render // //--------// + /** + * Render this peak on the provided graphics + * + * @param g graphics context + */ public void render (Graphics2D g) { g.setColor( @@ -460,6 +528,19 @@ public void render (Graphics2D g) g.fillRect(start, top, stop - start + 1, bottom - top + 1); } + //-----// + // set // + //-----// + /** + * Set the provided attribute to this instance. + * + * @param attr provided attribute + */ + public final void set (Attribute attr) + { + attrs.add(attr); + } + //---------------// // setBracketEnd // //---------------// @@ -481,38 +562,6 @@ public void setBracketEnd (VerticalSide side, } } - //-----------// - // setColumn // - //-----------// - /** - * @param column the column to set - */ - public void setColumn (BarColumn column) - { - this.column = column; - } - - //-------------// - // setFilament // - //-------------// - public void setFilament (Filament filament) - { - this.filament = filament; - } - - //----------// - // setInter // - //----------// - /** - * Set the related Inter. - * - * @param inter the inter to set (instance of BraceInter or AbstractVerticalInter) - */ - public void setInter (Inter inter) - { - this.inter = inter; - } - //-------------// // setStaffEnd // //-------------// @@ -551,9 +600,27 @@ public String toString () return sb.toString(); } + //-------// + // unset // + //-------// + /** + * Un-set the provided attribute to this instance. + * + * @param attr provided attribute + */ + public final void unset (Attribute attr) + { + attrs.remove(attr); + } + //-----------// // internals // //-----------// + /** + * Report a string description of class internals + * + * @return string description of internals + */ protected String internals () { StringBuilder sb = new StringBuilder(); @@ -564,4 +631,39 @@ protected String internals () return sb.toString(); } + + /** + * All attributes flags that can be assigned to a StaffPeak instance. + */ + public static enum Attribute + { + /** This is a thin peak */ + THIN, + /** This is a thick peak */ + THICK, + /** This peak defines staff left end */ + STAFF_LEFT_END, + /** This peak defines staff right end */ + STAFF_RIGHT_END, + /** This peak is a top portion of a bracket */ + BRACKET_TOP, + /** This peak is a middle portion of a bracket */ + BRACKET_MIDDLE, + /** This peak is a bottom portion of a bracket */ + BRACKET_BOTTOM, + /** This peak is the thick one of a C-Clef */ + CCLEF_ONE, + /** This peak is the thin one of a C-Clef */ + CCLEF_TWO, + /** This peak is part of the tail of a C-Clef */ + CCLEF_TAIL, + /** This peak is a portion of a brace */ + BRACE, + /** This peak is a top portion of a brace */ + BRACE_TOP, + /** This peak is a middle portion of a brace */ + BRACE_MIDDLE, + /** This peak is a bottom portion of a brace */ + BRACE_BOTTOM; + } } diff --git a/src/main/org/audiveris/omr/sheet/grid/StaffProjector.java b/src/main/org/audiveris/omr/sheet/grid/StaffProjector.java index e7fa5ea92..eb221f230 100644 --- a/src/main/org/audiveris/omr/sheet/grid/StaffProjector.java +++ b/src/main/org/audiveris/omr/sheet/grid/StaffProjector.java @@ -77,27 +77,37 @@ * a barline must be present. * The potential bar portions outside staff height are much less typical of a barline. *

                                                                - * A peak in staff projection can result from:

                                                                  + * A peak in staff projection can result from: + *
                                                                    *
                                                                  1. A thick or thin bar line:
                                                                    * Image of bar lines - * + * src= + * "http://upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Barlines.svg/400px-Barlines.svg.png"> + *

                                                                    + * or *

                                                                  2. A bracket portion:
                                                                    - * Image of bracket - * + *

                                                                    + * or *

                                                                  3. A brace portion:
                                                                    * Image of brace - * + * src= + * "http://upload.wikimedia.org/wikipedia/commons/thumb/2/28/Brace_(music).png/240px-Brace_(music).png"> + *

                                                                    + * or *

                                                                  4. An Alto C-clef portion:
                                                                    * Image of alto clef + * src= + * "http://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Alto_clef_with_ref.svg/90px-Alto_clef_with_ref.svg.png"> *
                                                                    * Such C-clef artifacts are detected later, based on their abscissa offset from the measure - * start (be it bar-based start or lines-only start). - * + * start (be it barline-based start or lines-only start). + *

                                                                    + * or *

                                                                  5. A stem (with note heads located outside the staff height). + *

                                                                    + * or *

                                                                  6. Just garbage. *
                                                                  *

                                                                  @@ -116,14 +126,11 @@ */ public class StaffProjector { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - StaffProjector.class); + private static final Logger logger = LoggerFactory.getLogger(StaffProjector.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying sheet. */ @Navigable(false) private final Sheet sheet; @@ -141,14 +148,13 @@ public class StaffProjector private final ByteProcessor pixelFilter; /** Sequence of all blank regions found, whatever their width. */ - private final List allBlanks = new ArrayList(); + private final List allBlanks = new ArrayList<>(); /** Selected (wide) ending blank region on each staff side. */ - private final Map endingBlanks = new EnumMap( - HorizontalSide.class); + private final Map endingBlanks = new EnumMap<>(HorizontalSide.class); /** Sequence of peaks found. */ - private final List peaks = new ArrayList(); + private final List peaks = new ArrayList<>(); /** (Unmodifiable) view on peaks. */ private final List peaksView = Collections.unmodifiableList(peaks); @@ -162,7 +168,6 @@ public class StaffProjector /** Initial brace peak, if any. */ private StaffPeak bracePeak; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StaffProjector} object. * @@ -185,7 +190,6 @@ public StaffProjector (Sheet sheet, params = new Parameters(scale, staff.getSpecificInterline()); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // checkLinesRoot // //----------------// @@ -315,6 +319,19 @@ public StaffPeak getBracePeak () return bracePeak; } + //--------------// + // setBracePeak // + //--------------// + /** + * Assign the brace peak. + * + * @param bracePeak the bracePeak to set + */ + public void setBracePeak (StaffPeak bracePeak) + { + this.bracePeak = bracePeak; + } + //-------------// // getLastPeak // //-------------// @@ -519,7 +536,7 @@ public void refineRightEnd () staff.setAbscissa(RIGHT, xMax); } else { // No significant line chunks, ignore them and stay with peak as the limit - final int peakMid = (endPeak.getStart() + endPeak.getStop()) / 2; + final int peakMid = (endPeak.getStart() + endPeak.getStop()) >>> 1; logger.debug( "Staff#{} RIGHT set at peak {} (vs {})", staff.getId(), @@ -567,17 +584,6 @@ public void removePeaks (Collection toRemove) } } - //--------------// - // setBracePeak // - //--------------// - /** - * @param bracePeak the bracePeak to set - */ - public void setBracePeak (StaffPeak bracePeak) - { - this.bracePeak = bracePeak; - } - //----------// // toString // //----------// @@ -606,7 +612,7 @@ private List browseRange (final int rangeStart, { logger.debug("Staff#{} browseRange [{}..{}]", staff.getId(), rangeStart, rangeStop); - final List list = new ArrayList(); + final List list = new ArrayList<>(); int start = rangeStart; int stop; @@ -711,10 +717,9 @@ private void computeLineThresholds () params.linesThreshold = (int) Math.rint(linesCumul); params.blankThreshold = (int) Math.rint( constants.blankThreshold.getValue() * lineThickness); - params.chunkThreshold = (4 * scale.getMaxFore()) - + InterlineScale.toPixels( - staff.getSpecificInterline(), - constants.chunkThreshold); + params.chunkThreshold = (4 * scale.getMaxFore()) + InterlineScale.toPixels( + staff.getSpecificInterline(), + constants.chunkThreshold); logger.debug( "Staff#{} linesThreshold:{} chunkThreshold:{}", staff.getId(), @@ -880,10 +885,8 @@ private StaffPeak createPeak (final int rawStart, // If peak is very thin, thicken the lookup area final int width = stop - start + 1; final int dx = (width <= 2) ? 1 : 0; - GeoPath leftLine = new GeoPath( - new Line2D.Double(start - dx, yTop, start - dx, yBottom)); - GeoPath rightLine = new GeoPath( - new Line2D.Double(stop + dx, yTop, stop + dx, yBottom)); + GeoPath leftLine = new GeoPath(new Line2D.Double(start - dx, yTop, start - dx, yBottom)); + GeoPath rightLine = new GeoPath(new Line2D.Double(stop + dx, yTop, stop + dx, yBottom)); final CoreData data = AreaUtil.verticalCore(pixelFilter, leftLine, rightLine); if (data.gap > params.gapThreshold) { @@ -1161,39 +1164,168 @@ private int xClamp (int x) return x; } - //~ Inner Classes ------------------------------------------------------------------------------ - //----------// - // PeakSide // - //----------// + //---------// + // Plotter // + //---------// /** - * Describes the (left or right) side of a peak. + * Handles the display of projection chart. */ - static class PeakSide + private class Plotter { - //~ Instance fields ------------------------------------------------------------------------ - /** Precise side abscissa. */ - final int abscissa; + final XYSeriesCollection dataset = new XYSeriesCollection(); - /** Quality based on derivative absolute value. */ - final double grade; + // Chart + final JFreeChart chart = ChartFactory.createXYLineChart( + sheet.getId() + " staff#" + getStaff().getId(), // Title + "Abscissae - staff interline:" + staff.getSpecificInterline(), // X-Axis label + "Counts", // Y-Axis label + dataset, // Dataset + PlotOrientation.VERTICAL, // orientation, + true, // Show legend + false, // Show tool tips + false // urls + ); + + final XYPlot plot = (XYPlot) chart.getPlot(); - //~ Constructors --------------------------------------------------------------------------- - public PeakSide (int abscissa, - double grade) + final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + + // Series index + int index = -1; + + public void plot () { - this.abscissa = abscissa; - this.grade = grade; + plot.setRenderer(renderer); + + final int xMin = 0; + final int xMax = sheet.getWidth() - 1; + + { + // Values + XYSeries valueSeries = new XYSeries("Cumuls", false); // No autosort + + for (int x = xMin; x <= xMax; x++) { + valueSeries.add(x, projection.getValue(x)); + } + + add(valueSeries, Colors.CHART_VALUE, false); + } + + { + // Derivatives + XYSeries derivativeSeries = new XYSeries("Derivatives", false); // No autosort + + for (int x = xMin; x <= xMax; x++) { + derivativeSeries.add(x, projection.getDerivative(x)); + } + + add(derivativeSeries, Colors.CHART_DERIVATIVE, false); + } + + { + // Derivatives positive threshold + XYSeries derSeries = new XYSeries("Der+", false); // No autosort + + derSeries.add(xMin, params.minDerivative); + derSeries.add(xMax, params.minDerivative); + add(derSeries, Colors.CHART_DERIVATIVE, false); + } + + { + // Derivatives negative threshold + XYSeries derSeries = new XYSeries("Der-", false); // No autosort + + derSeries.add(xMin, -params.minDerivative); + derSeries.add(xMax, -params.minDerivative); + add(derSeries, Colors.CHART_DERIVATIVE, false); + } + + { + // Theoretical staff height (assuming a 5-line staff) + XYSeries heightSeries = new XYSeries("StaffHeight", false); // No autosort + int totalHeight = 4 * staff.getSpecificInterline(); + heightSeries.add(xMin, totalHeight); + heightSeries.add(xMax, totalHeight); + add(heightSeries, Color.BLACK, true); + } + + { + // BarPeak min threshold + XYSeries minSeries = new XYSeries("MinBar", false); // No autosort + minSeries.add(xMin, params.barThreshold); + minSeries.add(xMax, params.barThreshold); + add(minSeries, Color.GREEN, true); + } + + { + // Chunk threshold (assuming a 5-line staff) + XYSeries chunkSeries = new XYSeries("MaxChunk", false); // No autosort + chunkSeries.add(xMin, params.chunkThreshold); + chunkSeries.add(xMax, params.chunkThreshold); + add(chunkSeries, Color.YELLOW, true); + } + + { + // BracePeak min threshold + XYSeries minSeries = new XYSeries("MinBrace", false); // No autosort + minSeries.add(xMin, params.braceThreshold); + minSeries.add(xMax, params.braceThreshold); + add(minSeries, Color.ORANGE, true); + } + + { + // Cumulated staff lines (assuming a 5-line staff) + XYSeries linesSeries = new XYSeries("Lines", false); // No autosort + linesSeries.add(xMin, params.linesThreshold); + linesSeries.add(xMax, params.linesThreshold); + add(linesSeries, Color.MAGENTA, true); + } + + { + // Threshold for no staff + final int nostaff = params.blankThreshold; + XYSeries holeSeries = new XYSeries("NoStaff", false); // No autosort + holeSeries.add(xMin, nostaff); + holeSeries.add(xMax, nostaff); + add(holeSeries, Color.CYAN, true); + } + + { + // Zero + XYSeries zeroSeries = new XYSeries("Zero", false); // No autosort + zeroSeries.add(xMin, 0); + zeroSeries.add(xMax, 0); + add(zeroSeries, Colors.CHART_ZERO, true); + } + + // Hosting frame + ChartFrame frame = new ChartFrame( + sheet.getId() + " staff#" + getStaff().getId(), + chart, + true); + frame.pack(); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + frame.setLocation(new Point(20 * getStaff().getId(), 20 * getStaff().getId())); + frame.setVisible(true); + } + + private void add (XYSeries series, + Color color, + boolean displayShapes) + { + dataset.addSeries(series); + renderer.setSeriesPaint(++index, color); + renderer.setSeriesShapesVisible(index, displayShapes); } } //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction staffAbscissaMargin = new Scale.Fraction( 15, @@ -1220,7 +1352,7 @@ private static final class Constants "Minimum cumul value to detect brace peak"); private final Scale.Fraction gapThreshold = new Scale.Fraction( - 0.85, + 0.6, "Maximum vertical gap length in a bar"); private final Scale.Fraction chunkThreshold = new Scale.Fraction( @@ -1264,7 +1396,6 @@ private static final class Constants private static class Blank implements Comparable { - //~ Instance fields ------------------------------------------------------------------------ /** First abscissa in region. */ private final int start; @@ -1272,26 +1403,48 @@ private static class Blank /** Last abscissa in region. */ private final int stop; - //~ Constructors --------------------------------------------------------------------------- - public Blank (int start, - int stop) + Blank (int start, + int stop) { this.start = start; this.stop = stop; } - //~ Methods -------------------------------------------------------------------------------- @Override public int compareTo (Blank that) { + // This is a total ordering of blanks (within the same staff projection) return Integer.compare(this.start, that.start); } + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + + if (obj instanceof Blank) { + return compareTo((Blank) obj) == 0; + } + + return false; + } + public int getWidth () { return stop - start + 1; } + @Override + public int hashCode () + { + int hash = 3; + hash = (79 * hash) + this.start; + + return hash; + } + @Override public String toString () { @@ -1307,7 +1460,6 @@ public String toString () //------------// private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int staffAbscissaMargin; @@ -1343,9 +1495,8 @@ private static class Parameters int chunkThreshold; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale, - int staffSpecific) + Parameters (Scale scale, + int staffSpecific) { { // Use sheet large interline value @@ -1372,161 +1523,27 @@ public Parameters (Scale scale, } } - //---------// - // Plotter // - //---------// + //----------// + // PeakSide // + //----------// /** - * Handles the display of projection chart. + * Describes the (left or right) side of a peak. */ - private class Plotter + static class PeakSide { - //~ Instance fields ------------------------------------------------------------------------ - - final XYSeriesCollection dataset = new XYSeriesCollection(); - - // Chart - final JFreeChart chart = ChartFactory.createXYLineChart( - sheet.getId() + " staff#" + getStaff().getId(), // Title - "Abscissae - staff interline:" + staff.getSpecificInterline(), // X-Axis label - "Counts", // Y-Axis label - dataset, // Dataset - PlotOrientation.VERTICAL, // orientation, - true, // Show legend - false, // Show tool tips - false // urls - ); - - final XYPlot plot = (XYPlot) chart.getPlot(); - final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + /** Precise side abscissa. */ + final int abscissa; - // Series index - int index = -1; + /** Quality based on derivative absolute value. */ + final double grade; - //~ Methods -------------------------------------------------------------------------------- - public void plot () + PeakSide (int abscissa, + double grade) { - plot.setRenderer(renderer); - - final int xMin = 0; - final int xMax = sheet.getWidth() - 1; - - { - // Values - XYSeries valueSeries = new XYSeries("Cumuls", false); // No autosort - - for (int x = xMin; x <= xMax; x++) { - valueSeries.add(x, projection.getValue(x)); - } - - add(valueSeries, Colors.CHART_VALUE, false); - } - - { - // Derivatives - XYSeries derivativeSeries = new XYSeries("Derivatives", false); // No autosort - - for (int x = xMin; x <= xMax; x++) { - derivativeSeries.add(x, projection.getDerivative(x)); - } - - add(derivativeSeries, Colors.CHART_DERIVATIVE, false); - } - - { - // Derivatives positive threshold - XYSeries derSeries = new XYSeries("Der+", false); // No autosort - - derSeries.add(xMin, params.minDerivative); - derSeries.add(xMax, params.minDerivative); - add(derSeries, Colors.CHART_DERIVATIVE, false); - } - - { - // Derivatives negative threshold - XYSeries derSeries = new XYSeries("Der-", false); // No autosort - - derSeries.add(xMin, -params.minDerivative); - derSeries.add(xMax, -params.minDerivative); - add(derSeries, Colors.CHART_DERIVATIVE, false); - } - - { - // Theoretical staff height (assuming a 5-line staff) - XYSeries heightSeries = new XYSeries("StaffHeight", false); // No autosort - int totalHeight = 4 * staff.getSpecificInterline(); - heightSeries.add(xMin, totalHeight); - heightSeries.add(xMax, totalHeight); - add(heightSeries, Color.BLACK, true); - } - - { - // BarPeak min threshold - XYSeries minSeries = new XYSeries("MinBar", false); // No autosort - minSeries.add(xMin, params.barThreshold); - minSeries.add(xMax, params.barThreshold); - add(minSeries, Color.GREEN, true); - } - - { - // Chunk threshold (assuming a 5-line staff) - XYSeries chunkSeries = new XYSeries("MaxChunk", false); // No autosort - chunkSeries.add(xMin, params.chunkThreshold); - chunkSeries.add(xMax, params.chunkThreshold); - add(chunkSeries, Color.YELLOW, true); - } - - { - // BracePeak min threshold - XYSeries minSeries = new XYSeries("MinBrace", false); // No autosort - minSeries.add(xMin, params.braceThreshold); - minSeries.add(xMax, params.braceThreshold); - add(minSeries, Color.ORANGE, true); - } - - { - // Cumulated staff lines (assuming a 5-line staff) - XYSeries linesSeries = new XYSeries("Lines", false); // No autosort - linesSeries.add(xMin, params.linesThreshold); - linesSeries.add(xMax, params.linesThreshold); - add(linesSeries, Color.MAGENTA, true); - } - - { - // Threshold for no staff - final int nostaff = params.blankThreshold; - XYSeries holeSeries = new XYSeries("NoStaff", false); // No autosort - holeSeries.add(xMin, nostaff); - holeSeries.add(xMax, nostaff); - add(holeSeries, Color.CYAN, true); - } - - { - // Zero - XYSeries zeroSeries = new XYSeries("Zero", false); // No autosort - zeroSeries.add(xMin, 0); - zeroSeries.add(xMax, 0); - add(zeroSeries, Colors.CHART_ZERO, true); - } - - // Hosting frame - ChartFrame frame = new ChartFrame( - sheet.getId() + " staff#" + getStaff().getId(), - chart, - true); - frame.pack(); - frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - frame.setLocation(new Point(20 * getStaff().getId(), 20 * getStaff().getId())); - frame.setVisible(true); + this.abscissa = abscissa; + this.grade = grade; } - private void add (XYSeries series, - Color color, - boolean displayShapes) - { - dataset.addSeries(series); - renderer.setSeriesPaint(++index, color); - renderer.setSeriesShapesVisible(index, displayShapes); - } } } diff --git a/src/main/org/audiveris/omr/sheet/grid/TargetBuilder.java b/src/main/org/audiveris/omr/sheet/grid/TargetBuilder.java index 8632dbf21..49a8512b9 100644 --- a/src/main/org/audiveris/omr/sheet/grid/TargetBuilder.java +++ b/src/main/org/audiveris/omr/sheet/grid/TargetBuilder.java @@ -69,13 +69,11 @@ public class TargetBuilder implements ItemRenderer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(TargetBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet */ @Navigable(false) private final Sheet sheet; @@ -93,15 +91,14 @@ public class TargetBuilder private TargetPage targetPage; /** All target lines */ - private final List allTargetLines = new ArrayList(); + private final List allTargetLines = new ArrayList<>(); /** Source points */ - private final List srcPoints = new ArrayList(); + private final List srcPoints = new ArrayList<>(); /** Destination points */ - private final List dstPoints = new ArrayList(); + private final List dstPoints = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TargetBuilder object. * @@ -112,10 +109,12 @@ public TargetBuilder (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // buildInfo // //-----------// + /** + * Build a de-warped image according to target grid. + */ public void buildInfo () { buildTarget(); @@ -231,9 +230,8 @@ public void renderWarpGrid (Graphics g, // buildTarget // //-------------// /** - * Build a perfect definition of target page, systems, staves and - * lines. - * + * Build a perfect definition of target page, systems, staves and lines. + *

                                                                  * We apply a rotation on every top-left corner */ private void buildTarget () @@ -340,7 +338,7 @@ private void buildWarpGrid (JaiDewarper dewarper) /** * This key method provides the source point (in original sheet image) * that corresponds to a given destination point (in target dewarped image). - * + *

                                                                  * The strategy is to stay consistent with the staff lines nearby which * are used as grid references. * @@ -404,14 +402,38 @@ private void storeImage (RenderedImage dewarpedImage) } } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------------// + // DewarpedView // + //--------------// + private class DewarpedView + extends ImageView + { + + DewarpedView (RenderedImage image) + { + super(image); + + // setModelSize(new Dimension(image.getWidth(), image.getHeight())); + // Location service + setLocationService(sheet.getLocationService()); + + setName("DewarpedView"); + } + + @Override + protected void renderItems (Graphics2D g) + { + // Display the Destination Points + renderWarpGrid(g, false); + } + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean showDewarpGrid = new Constant.Boolean( false, @@ -433,32 +455,4 @@ private static final class Constants false, "Should we store the dewarped image on disk?"); } - - //--------------// - // DewarpedView // - //--------------// - private class DewarpedView - extends ImageView - { - //~ Constructors --------------------------------------------------------------------------- - - public DewarpedView (RenderedImage image) - { - super(image); - - // setModelSize(new Dimension(image.getWidth(), image.getHeight())); - // Location service - setLocationService(sheet.getLocationService()); - - setName("DewarpedView"); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - protected void renderItems (Graphics2D g) - { - // Display the Destination Points - renderWarpGrid(g, false); - } - } } diff --git a/src/main/org/audiveris/omr/sheet/grid/TargetLine.java b/src/main/org/audiveris/omr/sheet/grid/TargetLine.java index 6a7e7a7c3..0d659bb7b 100644 --- a/src/main/org/audiveris/omr/sheet/grid/TargetLine.java +++ b/src/main/org/audiveris/omr/sheet/grid/TargetLine.java @@ -32,7 +32,6 @@ */ public class TargetLine { - //~ Instance fields ---------------------------------------------------------------------------- /** Related raw information */ public final LineInfo info; @@ -49,7 +48,6 @@ public class TargetLine /** Cosine of raw line angle */ private final double cos; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TargetLine object. * @@ -75,7 +73,6 @@ public TargetLine (LineInfo info, cos = dx / hypot; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // sourceOf // //----------// @@ -112,8 +109,8 @@ public Point2D sourceOf (double dstX) double left = staff.system.left; double right = staff.system.right; double xRatio = (dstX - left) / (right - left); - double srcX = ((1 - xRatio) * info.getEndPoint(LEFT).getX()) - + (xRatio * info.getEndPoint(RIGHT).getX()); + double srcX = ((1 - xRatio) * info.getEndPoint(LEFT).getX()) + (xRatio * info.getEndPoint( + RIGHT).getX()); double srcY = info.yAt(srcX); return new Point2D.Double(srcX, srcY); diff --git a/src/main/org/audiveris/omr/sheet/grid/TargetPage.java b/src/main/org/audiveris/omr/sheet/grid/TargetPage.java index 827008397..5c66e760a 100644 --- a/src/main/org/audiveris/omr/sheet/grid/TargetPage.java +++ b/src/main/org/audiveris/omr/sheet/grid/TargetPage.java @@ -31,7 +31,6 @@ */ public class TargetPage { - //~ Instance fields ---------------------------------------------------------------------------- /** Page width */ public final double width; @@ -40,9 +39,8 @@ public class TargetPage public final double height; /** Sequence of systems */ - public final List systems = new ArrayList(); + public final List systems = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TargetPage object. * @@ -56,7 +54,6 @@ public TargetPage (double width, this.height = height; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/sheet/grid/TargetStaff.java b/src/main/org/audiveris/omr/sheet/grid/TargetStaff.java index 7c45d57e7..5bec1a2ad 100644 --- a/src/main/org/audiveris/omr/sheet/grid/TargetStaff.java +++ b/src/main/org/audiveris/omr/sheet/grid/TargetStaff.java @@ -33,7 +33,6 @@ */ public class TargetStaff { - //~ Instance fields ---------------------------------------------------------------------------- /** Initial raw information */ public final Staff info; @@ -45,17 +44,16 @@ public class TargetStaff public final double top; /** Sequence of staff lines */ - public final List lines = new ArrayList(); + public final List lines = new ArrayList<>(); /** Containing system */ public final TargetSystem system; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TargetStaff object. * - * @param info initial raw information - * @param top Ordinate of top in containing page + * @param info initial raw information + * @param top Ordinate of top in containing page * @param system the containing system */ public TargetStaff (Staff info, @@ -69,7 +67,6 @@ public TargetStaff (Staff info, id = info.getId(); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/sheet/grid/TargetSystem.java b/src/main/org/audiveris/omr/sheet/grid/TargetSystem.java index 2ebd0e3a6..32d281e14 100644 --- a/src/main/org/audiveris/omr/sheet/grid/TargetSystem.java +++ b/src/main/org/audiveris/omr/sheet/grid/TargetSystem.java @@ -33,7 +33,6 @@ */ public class TargetSystem { - //~ Instance fields ---------------------------------------------------------------------------- /** Raw information */ public final SystemInfo info; @@ -51,9 +50,8 @@ public class TargetSystem public final double right; /** Sequence of staves */ - public final List staves = new ArrayList(); + public final List staves = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TargetSystem object. * @@ -75,7 +73,6 @@ public TargetSystem (SystemInfo info, id = info.getId(); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/sheet/header/ClefBuilder.java b/src/main/org/audiveris/omr/sheet/header/ClefBuilder.java index 6c30871c9..2d430495d 100644 --- a/src/main/org/audiveris/omr/sheet/header/ClefBuilder.java +++ b/src/main/org/audiveris/omr/sheet/header/ClefBuilder.java @@ -46,7 +46,6 @@ import org.audiveris.omr.sheet.Sheet; import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sheet.header.StaffHeader; import org.audiveris.omr.sig.GradeUtil; import org.audiveris.omr.sig.SIGraph; import org.audiveris.omr.sig.inter.ClefInter; @@ -90,7 +89,6 @@ */ public class ClefBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -109,7 +107,6 @@ public class ClefBuilder C_CLEF, PERCUSSION_CLEF); - //~ Instance fields ---------------------------------------------------------------------------- /** Dedicated staff to analyze. */ private final Staff staff; @@ -143,9 +140,8 @@ public class ClefBuilder private final Classifier classifier = ShapeClassifier.getInstance(); /** All glyphs submitted to classifier. */ - private final Set glyphCandidates = new LinkedHashSet(); + private final Set glyphCandidates = new LinkedHashSet<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ClefBuilder object. * @@ -170,7 +166,6 @@ public ClefBuilder (Staff staff) } } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // findClefs // //-----------// @@ -238,11 +233,11 @@ private Map getBestMap (boolean isFirstPass) // Formalize parts relationships in a global graph SimpleGraph graph = Glyphs.buildLinks(parts, params.maxPartGap); - List> sets = new ConnectivityInspector(graph).connectedSets(); + List> sets = new ConnectivityInspector<>(graph).connectedSets(); logger.debug("Staff#{} sets: {}", staff.getId(), sets.size()); // Best inter per clef kind - Map bestMap = new EnumMap(ClefKind.class); + Map bestMap = new EnumMap<>(ClefKind.class); for (Set set : sets) { // Use only the subgraph for this set @@ -368,7 +363,7 @@ private List getParts (boolean isFirstPass) private void purgeClefs (Map bestMap) { final double maxContrib = ClefKeyRelation.maxContributionForClef(); - final List inters = new ArrayList(bestMap.values()); + final List inters = new ArrayList<>(bestMap.values()); Collections.sort(inters, Inters.byReverseGrade); interLoop: @@ -405,7 +400,7 @@ private void purgeClefs (Map bestMap) private void purgeParts (List parts, boolean isFirstPass) { - List toRemove = new ArrayList(); + List toRemove = new ArrayList<>(); for (Glyph part : parts) { if (part.getWeight() < params.minPartWeight) { @@ -436,7 +431,7 @@ private void purgeParts (List parts, private void registerClefs (Collection clefSet) { // Sort clefs by decreasing grade - final List clefList = new ArrayList(clefSet); + final List clefList = new ArrayList<>(clefSet); Collections.sort(clefList, Inters.byReverseGrade); for (int idx = 0; idx < clefList.size(); idx++) { @@ -501,7 +496,77 @@ private void selectClef () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------// + // ClefAdapter // + //-------------// + /** + * Handles the integration between glyph clustering class and clef environment. + *

                                                                  + * For each clef kind, we keep the best result found if any. + */ + private class ClefAdapter + extends GlyphCluster.AbstractAdapter + { + + /** Best inter per clef kind. */ + private final Map bestMap; + + ClefAdapter (SimpleGraph graph, + Map bestMap) + { + super(graph); + this.bestMap = bestMap; + } + + @Override + public void evaluateGlyph (Glyph glyph, + Set parts) + { + trials++; + + if (glyph.getId() == 0) { + glyph = system.registerGlyph(glyph, null); + } + + glyphCandidates.add(glyph); + + logger.debug("ClefAdapter evaluateGlyph on {}", glyph); + + Evaluation[] evals = classifier.evaluate( + glyph, + staff.getSpecificInterline(), + params.maxEvalRank, + Grades.clefMinGrade / Grades.intrinsicRatio, + null); + + for (Evaluation eval : evals) { + final Shape shape = eval.shape; + + if (HEADER_CLEF_SHAPES.contains(shape)) { + final double grade = Grades.intrinsicRatio * eval.grade; + ClefKind kind = ClefInter.kindOf(glyph.getCenter(), shape, staff); + ClefInter bestInter = bestMap.get(kind); + + if ((bestInter == null) || (bestInter.getGrade() < grade)) { + bestMap.put(kind, ClefInter.create(glyph, shape, grade, staff)); + } + } + } + } + + @Override + public boolean isTooLarge (Rectangle bounds) + { + return bounds.height > params.maxGlyphHeight; + } + + @Override + public boolean isTooLight (int weight) + { + return weight < params.minGlyphWeight; + } + } + //--------// // Column // //--------// @@ -510,21 +575,22 @@ private void selectClef () */ public static class Column { - //~ Instance fields ------------------------------------------------------------------------ private final SystemInfo system; /** Map of clef builders. (one per staff) */ - private final Map builders = new TreeMap( - Staff.byId); + private final Map builders = new TreeMap<>(Staff.byId); - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a Column. + * + * @param system the containing system + */ public Column (SystemInfo system) { this.system = system; } - //~ Methods -------------------------------------------------------------------------------- //---------------// // retrieveClefs // //---------------// @@ -579,7 +645,6 @@ public void selectClefs () //------------// private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int maxPartCount; @@ -611,9 +676,8 @@ private static class Parameters final int minGlyphWeight; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale, - int staffSpecific) + Parameters (Scale scale, + int staffSpecific) { maxPartCount = constants.maxPartCount.getValue(); maxEvalRank = constants.maxEvalRank.getValue(); @@ -640,87 +704,12 @@ public Parameters (Scale scale, } } - //-------------// - // ClefAdapter // - //-------------// - /** - * Handles the integration between glyph clustering class and clef environment. - *

                                                                  - * For each clef kind, we keep the best result found if any. - */ - private class ClefAdapter - extends GlyphCluster.AbstractAdapter - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Best inter per clef kind. */ - private final Map bestMap; - - //~ Constructors --------------------------------------------------------------------------- - public ClefAdapter (SimpleGraph graph, - Map bestMap) - { - super(graph); - this.bestMap = bestMap; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void evaluateGlyph (Glyph glyph, - Set parts) - { - trials++; - - if (glyph.getId() == 0) { - glyph = system.registerGlyph(glyph, null); - } - - glyphCandidates.add(glyph); - - logger.debug("ClefAdapter evaluateGlyph on {}", glyph); - - Evaluation[] evals = classifier.evaluate( - glyph, - staff.getSpecificInterline(), - params.maxEvalRank, - Grades.clefMinGrade / Grades.intrinsicRatio, - null); - - for (Evaluation eval : evals) { - final Shape shape = eval.shape; - - if (HEADER_CLEF_SHAPES.contains(shape)) { - final double grade = Grades.intrinsicRatio * eval.grade; - ClefKind kind = ClefInter.kindOf(glyph, shape, staff); - ClefInter bestInter = bestMap.get(kind); - - if ((bestInter == null) || (bestInter.getGrade() < grade)) { - bestMap.put(kind, ClefInter.create(glyph, shape, grade, staff)); - } - } - } - } - - @Override - public boolean isTooLarge (Rectangle bounds) - { - return bounds.height > params.maxGlyphHeight; - } - - @Override - public boolean isTooLight (int weight) - { - return weight < params.minGlyphWeight; - } - } - //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxClefEnd = new Scale.Fraction( 4.5, diff --git a/src/main/org/audiveris/omr/sheet/header/HeaderBuilder.java b/src/main/org/audiveris/omr/sheet/header/HeaderBuilder.java index e6ee339a8..7be8f958c 100644 --- a/src/main/org/audiveris/omr/sheet/header/HeaderBuilder.java +++ b/src/main/org/audiveris/omr/sheet/header/HeaderBuilder.java @@ -55,7 +55,8 @@ * of components among system staves, and even the horizontal limits of items slices within the * key-sig components. *

                                                                  - * Cross-validation at system level:

                                                                    + * Cross-validation at system level: + *
                                                                      *
                                                                    • Clefs: * A clef is mandatory at the beginning of each staff (in the header) and may be different from one * staff to the other. @@ -73,13 +74,11 @@ */ public class HeaderBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(HeaderBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated system. */ @Navigable(false) private final SystemInfo system; @@ -99,7 +98,6 @@ public class HeaderBuilder /** Manager for column of time signatures. */ private final TimeBuilder.HeaderColumn timeColumn; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new HeaderBuilder object. * @@ -116,7 +114,6 @@ public HeaderBuilder (SystemInfo system) timeColumn = new TimeBuilder.HeaderColumn(system); } - //~ Methods ------------------------------------------------------------------------------------ //------// // plot // //------// @@ -279,7 +276,7 @@ private void purgeBarlines () final int start = staff.getHeaderStart(); final int stop = staff.getHeaderStop(); - for (BarlineInter bar : new ArrayList(staff.getBarlines())) { + for (BarlineInter bar : new ArrayList<>(staff.getBarlines())) { final Point center = bar.getCenter(); if ((center.x > start) && (center.x < stop) && !bar.isStaffEnd(LEFT)) { @@ -344,14 +341,12 @@ private void setSystemTimeStop (int largestOffset) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxHeaderWidth = new Scale.Fraction( 15.0, diff --git a/src/main/org/audiveris/omr/sheet/header/HeadersStep.java b/src/main/org/audiveris/omr/sheet/header/HeadersStep.java index 5cef38068..ef4f49ead 100644 --- a/src/main/org/audiveris/omr/sheet/header/HeadersStep.java +++ b/src/main/org/audiveris/omr/sheet/header/HeadersStep.java @@ -37,11 +37,9 @@ public class HeadersStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(HeadersStep.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code HeadersStep} object. */ @@ -49,7 +47,6 @@ public HeadersStep () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // doSystem // //----------// diff --git a/src/main/org/audiveris/omr/sheet/header/KeyBuilder.java b/src/main/org/audiveris/omr/sheet/header/KeyBuilder.java index 732ca8784..29d38aa01 100644 --- a/src/main/org/audiveris/omr/sheet/header/KeyBuilder.java +++ b/src/main/org/audiveris/omr/sheet/header/KeyBuilder.java @@ -28,6 +28,7 @@ import static org.audiveris.omr.glyph.Shape.FLAT; import static org.audiveris.omr.glyph.Shape.SHARP; import org.audiveris.omr.math.HiLoPeakFinder; +import org.audiveris.omr.math.HiLoPeakFinder.Quorum; import org.audiveris.omr.math.IntegerFunction; import org.audiveris.omr.math.Range; import org.audiveris.omr.sheet.Scale; @@ -101,7 +102,8 @@ * from a time signature. * A space, if any, between two key signature items is very narrow. *

                                                                      - * Strategy:

                                                                        + * Strategy: + *
                                                                          *
                                                                        1. Find first significant space right after clef, it's the space that separates the clef from * next item (key signature or time signature or first note/rest, etc). * This space may not be detected in the projection when the first key signature item is very close @@ -134,13 +136,11 @@ */ public class KeyBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(KeyBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Containing system KeyColumn. */ private final KeyColumn column; @@ -175,7 +175,7 @@ public class KeyBuilder private final int browseStart; /** (Competing) active clef(s) in staff, just before key signature. */ - private final List clefs = new ArrayList(); + private final List clefs = new ArrayList<>(); /** Projection of foreground pixels, indexed by abscissa. */ private final IntegerFunction projection; @@ -187,10 +187,8 @@ public class KeyBuilder private final HiLoPeakFinder peakFinder; /** Builders per shape. */ - private final Map shapeBuilders = new EnumMap( - Shape.class); + private final Map shapeBuilders = new EnumMap<>(Shape.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code KeyBuilder} object. * @@ -230,7 +228,6 @@ public class KeyBuilder shapeBuilders.put(FLAT, new ShapeBuilder(FLAT, browseRect)); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // addPlot // //---------// @@ -303,8 +300,7 @@ public void addPlot (ChartPlotter plotter) XYSeries series = new XYSeries("KeyArea", false); // No autosort int start = ((range != null) && range.hasStart()) ? range.getStart() : staff.getKeyStart(); - int stop = ((range != null) && range.hasStart()) ? range.getStop() - : staff.getKeyStop(); + int stop = ((range != null) && range.hasStart()) ? range.getStop() : staff.getKeyStop(); series.add(start, 0); series.add(start, staff.getHeight()); series.add(stop, staff.getHeight()); @@ -397,6 +393,11 @@ public KeyInter getBestKeyInter () //-------// // getId // //-------// + /** + * Report builder id. + * + * @return id of unerlying staff + */ public int getId () { return staff.getId(); @@ -503,37 +504,6 @@ public String toString () return "KeyBuilder#" + getId(); } - //----------------// - // checkReplicate // - //----------------// - /** - * Override local key with the one from the "best source" staff in part. - * - * @param sourceBuilder ShapeBuilder of source staff - * @return proper PartStatus value - */ - PartStatus checkReplicate (ShapeBuilder sourceBuilder) - { - final int fifths = sourceBuilder.getFifths(); - getShapeBuilder(-fifths).destroy(); - - return getShapeBuilder(fifths).checkReplicate(sourceBuilder); - } - - //-----------------// - // getShapeBuilder // - //-----------------// - ShapeBuilder getShapeBuilder (int fifths) - { - if (fifths == 0) { - return null; - } else if (fifths > 0) { - return shapeBuilders.get(SHARP); - } else { - return shapeBuilders.get(FLAT); - } - } - //--------// // dumpOf // //--------// @@ -612,9 +582,10 @@ private double getSmallestDx (KeyPeak peak, //-------------------// private List retrieveHiLoPeaks () { + peakFinder.setQuorum(new Quorum(params.minPeakCumul)); + final List hiloPeaks = peakFinder.findPeaks( params.maxSpaceCumul + 1, // minValue - params.minPeakCumul, // minTopValue params.minPeakDerivative, // minDerivative params.minGainRatio); // minGainRatio Collections.sort(hiloPeaks, Range.byMain); @@ -662,7 +633,37 @@ private void selectBestClef () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------------// + // checkReplicate // + //----------------// + /** + * Override local key with the one from the "best source" staff in part. + * + * @param sourceBuilder ShapeBuilder of source staff + * @return proper PartStatus value + */ + PartStatus checkReplicate (ShapeBuilder sourceBuilder) + { + final int fifths = sourceBuilder.getFifths(); + getShapeBuilder(-fifths).destroy(); + + return getShapeBuilder(fifths).checkReplicate(sourceBuilder); + } + + //-----------------// + // getShapeBuilder // + //-----------------// + ShapeBuilder getShapeBuilder (int fifths) + { + if (fifths == 0) { + return null; + } else if (fifths > 0) { + return shapeBuilders.get(SHARP); + } else { + return shapeBuilders.get(FLAT); + } + } + //--------------// // ShapeBuilder // //--------------// @@ -672,7 +673,6 @@ private void selectBestClef () */ class ShapeBuilder { - //~ Instance fields ------------------------------------------------------------------------ /** Shape used for key signature. */ private final Shape keyShape; @@ -680,7 +680,7 @@ class ShapeBuilder private final StaffHeader.Range range; /** Sequence of valid peaks found, ordered by abscissa. */ - private final List peaks = new ArrayList(); + private final List peaks = new ArrayList<>(); /** ROI with slices for key search. */ private final KeyRoi roi; @@ -688,9 +688,8 @@ class ShapeBuilder /** Resulting key inter, if any. */ private KeyInter keyInter; - //~ Constructors --------------------------------------------------------------------------- - public ShapeBuilder (Shape keyShape, - Rectangle browseRect) + ShapeBuilder (Shape keyShape, + Rectangle browseRect) { this.keyShape = keyShape; roi = new KeyRoi( @@ -704,7 +703,6 @@ public ShapeBuilder (Shape keyShape, range.browseStop = (browseRect.x + browseRect.width) - 1; } - //~ Methods -------------------------------------------------------------------------------- //---------------// // adjustPitches // //---------------// @@ -716,8 +714,7 @@ public void adjustPitches () // Use pitches for chosen clef, if any if (!clefs.isEmpty()) { final ClefInter bestClef = clefs.get(0); - final int[] stdPitches = KeyInter.getPitchesMap(keyShape).get( - bestClef.getKind()); + final int[] stdPitches = KeyInter.getPitchesMap(keyShape).get(bestClef.getKind()); for (int i = 0; i < roi.size(); i++) { KeySlice slice = roi.get(i); @@ -821,8 +818,8 @@ public PartStatus checkReplicate (ShapeBuilder sourceBuilder) final int sStart = sourceSlice.getStart(); final int sWidth = sourceSlice.getWidth(); - if ((Math.abs(sStart - slice.getStart()) > params.maxSliceDeltaX) - || (Math.abs(sWidth - slice.getWidth()) > params.maxSliceDeltaWidth)) { + if ((Math.abs(sStart - slice.getStart()) > params.maxSliceDeltaX) || (Math.abs( + sWidth - slice.getWidth()) > params.maxSliceDeltaWidth)) { it.remove(); while (it.hasNext()) { @@ -849,13 +846,11 @@ public PartStatus checkReplicate (ShapeBuilder sourceBuilder) if (localSlice == null) { // Choose start & stop values for the slice to be created final KeySlice prevSlice = roi.getStopSlice(targetStart); - final int start = (prevSlice != null) ? (prevSlice.getStop() + 1) - : targetStart; + final int start = (prevSlice != null) ? (prevSlice.getStop() + 1) : targetStart; int targetStop = (start + sourceSlice.getWidth()) - 1; KeySlice nextSlice = roi.getStartSlice(targetStop + 1); - final int stop = (nextSlice != null) ? (nextSlice.getStart() - 1) - : targetStop; + final int stop = (nextSlice != null) ? (nextSlice.getStart() - 1) : targetStop; localSlice = roi.createSlice(start, stop); localSlice.setPitchRect( @@ -1501,9 +1496,8 @@ private int computePitchedGrades (ClefKind clefKind, // Define dPitch threshold based on alters.length final double maxDeltaPitch = (n >= 4) ? params.maxDeltaPitch_4 - : (params.maxDeltaPitch_1 - + (((params.maxDeltaPitch_4 - params.maxDeltaPitch_1) * (n - - 1)) / 3)); + : (params.maxDeltaPitch_1 + (((params.maxDeltaPitch_4 - params.maxDeltaPitch_1) + * (n - 1)) / 3)); final int[] clefPitches = KeyInter.getPitches(clefKind, keyShape); int alterCount = 0; @@ -1550,7 +1544,7 @@ private int computePitchedGrades (ClefKind clefKind, */ private List computeStarts (int signature) { - List starts = new ArrayList(); + List starts = new ArrayList<>(); if (signature > 0) { // Sharps @@ -1593,13 +1587,13 @@ private List computeStarts (int signature) //----------------// private void createKeyInter () { - List alters = new ArrayList(); + List alters = new ArrayList<>(); Rectangle box = null; for (KeySlice slice : roi) { KeyAlterInter alter = slice.getAlter(); - if (alter != null) { + if ((alter != null) && !alter.isRemoved()) { alters.add(alter); if (box == null) { @@ -1965,7 +1959,7 @@ private void purgeLightPeaks () } // Compute area quorum on all peaks but the smallest one - List sortedPeaks = new ArrayList(peaks); + List sortedPeaks = new ArrayList<>(peaks); Collections.sort(sortedPeaks, KeyPeak.byArea); int totalArea = 0; // Sum of peaks areas @@ -2015,14 +2009,9 @@ private void refineAreaStart () final int xMin = range.getStart(); final int xMax = peaks.get(0).min - 1; - final int yMin = staff.getFirstLine().yAt(xMin) - - staff.getSpecificInterline(); + final int yMin = staff.getFirstLine().yAt(xMin) - staff.getSpecificInterline(); final int yMax = staff.getLastLine().yAt(xMin); - final Rectangle rect = new Rectangle( - xMin, - yMin, - xMax - xMin + 1, - yMax - yMin + 1); + final Rectangle rect = new Rectangle(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); final IntegerFunction staffProj = extractor.getProjection(xMin, rect); int start = xMax + 1; @@ -2126,10 +2115,9 @@ private int refineSignature (int signature) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String vipStaves = new Constant.String( "", @@ -2282,7 +2270,6 @@ private static final class Constants //------------// private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final double minGainRatio; @@ -2358,9 +2345,8 @@ private static class Parameters final int maxTrailingCumul; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale, - int staffSpecific) + Parameters (Scale scale, + int staffSpecific) { minGainRatio = constants.minGainRatio.getValue(); minBlackRatio = constants.minBlackRatio.getValue(); diff --git a/src/main/org/audiveris/omr/sheet/header/KeyColumn.java b/src/main/org/audiveris/omr/sheet/header/KeyColumn.java index 9bfa6aeb7..76c927592 100644 --- a/src/main/org/audiveris/omr/sheet/header/KeyColumn.java +++ b/src/main/org/audiveris/omr/sheet/header/KeyColumn.java @@ -48,7 +48,8 @@ * For each staff slice, a connected component is first looked up (phase #1) and, if * unsuccessful, then a hard slice-based glyph is searched (phase #2). *

                                                                          - * Second, it is assumed that, within the same containing system:

                                                                            + * Second, it is assumed that, within the same containing system: + *
                                                                              *
                                                                            1. All staff key signatures start at similar abscissa offset since measure start, *
                                                                            2. All staff key slices have similar widths across staves, even between small and standard * staves, and regardless of key alter shape (SHARP, FLAT or NATURAL), @@ -75,31 +76,11 @@ */ public class KeyColumn { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(KeyColumn.class); - //~ Enumerations ------------------------------------------------------------------------------- - /** Status of key replication within part. */ - public enum PartStatus - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Success. */ - OK, - /** Slice count to be reduced. */ - SHRINK, - /** No clef in staff. */ - NO_CLEF, - /** Replication failed. */ - NO_REPLICATE, - /** No key in part. */ - DESTROY; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Related system. */ private final SystemInfo system; @@ -107,12 +88,11 @@ public enum PartStatus private final Parameters params; /** Map of key builders. (one per staff) */ - private final Map builders = new TreeMap(Staff.byId); + private final Map builders = new TreeMap<>(Staff.byId); /** Theoretical abscissa offset for each slice. */ private List globalOffsets; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code KeyColumn} object. * @@ -124,7 +104,6 @@ public KeyColumn (SystemInfo system) params = new Parameters(system.getSheet().getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // addPlot // //---------// @@ -210,53 +189,6 @@ public int retrieveKeys (int projectionWidth) return maxKeyOffset; } - //-----------------// - // getMaxSliceDist // - //-----------------// - final int getMaxSliceDist () - { - return params.maxSliceDist; - } - - //----------------// - // getGlobalIndex // - //----------------// - /** - * Determine the corresponding global index for the provided abscissa offset. - * - * @param offset slice offset - * @return the global index, or null - */ - Integer getGlobalIndex (int offset) - { - Integer bestIndex = null; - double bestDist = Double.MAX_VALUE; - - for (int i = 0; i < globalOffsets.size(); i++) { - int gOffset = globalOffsets.get(i); - double dist = Math.abs(gOffset - offset); - - if (bestDist > dist) { - bestDist = dist; - bestIndex = i; - } - } - - if (bestDist <= getMaxSliceDist()) { - return bestIndex; - } else { - return null; - } - } - - //-----------------// - // getGlobalOffset // - //-----------------// - int getGlobalOffset (int index) - { - return globalOffsets.get(index); - } - //-------------------// // checkSystemSlices // //-------------------// @@ -381,8 +313,8 @@ private int getGlobalOffsets () int meanSliceWidth = 0; // Check that key-sig slices appear rather vertically aligned across system staves - List pops = new ArrayList(); // 1 population per slice index - List vals = new ArrayList(); // All offset values + List pops = new ArrayList<>(); // 1 population per slice index + List vals = new ArrayList<>(); // All offset values for (KeyBuilder builder : builders.values()) { KeyInter bestInter = builder.getBestKeyInter(); @@ -429,7 +361,7 @@ private int getGlobalOffsets () Clustering.EM(table, laws); - List theoreticals = new ArrayList(); + List theoreticals = new ArrayList<>(); for (int k = 0; k < G; k++) { Clustering.Gaussian law = laws[k]; @@ -474,14 +406,74 @@ private void printSliceTable () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //-----------------// + // getMaxSliceDist // + //-----------------// + final int getMaxSliceDist () + { + return params.maxSliceDist; + } + + //----------------// + // getGlobalIndex // + //----------------// + /** + * Determine the corresponding global index for the provided abscissa offset. + * + * @param offset slice offset + * @return the global index, or null + */ + Integer getGlobalIndex (int offset) + { + Integer bestIndex = null; + double bestDist = Double.MAX_VALUE; + + for (int i = 0; i < globalOffsets.size(); i++) { + int gOffset = globalOffsets.get(i); + double dist = Math.abs(gOffset - offset); + + if (bestDist > dist) { + bestDist = dist; + bestIndex = i; + } + } + + if (bestDist <= getMaxSliceDist()) { + return bestIndex; + } else { + return null; + } + } + + //-----------------// + // getGlobalOffset // + //-----------------// + int getGlobalOffset (int index) + { + return globalOffsets.get(index); + } + + /** Status of key replication within part. */ + public enum PartStatus + { + /** Success. */ + OK, + /** Slice count to be reduced. */ + SHRINK, + /** No clef in staff. */ + NO_CLEF, + /** Replication failed. */ + NO_REPLICATE, + /** No key in part. */ + DESTROY; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxSliceDist = new Scale.Fraction( 0.5, @@ -491,14 +483,12 @@ private static final class Constants //------------// // Parameters // //------------// - private static final class Parameters + private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int maxSliceDist; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale) + Parameters (Scale scale) { maxSliceDist = scale.toPixels(constants.maxSliceDist); } diff --git a/src/main/org/audiveris/omr/sheet/header/KeyEvent.java b/src/main/org/audiveris/omr/sheet/header/KeyEvent.java deleted file mode 100644 index b7e347e41..000000000 --- a/src/main/org/audiveris/omr/sheet/header/KeyEvent.java +++ /dev/null @@ -1,91 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// K e y E v e n t // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet.header; - -/** - * Formalizes an event (peak or space) while browsing key signature abscissa range. - * - * @author Hervé Bitteur - */ -@Deprecated -public abstract class KeyEvent -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** Left abscissa. */ - protected int min; - - /** Right abscissa. */ - protected int max; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new KeyEvent object. - * - * @param min first abscissa of object - * @param max last abscissa of object - */ - public KeyEvent (int min, - int max) - { - this.min = min; - this.max = max; - } - - //~ Methods ------------------------------------------------------------------------------------ - public int getWidth () - { - return max - min + 1; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-------// - // Space // - //-------// - /** - * An abscissa range where no chunk is detected on top of staff lines and thus - * indicates a possible separation. - */ - @Deprecated - public static class Space - extends KeyEvent - { - //~ Constructors --------------------------------------------------------------------------- - - public Space (int start, - int stop) - { - super(start, stop); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(); - - sb.append("Space(").append(min).append("-").append(max).append(")"); - - return sb.toString(); - } - } -} diff --git a/src/main/org/audiveris/omr/sheet/header/KeyExtractor.java b/src/main/org/audiveris/omr/sheet/header/KeyExtractor.java index 46df02347..c65494f86 100644 --- a/src/main/org/audiveris/omr/sheet/header/KeyExtractor.java +++ b/src/main/org/audiveris/omr/sheet/header/KeyExtractor.java @@ -37,9 +37,7 @@ import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.math.GeoUtil; import org.audiveris.omr.math.IntegerFunction; - import static org.audiveris.omr.run.Orientation.VERTICAL; - import org.audiveris.omr.run.RunTable; import org.audiveris.omr.run.RunTableFactory; import org.audiveris.omr.sheet.Picture; @@ -76,13 +74,11 @@ */ public class KeyExtractor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(KeyExtractor.class); - //~ Instance fields ---------------------------------------------------------------------------- private final Sheet sheet; private final SystemInfo system; @@ -103,9 +99,8 @@ public class KeyExtractor private final Classifier classifier = ShapeClassifier.getInstance(); /** All glyphs submitted to classifier. */ - private final Set glyphCandidates = new LinkedHashSet(); + private final Set glyphCandidates = new LinkedHashSet<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code KeyExtractor} object. * @@ -124,7 +119,6 @@ public KeyExtractor (Staff staff) staffFreeSource = sheet.getPicture().getSource(Picture.SourceKey.NO_STAFF); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // extractAlter // //--------------// @@ -161,8 +155,8 @@ public KeyAlterInter extractAlter (KeyRoi roi, double grade = Grades.intrinsicRatio * slice.getEval().grade; if (grade >= minGrade) { - if ((slice.getAlter() == null) - || (slice.getAlter().getGlyph() != slice.getGlyph())) { + if ((slice.getAlter() == null) || (slice.getAlter().getGlyph() != slice + .getGlyph())) { logger.debug("Glyph#{} {}", slice.getGlyph().getId(), slice.getEval()); KeyAlterInter alterInter = KeyAlterInter.create( @@ -347,19 +341,17 @@ public List retrieveCandidates (StaffHeader.Range range, // Key-signature area pixels ByteProcessor keyBuf = roi.getAreaPixels(staffFreeSource, range); RunTable runTable = new RunTableFactory(VERTICAL).createTable(keyBuf); - List parts = GlyphFactory.buildGlyphs( - runTable, - new Point(range.getStart(), roi.y)); + List parts = GlyphFactory.buildGlyphs(runTable, new Point(range.getStart(), roi.y)); purgeParts(parts, range.getStop()); system.registerGlyphs(parts, null); // Formalize parts relationships in a global graph SimpleGraph graph = Glyphs.buildLinks(parts, params.maxPartGap); - List> sets = new ConnectivityInspector(graph).connectedSets(); + List> sets = new ConnectivityInspector<>(graph).connectedSets(); logger.debug("Staff#{} sets:{}", id, sets.size()); - List allCandidates = new ArrayList(); + List allCandidates = new ArrayList<>(); for (Set set : sets) { // Use only the subgraph for this set @@ -482,7 +474,7 @@ private int getInk (Rectangle rect) */ private void purgeCandidates (List candidates) { - final List toRemove = new ArrayList(); + final List toRemove = new ArrayList<>(); Collections.sort(candidates, Candidate.byReverseGrade); for (int i = 0; i < candidates.size(); i++) { @@ -521,7 +513,7 @@ private void purgeCandidates (List candidates) private void purgeParts (List parts, int xMax) { - final List toRemove = new ArrayList(); + final List toRemove = new ArrayList<>(); for (Glyph glyph : parts) { if ((glyph.getWeight() < params.minPartWeight) || (glyph.getBounds().x == xMax)) { @@ -539,78 +531,6 @@ private void purgeParts (List parts, } } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Candidate // - //-----------// - /** - * Meant to mutually exclude candidates that have a part in common. - */ - public static class Candidate - { - //~ Static fields/initializers ------------------------------------------------------------- - - /** To sort according to decreasing grade. */ - public static final Comparator byReverseGrade = new Comparator() - { - @Override - public int compare (Candidate c1, - Candidate c2) - { - if (c1 == c2) { - return 0; - } - - return Double.compare(c2.eval.grade, c1.eval.grade); - } - }; - - /** To sort according to left abscissa. */ - public static final Comparator byAbscissa = new Comparator() - { - @Override - public int compare (Candidate c1, - Candidate c2) - { - if (c1 == c2) { - return 0; - } - - return Double.compare(c1.glyph.getLeft(), c2.glyph.getLeft()); - } - }; - - //~ Instance fields ------------------------------------------------------------------------ - final Glyph glyph; - - final Set parts; - - final Evaluation eval; - - //~ Constructors --------------------------------------------------------------------------- - public Candidate (Glyph glyph, - Set parts, - Evaluation eval) - { - this.glyph = glyph; - this.parts = parts; - this.eval = eval; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("Candidate{#"); - - sb.append(glyph.getId()); - sb.append(" ").append(eval); - sb.append("}"); - - return sb.toString(); - } - } - //--------------------// // AbstractKeyAdapter // //--------------------// @@ -620,7 +540,6 @@ public String toString () private abstract class AbstractKeyAdapter extends GlyphCluster.AbstractAdapter { - //~ Instance fields ------------------------------------------------------------------------ /** Relevant peaks. */ protected final List peaks; @@ -631,11 +550,10 @@ private abstract class AbstractKeyAdapter /** Relevant shapes. */ protected final EnumSet targetShapes = EnumSet.noneOf(Shape.class); - //~ Constructors --------------------------------------------------------------------------- - public AbstractKeyAdapter (SimpleGraph graph, - List peaks, - Set targetShapes, - double minGrade) + AbstractKeyAdapter (SimpleGraph graph, + List peaks, + Set targetShapes, + double minGrade) { super(graph); this.peaks = peaks; @@ -643,7 +561,6 @@ public AbstractKeyAdapter (SimpleGraph graph, this.minGrade = minGrade; } - //~ Methods -------------------------------------------------------------------------------- @Override public boolean isTooHeavy (int weight) { @@ -653,8 +570,7 @@ public boolean isTooHeavy (int weight) @Override public boolean isTooLarge (Rectangle bounds) { - return (bounds.width > params.maxGlyphWidth) - || (bounds.height > params.maxGlyphHeight); + return (bounds.width > params.maxGlyphWidth) || (bounds.height > params.maxGlyphHeight); } @Override @@ -666,8 +582,7 @@ public boolean isTooLight (int weight) @Override public boolean isTooSmall (Rectangle bounds) { - return (bounds.width < params.minGlyphWidth) - || (bounds.height < params.minGlyphHeight); + return (bounds.width < params.minGlyphWidth) || (bounds.height < params.minGlyphHeight); } protected boolean embracesSlicePeaks (KeySlice slice, @@ -740,13 +655,170 @@ protected abstract void keepCandidate (Glyph glyph, Evaluation eval); } + //-----------------// + // MultipleAdapter // + //-----------------// + /** + * Adapter for retrieving all items of the key (in key area). + */ + private class MultipleAdapter + extends AbstractKeyAdapter + { + + final KeyRoi roi; + + final List candidates = new ArrayList<>(); + + MultipleAdapter (KeyRoi roi, + List peaks, + SimpleGraph graph, + Set targetShapes, + double minGrade) + { + super(graph, peaks, targetShapes, minGrade); + this.roi = roi; + } + + @Override + public void evaluateGlyph (Glyph glyph, + Set parts) + { + // Retrieve impacted slice + final KeySlice slice = roi.sliceOf(glyph.getCentroid().x); + evaluateSliceGlyph(slice, glyph, parts); + } + + @Override + protected void keepCandidate (Glyph glyph, + Set parts, + Evaluation eval) + { + candidates.add(new Candidate(glyph, parts, eval)); + } + } + + //---------------// + // SingleAdapter // + //---------------// + /** + * Adapter for retrieving one key item (in a slice). + */ + private class SingleAdapter + extends AbstractKeyAdapter + { + + /** Related slice. */ + private final KeySlice slice; + + SingleAdapter (KeySlice slice, + List peaks, + List parts, + Set targetShapes, + double minGrade) + { + super(Glyphs.buildLinks(parts, params.maxPartGap), peaks, targetShapes, minGrade); + this.slice = slice; + } + + @Override + public void evaluateGlyph (Glyph glyph, + Set parts) + { + evaluateSliceGlyph(slice, glyph, parts); + } + + @Override + protected void keepCandidate (Glyph glyph, + Set parts, + Evaluation eval) + { + if ((slice.getEval() == null) || (slice.getEval().grade < eval.grade)) { + slice.setEval(eval); + slice.setGlyph(glyph); + } + } + } + + //-----------// + // Candidate // + //-----------// + /** + * Meant to mutually exclude candidates that have a part in common. + */ + public static class Candidate + { + + /** To sort according to decreasing grade. */ + public static final Comparator byReverseGrade = new Comparator() + { + @Override + public int compare (Candidate c1, + Candidate c2) + { + if (c1 == c2) { + return 0; + } + + return Double.compare(c2.eval.grade, c1.eval.grade); + } + }; + + /** To sort according to left abscissa. */ + public static final Comparator byAbscissa = new Comparator() + { + @Override + public int compare (Candidate c1, + Candidate c2) + { + if (c1 == c2) { + return 0; + } + + return Double.compare(c1.glyph.getLeft(), c2.glyph.getLeft()); + } + }; + + final Glyph glyph; + + final Set parts; + + final Evaluation eval; + + /** + * Create a Candidate object + * + * @param glyph the underlying glyph + * @param parts the parts the glyph was built from + * @param eval the evaluation result + */ + public Candidate (Glyph glyph, + Set parts, + Evaluation eval) + { + this.glyph = glyph; + this.parts = parts; + this.eval = eval; + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("Candidate{#"); + + sb.append(glyph.getId()); + sb.append(" ").append(eval); + sb.append("}"); + + return sb.toString(); + } + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer maxPartCount = new Constant.Integer( "Glyphs", @@ -766,13 +838,9 @@ private static final class Constants 1.5, "Maximum distance between two parts of a single alter symbol"); - private final Scale.Fraction minGlyphWidth = new Scale.Fraction( - 0.5, - "Minimum glyph width"); + private final Scale.Fraction minGlyphWidth = new Scale.Fraction(0.5, "Minimum glyph width"); - private final Scale.Fraction maxGlyphWidth = new Scale.Fraction( - 2.0, - "Maximum glyph width"); + private final Scale.Fraction maxGlyphWidth = new Scale.Fraction(2.0, "Maximum glyph width"); private final Scale.Fraction minGlyphHeight = new Scale.Fraction( 1.0, @@ -791,57 +859,11 @@ private static final class Constants "Maximum glyph weight"); } - //-----------------// - // MultipleAdapter // - //-----------------// - /** - * Adapter for retrieving all items of the key (in key area). - */ - private class MultipleAdapter - extends AbstractKeyAdapter - { - //~ Instance fields ------------------------------------------------------------------------ - - final KeyRoi roi; - - final List candidates = new ArrayList(); - - //~ Constructors --------------------------------------------------------------------------- - public MultipleAdapter (KeyRoi roi, - List peaks, - SimpleGraph graph, - Set targetShapes, - double minGrade) - { - super(graph, peaks, targetShapes, minGrade); - this.roi = roi; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void evaluateGlyph (Glyph glyph, - Set parts) - { - // Retrieve impacted slice - final KeySlice slice = roi.sliceOf(glyph.getCentroid().x); - evaluateSliceGlyph(slice, glyph, parts); - } - - @Override - protected void keepCandidate (Glyph glyph, - Set parts, - Evaluation eval) - { - candidates.add(new Candidate(glyph, parts, eval)); - } - } - //------------// // Parameters // //------------// private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int maxPartCount; @@ -866,9 +888,8 @@ private static class Parameters final int maxGlyphWeight; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale, - int staffSpecific) + Parameters (Scale scale, + int staffSpecific) { maxPartCount = constants.maxPartCount.getValue(); maxEvalRank = constants.maxEvalRank.getValue(); @@ -887,49 +908,4 @@ public Parameters (Scale scale, } } } - - //---------------// - // SingleAdapter // - //---------------// - /** - * Adapter for retrieving one key item (in a slice). - */ - private class SingleAdapter - extends AbstractKeyAdapter - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Related slice. */ - private final KeySlice slice; - - //~ Constructors --------------------------------------------------------------------------- - public SingleAdapter (KeySlice slice, - List peaks, - List parts, - Set targetShapes, - double minGrade) - { - super(Glyphs.buildLinks(parts, params.maxPartGap), peaks, targetShapes, minGrade); - this.slice = slice; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void evaluateGlyph (Glyph glyph, - Set parts) - { - evaluateSliceGlyph(slice, glyph, parts); - } - - @Override - protected void keepCandidate (Glyph glyph, - Set parts, - Evaluation eval) - { - if ((slice.getEval() == null) || (slice.getEval().grade < eval.grade)) { - slice.setEval(eval); - slice.setGlyph(glyph); - } - } - } } diff --git a/src/main/org/audiveris/omr/sheet/header/KeyPeak.java b/src/main/org/audiveris/omr/sheet/header/KeyPeak.java index 7bfc05675..1fb649d35 100644 --- a/src/main/org/audiveris/omr/sheet/header/KeyPeak.java +++ b/src/main/org/audiveris/omr/sheet/header/KeyPeak.java @@ -33,7 +33,6 @@ public class KeyPeak extends Range { - //~ Static fields/initializers ----------------------------------------------------------------- /** To sort peaks by their area value. */ public static final Comparator byArea = new Comparator() @@ -50,14 +49,12 @@ public int compare (KeyPeak p1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** Max cumulated height. */ protected int height; /** Weight of peak. */ protected int area; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code KeyPeak} object. * @@ -78,7 +75,11 @@ public KeyPeak (int start, this.area = area; } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Report peak center abscissa + * + * @return the abscissa of peak center + */ public double getCenter () { return 0.5 * (min + max); diff --git a/src/main/org/audiveris/omr/sheet/header/KeyRoi.java b/src/main/org/audiveris/omr/sheet/header/KeyRoi.java index 134f83f43..f841a6188 100644 --- a/src/main/org/audiveris/omr/sheet/header/KeyRoi.java +++ b/src/main/org/audiveris/omr/sheet/header/KeyRoi.java @@ -43,19 +43,17 @@ import java.util.List; /** - * Class {@code KeyRoi} handles the region of interest for key retrieval, split into - * vertical slices. + * Class {@code KeyRoi} handles the region of interest for key retrieval, + * split horizontally into vertical slices. * * @author Hervé Bitteur */ public class KeyRoi extends ArrayList { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(KeyRoi.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying staff. */ private final Staff staff; @@ -71,7 +69,6 @@ public class KeyRoi /** Maximum abscissa distance to theoretical slice. */ private final int maxSliceDist; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code KeyRoi} object. * @@ -94,10 +91,15 @@ public KeyRoi (Staff staff, this.maxSliceDist = maxSliceDist; } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // attachmentKey // //---------------// + /** + * Report a short string to uniquely describe this key roi (for visual check) + * + * @param id a sequential value + * @return the attachment label + */ public String attachmentKey (int id) { final String s = (keyShape == Shape.SHARP) ? "s" : "f"; @@ -165,6 +167,9 @@ public void destroy () //--------------// // freezeAlters // //--------------// + /** + * Freeze all alters. + */ public void freezeAlters () { for (KeySlice slice : this) { @@ -211,7 +216,7 @@ public List getEmptySlices () for (KeySlice slice : this) { if (slice.getAlter() == null) { if (emptySlices == null) { - emptySlices = new ArrayList(); + emptySlices = new ArrayList<>(); } emptySlices.add(slice); @@ -228,6 +233,11 @@ public List getEmptySlices () //-------------------// // getLastValidSlice // //-------------------// + /** + * Report right-most valid slice in key roi. + * + * @return the last valid slide in roi + */ public KeySlice getLastValidSlice () { KeySlice validSlice = null; @@ -257,10 +267,8 @@ public ByteProcessor getSlicePixels (ByteProcessor source, boolean cropNeighbors) { Rectangle sRect = slice.getRect(); - BufferedImage sImage = new BufferedImage( - sRect.width, - sRect.height, - BufferedImage.TYPE_BYTE_GRAY); + BufferedImage sImage = new BufferedImage(sRect.width, sRect.height, + BufferedImage.TYPE_BYTE_GRAY); ByteProcessor sBuffer = new ByteProcessor(sImage); sBuffer.copyBits(source, -sRect.x, -sRect.y, Blitter.COPY); @@ -284,9 +292,8 @@ public ByteProcessor getSlicePixels (ByteProcessor source, g.setColor(Color.white); } - final Point offset = new Point( - glyph.getLeft() - sRect.x, - glyph.getTop() - sRect.y); + final Point offset = new Point(glyph.getLeft() - sRect.x, glyph.getTop() + - sRect.y); logger.debug("Erasing glyph#{} from {}", glyph.getId(), slice); glyph.getRunTable().render(g, offset); } @@ -305,6 +312,11 @@ public ByteProcessor getSlicePixels (ByteProcessor source, //----------// // getStaff // //----------// + /** + * Report the containing staff. + * + * @return the containing staff + */ public final Staff getStaff () { return staff; @@ -313,9 +325,14 @@ public final Staff getStaff () //-----------// // getStarts // //-----------// + /** + * Report the sequence of abscissa starts, one per slice. + * + * @return the sequence of slices starting abscissae + */ public List getStarts () { - List starts = new ArrayList(); + List starts = new ArrayList<>(); for (KeySlice slice : this) { starts.add(slice.getRect().x); @@ -327,6 +344,12 @@ public List getStarts () //---------// // sliceOf // //---------// + /** + * Report the containing slice at provided abscissa value + * + * @param x the provided abscissa value + * @return the slice found, or null + */ public KeySlice sliceOf (int x) { for (KeySlice slice : this) { @@ -354,6 +377,12 @@ public void stuffSlicesFrom (int index) } } + @Override + public Object clone () + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //---------------// // getStartSlice // //---------------// diff --git a/src/main/org/audiveris/omr/sheet/header/KeySlice.java b/src/main/org/audiveris/omr/sheet/header/KeySlice.java index 828647c5d..64810b420 100644 --- a/src/main/org/audiveris/omr/sheet/header/KeySlice.java +++ b/src/main/org/audiveris/omr/sheet/header/KeySlice.java @@ -43,11 +43,9 @@ */ public class KeySlice { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(KeySlice.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Containing Roi. */ private final KeyRoi roi; @@ -66,7 +64,6 @@ public class KeySlice /** If occupied by non-valid material. */ private boolean stuffed; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code KeySlice} object. * @@ -80,7 +77,9 @@ public KeySlice (Rectangle rect, this.roi = roi; } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Remove the alter in this slice. + */ public void deleteAlter () { if (alter != null) { @@ -90,6 +89,8 @@ public void deleteAlter () } /** + * Report the slice alter + * * @return the alter, if any */ public KeyAlterInter getAlter () @@ -98,6 +99,19 @@ public KeyAlterInter getAlter () } /** + * Set slice alter + * + * @param alter the alter to set + */ + public void setAlter (KeyAlterInter alter) + { + deleteAlter(); + this.alter = alter; + } + + /** + * Report slice evaluation + * * @return the eval */ public Evaluation getEval () @@ -106,6 +120,18 @@ public Evaluation getEval () } /** + * Set slice evaluation + * + * @param eval the eval to set + */ + public void setEval (Evaluation eval) + { + this.eval = eval; + } + + /** + * Report slice glyph + * * @return the glyph */ public Glyph getGlyph () @@ -113,22 +139,36 @@ public Glyph getGlyph () return glyph; } + /** + * Set slice glyph + * + * @param glyph the glyph to set + */ + public void setGlyph (Glyph glyph) + { + this.glyph = glyph; + } + + /** + * Report slice ID + * + * @return 1-based rank of slice within key + */ public int getId () { return 1 + roi.indexOf(this); } /** + * Report a slice string description + * * @return a fixed-size description string (9 char) */ public String getLabel () { if (alter != null) { - return String.format( - "%s%+1d %-5d", - (alter.getShape() == Shape.FLAT) ? "b" : "#", - alter.getIntegerPitch(), - alter.getId()); + return String.format("%s%+1d %-5d", (alter.getShape() == Shape.FLAT) ? "b" : "#", alter + .getIntegerPitch(), alter.getId()); } else if (stuffed) { return "STUFFED "; } else { @@ -137,6 +177,8 @@ public String getLabel () } /** + * Report slice bounds + * * @return the rectangle definition */ public final Rectangle getRect () @@ -144,49 +186,54 @@ public final Rectangle getRect () return new Rectangle(rect); } - public final int getStart () - { - return rect.x; - } - - public final int getStop () - { - return (rect.x + rect.width) - 1; - } - - public final int getWidth () + /** + * Define a new rectangle for this slice. + * + * @param rect the slice new rectangle + */ + public void setRect (Rectangle rect) { - return rect.width; + this.rect = new Rectangle(rect); } - public boolean isStuffed () + /** + * Report slice starting abscissa + * + * @return the abscissa value on left side + */ + public final int getStart () { - return stuffed; + return rect.x; } /** - * @param alter the alter to set + * Report slice ending abscissa + * + * @return the abscissa value of right side */ - public void setAlter (KeyAlterInter alter) + public final int getStop () { - deleteAlter(); - this.alter = alter; + return (rect.x + rect.width) - 1; } /** - * @param eval the eval to set + * Report slice width + * + * @return the slice width */ - public void setEval (Evaluation eval) + public final int getWidth () { - this.eval = eval; + return rect.width; } /** - * @param glyph the glyph to set + * Tell whether slice is stuffed with some invalid material. + * + * @return true if so */ - public void setGlyph (Glyph glyph) + public boolean isStuffed () { - this.glyph = glyph; + return stuffed; } //--------------// @@ -216,15 +263,8 @@ public void setPitchRect (ClefInter clef, } /** - * Define a new rectangle for this slice. * - * @param rect the slice new rectangle */ - public void setRect (Rectangle rect) - { - this.rect = new Rectangle(rect); - } - public void setStuffed () { stuffed = true; diff --git a/src/main/org/audiveris/omr/sheet/header/StaffHeader.java b/src/main/org/audiveris/omr/sheet/header/StaffHeader.java index eaac03036..c6cbbc110 100644 --- a/src/main/org/audiveris/omr/sheet/header/StaffHeader.java +++ b/src/main/org/audiveris/omr/sheet/header/StaffHeader.java @@ -45,13 +45,9 @@ @XmlAccessorType(XmlAccessType.NONE) public class StaffHeader { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - StaffHeader.class); + private static final Logger logger = LoggerFactory.getLogger(StaffHeader.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -96,7 +92,6 @@ public class StaffHeader /** Abscissa range for time. */ public Range timeRange; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StaffHeader} object. * @@ -116,7 +111,6 @@ private StaffHeader () this.start = 0; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // freeze // //--------// @@ -173,13 +167,14 @@ public String toString () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-------// // Range // //-------// + /** + * Abscissa range for a header component. + */ public static class Range { - //~ Instance fields ------------------------------------------------------------------------ /** Was the item successfully retrieved?. */ public boolean valid; @@ -196,12 +191,31 @@ public static class Range /** Precise end of item, if any. */ private Integer stop; - //~ Methods -------------------------------------------------------------------------------- + /** + * Report range start abscissa + * + * @return the precise abscissa of item left side if known, null otherwise + */ public int getStart () { return start; } + /** + * Set range start abscissa + * + * @param start the precise abscissa of item left side + */ + public void setStart (int start) + { + this.start = start; + } + + /** + * Report range stop abscissa + * + * @return the precise abscissa of item right side if known, otherwise the range stop + */ public int getStop () { if (stop != null) { @@ -211,30 +225,40 @@ public int getStop () return browseStop; } + /** + * Set range stop abscissa + * + * @param stop the precise abscissa of item right side + */ + public void setStop (int stop) + { + this.stop = stop; + } + + /** + * Report range width. + * + * @return the item (or range by default) width + */ public int getWidth () { return getStop() - getStart() + 1; } + /** + * Tell whether start is known + * + * @return true if precise start is known + */ public boolean hasStart () { return start != null; } - public void setStart (int start) - { - this.start = start; - } - - public void setStop (int stop) - { - this.stop = stop; - } - /** * Change stop value ONLY IF new value is smaller than the existing one. * - * @param stop new stop value + * @param stop the new stop value */ public void shrinkStop (int stop) { diff --git a/src/main/org/audiveris/omr/sheet/header/TimeBuilder.java b/src/main/org/audiveris/omr/sheet/header/TimeBuilder.java index 76e9929bd..76604b0af 100644 --- a/src/main/org/audiveris/omr/sheet/header/TimeBuilder.java +++ b/src/main/org/audiveris/omr/sheet/header/TimeBuilder.java @@ -99,7 +99,6 @@ */ public abstract class TimeBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -111,23 +110,6 @@ public abstract class TimeBuilder /** Possible shapes for top or bottom half of time signatures. */ private static final EnumSet halfShapes = EnumSet.copyOf(ShapeSet.PartialTimes); - //~ Enumerations ------------------------------------------------------------------------------- - /** - * The different parts of a time signature. - */ - public static enum TimeKind - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Whole signature (common or cut or combo). */ - WHOLE, - /** Upper half (numerator number). */ - NUM, - /** Lower half (denominator number). */ - DEN; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Dedicated staff to analyze. */ protected final Staff staff; @@ -148,21 +130,20 @@ public static enum TimeKind protected final Parameters params; /** whole candidates. (common or cut or combo like 6/8 ...) */ - protected final List wholes = new ArrayList(); + protected final List wholes = new ArrayList<>(); /** Top half candidates. (6/ ...) */ - protected final List nums = new ArrayList(); + protected final List nums = new ArrayList<>(); /** Bottom half candidates. (/8 ...) */ - protected final List dens = new ArrayList(); + protected final List dens = new ArrayList<>(); /** The time inter instance chosen for the staff. */ protected AbstractTimeInter timeInter; /** All glyphs submitted to classifier. */ - protected final Set glyphCandidates = new LinkedHashSet(); + protected final Set glyphCandidates = new LinkedHashSet<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TimeBuilder} object. * @@ -182,7 +163,6 @@ public TimeBuilder (Staff staff, params = new Parameters(scale, staff.getSpecificInterline()); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // getTimeInter // //--------------// @@ -250,7 +230,7 @@ protected void createTimeSig (AbstractTimeInter bestTimeInter) */ protected void discardOthers () { - final Map compatibles = new EnumMap(Shape.class); + final Map compatibles = new EnumMap<>(Shape.class); if (timeInter instanceof TimeWholeInter) { // It's a whole sig: COMMON_TIME or CUT_TIME or combo @@ -392,7 +372,189 @@ protected boolean filterCandidates () return !wholes.isEmpty() || !nums.isEmpty(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------// + // HalfAdapter // + //-------------// + /** + * Handles the integration between glyph clustering class and time-sig environment. + *

                                                                              + * For each time kind, we keep the best result found if any. + */ + private class HalfAdapter + extends TimeAdapter + { + + /** Which half is being searched. (NUM or DEN) */ + private final TimeKind half; + + HalfAdapter (TimeKind half, + List parts) + { + super(parts); + this.half = half; + } + + @Override + public void evaluateGlyph (Glyph glyph, + Set parts) + { + trials++; + + if (glyph.getId() == 0) { + glyph = system.registerGlyph(glyph, null); + } + + glyphCandidates.add(glyph); + + Evaluation[] evals = ShapeClassifier.getInstance().evaluate( + glyph, + staff.getSpecificInterline(), + params.maxEvalRank, + Grades.timeMinGrade / Grades.intrinsicRatio, + null); + + for (Evaluation eval : evals) { + final Shape shape = eval.shape; + + if (halfShapes.contains(shape)) { + final double grade = Grades.intrinsicRatio * eval.grade; + logger.debug(" {} eval {} for glyph#{}", half, eval, glyph.getId()); + + Inter bestInter = bestMap.get(shape); + + if ((bestInter == null) || (bestInter.getGrade() < grade)) { + TimeNumberInter inter = TimeNumberInter.create(glyph, shape, grade, staff); + + if (inter != null) { + bestMap.put(shape, inter); + } + } + } + } + } + + @Override + public boolean isTooLight (int weight) + { + return weight < params.minHalfTimeWeight; + } + } + + //-------------// + // TimeAdapter // + //-------------// + private abstract class TimeAdapter + extends GlyphCluster.AbstractAdapter + { + + /** Best inter per time shape. */ + public Map bestMap = new EnumMap<>(Shape.class); + + TimeAdapter (List parts) + { + super(parts, params.maxPartGap); + } + + public void cleanup () + { + for (Inter inter : bestMap.values()) { + inter.remove(); + } + } + + public Inter getSingleInter () + { + for (Inter inter : bestMap.values()) { + if (!inter.isRemoved()) { + return inter; + } + } + + return null; + } + + @Override + public boolean isTooLarge (Rectangle bounds) + { + return bounds.width > params.maxTimeWidth; + } + } + + //--------------// + // WholeAdapter // + //--------------// + /** + * Handles the integration between glyph clustering class and time-sig environment. + *

                                                                              + * For each time value, we keep the best result found if any. + */ + private class WholeAdapter + extends TimeAdapter + { + + WholeAdapter (List parts) + { + super(parts); + } + + @Override + public void evaluateGlyph (Glyph glyph, + Set parts) + { + //TODO: check glyph centroid for a whole symbol is not too far from staff middle line + trials++; + + if (glyph.getId() == 0) { + glyph = system.registerGlyph(glyph, null); + } + + glyphCandidates.add(glyph); + + Evaluation[] evals = ShapeClassifier.getInstance().evaluate( + glyph, + staff.getSpecificInterline(), + params.maxEvalRank, + Grades.timeMinGrade / Grades.intrinsicRatio, + null); + + for (Evaluation eval : evals) { + final Shape shape = eval.shape; + + if (wholeShapes.contains(shape)) { + final double grade = Grades.intrinsicRatio * eval.grade; + logger.debug(" WHOLE eval {} for glyph#{}", eval, glyph.getId()); + + Inter bestInter = bestMap.get(shape); + + if ((bestInter == null) || (bestInter.getGrade() < grade)) { + TimeWholeInter inter = new TimeWholeInter(glyph, shape, grade); + inter.setStaff(staff); + bestMap.put(shape, inter); + } + } + } + } + + @Override + public boolean isTooLight (int weight) + { + return weight < params.minWholeTimeWeight; + } + } + + /** + * The different parts of a time signature. + */ + public static enum TimeKind + { + /** Whole signature (common or cut or combo). */ + WHOLE, + /** Upper half (numerator number). */ + NUM, + /** Lower half (denominator number). */ + DEN; + } + //-------------// // BasicColumn // //-------------// @@ -402,7 +564,6 @@ protected boolean filterCandidates () public static class BasicColumn extends Column { - //~ Instance fields ------------------------------------------------------------------------ /** The measure stack for which a column of times is checked. */ private final MeasureStack stack; @@ -413,7 +574,6 @@ public static class BasicColumn /** Maximum abscissa shift between de-skewed time items in stack. */ private final int maxDxOffset; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new {@code BasicColumn} object. * @@ -426,12 +586,11 @@ public BasicColumn (MeasureStack stack, super(stack.getSystem()); this.stack = stack; - this.timeSet = new LinkedHashSet(timeSet); + this.timeSet = new LinkedHashSet<>(timeSet); maxDxOffset = stack.getSystem().getSheet().getScale().toPixels(constants.maxDxOffset); } - //~ Methods -------------------------------------------------------------------------------- @Override protected TimeBuilder allocateBuilder (Staff staff) { @@ -459,8 +618,8 @@ class Item final double xOffset; - public Item (Inter time, - double xOffset) + Item (Inter time, + double xOffset) { this.time = time; this.xOffset = xOffset; @@ -470,7 +629,7 @@ public Item (Inter time, class Line { - List items = new ArrayList(); + List items = new ArrayList<>(); Double meanOffset = null; @@ -497,7 +656,7 @@ void addItem (Item item) } } - List lines = new ArrayList(); + List lines = new ArrayList<>(); for (Staff staff : system.getStaves()) { TimeBuilder builder = builders.get(staff); @@ -530,20 +689,18 @@ void addItem (Item item) } // Select a single vertical line (based on item count? or the left-most line?) - Collections.sort( - lines, - new Comparator() - { - @Override - public int compare (Line o1, - Line o2) - { - return Double.compare(o1.getOffset(), o2.getOffset()); - } - }); + Collections.sort(lines, new Comparator() + { + @Override + public int compare (Line o1, + Line o2) + { + return Double.compare(o1.getOffset(), o2.getOffset()); + } + }); Line chosenLine = lines.get(0); - List kept = new ArrayList(); + List kept = new ArrayList<>(); for (Item item : chosenLine.items) { kept.add(item.time); @@ -580,15 +737,19 @@ public int compare (Line o1, public static class BasicTimeBuilder extends TimeBuilder { - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a BasicTimeBuilder object. + * + * @param staff the containing staff + * @param column the related system column + */ public BasicTimeBuilder (Staff staff, BasicColumn column) { super(staff, column); } - //~ Methods -------------------------------------------------------------------------------- @Override protected void findCandidates () { @@ -625,14 +786,17 @@ protected void findCandidates () public static class HeaderColumn extends Column { - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a HeaderColumn object. + * + * @param system the containing system + */ public HeaderColumn (SystemInfo system) { super(system); } - //~ Methods -------------------------------------------------------------------------------- //---------// // addPlot // //---------// @@ -738,7 +902,6 @@ protected void cleanup () public static class HeaderTimeBuilder extends TimeBuilder { - //~ Instance fields ------------------------------------------------------------------------ /** Time range info. */ private final StaffHeader.Range range; @@ -750,10 +913,8 @@ public static class HeaderTimeBuilder private final Rectangle roi; /** 3 adapters for glyph building, one for each kind: whole, num & den. */ - private final Map adapters = new EnumMap( - TimeKind.class); + private final Map adapters = new EnumMap<>(TimeKind.class); - //~ Constructors --------------------------------------------------------------------------- /** * Creates an instance of {code HeaderTimeBuilder}. * @@ -781,7 +942,6 @@ public HeaderTimeBuilder (Staff staff, projection = getProjection(); } - //~ Methods -------------------------------------------------------------------------------- //---------// // addPlot // //---------// @@ -987,7 +1147,7 @@ private List getSpaces () { final int xMin = roi.x; final int xMax = (roi.x + roi.width) - 1; - final List spaces = new ArrayList(); + final List spaces = new ArrayList<>(); // Space parameters int spaceStart = -1; // Space start abscissa @@ -1132,7 +1292,7 @@ private void purgeParts (List parts, // core.grow(-params.xCoreMargin, -params.yCoreMargin); // staff.addAttachment("c", core); // - // List toRemove = new ArrayList(); + // List toRemove = new ArrayList<>(); // // for (Glyph part : parts) { // if ((part.getWeight() < params.minPartWeight) || !part.getBounds().intersects(core)) { @@ -1159,7 +1319,6 @@ private void purgeParts (List parts, */ protected abstract static class Column { - //~ Instance fields ------------------------------------------------------------------------ /** Containing system. */ protected final SystemInfo system; @@ -1168,16 +1327,16 @@ protected abstract static class Column protected TimeValue timeValue; /** Map of time builders. (one per staff) */ - protected final Map builders = new TreeMap( - Staff.byId); + protected final Map builders = new TreeMap<>(Staff.byId); - //~ Constructors --------------------------------------------------------------------------- - public Column (SystemInfo system) + /** + * @param system + */ + Column (SystemInfo system) { this.system = system; } - //~ Methods -------------------------------------------------------------------------------- //---------------// // getTimeInters // //---------------// @@ -1188,7 +1347,7 @@ public Column (SystemInfo system) */ public Map getTimeInters () { - Map times = new TreeMap(Staff.byId); + Map times = new TreeMap<>(Staff.byId); for (Entry entry : builders.entrySet()) { times.put(entry.getKey(), entry.getValue().getTimeInter()); @@ -1256,7 +1415,7 @@ public int retrieveTime () * appears in each staff as a AbstractTimeInter instance and assign a global grade (as * average of staff-based AbstractTimeInter instances for the same TimeValue).

                                                                            3. *
                                                                            4. The best system-based TimeValue is then chosen as THE time signature for this - * system column.
                                                                            5. + * system column. *
                                                                            6. All staff non compatible AbstractTimeInter instances are destroyed and the member * numbers that don't belong to the chosen AbstractTimeInter are destroyed.
                                                                            7. *
                                                                            @@ -1267,7 +1426,7 @@ protected boolean checkConsistency () { // Retrieve all time values found, organized by value and staff Map vectors = getValueVectors(); - Map grades = new HashMap(); + Map grades = new HashMap<>(); TimeLoop: for (Entry entry : vectors.entrySet()) { @@ -1345,7 +1504,7 @@ protected boolean checkConsistency () protected Map getValueVectors () { // Retrieve all occurrences of time values across staves. - final Map values = new HashMap(); + final Map values = new HashMap<>(); // Loop on system staves final List staves = system.getStaves(); @@ -1375,9 +1534,7 @@ protected Map getValueVectors () TimeNumberInter num = (TimeNumberInter) nInter; for (Relation rel : sig.getRelations(num, TimeTopBottomRelation.class)) { - TimeNumberInter den = (TimeNumberInter) sig.getOppositeInter( - nInter, - rel); + TimeNumberInter den = (TimeNumberInter) sig.getOppositeInter(nInter, rel); TimePairInter pair = TimePairInter.createAdded(num, den); TimeValue time = pair.getValue(); AbstractTimeInter[] vector = values.get(time); @@ -1386,8 +1543,8 @@ protected Map getValueVectors () values.put(time, vector = new AbstractTimeInter[staves.size()]); } - if ((vector[index] == null) - || (pair.getGrade() > vector[index].getGrade())) { + if ((vector[index] == null) || (pair.getGrade() > vector[index] + .getGrade())) { vector[index] = pair; } } @@ -1418,7 +1575,6 @@ protected void purgeUnaligned () */ protected static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int maxEvalRank; @@ -1450,9 +1606,12 @@ protected static class Parameters final int maxSpaceCumul; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale, - int staffSpecific) + /** + * @param scale + * @param staffSpecific + */ + Parameters (Scale scale, + int staffSpecific) { maxEvalRank = constants.maxEvalRank.getValue(); @@ -1482,10 +1641,9 @@ public Parameters (Scale scale, //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer maxEvalRank = new Constant.Integer( "none", @@ -1548,7 +1706,6 @@ private static final class Constants //-------// private static class Space { - //~ Instance fields ------------------------------------------------------------------------ /** Left abscissa. */ protected final int start; @@ -1556,15 +1713,13 @@ private static class Space /** Right abscissa. */ protected final int stop; - //~ Constructors --------------------------------------------------------------------------- - public Space (int start, - int stop) + Space (int start, + int stop) { this.start = start; this.stop = stop; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { @@ -1576,181 +1731,4 @@ public String toString () } } - //-------------// - // HalfAdapter // - //-------------// - /** - * Handles the integration between glyph clustering class and time-sig environment. - *

                                                                            - * For each time kind, we keep the best result found if any. - */ - private class HalfAdapter - extends TimeAdapter - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Which half is being searched. (NUM or DEN) */ - private final TimeKind half; - - //~ Constructors --------------------------------------------------------------------------- - public HalfAdapter (TimeKind half, - List parts) - { - super(parts); - this.half = half; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void evaluateGlyph (Glyph glyph, - Set parts) - { - trials++; - - if (glyph.getId() == 0) { - glyph = system.registerGlyph(glyph, null); - } - - glyphCandidates.add(glyph); - - Evaluation[] evals = ShapeClassifier.getInstance().evaluate( - glyph, - staff.getSpecificInterline(), - params.maxEvalRank, - Grades.timeMinGrade / Grades.intrinsicRatio, - null); - - for (Evaluation eval : evals) { - final Shape shape = eval.shape; - - if (halfShapes.contains(shape)) { - final double grade = Grades.intrinsicRatio * eval.grade; - logger.debug(" {} eval {} for glyph#{}", half, eval, glyph.getId()); - - Inter bestInter = bestMap.get(shape); - - if ((bestInter == null) || (bestInter.getGrade() < grade)) { - TimeNumberInter inter = TimeNumberInter.create(glyph, shape, grade, staff); - - if (inter != null) { - bestMap.put(shape, inter); - } - } - } - } - } - - @Override - public boolean isTooLight (int weight) - { - return weight < params.minHalfTimeWeight; - } - } - - //-------------// - // TimeAdapter // - //-------------// - private abstract class TimeAdapter - extends GlyphCluster.AbstractAdapter - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Best inter per time shape. */ - public Map bestMap = new EnumMap(Shape.class); - - //~ Constructors --------------------------------------------------------------------------- - public TimeAdapter (List parts) - { - super(parts, params.maxPartGap); - } - - //~ Methods -------------------------------------------------------------------------------- - public void cleanup () - { - for (Inter inter : bestMap.values()) { - inter.remove(); - } - } - - public Inter getSingleInter () - { - for (Inter inter : bestMap.values()) { - if (!inter.isRemoved()) { - return inter; - } - } - - return null; - } - - @Override - public boolean isTooLarge (Rectangle bounds) - { - return bounds.width > params.maxTimeWidth; - } - } - - //--------------// - // WholeAdapter // - //--------------// - /** - * Handles the integration between glyph clustering class and time-sig environment. - *

                                                                            - * For each time value, we keep the best result found if any. - */ - private class WholeAdapter - extends TimeAdapter - { - //~ Constructors --------------------------------------------------------------------------- - - public WholeAdapter (List parts) - { - super(parts); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void evaluateGlyph (Glyph glyph, - Set parts) - { - //TODO: check glyph centroid for a whole symbol is not too far from staff middle line - trials++; - - if (glyph.getId() == 0) { - glyph = system.registerGlyph(glyph, null); - } - - glyphCandidates.add(glyph); - - Evaluation[] evals = ShapeClassifier.getInstance().evaluate( - glyph, - staff.getSpecificInterline(), - params.maxEvalRank, - Grades.timeMinGrade / Grades.intrinsicRatio, - null); - - for (Evaluation eval : evals) { - final Shape shape = eval.shape; - - if (wholeShapes.contains(shape)) { - final double grade = Grades.intrinsicRatio * eval.grade; - logger.debug(" WHOLE eval {} for glyph#{}", eval, glyph.getId()); - - Inter bestInter = bestMap.get(shape); - - if ((bestInter == null) || (bestInter.getGrade() < grade)) { - TimeWholeInter inter = new TimeWholeInter(glyph, shape, grade); - inter.setStaff(staff); - bestMap.put(shape, inter); - } - } - } - } - - @Override - public boolean isTooLight (int weight) - { - return weight < params.minWholeTimeWeight; - } - } } diff --git a/src/main/org/audiveris/omr/sheet/ledger/LedgersBuilder.java b/src/main/org/audiveris/omr/sheet/ledger/LedgersBuilder.java index 3148ad830..cd6779803 100644 --- a/src/main/org/audiveris/omr/sheet/ledger/LedgersBuilder.java +++ b/src/main/org/audiveris/omr/sheet/ledger/LedgersBuilder.java @@ -99,7 +99,6 @@ */ public class LedgersBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -121,8 +120,6 @@ public class LedgersBuilder private static final Failure TOO_SHIFTED = new Failure("Hori-TooShifted"); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Related sheet. */ @Navigable(false) private final Sheet sheet; @@ -156,7 +153,6 @@ public class LedgersBuilder private final NamedDouble minLengthHigh; - //~ Constructors ------------------------------------------------------------------------------- /** * @param system the related system to process */ @@ -177,7 +173,6 @@ public LedgersBuilder (SystemInfo system) minAbscissaOverlap = largeScale.toPixels(constants.minAbscissaOverlap); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // addCheckBoard // //---------------// @@ -223,25 +218,6 @@ public void buildLedgers (List

                                                                            sections) } } - //-----------// - // getMiddle // - //-----------// - /** - * Retrieve the middle point of a stick, assumed rather horizontal. - * - * @param stick the stick to process - * @return the middle point - */ - private static Point2D getMiddle (Filament stick) - { - final Point2D startPoint = stick.getStartPoint(); - final Point2D stopPoint = stick.getStopPoint(); - - return new Point2D.Double( - (startPoint.getX() + stopPoint.getX()) / 2, - (startPoint.getY() + stopPoint.getY()) / 2); - } - //-------------// // beamOverlap // //-------------// @@ -338,8 +314,7 @@ private List getCandidateFilaments (List
                                                                            sections) */ private List getGoodBeams () { - List beams = sig.inters( - new Predicate() + List beams = sig.inters(new Predicate() { @Override public boolean check (Inter inter) @@ -389,11 +364,6 @@ private Double getYReference (Staff staff, Rectangle stickBox = stick.getBounds(); for (LedgerInter ledger : prevLedgers) { - if (ledger.getGlyph() == stick) { - // This may occur when manually using the ledger check - continue; - } - Rectangle ledgerBox = ledger.getBounds(); if (GeoUtil.xOverlap(stickBox, ledgerBox) > minAbscissaOverlap) { @@ -480,8 +450,7 @@ private int lookupLine (Staff staff, final CheckSuite suite = suites.getSuite(interline); final InterlineScale staffScale = scale.getInterlineScale(interline); final int yMargin = staffScale.toPixels(constants.ledgerMarginY); - final LineInfo staffLine = (index < 0) ? staff.getFirstLine() - : staff.getLastLine(); + final LineInfo staffLine = (index < 0) ? staff.getFirstLine() : staff.getLastLine(); final GlyphIndex glyphIndex = sheet.getGlyphIndex(); final int minWide = staffScale.toPixels(constants.minWideLedgerLength); @@ -490,7 +459,7 @@ private int lookupLine (Staff staff, virtualLineBox.y += (index * interline); virtualLineBox.grow(0, 2 * yMargin); - final List ledgers = new ArrayList(); + final List ledgers = new ArrayList<>(); // Filter enclosed candidates and populate acceptable ledgers for (StraightFilament stick : ledgerCandidates) { @@ -507,7 +476,7 @@ private int lookupLine (Staff staff, // Check for presence of ledger on previous line // and definition of a reference ordinate (staff line or ledger) - final Wrapper prevWrapper = new Wrapper(null); + final Wrapper prevWrapper = new Wrapper<>(null); final Double yRef = getYReference(staff, index, stick, prevWrapper); if (yRef == null) { @@ -582,7 +551,7 @@ private int lookupLine (Staff staff, */ private void purgeBeamOverlaps (List filaments) { - List toRemove = new ArrayList(); + List toRemove = new ArrayList<>(); for (Filament fil : filaments) { if (beamOverlap(fil)) { @@ -612,7 +581,7 @@ private void reduceLedgers (Staff staff, { final int interline = staff.getSpecificInterline(); int maxDx = largeScale.toPixels(constants.maxInterLedgerDx); - Set exclusions = new LinkedHashSet(); + Set exclusions = new LinkedHashSet<>(); Collections.sort(ledgers, Inters.byAbscissa); for (int i = 0; i < ledgers.size(); i++) { @@ -644,161 +613,23 @@ private void reduceLedgers (Staff staff, } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// - // Constants // + // getMiddle // //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print out the stop watch?"); - - private final Constant.Ratio minSideRatio = new Constant.Ratio( - 0.8, - "Minimum ratio of filament length to be actually enlarged"); - - private final Constant.Double convexityLow = new Constant.Double( - "end number", - -0.5, - "Minimum convexity ends"); - - private final Constant.Double maxSlopeForCheck = new Constant.Double( - "tangent", - 0.1, - "Maximum slope for visual check"); - - // Constants specified WRT mean line thickness - // ------------------------------------------- - private final Scale.LineFraction maxThicknessHigh = new Scale.LineFraction( - 3, - "High Maximum thickness of an interesting stick (WRT staff line)"); - - private final Scale.LineFraction maxThicknessLow = new Scale.LineFraction( - 1.0, - "Low Maximum thickness of an interesting stick (WRT staff line)"); - - // Constants specified WRT mean interline - // -------------------------------------- - private final Scale.Fraction minCoreSectionLength = new Scale.Fraction( - 1.0, - "Minimum length for a section to be considered as core"); - - private final Scale.Fraction maxThicknessHigh2 = new Scale.Fraction( - 0.4, - "High Maximum thickness of an interesting stick (WRT interline)"); - - private final Scale.Fraction ledgerMarginY = new Scale.Fraction( - 0.35, - "Margin on ledger ordinate WRT theoretical ordinate"); - - private final Scale.Fraction minAbscissaOverlap = new Scale.Fraction( - 0.75, - "Minimum abscissa overlap of a ledger with the previous one"); - - private final Scale.Fraction minLedgerLengthHigh = new Scale.Fraction( - 1.5, - "High Minimum length for a ledger"); - - private final Scale.Fraction minLedgerLengthLow = new Scale.Fraction( - 1.0, - "Low Minimum length for a ledger"); - - private final Scale.Fraction minWideLedgerLength = new Scale.Fraction( - 1.5, - "Minimum length for a wide ledger"); - - private final Scale.Fraction minLedgerLengthHigh2 = new Scale.Fraction( - 2.0, - "High Minimum long length for a ledger"); - - private final Scale.Fraction minLedgerLengthLow2 = new Scale.Fraction( - 1.4, - "Low Minimum long length for a ledger"); - - private final Scale.Fraction minThicknessHigh = new Scale.Fraction( - 0.25, - "High Minimum thickness of an interesting stick"); - - private final Scale.Fraction maxInterLedgerDx = new Scale.Fraction( - 2.5, - "Maximum inter-ledger abscissa gap for ordinate compatibility test"); - - private final Scale.Fraction maxDistanceHigh = new Scale.Fraction( - 0.3, - "Low Minimum radius for ledger"); - } - - //------------------// - // LedgerCheckBoard // - //------------------// /** - * A specific board to display intrinsic checks of ledger sticks. + * Retrieve the middle point of a stick, assumed rather horizontal. + * + * @param stick the stick to process + * @return the middle point */ - private static class LedgerCheckBoard - extends CheckBoard + private static Point2D getMiddle (Filament stick) { - //~ Instance fields ------------------------------------------------------------------------ - - private final Sheet sheet; - - //~ Constructors --------------------------------------------------------------------------- - public LedgerCheckBoard (Sheet sheet) - { - super("LedgerCheck", null, sheet.getFilamentIndex().getEntityService(), eventClasses); - this.sheet = sheet; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void onEvent (UserEvent event) - { - try { - // Ignore RELEASING - if (event.movement == MouseMovement.RELEASING) { - return; - } - - if (event instanceof EntityListEvent) { - EntityListEvent listEvent = (EntityListEvent) event; - final Filament fil = listEvent.getEntity(); - - // Make sure we have a rather horizontal stick - if ((fil != null) - && (fil instanceof StraightFilament) - && (Math.abs(fil.getSlope()) <= constants.maxSlopeForCheck.getValue())) { - // Use the closest staff - Point center = fil.getCenter(); - StaffManager mgr = sheet.getStaffManager(); - Staff staff = mgr.getClosestStaff(center); - LedgersBuilder builder = new LedgersBuilder(staff.getSystem()); - int interline = staff.getSpecificInterline(); - CheckSuite suite = builder.suites.getSuite(interline); - - // We need a yTarget (which should take previous ledger into account!) - // Instead, we use an index value based on distance to staff - int pitch = (int) Math.rint(staff.pitchPositionOf(center)); - int index = (pitch / 2) - (2 * Integer.signum(pitch)); - Wrapper p = new Wrapper(null); - Double yRef = builder.getYReference(staff, index, fil, p); - - if (yRef != null) { - double yTarget = yRef + (Integer.signum(index) * interline); - applySuite(suite, new StickContext((StraightFilament) fil, yTarget)); - - return; - } - } + final Point2D startPoint = stick.getStartPoint(); + final Point2D stopPoint = stick.getStopPoint(); - tellObject(null); - } - } catch (Exception ex) { - logger.warn(getClass().getName() + " onEvent error", ex); - } - } + return new Point2D.Double( + (startPoint.getX() + stopPoint.getX()) / 2, + (startPoint.getY() + stopPoint.getY()) / 2); } //-------------// @@ -810,7 +641,6 @@ public void onEvent (UserEvent event) private class LedgerSuite extends CheckSuite { - //~ Instance fields ------------------------------------------------------------------------ /** * Staff 'specific' interline scale. @@ -818,11 +648,10 @@ private class LedgerSuite */ private final InterlineScale specific; - //~ Constructors --------------------------------------------------------------------------- /** * Create a check suite. */ - public LedgerSuite (InterlineScale interlineScale) + LedgerSuite (InterlineScale interlineScale) { super("Ledger"); this.specific = interlineScale; @@ -836,13 +665,11 @@ public LedgerSuite (InterlineScale interlineScale) add(0.5, new RightPitchCheck()); } - //~ Inner Classes -------------------------------------------------------------------------- private class ConvexityCheck extends Check { - //~ Constructors ----------------------------------------------------------------------- - public ConvexityCheck () + ConvexityCheck () { super( "Convex", @@ -853,7 +680,6 @@ public ConvexityCheck () TOO_CONCAVE); } - //~ Methods ---------------------------------------------------------------------------- @Override protected double getValue (StickContext context) { @@ -893,7 +719,6 @@ protected double getValue (StickContext context) private class LeftPitchCheck extends Check { - //~ Constructors ----------------------------------------------------------------------- protected LeftPitchCheck () { @@ -906,7 +731,6 @@ protected LeftPitchCheck () TOO_SHIFTED); } - //~ Methods ---------------------------------------------------------------------------- @Override protected double getValue (StickContext context) { @@ -921,7 +745,6 @@ protected double getValue (StickContext context) private class MaxThicknessCheck extends Check { - //~ Constructors ----------------------------------------------------------------------- protected MaxThicknessCheck () { @@ -934,7 +757,6 @@ protected MaxThicknessCheck () TOO_THICK); } - //~ Methods ---------------------------------------------------------------------------- // Retrieve the thickness data @Override protected double getValue (StickContext context) @@ -948,7 +770,6 @@ protected double getValue (StickContext context) private class MinLengthCheck extends Check { - //~ Constructors ----------------------------------------------------------------------- protected MinLengthCheck () { @@ -961,7 +782,6 @@ protected MinLengthCheck () TOO_SHORT); } - //~ Methods ---------------------------------------------------------------------------- // Retrieve the length data @Override protected double getValue (StickContext context) @@ -975,7 +795,6 @@ protected double getValue (StickContext context) private class MinThicknessCheck extends Check { - //~ Constructors ----------------------------------------------------------------------- protected MinThicknessCheck () { @@ -988,7 +807,6 @@ protected MinThicknessCheck () TOO_THIN); } - //~ Methods ---------------------------------------------------------------------------- // Retrieve the thickness data @Override protected double getValue (StickContext context) @@ -1002,7 +820,6 @@ protected double getValue (StickContext context) private class RightPitchCheck extends Check { - //~ Constructors ----------------------------------------------------------------------- protected RightPitchCheck () { @@ -1015,7 +832,6 @@ protected RightPitchCheck () TOO_SHIFTED); } - //~ Methods ---------------------------------------------------------------------------- @Override protected double getValue (StickContext context) { @@ -1030,7 +846,6 @@ protected double getValue (StickContext context) private class StraightCheck extends Check { - //~ Constructors ----------------------------------------------------------------------- protected StraightCheck () { @@ -1043,7 +858,6 @@ protected StraightCheck () TOO_BENDED); } - //~ Methods ---------------------------------------------------------------------------- @Override protected double getValue (StickContext context) { @@ -1054,35 +868,6 @@ protected double getValue (StickContext context) } } - //--------------// - // StickContext // - //--------------// - private static class StickContext - { - //~ Instance fields ------------------------------------------------------------------------ - - /** The stick being checked. */ - final StraightFilament stick; - - /** Target ordinate. */ - final double yTarget; - - //~ Constructors --------------------------------------------------------------------------- - public StickContext (StraightFilament stick, - double yTarget) - { - this.stick = stick; - this.yTarget = yTarget; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return "stick#" + stick.getId(); - } - } - //--------// // Suites // //--------// @@ -1091,12 +876,10 @@ public String toString () */ private class Suites { - //~ Instance fields ------------------------------------------------------------------------ - final Map map = new HashMap(); + final Map map = new HashMap<>(); - //~ Constructors --------------------------------------------------------------------------- - public Suites (Scale sheetScale) + Suites (Scale sheetScale) { final Integer large = sheetScale.getInterline(); map.put(large, new LedgerSuite(sheetScale.getInterlineScale())); @@ -1108,10 +891,187 @@ public Suites (Scale sheetScale) } } - //~ Methods -------------------------------------------------------------------------------- LedgerSuite getSuite (int interline) { return map.get(interline); } } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print out the stop watch?"); + + private final Constant.Ratio minSideRatio = new Constant.Ratio( + 0.8, + "Minimum ratio of filament length to be actually enlarged"); + + private final Constant.Double convexityLow = new Constant.Double( + "end number", + -0.5, + "Minimum convexity ends"); + + private final Constant.Double maxSlopeForCheck = new Constant.Double( + "tangent", + 0.1, + "Maximum slope for visual check"); + + // Constants specified WRT mean line thickness + // ------------------------------------------- + private final Scale.LineFraction maxThicknessHigh = new Scale.LineFraction( + 3, + "High Maximum thickness of an interesting stick (WRT staff line)"); + + private final Scale.LineFraction maxThicknessLow = new Scale.LineFraction( + 1.0, + "Low Maximum thickness of an interesting stick (WRT staff line)"); + + // Constants specified WRT mean interline + // -------------------------------------- + private final Scale.Fraction minCoreSectionLength = new Scale.Fraction( + 1.0, + "Minimum length for a section to be considered as core"); + + private final Scale.Fraction maxThicknessHigh2 = new Scale.Fraction( + 0.4, + "High Maximum thickness of an interesting stick (WRT interline)"); + + private final Scale.Fraction ledgerMarginY = new Scale.Fraction( + 0.35, + "Margin on ledger ordinate WRT theoretical ordinate"); + + private final Scale.Fraction minAbscissaOverlap = new Scale.Fraction( + 0.75, + "Minimum abscissa overlap of a ledger with the previous one"); + + private final Scale.Fraction minLedgerLengthHigh = new Scale.Fraction( + 1.5, + "High Minimum length for a ledger"); + + private final Scale.Fraction minLedgerLengthLow = new Scale.Fraction( + 1.0, + "Low Minimum length for a ledger"); + + private final Scale.Fraction minWideLedgerLength = new Scale.Fraction( + 1.5, + "Minimum length for a wide ledger"); + + private final Scale.Fraction minLedgerLengthHigh2 = new Scale.Fraction( + 2.0, + "High Minimum long length for a ledger"); + + private final Scale.Fraction minLedgerLengthLow2 = new Scale.Fraction( + 1.4, + "Low Minimum long length for a ledger"); + + private final Scale.Fraction minThicknessHigh = new Scale.Fraction( + 0.25, + "High Minimum thickness of an interesting stick"); + + private final Scale.Fraction maxInterLedgerDx = new Scale.Fraction( + 2.5, + "Maximum inter-ledger abscissa gap for ordinate compatibility test"); + + private final Scale.Fraction maxDistanceHigh = new Scale.Fraction( + 0.3, + "Low Minimum radius for ledger"); + } + + //------------------// + // LedgerCheckBoard // + //------------------// + /** + * A specific board to display intrinsic checks of ledger sticks. + */ + private static class LedgerCheckBoard + extends CheckBoard + { + + private final Sheet sheet; + + LedgerCheckBoard (Sheet sheet) + { + super("LedgerCheck", null, sheet.getFilamentIndex().getEntityService(), eventClasses); + this.sheet = sheet; + } + + @Override + public void onEvent (UserEvent event) + { + try { + // Ignore RELEASING + if (event.movement == MouseMovement.RELEASING) { + return; + } + + if (event instanceof EntityListEvent) { + EntityListEvent listEvent = (EntityListEvent) event; + final Filament fil = listEvent.getEntity(); + + // Make sure we have a rather horizontal stick + if ((fil != null) && (fil instanceof StraightFilament) && (Math.abs( + fil.getSlope()) <= constants.maxSlopeForCheck.getValue())) { + // Use the closest staff + Point center = fil.getCenter(); + StaffManager mgr = sheet.getStaffManager(); + Staff staff = mgr.getClosestStaff(center); + LedgersBuilder builder = new LedgersBuilder(staff.getSystem()); + int interline = staff.getSpecificInterline(); + CheckSuite suite = builder.suites.getSuite(interline); + + // We need a yTarget (which should take previous ledger into account!) + // Instead, we use an index value based on distance to staff + int pitch = (int) Math.rint(staff.pitchPositionOf(center)); + int index = (pitch / 2) - (2 * Integer.signum(pitch)); + Wrapper p = new Wrapper<>(null); + Double yRef = builder.getYReference(staff, index, fil, p); + + if (yRef != null) { + double yTarget = yRef + (Integer.signum(index) * interline); + applySuite(suite, new StickContext((StraightFilament) fil, yTarget)); + + return; + } + } + + tellObject(null); + } + } catch (Exception ex) { + logger.warn(getClass().getName() + " onEvent error", ex); + } + } + } + + //--------------// + // StickContext // + //--------------// + private static class StickContext + { + + /** The stick being checked. */ + final StraightFilament stick; + + /** Target ordinate. */ + final double yTarget; + + StickContext (StraightFilament stick, + double yTarget) + { + this.stick = stick; + this.yTarget = yTarget; + } + + @Override + public String toString () + { + return "stick#" + stick.getId(); + } + } + } diff --git a/src/main/org/audiveris/omr/sheet/ledger/LedgersFilter.java b/src/main/org/audiveris/omr/sheet/ledger/LedgersFilter.java index ae1b34aba..e9418bd7c 100644 --- a/src/main/org/audiveris/omr/sheet/ledger/LedgersFilter.java +++ b/src/main/org/audiveris/omr/sheet/ledger/LedgersFilter.java @@ -66,21 +66,17 @@ */ public class LedgersFilter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(LedgersFilter.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Related sheet. */ private final Sheet sheet; // Debug final List vipSections; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LedgersFilter object. * @@ -98,7 +94,6 @@ public LedgersFilter (Sheet sheet) } } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// @@ -115,8 +110,7 @@ public LedgersFilter (Sheet sheet) public Map> process () { final Scale scale = sheet.getScale(); - final int minDistanceFromStaff = scale.toPixels( - constants.minDistanceFromStaff); + final int minDistanceFromStaff = scale.toPixels(constants.minDistanceFromStaff); final StaffManager staffManager = sheet.getStaffManager(); // Filter to keep only the runs which stand outside of staves cores. @@ -166,8 +160,8 @@ public boolean check (int x, */ private Map> dispatchLedgerSections (Collection
                                                                            sections) { - Map> sectionMap = new TreeMap>(); - List relevants = new ArrayList(); + Map> sectionMap = new TreeMap<>(); + List relevants = new ArrayList<>(); SystemManager systemManager = sheet.getSystemManager(); for (Section section : sections) { @@ -180,7 +174,7 @@ private Map> dispatchLedgerSections (Collection list = sectionMap.get(system); if (list == null) { - sectionMap.put(system, list = new ArrayList
                                                                            ()); + sectionMap.put(system, list = new ArrayList<>()); } list.add(section); @@ -207,14 +201,12 @@ private void setVipSections (Lag lag) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean displayLedgers = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/sheet/ledger/LedgersStep.java b/src/main/org/audiveris/omr/sheet/ledger/LedgersStep.java index f438c1ce5..d9ba7f814 100644 --- a/src/main/org/audiveris/omr/sheet/ledger/LedgersStep.java +++ b/src/main/org/audiveris/omr/sheet/ledger/LedgersStep.java @@ -44,11 +44,9 @@ public class LedgersStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(LedgersStep.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LedgersStep object. */ @@ -56,7 +54,6 @@ public LedgersStep () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // displayUI // //-----------// @@ -99,18 +96,26 @@ protected Context doProlog (Sheet sheet) return new Context(new LedgersFilter(sheet).process()); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Context // //---------// + /** + * Context for step processing. + */ protected static class Context { - //~ Instance fields ------------------------------------------------------------------------ + /** + * Ledger candidate sections per system. + */ public final Map> sectionMap; - //~ Constructors --------------------------------------------------------------------------- - public Context (Map> sectionMap) + /** + * Create a Context. + * + * @param sectionMap + */ + Context (Map> sectionMap) { this.sectionMap = sectionMap; } diff --git a/src/main/org/audiveris/omr/sheet/note/ChordsBuilder.java b/src/main/org/audiveris/omr/sheet/note/ChordsBuilder.java index ed1e8a94d..a6d3ce28c 100644 --- a/src/main/org/audiveris/omr/sheet/note/ChordsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/note/ChordsBuilder.java @@ -74,7 +74,6 @@ */ public class ChordsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ChordsBuilder.class); @@ -92,7 +91,6 @@ public int compare (Relation o1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated system. */ @Navigable(false) private final SystemInfo system; @@ -100,7 +98,6 @@ public int compare (Relation o1, /** System SIG. */ private final SIGraph sig; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ChordsBuilder} object. * @@ -112,7 +109,6 @@ public ChordsBuilder (SystemInfo system) sig = system.getSig(); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // buildHeadChords // //-----------------// @@ -123,7 +119,7 @@ public void buildHeadChords () { for (Part part : system.getParts()) { // Stem-based chords defined so far in part - List stemChords = new ArrayList(); + List stemChords = new ArrayList<>(); for (Staff staff : part.getStaves()) { // Heads in staff @@ -132,7 +128,7 @@ public void buildHeadChords () logger.debug("Staff#{} heads:{}", staff.getId(), heads.size()); // Isolated heads (instances of WholeInter or SmallWholeInter) found so far in staff - List wholeHeads = new ArrayList(); + List wholeHeads = new ArrayList<>(); for (Inter inter : heads) { HeadInter head = (HeadInter) inter; @@ -154,6 +150,9 @@ public void buildHeadChords () //-----------------// // buildRestChords // //-----------------// + /** + * Allocate a chord for every rest. + */ public void buildRestChords () { List rests = sig.inters(RestInter.class); @@ -181,7 +180,7 @@ private HorizontalSide checkCanonicalShare (HeadInter head, List rels) { boolean ok = true; - final List stems = new ArrayList(); + final List stems = new ArrayList<>(); for (HorizontalSide side : HorizontalSide.values()) { int idx = (side == LEFT) ? 0 : 1; @@ -218,8 +217,8 @@ private HorizontalSide checkCanonicalShare (HeadInter head, } // No beam-stuck stem found, use global quality... - double leftGrade = stems.get(0).getGrade() * ((HeadStemRelation) rels.get(0)).getGrade(); - double rightGrade = stems.get(1).getGrade() * ((HeadStemRelation) rels.get(1)).getGrade(); + double leftGrade = stems.get(0).getGrade() * ((Support) rels.get(0)).getGrade(); + double rightGrade = stems.get(1).getGrade() * ((Support) rels.get(1)).getGrade(); final int idx = (leftGrade < rightGrade) ? 0 : 1; return HorizontalSide.values()[idx]; @@ -260,8 +259,7 @@ private boolean connectHead (HeadInter head, } // Look for connected stems - List rels = new ArrayList( - sig.getRelations(head, HeadStemRelation.class)); + List rels = new ArrayList<>(sig.getRelations(head, HeadStemRelation.class)); if (rels.size() == 2) { // A head with 2 stems needs to be logically duplicated @@ -322,8 +320,6 @@ private boolean connectHead (HeadInter head, } if (mirrorHead != null) { - head.getChord().setMirror(mirrorHead.getChord()); - mirrorHead.getChord().setMirror(head.getChord()); sig.addEdge(head, mirrorHead.getChord(), new NoExclusion()); sig.addEdge(head.getChord(), mirrorHead, new NoExclusion()); } @@ -409,7 +405,10 @@ private HeadInter duplicateHead (HeadInter head, final HeadInter rightHead; // TODO: perhaps duplicate void -> black when flag/beam involved + // But in this CHORDS step, the beams exist but not the flags yet rightHead = leftHead.duplicate(); + leftHead.getSig().addVertex(rightHead); + rightHead.setMirror(leftHead); // Handle relations as well Set supports = sig.getRelations(leftHead, Support.class); @@ -448,7 +447,9 @@ private HeadInter duplicateHead (HeadInter head, } else if (rel instanceof AlterHeadRelation) { // TODO } else if (rel instanceof AugmentationRelation) { - // TODO: to which head(s) does the dot apply? + // To which head(s) does the dot apply? + // This method runs in CHORDS step, and flags are not yet available (only beams are) + // So we must postpone the decision. } } @@ -461,7 +462,8 @@ private HeadInter duplicateHead (HeadInter head, /** * Report the chord(s) currently attached to the provided stem. *

                                                                            - * We can have:

                                                                              + * We can have: + *
                                                                                *
                                                                              • No chord found, simply because this stem has not yet been processed.
                                                                              • *
                                                                              • One chord found, this is the normal case.
                                                                              • *
                                                                              • Two chords found, when the same stem is "shared" by two chords (as in complex structures @@ -475,7 +477,7 @@ private HeadInter duplicateHead (HeadInter head, private List getStemChords (StemInter stem, List stemChords) { - final List found = new ArrayList(); + final List found = new ArrayList<>(); for (HeadChordInter chord : stemChords) { if (chord.getStem() == stem) { diff --git a/src/main/org/audiveris/omr/sheet/note/ChordsLinker.java b/src/main/org/audiveris/omr/sheet/note/ChordsLinker.java index 5fd69b6b9..445b0d808 100644 --- a/src/main/org/audiveris/omr/sheet/note/ChordsLinker.java +++ b/src/main/org/audiveris/omr/sheet/note/ChordsLinker.java @@ -53,13 +53,11 @@ */ public class ChordsLinker { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(ChordsLinker.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated system. */ @Navigable(false) private final SystemInfo system; @@ -68,7 +66,6 @@ public class ChordsLinker @Navigable(false) private final SIGraph sig; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ChordsLinker} object. * @@ -80,7 +77,6 @@ public ChordsLinker (SystemInfo system) sig = system.getSig(); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // checkBeamChords // //-----------------// @@ -121,12 +117,16 @@ public void checkBeamChords () logger.info("{} Overlapping {} {} vs {}", beam, ratio, prevChord, chord); StemInter prevStem = prevChord.getStem(); - BeamStemRelation prevRel = (BeamStemRelation) sig.getEdge( + BeamStemRelation prevRel = (BeamStemRelation) sig.getRelation( beam, - prevStem); + prevStem, + BeamStemRelation.class); StemInter stem = chord.getStem(); - BeamStemRelation rel = (BeamStemRelation) sig.getEdge(beam, stem); + BeamStemRelation rel = (BeamStemRelation) sig.getRelation( + beam, + stem, + BeamStemRelation.class); final BeamStemRelation guiltyRel; @@ -159,9 +159,11 @@ public void checkBeamChords () //------------// // linkChords // //------------// + /** + * Allocate beam groups per measure. + */ public void linkChords () { - // Allocate beam groups per stack for (MeasureStack stack : system.getStacks()) { for (Measure measure : stack.getMeasures()) { BeamGroup.populate(measure, true); // True for checkGroupSplit @@ -169,14 +171,12 @@ public void linkChords () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio maxAbscissaOverlapRatio = new Constant.Ratio( 0.25, diff --git a/src/main/org/audiveris/omr/sheet/note/ChordsStep.java b/src/main/org/audiveris/omr/sheet/note/ChordsStep.java index 5414a9c6d..a6aafd60b 100644 --- a/src/main/org/audiveris/omr/sheet/note/ChordsStep.java +++ b/src/main/org/audiveris/omr/sheet/note/ChordsStep.java @@ -55,7 +55,6 @@ public class ChordsStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ChordsStep.class); @@ -64,7 +63,7 @@ public class ChordsStep static { // Inters - impactingClasses = new HashSet(); + impactingClasses = new HashSet<>(); impactingClasses.add(AbstractBeamInter.class); impactingClasses.add(HeadInter.class); impactingClasses.add(StemInter.class); @@ -74,7 +73,6 @@ public class ChordsStep impactingClasses.add(HeadStemRelation.class); } - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ChordsStep object. */ @@ -82,7 +80,6 @@ public ChordsStep () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // doSystem // //----------// @@ -108,7 +105,8 @@ public void doSystem (SystemInfo system, /** * {@inheritDoc}. *

                                                                                - * For CHORDS step, in seq argument, we can have either:

                                                                                  + * For CHORDS step, in seq argument, we can have either: + *
                                                                                    *
                                                                                  • Beam created/removed or linked with stem *
                                                                                  • Head created/removed or linked with stem *
                                                                                  • Stem created/removed diff --git a/src/main/org/audiveris/omr/sheet/note/DistancesBuilder.java b/src/main/org/audiveris/omr/sheet/note/DistancesBuilder.java index 58e4787cd..3b6e039ea 100644 --- a/src/main/org/audiveris/omr/sheet/note/DistancesBuilder.java +++ b/src/main/org/audiveris/omr/sheet/note/DistancesBuilder.java @@ -45,7 +45,6 @@ import org.audiveris.omr.ui.BoardsPane; import org.audiveris.omr.ui.selection.AnchoredTemplateEvent; import org.audiveris.omr.ui.selection.SelectionService; - import static org.audiveris.omr.util.HorizontalSide.LEFT; import static org.audiveris.omr.util.HorizontalSide.RIGHT; @@ -65,20 +64,17 @@ */ public class DistancesBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(DistancesBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ private final Sheet sheet; /** Table of distances to fore. */ private DistanceTable table; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code DistancesBuilder} object. * @@ -89,10 +85,14 @@ public DistancesBuilder (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // buildDistances // //----------------// + /** + * Build the table of distances. + * + * @return the table of distance values + */ public DistanceTable buildDistances () { // Compute the distance-to-foreground transform image @@ -112,7 +112,9 @@ public DistanceTable buildDistances () TemplateBoard templateBoard = new TemplateBoard(sheet, table, templateService); sheet.getStub().getAssembly().addViewTab( SheetTab.TEMPLATE_TAB, - new ScrollImageView(sheet, new TemplateView(sheet, img, table, templateService)), + new ScrollImageView( + sheet, + new TemplateView(sheet, img, table, templateService)), new BoardsPane(new DistanceBoard(sheet, table), templateBoard)); templateBoard.stateChanged(null); // To feed template service } @@ -183,14 +185,12 @@ private void paintLines () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean displayTemplates = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/sheet/note/HeadSpotsBuilder.java b/src/main/org/audiveris/omr/sheet/note/HeadSpotsBuilder.java index 581b620b7..c86864263 100644 --- a/src/main/org/audiveris/omr/sheet/note/HeadSpotsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/note/HeadSpotsBuilder.java @@ -44,15 +44,13 @@ */ public class HeadSpotsBuilder { - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ private final Sheet sheet; /** Spot glyphs, per system. */ - Map> glyphMap = new HashMap>(); + Map> glyphMap = new HashMap<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code HeadSpotsBuilder} object. * @@ -63,7 +61,6 @@ public HeadSpotsBuilder (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getSpots // //----------// @@ -75,7 +72,10 @@ public HeadSpotsBuilder (Sheet sheet) public Map> getSpots () { RunTable headRuns = sheet.getPicture().getTable(Picture.TableKey.HEAD_SPOTS); - List spots = GlyphFactory.buildGlyphs(headRuns, new Point(0, 0), GlyphGroup.HEAD_SPOT); + List spots = GlyphFactory.buildGlyphs( + headRuns, + new Point(0, 0), + GlyphGroup.HEAD_SPOT); // Dispose the runTable sheet.getPicture().removeTable(Picture.TableKey.HEAD_SPOTS); @@ -95,9 +95,9 @@ public Map> getSpots () */ private Map> dispatchSheetSpots (List spots) { - Map> spotMap = new TreeMap>(); + Map> spotMap = new TreeMap<>(); - List relevants = new ArrayList(); + List relevants = new ArrayList<>(); SystemManager systemManager = sheet.getSystemManager(); for (Glyph spot : spots) { @@ -110,7 +110,7 @@ private Map> dispatchSheetSpots (List spots) List list = spotMap.get(system); if (list == null) { - spotMap.put(system, list = new ArrayList()); + spotMap.put(system, list = new ArrayList<>()); } spot.addGroup(GlyphGroup.HEAD_SPOT); // Needed diff --git a/src/main/org/audiveris/omr/sheet/note/HeadsStep.java b/src/main/org/audiveris/omr/sheet/note/HeadsStep.java index fe575ad8b..4724d2748 100644 --- a/src/main/org/audiveris/omr/sheet/note/HeadsStep.java +++ b/src/main/org/audiveris/omr/sheet/note/HeadsStep.java @@ -44,11 +44,9 @@ public class HeadsStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(HeadsStep.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code HeadsStep} object. */ @@ -56,7 +54,6 @@ public HeadsStep () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // doSystem // //----------// @@ -85,21 +82,33 @@ protected Context doProlog (Sheet sheet) return new Context(distances, sheetSpots); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Context // //---------// + /** + * COntext for step processing. + */ protected static class Context { - //~ Instance fields ------------------------------------------------------------------------ + /** + * Table of distances. + */ public final DistanceTable distanceTable; + /** + * Spots per system. + */ public final Map> sheetSpots; - //~ Constructors --------------------------------------------------------------------------- - public Context (DistanceTable distanceTable, - Map> sheetSpots) + /** + * Create a Context. + * + * @param distanceTable + * @param sheetSpots + */ + Context (DistanceTable distanceTable, + Map> sheetSpots) { this.distanceTable = distanceTable; this.sheetSpots = sheetSpots; diff --git a/src/main/org/audiveris/omr/sheet/note/NoteHeadsBuilder.java b/src/main/org/audiveris/omr/sheet/note/NoteHeadsBuilder.java index 96e0ee5f5..26e3603a9 100644 --- a/src/main/org/audiveris/omr/sheet/note/NoteHeadsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/note/NoteHeadsBuilder.java @@ -81,11 +81,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.EnumSet; import java.util.Iterator; import java.util.List; import java.util.Set; +import org.audiveris.omr.sig.inter.AbstractNoteInter; /** * Class {@code NoteHeadsBuilder} retrieves the void note heads, the black note heads, @@ -95,7 +95,8 @@ * shape, with a combination of foreground and background information. *

                                                                                    * We don't need to check each and every location in the system, but only the locations where such - * note kind is possible:

                                                                                      + * note kind is possible: + *
                                                                                        *
                                                                                      • We can stick to staff lines and ledgers locations.
                                                                                      • *
                                                                                      • We cannot fully use stems, since at this time we just have vertical seeds and not all stems * will contain seeds. However, if a vertical seed exists nearby we can use it to evaluate a note @@ -108,12 +109,10 @@ */ public class NoteHeadsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - NoteHeadsBuilder.class); + private static final Logger logger = LoggerFactory.getLogger(NoteHeadsBuilder.class); /** Shapes of note head competitors. */ private static final Set COMPETING_SHAPES = EnumSet.copyOf( @@ -130,7 +129,6 @@ public class NoteHeadsBuilder /** Specific value for no offsets. */ private static final int[] NO_OFFSETS = new int[]{0}; - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated system. */ @Navigable(false) private final SystemInfo system; @@ -157,7 +155,7 @@ public class NoteHeadsBuilder private final Parameters params; /** Minimum width of templates. */ - private int minTemplateWidth = 0; // TODO + private final int minTemplateWidth; /** The properly scaled templates to use, based on current staff. */ private Catalog catalog; @@ -165,8 +163,8 @@ public class NoteHeadsBuilder /** The competing interpretations for the system. */ private List systemCompetitors; - /** The forbidden rectangles around connectors and frozen barlines. */ - private List systemBarRectangles; + /** The forbidden areas around connectors and frozen barlines. */ + private List systemBarAreas; /** The vertical (stem) seeds for the system. */ private List systemSeeds; @@ -191,7 +189,6 @@ public class NoteHeadsBuilder private final Perf rangePerf = new Perf(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code NoteHeadsBuilder} object. * @@ -223,9 +220,11 @@ public NoteHeadsBuilder (SystemInfo system, // Compute window in x xOffsets = computeXOffsets(); + + // Compute a reasonable minTemplateWidth + minTemplateWidth = computeMinTemplateWidth(); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // buildHeads // //------------// @@ -236,7 +235,7 @@ public NoteHeadsBuilder (SystemInfo system, public void buildHeads () { StopWatch watch = new StopWatch("buildHeads S#" + system.getId()); - systemBarRectangles = getSystemBarRectangles(); + systemBarAreas = getSystemBarAreas(); systemCompetitors = getSystemCompetitors(); // Competitors systemSeeds = system.getGroupedGlyphs(GlyphGroup.VERTICAL_SEED); // Vertical seeds Collections.sort(systemSeeds, Glyphs.byOrdinate); @@ -250,7 +249,7 @@ public void buildHeads () final int pointSize = staff.getHeadPointSize(); catalog = TemplateFactory.getInstance().getCatalog(pointSize); - List ch = new ArrayList(); // Created Heads for this staff + List ch = new ArrayList<>(); // Created Heads for this staff // First, process all seed-based heads for the staff watch.start("Staff #" + staff.getId() + " seed"); @@ -281,7 +280,7 @@ public void buildHeads () } // Keep created heads in staff - staff.addNote((HeadInter) inter); + staff.addNote((AbstractNoteInter) inter); } } @@ -303,7 +302,7 @@ private List aggregateMatches (List inters) // Gather matches per close locations // Avoid duplicate locations - List aggregates = new ArrayList(); + List aggregates = new ArrayList<>(); for (HeadInter inter : inters) { Point loc = GeoUtil.centerOf(inter.getBounds()); @@ -329,7 +328,7 @@ private List aggregateMatches (List inters) aggregate.add(inter); } - List filtered = new ArrayList(); + List filtered = new ArrayList<>(); for (Aggregate ag : aggregates) { filtered.add(ag.getMainInter()); @@ -338,6 +337,19 @@ private List aggregateMatches (List inters) return filtered; } + //-------------------------// + // computeMinTemplateWidth // + //-------------------------// + /** + * Report a reasonable minimum template width (to protect against image right limit). + * + * @return min template width + */ + private int computeMinTemplateWidth () + { + return sheet.getScale().getInterline(); // Not too stupid... + } + //-----------------// // computeXOffsets // //-----------------// @@ -418,7 +430,7 @@ private HeadInter createInter (PixelDistance loc, private List filterSeedConflicts (List inters, List competitors) { - List filtered = new ArrayList(); + List filtered = new ArrayList<>(); for (HeadInter inter : inters) { if (!overlapSeed(inter, competitors)) { @@ -446,7 +458,7 @@ private List getCompetitorsSlice (Area area) area); // Keep only the "really good" competitors - List kept = new ArrayList(); + List kept = new ArrayList<>(); for (Inter inter : rawComps) { if (inter.isGood()) { @@ -474,7 +486,7 @@ private List getCompetitorsSlice (Area area) private List getGlyphsSlice (List glyphs, Area area) { - List slice = new ArrayList(Glyphs.intersectedGlyphs(glyphs, area)); + List slice = new ArrayList<>(Glyphs.intersectedGlyphs(glyphs, area)); Collections.sort(slice, Glyphs.byAbscissa); return slice; @@ -498,7 +510,7 @@ private List getLedgerAdapters (Staff staff, return Collections.emptyList(); } - List list = new ArrayList(); + List list = new ArrayList<>(); // Check for ledgers final int dir = Integer.signum(pitch); @@ -526,34 +538,30 @@ private List getLedgerAdapters (Staff staff, return list; } - //------------------------// - // getSystemBarRectangles // - //------------------------// - private List getSystemBarRectangles () + //-------------------// + // getSystemBarAreas // + //-------------------// + private List getSystemBarAreas () { - List rects = new ArrayList(); - List inters = sig.inters( - new Predicate() + List areas = new ArrayList<>(); + List inters = sig.inters(new Predicate() { @Override public boolean check (Inter inter) { - return inter.isFrozen() - && (inter instanceof BarlineInter - || inter instanceof BarConnectorInter); + return inter.isFrozen() && (inter instanceof BarlineInter + || inter instanceof BarConnectorInter); } }); Collections.sort(inters, Inters.byOrdinate); for (Inter inter : inters) { - // Add margin around bar bounds - Rectangle box = inter.getBounds(); - box.grow(params.hBarMargin, params.vBarMargin); - rects.add(box); + AbstractVerticalInter vertical = (AbstractVerticalInter) inter; + areas.add(vertical.getArea()); } - return rects; + return areas; } //----------------------// @@ -567,8 +575,7 @@ public boolean check (Inter inter) */ private List getSystemCompetitors () { - List comps = sig.inters( - new Predicate() + List comps = sig.inters(new Predicate() { @Override public boolean check (Inter inter) @@ -695,7 +702,7 @@ private boolean overlapSeed (Inter inter, private List processStaff (Staff staff, boolean useSeeds) { - List ch = new ArrayList(); // Created heads + List ch = new ArrayList<>(); // Created heads // Use all staff lines int pitch = -5; // Current pitch @@ -754,7 +761,7 @@ private List processStaff (Staff staff, //-----------------// private int purgeDuplicates (List inters) { - List removed = new ArrayList(); + List removed = new ArrayList<>(); LeftLoop: for (int i = 0, iBreak = inters.size() - 1; i < iBreak; i++) { @@ -805,220 +812,6 @@ private int purgeDuplicates (List inters) return removed.size(); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-------------// - // LineAdapter // - //-------------// - /** - * Such adapter is needed to interact with staff LineInfo or ledger glyph line in a - * consistent way. - */ - private abstract static class LineAdapter - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Staff staff; - - private final String prefix; - - //~ Constructors --------------------------------------------------------------------------- - public LineAdapter (Staff staff, - String prefix) - { - this.staff = staff; - this.prefix = prefix; - } - - //~ Methods -------------------------------------------------------------------------------- - /** - * Report the competitors lookup area, according to limits above - * and below, defined as ordinate shifts relative to the reference line. - * - * @param above offset (positive or negative) from line to top limit. - * @param below offset (positive or negative) from line to bottom limit. - */ - public abstract Area getArea (double above, - double below); - - /** Report the abscissa at beginning of line. */ - public abstract int getLeftAbscissa (); - - /** Needed to allow various attachments on the same staff. */ - public String getPrefix () - { - return prefix; - } - - /** Report the abscissa at end of line. */ - public abstract int getRightAbscissa (); - - public Staff getStaff () - { - return staff; - } - - /** Report the ordinate at provided abscissa. */ - public abstract int yAt (int x); - - /** Report the precise ordinate at provided precise abscissa. */ - public abstract double yAt (double x); - } - - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print out the stop watch?"); - - private final Constant.Boolean printParameters = new Constant.Boolean( - false, - "Should we print out the class parameters?"); - - private final Constant.Boolean allowAttachments = new Constant.Boolean( - false, - "Should we allow staff attachments for created areas?"); - - private final Scale.Fraction maxTemplateDx = new Scale.Fraction( - 0.375, - "Maximum dx between similar template instances"); - - private final Scale.Fraction maxOpenDy = new Scale.Fraction( - 0.25, - "Extension allowed in y for open lines"); - - private final Constant.Ratio gradeMargin = new Constant.Ratio( - 0.1, - "Grade margin to boost seed-based competitors"); - - private final Constant.Ratio pitchMargin = new Constant.Ratio( - 0.75, - "Vertical margin for intercepting stem seed around a target pitch"); - - private final Constant.Ratio wholeBoost = new Constant.Ratio( - 0.4, - "How much do we boost whole notes (always isolated)"); - - private final Scale.Fraction minBeamWidth = new Scale.Fraction( - 2.5, - "Minimum good beam width to exclude heads"); - - private final Scale.Fraction barHorizontalMargin = new Scale.Fraction( - 0.35, - "Horizontal margin around frozen barline or connector"); - - private final Scale.Fraction barVerticalMargin = new Scale.Fraction( - 2.0, - "Vertical margin around frozen barline or connector"); - - private final Constant.Ratio minHoleWhiteRatio = new Constant.Ratio( - 0.2, - "Minimum ratio of hole white pixel to reassign Black to Void"); - } - - //-----------// - // Aggregate // - //-----------// - /** - * Describes an aggregate of matches around similar location. - */ - private static class Aggregate - { - //~ Instance fields ------------------------------------------------------------------------ - - Point point; - - List matches = new ArrayList(); - - //~ Methods -------------------------------------------------------------------------------- - public void add (HeadInter inter) - { - if (point == null) { - point = GeoUtil.centerOf(inter.getBounds()); - } - - matches.add(inter); - } - - public HeadInter getMainInter () - { - return matches.get(0); - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()); - sb.append("{"); - - if (point != null) { - sb.append(" point:(").append(point.x).append(",").append(point.y).append(")"); - } - - sb.append(" ").append(matches.size()).append(" matches: "); - - for (Inter match : matches) { - sb.append(match); - } - - sb.append("}"); - - return sb.toString(); - } - } - - //------------// - // Parameters // - //------------// - /** - * Class {@code Parameters} gathers all pre-scaled constants. - */ - private static class Parameters - { - //~ Instance fields ------------------------------------------------------------------------ - - final double maxDistanceLow; - - final double maxDistanceHigh; - - final double reallyBadDistance; - - final int maxTemplateDx; - - final int maxOpenDy; - - final int minBeamWidth; - - final int hBarMargin; - - final int vBarMargin; - - //~ Constructors --------------------------------------------------------------------------- - /** - * Creates a new Parameters object. - * - * @param scale the scaling factor - */ - public Parameters (Scale scale) - { - maxDistanceLow = Template.maxDistanceLow(); - maxDistanceHigh = Template.maxDistanceHigh(); - reallyBadDistance = Template.reallyBadDistance(); - - maxTemplateDx = scale.toPixels(constants.maxTemplateDx); - maxOpenDy = Math.max(1, scale.toPixels(constants.maxOpenDy)); - minBeamWidth = scale.toPixels(constants.minBeamWidth); - - hBarMargin = scale.toPixels(constants.barHorizontalMargin); - vBarMargin = scale.toPixels(constants.barVerticalMargin); - } - } - //---------------// // LedgerAdapter // //---------------// @@ -1028,7 +821,6 @@ public Parameters (Scale scale) private class LedgerAdapter extends LineAdapter { - //~ Instance fields ------------------------------------------------------------------------ private final Glyph ledger; @@ -1036,10 +828,9 @@ private class LedgerAdapter private final Point2D right; - //~ Constructors --------------------------------------------------------------------------- - public LedgerAdapter (Staff staff, - String prefix, - Glyph ledger) + LedgerAdapter (Staff staff, + String prefix, + Glyph ledger) { super(staff, prefix); this.ledger = ledger; @@ -1047,7 +838,6 @@ public LedgerAdapter (Staff staff, right = ledger.getStopPoint(Orientation.HORIZONTAL); } - //~ Methods -------------------------------------------------------------------------------- @Override public Area getArea (double above, double below) @@ -1087,34 +877,6 @@ public double yAt (double x) } } - /** - * DEBUG: meant to precisely measure behavior of notes retrieval. - */ - private static class Perf - { - //~ Instance fields ------------------------------------------------------------------------ - - int bars; - - int overlaps; - - int evals; - - int abandons; - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return String.format( - "%7d bars, %7d overlaps, %7d evals, %7d abandons", - bars, - overlaps, - evals, - abandons); - } - } - //---------// // Scanner // //---------// @@ -1123,7 +885,6 @@ public String toString () */ private class Scanner { - //~ Instance fields ------------------------------------------------------------------------ private final int interline; @@ -1143,16 +904,15 @@ private class Scanner private final List competitors; - private final List barRectangles; + private final List barAreas; private final List ledgers; - private List inters = new ArrayList(); + private List inters = new ArrayList<>(); /** Offsets tried around a given ordinate. */ private final int[] yOffsets; - //~ Constructors --------------------------------------------------------------------------- /** * Create a Scanner. * @@ -1162,11 +922,11 @@ private class Scanner * @param pitch pitch position value * @param useSeeds true for seed-based notes, false for x-based notes */ - public Scanner (LineAdapter line, - LineAdapter line2, - int dir, - int pitch, - boolean useSeeds) + Scanner (LineAdapter line, + LineAdapter line2, + int dir, + int pitch, + boolean useSeeds) { this.line = line; this.line2 = line2; @@ -1202,10 +962,10 @@ public Scanner (LineAdapter line, { // Horizontal slice to detect bars/connectors final double vMargin = scale.toPixelsDouble(constants.barVerticalMargin); - final double above = ((interline * dir) / 2) - vMargin; - final double below = ((interline * dir) / 2) + vMargin; + final double above = ((interline * dir) / 2.0) - vMargin; + final double below = ((interline * dir) / 2.0) + vMargin; Area barsArea = line.getArea(above, below); - barRectangles = getBarRectangles(barsArea); + barAreas = getBarAreas(barsArea); } if (constants.allowAttachments.isSet()) { @@ -1216,7 +976,6 @@ public Scanner (LineAdapter line, competitors = getCompetitorsSlice(competitorsArea); } - //~ Methods -------------------------------------------------------------------------------- public List lookup () { return useSeeds ? lookupSeeds() : lookupRange(); @@ -1226,24 +985,18 @@ public List lookup () // barInvolved // //-------------// /** - * Check whether the provided rectangle would intersect neighborhood of frozen + * Check whether the provided rectangle would intersect area of frozen * barline/connector. * * @param rect provided rectangle - * @return true if neighborhood hit + * @return true if area hit */ private boolean barInvolved (Rectangle rect) { - int xBreak = rect.x + rect.width; - - for (Rectangle r : barRectangles) { - if (r.intersects(rect)) { + for (Area a : barAreas) { + if (a.intersects(rect)) { return true; } - - if (r.x >= xBreak) { - break; // Since barRectangles are ordered by abscissa - } } return false; @@ -1368,69 +1121,25 @@ private Shape evalBlackAsVoid (int x, } } - //------------------// - // getBarRectangles // - //------------------// + //-------------// + // getBarAreas // + //-------------// /** - * Build the list of rectangles around connectors and frozen barlines. + * Build the list of areas around connectors and frozen barlines. * - * @return the bar-centered rectangles + * @return the bar-centered areas */ - private List getBarRectangles (Area area) + private List getBarAreas (Area area) { - List kept = new ArrayList(); - - for (Rectangle r : systemBarRectangles) { - if (area.intersects(r)) { + List kept = new ArrayList<>(); + for (Area r : systemBarAreas) { + if (area.intersects(r.getBounds())) { kept.add(r); } } - - // Sort by abscissa for more efficient lookup - Collections.sort( - kept, - new Comparator() - { - @Override - public int compare (Rectangle r1, - Rectangle r2) - { - return Integer.compare(r1.x, r2.x); - } - }); - return kept; } - //---------------------// - // getBarSafeAbscissae // - //---------------------// - /** - * Select the x values sufficiently away from frozen barlines and connectors. - * - * @param scanLeft range starting abscissa - * @param scanRight range stopping abscissa - * @return an array of booleans, telling which x values are allowed - */ - private boolean[] getBarSafeAbscissae (int scanLeft, - int scanRight) - { - final boolean[] allowed = new boolean[scanRight - scanLeft + 1]; - Arrays.fill(allowed, true); - - for (Rectangle rect : barRectangles) { - for (int x = rect.x; x < (rect.x + rect.width); x++) { - int ix = x - scanLeft; - - if ((ix >= 0) && (ix < allowed.length)) { - allowed[ix] = false; - } - } - } - - return allowed; - } - //---------------------------// // getRelevantBlackAbscissae // //---------------------------// @@ -1487,8 +1196,8 @@ private int getTheoreticalOrdinate (int x) //TODO: refine using width of template? if (Math.abs(pitch) > 5) { for (LedgerAdapter ledger : ledgers) { - if ((x >= ledger.getLeftAbscissa()) - && (x <= ledger.getRightAbscissa())) { + if ((x >= ledger.getLeftAbscissa()) && (x <= ledger + .getRightAbscissa())) { return (int) Math.rint( (line.yAt((double) x) + ledger.yAt((double) x)) / 2); } @@ -1531,18 +1240,13 @@ private int getTheoreticalOrdinate (int x) private List lookupRange () { // Abscissa range for scan - final int scanLeft = Math.max( - line.getLeftAbscissa(), - (int) line.getStaff().getHeaderStop()); + final int scanLeft = Math.max(line.getLeftAbscissa(), line.getStaff().getHeaderStop()); final int scanRight = line.getRightAbscissa() - minTemplateWidth; - if (scanRight < scanLeft) { return inters; } - // Use the note spots to limit the abscissae to be checked for blacks boolean[] blackRelevants = getRelevantBlackAbscissae(scanLeft, scanRight); - // Scan from left to right for (int x0 = scanLeft; x0 <= scanRight; x0++) { final int y0 = getTheoreticalOrdinate(x0); @@ -1596,13 +1300,10 @@ private List lookupRange () } } } - // Aggregate matching inters inters = aggregateMatches(inters); - // Check conflict with seed-based instances inters = filterSeedConflicts(inters, competitors); - for (Iterator it = inters.iterator(); it.hasNext();) { HeadInter inter = it.next(); Glyph glyph = inter.retrieveGlyph(image); @@ -1613,7 +1314,6 @@ private List lookupRange () it.remove(); } } - return inters; } @@ -1717,19 +1417,16 @@ private List lookupSeeds () private class StaffLineAdapter extends LineAdapter { - //~ Instance fields ------------------------------------------------------------------------ private final LineInfo line; - //~ Constructors --------------------------------------------------------------------------- - public StaffLineAdapter (Staff staff, - LineInfo line) + StaffLineAdapter (Staff staff, + LineInfo line) { super(staff, ""); this.line = line; } - //~ Methods -------------------------------------------------------------------------------- @Override public Area getArea (double above, double below) @@ -1775,6 +1472,238 @@ public double yAt (double x) return line.yAt(x); } } + + //-------------// + // LineAdapter // + //-------------// + /** + * Such adapter is needed to interact with staff LineInfo or ledger glyph line in a + * consistent way. + */ + private abstract static class LineAdapter + { + + private final Staff staff; + + private final String prefix; + + LineAdapter (Staff staff, + String prefix) + { + this.staff = staff; + this.prefix = prefix; + } + + /** + * Report the competitors lookup area, according to limits above + * and below, defined as ordinate shifts relative to the reference line. + * + * @param above offset (positive or negative) from line to top limit. + * @param below offset (positive or negative) from line to bottom limit. + */ + public abstract Area getArea (double above, + double below); + + /** Report the abscissa at beginning of line. */ + public abstract int getLeftAbscissa (); + + /** Needed to allow various attachments on the same staff. */ + public String getPrefix () + { + return prefix; + } + + /** Report the abscissa at end of line. */ + public abstract int getRightAbscissa (); + + public Staff getStaff () + { + return staff; + } + + /** Report the ordinate at provided abscissa. */ + public abstract int yAt (int x); + + /** Report the precise ordinate at provided precise abscissa. */ + public abstract double yAt (double x); + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print out the stop watch?"); + + private final Constant.Boolean printParameters = new Constant.Boolean( + false, + "Should we print out the class parameters?"); + + private final Constant.Boolean allowAttachments = new Constant.Boolean( + false, + "Should we allow staff attachments for created areas?"); + + private final Scale.Fraction maxTemplateDx = new Scale.Fraction( + 0.375, + "Maximum dx between similar template instances"); + + private final Scale.Fraction maxOpenDy = new Scale.Fraction( + 0.25, + "Extension allowed in y for open lines"); + + private final Constant.Ratio gradeMargin = new Constant.Ratio( + 0.1, + "Grade margin to boost seed-based competitors"); + + private final Constant.Ratio pitchMargin = new Constant.Ratio( + 0.75, + "Vertical margin for intercepting stem seed around a target pitch"); + + private final Constant.Ratio wholeBoost = new Constant.Ratio( + 0.38, + "How much do we boost whole notes (always isolated)"); + + private final Scale.Fraction minBeamWidth = new Scale.Fraction( + 2.5, + "Minimum good beam width to exclude heads"); + + private final Scale.Fraction barHorizontalMargin = new Scale.Fraction( + 0.1, + "Horizontal margin around frozen barline or connector"); + + private final Scale.Fraction barVerticalMargin = new Scale.Fraction( + 2.0, + "Vertical margin around frozen barline or connector"); + + private final Constant.Ratio minHoleWhiteRatio = new Constant.Ratio( + 0.2, + "Minimum ratio of hole white pixel to reassign Black to Void"); + } + + //-----------// + // Aggregate // + //-----------// + /** + * Describes an aggregate of matches around similar location. + */ + private static class Aggregate + { + + Point point; + + List matches = new ArrayList<>(); + + public void add (HeadInter inter) + { + if (point == null) { + point = GeoUtil.centerOf(inter.getBounds()); + } + + matches.add(inter); + } + + public HeadInter getMainInter () + { + return matches.get(0); + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + sb.append("{"); + + if (point != null) { + sb.append(" point:(").append(point.x).append(",").append(point.y).append(")"); + } + + sb.append(" ").append(matches.size()).append(" matches: "); + + for (Inter match : matches) { + sb.append(match); + } + + sb.append("}"); + + return sb.toString(); + } + } + + //------------// + // Parameters // + //------------// + /** + * Class {@code Parameters} gathers all pre-scaled constants. + */ + private static class Parameters + { + + final double maxDistanceLow; + + final double maxDistanceHigh; + + final double reallyBadDistance; + + final int maxTemplateDx; + + final int maxOpenDy; + + final int minBeamWidth; + + final int hBarMargin; + + final int vBarMargin; + + /** + * Creates a new Parameters object. + * + * @param scale the scaling factor + */ + Parameters (Scale scale) + { + maxDistanceLow = Template.maxDistanceLow(); + maxDistanceHigh = Template.maxDistanceHigh(); + reallyBadDistance = Template.reallyBadDistance(); + + maxTemplateDx = scale.toPixels(constants.maxTemplateDx); + maxOpenDy = Math.max(1, scale.toPixels(constants.maxOpenDy)); + minBeamWidth = scale.toPixels(constants.minBeamWidth); + + hBarMargin = scale.toPixels(constants.barHorizontalMargin); + vBarMargin = scale.toPixels(constants.barVerticalMargin); + } + } + + /** + * DEBUG: meant to precisely measure behavior of notes retrieval. + */ + private static class Perf + { + + int bars; + + int overlaps; + + int evals; + + int abandons; + + @Override + public String toString () + { + return String.format( + "%7d bars, %7d overlaps, %7d evals, %7d abandons", + bars, + overlaps, + evals, + abandons); + } + } + } // // //--------------// diff --git a/src/main/org/audiveris/omr/sheet/note/NotePosition.java b/src/main/org/audiveris/omr/sheet/note/NotePosition.java index 0a030d886..1c504f6fb 100644 --- a/src/main/org/audiveris/omr/sheet/note/NotePosition.java +++ b/src/main/org/audiveris/omr/sheet/note/NotePosition.java @@ -32,7 +32,6 @@ */ public class NotePosition { - //~ Instance fields ---------------------------------------------------------------------------- /** The related staff. */ private final Staff staff; @@ -43,7 +42,6 @@ public class NotePosition /** The closest ledger if any. */ private final IndexedLedger indexedLedger; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new NotePosition object. * @@ -60,7 +58,6 @@ public NotePosition (Staff staff, this.pitchPosition = pitchPosition; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getLedger // //-----------// diff --git a/src/main/org/audiveris/omr/sheet/package-info.java b/src/main/org/audiveris/omr/sheet/package-info.java index b200eaf38..8122e106f 100644 --- a/src/main/org/audiveris/omr/sheet/package-info.java +++ b/src/main/org/audiveris/omr/sheet/package-info.java @@ -1,20 +1,4 @@ /** * Package for processing of a sheet image. */ -@XmlJavaTypeAdapters({ - @XmlJavaTypeAdapter(value = Jaxb.PathAdapter.class, type = Path.class), - @XmlJavaTypeAdapter(value = Jaxb.PointAdapter.class, type = Point.class), - @XmlJavaTypeAdapter(value = Jaxb.Point2DAdapter.class, type = Point2D.class), - @XmlJavaTypeAdapter(value = Jaxb.RectangleAdapter.class, type = Rectangle.class) -}) package org.audiveris.omr.sheet; - -import org.audiveris.omr.util.Jaxb; - -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.Point2D; -import java.nio.file.Path; - -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; diff --git a/src/main/org/audiveris/omr/sheet/rhythm/Measure.java b/src/main/org/audiveris/omr/sheet/rhythm/Measure.java index 471920a0a..eda96812b 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/Measure.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/Measure.java @@ -41,6 +41,7 @@ import org.audiveris.omr.sig.inter.KeyInter; import org.audiveris.omr.sig.inter.RestChordInter; import org.audiveris.omr.sig.inter.RestInter; +import org.audiveris.omr.sig.inter.SmallChordInter; import org.audiveris.omr.sig.inter.StaffBarlineInter; import org.audiveris.omr.sig.inter.TupletInter; import org.audiveris.omr.util.HorizontalSide; @@ -79,23 +80,18 @@ * all the staves (usually 1 or 2) of the containing part. * * @see MeasureStack - * * @author Hervé Bitteur */ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "measure") public class Measure { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - Measure.class); + private static final Logger logger = LoggerFactory.getLogger(Measure.class); /** Offset in voice ID, according to its initial staff. */ private static int ID_STAFF_OFFSET = 4; - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -113,10 +109,12 @@ public class Measure /** Groups of beams in this measure. Populated by CHORDS step. */ @XmlElementRef - private final Set beamGroups = new LinkedHashSet(); + private final Set beamGroups = new LinkedHashSet<>(); - /** Possibly several Clefs per staff. - * Implemented as a list, kept ordered by clef full abscissa */ + /** + * Possibly several Clefs per staff. + * Implemented as a list, kept ordered by clef full abscissa + */ @XmlList @XmlIDREF @XmlElement(name = "clefs") @@ -134,7 +132,7 @@ public class Measure @XmlElement(name = "times") private Set timeSigs; - /** Head chords. Populated by CHORDS step. */ + /** Head chords, both standard and small. Populated by CHORDS step. */ @XmlList @XmlIDREF @XmlElement(name = "head-chords") @@ -166,7 +164,7 @@ public class Measure /** Voices within this measure, sorted by voice id. Populated by RHYTHMS step. */ @XmlElement(name = "voice") - private final List voices = new ArrayList(); + private final List voices = new ArrayList<>(); // Transient data //--------------- @@ -182,7 +180,6 @@ public class Measure @Navigable(false) private MeasureStack stack; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code Measure} object. * @@ -201,7 +198,6 @@ private Measure () this.part = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // addBeamGroup // //--------------// @@ -251,7 +247,7 @@ class FakeRest FakeChord chord; - public FakeRest (Staff staff) + FakeRest (Staff staff) { super(null, Shape.WHOLE_REST, 0, staff, -1.0); } @@ -302,7 +298,7 @@ public void addInter (Inter inter) final ClefInter clef = (ClefInter) inter; if (clefs == null) { - clefs = new ArrayList(); + clefs = new ArrayList<>(); } if (!clefs.contains(clef)) { @@ -313,7 +309,7 @@ public void addInter (Inter inter) final KeyInter key = (KeyInter) inter; if (keys == null) { - keys = new LinkedHashSet(); + keys = new LinkedHashSet<>(); } keys.add(key); @@ -321,25 +317,25 @@ public void addInter (Inter inter) final AbstractTimeInter time = (AbstractTimeInter) inter; if (timeSigs == null) { - timeSigs = new LinkedHashSet(); + timeSigs = new LinkedHashSet<>(); } timeSigs.add(time); } else if (inter instanceof FlagInter) { if (flags == null) { - flags = new LinkedHashSet(); + flags = new LinkedHashSet<>(); } flags.add((FlagInter) inter); } else if (inter instanceof AugmentationDotInter) { if (augDots == null) { - augDots = new LinkedHashSet(); + augDots = new LinkedHashSet<>(); } augDots.add((AugmentationDotInter) inter); } else if (inter instanceof TupletInter) { if (tuplets == null) { - tuplets = new LinkedHashSet(); + tuplets = new LinkedHashSet<>(); } tuplets.add((TupletInter) inter); @@ -351,6 +347,11 @@ public void addInter (Inter inter) //----------// // addVoice // //----------// + /** + * Add a voice into measure. + * + * @param voice the voice to add + */ public void addVoice (Voice voice) { voices.add(voice); @@ -359,6 +360,9 @@ public void addVoice (Voice voice) //-------------// // afterReload // //-------------// + /** + * To be called right after unmarshalling. + */ public void afterReload () { try { @@ -368,8 +372,10 @@ public void afterReload () List measureInters = filter( sig.inters( new Class[]{ - ClefInter.class, KeyInter.class, AbstractTimeInter.class, TupletInter.class - })); + ClefInter.class, + KeyInter.class, + AbstractTimeInter.class, + TupletInter.class})); for (Inter inter : measureInters) { addInter(inter); @@ -401,52 +407,28 @@ public void afterReload () //-----------------// // clearBeamGroups // //-----------------// + /** + * Reset collection of beam groups for this measure. + */ public void clearBeamGroups () { beamGroups.clear(); } - //------------// - // clearFrats // - //------------// - public void clearFrats () - { - flags = null; - restChords = null; - augDots = null; - tuplets = null; - } - - //----------// - // contains // - //----------// - public boolean contains (PartBarline partBarline) - { - return getContainedPartBarlines().contains(partBarline); - } - - //----------// - // contains // - //----------// - public boolean contains (StaffBarlineInter staffBarline) - { - for (PartBarline partBarline : getContainedPartBarlines()) { - if (partBarline.contains(staffBarline)) { - return true; - } - } - - return false; - } - //--------// // filter // //--------// + /** + * Retrieve among the provided inters the ones contains in this measure. + * + * @param inters the provided inters + * @return the contained inters + */ public List filter (Collection inters) { final int left = getLeft(); final int right = getRight(); - final List kept = new ArrayList(); + final List kept = new ArrayList<>(); for (Inter inter : inters) { Point center = inter.getCenter(); @@ -552,6 +534,11 @@ public int getAbscissa (HorizontalSide side, //---------------------// // getAugmentationDots // //---------------------// + /** + * Report the augmentation dots in this measure. + * + * @return the augmentation dots in measure + */ public Set getAugmentationDots () { return (augDots != null) ? Collections.unmodifiableSet(augDots) : Collections.EMPTY_SET; @@ -635,7 +622,7 @@ public List getClefs () */ public List getContainedPartBarlines () { - List list = new ArrayList(); + List list = new ArrayList<>(); if (leftBarline != null) { list.add(leftBarline); @@ -679,6 +666,11 @@ public ClefInter getFirstMeasureClef (int staffIndexInPart) //----------// // getFlags // //----------// + /** + * Report the flags in this measure. + * + * @return the flags in measure + */ public Set getFlags () { return (flags != null) ? Collections.unmodifiableSet(flags) : Collections.EMPTY_SET; @@ -687,9 +679,15 @@ public Set getFlags () //---------------// // getHeadChords // //---------------// + /** + * Report the head chords in this measure. + * + * @return the measure head chords + */ public Set getHeadChords () { - return (headChords != null) ? Collections.unmodifiableSet(headChords) : Collections.EMPTY_SET; + return (headChords != null) ? Collections.unmodifiableSet(headChords) + : Collections.EMPTY_SET; } //--------------------// @@ -705,7 +703,7 @@ public Set getHeadChords () public Collection getHeadChordsAbove (Point point) { Staff desiredStaff = stack.getSystem().getStaffAtOrAbove(point); - Collection found = new ArrayList(); + Collection found = new ArrayList<>(); for (HeadChordInter chord : getHeadChords()) { if (chord.getBottomStaff() == desiredStaff) { @@ -733,7 +731,7 @@ public Collection getHeadChordsAbove (Point point) public Collection getHeadChordsBelow (Point point) { Staff desiredStaff = stack.getSystem().getStaffAtOrBelow(point); - Collection found = new ArrayList(); + Collection found = new ArrayList<>(); for (HeadChordInter chord : getHeadChords()) { if (chord.getTopStaff() == desiredStaff) { @@ -830,7 +828,8 @@ public ClefInter getLastMeasureClef (Staff staff) { // Going backwards if (clefs != null) { - for (ListIterator lit = clefs.listIterator(clefs.size()); lit.hasPrevious();) { + for (ListIterator lit = clefs.listIterator(clefs.size()); lit + .hasPrevious();) { ClefInter clef = lit.previous(); if (clef.getStaff() == staff) { @@ -845,11 +844,29 @@ public ClefInter getLastMeasureClef (Staff staff) //--------------------// // getLeftPartBarline // //--------------------// + /** + * Report the PartBarline, if any, on left. + * + * @return left PartBarline or null + */ public PartBarline getLeftPartBarline () { return leftBarline; } + //--------------------// + // setLeftPartBarline // + //--------------------// + /** + * Set the PartBarline on left. + * + * @param leftBarline left barline + */ + public void setLeftPartBarline (PartBarline leftBarline) + { + this.leftBarline = leftBarline; + } + //----------------------// // getMeasureClefBefore // //----------------------// @@ -868,7 +885,8 @@ public ClefInter getMeasureClefBefore (Point point, // Look in this measure, with same staff, going backwards if (clefs != null) { - for (ListIterator lit = clefs.listIterator(clefs.size()); lit.hasPrevious();) { + for (ListIterator lit = clefs.listIterator(clefs.size()); lit + .hasPrevious();) { ClefInter clef = lit.previous(); if ((clef.getStaff() == staff) && (clef.getCenter().x <= point.x)) { @@ -893,9 +911,27 @@ public PartBarline getMidPartBarline () return midBarline; } + //-------------------// + // setMidPartBarline // + //-------------------// + /** + * Set the middle PartBarline. + * + * @param midBarline mid barline + */ + public void setMidPartBarline (PartBarline midBarline) + { + this.midBarline = midBarline; + } + //---------// // getPart // //---------// + /** + * Report the containing part. + * + * @return the part that contains this measure + */ public Part getPart () { return part; @@ -994,27 +1030,52 @@ public Measure getPrecedingInSystem () //---------------// // getRestChords // //---------------// + /** + * Report the rest chords in this measure. + * + * @return all rest chords in this measure + */ public Set getRestChords () { - return (restChords != null) ? Collections.unmodifiableSet(restChords) : Collections.EMPTY_SET; + return (restChords != null) ? Collections.unmodifiableSet(restChords) + : Collections.EMPTY_SET; } //---------------------// // getRightPartBarline // //---------------------// /** - * Report the ending PartBarline. + * Report the right PartBarline, if any. * - * @return the ending PartBarline + * @return the ending PartBarline or null */ public PartBarline getRightPartBarline () { return rightBarline; } + //---------------------// + // setRightPartBarline // + //---------------------// + /** + * Assign the (right) PartBarline that ends this measure + * + * @param rightBarline the right PartBarline + */ + public void setRightPartBarline (PartBarline rightBarline) + { + this.rightBarline = rightBarline; + } + //------------// // getSibling // //------------// + /** + * Report the sibling measure on the provided side. + * + * @param side horizontal side + * @return sibling measure, or null if none + */ public Measure getSibling (HorizontalSide side) { final List measures = part.getMeasures(); @@ -1110,20 +1171,39 @@ public MeasureStack getStack () return stack; } + //----------// + // setStack // + //----------// + /** + * @param stack the stack to set + */ + public void setStack (MeasureStack stack) + { + this.stack = stack; + } + //-------------------// // getStandardChords // //-------------------// /** - * Report the collection of standard chords (head chords, rest chords) + * Report the collection of standard chords (head chords, rest chords) but not the + * SmallChordInter instances. * * @return the set of all standard chords in this measure */ public Set getStandardChords () { - final Set stdChords = new LinkedHashSet(); + final Set stdChords = new LinkedHashSet<>(); stdChords.addAll(getHeadChords()); stdChords.addAll(getRestChords()); + // Remove small head chords if any + for (Iterator it = stdChords.iterator(); it.hasNext();) { + if (it.next() instanceof SmallChordInter) { + it.remove(); + } + } + return stdChords; } @@ -1178,7 +1258,7 @@ public AbstractTimeInter getTimeSignature (int staffIndexInPart) */ public Set getTimingInters () { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); for (BeamGroup beamGroup : beamGroups) { set.addAll(beamGroup.getBeams()); @@ -1196,6 +1276,11 @@ public Set getTimingInters () //------------// // getTuplets // //------------// + /** + * Report the tuplets in this measure. + * + * @return all tuplets in measure + */ public Set getTuplets () { return (tuplets != null) ? Collections.unmodifiableSet(tuplets) : Collections.EMPTY_SET; @@ -1204,6 +1289,11 @@ public Set getTuplets () //-----------// // getVoices // //-----------// + /** + * Report the sequence of voices in this measure. + * + * @return sequence of voices + */ public List getVoices () { return Collections.unmodifiableList(voices); @@ -1277,6 +1367,11 @@ public boolean hasSameKeys () //---------// // isDummy // //---------// + /** + * Tell whether this measure is dummy (in a dummy part). + * + * @return true if so + */ public boolean isDummy () { return dummy; @@ -1381,7 +1476,7 @@ public void mergeWithRight (Measure right) // Clefs if ((right.clefs != null) && !right.clefs.isEmpty()) { if (clefs == null) { - clefs = new ArrayList(); + clefs = new ArrayList<>(); } clefs.removeAll(right.clefs); // Just in cases @@ -1401,7 +1496,7 @@ public void mergeWithRight (Measure right) // Times if (right.timeSigs != null) { if (timeSigs == null) { - timeSigs = new LinkedHashSet(); + timeSigs = new LinkedHashSet<>(); } timeSigs.addAll(right.timeSigs); @@ -1428,7 +1523,7 @@ public void mergeWithRight (Measure right) // Flags if (!right.getFlags().isEmpty()) { if (flags == null) { - flags = new LinkedHashSet(); + flags = new LinkedHashSet<>(); } flags.addAll(right.getFlags()); @@ -1437,7 +1532,7 @@ public void mergeWithRight (Measure right) // Tuplets if (!right.getTuplets().isEmpty()) { if (tuplets == null) { - tuplets = new LinkedHashSet(); + tuplets = new LinkedHashSet<>(); } tuplets.addAll(right.getTuplets()); @@ -1446,7 +1541,7 @@ public void mergeWithRight (Measure right) // Augmentation dots if (!right.getAugmentationDots().isEmpty()) { if (augDots == null) { - augDots = new LinkedHashSet(); + augDots = new LinkedHashSet<>(); } augDots.addAll(right.getAugmentationDots()); @@ -1463,6 +1558,11 @@ public void mergeWithRight (Measure right) //-----------------// // removeBeamGroup // //-----------------// + /** + * Remove the provided beamGroup from this measure. + * + * @param beamGroup the beam group to remove + */ public void removeBeamGroup (BeamGroup beamGroup) { beamGroups.remove(beamGroup); @@ -1484,7 +1584,7 @@ public void removeInter (Inter inter) if (inter instanceof FlagInter) { if (flags != null) { - flags.remove((FlagInter) inter); + flags.remove(inter); if (flags.isEmpty()) { flags = null; @@ -1492,7 +1592,7 @@ public void removeInter (Inter inter) } } else if (inter instanceof RestChordInter) { if (restChords != null) { - restChords.remove((RestChordInter) inter); + restChords.remove(inter); if (restChords.isEmpty()) { restChords = null; @@ -1500,7 +1600,7 @@ public void removeInter (Inter inter) } } else if (inter instanceof AugmentationDotInter) { if (augDots != null) { - augDots.remove((AugmentationDotInter) inter); + augDots.remove(inter); if (augDots.isEmpty()) { augDots = null; @@ -1508,7 +1608,7 @@ public void removeInter (Inter inter) } } else if (inter instanceof TupletInter) { if (tuplets != null) { - tuplets.remove((TupletInter) inter); + tuplets.remove(inter); if (tuplets.isEmpty()) { tuplets = null; @@ -1516,7 +1616,7 @@ public void removeInter (Inter inter) } } else if (inter instanceof HeadChordInter) { if (headChords != null) { - headChords.remove((HeadChordInter) inter); + headChords.remove(inter); if (headChords.isEmpty()) { headChords = null; @@ -1524,7 +1624,7 @@ public void removeInter (Inter inter) } } else if (inter instanceof KeyInter) { if (keys != null) { - keys.remove((KeyInter) inter); + keys.remove(inter); if (keys.isEmpty()) { keys = null; @@ -1532,7 +1632,7 @@ public void removeInter (Inter inter) } } else if (inter instanceof AbstractTimeInter) { if (timeSigs != null) { - timeSigs.remove((AbstractTimeInter) inter); + timeSigs.remove(inter); if (timeSigs.isEmpty()) { timeSigs = null; @@ -1540,7 +1640,7 @@ public void removeInter (Inter inter) } } else if (inter instanceof ClefInter) { if (clefs != null) { - clefs.remove((ClefInter) inter); + clefs.remove(inter); if (clefs.isEmpty()) { clefs = null; @@ -1594,6 +1694,9 @@ public Measure replicate (Part targetPart) //-------------// // resetRhythm // //-------------// + /** + * Nullify rhythm information in this measure. + */ public void resetRhythm () { voices.clear(); @@ -1603,63 +1706,33 @@ public void resetRhythm () group.resetTiming(); } - // Forward reset to every chord handled - for (AbstractChordInter chord : getStandardChords()) { + // Forward reset to every chord in measure (standard and small) + for (AbstractChordInter chord : getHeadChords()) { chord.resetTiming(); } - } - //----------// - // setDummy // - //----------// - public void setDummy () - { - dummy = true; - } - - //--------------------// - // setLeftPartBarline // - //--------------------// - public void setLeftPartBarline (PartBarline leftBarline) - { - this.leftBarline = leftBarline; - } - - //-------------------// - // setMidPartBarline // - //-------------------// - public void setMidPartBarline (PartBarline midBarline) - { - this.midBarline = midBarline; - } - - //---------------------// - // setRightPartBarline // - //---------------------// - /** - * Assign the (right) PartBarline that ends this measure - * - * @param rightBarline the right PartBarline - */ - public void setRightPartBarline (PartBarline rightBarline) - { - this.rightBarline = rightBarline; + for (AbstractChordInter chord : getRestChords()) { + chord.resetTiming(); + } } //----------// - // setStack // + // setDummy // //----------// /** - * @param stack the stack to set + * Flag this measure as dummy. */ - public void setStack (MeasureStack stack) + public void setDummy () { - this.stack = stack; + dummy = true; } //------------// // sortVoices // //------------// + /** + * Sort measure voices. + */ public void sortVoices () { Collections.sort(voices, Voices.byOrdinate); @@ -1843,7 +1916,23 @@ public Voice swapVoiceId (Voice voice, @Override public String toString () { - return "{Measure#" + stack.getPageId() + "P" + part.getId() + "}"; + StringBuilder sb = new StringBuilder("Measure{"); + + if (stack != null) { + sb.append('#').append(stack.getPageId()); + } else { + sb.append("-NOSTACK-"); + } + + if (part != null) { + sb.append("P").append(part.getId()); + } else { + sb.append("-NOPART-"); + } + + sb.append('}'); + + return sb.toString(); } //----------------// @@ -1873,7 +1962,7 @@ private void afterUnmarshal (Unmarshaller um, private Set filterByStaff (Set inters, Staff staff) { - Set found = new LinkedHashSet(); + Set found = new LinkedHashSet<>(); for (Inter inter : inters) { if (inter.getStaff() == staff) { @@ -1954,7 +2043,7 @@ private Voice getVoiceById (int id) private Set needHeadChords () { if (headChords == null) { - headChords = new LinkedHashSet(); + headChords = new LinkedHashSet<>(); } return headChords; @@ -1966,13 +2055,40 @@ private Set needHeadChords () private Set needRestChords () { if (restChords == null) { - restChords = new LinkedHashSet(); + restChords = new LinkedHashSet<>(); } return restChords; } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------------// + // setCueVoices // + //--------------// + /** + * Voice of every (standard) head chord is extended to its related preceding cue + * chord(s) if any. + */ + public void setCueVoices () + { + if (headChords == null) { + return; + } + + for (HeadChordInter ch : headChords) { + if (!(ch instanceof SmallChordInter)) { + SmallChordInter small = ch.getGraceChord(); + + if (small != null) { + final Voice voice = ch.getVoice(); + + if (voice != null) { + small.setVoice(voice); + } + } + } + } + } + //----------// // KeyEntry // //----------// @@ -1982,15 +2098,13 @@ private Set needRestChords () private static class KeyEntry implements Comparable { - //~ Instance fields ------------------------------------------------------------------------ private final int staffIndexInPart; // Staff index in part private final KeyInter key; // The key - //~ Constructors --------------------------------------------------------------------------- - public KeyEntry (Integer staffIndex, - KeyInter key) + KeyEntry (Integer staffIndex, + KeyInter key) { this.staffIndexInPart = staffIndex; this.key = key; @@ -2003,12 +2117,34 @@ private KeyEntry () key = null; } - //~ Methods -------------------------------------------------------------------------------- @Override public int compareTo (KeyEntry that) { return Integer.compare(staffIndexInPart, that.staffIndexInPart); } + + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + + if (obj instanceof KeyEntry) { + return compareTo((KeyEntry) obj) == 0; + } + + return false; + } + + @Override + public int hashCode () + { + int hash = 7; + hash = (37 * hash) + this.staffIndexInPart; + + return hash; + } } } // diff --git a/src/main/org/audiveris/omr/sheet/rhythm/MeasureFiller.java b/src/main/org/audiveris/omr/sheet/rhythm/MeasureFiller.java index b5b331621..f306cfe96 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/MeasureFiller.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/MeasureFiller.java @@ -40,18 +40,17 @@ */ public class MeasureFiller { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(MeasureFiller.class); /** Filling classes. (Clefs and Key signatures) */ - public static final Class[] FILLING_CLASSES = new Class[]{ClefInter.class, KeyInter.class}; + private static final Class[] FILLING_CLASSES = new Class[]{ + ClefInter.class, + KeyInter.class}; - //~ Instance fields ---------------------------------------------------------------------------- /** Containing system. */ private final SystemInfo system; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code MeasureFiller} object. * @@ -62,7 +61,9 @@ public MeasureFiller (SystemInfo system) this.system = system; } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Fill all system measures with Clef and Key entities. + */ public void process () { // Lookup the relevant inters from system SIG diff --git a/src/main/org/audiveris/omr/sheet/rhythm/MeasureStack.java b/src/main/org/audiveris/omr/sheet/rhythm/MeasureStack.java index 526fb7593..d093f19fe 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/MeasureStack.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/MeasureStack.java @@ -95,14 +95,12 @@ *
                                                                          * * @see Measure - * * @author Hervé Bitteur */ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "stack") public class MeasureStack { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(MeasureStack.class); @@ -112,22 +110,6 @@ public class MeasureStack /** String suffix for a cautionary id: {@value}. */ public static final String CAUTIONARY_SUFFIX = "C"; - //~ Enumerations ------------------------------------------------------------------------------- - /** - * All special kinds of measures. - */ - public enum Special - { - //~ Enumeration constant initializers ------------------------------------------------------ - - PICKUP, - FIRST_HALF, - SECOND_HALF, - CAUTIONARY; - } - - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -145,7 +127,7 @@ public enum Special /** Sequence of time slots within the measure, from left to right. */ @XmlElementRef - private final List slots = new ArrayList(); + private final List slots = new ArrayList<>(); /** Indication for special measure stack. */ @XmlAttribute @@ -190,12 +172,11 @@ public enum Special private SystemInfo system; /** Vertical sequence of (Part) measures, from top to bottom. */ - private final List measures = new ArrayList(); + private final List measures = new ArrayList<>(); /** Unassigned tuplets within stack. */ - private final Set stackTuplets = new LinkedHashSet(); + private final Set stackTuplets = new LinkedHashSet<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code MeasureStack} object. * @@ -214,10 +195,14 @@ private MeasureStack () this.system = null; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // addInter // //----------// + /** + * Add an inter to stack. + * + * @param inter the inter to add + */ public void addInter (Inter inter) { final Part part = inter.getPart(); @@ -227,7 +212,7 @@ public void addInter (Inter inter) measure.addInter(inter); if (inter instanceof TupletInter) { - stackTuplets.remove((TupletInter) inter); + stackTuplets.remove(inter); } } else if (inter instanceof TupletInter) { stackTuplets.add((TupletInter) inter); @@ -239,6 +224,11 @@ public void addInter (Inter inter) //------------// // addMeasure // //------------// + /** + * Append a measure in stack. + * + * @param measure the measure to append + */ public void addMeasure (Measure measure) { if (measures.isEmpty()) { @@ -274,6 +264,11 @@ public void addRepeat (HorizontalSide side) //------------------// // addTimeSignature // //------------------// + /** + * Add a time signature. + * + * @param ts the time signature to add + */ public void addTimeSignature (AbstractTimeInter ts) { // Populate (part) measure with provided time signature @@ -291,6 +286,11 @@ public void addTimeSignature (AbstractTimeInter ts) //-------------// // afterReload // //-------------// + /** + * To be called right after unmarshalling. + * + * @param system the containing system + */ public void afterReload (SystemInfo system) { try { @@ -331,22 +331,6 @@ public void checkDuration () } } - //------------// - // clearFrats // - //------------// - /** - * Get rid of all FRAT inters (both good and poor) in this stack, before a new - * configuration is installed. - */ - public void clearFrats () - { - for (Measure measure : measures) { - measure.clearFrats(); - } - - stackTuplets.clear(); - } - //----------------// // computeRepeats // //----------------// @@ -445,6 +429,12 @@ public void connectTiedVoices () //----------// // contains // //----------// + /** + * Tell whether the stack contains the provided point. + * + * @param point the provided point + * @return true if so + */ public boolean contains (Point2D point) { return system.getStackAt(point) == this; @@ -462,7 +452,7 @@ public boolean contains (Point2D point) */ public List filter (Collection systemInters) { - List kept = new ArrayList(); + List kept = new ArrayList<>(); for (Inter inter : systemInters) { Point center = inter.getCenter(); @@ -485,8 +475,9 @@ public List filter (Collection systemInters) } // Precise abscissa limits - if ((measure.getAbscissa(LEFT, staff) <= center.x) - && (center.x <= measure.getAbscissa(RIGHT, staff))) { + if ((measure.getAbscissa(LEFT, staff) <= center.x) && (center.x <= measure.getAbscissa( + RIGHT, + staff))) { kept.add(inter); } } @@ -508,6 +499,19 @@ public Rational getActualDuration () return actualDuration; } + //-------------------// + // setActualDuration // + //-------------------// + /** + * Register in this measure stack its actual duration. + * + * @param actualDuration the duration value + */ + public void setActualDuration (Rational actualDuration) + { + this.actualDuration = actualDuration; + } + //-----------------// // getClosestChord // //-----------------// @@ -573,7 +577,8 @@ public Slot getClosestSlot (Point2D point) * actually starts this stack in whatever staff, or whether a time signature was * found in a previous stack, even in preceding pages. *

                                                                          - * NOTAThis method looks up for time sig in preceding pages as well

                                                                          + * NOTAThis method looks up for time sig in preceding pages as well + *

                                                                          * * @return the current time signature, or null if not found at all */ @@ -646,6 +651,20 @@ public Rational getExcess () return excess; } + //-----------// + // setExcess // + //-----------// + /** + * Assign an excess duration for this stack. + * + * @param excess the duration in excess + */ + public void setExcess (Rational excess) + { + this.excess = excess; + setAbnormal(true); + } + //---------------------// // getExpectedDuration // //---------------------// @@ -681,9 +700,27 @@ public Rational getExpectedDuration () // } } + //---------------------// + // setExpectedDuration // + //---------------------// + /** + * Set measure expected duration. + * + * @param expectedDuration the expected duration + */ + public void setExpectedDuration (Rational expectedDuration) + { + this.expectedDuration = expectedDuration; + } + //-----------------// // getFirstMeasure // //-----------------// + /** + * Report the top measure in stack. + * + * @return top measure + */ public Measure getFirstMeasure () { if (measures.isEmpty()) { @@ -723,9 +760,14 @@ public MeasureStack getFollowingInPage () //---------------// // getHeadChords // //---------------// + /** + * Report all head chords in stack. + * + * @return stack head chords + */ public Set getHeadChords () { - Set headChords = new LinkedHashSet(); + Set headChords = new LinkedHashSet<>(); for (Measure measure : measures) { headChords.addAll(measure.getHeadChords()); @@ -748,6 +790,19 @@ public int getIdValue () return id; } + //------------// + // setIdValue // + //------------// + /** + * Assign the proper page-based id value to this measure stack. + * + * @param id the proper page-based measure stack id value + */ + public void setIdValue (int id) + { + this.id = id; + } + //-------------// // getLastSlot // //-------------// @@ -819,6 +874,11 @@ public Measure getMeasureAt (Part part) //-------------// // getMeasures // //-------------// + /** + * Report the vertical sequence of measures in this stack. + * + * @return measures in stack + */ public List getMeasures () { return measures; @@ -856,7 +916,7 @@ public String getPageId () { if (id != null) { return ((special == Special.SECOND_HALF) ? SECOND_HALF_PREFIX : "") + id - + (isCautionary() ? CAUTIONARY_SUFFIX : ""); + + (isCautionary() ? CAUTIONARY_SUFFIX : ""); } // No id defined yet @@ -943,8 +1003,8 @@ public String getScoreId (Score score) final Page page = system.getPage(); final int pageMeasureIdOffset = score.getMeasureIdOffset(page); - return ((special == Special.SECOND_HALF) ? SECOND_HALF_PREFIX : "") - + (pageMeasureIdOffset + id); + return ((special == Special.SECOND_HALF) ? SECOND_HALF_PREFIX : "") + (pageMeasureIdOffset + + id); } //----------// @@ -1039,9 +1099,14 @@ public AbstractChordInter getStandardChordBelow (Point2D point, //-------------------// // getStandardChords // //-------------------// + /** + * Report all the standard (i.e."non-small") chords in the stack. + * + * @return all standard chords in stack + */ public Set getStandardChords () { - Set stdChords = new LinkedHashSet(); + Set stdChords = new LinkedHashSet<>(); for (Measure measure : measures) { stdChords.addAll(measure.getStandardChords()); @@ -1065,7 +1130,7 @@ public Set getStandardChordsAbove (Point2D point, Rectangle xRange) { Staff desiredStaff = getSystem().getStaffAtOrAbove(point); - Set found = new LinkedHashSet(); + Set found = new LinkedHashSet<>(); Measure measure = getMeasureAt(desiredStaff); if (measure != null) { @@ -1100,7 +1165,7 @@ public Set getStandardChordsBelow (Point2D point, Rectangle xRange) { Staff desiredStaff = getSystem().getStaffAtOrBelow(point); - Set found = new LinkedHashSet(); + Set found = new LinkedHashSet<>(); Measure measure = getMeasureAt(desiredStaff); if (measure != null) { @@ -1166,7 +1231,7 @@ public AbstractTimeInter getTimeSignature () */ public Set getTuplets () { - Set all = new LinkedHashSet(); + Set all = new LinkedHashSet<>(); for (Measure measure : measures) { all.addAll(measure.getTuplets()); @@ -1181,9 +1246,14 @@ public Set getTuplets () //-----------// // getVoices // //-----------// + /** + * Report the sequence of voices in stack + * + * @return the stack voices + */ public List getVoices () { - List stackVoices = new ArrayList(); + List stackVoices = new ArrayList<>(); for (Measure measure : measures) { stackVoices.addAll(measure.getVoices()); @@ -1195,9 +1265,14 @@ public List getVoices () //--------------------// // getWholeRestChords // //--------------------// + /** + * Report all whole rest-chords in stack. + * + * @return all whole rest chords in stack + */ public Set getWholeRestChords () { - final Set set = new LinkedHashSet(); + final Set set = new LinkedHashSet<>(); for (Measure measure : measures) { for (RestChordInter chord : measure.getRestChords()) { @@ -1296,6 +1371,19 @@ public boolean isAbnormal () return abnormal; } + //-------------// + // setAbnormal // + //-------------// + /** + * Mark this stack as being abnormal or not. + * + * @param abnormal new value + */ + public void setAbnormal (boolean abnormal) + { + this.abnormal = abnormal; + } + //--------------// // isCautionary // //--------------// @@ -1338,6 +1426,12 @@ public boolean isImplicit () //----------// // isRepeat // //----------// + /** + * Tell whether the stack has a repeat sign on provided side. + * + * @param side horizontal side + * @return true if so + */ public boolean isRepeat (HorizontalSide side) { return (repeats != null) && repeats.contains(side); @@ -1432,6 +1526,11 @@ public void printVoices (String title) //-------------// // removeInter // //-------------// + /** + * Remove an inter from the stack. + * + * @param inter the inter to remove + */ public void removeInter (Inter inter) { if (inter.isVip()) { @@ -1511,6 +1610,9 @@ public void render (Graphics2D g, //-------------// // resetRhythm // //-------------// + /** + * Reset rhythm info in this stack. + */ public void resetRhythm () { setAbnormal(false); @@ -1524,86 +1626,34 @@ public void resetRhythm () } } - //-------------// - // setAbnormal // - //-------------// - /** - * Mark this stack as being abnormal or not. - * - * @param abnormal new value - */ - public void setAbnormal (boolean abnormal) - { - this.abnormal = abnormal; - } - - //-------------------// - // setActualDuration // - //-------------------// + //---------------// + // setCautionary // + //---------------// /** - * Register in this measure stack its actual duration. - * - * @param actualDuration the duration value + * Flag stack as cautionary. */ - public void setActualDuration (Rational actualDuration) - { - this.actualDuration = actualDuration; - } - - //-----------// - // setPickup // - //-----------// public void setCautionary () { special = Special.CAUTIONARY; } - //-----------// - // setExcess // - //-----------// - /** - * Assign an excess duration for this stack. - * - * @param excess the duration in excess - */ - public void setExcess (Rational excess) - { - this.excess = excess; - setAbnormal(true); - } - - //---------------------// - // setExpectedDuration // - //---------------------// - public void setExpectedDuration (Rational expectedDuration) - { - this.expectedDuration = expectedDuration; - } - //--------------// // setFirstHalf // //--------------// - public void setFirstHalf () - { - special = Special.FIRST_HALF; - } - - //------------// - // setIdValue // - //------------// /** - * Assign the proper page-based id value to this measure stack. - * - * @param id the proper page-based measure stack id value + * Flag stack as first half. */ - public void setIdValue (int id) + public void setFirstHalf () { - this.id = id; + special = Special.FIRST_HALF; } //-----------// // setPickup // //-----------// + /** + * Flag stack as pickup. + */ public void setPickup () { special = Special.PICKUP; @@ -1612,19 +1662,14 @@ public void setPickup () //---------------// // setSecondHalf // //---------------// + /** + * Flag stack as second half. + */ public void setSecondHalf () { special = Special.SECOND_HALF; } - //------------// - // setSpecial // - //------------// - public void setSpecial (Special special) - { - this.special = special; - } - //---------// // shorten // //---------// @@ -1702,7 +1747,7 @@ public MeasureStack splitAtBarline (List systemBarline) for (int partIndex = 0; partIndex < systemParts.size(); partIndex++) { final Part part = systemParts.get(partIndex); final PartBarline partBarline = systemBarline.get(partIndex); - Map xRefs = new HashMap(); + Map xRefs = new HashMap<>(); for (Staff staff : part.getStaves()) { final int xRef = partBarline.getRightX(part, staff); @@ -1750,4 +1795,16 @@ public String toString () return sb.toString(); } + + /** + * All special kinds of measures. + */ + public enum Special + { + + PICKUP, + FIRST_HALF, + SECOND_HALF, + CAUTIONARY + } } diff --git a/src/main/org/audiveris/omr/sheet/rhythm/MeasuresBuilder.java b/src/main/org/audiveris/omr/sheet/rhythm/MeasuresBuilder.java index 5fe45c0a8..cfc343af0 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/MeasuresBuilder.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/MeasuresBuilder.java @@ -54,6 +54,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.TreeMap; /** @@ -65,22 +66,18 @@ @NotThreadSafe public class MeasuresBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - MeasuresBuilder.class); + private static final Logger logger = LoggerFactory.getLogger(MeasuresBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated system. */ @Navigable(false) private final SystemInfo system; /** Sequence of groups of barlines per staff. */ - private final Map> staffMap = new TreeMap>(Staff.byId); + private final Map> staffMap = new TreeMap<>(Staff.byId); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code MeasuresBuilder} object. * @@ -91,7 +88,6 @@ public MeasuresBuilder (SystemInfo system) this.system = system; } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // buildMeasures // //---------------// @@ -101,7 +97,8 @@ public MeasuresBuilder (SystemInfo system) * Parts and physical BarlineInter's have been identified within the system. * Each staff has its BarlineInter's attached. *

                                                                          - * To build the logical StaffBarlineInter's, PartBarline's and Measures, the strategy is:

                                                                            + * To build the logical StaffBarlineInter's, PartBarline's and Measures, the strategy is: + *
                                                                              *
                                                                            1. Staff by staff, gather barlines into groups of closely located barlines * (StaffBarlineInter's).
                                                                            2. *
                                                                            3. Check and adjust consistency across all staves within the system
                                                                            4. @@ -109,7 +106,8 @@ public MeasuresBuilder (SystemInfo system) * allocate the corresponding PartBarline's and measures. *
                                                                            *

                                                                            - * Strategy for assigning barlines to measures:

                                                                              + * Strategy for assigning barlines to measures: + *
                                                                                *
                                                                              • A group of 2 physical barlines, whatever their thickness, gives a single logical * barline.
                                                                              • *
                                                                              • A group of 3 or 4 physical barlines (thin | thick | thin) or (thin | thick | thick | @@ -150,7 +148,7 @@ public void buildMeasures () private List buildGroups (List barlines) { final SIGraph sig = system.getSig(); - final List groups = new ArrayList(); + final List groups = new ArrayList<>(); for (int i = 0; i < barlines.size(); i++) { BarlineInter bLast = barlines.get(i); @@ -166,7 +164,7 @@ private List buildGroups (List barlines) } int ibLast = barlines.indexOf(bLast); - groups.add(new Group(barlines.subList(i, ibLast + 1))); + groups.add(new Group(barlines.subList(i, ibLast + 1), system)); i = ibLast; } @@ -193,9 +191,8 @@ private void buildPartMeasures (Part part) for (int ig = 0; ig <= igMax; ig++) { Group topGroup = (ig < topGroups.size()) ? topGroups.get(ig) : null; - Measure measure = ((topGroup != null) - && topGroup.get(0).isStaffEnd(HorizontalSide.LEFT)) ? null - : new Measure(part); + Measure measure = ((topGroup != null) && topGroup.get(0).isStaffEnd( + HorizontalSide.LEFT)) ? null : new Measure(part); if (measure != null) { part.addMeasure(measure); @@ -314,7 +311,7 @@ private void enforceSystemConsistency () final int maxShift = system.getSheet().getScale().toPixels(constants.maxShift); // Build list of columns, kept sorted on abscissa - final List columns = new ArrayList(); + final List columns = new ArrayList<>(); for (Staff staff : system.getStaves()) { List groups = staffMap.get(staff); @@ -378,7 +375,6 @@ private void enforceSystemConsistency () } } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Column // //--------// @@ -389,15 +385,13 @@ private void enforceSystemConsistency () private class Column implements Comparable { - //~ Instance fields ------------------------------------------------------------------------ /** In theory, we should have exactly one group per staff. */ - final Map groups = new TreeMap(Staff.byId); + final Map groups = new TreeMap<>(Staff.byId); /** De-skewed column mean abscissa. */ Double xDsk; - //~ Methods -------------------------------------------------------------------------------- /** * Populate the cell for provided staff by the provided group. * @@ -436,6 +430,29 @@ public int compareTo (Column that) return Double.compare(this.getDeskewedAbscissa(), that.getDeskewedAbscissa()); } + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + + if (obj instanceof Column) { + return compareTo((Column) obj) == 0; + } + + return false; + } + + @Override + public int hashCode () + { + int hash = 7; + hash = (23 * hash) + Objects.hashCode(this.getDeskewedAbscissa()); + + return hash; + } + @Override public String toString () { @@ -509,8 +526,8 @@ private void expand () Group group = groups.get(staff); if (group == null) { - double xStaffMiddle = (staff.getAbscissa(LEFT) - + staff.getAbscissa(RIGHT)) / 2.0; + double xStaffMiddle = (staff.getAbscissa(LEFT) + staff.getAbscissa(RIGHT)) + / 2.0; double yStaffMiddle = staff.getFirstLine().yAt(xStaffMiddle); double x = line.xAtY(yStaffMiddle); // Roughly double y1 = staff.getFirstLine().yAt(x); @@ -522,7 +539,7 @@ private void expand () barline.freeze(); List staffGroups = staffMap.get(staff); - staffGroups.add(new Group(Collections.singletonList(barline))); + staffGroups.add(new Group(Collections.singletonList(barline), system)); Collections.sort(staffGroups); staff.addBarline(barline); } @@ -538,9 +555,7 @@ private double getDeskewedAbscissa () x += group.getDeskewedAbscissa(); } - if (!groups.isEmpty()) { - xDsk = x / groups.size(); - } + xDsk = x / groups.size(); } return xDsk; @@ -550,10 +565,9 @@ private double getDeskewedAbscissa () //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxShift = new Scale.Fraction( 1.0, @@ -570,11 +584,10 @@ private static final class Constants /** * A group of barlines in a staff. */ - private class Group + private static class Group extends ArrayList implements Comparable { - //~ Instance fields ------------------------------------------------------------------------ /** (Skewed) group center. */ final Point2D center; @@ -582,8 +595,8 @@ private class Group /** De-skewed group center. */ final Point2D dsk; - //~ Constructors --------------------------------------------------------------------------- - public Group (List barlines) + Group (List barlines, + SystemInfo system) { addAll(barlines); @@ -591,7 +604,6 @@ public Group (List barlines) dsk = system.getSkew().deskewed(center); } - //~ Methods -------------------------------------------------------------------------------- @Override public int compareTo (Group that) { @@ -614,6 +626,12 @@ public String toString () return midString() + Inters.ids(this); } + @Override + public Object clone () + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + private Point2D computeCenter () { final int n = size(); diff --git a/src/main/org/audiveris/omr/sheet/rhythm/MeasuresStep.java b/src/main/org/audiveris/omr/sheet/rhythm/MeasuresStep.java index 6c92438e0..46afd0fc7 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/MeasuresStep.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/MeasuresStep.java @@ -54,7 +54,6 @@ public class MeasuresStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(MeasuresStep.class); @@ -62,11 +61,10 @@ public class MeasuresStep private static final Set impactingClasses; static { - impactingClasses = new HashSet(); + impactingClasses = new HashSet<>(); impactingClasses.add(StaffBarlineInter.class); } - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code MeasuresStep} object. */ @@ -74,7 +72,6 @@ public MeasuresStep () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // doSystem // //----------// @@ -92,7 +89,8 @@ public void doSystem (SystemInfo system, /** * {@inheritDoc} *

                                                                                - * For MEASURES step, in seq argument, we can have either:

                                                                                  + * For MEASURES step, in seq argument, we can have either: + *
                                                                                    *
                                                                                  • BarlineInter instances *
                                                                                  • StaffBarlineInter instances *
                                                                                  @@ -117,8 +115,9 @@ public void impact (UITaskList seq, MeasureStack stack = system.getStackAt(centerLeft); final boolean isAddition = isAddition(seq); - if ((!isAddition && (opKind != UITask.OpKind.UNDO)) - || (isAddition && (opKind == UITask.OpKind.UNDO))) { + if ((!isAddition && (opKind != UITask.OpKind.UNDO)) || (isAddition + && (opKind + == UITask.OpKind.UNDO))) { // Remove barlines MeasureStack rightStack = stack.getNextSibling(); @@ -181,7 +180,7 @@ protected void doEpilog (Sheet sheet, */ private List buildFromStaffBarlines (List staffBarlines) { - final List systemBarline = new ArrayList(); + final List systemBarline = new ArrayList<>(); Part lastPart = null; PartBarline partBarline = null; diff --git a/src/main/org/audiveris/omr/sheet/rhythm/PageRhythm.java b/src/main/org/audiveris/omr/sheet/rhythm/PageRhythm.java index 82fd9d1e8..1a04398e7 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/PageRhythm.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/PageRhythm.java @@ -78,24 +78,22 @@ */ public class PageRhythm { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(PageRhythm.class); /** Adjustable rhythm classes. (FRAT: Flag, RestChord, AugmentationDot, Tuplet) */ - public static final Class[] FRAT_CLASSES = new Class[]{ - FlagInter.class, RestChordInter.class, - AugmentationDotInter.class, TupletInter.class - }; + private static final Class[] FRAT_CLASSES = new Class[]{ + FlagInter.class, + RestChordInter.class, + AugmentationDotInter.class, + TupletInter.class}; - //~ Instance fields ---------------------------------------------------------------------------- - /** The page being processed.. */ + /** The page being processed. */ private final Page page; /** Sequence of time-sig ranges found in page. */ - private final List ranges = new ArrayList(); + private final List ranges = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PageRhythm} object. * @@ -106,7 +104,6 @@ public PageRhythm (Page page) this.page = page; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// @@ -194,23 +191,23 @@ private void populateTimeSignatures () } if (found) { - ranges.add(new Range(stack.getIdValue(), stack.getTimeSignature())); + ranges.add(new Range(seqNumOf(stack), stack.getTimeSignature())); } } } } // If there was no time sig at beginning of page - if (ranges.isEmpty() || (ranges.get(0).startId > 1)) { + if (ranges.isEmpty() || (ranges.get(0).startSN > 1)) { ranges.add(0, new Range(1, null)); } - // Assign the stopId values + // Assign the stopSN values for (int i = 0; i < (ranges.size() - 1); i++) { - ranges.get(i).stopId = ranges.get(i + 1).startId - 1; + ranges.get(i).stopSN = ranges.get(i + 1).startSN - 1; } - ranges.get(ranges.size() - 1).stopId = page.getLastSystem().getLastStack().getIdValue(); + ranges.get(ranges.size() - 1).stopSN = seqNumOf(page.getLastSystem().getLastStack()); } //---------------// @@ -226,31 +223,32 @@ private void processRanges () for (SystemInfo system : page.getSystems()) { for (MeasureStack stack : system.getStacks()) { + final int sn = seqNumOf(stack); + // Start of range? - if (stack.getIdValue() == range.startId) { + if (sn == range.startSN) { logger.debug("Starting {}", range); - - // Adjust time signature? (TODO: today we don't adjust anything in fact) - if ((range.duration != null) - && ((range.ts == null) - || !range.ts.getTimeRational().getValue().equals(range.duration))) { - logger.info( - "{}{} should update to {}-based time sig?", - stack.getSystem().getLogPrefix(), - range, - range.duration); - } + // + // // Adjust time signature? (TODO: today we don't adjust anything in fact) + // if ((range.duration != null) && ((range.ts == null) || !range.ts + // .getTimeRational().getValue().equals(range.duration))) { + // logger.info( + // "{}{} should update to {}-based time sig?", + // stack.getSystem().getLogPrefix(), + // range, + // range.duration); + // } } try { - logger.debug("\n--- Processing {} expDur: {} ---", stack, range.duration); + logger.debug("\n--- Processing {} {} expDur:{}", sn, stack, range.duration); new StackTuner(stack, false).process(range.duration); } catch (Exception ex) { logger.warn("Error on stack " + stack + " " + ex, ex); } // End of range? - if (stack.getIdValue() == range.stopId) { + if (sn == range.stopSN) { if (it.hasNext()) { range = it.next(); } @@ -274,29 +272,24 @@ private void processRanges () */ private Rational retrieveExpectedDuration (Range range) { - Histogram histo = new Histogram(); - int stackNb = 0; - int voiceNb = 0; + Histogram histo = new Histogram<>(); SystemLoop: for (SystemInfo system : page.getSystems()) { for (MeasureStack stack : system.getStacks()) { - int stackId = stack.getIdValue(); + final int sn = seqNumOf(stack); - if (stackId < range.startId) { + if (sn < range.startSN) { continue; - } else if (stackId > range.stopId) { + } else if (sn > range.stopSN) { break SystemLoop; } - stackNb++; - for (Voice voice : stack.getVoices()) { Rational dur = voice.getDuration(); if (dur != null) { histo.increaseCount(dur, 1); - voiceNb++; } } } @@ -355,18 +348,21 @@ private void retrieveRangeDurations () for (SystemInfo system : page.getSystems()) { for (MeasureStack stack : system.getStacks()) { + final int sn = seqNumOf(stack); + try { - logger.debug("\n--- Raw processing {} ---", stack); + logger.debug("\n--- Raw processing {} {} ---", sn, stack); new StackTuner(stack, true).process(null); } catch (Exception ex) { logger.warn("Error on stack " + stack + " " + ex, ex); } // End of range? - if (stack.getIdValue() == range.stopId) { + if (sn == range.stopSN) { // If range is governed by a manual time signature, use it! if ((range.ts != null) && range.ts.isManual()) { range.duration = range.ts.getTimeRational().getValue(); + logger.debug("{} manual:{}", range, range.duration); } else { // Use CURRENT MATERIAL of voices to determine expected duration on this range Rational guess = retrieveExpectedDuration(range); @@ -377,7 +373,7 @@ private void retrieveRangeDurations () range.duration = range.ts.getTimeRational().getValue(); } - logger.info("{} guess:{}", range, guess); + logger.debug("{} guess:{}", range, guess); } if (it.hasNext()) { @@ -388,41 +384,72 @@ private void retrieveRangeDurations () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------// + // seqNumOf // + //----------// + /** + * Report the 1-based sequence number of the provided stack in sheet. + * + * @param stack the provided stack + * @return the stack sequence number + */ + private int seqNumOf (MeasureStack stack) + { + final SystemInfo stackSystem = stack.getSystem(); + int sn = 1; + + for (SystemInfo system : page.getSystems()) { + if (system != stackSystem) { + sn += system.getStacks().size(); + } else { + sn += system.getStacks().indexOf(stack); + break; + } + } + + return sn; + } + //-------// // Range // //-------// /** - * Describes a range of stack governed by a time signature. + * This private class describes a range of stacks governed by a time signature. + *

                                                                                  * The very first range in sheet may have no time signature. + *

                                                                                  + * NOTA: We use a local 1-based sequence number to identify all stacks in page, + * because the stack id value can be modified with measure renumbering, + * it can be zero (pickup measure) and may not be unique (second half or cautionary measure). */ private static class Range { - //~ Instance fields ------------------------------------------------------------------------ - final int startId; // Id of first stack + final int startSN; // 1-based sequence number of first stack in page - int stopId; // Id of last stack + int stopSN; // 1-based sequence number of last stack in page AbstractTimeInter ts; // Time signature found in first stack of range, if any Rational duration; // Inferred measure duration for the range - //~ Constructors --------------------------------------------------------------------------- - public Range (int startId, - AbstractTimeInter ts) + Range (int startSN, + AbstractTimeInter ts) { - this.startId = startId; + this.startSN = startSN; this.ts = ts; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { StringBuilder sb = new StringBuilder(getClass().getSimpleName()); sb.append("{"); - sb.append(startId).append('-').append(stopId); + sb.append("SN").append(startSN).append("-"); + + if (stopSN != 0) { + sb.append(stopSN); + } if (ts != null) { sb.append(" ts:").append(ts.getTimeRational()); diff --git a/src/main/org/audiveris/omr/sheet/rhythm/Profile.java b/src/main/org/audiveris/omr/sheet/rhythm/Profile.java deleted file mode 100644 index 9d473c138..000000000 --- a/src/main/org/audiveris/omr/sheet/rhythm/Profile.java +++ /dev/null @@ -1,245 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// P r o f i l e // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet.rhythm; - -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.inter.Inters; - -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.SortedMap; -import java.util.TreeMap; - -/** - * Class {@code Profile} - * - * @author Hervé Bitteur - */ -public class Profile -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** The sorted map of rhythm items. (typically within a stack) */ - private final SortedMap map = new TreeMap(Inters.byFullAbscissa); - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // equals // - //--------// - @Override - public boolean equals (Object obj) - { - if (obj == this) { - return true; - } - - if (!(obj instanceof Profile)) { - return false; - } - - Profile that = (Profile) obj; - - if (this.map.size() != that.map.size()) { - return false; - } - - Iterator> thisIt = this.map.entrySet().iterator(); - Iterator> thatIt = that.map.entrySet().iterator(); - - while (thisIt.hasNext()) { - Entry thisEntry = thisIt.next(); - Entry thatEntry = thatIt.next(); - Inter thisKey = thisEntry.getKey(); - Inter thatKey = thatEntry.getKey(); - - if (thisKey != thatKey) { - return false; - } - - Attrs thisAttrs = thisEntry.getValue(); - Attrs thatAttrs = thatEntry.getValue(); - - if (thisAttrs == null) { - if (thatAttrs != null) { - return false; - } - } else if (!thisAttrs.equals(thatAttrs)) { - return false; - } - } - - return true; - } - - @Override - public int hashCode () - { - int hash = 7; - hash = (83 * hash) + Objects.hashCode(this.map); - - return hash; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-------// - // Attrs // - //-------// - /** - * Define the attributes assigned to a profile item. - */ - public static class Attrs - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public boolean equals (Object obj) - { - if (this == obj) { - return true; - } - - if (!(obj instanceof Attrs)) { - return false; - } - - return true; - } - - @Override - public int hashCode () - { - int hash = 5; - - return hash; - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("["); - sb.append(internals()); - sb.append(']'); - - return sb.toString(); - } - - protected String internals () - { - return ""; - } - } - - //----------// - // DotAttrs // - //----------// - public static class DotAttrs - extends Attrs - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Number of dots for the item. */ - int dotCount; - - //~ Methods -------------------------------------------------------------------------------- - @Override - public boolean equals (Object obj) - { - if (this == obj) { - return true; - } - - if (!(obj instanceof DotAttrs)) { - return false; - } - - DotAttrs that = (DotAttrs) obj; - - return this.dotCount == that.dotCount; - } - - @Override - public int hashCode () - { - int hash = 3; - hash = (29 * hash) + this.dotCount; - - return hash; - } - - @Override - protected String internals () - { - StringBuilder sb = new StringBuilder(super.internals()); - sb.append(" d:").append(dotCount); - - return sb.toString(); - } - } - - //--------------// - // FlagDotAttrs // - //--------------// - public static class FlagDotAttrs - extends DotAttrs - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Number of resulting flags for the item. */ - int flagCount; - - //~ Methods -------------------------------------------------------------------------------- - @Override - public boolean equals (Object obj) - { - if (this == obj) { - return true; - } - - if (!(obj instanceof FlagDotAttrs)) { - return false; - } - - FlagDotAttrs that = (FlagDotAttrs) obj; - - return (this.flagCount == that.flagCount) && (this.dotCount == that.dotCount); - } - - @Override - public int hashCode () - { - int hash = 7; - hash = (89 * hash) + this.flagCount; - - return hash; - } - - @Override - protected String internals () - { - StringBuilder sb = new StringBuilder(super.internals()); - sb.append(" f:").append(flagCount); - - return sb.toString(); - } - } -} diff --git a/src/main/org/audiveris/omr/sheet/rhythm/RhythmsStep.java b/src/main/org/audiveris/omr/sheet/rhythm/RhythmsStep.java index 54a27671e..0b7c2e66d 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/RhythmsStep.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/RhythmsStep.java @@ -36,13 +36,18 @@ import org.audiveris.omr.sig.inter.RestChordInter; import org.audiveris.omr.sig.inter.RestInter; import org.audiveris.omr.sig.inter.SlurInter; +import org.audiveris.omr.sig.inter.SmallBeamInter; +import org.audiveris.omr.sig.inter.SmallChordInter; +import org.audiveris.omr.sig.inter.SmallFlagInter; import org.audiveris.omr.sig.inter.StaffBarlineInter; import org.audiveris.omr.sig.inter.StemInter; import org.audiveris.omr.sig.inter.TimeNumberInter; import org.audiveris.omr.sig.inter.TimePairInter; import org.audiveris.omr.sig.inter.TimeWholeInter; import org.audiveris.omr.sig.inter.TupletInter; +import org.audiveris.omr.sig.relation.AugmentationRelation; import org.audiveris.omr.sig.relation.BeamStemRelation; +import org.audiveris.omr.sig.relation.DoubleDotRelation; import org.audiveris.omr.sig.relation.HeadStemRelation; import org.audiveris.omr.sig.relation.Relation; import org.audiveris.omr.sig.ui.AdditionTask; @@ -73,15 +78,20 @@ public class RhythmsStep extends AbstractStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(RhythmsStep.class); /** Classes that impact just a measure stack. */ private static final Set forStack; + /** Classes that impact a whole page. */ + private static final Set forPage; + + /** All impacting classes. */ + private static final Set impactingClasses; + static { - forStack = new HashSet(); + forStack = new HashSet<>(); forStack.add(AugmentationDotInter.class); forStack.add(BarlineInter.class); forStack.add(BeamHookInter.class); @@ -91,38 +101,34 @@ public class RhythmsStep forStack.add(HeadInter.class); forStack.add(RestChordInter.class); forStack.add(RestInter.class); + forStack.add(SmallBeamInter.class); + forStack.add(SmallChordInter.class); + forStack.add(SmallFlagInter.class); forStack.add(StaffBarlineInter.class); forStack.add(StemInter.class); forStack.add(TupletInter.class); - forStack.add(MeasureStack.class); - // Relations + forStack.add(AugmentationRelation.class); forStack.add(BeamStemRelation.class); + forStack.add(DoubleDotRelation.class); forStack.add(HeadStemRelation.class); } - /** Classes that impact a whole page. */ - private static final Set forPage; - static { - forPage = new HashSet(); + forPage = new HashSet<>(); forPage.add(SlurInter.class); // Because of possibility of ties forPage.add(TimeNumberInter.class); forPage.add(TimePairInter.class); forPage.add(TimeWholeInter.class); } - /** All impacting classes. */ - private static final Set impactingClasses; - static { - impactingClasses = new HashSet(); + impactingClasses = new HashSet<>(); impactingClasses.addAll(forStack); impactingClasses.addAll(forPage); } - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code RhythmsStep} object. */ @@ -130,7 +136,6 @@ public RhythmsStep () { } - //~ Methods ------------------------------------------------------------------------------------ //------// // doit // //------// @@ -179,7 +184,7 @@ public void impact (UITaskList seq, if (inter instanceof BarlineInter || inter instanceof StaffBarlineInter) { if ((task instanceof RemovalTask && (opKind == OpKind.UNDO)) - || (task instanceof AdditionTask && (opKind != OpKind.UNDO))) { + || (task instanceof AdditionTask && (opKind != OpKind.UNDO))) { // Add next stack as well impact.onStacks.add(stack.getNextSibling()); } @@ -200,11 +205,9 @@ public void impact (UITaskList seq, Class classe = relation.getClass(); if (isImpactedBy(classe, forStack)) { - if (sig.containsEdge(relation)) { - Inter source = sig.getEdgeSource(relation); - MeasureStack stack = system.getStackAt(source.getCenter()); - impact.onStacks.add(stack); - } + Inter source = relationTask.getSource(); + MeasureStack stack = system.getStackAt(source.getCenter()); + impact.onStacks.add(stack); } } } @@ -228,19 +231,16 @@ public boolean isImpactedBy (Class classe) return isImpactedBy(classe, impactingClasses); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Impact // //--------// private static class Impact { - //~ Instance fields ------------------------------------------------------------------------ boolean onPage = false; - Set onStacks = new LinkedHashSet(); + Set onStacks = new LinkedHashSet<>(); - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { diff --git a/src/main/org/audiveris/omr/sheet/rhythm/Slot.java b/src/main/org/audiveris/omr/sheet/rhythm/Slot.java index 3a746cd74..bf5a66ce1 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/Slot.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/Slot.java @@ -47,6 +47,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; @@ -61,10 +62,9 @@ * Class {@code Slot} represents a roughly defined time slot within a measure stack, * to gather all chords that start at the same time. *

                                                                                  - * On the diagram shown, slots are indicated by vertical blue lines.

                                                                                  + * On the diagram shown, slots are indicated by vertical blue lines. *

                                                                                  - * The slot embraces all the staves of the system. - * + * NOTA: The slot embraces all the staves in the system. *

                                                                                  * diagram *
                                                                                  @@ -76,12 +76,9 @@ public class Slot implements Comparable { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - Slot.class); + private static final Logger logger = LoggerFactory.getLogger(Slot.class); - //~ Instance fields ---------------------------------------------------------------------------- // Persistent data //---------------- // @@ -113,7 +110,6 @@ public class Slot /** Chords incoming into this slot, sorted by ordinate. */ private List incomings; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Slot object. * @@ -127,7 +123,7 @@ public Slot (int id, { this.id = id; this.stack = stack; - this.incomings = new ArrayList(incomings); + this.incomings = new ArrayList<>(incomings); for (AbstractChordInter chord : incomings) { chord.setSlot(this); @@ -144,15 +140,17 @@ private Slot () this.id = 0; } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // afterReload // //-------------// + /** + * To be called right after unmarshalling. + */ public void afterReload () { try { // Populate incomings - incomings = new ArrayList(); + incomings = new ArrayList<>(); final SystemInfo system = stack.getSystem(); @@ -218,6 +216,20 @@ public int compareTo (Slot other) return Double.compare(xOffset, other.xOffset); } + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + + if (obj instanceof Slot) { + return compareTo((Slot) obj) == 0; + } + + return false; + } + //-------------------// // getChordJustAbove // //-------------------// @@ -307,7 +319,7 @@ public List getChords () public List getEmbracedChords (Point top, Point bottom) { - List embracedChords = new ArrayList(); + List embracedChords = new ArrayList<>(); for (AbstractChordInter chord : getChords()) { if (chord.isEmbracedBy(top, bottom)) { @@ -344,6 +356,20 @@ public MeasureStack getStack () return stack; } + //----------// + // setStack // + //----------// + /** + * Set the containing stack for this slot. + * + * @param stack containing stack + */ + public void setStack (MeasureStack stack) + { + this.stack = stack; + computeXOffset(); + } + //---------------// // getTimeOffset // //---------------// @@ -370,6 +396,15 @@ public int getXOffset () return xOffset; } + @Override + public int hashCode () + { + int hash = 7; + hash = (59 * hash) + this.xOffset; + + return hash; + } + //--------------// // isSuspicious // //--------------// @@ -381,15 +416,6 @@ public boolean isSuspicious () return suspicious; } - //----------// - // setStack // - //----------// - public void setStack (MeasureStack stack) - { - this.stack = stack; - computeXOffset(); - } - //---------------// // setSuspicious // //---------------// @@ -398,7 +424,7 @@ public void setStack (MeasureStack stack) */ public void setSuspicious (boolean suspicious) { - this.suspicious = suspicious ? true : null; + this.suspicious = suspicious; } //---------------// @@ -440,10 +466,7 @@ public boolean setTimeOffset (Rational timeOffset) voice.updateSlotTable(); } } else if (!this.timeOffset.equals(timeOffset)) { - logger.warn( - "Reassigning timeOffset from " + this.timeOffset + " to " + timeOffset + " in " - + this); - + logger.warn("TimeOffset set from {} to {} in {}", this.timeOffset, timeOffset, this); failed = true; } @@ -491,7 +514,7 @@ public String toChordString () @Override public String toString () { - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); sb.append("Slot{#").append(id); sb.append(" xOffset=").append(xOffset); @@ -500,13 +523,15 @@ public String toString () sb.append(" timeOffset=").append(timeOffset); } - sb.append(" incomings=["); + if (incomings != null) { + sb.append(" incomings=["); - for (AbstractChordInter chord : incomings) { - sb.append("#").append(chord.getId()); - } + for (AbstractChordInter chord : incomings) { + sb.append("#").append(chord.getId()); + } - sb.append("]"); + sb.append("]"); + } if (isSuspicious()) { sb.append(" SUSPICIOUS"); @@ -529,26 +554,25 @@ public String toVoiceString () { StringBuilder sb = new StringBuilder(); - sb.append("slot#").append(getId()).append(" start=") - .append(String.format("%5s", getTimeOffset())).append(" ["); + sb.append("slot#").append(getId()).append(" start=").append( + String.format("%5s", getTimeOffset())).append(" ["); - SortedMap voiceChords = new TreeMap(); + SortedMap voiceChords = new TreeMap<>(); for (AbstractChordInter chord : getChords()) { voiceChords.put(chord.getVoice().getId(), chord); } - final int voiceMax = stack.getVoices().size(); boolean started = false; - for (int iv = 1; iv <= voiceMax; iv++) { + for (Entry entry : voiceChords.entrySet()) { if (started) { sb.append(", "); } else { started = true; } - AbstractChordInter chord = voiceChords.get(iv); + final AbstractChordInter chord = entry.getValue(); if (chord != null) { sb.append("V").append(chord.getVoice().getId()); @@ -604,7 +628,7 @@ private void assignMeasureVoices (Measure measure, // Try to reuse an existing voice in same part for (Voice voice : measure.getVoices()) { - if (voice.isFree(this) && voice.getStartingStaff() == chord.getTopStaff()) { + if (voice.isFree(this) && (voice.getStartingStaff() == chord.getTopStaff())) { chord.setVoice(voice); break; @@ -641,7 +665,7 @@ private void buildMeasureVoices (Measure measure, return; } - List partRookies = new ArrayList(); + List partRookies = new ArrayList<>(); // Some chords already have their voice assigned for (AbstractChordInter ch : partIncomings) { @@ -703,14 +727,14 @@ private void buildMeasureVoices (Measure measure, */ private Map> buildPartMap (List stackChords) { - Map> map = new LinkedHashMap>(); + Map> map = new LinkedHashMap<>(); for (AbstractChordInter ch : stackChords) { Part part = ch.getPart(); List partChords = map.get(part); if (partChords == null) { - map.put(part, partChords = new ArrayList()); + map.put(part, partChords = new ArrayList<>()); } partChords.add(ch); @@ -740,8 +764,6 @@ private void computeXOffset () xOffset = (int) Math.rint(stack.getXOffset(ref)); } - //~ Inner Classes ------------------------------------------------------------------------------ - // //------------// // MyDistance // //------------// @@ -749,10 +771,9 @@ private void computeXOffset () * Implementation of a 'distance' between an old chord and a new chord to be * potentially linked in the same voice. */ - private static final class MyDistance + private static class MyDistance implements InjectionSolver.Distance { - //~ Static fields/initializers ------------------------------------------------------------- private static final int NOT_A_REST = 5; @@ -762,26 +783,23 @@ private static final class MyDistance private static final int STAFF_DIFF = 50; // 40; - private static final int INCOMPATIBLE_VOICES = 10000; // Forbidden + private static final int INCOMPATIBLE_VOICES = 10_000; // Forbidden - //~ Instance fields ------------------------------------------------------------------------ private final List news; private final List olds; private final Scale scale; - //~ Constructors --------------------------------------------------------------------------- - public MyDistance (List news, - List olds, - Scale scale) + MyDistance (List news, + List olds, + Scale scale) { this.news = news; this.olds = olds; this.scale = scale; } - //~ Methods -------------------------------------------------------------------------------- /** * Distance between newChord (index 'in') and oldChord (index 'ip'). * @@ -802,9 +820,8 @@ public int getDistance (int in, AbstractChordInter oldChord = olds.get(ip); // Different assigned voices? - if ((newChord.getVoice() != null) - && (oldChord.getVoice() != null) - && (newChord.getVoice() != oldChord.getVoice())) { + if ((newChord.getVoice() != null) && (oldChord.getVoice() != null) + && (newChord.getVoice() != oldChord.getVoice())) { return INCOMPATIBLE_VOICES; } @@ -832,7 +849,8 @@ public int getDistance (int in, } // Pitch difference - int dy = Math.abs(newChord.getHeadLocation().y - oldChord.getHeadLocation().y) / scale.getInterline(); + int dy = Math.abs(newChord.getHeadLocation().y - oldChord.getHeadLocation().y) / scale + .getInterline(); // Stem direction difference int dStem = Math.abs(newChord.getStemDir() - oldChord.getStemDir()); diff --git a/src/main/org/audiveris/omr/sheet/rhythm/SlotsBuilder.java b/src/main/org/audiveris/omr/sheet/rhythm/SlotsBuilder.java index c3f7cfec5..93c45aa57 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/SlotsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/SlotsBuilder.java @@ -39,6 +39,7 @@ import org.audiveris.omr.sig.inter.Inter; import org.audiveris.omr.sig.inter.Inters; import org.audiveris.omr.sig.inter.RestChordInter; +import org.audiveris.omr.sig.inter.SmallBeamInter; import org.audiveris.omr.sig.inter.StemInter; import org.audiveris.omr.sig.relation.Relation; import org.audiveris.omr.sig.relation.StemAlignmentRelation; @@ -88,69 +89,11 @@ @NotThreadSafe public class SlotsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(SlotsBuilder.class); - //~ Enumerations ------------------------------------------------------------------------------- - //-----// - // Rel // - //-----// - /** - * Describes the oriented relationship between two chords of the measure stack. - */ - protected static enum Rel - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** - * Strongly before. - * Stem-located before in the same beam group. - * Abscissa-located before the vertically overlapping chord. - * Important abscissa difference in different staves. - */ - BEFORE("B"), - // - /** Strongly after. - * Stem-located after in the same beam group. - * Abscissa-located after the vertically overlapping chord. - * Important abscissa difference in different staves. - */ - AFTER("A"), - // - /** - * Strongly equal. - * Identical thanks to an originating glyph in common. - * Adjacency detected in same staff. - */ - EQUAL("="), - // - /** - * Weakly close. - * No important difference, use other separation criteria. - */ - CLOSE("?"); - //~ Instance fields ------------------------------------------------------------------------ - - private final String mnemo; - - //~ Constructors --------------------------------------------------------------------------- - Rel (String mnemo) - { - this.mnemo = mnemo; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return mnemo; - } - } - - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated measure stack. */ private final MeasureStack stack; @@ -161,11 +104,11 @@ public String toString () private final Parameters params; /** Inter-chord relationships for the current measure stack. */ - private SimpleDirectedGraph graph = new SimpleDirectedGraph( + private SimpleDirectedGraph graph = new SimpleDirectedGraph<>( Edge.class); /** Current earliest term for each staff in stack. */ - private final Map stackTerms = new LinkedHashMap(); + private final Map stackTerms = new LinkedHashMap<>(); /** Comparator based on inter-chord relationships, then on timeOffset when known. */ private final Comparator byRel = new Comparator() @@ -203,7 +146,6 @@ public int compare (AbstractChordInter c1, } }; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SlotsBuilder} object for a measure stack. * @@ -219,7 +161,6 @@ public SlotsBuilder (MeasureStack stack, params = new Parameters(stack.getSystem().getSheet().getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// @@ -335,8 +276,7 @@ private boolean areAdjacent (AbstractChordInter ch1, private void buildRelationships () { // Sort measure standard chords by abscissa - List stdChords = new ArrayList( - stack.getStandardChords()); + List stdChords = new ArrayList<>(stack.getStandardChords()); Collections.sort(stdChords, Inters.byAbscissa); // Populate graph with chords @@ -374,8 +314,7 @@ private boolean buildSlots () // The 'actives' collection gathers the chords that are not terminated at the // time slot being considered. Initially, it contains just the whole chords. - List actives = new ArrayList( - stack.getWholeRestChords()); + List actives = new ArrayList<>(stack.getWholeRestChords()); Collections.sort(actives, Inters.byAbscissa); // Create voices for whole rest chords @@ -408,8 +347,8 @@ private boolean buildSlots () // Which chords end here, and is their voice available or not for the slot? // (if a beam group continues, its voice remains locked) - List freeEndings = new ArrayList(); - List endings = new ArrayList(); + List freeEndings = new ArrayList<>(); + List endings = new ArrayList<>(); detectEndings(actives, term, endings, freeEndings); // Do we have pending chords that start at this slot? @@ -474,11 +413,6 @@ private boolean checkInterSlot (Slot slot) Slot prevSlot = stack.getSlots().get(slot.getId() - 2); int dx = slot.getXOffset() - prevSlot.getXOffset(); - - if (dx >= params.minInterSlotDx) { - return true; - } - // // // We are too close, find out the guilty chord // logger.debug("Too close slot {}", slot); @@ -506,7 +440,8 @@ private boolean checkInterSlot (Slot slot) // } // } // - return false; + + return dx >= params.minInterSlotDx; } //----------------------// @@ -520,7 +455,7 @@ private boolean checkInterSlot (Slot slot) private boolean checkStavesAreFilled () { // Use a temporary map: (staff -> chords) - Map> map = new HashMap>(); + Map> map = new HashMap<>(); for (Staff staff : stack.getSystem().getStaves()) { map.put(staff, new ArrayList()); @@ -733,7 +668,7 @@ private void dumpRelationships (List stdChords) */ private Set getClosure (AbstractChordInter chord) { - Set closes = new LinkedHashSet(); + Set closes = new LinkedHashSet<>(); closes.add(chord); for (AbstractChordInter ch : stack.getStandardChords()) { @@ -758,7 +693,7 @@ private Set getClosure (AbstractChordInter chord) */ private List getPendingChords () { - List pendings = new ArrayList(); + List pendings = new ArrayList<>(); for (AbstractChordInter chord : stack.getStandardChords()) { if (!chord.isWholeRest()) { @@ -847,21 +782,27 @@ private boolean haveCommonHead (AbstractChordInter ch1, // inspectBeams // //--------------// /** - * Derive some inter-chord relationships from BeamGroup instances. + * Derive some inter-chord relationships from BeamGroup instances, excepting the + * cue beams of course. + *

                                                                                  * Within a single BeamGroup, there are strict relationships between the chords. */ private void inspectBeams () { for (Measure measure : stack.getMeasures()) { + GroupLoop: for (BeamGroup group : measure.getBeamGroups()) { - Set chordSet = new LinkedHashSet(); + Set chordSet = new LinkedHashSet<>(); for (AbstractBeamInter beam : group.getBeams()) { + if (beam instanceof SmallBeamInter) { + continue GroupLoop; // Exclude cue beam group + } + chordSet.addAll(beam.getChords()); } - final List groupChords = new ArrayList( - chordSet); + final List groupChords = new ArrayList<>(chordSet); Collections.sort(groupChords, Inters.byAbscissa); for (int i = 0; i < groupChords.size(); i++) { @@ -886,7 +827,7 @@ private void inspectBeams () */ private void inspectLocations (List stdChords) { - final List adjacencies = new ArrayList(); + final List adjacencies = new ArrayList<>(); for (int i = 0; i < stdChords.size(); i++) { AbstractChordInter ch1 = stdChords.get(i); @@ -975,8 +916,7 @@ private void inspectLocations (List stdChords) */ private void inspectMirrors () { - final List headChords = new ArrayList( - stack.getHeadChords()); + final List headChords = new ArrayList<>(stack.getHeadChords()); for (int i = 0; i < headChords.size(); i++) { HeadChordInter ch1 = headChords.get(i); @@ -1014,8 +954,7 @@ private void inspectRootStems () { final SIGraph sig = stack.getSystem().getSig(); - final List headChords = new ArrayList( - stack.getHeadChords()); + final List headChords = new ArrayList<>(stack.getHeadChords()); for (int i = 0; i < headChords.size(); i++) { HeadChordInter ch1 = headChords.get(i); @@ -1031,7 +970,7 @@ private void inspectRootStems () continue; } - Set alignedStems = new LinkedHashSet(); + Set alignedStems = new LinkedHashSet<>(); for (Relation rel : aligns) { Inter inter = sig.getOppositeInter(stem1, rel); @@ -1107,7 +1046,7 @@ private List retrieveIncomingChords (List incomings = new ArrayList(); + List incomings = new ArrayList<>(); AbstractChordInter firstChord = pendings.get(0); PendingLoop: @@ -1168,7 +1107,57 @@ private void setRel (AbstractChordInter from, } } - //~ Inner Classes ------------------------------------------------------------------------------ + //-----// + // Rel // + //-----// + /** + * Describes the oriented relationship between two chords of the measure stack. + */ + protected static enum Rel + { + /** + * Strongly before. + * Stem-located before in the same beam group. + * Abscissa-located before the vertically overlapping chord. + * Important abscissa difference in different staves. + */ + BEFORE("B"), + // + /** + * Strongly after. + * Stem-located after in the same beam group. + * Abscissa-located after the vertically overlapping chord. + * Important abscissa difference in different staves. + */ + AFTER("A"), + // + /** + * Strongly equal. + * Identical thanks to an originating glyph in common. + * Adjacency detected in same staff. + */ + EQUAL("="), + // + /** + * Weakly close. + * No important difference, use other separation criteria. + */ + CLOSE("?"); + + private final String mnemo; + + Rel (String mnemo) + { + this.mnemo = mnemo; + } + + @Override + public String toString () + { + return mnemo; + } + } + //------// // Edge // //------// @@ -1177,12 +1166,13 @@ private void setRel (AbstractChordInter from, */ protected static class Edge { - //~ Instance fields ------------------------------------------------------------------------ Rel rel; // Relationship carried by the concrete edge - //~ Constructors --------------------------------------------------------------------------- - public Edge (Rel rel) + /** + * @param rel + */ + Edge (Rel rel) { this.rel = rel; } @@ -1191,10 +1181,9 @@ public Edge (Rel rel) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxSlotDx = new Scale.Fraction( 1.05, @@ -1221,15 +1210,13 @@ private static final class Constants */ private static class ChordPair { - //~ Instance fields ------------------------------------------------------------------------ final AbstractChordInter one; final AbstractChordInter two; - //~ Constructors --------------------------------------------------------------------------- - public ChordPair (AbstractChordInter one, - AbstractChordInter two) + ChordPair (AbstractChordInter one, + AbstractChordInter two) { this.one = one; this.two = two; @@ -1241,7 +1228,6 @@ public ChordPair (AbstractChordInter one, two.getHeadLocation()); } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { @@ -1254,7 +1240,6 @@ public String toString () //------------// private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ private final int maxSlotDx; @@ -1264,8 +1249,7 @@ private static class Parameters private final int maxVerticalOverlap; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale) + Parameters (Scale scale) { maxSlotDx = scale.toPixels(constants.maxSlotDx); minInterSlotDx = scale.toPixels(constants.minInterSlotDx); diff --git a/src/main/org/audiveris/omr/sheet/rhythm/StackConfig.java b/src/main/org/audiveris/omr/sheet/rhythm/StackConfig.java deleted file mode 100644 index 4cc8bcf4d..000000000 --- a/src/main/org/audiveris/omr/sheet/rhythm/StackConfig.java +++ /dev/null @@ -1,180 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// S t a c k C o n f i g // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet.rhythm; - -import org.audiveris.omr.sig.inter.AugmentationDotInter; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.inter.Inters; -import org.audiveris.omr.sig.inter.RestChordInter; -import org.audiveris.omr.sig.inter.RestInter; - -import java.util.Collection; -import java.util.Comparator; -import java.util.TreeSet; - -/** - * Meant to store one configuration of rhythm adjustable data for a stack. - * - * @author Hervé Bitteur - */ -public class StackConfig -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - /** Compare configs by their decreasing size. */ - public static final Comparator byReverseSize = new Comparator() - { - @Override - public int compare (StackConfig c1, - StackConfig c2) - { - return Integer.compare(c2.inters.size(), c1.inters.size()); - } - }; - - //~ Instance fields ---------------------------------------------------------------------------- - /** Rhythm data for this config, always ordered byFullAbscissa. */ - private final TreeSet inters = new TreeSet(Inters.byFullAbscissa); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code RhythmConfig} object. - * - * @param inters good FRAT inters for the stack - */ - public StackConfig (Collection inters) - { - this.inters.addAll(inters); - } - - //~ Methods ------------------------------------------------------------------------------------ - //-----// - // add // - //-----// - public void add (Inter inter) - { - // Add inter to config - inters.add(inter); - - // As well as its augmentation dot(s) if relevant (checked in backup sig) - if (inter instanceof RestChordInter) { - RestChordInter restChord = (RestChordInter) inter; - RestInter rest = (RestInter) restChord.getNotes().get(0); - AugmentationDotInter firstDot = rest.getFirstAugmentationDot(); - - if (firstDot != null) { - inters.add(firstDot); - - AugmentationDotInter secondDot = firstDot.getSecondAugmentationDot(); - - if (secondDot != null) { - inters.add(secondDot); - } - } - } - } - - //------// - // copy // - //------// - public StackConfig copy () - { - return new StackConfig(inters); - } - - //--------// - // equals // - //--------// - @Override - public boolean equals (Object obj) - { - if (!(obj instanceof StackConfig)) { - return false; - } - - StackConfig that = (StackConfig) obj; - - return inters.equals(that.inters); - } - - //-----------// - // getInters // - //-----------// - public TreeSet getInters () - { - return inters; - } - - //----------// - // hashCode // - //----------// - @Override - public int hashCode () - { - int hash = 7; - - return hash; - } - - //-----// - // ids // - //-----// - public String ids () - { - return Inters.ids(inters); - } - - //--------// - // remove // - //--------// - public void remove (Inter inter) - { - // Remove the inter from config - inters.remove(inter); - - // As well as its augmentation dot(s) if relevant - if (inter instanceof RestChordInter) { - RestChordInter restChord = (RestChordInter) inter; - RestInter rest = (RestInter) restChord.getNotes().get(0); - AugmentationDotInter firstDot = rest.getFirstAugmentationDot(); - - if (firstDot != null) { - inters.remove(firstDot); - - AugmentationDotInter secondDot = firstDot.getSecondAugmentationDot(); - - if (secondDot != null) { - inters.remove(secondDot); - } - } - } - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - return inters.toString(); - } -} diff --git a/src/main/org/audiveris/omr/sheet/rhythm/StackTuner.java b/src/main/org/audiveris/omr/sheet/rhythm/StackTuner.java index df8e631c2..a8c63b664 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/StackTuner.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/StackTuner.java @@ -49,18 +49,15 @@ */ public class StackTuner { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(StackTuner.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated measure stack. */ private final MeasureStack stack; /** Fail-fast mode, just meant to guess expected duration. */ private final boolean failFast; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StackTuner} object. * @@ -74,7 +71,6 @@ public StackTuner (MeasureStack stack, this.failFast = failFast; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// diff --git a/src/main/org/audiveris/omr/sheet/rhythm/SystemBackup.java b/src/main/org/audiveris/omr/sheet/rhythm/SystemBackup.java deleted file mode 100644 index 9ad495e4e..000000000 --- a/src/main/org/audiveris/omr/sheet/rhythm/SystemBackup.java +++ /dev/null @@ -1,54 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// S y s t e m B a c k u p // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet.rhythm; - -import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sig.SigBackup; - -/** - * Class {@code SystemBackup} manages the saving of selected inters in a system. - *

                                                                                  - * It gathers the rhythm data that has been discarded by symbol reduction, but may be reconsidered - * for rhythm tunings. - * - * @author Hervé Bitteur - */ -public class SystemBackup - extends SigBackup -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** Dedicated system. */ - private final SystemInfo system; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SystemBackup} object. - * - * @param system the dedicated system - */ - public SystemBackup (SystemInfo system) - { - super(system.getSig()); - this.system = system; - } -} diff --git a/src/main/org/audiveris/omr/sheet/rhythm/TupletsBuilder.java b/src/main/org/audiveris/omr/sheet/rhythm/TupletsBuilder.java index f62d83037..7e0045df7 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/TupletsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/TupletsBuilder.java @@ -25,7 +25,6 @@ import org.audiveris.omr.math.GeoUtil; import org.audiveris.omr.math.Rational; import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sig.SIGraph; import org.audiveris.omr.sig.inter.AbstractBeamInter; import org.audiveris.omr.sig.inter.AbstractChordInter; import org.audiveris.omr.sig.inter.Inters; @@ -33,6 +32,7 @@ import org.audiveris.omr.sig.inter.TupletInter; import org.audiveris.omr.sig.relation.ChordTupletRelation; import org.audiveris.omr.sig.relation.Link; +import org.audiveris.omr.sig.relation.Relation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +47,7 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import org.audiveris.omr.sig.SIGraph; /** * Class {@code TupletsBuilder} tries to connect every tuplet symbol in a measure stack @@ -56,15 +57,12 @@ */ public class TupletsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TupletsBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated measure stack. */ private final MeasureStack stack; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TupletsBuilder} object. * @@ -75,62 +73,6 @@ public TupletsBuilder (MeasureStack stack) this.stack = stack; } - //~ Methods ------------------------------------------------------------------------------------ - //-------------------// - // getEmbracedChords // - //-------------------// - /** - * Report the proper collection of chords that are embraced by the tuplet. - * - * @param tuplet underlying tuplet sign - * @param candidates the chords candidates, ordered by euclidian distance to sign - * @return the set of embraced chords, ordered from left to right, or null if retrieval failed - */ - public static SortedSet getEmbracedChords (TupletInter tuplet, - List candidates) - { - logger.trace("{} getEmbracedChords", tuplet); - - // We consider each candidate in turn, with its duration - // in order to determine the duration base of the tuplet - TupletCollector collector = new TupletCollector( - tuplet, - new TreeSet(Inters.byFullAbscissa)); - - final Staff targetStaff = getTargetStaff(candidates); - - for (AbstractChordInter chord : candidates) { - // We assume that chords with 2 staves have their tuplet sign above... - Staff staff = chord.getTopStaff(); - - // Check that all chords are on the same staff - if (staff != targetStaff) { - continue; - } - - collector.include(chord); - - // Check we have collected the exact amount of time - // TODO: Test is questionable for non-reliable candidates - if (collector.isNotOk()) { - logger.debug("{} {}", tuplet, collector.getStatusMessage()); - - return null; - } else if (collector.isOk()) { - if (logger.isDebugEnabled()) { - collector.dump(); - } - - return collector.getChords(); // Normal exit - } - } - - // Candidates are exhausted, we lack chords - logger.debug("{} {}", tuplet, collector.getStatusMessage()); - - return null; - } - //------------------// // linkStackTuplets // //------------------// @@ -148,21 +90,27 @@ public static SortedSet getEmbracedChords (TupletInter tuple */ public Set linkStackTuplets () { - final Set toDelete = new LinkedHashSet(); + final Set toDelete = new LinkedHashSet<>(); final Set tuplets = stack.getTuplets(); for (TupletInter tuplet : tuplets) { - // Clear existing tuplet-chord relations, if any + // Purge existing tuplet-chord relations, except manual ones if any final SIGraph sig = stack.getSystem().getSig(); - sig.removeAllEdges(sig.getRelations(tuplet, ChordTupletRelation.class)); - - Collection links = lookupLinks(tuplet); + final Set rels = sig.getRelations(tuplet, ChordTupletRelation.class); - if (!links.isEmpty()) { - for (Link link : links) { - link.applyTo(tuplet); + for (Relation rel : rels) { + if (!rel.isManual()) { + sig.removeEdge(rel); } - } else if (!tuplet.isManual()) { + } + + final Collection links = lookupLinks(tuplet); + + for (Link link : links) { + link.applyTo(tuplet); + } + + if (!tuplet.isManual() && !sig.hasRelation(tuplet, ChordTupletRelation.class)) { toDelete.add(tuplet); } } @@ -194,7 +142,7 @@ public Collection lookupLinks (TupletInter tuplet) logger.trace("{} connectable to {}", tuplet, chords); - List links = new ArrayList(); + List links = new ArrayList<>(); for (AbstractChordInter chord : chords) { links.add(new Link(chord, new ChordTupletRelation(tuplet.getShape()), false)); @@ -203,6 +151,98 @@ public Collection lookupLinks (TupletInter tuplet) return links; } + //-----------------// + // getChordsAround // + //-----------------// + /** + * Report the list of AbstractChordInter instances (rests & heads) in the + * neighborhood of the specified Inter. + *

                                                                                  + * Neighborhood is limited horizontally by the measure sides and vertically by the staves above + * and below. + * + * @param tuplet the inter of interest + * @return the list of neighbors, sorted by euclidian distance to tuplet sign + */ + private List getChordsAround (TupletInter tuplet) + { + Point center = tuplet.getCenter(); + List stavesAround = stack.getSystem().getStavesAround(center); + logger.trace("{} around:{}", tuplet, stavesAround); + + // Collect candidate chords (heads & rests based) within stack + List chords = new ArrayList<>(); + + for (AbstractChordInter chord : stack.getStandardChords()) { + final List chordStaves = new ArrayList<>(chord.getStaves()); + chordStaves.retainAll(stavesAround); + + if (!chordStaves.isEmpty()) { + chords.add(chord); + } + } + + Collections.sort(chords, new ByEuclidian(center)); + logger.trace("Chords: {}", Inters.ids(chords)); + + return chords; + } + + //-------------------// + // getEmbracedChords // + //-------------------// + /** + * Report the proper collection of chords that are embraced by the tuplet. + * + * @param tuplet underlying tuplet sign + * @param candidates the chords candidates, ordered by euclidian distance to sign + * @return the set of embraced chords, ordered from left to right, or null if retrieval failed + */ + public static SortedSet getEmbracedChords (TupletInter tuplet, + List candidates) + { + logger.trace("{} getEmbracedChords", tuplet); + + // We consider each candidate in turn, with its duration + // in order to determine the duration base of the tuplet + TupletCollector collector = new TupletCollector( + tuplet, + new TreeSet(Inters.byFullAbscissa)); + + final Staff targetStaff = getTargetStaff(candidates); + + for (AbstractChordInter chord : candidates) { + // We assume that chords with 2 staves have their tuplet sign above... + Staff staff = chord.getTopStaff(); + + // Check that all chords are on the same staff + if (staff != targetStaff) { + continue; + } + + collector.include(chord); + + // Check we have collected the exact amount of time + // TODO: Test is questionable for non-reliable candidates + if (collector.isNotOk()) { + logger.debug("{} {}", tuplet, collector.getStatusMessage()); + + return null; + } else if (collector.isOk()) { + if (logger.isDebugEnabled()) { + collector.dump(); + } + + return collector.getChords(); // Normal exit + } + } + + // Candidates are exhausted, we lack chords + logger.debug("{} {}", tuplet, collector.getStatusMessage()); + + return null; + } + //---------------// // expectedCount // //---------------// @@ -249,62 +289,21 @@ private static Staff getTargetStaff (List candidates) return null; } - //-----------------// - // getChordsAround // - //-----------------// - /** - * Report the list of AbstractChordInter instances (rests & heads) in the - * neighborhood of the specified Inter. - *

                                                                                  - * Neighborhood is limited horizontally by the measure sides and vertically by the staves above - * and below. - * - * @param tuplet the inter of interest - * @return the list of neighbors, sorted by euclidian distance to tuplet sign - */ - private List getChordsAround (TupletInter tuplet) - { - Point center = tuplet.getCenter(); - List stavesAround = stack.getSystem().getStavesAround(center); - logger.trace("{} around:{}", tuplet, stavesAround); - - // Collect candidate chords (heads & rests based) within stack - List chords = new ArrayList(); - - for (AbstractChordInter chord : stack.getStandardChords()) { - final List chordStaves = new ArrayList(chord.getStaves()); - chordStaves.retainAll(stavesAround); - - if (!chordStaves.isEmpty()) { - chords.add(chord); - } - } - - Collections.sort(chords, new ByEuclidian(center)); - logger.trace("Chords: {}", Inters.ids(chords)); - - return chords; - } - - //~ Inner Classes ------------------------------------------------------------------------------ //-------------// // ByEuclidian // //-------------// private static class ByEuclidian implements Comparator { - //~ Instance fields ------------------------------------------------------------------------ /** The location of the tuplet sign */ private final Point signPoint; - //~ Constructors --------------------------------------------------------------------------- - public ByEuclidian (Point signPoint) + ByEuclidian (Point signPoint) { this.signPoint = signPoint; } - //~ Methods -------------------------------------------------------------------------------- /** Compare their euclidian distance from the signPoint reference */ @Override public int compare (AbstractChordInter c1, @@ -317,35 +316,9 @@ public int compare (AbstractChordInter c1, } } - //-----------------// - // TupletCollector // - //-----------------// - /** - * In charge of incrementally collecting the chords for a given tuplet sign. - *

                                                                                  - * If some of its embraced heads are linked to a beam, then the whole beam of heads must be - * embraced by the tuplet. If not, this is not a true tuplet. - *

                                                                                  - * The tuplet must be located close to the middle abscissa of the embraced notes. - * Vertically, it should be away from the middle chord(s). - */ private static class TupletCollector { - //~ Enumerations --------------------------------------------------------------------------- - - /** Describe the current status of the tuplet collector */ - public enum Status - { - //~ Enumeration constant initializers -------------------------------------------------- - - TOO_SHORT, - OK, - TOO_LONG, - TOO_MANY, - OUTSIDE; - } - //~ Instance fields ------------------------------------------------------------------------ /** Underlying sign. */ private final TupletInter tuplet; @@ -367,16 +340,14 @@ public enum Status /** Current status. */ private Status status; - //~ Constructors --------------------------------------------------------------------------- - public TupletCollector (TupletInter tuplet, - SortedSet chords) + TupletCollector (TupletInter tuplet, + SortedSet chords) { this.tuplet = tuplet; expectedCount = expectedCount(tuplet.getShape()); this.chords = chords; } - //~ Methods -------------------------------------------------------------------------------- public void dump () { StringBuilder sb = new StringBuilder(); @@ -406,8 +377,8 @@ public SortedSet getChords () public String getStatusMessage () { StringBuilder sb = new StringBuilder(); - sb.append(status).append(" sequence in ").append(tuplet.getShape()).append(": ") - .append(total); + sb.append(status).append(" sequence in ").append(tuplet.getShape()).append(": ").append( + total); if (expectedTotal != Rational.MAX_VALUE) { sb.append(" vs ").append(expectedTotal); @@ -467,7 +438,7 @@ public boolean isOk () */ protected Set getBeamSiblings (AbstractChordInter chord) { - final Set set = new LinkedHashSet(); + final Set set = new LinkedHashSet<>(); set.add(chord); StemInter stem = chord.getStem(); @@ -504,8 +475,18 @@ private boolean isWithinChordsAbscissaRange () { int signX = tuplet.getCenter().x; - return (signX >= chords.first().getTailLocation().x) - && (signX <= chords.last().getTailLocation().x); + return (signX >= chords.first().getTailLocation().x) && (signX <= chords.last() + .getTailLocation().x); + } + + /** Describe the current status of the tuplet collector */ + public enum Status + { + TOO_SHORT, + OK, + TOO_LONG, + TOO_MANY, + OUTSIDE; } } } diff --git a/src/main/org/audiveris/omr/sheet/rhythm/Voice.java b/src/main/org/audiveris/omr/sheet/rhythm/Voice.java index 186b8ebe1..bd3b7820a 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/Voice.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/Voice.java @@ -52,7 +52,8 @@ /** * Class {@code Voice} gathers all informations related to a voice within a measure. *

                                                                                  - * We now assign voice ID according to the part staff where this voice starts:

                                                                                    + * We now assign voice ID according to the part staff where this voice starts: + *
                                                                                      *
                                                                                    1. If starting on first staff, we use IDs: 1..4 *
                                                                                    2. If starting on second staff, we use IDs: 5..8 *
                                                                                    @@ -63,22 +64,9 @@ @XmlRootElement(name = "voice") public class Voice { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Voice.class); - //~ Enumerations ------------------------------------------------------------------------------- - public static enum Status - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** A chord begins at this slot. */ - BEGIN, - /** A chord is still active at this slot. */ - CONTINUE; - } - - //~ Instance fields ---------------------------------------------------------------------------- // Persistent data //---------------- // @@ -132,7 +120,6 @@ public static enum Status /** Inferred time signature based on this voice content. */ private TimeRational inferredTimeSig; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Voice object. * @@ -163,10 +150,14 @@ private Voice () { } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // afterReload // //-------------// + /** + * To be called right after unmarshalling. + * + * @param measure the containing measure + */ public void afterReload (Measure measure) { try { @@ -193,27 +184,6 @@ public void afterReload (Measure measure) } } - //------------------// - // createWholeVoice // - //------------------// - /** - * Factory method to create a voice made of just one whole/multi rest. - * - * @param wholeChord the whole/multi rest chord - * @param measure the containing measure - * @return the created voice instance - */ - public static Voice createWholeVoice (RestChordInter wholeChord, - Measure measure) - { - logger.debug("createWholeVoice for {} in {}", wholeChord, measure); - - Voice voice = new Voice(wholeChord, measure); - voice.wholeRestChord = wholeChord; - - return voice; - } - //---------------// // checkDuration // //---------------// @@ -374,6 +344,20 @@ public int getId () return id; } + //-------// + // setId // + //-------// + /** + * Change the voice id (to rename voices) + * + * @param id the new id value + */ + public void setId (int id) + { + ///logger.debug("measure#{} {} renamed as {}", measure.getIdValue(), this, id); + this.id = id; + } + //--------------------------// // getInferredTimeSignature // //--------------------------// @@ -385,110 +369,116 @@ public int getId () public TimeRational getInferredTimeSignature () { if (inferredTimeSig == null) { - // Sequence of group (beamed or isolated chords) durations - List durations = new ArrayList(); + try { + // Sequence of group (beamed or isolated chords) durations + List durations = new ArrayList<>(); - // Voice time offset - Rational timeOffset = null; + // Voice time offset + Rational timeOffset = null; - // Start time of last note in group, if any - Rational groupLastTime = null; + // Start time of last note in group, if any + Rational groupLastTime = null; - if (slots != null) { - for (Map.Entry entry : slots.entrySet()) { - SlotVoice info = entry.getValue(); + if (slots != null) { + for (Map.Entry entry : slots.entrySet()) { + SlotVoice info = entry.getValue(); - if (info.status == Voice.Status.BEGIN) { - AbstractChordInter chord = info.chord; + if (info.status == Voice.Status.BEGIN) { + AbstractChordInter chord = info.chord; - // Skip the remaining parts of beam group, including embraced rests - if ((groupLastTime != null) - && (chord.getTimeOffset().compareTo(groupLastTime) <= 0)) { - continue; - } + // Skip the remaining parts of beam group, including embraced rests + if ((groupLastTime != null) && (chord.getTimeOffset().compareTo( + groupLastTime) <= 0)) { + continue; + } - BeamGroup group = chord.getBeamGroup(); + BeamGroup group = chord.getBeamGroup(); - if (group == null) { - // Isolated chord - durations.add(chord.getDuration()); - } else { - // Starting a new group - durations.add(group.getDuration()); - groupLastTime = group.getLastChord().getTimeOffset(); - } + if (group == null) { + // Isolated chord + durations.add(chord.getDuration()); + } else { + // Starting a new group + durations.add(group.getDuration()); + groupLastTime = group.getLastChord().getTimeOffset(); + } - if (timeOffset == null) { - Slot slot = measure.getStack().getSlots().get(entry.getKey() - 1); - timeOffset = slot.getTimeOffset(); + if (timeOffset == null) { + Slot slot = measure.getStack().getSlots().get(entry.getKey() - 1); + timeOffset = slot.getTimeOffset(); + } } } } - } - // Debug - if (logger.isDebugEnabled()) { - StringBuilder sb = new StringBuilder("["); - boolean started = false; - Rational total = null; + // Debug + if (logger.isDebugEnabled()) { + StringBuilder sb = new StringBuilder("["); + boolean started = false; + Rational total = null; - for (Rational dur : durations) { - if (started) { - sb.append(","); - } - - started = true; + for (Rational dur : durations) { + if (started) { + sb.append(","); + } - if (dur == null) { - sb.append("null"); - } else { - sb.append(dur); + started = true; - if (total == null) { - total = dur; + if (dur == null) { + sb.append("null"); } else { - total = total.plus(dur); + sb.append(dur); + + if (total == null) { + total = dur; + } else { + total = total.plus(dur); + } } } - } - sb.append("] total:"); + sb.append("] total:"); - if (total != null) { - sb.append(total); - } else { - sb.append("null"); + if (total != null) { + sb.append(total); + } else { + sb.append("null"); + } + + logger.debug("{}: {}", this, sb); } - logger.debug("{}: {}", this, sb); - } + // Check this voice fills the measure stack + if ((timeOffset == null) || !timeOffset.equals(Rational.ZERO)) { + return null; + } - // Check this voice fills the measure stack - if ((timeOffset == null) || !timeOffset.equals(Rational.ZERO)) { - return null; - } + if ((termination == null) || !termination.equals(Rational.ZERO)) { + return null; + } - if ((termination == null) || !termination.equals(Rational.ZERO)) { - return null; - } + // Do we have a regular pattern? + int count = 0; + Rational common = null; - // Do we have a regular pattern? - int count = 0; - Rational common = null; + for (Rational dur : durations) { + if (common == null) { + common = dur; + } else if (!common.equals(dur)) { + break; + } - for (Rational dur : durations) { - if (common == null) { - common = dur; - } else if (!common.equals(dur)) { - break; + count++; } - count++; - } + if ((common != null) && (count == durations.size())) { + // All the durations are equal + inferredTimeSig = timeSigOf(count, common); + } + } catch (Exception ex) { + logger.warn("Could not guess time signature for {} {}", this, ex.toString(), ex); - if ((common != null) && (count == durations.size())) { - // All the durations are equal - inferredTimeSig = timeSigOf(count, common); + return null; } } @@ -528,6 +518,17 @@ public Measure getMeasure () return measure; } + //------------// + // setMeasure // + //------------// + /** + * @param measure the measure to set + */ + public void setMeasure (Measure measure) + { + this.measure = measure; + } + //----------// // getRests // //----------// @@ -538,7 +539,7 @@ public Measure getMeasure () */ public List getRests () { - List rests = new ArrayList(); + List rests = new ArrayList<>(); if (isWhole()) { rests.add(wholeRestChord); @@ -588,6 +589,19 @@ public Staff getStartingStaff () return startingStaff; } + //------------------// + // setStartingStaff // + //------------------// + /** + * Set voice starting staff. + * + * @param startingStaff the startingStaff to set + */ + public void setStartingStaff (Staff startingStaff) + { + this.startingStaff = startingStaff; + } + //----------------// // getTermination // //----------------// @@ -601,6 +615,14 @@ public Rational getTermination () return termination; } + //----------------// + // setTermination // + //----------------// + private void setTermination (Rational termination) + { + this.termination = termination; + } + //---------------// // getWholeChord // //---------------// @@ -617,6 +639,9 @@ public AbstractChordInter getWholeChord () //---------------// // initTransient // //---------------// + /** + * @param measure the containing measure + */ public final void initTransient (Measure measure) { this.measure = measure; @@ -649,31 +674,6 @@ public boolean isWhole () return wholeRestChord != null; } - //-------// - // setId // - //-------// - /** - * Change the voice id (to rename voices) - * - * @param id the new id value - */ - public void setId (int id) - { - ///logger.debug("measure#{} {} renamed as {}", measure.getIdValue(), this, id); - this.id = id; - } - - //------------// - // setMeasure // - //------------// - /** - * @param measure the measure to set - */ - public void setMeasure (Measure measure) - { - this.measure = measure; - } - //-------------// // setSlotInfo // //-------------// @@ -693,7 +693,7 @@ public void setSlotInfo (Slot slot, } if (slots == null) { - slots = new TreeMap(); + slots = new TreeMap<>(); } slots.put(slot.getId(), chordInfo); @@ -701,19 +701,6 @@ public void setSlotInfo (Slot slot, logger.debug("setSlotInfo slot#{} {}", slot.getId(), this); } - //------------------// - // setStartingStaff // - //------------------// - /** - * Set voice starting staff. - * - * @param startingStaff the startingStaff to set - */ - public void setStartingStaff (Staff startingStaff) - { - this.startingStaff = startingStaff; - } - //------------// // startChord // //------------// @@ -826,8 +813,8 @@ public void updateSlotTable () SlotVoice info = getSlotInfo(slot); if (info == null) { - if ((lastChord != null) - && (lastChord.getEndTime().compareTo(slot.getTimeOffset()) > 0)) { + if ((lastChord != null) && (lastChord.getEndTime().compareTo( + slot.getTimeOffset()) > 0)) { setSlotInfo(slot, new SlotVoice(lastChord, Status.CONTINUE)); } } else { @@ -859,14 +846,6 @@ private void insertForward (Rational duration, // chord.addMark(mark); } - //----------------// - // setTermination // - //----------------// - private void setTermination (Rational termination) - { - this.termination = termination; - } - //-----------// // timeSigOf // //-----------// @@ -903,7 +882,38 @@ private TimeRational timeSigOf (int count, return timeRational; } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------------// + // createWholeVoice // + //------------------// + /** + * Factory method to create a voice made of just one whole/multi rest. + * + * @param wholeChord the whole/multi rest chord + * @param measure the containing measure + * @return the created voice instance + */ + public static Voice createWholeVoice (RestChordInter wholeChord, + Measure measure) + { + logger.debug("createWholeVoice for {} in {}", wholeChord, measure); + + Voice voice = new Voice(wholeChord, measure); + voice.wholeRestChord = wholeChord; + + return voice; + } + + /** + * Voice status with respect to a slot. + */ + public static enum Status + { + /** A chord begins at this slot. */ + BEGIN, + /** A chord is still active at this slot. */ + CONTINUE + } + //-----------// // SlotVoice // //-----------// @@ -914,7 +924,6 @@ private TimeRational timeSigOf (int count, @XmlRootElement(name = "slot-voice") public static class SlotVoice { - //~ Instance fields ------------------------------------------------------------------------ /** Related chord. */ @XmlIDREF @@ -925,7 +934,12 @@ public static class SlotVoice @XmlAttribute public final Status status; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a SlotVoice object. + * + * @param chord implementing chord at this slot + * @param status tell if the chord is starting or continuing at this slot + */ public SlotVoice (AbstractChordInter chord, Status status) { @@ -940,15 +954,21 @@ private SlotVoice () this.status = null; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { - StringBuilder sb = new StringBuilder(); - sb.append("{Info"); - sb.append(" Ch#").append(chord.getId()); - sb.append(" ").append(status); - sb.append("}"); + final StringBuilder sb = new StringBuilder(); + sb.append("{SlotVoice"); + + if (chord != null) { + sb.append(" Ch#").append(chord.getId()); + } + + if (status != null) { + sb.append(' ').append(status); + } + + sb.append('}'); return sb.toString(); } diff --git a/src/main/org/audiveris/omr/sheet/rhythm/Voices.java b/src/main/org/audiveris/omr/sheet/rhythm/Voices.java index fb03d9b90..815814a84 100644 --- a/src/main/org/audiveris/omr/sheet/rhythm/Voices.java +++ b/src/main/org/audiveris/omr/sheet/rhythm/Voices.java @@ -52,7 +52,6 @@ */ public abstract class Voices { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Voices.class); @@ -126,7 +125,11 @@ public int compare (Voice v1, } }; - //~ Methods ------------------------------------------------------------------------------------ + // Not meant to be instantiated. + private Voices () + { + } + //------------// // refinePage // //------------// @@ -205,8 +208,7 @@ public static int refineScore (Score score) final List orphans = part.getSlurs(SlurInter.isBeginningOrphan); - final Part precedingPart = prevSystem.getPartById( - logicalPart.getId()); + final Part precedingPart = prevSystem.getPartById(logicalPart.getId()); if (precedingPart != null) { final List precOrphans = precedingPart.getSlurs( @@ -268,6 +270,8 @@ public SlurInter getInitialSlur (SlurInter slur) * See {@link Slot#buildVoices(java.util.List)} method. *

                                                                                    * Here we simply rename the IDs from top to bottom (roughly), within each staff. + *

                                                                                    + * We then extend each chord voice to its preceding cue chords. * * @param stack the stack to process */ @@ -277,6 +281,7 @@ public static void refineStack (MeasureStack stack) for (Measure measure : stack.getMeasures()) { measure.sortVoices(); measure.renameVoices(); + measure.setCueVoices(); } } @@ -373,13 +378,11 @@ private static Integer getTiedId (Voice voice, return null; } - //~ Inner Interfaces --------------------------------------------------------------------------- //-------------// // SlurAdapter // //-------------// private static interface SlurAdapter { - //~ Methods -------------------------------------------------------------------------------- /** * Report the slur connected to the left of the provided one. diff --git a/src/main/org/audiveris/omr/sheet/stem/StemScaler.java b/src/main/org/audiveris/omr/sheet/stem/StemScaler.java index 105d96d61..5b563d31d 100644 --- a/src/main/org/audiveris/omr/sheet/stem/StemScaler.java +++ b/src/main/org/audiveris/omr/sheet/stem/StemScaler.java @@ -28,6 +28,7 @@ import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.image.ImageUtil; import org.audiveris.omr.math.HiLoPeakFinder; +import org.audiveris.omr.math.HiLoPeakFinder.Quorum; import org.audiveris.omr.math.IntegerFunction; import org.audiveris.omr.math.Range; import org.audiveris.omr.run.Orientation; @@ -68,14 +69,11 @@ */ public class StemScaler { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(StemScaler.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Related sheet. */ private final Sheet sheet; @@ -85,7 +83,6 @@ public class StemScaler /** Most frequent length of horizontal foreground runs found, if any. */ private Range peak; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new StemScaler object. * @@ -96,7 +93,6 @@ public StemScaler (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // displayChart // //--------------// @@ -155,9 +151,11 @@ public StemScale retrieveStemWidth () private StemScale computeStem () { final int area = histoKeeper.function.getArea(); + histoKeeper.peakFinder.setQuorum( + new Quorum((int) Math.rint(area * constants.minValueRatio.getValue()))); + final List stemPeaks = histoKeeper.peakFinder.findPeaks( 1, - (int) Math.rint(area * constants.minValueRatio.getValue()), (int) Math.rint(area * constants.minDerivativeRatio.getValue()), constants.minGainRatio.getValue()); @@ -208,48 +206,6 @@ private ByteProcessor getBuffer () return buf; } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean printWatch = new Constant.Boolean( - false, - "Should we print the StopWatch on stem computation?"); - - private final Constant.Boolean keepStemImage = new Constant.Boolean( - false, - "Should we store stem images on disk?"); - - private final Constant.Boolean useHeader = new Constant.Boolean( - true, - "Should we erase the header at system start"); - - private final Scale.Fraction systemVerticalMargin = new Scale.Fraction( - 2.0, - "Margin erased above & below system header area"); - - private final Constant.Ratio minValueRatio = new Constant.Ratio( - 0.1, - "Absolute ratio of total pixels for peak acceptance"); - - private final Constant.Ratio minDerivativeRatio = new Constant.Ratio( - 0.05, - "Absolute ratio of total pixels for strong derivative"); - - private final Constant.Ratio minGainRatio = new Constant.Ratio( - 0.1, - "Minimum ratio of peak runs for stem peak extension"); - - private final Constant.Ratio stemAsForeRatio = new Constant.Ratio( - 1.0, - "Default stem thickness defined as ratio of foreground peak"); - } - //-------------// // HistoKeeper // //-------------// @@ -258,27 +214,24 @@ private static final class Constants */ private class HistoKeeper { - //~ Instance fields ------------------------------------------------------------------------ private final IntegerFunction function; private final HiLoPeakFinder peakFinder; - //~ Constructors --------------------------------------------------------------------------- /** * Create an instance of histoKeeper. * * @param maxLength the maximum possible horizontal run length value */ - public HistoKeeper (RunTable horiTable, - int maxLength) + HistoKeeper (RunTable horiTable, + int maxLength) { function = new IntegerFunction(0, maxLength); populateFunction(horiTable); peakFinder = new HiLoPeakFinder("stem", function); } - //~ Methods -------------------------------------------------------------------------------- //-----------// // writePlot // //-----------// @@ -306,27 +259,64 @@ private void populateFunction (RunTable horiTable) } if (logger.isDebugEnabled()) { - function.print(System.out); } } } + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print the StopWatch on stem computation?"); + + private final Constant.Boolean keepStemImage = new Constant.Boolean( + false, + "Should we store stem images on disk?"); + + private final Constant.Boolean useHeader = new Constant.Boolean( + true, + "Should we erase the header at system start"); + + private final Scale.Fraction systemVerticalMargin = new Scale.Fraction( + 2.0, + "Margin erased above & below system header area"); + + private final Constant.Ratio minValueRatio = new Constant.Ratio( + 0.1, + "Absolute ratio of total pixels for peak acceptance"); + + private final Constant.Ratio minDerivativeRatio = new Constant.Ratio( + 0.05, + "Absolute ratio of total pixels for strong derivative"); + + private final Constant.Ratio minGainRatio = new Constant.Ratio( + 0.1, + "Minimum ratio of peak runs for stem peak extension"); + + private final Constant.Ratio stemAsForeRatio = new Constant.Ratio( + 1.0, + "Default stem thickness defined as ratio of foreground peak"); + } + //--------------// // StemsCleaner // //--------------// - private class StemsCleaner + private static class StemsCleaner extends PageCleaner { - //~ Constructors --------------------------------------------------------------------------- - public StemsCleaner (ByteProcessor buffer, - Graphics2D g, - Sheet sheet) + StemsCleaner (ByteProcessor buffer, + Graphics2D g, + Sheet sheet) { super(buffer, g, sheet); } - //~ Methods -------------------------------------------------------------------------------- //-------------// // eraseShapes // //-------------// @@ -339,7 +329,7 @@ public void eraseShapes (Collection shapes) { for (SystemInfo system : sheet.getSystems()) { final SIGraph sig = system.getSig(); - final List erased = new ArrayList(); + final List erased = new ArrayList<>(); for (Inter inter : sig.vertexSet()) { if (!inter.isRemoved() && shapes.contains(inter.getShape())) { diff --git a/src/main/org/audiveris/omr/sheet/stem/StemSeedsStep.java b/src/main/org/audiveris/omr/sheet/stem/StemSeedsStep.java index 8f6976997..4c7e1aad3 100644 --- a/src/main/org/audiveris/omr/sheet/stem/StemSeedsStep.java +++ b/src/main/org/audiveris/omr/sheet/stem/StemSeedsStep.java @@ -41,11 +41,9 @@ public class StemSeedsStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(StemSeedsStep.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new StemSeedsStep object. */ @@ -53,7 +51,6 @@ public StemSeedsStep () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // displayUI // //-----------// @@ -86,11 +83,16 @@ public void doSystem (SystemInfo system, protected Void doProlog (Sheet sheet) throws StepException { - // Retrieve typical stem width on global sheet - StemScale stemScale = new StemScaler(sheet).retrieveStemWidth(); + StemScale stemScale = sheet.getScale().getStemScale(); - logger.info("{}", stemScale); - sheet.getScale().setStemScale(stemScale); + // Respect user-assigned stem scale if any + if (stemScale == null) { + // Retrieve typical stem width on global sheet + stemScale = new StemScaler(sheet).retrieveStemWidth(); + + logger.info("{}", stemScale); + sheet.getScale().setStemScale(stemScale); + } return null; } diff --git a/src/main/org/audiveris/omr/sheet/stem/StemsBuilder.java b/src/main/org/audiveris/omr/sheet/stem/StemsBuilder.java index 7ec4a84b0..d1c2d3abc 100644 --- a/src/main/org/audiveris/omr/sheet/stem/StemsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/stem/StemsBuilder.java @@ -25,12 +25,11 @@ import org.audiveris.omr.constant.ConstantSet; import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.GlyphFactory; +import org.audiveris.omr.glyph.GlyphGroup; import org.audiveris.omr.glyph.Glyphs; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.glyph.ShapeSet; -import org.audiveris.omr.glyph.GlyphGroup; import org.audiveris.omr.glyph.dynamic.CompoundFactory; -import org.audiveris.omr.glyph.dynamic.CompoundFactory.CompoundConstructor; import org.audiveris.omr.glyph.dynamic.SectionCompound; import org.audiveris.omr.glyph.dynamic.StraightFilament; import org.audiveris.omr.lag.Section; @@ -38,11 +37,10 @@ import org.audiveris.omr.math.GeoUtil; import org.audiveris.omr.math.LineUtil; import org.audiveris.omr.run.Orientation; - import static org.audiveris.omr.run.Orientation.*; - import org.audiveris.omr.run.Run; import org.audiveris.omr.sheet.Scale; +import org.audiveris.omr.sheet.Scale.Fraction; import org.audiveris.omr.sheet.Sheet; import org.audiveris.omr.sheet.Skew; import org.audiveris.omr.sheet.SystemInfo; @@ -59,23 +57,15 @@ import org.audiveris.omr.sig.relation.Exclusion.Cause; import org.audiveris.omr.sig.relation.HeadStemRelation; import org.audiveris.omr.sig.relation.Relation; -import org.audiveris.omr.sig.relation.StemPortion; - -import static org.audiveris.omr.sig.relation.StemPortion.*; - import org.audiveris.omr.ui.symbol.MusicFont; import org.audiveris.omr.ui.symbol.ShapeSymbol; import org.audiveris.omr.util.Corner; import org.audiveris.omr.util.Dumping; import org.audiveris.omr.util.HorizontalSide; - import static org.audiveris.omr.util.HorizontalSide.*; - import org.audiveris.omr.util.Navigable; import org.audiveris.omr.util.StopWatch; - import static org.audiveris.omr.util.VerticalSide.*; - import org.audiveris.omr.util.Wrapper; import org.slf4j.Logger; @@ -115,7 +105,8 @@ * A stem can aggregate several items: stem seeds (built from long vertical sticks) and chunks * (built from suitable sections found in the corner), all being separated by vertical gaps. * Up to which point should we try to accept vertical gaps and increase a stem length starting from - * a head?

                                                                                      + * a head? + *
                                                                                        *
                                                                                      1. If there is a beam in the corner, try a stem that at least reaches the beam.
                                                                                      2. *
                                                                                      3. Use a similar approach for the case of flag (if the flag is in the right direction), except * that we don't have identified flags yet!
                                                                                      4. @@ -146,15 +137,11 @@ */ public class StemsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - StemsBuilder.class); + private static final Logger logger = LoggerFactory.getLogger(StemsBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** The dedicated system. */ @Navigable(false) private final SystemInfo system; @@ -183,14 +170,13 @@ public class StemsBuilder private List systemBeams; /** Stems interpretations for this system. */ - private List systemStems = new ArrayList(); + private final List systemStems = new ArrayList<>(); private VerticalsBuilder verticalsBuilder; /** Constructor for stem compound. */ private final CompoundFactory.CompoundConstructor stemConstructor; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new StemsBuilder object. * @@ -209,17 +195,9 @@ public StemsBuilder (SystemInfo system) ShapeSymbol symbol = Shape.NOTEHEAD_BLACK.getSymbol(); headSymbolDim = symbol.getDimension(MusicFont.getHeadFont(scale, scale.getInterline())); - stemConstructor = new CompoundConstructor() - { - @Override - public SectionCompound newInstance () - { - return new StraightFilament(scale.getInterline()); - } - }; + stemConstructor = new StraightFilament.Constructor(scale.getInterline()); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // linkCueBeams // //--------------// @@ -251,6 +229,7 @@ public void linkCueBeams (Inter head, /** * Link stems to suitable heads and beams in the system. * Retrieval is driven by heads (since a stem always needs a head), and can use beams. + * *
                                                                                              * Synopsis:
                                                                                              *
                                                                                        @@ -297,7 +276,8 @@ public void linkStems ()
                                                                                                 Collections.sort(systemBeams, Inters.byAbscissa);
                                                                                         
                                                                                                 // The abscissa-sorted head interpretations for this system
                                                                                        -        final List systemHeads = sig.inters(ShapeSet.getStemTemplateNotes(system.getSheet()));
                                                                                        +        final List systemHeads = sig.inters(
                                                                                        +                ShapeSet.getStemTemplateNotes(system.getSheet()));
                                                                                                 Collections.sort(systemHeads, Inters.byAbscissa);
                                                                                         
                                                                                                 // First phase, look around heads for stems (and beams if any)
                                                                                        @@ -331,7 +311,7 @@ public void linkStems ()
                                                                                                 watch.start("checkHeadStems");
                                                                                         
                                                                                                 for (Inter head : systemHeads) {
                                                                                        -            checkHeadStems(head);
                                                                                        +            checkHeadStems((HeadInter) head);
                                                                                                 }
                                                                                         
                                                                                                 // Discard heads with no stem link
                                                                                        @@ -352,7 +332,7 @@ private void boostBeamSides (Inter beam)
                                                                                                     return;
                                                                                                 }
                                                                                         
                                                                                        -        List rels = new ArrayList();
                                                                                        +        List rels = new ArrayList<>();
                                                                                         
                                                                                                 for (Relation rel : sig.edgesOf(beam)) {
                                                                                                     if (rel instanceof BeamStemRelation) {
                                                                                        @@ -383,7 +363,7 @@ private void checkBeamStems (Inter beam)
                                                                                                     logger.info("VIP checkBeamStems? {}");
                                                                                                 }
                                                                                         
                                                                                        -        List rels = new ArrayList();
                                                                                        +        List rels = new ArrayList<>();
                                                                                         
                                                                                                 for (Relation rel : sig.edgesOf(beam)) {
                                                                                                     if (rel instanceof BeamStemRelation) {
                                                                                        @@ -392,19 +372,16 @@ private void checkBeamStems (Inter beam)
                                                                                                 }
                                                                                         
                                                                                                 // Sort by abscissae
                                                                                        -        Collections.sort(
                                                                                        -                rels,
                                                                                        -                new Comparator()
                                                                                        -        {
                                                                                        -            @Override
                                                                                        -            public int compare (BeamStemRelation o1,
                                                                                        -                                BeamStemRelation o2)
                                                                                        -            {
                                                                                        -                return Double.compare(
                                                                                        -                        o1.getExtensionPoint().getX(),
                                                                                        -                        o2.getExtensionPoint().getX());
                                                                                        -            }
                                                                                        -        });
                                                                                        +        Collections.sort(rels, new Comparator()
                                                                                        +                 {
                                                                                        +                     @Override
                                                                                        +                     public int compare (BeamStemRelation o1,
                                                                                        +                                         BeamStemRelation o2)
                                                                                        +                     {
                                                                                        +                         return Double.compare(o1.getExtensionPoint().getX(), o2.getExtensionPoint()
                                                                                        +                                               .getX());
                                                                                        +                     }
                                                                                        +                 });
                                                                                         
                                                                                                 final int size = rels.size();
                                                                                         
                                                                                        @@ -442,10 +419,10 @@ public int compare (BeamStemRelation o1,
                                                                                              *
                                                                                              * @param head the note head to check
                                                                                              */
                                                                                        -    private void checkHeadStems (Inter head)
                                                                                        +    private void checkHeadStems (HeadInter head)
                                                                                             {
                                                                                                 // Retrieve all stems connected to this head
                                                                                        -        List allStems = new ArrayList();
                                                                                        +        List allStems = new ArrayList<>();
                                                                                         
                                                                                                 for (Relation rel : sig.getRelations(head, HeadStemRelation.class)) {
                                                                                                     allStems.add(sig.getEdgeTarget(rel));
                                                                                        @@ -529,65 +506,17 @@ private void performMutualExclusions ()
                                                                                                 }
                                                                                             }
                                                                                         
                                                                                        -    //~ Inner Classes ------------------------------------------------------------------------------
                                                                                        -    //-----------//
                                                                                        -    // Constants //
                                                                                        -    //-----------//
                                                                                        -    private static final class Constants
                                                                                        -            extends ConstantSet
                                                                                        +    //---------------------//
                                                                                        +    // getMinStemExtension //
                                                                                        +    //---------------------//
                                                                                        +    /**
                                                                                        +     * Report the minimum extension that goes from last head to end of stem.
                                                                                        +     *
                                                                                        +     * @return the defined constant
                                                                                        +     */
                                                                                        +    public static Fraction getMinStemExtension ()
                                                                                             {
                                                                                        -        //~ Instance fields ------------------------------------------------------------------------
                                                                                        -
                                                                                        -        private final Constant.Boolean printWatch = new Constant.Boolean(
                                                                                        -                false,
                                                                                        -                "Should we print out the stop watch?");
                                                                                        -
                                                                                        -        private final Scale.Fraction vicinityMargin = new Scale.Fraction(
                                                                                        -                1.0,
                                                                                        -                "Rough abscissa margin when looking for neighbors");
                                                                                        -
                                                                                        -        private final Constant.Double slopeMargin = new Constant.Double(
                                                                                        -                "tangent",
                                                                                        -                0.02,
                                                                                        -                "Margin around slope to define corner lookup area");
                                                                                        -
                                                                                        -        private final Scale.Fraction minHeadSectionContribution = new Scale.Fraction(
                                                                                        -                0.2,
                                                                                        -                "Minimum stem contribution for a section near head");
                                                                                        -
                                                                                        -        private final Scale.Fraction minStemExtension = new Scale.Fraction(
                                                                                        -                0.8,
                                                                                        -                "Minimum vertical distance from head to end of stem");
                                                                                        -
                                                                                        -        private final Scale.Fraction minHeadBeamDistance = new Scale.Fraction(
                                                                                        -                0.125,
                                                                                        -                "Minimum vertical distance between head and beam");
                                                                                        -
                                                                                        -        private final Scale.Fraction maxBeamDistance = new Scale.Fraction(
                                                                                        -                1.5,
                                                                                        -                "Default maximum vertical distance between two consecutive grouped beams");
                                                                                        -
                                                                                        -        private final Scale.Fraction minBeamStemsGap = new Scale.Fraction(
                                                                                        -                1.0,
                                                                                        -                "Minimum x gap between two stems on the same beam");
                                                                                        -
                                                                                        -        private final Constant.Ratio maxSeedJitter = new Constant.Ratio(
                                                                                        -                2.0,
                                                                                        -                "Maximum distance from stem seed to theoretical line,"
                                                                                        -                + " as ratio of typical stem width");
                                                                                        -
                                                                                        -        private final Constant.Ratio maxSectionJitter = new Constant.Ratio(
                                                                                        -                1.0,
                                                                                        -                "Maximum distance from section center to target line,"
                                                                                        -                + " as ratio of typical stem width");
                                                                                        -
                                                                                        -        private final Scale.Fraction yGapTiny = new Scale.Fraction(
                                                                                        -                0.1,
                                                                                        -                "Maximum vertical tiny gap between stem & head");
                                                                                        -
                                                                                        -        private final Constant.Ratio sideStemBoost = new Constant.Ratio(
                                                                                        -                0.5,
                                                                                        -                "How much do we boost beam side stems");
                                                                                        +        return constants.minStemExtension;
                                                                                             }
                                                                                         
                                                                                             //------------//
                                                                                        @@ -599,7 +528,6 @@ private static final class Constants
                                                                                              */
                                                                                             private class HeadLinker
                                                                                             {
                                                                                        -        //~ Instance fields ------------------------------------------------------------------------
                                                                                         
                                                                                                 /** The head interpretation being processed. */
                                                                                                 private final Inter head;
                                                                                        @@ -616,14 +544,12 @@ private class HeadLinker
                                                                                                 /** All stems interpretations in head vicinity. */
                                                                                                 private List neighborStems;
                                                                                         
                                                                                        -        //~ Constructors ---------------------------------------------------------------------------
                                                                                        -        public HeadLinker (Inter head)
                                                                                        +        HeadLinker (Inter head)
                                                                                                 {
                                                                                                     this.head = head;
                                                                                                     headBox = head.getBounds();
                                                                                                 }
                                                                                         
                                                                                        -        //~ Methods --------------------------------------------------------------------------------
                                                                                                 //---------------//
                                                                                                 // linkCueCorner //
                                                                                                 //---------------//
                                                                                        @@ -715,7 +641,6 @@ private Set getNeighboringSeeds ()
                                                                                                     return Glyphs.intersectedGlyphs(systemSeeds, fatBox);
                                                                                                 }
                                                                                         
                                                                                        -        //~ Inner Classes --------------------------------------------------------------------------
                                                                                                 //
                                                                                                 //--------------//
                                                                                                 // CornerLinker //
                                                                                        @@ -725,7 +650,6 @@ private Set getNeighboringSeeds ()
                                                                                                  */
                                                                                                 protected class CornerLinker
                                                                                                 {
                                                                                        -            //~ Instance fields --------------------------------------------------------------------
                                                                                         
                                                                                                     /** The corner being processed. */
                                                                                                     private final Corner corner;
                                                                                        @@ -754,8 +678,7 @@ protected class CornerLinker
                                                                                                     /** Ordinate range between refPt & limit. */
                                                                                                     private Rectangle yRange;
                                                                                         
                                                                                        -            //~ Constructors -----------------------------------------------------------------------
                                                                                        -            public CornerLinker (Corner corner)
                                                                                        +            CornerLinker (Corner corner)
                                                                                                     {
                                                                                                         this.corner = corner;
                                                                                         
                                                                                        @@ -764,7 +687,6 @@ public CornerLinker (Corner corner)
                                                                                                         refPt = getReferencePoint();
                                                                                                     }
                                                                                         
                                                                                        -            //~ Methods ----------------------------------------------------------------------------
                                                                                                     //------//
                                                                                                     // link //
                                                                                                     //------//
                                                                                        @@ -806,11 +728,11 @@ public void link ()
                                                                                         
                                                                                                         // Look for additional chunks built out of sections found.
                                                                                                         // Assign special role to a fat section part of head (if any)
                                                                                        -                Wrapper
                                                                                        fatHeadSection = new Wrapper
                                                                                        (null); + Wrapper
                                                                                        fatHeadSection = new Wrapper<>(null); List chunks = lookupChunks(fatHeadSection); // Aggregate seeds and chunks up to the limit - List items = new ArrayList(seeds); + List items = new ArrayList<>(seeds); if (!chunks.isEmpty()) { items.addAll(chunks); @@ -946,8 +868,9 @@ private BeamStemRelation connectBeamStem (AbstractBeamInter beam, double toRight = beamLimit.getX2() - crossPt.getX(); final double xGap; - if (beam instanceof BeamInter - && (Math.min(toLeft, toRight) > params.maxBeamInDx)) { + if (beam instanceof BeamInter && (Math.min( + toLeft, + toRight) > params.maxBeamInDx)) { // It's a beam center connection bRel.setBeamPortion(BeamPortion.CENTER); xGap = 0; @@ -1040,7 +963,8 @@ private HeadStemRelation connectHeadStem (Section headSection, hRel.setExtensionPoint( new Point2D.Double( xAnchor, - (yDir > 0) ? headBox.y : ((headBox.y + headBox.height) - 1))); + (yDir > 0) ? headBox.y + : ((headBox.y + headBox.height) - 1))); sig.addEdge(head, stemInter, hRel); if (stemInter.isVip()) { @@ -1265,8 +1189,8 @@ private Rectangle getRunBox (Section section, */ private StemInter getStemInter (Glyph glyph) { - for (ListIterator it = systemStems.listIterator(systemStems.size()); - it.hasPrevious();) { + for (ListIterator it = systemStems.listIterator(systemStems.size()); it + .hasPrevious();) { StemInter inter = it.previous(); if (inter.getGlyph() == glyph) { @@ -1294,7 +1218,7 @@ private Line2D getTargetLine () Line2D theory = new Line2D.Double(refPt, targetPt); // Look for stems seeds - seeds = new ArrayList(Glyphs.intersectedGlyphs(neighborSeeds, area)); + seeds = new ArrayList<>(Glyphs.intersectedGlyphs(neighborSeeds, area)); if (!seeds.isEmpty()) { // Purge seeds that do not contribute to ordinate range @@ -1318,7 +1242,7 @@ private Line2D getTargetLine () } // In case of overlap, simply keep the most contributive - List kept = new ArrayList(); + List kept = new ArrayList<>(); sortByContrib(seeds); StemLoop: @@ -1406,7 +1330,7 @@ private List includeItems (List items, Section fatHeadSection) { double lastY = refY; // Current end of stem - List allStemInters = new ArrayList(); + List allStemInters = new ArrayList<>(); for (int i = 0; i < items.size(); i++) { Glyph item = items.get(i); @@ -1418,8 +1342,7 @@ private List includeItems (List items, } // Is gap with previous item acceptable? - final int itemY = (yDir > 0) ? itemBox.y : ((itemBox.y + itemBox.height) - - 1); + final int itemY = (yDir > 0) ? itemBox.y : ((itemBox.y + itemBox.height) - 1); final double itemStart = (yDir > 0) ? Math.max(itemY, refY) : Math.min(itemY, refY); final double yGap = yDir * (itemStart - lastY); @@ -1479,7 +1402,10 @@ private void linkBeamsAndStems (List> beamGroups, // Extend stem connection till end of current beam group, if relevant if (firstBeam.isGood() && (group.size() > 1)) { for (AbstractBeamInter next : group.subList(1, group.size())) { - if (sig.getRelation(next, stem, BeamStemRelation.class) == null) { + if (sig.getRelation( + next, + stem, + BeamStemRelation.class) == null) { BeamStemRelation r = new BeamStemRelation(); r.setBeamPortion(rel.getBeamPortion()); @@ -1487,7 +1413,8 @@ private void linkBeamsAndStems (List> beamGroups, r.setExtensionPoint( new Point2D.Double( crossPt.getX(), - crossPt.getY() + (yDir * (next.getHeight() - 1)))); + crossPt.getY() + (yDir * (next.getHeight() + - 1)))); r.setGrade(rel.getGrade()); sig.addEdge(next, stem, r); } @@ -1520,25 +1447,23 @@ private List> lookupBeamGroups (List candidates) } // Sort candidates by distance from head - Collections.sort( - candidates, - new Comparator() - { - @Override - public int compare (Inter i1, - Inter i2) - { - AbstractBeamInter b1 = (AbstractBeamInter) i1; - AbstractBeamInter b2 = (AbstractBeamInter) i2; - - return Double.compare( - yDir * (getTargetPt(getLimit(b1)).getY() - refPt.getY()), - yDir * (getTargetPt(getLimit(b2)).getY() - refPt.getY())); - } - }); + Collections.sort(candidates, new Comparator() + { + @Override + public int compare (Inter i1, + Inter i2) + { + AbstractBeamInter b1 = (AbstractBeamInter) i1; + AbstractBeamInter b2 = (AbstractBeamInter) i2; + + return Double.compare( + yDir * (getTargetPt(getLimit(b1)).getY() - refPt.getY()), + yDir * (getTargetPt(getLimit(b2)).getY() - refPt.getY())); + } + }); // Build the list of (groups of) beams - List> groups = new ArrayList>(); + List> groups = new ArrayList<>(); List group = null; AbstractBeamInter prevBeam = null; boolean groupIsGood = false; @@ -1561,7 +1486,7 @@ public int compare (Inter i1, group.add(beam); } else { // Start a brand new group - groups.add(group = new ArrayList()); + groups.add(group = new ArrayList<>()); group.add(beam); groupIsGood = beam.isGood(); } @@ -1593,7 +1518,7 @@ private List lookupChunks (Wrapper
                                                                                        fatHeadSection) stemConstructor); // Remove useless glyphs and put wide glyphs apart - List wides = new ArrayList(); + List wides = new ArrayList<>(); for (Iterator it = chunks.iterator(); it.hasNext();) { SectionCompound chunk = it.next(); @@ -1617,7 +1542,7 @@ private List lookupChunks (Wrapper
                                                                                        fatHeadSection) // For too wide chunks we just keep the biggest section if (!wides.isEmpty()) { for (SectionCompound wide : wides) { - List
                                                                                        members = new ArrayList
                                                                                        (wide.getMembers()); + List
                                                                                        members = new ArrayList<>(wide.getMembers()); Collections.sort(members, Section.reverseWeightComparator); SectionCompound compound = CompoundFactory.buildCompound( @@ -1628,7 +1553,7 @@ private List lookupChunks (Wrapper
                                                                                        fatHeadSection) } // Convert section compounds to glyphs - List glyphs = new ArrayList(chunks.size()); + List glyphs = new ArrayList<>(chunks.size()); for (SectionCompound chunk : chunks) { glyphs.add(chunk.toGlyph(null)); @@ -1656,8 +1581,8 @@ private List
                                                                                        lookupSections (Wrapper
                                                                                        fatHeadSection) final Line2D hLine = (corner.hSide == LEFT) ? new Line2D.Double(outPt, inPt) : new Line2D.Double(inPt, outPt); final int refY = (int) Math.rint(refPt.getY()); - final List
                                                                                        sections = new ArrayList
                                                                                        (); - final List
                                                                                        headSections = new ArrayList
                                                                                        (); + final List
                                                                                        sections = new ArrayList<>(); + final List
                                                                                        headSections = new ArrayList<>(); // Widen head box with max stem width final Rectangle wideHeadBox = head.getBounds(); @@ -1677,15 +1602,16 @@ private List
                                                                                        lookupSections (Wrapper
                                                                                        fatHeadSection) // Check intersection at least if (!area.intersects(sectBox)) { - continue SectionLoop; + continue; } // Containment is mandatory except for a head section // (a section that intersects head glyph) if (!area.contains(sectBox)) { - if (!sectBox.intersects(wideHeadBox) - || !GeoUtil.yEmbraces(sectBox, refY)) { - continue SectionLoop; + if (!sectBox.intersects(wideHeadBox) || !GeoUtil.yEmbraces( + sectBox, + refY)) { + continue; } // Section is likely to be part of head itself. @@ -1717,7 +1643,7 @@ private List
                                                                                        lookupSections (Wrapper
                                                                                        fatHeadSection) } } - continue SectionLoop; + continue; } // A headSection must provide significant contribution @@ -1727,7 +1653,7 @@ private List
                                                                                        lookupSections (Wrapper
                                                                                        fatHeadSection) if (sectContrib < params.minHeadSectionContribution) { logger.debug("Discarding tiny headSection {}", section); - continue SectionLoop; + continue; } headSections.add(section); @@ -1735,7 +1661,7 @@ private List
                                                                                        lookupSections (Wrapper
                                                                                        fatHeadSection) // Contraint section width <= stem width if (sectBox.width > params.maxStemThickness) { - continue SectionLoop; + continue; } // A section which intersects an existing seed is useless @@ -1812,21 +1738,19 @@ private List
                                                                                        lookupSections (Wrapper
                                                                                        fatHeadSection) */ private void sortByContrib (List glyphs) { - Collections.sort( - glyphs, - new Comparator() - { - @Override - public int compare (Glyph o1, - Glyph o2) - { - // Sort by decreasing contribution - int c1 = getContrib(o1.getBounds()); - int c2 = getContrib(o2.getBounds()); - - return Integer.signum(c2 - c1); - } - }); + Collections.sort(glyphs, new Comparator() + { + @Override + public int compare (Glyph o1, + Glyph o2) + { + // Sort by decreasing contribution + int c1 = getContrib(o1.getBounds()); + int c2 = getContrib(o2.getBounds()); + + return Integer.signum(c2 - c1); + } + }); } //----------------// @@ -1842,91 +1766,6 @@ private void sortByDistance (List glyphs) } } - //------------// - // Parameters // - //------------// - /** - * Class {@code Parameters} gathers all pre-scaled constants. - */ - private static class Parameters - { - //~ Instance fields ------------------------------------------------------------------------ - - final double slopeMargin; - - final int maxHeadOutDx; - - final int maxBeamInDx; - - final int maxHeadInDx; - - final int vicinityMargin; - - final int maxStemHeadGapY; - - final int maxYGap; - - final int maxStemThickness; - - final int minChunkWeight; - - final int minHeadSectionContribution; - - final int minStemExtension; - - final int minHeadBeamDistance; - - final int minBeamStemsGap; - - final double maxSeedJitter; - - final double maxSectionJitter; - - final int maxBeamDistance; - - //~ Constructors --------------------------------------------------------------------------- - /** - * Creates a new Parameters object. - * - * @param scale the scaling factor - */ - public Parameters (SystemInfo system, - Scale scale) - { - slopeMargin = constants.slopeMargin.getValue(); - maxHeadOutDx = scale.toPixels(HeadStemRelation.getXOutGapMaximum(false)); - maxBeamInDx = scale.toPixels(BeamStemRelation.getXInGapMaximum(false)); - maxHeadInDx = scale.toPixels(HeadStemRelation.getXInGapMaximum(false)); - vicinityMargin = scale.toPixels(constants.vicinityMargin); - maxStemHeadGapY = scale.toPixels(HeadStemRelation.getYGapMaximum(false)); - maxYGap = scale.toPixels(VerticalsBuilder.getMaxYGap()); - minHeadSectionContribution = scale.toPixels(constants.minHeadSectionContribution); - minStemExtension = scale.toPixels(constants.minStemExtension); - minHeadBeamDistance = scale.toPixels(constants.minHeadBeamDistance); - minBeamStemsGap = scale.toPixels(constants.minBeamStemsGap); - - final int stemThickness = scale.getMaxStem(); - maxStemThickness = stemThickness; - maxSeedJitter = constants.maxSeedJitter.getValue() * stemThickness; - maxSectionJitter = constants.maxSectionJitter.getValue() * stemThickness; - - minChunkWeight = scale.getStemThickness(); // TODO: check this - - Double beamDistance = scale.getBeamMeanDistance(); - - if (beamDistance != null) { - maxBeamDistance = (int) Math.ceil( - beamDistance + (2 * scale.getBeamSigmaDistance())); - } else { - maxBeamDistance = scale.toPixels(constants.maxBeamDistance); - } - - if (logger.isDebugEnabled()) { - new Dumping().dump(this); - } - } - } - //--------------// // ShareChecker // //--------------// @@ -1935,19 +1774,16 @@ public Parameters (SystemInfo system, */ private class ShareChecker { - //~ Instance fields ------------------------------------------------------------------------ - private final Inter head; + private final HeadInter head; - private final List rels = new ArrayList(); + private final List rels = new ArrayList<>(); - //~ Constructors --------------------------------------------------------------------------- - public ShareChecker (Inter head) + ShareChecker (HeadInter head) { this.head = head; } - //~ Methods -------------------------------------------------------------------------------- public void check (List stems) { // Retrieve stem relations @@ -2063,43 +1899,175 @@ private void discardWeakerStem () /** * Check whether this is the canonical "shared" configuration. - * (STEM_TOP on head LEFT side and STEM_BOTTOM on head RIGHT side). - *
                                                                                        -         *    |
                                                                                        -         *  +O+
                                                                                        -         *  |
                                                                                        -         * 
                                                                                        + *

                                                                                        + * For this test, we cannot trust stem extensions and must stay with physical stem limits. * * @return true if canonical */ private boolean isCanonicalShare () { - boolean left = false; - boolean right = false; + StemInter leftStem = null; + StemInter rightStem = null; for (HeadStemRelation rel : rels) { - StemInter stem = (StemInter) sig.getOppositeInter(head, rel); - - // For this test, we cannot trust stem extensions and must stay with physical stem - Line2D stemLine = new Line2D.Double(stem.getTop(), stem.getBottom()); - StemPortion portion = rel.getStemPortion(head, stemLine, scale); - HorizontalSide side = rel.getHeadSide(); - double yGap = rel.getDy(); + if (rel.getDy() > constants.yGapTiny.getValue()) { + return false; + } - if (yGap <= constants.yGapTiny.getValue()) { - if (portion == STEM_TOP) { - if (side == LEFT) { - left = true; - } - } else if (portion == STEM_BOTTOM) { - if (side == RIGHT) { - right = true; - } - } + if (rel.getHeadSide() == LEFT) { + leftStem = (StemInter) sig.getOppositeInter(head, rel); + } else { + rightStem = (StemInter) sig.getOppositeInter(head, rel); } } - return left && right; + if (leftStem == null || rightStem == null) { + return false; + } + + return HeadStemRelation.isCanonicalShare(leftStem, head, rightStem); + } + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean printWatch = new Constant.Boolean( + false, + "Should we print out the stop watch?"); + + private final Scale.Fraction vicinityMargin = new Scale.Fraction( + 1.0, + "Rough abscissa margin when looking for neighbors"); + + private final Constant.Double slopeMargin = new Constant.Double( + "tangent", + 0.02, + "Margin around slope to define corner lookup area"); + + private final Scale.Fraction minHeadSectionContribution = new Scale.Fraction( + 0.2, + "Minimum stem contribution for a section near head"); + + private final Scale.Fraction minStemExtension = new Scale.Fraction( + 0.8, + "Minimum vertical distance from head to end of stem"); + + private final Scale.Fraction minHeadBeamDistance = new Scale.Fraction( + 0.125, + "Minimum vertical distance between head and beam"); + + private final Scale.Fraction maxBeamDistance = new Scale.Fraction( + 1.5, + "Default maximum vertical distance between two consecutive grouped beams"); + + private final Scale.Fraction minBeamStemsGap = new Scale.Fraction( + 1.0, + "Minimum x gap between two stems on the same beam"); + + private final Constant.Ratio maxSeedJitter = new Constant.Ratio( + 2.0, + "Maximum distance from stem seed to theoretical line," + + " as ratio of typical stem width"); + + private final Constant.Ratio maxSectionJitter = new Constant.Ratio( + 1.0, + "Maximum distance from section center to target line," + + " as ratio of typical stem width"); + + private final Scale.Fraction yGapTiny = new Scale.Fraction( + 0.1, + "Maximum vertical tiny gap between stem & head"); + + private final Constant.Ratio sideStemBoost = new Constant.Ratio( + 0.5, + "How much do we boost beam side stems"); + } + + //------------// + // Parameters // + //------------// + /** + * Class {@code Parameters} gathers all pre-scaled constants. + */ + private static class Parameters + { + + final double slopeMargin; + + final int maxHeadOutDx; + + final int maxBeamInDx; + + final int maxHeadInDx; + + final int vicinityMargin; + + final int maxStemHeadGapY; + + final int maxYGap; + + final int maxStemThickness; + + final int minChunkWeight; + + final int minHeadSectionContribution; + + final int minStemExtension; + + final int minHeadBeamDistance; + + final int minBeamStemsGap; + + final double maxSeedJitter; + + final double maxSectionJitter; + + final int maxBeamDistance; + + /** + * Creates a new Parameters object. + * + * @param scale the scaling factor + */ + Parameters (SystemInfo system, + Scale scale) + { + slopeMargin = constants.slopeMargin.getValue(); + maxHeadOutDx = scale.toPixels(HeadStemRelation.getXOutGapMaximum(false)); + maxBeamInDx = scale.toPixels(BeamStemRelation.getXInGapMaximum(false)); + maxHeadInDx = scale.toPixels(HeadStemRelation.getXInGapMaximum(false)); + vicinityMargin = scale.toPixels(constants.vicinityMargin); + maxStemHeadGapY = scale.toPixels(HeadStemRelation.getYGapMaximum(false)); + maxYGap = scale.toPixels(VerticalsBuilder.getMaxYGap()); + minHeadSectionContribution = scale.toPixels(constants.minHeadSectionContribution); + minStemExtension = scale.toPixels(getMinStemExtension()); + minHeadBeamDistance = scale.toPixels(constants.minHeadBeamDistance); + minBeamStemsGap = scale.toPixels(constants.minBeamStemsGap); + + final int stemThickness = scale.getMaxStem(); + maxStemThickness = stemThickness; + maxSeedJitter = constants.maxSeedJitter.getValue() * stemThickness; + maxSectionJitter = constants.maxSectionJitter.getValue() * stemThickness; + + minChunkWeight = scale.getStemThickness(); // TODO: check this + + Double beamDistance = scale.getBeamMeanDistance(); + + if (beamDistance != null) { + maxBeamDistance = (int) Math.ceil( + beamDistance + (2 * scale.getBeamSigmaDistance())); + } else { + maxBeamDistance = scale.toPixels(constants.maxBeamDistance); + } + + if (logger.isDebugEnabled()) { + new Dumping().dump(this); + } } } } diff --git a/src/main/org/audiveris/omr/sheet/stem/StemsStep.java b/src/main/org/audiveris/omr/sheet/stem/StemsStep.java index 93c00f460..da282a9c6 100644 --- a/src/main/org/audiveris/omr/sheet/stem/StemsStep.java +++ b/src/main/org/audiveris/omr/sheet/stem/StemsStep.java @@ -37,11 +37,9 @@ public class StemsStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(StemsStep.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new StemsStep object. */ @@ -49,7 +47,6 @@ public StemsStep () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // doSystem // //----------// diff --git a/src/main/org/audiveris/omr/sheet/stem/VerticalsBuilder.java b/src/main/org/audiveris/omr/sheet/stem/VerticalsBuilder.java index a12dee22a..739c68845 100644 --- a/src/main/org/audiveris/omr/sheet/stem/VerticalsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/stem/VerticalsBuilder.java @@ -22,6 +22,7 @@ package org.audiveris.omr.sheet.stem; import ij.process.ByteProcessor; +import java.awt.Point; import org.audiveris.omr.check.Check; import org.audiveris.omr.check.CheckBoard; @@ -30,17 +31,15 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.GlyphGroup; import org.audiveris.omr.glyph.GlyphIndex; import org.audiveris.omr.glyph.NearLine; -import org.audiveris.omr.glyph.GlyphGroup; import org.audiveris.omr.glyph.dynamic.StickFactory; import org.audiveris.omr.glyph.dynamic.StraightFilament; import org.audiveris.omr.lag.Section; import org.audiveris.omr.math.LineUtil; import org.audiveris.omr.run.Orientation; - import static org.audiveris.omr.run.Orientation.*; - import org.audiveris.omr.sheet.Picture; import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sheet.Sheet; @@ -58,7 +57,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.Line2D; import java.awt.geom.Point2D; @@ -76,7 +74,6 @@ */ public class VerticalsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -106,7 +103,6 @@ public class VerticalsBuilder /** Seed is not straight enough. */ private static final Failure NON_STRAIGHT = new Failure("Stem-NonStraight"); - //~ Instance fields ---------------------------------------------------------------------------- /** The system to process. */ private final SystemInfo system; @@ -122,7 +118,6 @@ public class VerticalsBuilder /** Suite of checks for a vertical seed. */ private final SeedCheckSuite suite = new SeedCheckSuite(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new VerticalsBuilder object. * @@ -138,7 +133,6 @@ public VerticalsBuilder (SystemInfo system) pixelFilter = sheet.getPicture().getSource(Picture.SourceKey.NO_STAFF); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // addCheckBoard // //---------------// @@ -184,17 +178,14 @@ public GradeImpacts checkStem (NearLine stick) return suite.getImpacts(new StickContext(stick)); } - //------------// - // getMaxYGap // - //------------// - public static Scale.Fraction getMaxYGap () - { - return constants.gapHigh; - } - //----------// // getSuite // //----------// + /** + * Report the check suite for vertical candidates. + * + * @return the CheckSuite for candidates + */ public CheckSuite getSuite () { return suite; @@ -269,7 +260,7 @@ private List retrieveCandidates () { // Select suitable (vertical) sections // Since we are looking for major seeds, we'll use only vertical sections - List

                                                                                        vSections = new ArrayList
                                                                                        (); + List
                                                                                        vSections = new ArrayList<>(); for (Section section : system.getVerticalSections()) { // Check section is within system left and right boundaries @@ -281,7 +272,7 @@ private List retrieveCandidates () } // Horizontal sections (to contribute to stickers) - List
                                                                                        hSections = new ArrayList
                                                                                        (); + List
                                                                                        hSections = new ArrayList<>(); for (Section section : system.getHorizontalSections()) { // Limit width to 1 pixel @@ -307,7 +298,19 @@ private List retrieveCandidates () return factory.retrieveSticks(vSections, hSections); } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------// + // getMaxYGap // + //------------// + /** + * Report the maximum vertical gap between chunks. + * + * @return the maximum acceptable vertical gap between chunks; + */ + public static Scale.Fraction getMaxYGap () + { + return constants.gapHigh; + } + //------------// // BlackCheck // //------------// @@ -317,7 +320,6 @@ private List retrieveCandidates () private class BlackCheck extends Check { - //~ Constructors --------------------------------------------------------------------------- protected BlackCheck () { @@ -330,7 +332,6 @@ protected BlackCheck () TOO_SHORT); } - //~ Methods -------------------------------------------------------------------------------- // Retrieve the length data @Override protected double getValue (StickContext context) @@ -350,7 +351,6 @@ protected double getValue (StickContext context) private class CleanCheck extends Check { - //~ Constructors --------------------------------------------------------------------------- protected CleanCheck () { @@ -363,7 +363,6 @@ protected CleanCheck () TOO_HIGH_ADJACENCY); } - //~ Methods -------------------------------------------------------------------------------- @Override protected double getValue (StickContext context) { @@ -377,9 +376,8 @@ protected double getValue (StickContext context) // Sanity check double invSlope = LineUtil.getInvertedSlope(start, stop); - if (Double.isNaN(invSlope) - || Double.isInfinite(invSlope) - || (Math.abs(invSlope) > 0.5)) { + if (Double.isNaN(invSlope) || Double.isInfinite(invSlope) || (Math.abs( + invSlope) > 0.5)) { if (stick.isVip()) { logger.info("VIP too far from vertical {}", stick); } @@ -501,69 +499,6 @@ protected double getValue (StickContext context) } } - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Ratio minSideRatio = new Constant.Ratio( - 0.4, - "Minimum ratio of filament length to be actually enlarged"); - - private final Scale.Fraction minCoreSectionLength = new Scale.Fraction( - 1.5, - "Minimum length for core sections"); - - private final Scale.Fraction beltMarginDx = new Scale.Fraction( - 0.15, - "Horizontal belt margin checked around stem"); - - private final Check.Grade minCheckResult = new Check.Grade( - 0.2, - "Minimum result for suite of check"); - - private final Check.Grade goodCheckResult = new Check.Grade( - 0.5, - "Good result for suite of check"); - - private final Scale.Fraction blackHigh = new Scale.Fraction( - 2.5, - "High minimum length for a stem"); - - private final Scale.Fraction blackLow = new Scale.Fraction( - 1.25, - "Low minimum length for a stem"); - - private final Scale.Fraction cleanHigh = new Scale.Fraction( - 2.0, - "High minimum clean length for a stem"); - - private final Scale.Fraction cleanLow = new Scale.Fraction( - 0.5, - "Low minimum clean length for a stem"); - - private final Scale.Fraction gapHigh = new Scale.Fraction( - 0.3, - "Maximum vertical gap between stem segments"); - - private final Constant.Double slopeHigh = new Constant.Double( - "tangent", - 0.06, - "Maximum difference with global slope"); - - private final Scale.Fraction straightHigh = new Scale.Fraction( - 0.2, - "High maximum distance to average stem line"); - - private final Constant.Double maxCoTangentForCheck = new Constant.Double( - "cotangent", - 0.1, - "Maximum cotangent for visual check"); - } - //----------// // GapCheck // //----------// @@ -573,7 +508,6 @@ private static final class Constants private class GapCheck extends Check { - //~ Constructors --------------------------------------------------------------------------- protected GapCheck () { @@ -586,7 +520,6 @@ protected GapCheck () TOO_HOLLOW); } - //~ Methods -------------------------------------------------------------------------------- // Retrieve the length data @Override protected double getValue (StickContext context) @@ -604,12 +537,11 @@ protected double getValue (StickContext context) private class SeedCheckSuite extends CheckSuite { - //~ Constructors --------------------------------------------------------------------------- /** * Create a new instance */ - public SeedCheckSuite () + SeedCheckSuite () { super( "Seed", @@ -627,7 +559,6 @@ public SeedCheckSuite () } } - //~ Methods -------------------------------------------------------------------------------- @Override protected void dumpSpecific (StringBuilder sb) { @@ -645,7 +576,6 @@ protected void dumpSpecific (StringBuilder sb) private class SlopeCheck extends Check { - //~ Constructors --------------------------------------------------------------------------- protected SlopeCheck () { @@ -658,7 +588,6 @@ protected SlopeCheck () NON_VERTICAL); } - //~ Methods -------------------------------------------------------------------------------- // Retrieve the difference between stick slope and global slope @Override protected double getValue (StickContext context) @@ -674,39 +603,6 @@ protected double getValue (StickContext context) } } - //--------------// - // StickContext // - //--------------// - private static class StickContext - { - //~ Instance fields ------------------------------------------------------------------------ - - /** The stick being checked. */ - NearLine stick; - - /** Length of largest gap found. */ - int gap; - - /** Total length of black portions of stem. */ - int black; - - /** Total length of white portions of stem. */ - int white; - - //~ Constructors --------------------------------------------------------------------------- - public StickContext (NearLine stick) - { - this.stick = stick; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return "stick#" + stick.getId(); - } - } - //---------------// // StraightCheck // //---------------// @@ -716,7 +612,6 @@ public String toString () private class StraightCheck extends Check { - //~ Constructors --------------------------------------------------------------------------- protected StraightCheck () { @@ -729,7 +624,6 @@ protected StraightCheck () NON_STRAIGHT); } - //~ Methods -------------------------------------------------------------------------------- @Override protected double getValue (StickContext context) { @@ -739,6 +633,98 @@ protected double getValue (StickContext context) } } + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Ratio minSideRatio = new Constant.Ratio( + 0.4, + "Minimum ratio of filament length to be actually enlarged"); + + private final Scale.Fraction minCoreSectionLength = new Scale.Fraction( + 1.5, + "Minimum length for core sections"); + + private final Scale.Fraction beltMarginDx = new Scale.Fraction( + 0.15, + "Horizontal belt margin checked around stem"); + + private final Check.Grade minCheckResult = new Check.Grade( + 0.2, + "Minimum result for suite of check"); + + private final Check.Grade goodCheckResult = new Check.Grade( + 0.5, + "Good result for suite of check"); + + private final Scale.Fraction blackHigh = new Scale.Fraction( + 2.5, + "High minimum length for a stem"); + + private final Scale.Fraction blackLow = new Scale.Fraction( + 1.25, + "Low minimum length for a stem"); + + private final Scale.Fraction cleanHigh = new Scale.Fraction( + 2.0, + "High minimum clean length for a stem"); + + private final Scale.Fraction cleanLow = new Scale.Fraction( + 0.5, + "Low minimum clean length for a stem"); + + private final Scale.Fraction gapHigh = new Scale.Fraction( + 0.3, + "Maximum vertical gap between stem segments"); + + private final Constant.Double slopeHigh = new Constant.Double( + "tangent", + 0.06, + "Maximum difference with global slope"); + + private final Scale.Fraction straightHigh = new Scale.Fraction( + 0.2, + "High maximum distance to average stem line"); + + private final Constant.Double maxCoTangentForCheck = new Constant.Double( + "cotangent", + 0.1, + "Maximum cotangent for visual check"); + } + + //--------------// + // StickContext // + //--------------// + private static class StickContext + { + + /** The stick being checked. */ + NearLine stick; + + /** Length of largest gap found. */ + int gap; + + /** Total length of black portions of stem. */ + int black; + + /** Total length of white portions of stem. */ + int white; + + StickContext (NearLine stick) + { + this.stick = stick; + } + + @Override + public String toString () + { + return "stick#" + stick.getId(); + } + } + //----------------// // VertCheckBoard // //----------------// @@ -749,20 +735,17 @@ protected double getValue (StickContext context) private static class VertCheckBoard extends CheckBoard { - //~ Instance fields ------------------------------------------------------------------------ private final Sheet sheet; - //~ Constructors --------------------------------------------------------------------------- - public VertCheckBoard (Sheet sheet, - SelectionService eventService, - Class[] eventList) + VertCheckBoard (Sheet sheet, + SelectionService eventService, + Class[] eventList) { super("SeedCheck", null, eventService, eventList); this.sheet = sheet; } - //~ Methods -------------------------------------------------------------------------------- @Override public void onEvent (UserEvent event) { @@ -778,7 +761,8 @@ public void onEvent (UserEvent event) if (glyph != null) { // Make sure this is a rather vertical stick - if (Math.abs(glyph.getInvertedSlope()) <= constants.maxCoTangentForCheck.getValue()) { + if (Math.abs(glyph.getInvertedSlope()) <= constants.maxCoTangentForCheck + .getValue()) { // Apply the check suite SystemManager systemManager = sheet.getSystemManager(); diff --git a/src/main/org/audiveris/omr/sheet/symbol/DotFactory.java b/src/main/org/audiveris/omr/sheet/symbol/DotFactory.java index 9693813c8..7f7d63007 100644 --- a/src/main/org/audiveris/omr/sheet/symbol/DotFactory.java +++ b/src/main/org/audiveris/omr/sheet/symbol/DotFactory.java @@ -22,11 +22,12 @@ package org.audiveris.omr.sheet.symbol; import org.audiveris.omr.classifier.Evaluation; +import org.audiveris.omr.constant.ConstantSet; import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Glyphs; import org.audiveris.omr.glyph.Grades; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.math.GeoOrder; +import org.audiveris.omr.math.GeoUtil; import org.audiveris.omr.math.LineUtil; import org.audiveris.omr.math.Rational; import org.audiveris.omr.sheet.Part; @@ -34,6 +35,7 @@ import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sheet.note.NotePosition; import org.audiveris.omr.sheet.rhythm.Measure; import org.audiveris.omr.sheet.rhythm.MeasureStack; import org.audiveris.omr.sig.SIGraph; @@ -56,6 +58,7 @@ import org.audiveris.omr.sig.relation.RepeatDotPairRelation; import org.audiveris.omr.util.HorizontalSide; import static org.audiveris.omr.util.HorizontalSide.*; +import org.audiveris.omrdataset.api.OmrShape; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,18 +67,20 @@ import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Set; /** - * Class {@code DotFactory} is a companion of {@link SymbolFactory}, dedicated to the + * Class {@code DotFactory} is a companion of {@link InterFactory}, dedicated to the * interpretation of dot-shaped symbols. *

                                                                                        * Some dot processing can be done instantly while the symbol is being built, other dot processing * may require symbols nearby and thus can take place only when all other symbols have been built. * Hence implementing methods are named "instant*()" or "late*()" respectively. *

                                                                                        - * A dot can be:

                                                                                          + * A dot can be: + *
                                                                                            *
                                                                                          • a part of a repeat sign (upper or lower dot), *
                                                                                          • a staccato sign, *
                                                                                          • an augmentation dot (first or second dot), [TODO: Handle augmentation dot for mirrored notes] @@ -89,13 +94,13 @@ */ public class DotFactory { - //~ Static fields/initializers ----------------------------------------------------------------- + + private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(DotFactory.class); - //~ Instance fields ---------------------------------------------------------------------------- - /** The related symbol factory. */ - private final SymbolFactory symbolFactory; + /** The related inter factory. */ + private final InterFactory interFactory; /** The related system. */ private final SystemInfo system; @@ -104,26 +109,24 @@ public class DotFactory private final Scale scale; - /** Dot candidates. Sorted bottom up */ - private final List dots = new ArrayList(); + /** Dot candidates. Sorted top down, then left to right. */ + private final List dots = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new DotFactory object. * - * @param symbolFactory the mother factory - * @param system underlying system + * @param interFactory the mother factory + * @param system underlying system */ - public DotFactory (SymbolFactory symbolFactory, + public DotFactory (InterFactory interFactory, SystemInfo system) { - this.symbolFactory = symbolFactory; + this.interFactory = interFactory; this.system = system; sig = system.getSig(); scale = system.getSheet().getScale(); } - //~ Methods ------------------------------------------------------------------------------------ //------------------// // instantDotChecks // //------------------// @@ -133,16 +136,25 @@ public DotFactory (SymbolFactory symbolFactory, * All symbols may not be available yet, so only instant processing is launched on the dot (as * a repeat dot, as a staccato dot). *

                                                                                            + * Whatever the role of a dot, it cannot be stuck (or too close) to a staff line or ledger. + *

                                                                                            * The symbol is also saved as a dot candidate for later processing. * - * @param eval evaluation result - * @param glyph underlying glyph + * @param eval evaluation result + * @param glyph underlying glyph + * @param closestStaff staff closest to the dot */ public void instantDotChecks (Evaluation eval, - Glyph glyph) + Glyph glyph, + Staff closestStaff) { + // Discard glyph too close to staff line or ledger + if (!checkDistanceToLine(glyph, closestStaff)) { + return; + } + // Simply record the candidate dot - Dot dot = new Dot(eval, glyph); + Dot dot = new GlyphDot(eval, glyph); dots.add(dot); // Run instant checks @@ -161,8 +173,8 @@ public void instantDotChecks (Evaluation eval, */ public void lateDotChecks () { - // Sort dots by reverse ordinate - Collections.sort(dots); + // Sort dots carefully + Collections.sort(dots, Dot.comparator); // Run all late checks lateAugmentationChecks(); // Note-Dot and Note-Dot-Dot configurations @@ -204,6 +216,56 @@ private void buildRepeatPairs () } } + //---------------------// + // checkDistanceToLine // + //---------------------// + /** + * Check that dot glyph is vertically distant from staff line or ledger. + * + * @param glyph the dot glyph to check + * @param staff the closest staff + * @return true if OK + */ + private boolean checkDistanceToLine (Glyph glyph, + Staff staff) + { + final Point center = glyph.getCenter(); + final NotePosition notePosition = staff.getNotePosition(center); + final double pitch = notePosition.getPitchPosition(); + + if ((Math.abs(pitch) > staff.getLineCount()) && (notePosition.getLedger() == null)) { + if (glyph.isVip()) { + logger.info("VIP glyph#{} dot isolated OK", glyph.getId()); + } + + return true; + } else { + // Distance to line/ledger specified in interline fraction + double linePitch = 2 * Math.rint(pitch / 2); + double distance = Math.abs(pitch - linePitch) / 2; + + if (distance >= constants.minDyFromLine.getValue()) { + if (glyph.isVip()) { + logger.info( + "VIP glyph#{} dot distance:{} OK", + glyph.getId(), + String.format("%.2f", distance)); + } + + return true; + } else { + if (glyph.isVip()) { + logger.info( + "VIP glyph#{} dot distance:{} too close", + glyph.getId(), + String.format("%.2f", distance)); + } + + return false; + } + } + } + //------------------// // checkRepeatPairs // //------------------// @@ -239,7 +301,7 @@ private void checkStackRepeats () { for (MeasureStack stack : system.getStacks()) { for (HorizontalSide side : HorizontalSide.values()) { - final List repeatDots = new ArrayList(); + final List repeatDots = new ArrayList<>(); int virtualDotCount = 0; // Virtual dots inferred from StaffBarline shape for (Measure measure : stack.getMeasures()) { @@ -276,12 +338,12 @@ private void checkStackRepeats () if (side == LEFT) { if ((shape == Shape.LEFT_REPEAT_SIGN) - || (shape == Shape.BACK_TO_BACK_REPEAT_SIGN)) { + || (shape == Shape.BACK_TO_BACK_REPEAT_SIGN)) { virtualDotCount += 2; } } else { if ((shape == Shape.RIGHT_REPEAT_SIGN) - || (shape == Shape.BACK_TO_BACK_REPEAT_SIGN)) { + || (shape == Shape.BACK_TO_BACK_REPEAT_SIGN)) { virtualDotCount += 2; } } @@ -296,7 +358,7 @@ private void checkStackRepeats () if (dotCount >= staffCount) { // It's a repeat side, delete inters that conflict with repeat dots // This works for real dots only, not for virtual ones - List toDelete = new ArrayList(); + List toDelete = new ArrayList<>(); for (RepeatDotInter dot : repeatDots) { Rectangle dotBox = dot.getBounds(); @@ -399,8 +461,9 @@ private void filterMirrorHeads (List heads) private void instantCheckRepeat (Dot dot) { // Check vertical pitch position within the staff: close to +1 or -1 - final Point center = dot.glyph.getCenter(); - final double pp = system.estimatedPitch(center); + final Rectangle dotBounds = dot.getBounds(); + final Point dotPt = GeoUtil.centerOf(dotBounds); + final double pp = system.estimatedPitch(dotPt); double pitchDif = Math.abs(Math.abs(pp) - 1); double maxDif = RepeatDotBarRelation.getYGapMaximum(false).getValue(); @@ -411,12 +474,11 @@ private void instantCheckRepeat (Dot dot) final int maxDx = scale.toPixels(RepeatDotBarRelation.getXOutGapMaximum(false)); final int maxDy = scale.toPixels(RepeatDotBarRelation.getYGapMaximum(false)); - final Point dotPt = dot.glyph.getCenter(); final Rectangle luBox = new Rectangle(dotPt); luBox.grow(maxDx, maxDy); final List bars = Inters.intersectedInters( - symbolFactory.getSystemBars(), + interFactory.getSystemBars(), GeoOrder.BY_ABSCISSA, luBox); @@ -434,11 +496,9 @@ private void instantCheckRepeat (Dot dot) Point barCenter = bar.getCenter(); // Select proper bar reference point (left or right side and proper vertical side) - double barY = barCenter.y - + ((box.height / 8d) * Integer.signum(dotPt.y - barCenter.y)); - double barX = LineUtil.xAtY(bar.getMedian(), barY) - + ((bar.getWidth() / 2) * Integer.signum( - dotPt.x - barCenter.x)); + double barY = barCenter.y + ((box.height / 8d) * Integer.signum(dotPt.y - barCenter.y)); + double barX = LineUtil.xAtY(bar.getMedian(), barY) + ((bar.getWidth() / 2) * Integer + .signum(dotPt.x - barCenter.x)); double xGap = Math.abs(barX - dotPt.x); double yGap = Math.abs(barY - dotPt.y); @@ -455,15 +515,28 @@ private void instantCheckRepeat (Dot dot) } if (bestRel != null) { - final Staff staff = system.getClosestStaff(center); // Staff is OK - double grade = Grades.intrinsicRatio * dot.eval.grade; + final Staff staff = system.getClosestStaff(dotPt); // Staff is OK + double grade = Grades.intrinsicRatio * dot.getGrade(); double pitch = (pp > 0) ? 1 : (-1); - RepeatDotInter repeat = new RepeatDotInter(dot.glyph, grade, staff, pitch); + Glyph glyph = dot.getGlyph(); + int annId = dot.getAnnotationId(); + final RepeatDotInter repeat; + + if (glyph != null) { + repeat = new RepeatDotInter(glyph, grade, staff, pitch); + } else { + repeat = null; // Placeholder + } + sig.addVertex(repeat); sig.addEdge(repeat, bestBar, bestRel); - if (dot.glyph.isVip()) { - logger.info("VIP Created {} from glyph#{}", repeat, dot.glyph.getId()); + if (dot.isVip()) { + if (glyph != null) { + logger.info("VIP Created {} from glyph#{}", repeat, glyph.getId()); + } else { + logger.info("VIP Created {} from annotation#{}", repeat, annId); + } } } } @@ -480,12 +553,25 @@ private void instantCheckRepeat (Dot dot) */ private void instantCheckStaccato (Dot dot) { - ArticulationInter.createValidAdded( - dot.glyph, - Shape.STACCATO, - Grades.intrinsicRatio * dot.eval.grade, - system, - symbolFactory.getSystemHeadChords()); + Glyph glyph = dot.getGlyph(); + + if (glyph != null) { + ArticulationInter.createValidAdded( + glyph, + Shape.STACCATO, + Grades.intrinsicRatio * dot.getGrade(), + system, + interFactory.getSystemHeadChords()); + + // } else { + // ArticulationInter.createValidAdded( + // dot.getAnnotationId(), + // dot.getBounds(), + // dot.getOmrShape(), + // Grades.intrinsicRatio * dot.getGrade(), + // system, + // interFactory.getSystemHeadChords()); + } } //------------------------// @@ -526,12 +612,13 @@ private void lateAugmentationChecks () private void lateDotAugmentationCheck (Dot dot, List systemFirsts) { - if (dot.glyph.isVip()) { + if (dot.isVip()) { logger.info("VIP lateDotAugmentationCheck for {}", dot); } - double grade = Grades.intrinsicRatio * dot.eval.grade; - AugmentationDotInter second = new AugmentationDotInter(dot.glyph, grade); + double grade = Grades.intrinsicRatio * dot.getGrade(); + Glyph glyph = dot.getGlyph(); + AugmentationDotInter second = new AugmentationDotInter(glyph, grade); Link bestDotLink = Link.bestOf(second.lookupDotLinks(systemFirsts, system)); if (bestDotLink != null) { @@ -557,7 +644,13 @@ private void lateFermataChecks () } for (Dot dot : dots) { - Rectangle dotBox = dot.glyph.getBounds(); + Glyph glyph = dot.getGlyph(); + + if (glyph == null) { + continue; + } + + Rectangle dotBox = dot.getBounds(); FermataDotInter dotInter = null; for (Inter arc : arcs) { @@ -570,11 +663,11 @@ private void lateFermataChecks () } if (halfBox.intersects(dotBox)) { - final Point dotCenter = dot.glyph.getCenter(); - double xGap = Math.abs( - dotCenter.x - (halfBox.x + (halfBox.width / 2))); - double yTarget = (arc.getShape() == Shape.FERMATA_ARC_BELOW) - ? (halfBox.y + (halfBox.height * 0.25)) + final Point dotCenter = GeoUtil.centerOf(dotBox); + double xGap = Math.abs(dotCenter.x - (halfBox.x + (halfBox.width / 2))); + double yTarget = (arc.getShape() == Shape.FERMATA_ARC_BELOW) ? (halfBox.y + + (halfBox.height + * 0.25)) : (halfBox.y + (halfBox.height * 0.75)); double yGap = Math.abs(dotCenter.y - yTarget); DotFermataRelation rel = new DotFermataRelation(); @@ -582,14 +675,14 @@ private void lateFermataChecks () if (rel.getGrade() >= rel.getMinGrade()) { if (dotInter == null) { - double grade = Grades.intrinsicRatio * dot.eval.grade; - dotInter = new FermataDotInter(dot.glyph, grade); + double grade = Grades.intrinsicRatio * dot.getGrade(); + dotInter = new FermataDotInter(glyph, grade); sig.addVertex(dotInter); logger.debug("Created {}", dotInter); } sig.addEdge(dotInter, arc, rel); - logger.debug("{} matches dot glyph#{}", arc, dot.glyph.getId()); + logger.debug("{} matches dot glyph#{}", arc, glyph.getId()); } } } @@ -610,23 +703,18 @@ private void lateFermataChecks () */ private void lateNoteAugmentationCheck (Dot dot) { - if (dot.glyph.isVip()) { + if (dot.isVip()) { logger.info("VIP lateNoteAugmentationCheck for {}", dot); } - double grade = Grades.intrinsicRatio * dot.eval.grade; - AugmentationDotInter aug = new AugmentationDotInter(dot.glyph, grade); + double grade = Grades.intrinsicRatio * dot.getGrade(); + Glyph glyph = dot.getGlyph(); + AugmentationDotInter aug = new AugmentationDotInter(glyph, grade); - List links = new ArrayList(); - Link headLink = aug.lookupHeadLink( - symbolFactory.getSystemHeadChords(), - system); - - if (headLink != null) { - links.add(headLink); - } - - links.addAll(aug.lookupRestLinks(symbolFactory.getSystemRests(), system)); + List links = new ArrayList<>(); + Link headLink = aug.lookupHeadLink(interFactory.getSystemHeadChords(), system); + links.addAll(aug.sharedHeadLinks(headLink)); + links.addAll(aug.lookupRestLinks(interFactory.getSystemRests(), system)); if (!links.isEmpty()) { sig.addVertex(aug); @@ -653,42 +741,132 @@ private void lateRepeatChecks () checkStackRepeats(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Scale.Fraction minDyFromLine = new Scale.Fraction( + 0.3, + "Minimum vertical distance between dot center and staff line/ledger"); + } + //-----// // Dot // //-----// /** * Remember a dot candidate, for late processing. */ - private static class Dot - implements Comparable + private abstract static class Dot + { + + /** + * Very specific sorting of dots. + *

                                                                                            + * If the 2 dots overlap vertically, return left one first. + * Otherwise, return top one first. + * + * @param that the other dot + * @return order sign + */ + public static final Comparator comparator = new Comparator() + { + @Override + public int compare (Dot d1, + Dot d2) + { + if (d1 == d2) { + return 0; + } + + final Rectangle b1 = d1.getBounds(); + final Rectangle b2 = d2.getBounds(); + + if (GeoUtil.yOverlap(b1, b2) > 0) { + return Integer.compare(b1.x, b2.x); + } else { + return Integer.compare(b1.y, b2.y); + } + } + }; + + public abstract int getAnnotationId (); + + public abstract Rectangle getBounds (); + + public abstract Glyph getGlyph (); + + public abstract double getGrade (); + + public abstract OmrShape getOmrShape (); + + public abstract boolean isVip (); + } + + //----------// + // GlyphDot // + //----------// + /** + * Glyph-based dot. + */ + private static class GlyphDot + extends Dot { - //~ Instance fields ------------------------------------------------------------------------ - final Evaluation eval; // Evaluation result + private final Glyph glyph; // Underlying glyph - final Glyph glyph; // Underlying glyph + private final Evaluation eval; // Evaluation result - //~ Constructors --------------------------------------------------------------------------- - public Dot (Evaluation eval, - Glyph glyph) + GlyphDot (Evaluation eval, + Glyph glyph) { this.eval = eval; this.glyph = glyph; } - //~ Methods -------------------------------------------------------------------------------- @Override - public int compareTo (Dot that) + public int getAnnotationId () + { + return 0; + } + + @Override + public Rectangle getBounds () + { + return glyph.getBounds(); + } + + @Override + public Glyph getGlyph () + { + return glyph; + } + + @Override + public double getGrade () + { + return eval.grade; + } + + @Override + public OmrShape getOmrShape () + { + return null; + } + + @Override + public boolean isVip () { - return Glyphs.byReverseBottom.compare(glyph, that.glyph); + return glyph.isVip(); } @Override public String toString () { - StringBuilder sb = new StringBuilder("{Dot"); - sb.append(" glyph#").append(glyph.getId()); + StringBuilder sb = new StringBuilder("GlyphDot{"); + sb.append("glyph#").append(glyph.getId()); sb.append(" ").append(eval); sb.append("}"); diff --git a/src/main/org/audiveris/omr/sheet/symbol/InterCleaner.java b/src/main/org/audiveris/omr/sheet/symbol/InterCleaner.java index a9067d1d9..adc06f42d 100644 --- a/src/main/org/audiveris/omr/sheet/symbol/InterCleaner.java +++ b/src/main/org/audiveris/omr/sheet/symbol/InterCleaner.java @@ -28,7 +28,8 @@ * Class {@code InterCleaner} is a workaround to purge containers of Inter instances * deleted from SIG but still referenced from some containers. *

                                                                                            - * This accounts for:

                                                                                              + * This accounts for: + *
                                                                                                *
                                                                                              • Part -> lyrics
                                                                                              • *
                                                                                              * @@ -36,22 +37,19 @@ */ class InterCleaner { - //~ Instance fields ---------------------------------------------------------------------------- private final SystemInfo system; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code InterCleaner} object. * * @param system the system to be processed */ - public InterCleaner (SystemInfo system) + InterCleaner (SystemInfo system) { this.system = system; } - //~ Methods ------------------------------------------------------------------------------------ public void purgeContainers () { // Parts diff --git a/src/main/org/audiveris/omr/sheet/symbol/SymbolFactory.java b/src/main/org/audiveris/omr/sheet/symbol/InterFactory.java similarity index 86% rename from src/main/org/audiveris/omr/sheet/symbol/SymbolFactory.java rename to src/main/org/audiveris/omr/sheet/symbol/InterFactory.java index 9696e390e..4bca4d513 100644 --- a/src/main/org/audiveris/omr/sheet/symbol/SymbolFactory.java +++ b/src/main/org/audiveris/omr/sheet/symbol/InterFactory.java @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------------------------// // // -// S y m b o l F a c t o r y // +// I n t e r F a c t o r y // // // //------------------------------------------------------------------------------------------------// // @@ -98,7 +98,7 @@ import java.util.TreeMap; /** - * Class {@code SymbolFactory} generates the inter instances corresponding to + * Class {@code InterFactory} generates the inter instances corresponding to * to an acceptable symbol evaluation in a given system. *

                                                                                              * (Generally there is one inter instance per evaluation, an exception is the case of full time @@ -106,13 +106,11 @@ * * @author Hervé Bitteur */ -public class SymbolFactory +public class InterFactory { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger(SymbolFactory.class); + private static final Logger logger = LoggerFactory.getLogger(InterFactory.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated system. */ @Navigable(false) private final SystemInfo system; @@ -144,13 +142,12 @@ public class SymbolFactory /** Processing switches. */ private final ProcessingSwitches switches; - //~ Constructors ------------------------------------------------------------------------------- /** - * Creates a new SymbolsFactory object. + * Creates a new InterFactory object. * * @param system the dedicated system */ - public SymbolFactory (SystemInfo system) + public InterFactory (SystemInfo system) { this.system = system; @@ -170,7 +167,6 @@ public SymbolFactory (SystemInfo system) switches = system.getSheet().getStub().getProcessingSwitches(); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // create // //--------// @@ -193,31 +189,14 @@ public void create (Evaluation eval, } } - //--------------// - // createManual // - //--------------// - /** - * Create a manual inter instance to handle the provided shape. - * - * @param shape provided shape - * @param sheet related sheet - * @return the created manual inter or null - */ - public static Inter createManual (Shape shape, - Sheet sheet) - { - Inter ghost = doCreateManual(shape, sheet); - - if (ghost != null) { - ghost.setManual(true); - } - - return ghost; - } - //---------------// // getSystemBars // //---------------// + /** + * Report all system barlines. + * + * @return all barlines in the containing system + */ public List getSystemBars () { if (systemBars == null) { @@ -231,6 +210,11 @@ public List getSystemBars () //----------------// // getSystemRests // //----------------// + /** + * Report all rests in system. + * + * @return all rests in the containing system + */ public List getSystemRests () { if (systemRests == null) { @@ -244,6 +228,9 @@ public List getSystemRests () //------------// // lateChecks // //------------// + /** + * Perform late checks. + */ public void lateChecks () { // Conflicting dot interpretations @@ -256,37 +243,6 @@ public void lateChecks () handleTimes(); } - //---------------------// - // getSystemHeadChords // - //---------------------// - List getSystemHeadChords () - { - return systemHeadChords; - } - - //----------------// - // getSystemHeads // - //----------------// - List getSystemHeads () - { - return systemHeads; - } - - //----------------// - // getSystemNotes // - //----------------// - List getSystemNotes () - { - if (systemNotes == null) { - systemNotes = new ArrayList(getSystemHeads().size() + getSystemRests().size()); - systemNotes.addAll(getSystemHeads()); - systemNotes.addAll(getSystemRests()); - Collections.sort(systemNotes, Inters.byAbscissa); - } - - return systemNotes; - } - //----------// // doCreate // //----------// @@ -294,7 +250,8 @@ List getSystemNotes () * Create proper inter instance(s) for provided evaluated glyph. *

                                                                                              * This method addresses only the symbols handled via a classifier. - * In current OMR design, this does not address:

                                                                                                + * In current OMR design, this does not address: + *
                                                                                                  *
                                                                                                • Brace *
                                                                                                • Bracket *
                                                                                                • Barline @@ -338,7 +295,7 @@ private Inter doCreate (Evaluation eval, // - STACCATO_DOT // - AUGMENTATION_DOT case DOT_set: - dotFactory.instantDotChecks(eval, glyph); + dotFactory.instantDotChecks(eval, glyph, closestStaff); return null; @@ -413,7 +370,7 @@ private Inter doCreate (Evaluation eval, case SMALL_FLAG: case SMALL_FLAG_SLASH: - return new SmallFlagInter(null, shape, grade); + return new SmallFlagInter(glyph, shape, grade); // Rests case LONG_REST: @@ -453,13 +410,12 @@ private Inter doCreate (Evaluation eval, case STACCATO: case STACCATISSIMO: case STRONG_ACCENT: - return switches.getValue(Switch.articulations) - ? ArticulationInter.createValidAdded( - glyph, - shape, - grade, - system, - systemHeadChords) : null; + return switches.getValue(Switch.articulations) ? ArticulationInter.createValidAdded( + glyph, + shape, + grade, + system, + systemHeadChords) : null; // Markers case CODA: @@ -538,7 +494,8 @@ private Inter doCreate (Evaluation eval, case PLUCK_I: case PLUCK_M: case PLUCK_A: - return switches.getValue(Switch.pluckings) ? new PluckingInter(glyph, shape, grade) : null; + return switches.getValue(Switch.pluckings) ? new PluckingInter(glyph, shape, grade) + : null; // Romans case ROMAN_I: @@ -563,6 +520,180 @@ private Inter doCreate (Evaluation eval, } } + //-----------------------// + // handleComplexDynamics // + //-----------------------// + /** + * Handle competition between complex and shorter dynamics. + */ + private void handleComplexDynamics () + { + // All dynamics in system + final List dynamics = sig.inters(DynamicsInter.class); + // Complex dynamics in system, sorted by decreasing length + final List complexes = new ArrayList<>(); + for (Inter inter : dynamics) { + DynamicsInter dyn = (DynamicsInter) inter; + + if (dyn.getSymbolString().length() > 1) { + complexes.add(dyn); + } + } + Collections.sort(complexes, new Comparator() + { + @Override + public int compare (DynamicsInter d1, + DynamicsInter d2) + { + // Sort by decreasing length + return Integer.compare( + d2.getSymbolString().length(), + d1.getSymbolString().length()); + } + }); + for (DynamicsInter complex : complexes) { + complex.swallowShorterDynamics(dynamics); + } + } + + //-------------// + // handleTimes // + //-------------// + /** + * Handle time inters outside of system header. + *

                                                                                                  + * Isolated time inters found outside of system header lead to the retrieval of a column of + * time signatures. + */ + private void handleTimes () + { + // Retrieve all time inters (outside staff headers) + List systemTimes = sig.inters(new Class[]{ + TimeWholeInter.class, // Whole symbol like C or 6/8 + TimeNumberInter.class}); // Partial symbol like 6 or 8 + List headerTimes = new ArrayList<>(); + for (Inter inter : systemTimes) { + Staff staff = inter.getStaff(); + + if (inter.getCenter().x < staff.getHeaderStop()) { + headerTimes.add(inter); + } + } + systemTimes.removeAll(headerTimes); + if (systemTimes.isEmpty()) { + return; + } + // Dispatch these time inters into their containing stack + Map> timeMap = new TreeMap<>(new Comparator() + { + @Override + public int compare (MeasureStack s1, + MeasureStack s2) + { + return Integer.compare(s1.getIdValue(), s2.getIdValue()); + } + }); + for (Inter inter : systemTimes) { + MeasureStack stack = system.getStackAt(inter.getCenter()); + Set stackSet = timeMap.get(stack); + if (stackSet == null) { + timeMap.put(stack, stackSet = new LinkedHashSet<>()); + } + stackSet.add(inter); + } + // Finally, scan each stack populated with some time sig(s) + for (Entry> entry : timeMap.entrySet()) { + MeasureStack stack = entry.getKey(); + TimeBuilder.BasicColumn column = new TimeBuilder.BasicColumn(stack, entry.getValue()); + int res = column.retrieveTime(); + + // If the stack does have a validated time sig, discard overlapping stuff right now! + if (res != -1) { + final Collection times = column.getTimeInters().values(); + final Rectangle columnBox = Inters.getBounds(times); + List neighbors = sig.inters(new Predicate() + { + @Override + public boolean check (Inter inter) + { + return inter.getBounds().intersects(columnBox) + && !(inter instanceof InterEnsemble); + } + }); + + neighbors.removeAll(times); + + for (AbstractTimeInter time : times) { + for (Iterator it = neighbors.iterator(); it.hasNext();) { + Inter neighbor = it.next(); + + try { + if (neighbor.overlaps(time)) { + logger.debug("Deleting time overlapping {}", neighbor); + neighbor.remove(); + it.remove(); + } + } catch (DeletedInterException ignored) { + } + } + } + } + } + } + + //---------------------// + // getSystemHeadChords // + //---------------------// + List getSystemHeadChords () + { + return systemHeadChords; + } + + //----------------// + // getSystemHeads // + //----------------// + List getSystemHeads () + { + return systemHeads; + } + + //----------------// + // getSystemNotes // + //----------------// + List getSystemNotes () + { + if (systemNotes == null) { + systemNotes = new ArrayList<>(getSystemHeads().size() + getSystemRests().size()); + systemNotes.addAll(getSystemHeads()); + systemNotes.addAll(getSystemRests()); + Collections.sort(systemNotes, Inters.byAbscissa); + } + + return systemNotes; + } + + //--------------// + // createManual // + //--------------// + /** + * Create a manual inter instance to handle the provided shape. + * + * @param shape provided shape + * @param sheet related sheet + * @return the created manual inter or null + */ + public static Inter createManual (Shape shape, + Sheet sheet) + { + Inter ghost = doCreateManual(shape, sheet); + + if (ghost != null) { + ghost.setManual(true); + } + + return ghost; + } + //----------------// // doCreateManual // //----------------// @@ -578,16 +709,15 @@ private Inter doCreate (Evaluation eval, private static Inter doCreateManual (Shape shape, Sheet sheet) { - final ProcessingSwitches switches = sheet.getStub().getProcessingSwitches(); final double GRADE = 1.0; // Grade value for any manual shape switch (shape) { // // Ottava TODO ??? - case OTTAVA_ALTA: - case OTTAVA_BASSA: - return null; - + // case OTTAVA_ALTA: + // case OTTAVA_BASSA: + // return null; + // // Brace, bracket TODO ??? // // Barlines @@ -608,7 +738,6 @@ private static Inter doCreateManual (Shape shape, case BACK_TO_BACK_REPEAT_SIGN: return new StaffBarlineInter(shape, GRADE); - // // Beams case BEAM: return new BeamInter(GRADE); @@ -757,8 +886,7 @@ private static Inter doCreateManual (Shape shape, case STACCATO: case STACCATISSIMO: case STRONG_ACCENT: - return switches.getValue(Switch.articulations) - ? new ArticulationInter(null, shape, GRADE) : null; // No visit + return new ArticulationInter(null, shape, GRADE); // No visit // Markers case CODA: @@ -826,15 +954,14 @@ private static Inter doCreateManual (Shape shape, case DIGIT_3: case DIGIT_4: case DIGIT_5: - return switches.getValue(Switch.fingerings) ? new FingeringInter(null, shape, GRADE) - : null; // No visit + return new FingeringInter(null, shape, GRADE); // No visit // Plucking case PLUCK_P: case PLUCK_I: case PLUCK_M: case PLUCK_A: - return switches.getValue(Switch.pluckings) ? new PluckingInter(null, shape, GRADE) : null; // No visit + return new PluckingInter(null, shape, GRADE); // No visit // Romans case ROMAN_I: @@ -849,7 +976,7 @@ private static Inter doCreateManual (Shape shape, case ROMAN_X: case ROMAN_XI: case ROMAN_XII: - return switches.getValue(Switch.frets) ? new FretInter(null, shape, GRADE) : null; // No visit + return new FretInter(null, shape, GRADE); // No visit // Others default: @@ -859,141 +986,4 @@ private static Inter doCreateManual (Shape shape, throw new IllegalArgumentException(msg); } } - - //-----------------------// - // handleComplexDynamics // - //-----------------------// - /** - * Handle competition between complex and shorter dynamics. - */ - private void handleComplexDynamics () - { - // All dynamics in system - final List dynamics = sig.inters(DynamicsInter.class); - - // Complex dynamics in system, sorted by decreasing length - final List complexes = new ArrayList(); - - for (Inter inter : dynamics) { - DynamicsInter dyn = (DynamicsInter) inter; - - if (dyn.getSymbolString().length() > 1) { - complexes.add(dyn); - } - } - - Collections.sort( - complexes, - new Comparator() - { - @Override - public int compare (DynamicsInter d1, - DynamicsInter d2) - { - // Sort by decreasing length - return Integer.compare( - d2.getSymbolString().length(), - d1.getSymbolString().length()); - } - }); - - for (DynamicsInter complex : complexes) { - complex.swallowShorterDynamics(dynamics); - } - } - - //-------------// - // handleTimes // - //-------------// - /** - * Handle time symbols outside of system header. - *

                                                                                                  - * Isolated time symbols found outside of system header lead to the retrieval of a column of - * time signatures. - */ - private void handleTimes () - { - // Retrieve all time symbols (outside staff headers) - List systemTimes = sig.inters( - new Class[]{TimeWholeInter.class, // Whole symbol like C or 6/8 - TimeNumberInter.class}); // Partial symbol like 6 or 8 - List headerTimes = new ArrayList(); - - for (Inter inter : systemTimes) { - Staff staff = inter.getStaff(); - - if (inter.getCenter().x < staff.getHeaderStop()) { - headerTimes.add(inter); - } - } - - systemTimes.removeAll(headerTimes); - - if (systemTimes.isEmpty()) { - return; - } - - // Dispatch these time symbols into their containing stack - Map> timeMap = new TreeMap>( - new Comparator() - { - @Override - public int compare (MeasureStack s1, - MeasureStack s2) - { - return Integer.compare(s1.getIdValue(), s2.getIdValue()); - } - }); - - for (Inter inter : systemTimes) { - MeasureStack stack = system.getStackAt(inter.getCenter()); - Set stackSet = timeMap.get(stack); - - if (stackSet == null) { - timeMap.put(stack, stackSet = new LinkedHashSet()); - } - - stackSet.add(inter); - } - - // Finally, scan each stack populated with some time sig(s) - for (Entry> entry : timeMap.entrySet()) { - MeasureStack stack = entry.getKey(); - TimeBuilder.BasicColumn column = new TimeBuilder.BasicColumn(stack, entry.getValue()); - int res = column.retrieveTime(); - - // If the stack does have a validated time sig, discard overlapping stuff right now! - if (res != -1) { - final Collection times = column.getTimeInters().values(); - final Rectangle columnBox = Inters.getBounds(times); - List neighbors = sig.inters( - new Predicate() - { - @Override - public boolean check (Inter inter) - { - return inter.getBounds().intersects(columnBox) - && !(inter instanceof InterEnsemble); - } - }); - - neighbors.removeAll(times); - - for (AbstractTimeInter time : times) { - for (Iterator it = neighbors.iterator(); it.hasNext();) { - Inter neighbor = it.next(); - - try { - if (neighbor.overlaps(time)) { - logger.debug("Deleting time overlapping {}", neighbor); - neighbor.remove(); - it.remove(); - } - } catch (DeletedInterException ignored) { - } - } - } - } - } - } } diff --git a/src/main/org/audiveris/omr/sheet/symbol/LinksStep.java b/src/main/org/audiveris/omr/sheet/symbol/LinksStep.java index 3098851d6..368688923 100644 --- a/src/main/org/audiveris/omr/sheet/symbol/LinksStep.java +++ b/src/main/org/audiveris/omr/sheet/symbol/LinksStep.java @@ -60,7 +60,6 @@ public class LinksStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -69,21 +68,20 @@ public class LinksStep /** Classes that may impact texts. */ private static final Set forTexts; + /** All impacting classes. */ + private static final Set impactingClasses; + static { - forTexts = new HashSet(); + forTexts = new HashSet<>(); forTexts.add(WordInter.class); forTexts.add(SentenceInter.class); } - /** All impacting classes. */ - private static final Set impactingClasses; - static { - impactingClasses = new HashSet(); + impactingClasses = new HashSet<>(); impactingClasses.addAll(forTexts); } - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code LinksStep} object. */ @@ -91,7 +89,6 @@ public LinksStep () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // doSystem // //----------// @@ -142,7 +139,7 @@ public void impact (UITaskList seq, InterTask interTask = (InterTask) task; Inter inter = interTask.getInter(); SystemInfo system = inter.getSig().getSystem(); - Class interClass = (Class) inter.getClass(); + Class interClass = inter.getClass(); if (isImpactedBy(interClass, forTexts)) { if (inter instanceof LyricItemInter) { @@ -199,14 +196,12 @@ protected void doEpilog (Sheet sheet, } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/sheet/symbol/SymbolsBuilder.java b/src/main/org/audiveris/omr/sheet/symbol/SymbolsBuilder.java index c5e16d61b..f52b9c87a 100644 --- a/src/main/org/audiveris/omr/sheet/symbol/SymbolsBuilder.java +++ b/src/main/org/audiveris/omr/sheet/symbol/SymbolsBuilder.java @@ -67,14 +67,11 @@ */ public class SymbolsBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - SymbolsBuilder.class); + private static final Logger logger = LoggerFactory.getLogger(SymbolsBuilder.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated system. */ @Navigable(false) private final SystemInfo system; @@ -86,20 +83,18 @@ public class SymbolsBuilder /** Shape classifier to use. */ private final Classifier classifier = ShapeClassifier.getInstance(); - // // /** Shape second classifier to use. */ // private final Classifier classifier2 = ShapeClassifier.getSecondInstance(); // /** Companion factory for symbols inters. */ - private final SymbolFactory factory; + private final InterFactory factory; /** Aras where fine glyphs may be needed. */ - private final List fineBoxes = new ArrayList(); + private final List fineBoxes = new ArrayList<>(); /** Scale-dependent global constants. */ private final Parameters params; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SymbolsBuilder object. * @@ -107,7 +102,7 @@ public class SymbolsBuilder * @param factory the dedicated symbol factory */ public SymbolsBuilder (SystemInfo system, - SymbolFactory factory) + InterFactory factory) { this.system = system; this.factory = factory; @@ -117,7 +112,6 @@ public SymbolsBuilder (SystemInfo system, params = new Parameters(sheet.getScale()); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // buildSymbols // //--------------// @@ -125,6 +119,7 @@ public SymbolsBuilder (SystemInfo system, * Find all possible interpretations of symbols composed from available system glyphs. *

                                                                                                  * Synopsis: + * *

                                                                                                        * - retrieveFineBoxes()                            // Retrieve areas around small chords
                                                                                                        * - getSymbolsGlyphs()                             // Retrieve all glyphs usable for symbols
                                                                                                  @@ -136,7 +131,7 @@ public SymbolsBuilder (SystemInfo system,
                                                                                                        *          - build compound glyph                  // Build one compound glyph per subset
                                                                                                        *          - evaluateGlyph(compound)               // Run shape classifier on compound
                                                                                                        *          - FOREACH acceptable evaluation
                                                                                                  -     *             + symbolFactory.create(eval, glyph) // Create inter(s) related to evaluation
                                                                                                  +     *             + symbolFactory.create(eval, glyph)  // Create inter(s) related to evaluation
                                                                                                        * 
                                                                                                  * * @param optionalsMap the optional (weak) glyphs per system @@ -253,7 +248,7 @@ private void evaluateGlyph (Glyph glyph) private List getSymbolsGlyphs (Map> optionalsMap) { // Sorted by abscissa, ordinate, id - List glyphs = new ArrayList(); + List glyphs = new ArrayList<>(); for (Glyph glyph : system.getGroupedGlyphs(GlyphGroup.SYMBOL)) { final int weight = glyph.getWeight(); @@ -316,7 +311,7 @@ private boolean hitFineBox (Glyph glyph) private void processClusters (SimpleGraph systemGraph) { // Retrieve all the clusters of glyphs (sets of connected glyphs) - final ConnectivityInspector inspector = new ConnectivityInspector( + final ConnectivityInspector inspector = new ConnectivityInspector<>( systemGraph); final List> sets = inspector.connectedSets(); logger.debug("symbols sets: {}", sets.size()); @@ -334,10 +329,10 @@ private void processClusters (SimpleGraph systemGraph) if (setSize <= maxPartCount) { subSet = set; } else { - List list = new ArrayList(set); + List list = new ArrayList<>(set); Collections.sort(list, Glyphs.byReverseWeight); list = list.subList(0, Math.min(list.size(), maxPartCount)); - subSet = new LinkedHashSet(list); + subSet = new LinkedHashSet<>(list); logger.info("Symbol parts shrunk from {} to {}", setSize, maxPartCount); } @@ -375,14 +370,58 @@ private void retrieveFineBoxes () } } - //~ Inner Classes ------------------------------------------------------------------------------ + //---------------// + // SymbolAdapter // + //---------------// + private class SymbolAdapter + extends GlyphCluster.AbstractAdapter + { + + private final Scale scale = sheet.getScale(); + + SymbolAdapter (SimpleGraph graph) + { + super(graph); + } + + @Override + public void evaluateGlyph (Glyph glyph, + Set parts) + { + SymbolsBuilder.this.evaluateGlyph(glyph); + } + + @Override + public boolean isTooLarge (Rectangle symBox) + { + // Check width + if (symBox.width > params.maxSymbolWidth) { + return true; + } + + // Check height (not limited if on left of system: braces / brackets) + if (GeoUtil.centerOf(symBox).x < system.getLeft()) { + return false; + } else { + return symBox.height > params.maxSymbolHeight; + } + } + + @Override + public boolean isTooLight (int weight) + { + double normed = scale.pixelsToAreaFrac(weight); + + return !classifier.isBigEnough(normed); + } + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, @@ -426,7 +465,6 @@ private static final class Constants */ private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final double maxGap; @@ -440,8 +478,7 @@ private static class Parameters final int minFineWeight; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale) + Parameters (Scale scale) { maxGap = scale.toPixelsDouble(constants.maxGap); maxSymbolWidth = scale.toPixels(constants.maxSymbolWidth); @@ -456,52 +493,4 @@ public Parameters (Scale scale) } } - //---------------// - // SymbolAdapter // - //---------------// - private class SymbolAdapter - extends GlyphCluster.AbstractAdapter - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Scale scale = sheet.getScale(); - - //~ Constructors --------------------------------------------------------------------------- - public SymbolAdapter (SimpleGraph graph) - { - super(graph); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void evaluateGlyph (Glyph glyph, - Set parts) - { - SymbolsBuilder.this.evaluateGlyph(glyph); - } - - @Override - public boolean isTooLarge (Rectangle symBox) - { - // Check width - if (symBox.width > params.maxSymbolWidth) { - return true; - } - - // Check height (not limited if on left of system: braces / brackets) - if (GeoUtil.centerOf(symBox).x < system.getLeft()) { - return false; - } else { - return symBox.height > params.maxSymbolHeight; - } - } - - @Override - public boolean isTooLight (int weight) - { - double normed = scale.pixelsToAreaFrac(weight); - - return !classifier.isBigEnough(normed); - } - } } diff --git a/src/main/org/audiveris/omr/sheet/symbol/SymbolsFilter.java b/src/main/org/audiveris/omr/sheet/symbol/SymbolsFilter.java index ffcca853d..a06e69767 100644 --- a/src/main/org/audiveris/omr/sheet/symbol/SymbolsFilter.java +++ b/src/main/org/audiveris/omr/sheet/symbol/SymbolsFilter.java @@ -28,8 +28,8 @@ import org.audiveris.omr.constant.ConstantSet; import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.GlyphFactory; -import org.audiveris.omr.glyph.GlyphIndex; import org.audiveris.omr.glyph.GlyphGroup; +import org.audiveris.omr.glyph.GlyphIndex; import org.audiveris.omr.image.ImageUtil; import org.audiveris.omr.image.ShapeDescriptor; import org.audiveris.omr.image.Template; @@ -82,7 +82,6 @@ */ public class SymbolsFilter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -91,11 +90,9 @@ public class SymbolsFilter /** Orientation chosen for symbol runs. */ public static final Orientation SYMBOL_ORIENTATION = Orientation.VERTICAL; - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SymbolsFilter object. * @@ -106,7 +103,6 @@ public SymbolsFilter (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// @@ -185,7 +181,7 @@ private void buildSymbolsGlyphs (ByteProcessor buffer) private void dispatchPageSymbols (List glyphs) { final GlyphIndex glyphIndex = sheet.getGlyphIndex(); - final List relevants = new ArrayList(); + final List relevants = new ArrayList<>(); final SystemManager systemManager = sheet.getSystemManager(); for (Glyph glyph : glyphs) { @@ -201,33 +197,6 @@ private void dispatchPageSymbols (List glyphs) } } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean displaySymbols = new Constant.Boolean( - false, - "Should we display the symbols image?"); - - private final Constant.Boolean keepSymbolsBuffer = new Constant.Boolean( - false, - "Should we store symbols image on disk?"); - - private final Scale.Fraction staffVerticalMargin = new Scale.Fraction( - 0.5, - "Margin erased above & below staff header area"); - - private final Constant.Integer minWordLength = new Constant.Integer( - "letter count", - 4, - "Minimum number of chars in a sentence word"); - } - //--------// // MyView // //--------// @@ -237,25 +206,22 @@ private static final class Constants private class MyView extends ImageView { - //~ Instance fields ------------------------------------------------------------------------ // All optional glyphs. */ private final Set optionals; - //~ Constructors --------------------------------------------------------------------------- - public MyView (BufferedImage image, - Map> optionalMap) + MyView (BufferedImage image, + Map> optionalMap) { super(image); - optionals = new LinkedHashSet(); + optionals = new LinkedHashSet<>(); for (List glyphs : optionalMap.values()) { optionals.addAll(glyphs); } } - //~ Methods -------------------------------------------------------------------------------- @Override protected void renderItems (Graphics2D g) { @@ -281,6 +247,31 @@ protected void renderItems (Graphics2D g) } } + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean displaySymbols = new Constant.Boolean( + false, + "Should we display the symbols image?"); + + private final Constant.Boolean keepSymbolsBuffer = new Constant.Boolean( + false, + "Should we store symbols image on disk?"); + + private final Scale.Fraction staffVerticalMargin = new Scale.Fraction( + 0.5, + "Margin erased above & below staff header area"); + + private final Constant.Integer minWordLength = new Constant.Integer( + "letter count", + 4, + "Minimum number of chars in a sentence word"); + } + //----------------// // SymbolsCleaner // //----------------// @@ -302,7 +293,6 @@ protected void renderItems (Graphics2D g) private static class SymbolsCleaner extends PageCleaner { - //~ Instance fields ------------------------------------------------------------------------ /** * Current system list of weak glyphs. @@ -310,7 +300,6 @@ private static class SymbolsCleaner */ private List systemWeaks; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new {@code SymbolsEraser} object. * @@ -318,14 +307,13 @@ private static class SymbolsCleaner * @param g graphics context on buffer * @param sheet related sheet */ - public SymbolsCleaner (ByteProcessor buffer, - Graphics2D g, - Sheet sheet) + SymbolsCleaner (ByteProcessor buffer, + Graphics2D g, + Sheet sheet) { super(buffer, g, sheet); } - //~ Methods -------------------------------------------------------------------------------- //-------------// // eraseInters // //-------------// @@ -348,8 +336,8 @@ public void eraseInters (Map> weaksMap) eraseStavesHeader(system, constants.staffVerticalMargin); // Partition inters into strongs and weaks - final List strongs = new ArrayList(); - final List weaks = new ArrayList(); + final List strongs = new ArrayList<>(); + final List weaks = new ArrayList<>(); systemWeaks = null; for (Inter inter : sig.vertexSet()) { @@ -391,7 +379,7 @@ public void eraseInters (Map> weaksMap) } // Save the weaks apart and erase them - systemWeaks = new ArrayList(); + systemWeaks = new ArrayList<>(); weaksMap.put(system, systemWeaks); for (Inter inter : weaks) { @@ -485,7 +473,8 @@ protected void processGlyph (Glyph glyph) // Save the glyph? if (systemWeaks != null) { // The glyph may be made of several parts, so it's safer to restart from pixels - List glyphs = GlyphFactory.buildGlyphs(glyph.getRunTable(), + List glyphs = GlyphFactory.buildGlyphs( + glyph.getRunTable(), glyph.getTopLeft(), GlyphGroup.SYMBOL); systemWeaks.addAll(glyphs); @@ -516,7 +505,8 @@ private void savePixels (Rectangle box, RunTable runTable = factory.createTable(buf); // Glyphs - List glyphs = GlyphFactory.buildGlyphs(runTable, + List glyphs = GlyphFactory.buildGlyphs( + runTable, new Point(0, 0), GlyphGroup.SYMBOL); diff --git a/src/main/org/audiveris/omr/sheet/symbol/SymbolsLinker.java b/src/main/org/audiveris/omr/sheet/symbol/SymbolsLinker.java index 3aca823f2..281d44dd8 100644 --- a/src/main/org/audiveris/omr/sheet/symbol/SymbolsLinker.java +++ b/src/main/org/audiveris/omr/sheet/symbol/SymbolsLinker.java @@ -21,7 +21,6 @@ // package org.audiveris.omr.sheet.symbol; -import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.sheet.Part; import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sheet.Staff; @@ -52,7 +51,6 @@ import org.audiveris.omr.sig.relation.SlurHeadRelation; import org.audiveris.omr.text.TextRole; import org.audiveris.omr.util.HorizontalSide; - import static org.audiveris.omr.util.HorizontalSide.LEFT; import org.slf4j.Logger; @@ -62,7 +60,6 @@ import java.awt.Rectangle; import java.awt.geom.Line2D; import java.awt.geom.Point2D; -import java.util.Collection; import java.util.List; /** @@ -74,18 +71,15 @@ */ public class SymbolsLinker { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SymbolsLinker.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Dedicated system. */ private final SystemInfo system; /** SIG for the system. */ private final SIGraph sig; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SymbolsLinker} object. * @@ -98,7 +92,6 @@ public SymbolsLinker (SystemInfo system) sig = system.getSig(); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // linkOneSentence // //-----------------// @@ -205,6 +198,9 @@ public void linkOneSentence (SentenceInter sentence) //---------// // process // //---------// + /** + * Process all links. + */ public void process () { linkDynamics(); @@ -338,19 +334,9 @@ private void linkFermatas () // Look for a related barline if (!fermata.linkWithBarline()) { // Look for a chord (head or rest) related to this fermata - final Point center = fermata.getCenter(); - final Rectangle bounds = arc.getBounds(); - final MeasureStack stack = system.getStackAt(center); - final Collection chords = (fermata.getShape() == Shape.FERMATA_BELOW) - ? stack.getStandardChordsAbove( - center, - bounds) : stack.getStandardChordsBelow(center, bounds); - - if (!fermata.linkWithChords(chords)) { + if (!fermata.linkWithChord()) { // No link to barline, no link to chord, discard it - fermata.remove(); - arc.remove(); - dot.remove(); + fermata.remove(); // Which also removes arc and dot members } } } catch (Exception ex) { @@ -467,13 +453,10 @@ private void linkWedges () final Line2D topLine = wedge.getLine1(); for (HorizontalSide side : HorizontalSide.values()) { - final Point2D location = (side == LEFT) - ? new Point2D.Double( - topLine.getX1() + xMargin, - topLine.getY1()) - : new Point2D.Double( - topLine.getX2() - xMargin, - topLine.getY2()); + final Point2D location = (side == LEFT) ? new Point2D.Double( + topLine.getX1() + xMargin, + topLine.getY1()) + : new Point2D.Double(topLine.getX2() - xMargin, topLine.getY2()); final MeasureStack stack = system.getStackAt(location); final AbstractChordInter chordAbove = stack.getStandardChordAbove( location, diff --git a/src/main/org/audiveris/omr/sheet/symbol/SymbolsStep.java b/src/main/org/audiveris/omr/sheet/symbol/SymbolsStep.java index 0fb24b739..250d6d756 100644 --- a/src/main/org/audiveris/omr/sheet/symbol/SymbolsStep.java +++ b/src/main/org/audiveris/omr/sheet/symbol/SymbolsStep.java @@ -51,13 +51,11 @@ public class SymbolsStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(SymbolsStep.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SymbolsStep object. */ @@ -65,7 +63,6 @@ public SymbolsStep () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // displayUI // //-----------// @@ -96,7 +93,7 @@ public void doSystem (SystemInfo system, StopWatch watch = new StopWatch("SymbolsStep doSystem #" + system.getId()); watch.start("factory"); - final SymbolFactory factory = new SymbolFactory(system); + final InterFactory factory = new InterFactory(system); // Retrieve symbols inters watch.start("buildSymbols"); @@ -132,25 +129,25 @@ protected Context doProlog (Sheet sheet) return context; } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Context // //---------// + /** + * Context for step processing. + */ protected static class Context { - //~ Instance fields ------------------------------------------------------------------------ /** Map of optional (weak) glyphs per system. */ - public final Map> optionalsMap = new TreeMap>(); + public final Map> optionalsMap = new TreeMap<>(); } //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, diff --git a/src/main/org/audiveris/omr/sheet/ui/BinarizationBoard.java b/src/main/org/audiveris/omr/sheet/ui/BinarizationBoard.java index 3045f296c..d4734ad56 100644 --- a/src/main/org/audiveris/omr/sheet/ui/BinarizationBoard.java +++ b/src/main/org/audiveris/omr/sheet/ui/BinarizationBoard.java @@ -55,7 +55,6 @@ public class BinarizationBoard extends Board { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(BinarizationBoard.class); @@ -65,8 +64,6 @@ public class BinarizationBoard /** Format used for every double field. */ private static final String format = "%.2f"; - //~ Instance fields ---------------------------------------------------------------------------- - // /** The related sheet. */ private final Sheet sheet; @@ -83,7 +80,6 @@ public class BinarizationBoard /** Computed threshold. */ private final LDoubleField threshold = new LDoubleField(false, "Thres.", "Threshold", format); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BinarizationBoard object. * @@ -105,8 +101,6 @@ public BinarizationBoard (Sheet sheet) defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ - // //---------// // onEvent // //---------// diff --git a/src/main/org/audiveris/omr/sheet/ui/BookActions.java b/src/main/org/audiveris/omr/sheet/ui/BookActions.java index 0774d9d9c..75439387f 100644 --- a/src/main/org/audiveris/omr/sheet/ui/BookActions.java +++ b/src/main/org/audiveris/omr/sheet/ui/BookActions.java @@ -30,7 +30,7 @@ import org.audiveris.omr.plugin.Plugin; import org.audiveris.omr.plugin.PluginsManager; import org.audiveris.omr.score.ui.ScoreParameters; -import org.audiveris.omr.sheet.BasicSheet; +import org.audiveris.omr.score.ui.SheetParameters; import org.audiveris.omr.sheet.Book; import org.audiveris.omr.sheet.BookManager; import org.audiveris.omr.sheet.ExportPattern; @@ -100,26 +100,14 @@ public class BookActions extends StubDependent { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(BookActions.class); - /** Singleton. */ - private static BookActions INSTANCE; - - /** Should we rebuild the book on each user action. */ - private static final String REBUILD_ALLOWED = "rebuildAllowed"; - - /** Should we persist any manual assignment (for later training). */ - private static final String MANUAL_PERSISTED = "manualPersisted"; - /** Default parameter. */ public static final Param defaultPrompt = new Default(); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Flag to allow automatic book rebuild on every user edition action. */ private boolean rebuildAllowed = true; @@ -129,7 +117,6 @@ public class BookActions /** Sub-menu on books history. */ private final HistoryMenu bookHistoryMenu; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BookActions object. */ @@ -140,122 +127,6 @@ private BookActions () bookHistoryMenu = new HistoryMenu(mgr.getBookHistory(), LoadBookTask.class); } - //~ Methods ------------------------------------------------------------------------------------ - //-----------------// - // checkParameters // - //-----------------// - /** - * Make sure that the book parameters are properly set up, even by - * prompting the user for them, otherwise return false - * - * @param sheet the provided sheet - * @return true if OK, false otherwise - */ - public static boolean checkParameters (Sheet sheet) - { - // if (constants.promptParameters.getValue()) { - // return applyUserSettings(sheet); - // } else { - // return true; ///////////////////////////////////////////////////////////////////////////////////////////// - // ///return fillParametersWithDefaults(sheet.getBook()); - // } - return true; - } - - //-----------------// - // checkParameters // - //-----------------// - /** - * Make sure that the book parameters are properly set up, even by - * prompting the user for them, otherwise return false - * - * @param book the provided book - * @return true if OK, false otherwise - */ - public static boolean checkParameters (Book book) - { - // if (constants.promptParameters.getValue()) { - // return applyUserSettings(sheet); - // } else { - // return true; ///////////////////////////////////////////////////////////////////////////////////////////// - // ///return fillParametersWithDefaults(sheet.getBook()); - // } - return true; - } - - //-------------// - // checkStored // - //-------------// - /** - * Check whether the provided book has been saved if needed - * (and therefore, if it can be closed) - * - * @param book the book to check - * @return true if close is allowed, false if not - */ - public static boolean checkStored (Book book) - { - if (book.isModified() && defaultPrompt.getValue()) { - int answer = JOptionPane.showConfirmDialog( - OMR.gui.getFrame(), - "Save modified book " + book.getRadix() + "?"); - - if (answer == JOptionPane.YES_OPTION) { - Path bookPath; - - if (book.getBookPath() == null) { - // Find a suitable target file - bookPath = BookManager.getDefaultSavePath(book); - - // Check the target is fine - if (!confirmed(bookPath)) { - // Let the user select an alternate output file - bookPath = selectBookPath(true, BookManager.getDefaultSavePath(book)); - - if ((bookPath == null) || !confirmed(bookPath)) { - return false; // No suitable target found - } - } - } else { - bookPath = book.getBookPath(); - } - - try { - // Save the book to target file - book.store(bookPath, false); - - return true; // Book successfully saved - } catch (Exception ex) { - logger.warn("Error saving book", ex); - - return false; // Saving failed - } - } - - // Check whether user specifically chose NOT to save the book - return answer == JOptionPane.NO_OPTION; - } else { - return true; - } - } - - //-------------// - // getInstance // - //-------------// - /** - * Report the singleton - * - * @return the unique instance of this class - */ - public static synchronized BookActions getInstance () - { - if (INSTANCE == null) { - INSTANCE = new BookActions(); - } - - return INSTANCE; - } - //--------------// // annotateBook // //--------------// @@ -383,6 +254,67 @@ public void defineParameters (ActionEvent e) applyUserSettings(StubsController.getCurrentStub()); } + //-----------------------// + // defineSheetParameters // + //-----------------------// + /** + * Launch the dialog to set up sheet parameters. + * + * @param e the event that triggered this action + */ + @Action(enabledProperty = STUB_AVAILABLE) + public void defineSheetParameters (ActionEvent e) + { + final SheetStub stub = StubsController.getCurrentStub(); + + try { + final WrappedBoolean apply = new WrappedBoolean(false); + final SheetParameters sheetParams = new SheetParameters(stub.getSheet()); + final JOptionPane optionPane = new JOptionPane( + sheetParams.getComponent(), + JOptionPane.QUESTION_MESSAGE, + JOptionPane.OK_CANCEL_OPTION); + final String frameTitle = stub.getId() + " parameters"; + final JDialog dialog = new JDialog(OMR.gui.getFrame(), frameTitle, true); // Modal flag + dialog.setContentPane(optionPane); + dialog.setName("sheetParams"); + + optionPane.addPropertyChangeListener(new PropertyChangeListener() + { + @Override + public void propertyChange (PropertyChangeEvent e) + { + String prop = e.getPropertyName(); + + if (dialog.isVisible() && (e.getSource() == optionPane) + && (prop.equals(JOptionPane.VALUE_PROPERTY))) { + Object obj = optionPane.getValue(); + int value = (Integer) obj; + apply.set(value == JOptionPane.OK_OPTION); + + // Exit only if user gives up or enters correct data + if (!apply.isSet() || sheetParams.commit()) { + dialog.setVisible(false); + dialog.dispose(); + } else { + // Incorrect data, so don't exit yet + try { + // TODO: Is there a more civilized way? + optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE); + } catch (Exception ignored) { + } + } + } + } + }); + + dialog.pack(); + OmrGui.getApplication().show(dialog); + } catch (Exception ex) { + logger.warn("Error in SheetParameters", ex); + } + } + //-------------// // displayData // //-------------// @@ -418,17 +350,21 @@ public void displayNoStaff (ActionEvent e) return; } - SheetAssembly assembly = stub.getAssembly(); + final SheetAssembly assembly = stub.getAssembly(); + final SheetTab tab = SheetTab.NO_STAFF_TAB; - if (assembly.getPane(SheetTab.NO_STAFF_TAB.label) == null) { + if (assembly.getPane(tab.label) == null) { Sheet sheet = stub.getSheet(); // This may load the sheet... assembly.addViewTab( - SheetTab.NO_STAFF_TAB, + tab, new ScrollImageView( sheet, new ImageView( - sheet.getPicture().getSource(Picture.SourceKey.NO_STAFF).getBufferedImage())), + sheet.getPicture().getSource(Picture.SourceKey.NO_STAFF) + .getBufferedImage())), new BoardsPane(new PixelBoard(sheet))); + } else { + assembly.selectViewTab(tab); } } @@ -450,9 +386,9 @@ public void displayPicture (ActionEvent e) } if (stub.isDone(Step.BINARY)) { - ((BasicSheet) stub.getSheet()).createBinaryView(); + stub.getSheet().createBinaryView(); } else { - ((BasicSheet) stub.getSheet()).createPictureView(); + stub.getSheet().createPictureView(); } } @@ -473,16 +409,19 @@ public void displayStaffLineGlyphs (ActionEvent e) return; } - SheetAssembly assembly = stub.getAssembly(); + final SheetAssembly assembly = stub.getAssembly(); + final SheetTab tab = SheetTab.STAFF_LINE_TAB; - if (assembly.getPane(SheetTab.STAFF_LINE_TAB.label) == null) { + if (assembly.getPane(tab.label) == null) { Sheet sheet = stub.getSheet(); // This may load the sheet... assembly.addViewTab( - SheetTab.STAFF_LINE_TAB, + tab, new ScrollImageView( sheet, new ImageView(sheet.getPicture().buildStaffLineGlyphsImage())), new BoardsPane(new PixelBoard(sheet))); + } else { + assembly.selectViewTab(tab); } } @@ -703,14 +642,6 @@ public Task invokeDefaultPlugin (ActionEvent e) } } - //------------------// - // isRebuildAllowed // - //------------------// - public boolean isRebuildAllowed () - { - return rebuildAllowed; - } - //----------// // openBook // //----------// @@ -927,7 +858,7 @@ public Task printBookAs (ActionEvent e) true, OMR.gui.getFrame(), BookManager.getDefaultPrintPath(book), - new OmrFileFilter(OMR.PDF_EXTENSION), + new OmrFileFilter(OMR.PRINT_EXTENSION), "Choose book print target"); if ((bookPrintPath == null) || !confirmed(bookPrintPath)) { @@ -957,9 +888,9 @@ public Task printSheetAs (ActionEvent e) // Let the user select a PDF output file final Book book = stub.getBook(); - final String ext = OMR.PDF_EXTENSION; + final String ext = OMR.PRINT_EXTENSION; final Path defaultBookPath = BookManager.getDefaultPrintPath(book); - final Path bookSansExt = FileUtil.avoidExtensions(defaultBookPath, OMR.PDF_EXTENSION); + final Path bookSansExt = FileUtil.avoidExtensions(defaultBookPath, OMR.PRINT_EXTENSION); final String suffix = book.isMultiSheet() ? (OMR.SHEET_SUFFIX + stub.getNumber()) : ""; final Path defaultSheetPath = Paths.get(bookSansExt + suffix + ext); @@ -1016,7 +947,8 @@ public void resetBook (ActionEvent e) int answer = JOptionPane.showConfirmDialog( OMR.gui.getFrame(), "About to reset all valid sheets of " + book.getRadix() - + " to their initial state." + "\nDo you confirm?"); + + " to their initial state." + + "\nDo you confirm?"); if (answer == JOptionPane.YES_OPTION) { book.reset(); @@ -1041,7 +973,8 @@ public void resetBookToBinary (ActionEvent e) int answer = JOptionPane.showConfirmDialog( OMR.gui.getFrame(), "About to reset all valid sheets of " + book.getRadix() - + " to their BINARY state." + "\nDo you confirm?"); + + " to their BINARY state." + + "\nDo you confirm?"); if (answer == JOptionPane.YES_OPTION) { book.resetToBinary(); @@ -1065,8 +998,9 @@ public void resetSheet (ActionEvent e) if (stub != null) { int answer = JOptionPane.showConfirmDialog( OMR.gui.getFrame(), - "About to reset sheet " + stub.getId() + " to its initial state." - + "\nDo you confirm?"); + "About to reset sheet " + stub.getId() + + " to its initial state." + + "\nDo you confirm?"); if (answer == JOptionPane.YES_OPTION) { stub.reset(); @@ -1090,8 +1024,9 @@ public void resetSheetToBinary (ActionEvent e) if (stub != null) { int answer = JOptionPane.showConfirmDialog( OMR.gui.getFrame(), - "About to reset sheet " + stub.getId() + " to its BINARY state." - + "\nDo you confirm?"); + "About to reset sheet " + stub.getId() + + " to its BINARY state." + + "\nDo you confirm?"); if (answer == JOptionPane.YES_OPTION) { stub.resetToBinary(); @@ -1138,7 +1073,7 @@ public Task sampleSheet (ActionEvent e) * @param e the event that triggered this action * @return the UI task to perform */ - @Action(enabledProperty = BOOK_MODIFIED) + @Action(enabledProperty = BOOK_MODIFIED_OR_UPGRADED) public Task saveBook (ActionEvent e) { final Book book = StubsController.getCurrentBook(); @@ -1149,9 +1084,8 @@ public Task saveBook (ActionEvent e) final Path bookPath = BookManager.getDefaultSavePath(book); - if ((book.getBookPath() != null) - && (bookPath.toAbsolutePath().equals(book.getBookPath().toAbsolutePath()) - || confirmed(bookPath))) { + if ((book.getBookPath() != null) && (bookPath.toAbsolutePath().equals( + book.getBookPath().toAbsolutePath()) || confirmed(bookPath))) { return new StoreBookTask(book, bookPath); } @@ -1175,9 +1109,8 @@ public Task saveBookAs (ActionEvent e) final Path targetPath = selectBookPath(true, defaultBookPath); final Path ownPath = book.getBookPath(); - if ((targetPath != null) - && (((ownPath != null) && ownPath.toAbsolutePath().equals(targetPath.toAbsolutePath())) - || confirmed(targetPath))) { + if ((targetPath != null) && (((ownPath != null) && ownPath.toAbsolutePath().equals( + targetPath.toAbsolutePath())) || confirmed(targetPath))) { return new StoreBookTask(book, targetPath); } @@ -1193,7 +1126,7 @@ public Task saveBookAs (ActionEvent e) * @param e the event that triggered this action * @return the UI task to perform */ - @Action(enabledProperty = BOOK_MODIFIED) + @Action(enabledProperty = BOOK_MODIFIED_OR_UPGRADED) public Task saveBookRepository (ActionEvent e) { final Book book = StubsController.getCurrentBook(); @@ -1213,16 +1146,6 @@ public Task saveBookRepository (ActionEvent e) return null; } - //-------------------// - // setRebuildAllowed // - //-------------------// - public void setRebuildAllowed (boolean value) - { - boolean oldValue = this.rebuildAllowed; - this.rebuildAllowed = value; - firePropertyChange(REBUILD_ALLOWED, oldValue, value); - } - //------------// // swapSheets // //------------// @@ -1243,19 +1166,6 @@ public void swapSheets (ActionEvent e) book.swapAllSheets(); } - //---------------// - // toggleRebuild // - //---------------// - /** - * Action that toggles the rebuild of book on every user edition - * - * @param e the event that triggered this action - */ - @Action(selectedProperty = REBUILD_ALLOWED) - public void toggleRebuild (ActionEvent e) - { - } - //----------------// // transcribeBook // //----------------// @@ -1422,6 +1332,129 @@ public void zoomWidth (ActionEvent e) scrollView.fitWidth(); } + //-----------------// + // checkParameters // + //-----------------// + /** + * Make sure that the book parameters are properly set up, even by + * prompting the user for them, otherwise return false + * + * @param sheet the provided sheet + * @return true if OK, false otherwise + */ + public static boolean checkParameters (Sheet sheet) + { + // if (constants.promptParameters.getValue()) { + // return applyUserSettings(sheet); + // } else { + // return true; ///////////////////////////////////////////////////////////////////////////////////////////// + // ///return fillParametersWithDefaults(sheet.getBook()); + // } + return true; + } + + //-----------------// + // checkParameters // + //-----------------// + /** + * Make sure that the book parameters are properly set up, even by + * prompting the user for them, otherwise return false + * + * @param book the provided book + * @return true if OK, false otherwise + */ + public static boolean checkParameters (Book book) + { + // if (constants.promptParameters.getValue()) { + // return applyUserSettings(sheet); + // } else { + // return true; ///////////////////////////////////////////////////////////////////////////////////////////// + // ///return fillParametersWithDefaults(sheet.getBook()); + // } + return true; + } + + //-------------// + // checkStored // + //-------------// + /** + * Check whether the provided book has been saved if needed + * (and therefore, if it can be closed) + * + * @param book the book to check + * @return true if close is allowed, false if not + */ + public static boolean checkStored (Book book) + { + final String bookStatus = book.isModified() ? "modified" + : (book.isUpgraded() ? "upgraded" : null); + + if (bookStatus != null && defaultPrompt.getValue()) { + int answer = JOptionPane.showConfirmDialog( + OMR.gui.getFrame(), + "Save " + bookStatus + " book " + book.getRadix() + "?"); + + if (answer == JOptionPane.YES_OPTION) { + Path bookPath; + + if (book.getBookPath() == null) { + // Find a suitable target file + bookPath = BookManager.getDefaultSavePath(book); + + // Check the target is fine + if (!confirmed(bookPath)) { + // Let the user select an alternate output file + bookPath = selectBookPath(true, BookManager.getDefaultSavePath(book)); + + if ((bookPath == null) || !confirmed(bookPath)) { + return false; // No suitable target found + } + } + } else { + bookPath = book.getBookPath(); + } + + try { + // Save the book to target file + book.store(bookPath, false); + + return true; // Book successfully saved + } catch (Exception ex) { + logger.warn("Error saving book", ex); + + return false; // Saving failed + } + } + + // Check whether user specifically chose NOT to save the book + return answer == JOptionPane.NO_OPTION; + } else { + return true; + } + } + + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of BookActions in the application. + * + * @return the instance + */ + public static BookActions getInstance () + { + return LazySingleton.INSTANCE; + } + + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { + + static final BookActions INSTANCE = new BookActions(); + } + //-------------------// // applyUserSettings // //-------------------// @@ -1441,24 +1474,21 @@ private static boolean applyUserSettings (final SheetStub stub) scoreParams.getComponent(), JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION); - final String frameTitle = (stub != null) - ? (stub.getBook().getRadix() + " parameters") + final String frameTitle = (stub != null) ? (stub.getBook().getRadix() + " parameters") : "General parameters"; final JDialog dialog = new JDialog(OMR.gui.getFrame(), frameTitle, true); // Modal flag dialog.setContentPane(optionPane); dialog.setName("scoreParams"); - optionPane.addPropertyChangeListener( - new PropertyChangeListener() + optionPane.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange (PropertyChangeEvent e) { String prop = e.getPropertyName(); - if (dialog.isVisible() - && (e.getSource() == optionPane) - && (prop.equals(JOptionPane.VALUE_PROPERTY))) { + if (dialog.isVisible() && (e.getSource() == optionPane) + && (prop.equals(JOptionPane.VALUE_PROPERTY))) { Object obj = optionPane.getValue(); int value = (Integer) obj; apply.set(value == JOptionPane.OK_OPTION); @@ -1535,7 +1565,6 @@ private static Path selectBookPath (boolean save, return (prjPath == null) ? null : prjPath; } - //~ Inner Classes ------------------------------------------------------------------------------ //--------------// // LoadBookTask // //--------------// @@ -1545,7 +1574,6 @@ private static Path selectBookPath (boolean save, public static class LoadBookTask extends PathTask { - //~ Constructors --------------------------------------------------------------------------- public LoadBookTask (Path path) { @@ -1556,7 +1584,6 @@ public LoadBookTask () { } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1597,7 +1624,6 @@ protected Void doInBackground () public static class LoadImageTask extends PathTask { - //~ Constructors --------------------------------------------------------------------------- public LoadImageTask (Path path) { @@ -1608,7 +1634,6 @@ public LoadImageTask () { } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1642,13 +1667,11 @@ protected Void doInBackground () public static class PrintBookTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ final Book book; final Path bookPrintPath; - //~ Constructors --------------------------------------------------------------------------- public PrintBookTask (Book book, Path bookPrintPath) { @@ -1656,7 +1679,6 @@ public PrintBookTask (Book book, this.bookPrintPath = bookPrintPath; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1679,13 +1701,11 @@ protected Void doInBackground () public static class PrintSheetTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ final Sheet sheet; final Path sheetPrintPath; - //~ Constructors --------------------------------------------------------------------------- public PrintSheetTask (Sheet sheet, Path sheetPrintPath) { @@ -1693,7 +1713,6 @@ public PrintSheetTask (Sheet sheet, this.sheetPrintPath = sheetPrintPath; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1715,17 +1734,14 @@ protected Void doInBackground () public static class SampleSheetTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ final Sheet sheet; - //~ Constructors --------------------------------------------------------------------------- public SampleSheetTask (Sheet sheet) { this.sheet = sheet; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1747,22 +1763,19 @@ protected Void doInBackground () private static class CloseBookTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ final Book book; - //~ Constructors --------------------------------------------------------------------------- /** * Create an asynchronous task to close the book. * * @param book the book to close */ - public CloseBookTask (Book book) + CloseBookTask (Book book) { this.book = book; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1781,10 +1794,9 @@ protected Void doInBackground () //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean promptParameters = new Constant.Boolean( false, @@ -1805,7 +1817,6 @@ private static final class Constants private static class Default extends Param { - //~ Methods -------------------------------------------------------------------------------- @Override public Boolean getSpecific () @@ -1851,25 +1862,22 @@ public boolean setSpecific (Boolean specific) private static class ExportBookTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ final Book book; - //~ Constructors --------------------------------------------------------------------------- /** * Create an asynchronous task to export the book. * * @param book the book to export * @param bookPathSansExt (non-null) the target export book path with no extension */ - public ExportBookTask (Book book, - Path bookPathSansExt) + ExportBookTask (Book book, + Path bookPathSansExt) { this.book = book; book.setExportPathSansExt(bookPathSansExt); } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1894,21 +1902,18 @@ protected Void doInBackground () private static class ExportSheetTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ final Sheet sheet; final Path sheetExportPath; - //~ Constructors --------------------------------------------------------------------------- - public ExportSheetTask (Sheet sheet, - Path sheetExportPath) + ExportSheetTask (Sheet sheet, + Path sheetExportPath) { this.sheet = sheet; this.sheetExportPath = sheetExportPath; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1934,17 +1939,14 @@ protected Void doInBackground () private static class RebuildTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ private final Sheet sheet; - //~ Constructors --------------------------------------------------------------------------- - public RebuildTask (Sheet sheet) + RebuildTask (Sheet sheet) { this.sheet = sheet; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1966,17 +1968,14 @@ protected Void doInBackground () private static class SampleBookTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ final Book book; - //~ Constructors --------------------------------------------------------------------------- - public SampleBookTask (Book book) + SampleBookTask (Book book) { this.book = book; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -1998,27 +1997,24 @@ protected Void doInBackground () private static class StoreBookTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ final Book book; final Path bookPath; - //~ Constructors --------------------------------------------------------------------------- /** * Create an asynchronous task to store the book. * * @param book the book to export * @param bookPath (non-null) the target to store book path */ - public StoreBookTask (Book book, - Path bookPath) + StoreBookTask (Book book, + Path bookPath) { this.book = book; this.bookPath = bookPath; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -2026,7 +2022,7 @@ protected Void doInBackground () try { LogUtil.start(book); book.store(bookPath, false); - BookActions.getInstance().setBookModified(false); + BookActions.getInstance().setBookModifiedOrUpgraded(false); } finally { LogUtil.stopBook(); } @@ -2041,20 +2037,17 @@ protected Void doInBackground () private static class TranscribeBookTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ private final SheetStub stub; private final Book book; - //~ Constructors --------------------------------------------------------------------------- - public TranscribeBookTask (SheetStub stub) + TranscribeBookTask (SheetStub stub) { this.stub = stub; this.book = stub.getBook(); } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException @@ -2084,17 +2077,14 @@ protected void finished () private static class TranscribeSheetTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ private final Sheet sheet; - //~ Constructors --------------------------------------------------------------------------- - public TranscribeSheetTask (Sheet sheet) + TranscribeSheetTask (Sheet sheet) { this.sheet = sheet; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws InterruptedException diff --git a/src/main/org/audiveris/omr/sheet/ui/BookBrowser.java b/src/main/org/audiveris/omr/sheet/ui/BookBrowser.java index 60f5c6bb1..c59a0ae6a 100644 --- a/src/main/org/audiveris/omr/sheet/ui/BookBrowser.java +++ b/src/main/org/audiveris/omr/sheet/ui/BookBrowser.java @@ -48,6 +48,7 @@ import java.awt.BorderLayout; import java.awt.event.ActionEvent; +import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; @@ -81,7 +82,6 @@ */ public class BookBrowser { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -90,7 +90,6 @@ public class BookBrowser /** The filter for relevant classes and fields. */ private static final Relevance filter = new PackageRelevance(Main.class.getPackage()); - //~ Instance fields ---------------------------------------------------------------------------- /** Concrete UI component. */ private final JPanel component; @@ -101,7 +100,7 @@ public class BookBrowser private final Book book; /** Cache to avoid recomputing sets of children. */ - private final HashMap> nodeMap = new HashMap>(); + private final HashMap> nodeMap = new HashMap<>(); /** The tree model. */ private final Model model; @@ -109,7 +108,6 @@ public class BookBrowser /** The enclosing frame. */ private JFrame frame; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code BookBrowser} object. * @@ -162,10 +160,12 @@ public BookBrowser (Book book) component.add("Center", splitPane); } - //~ Methods ------------------------------------------------------------------------------------ //-------// // close // //-------// + /** + * Close the browser. + */ public void close () { if (frame != null) { @@ -195,8 +195,7 @@ public JFrame getFrame () frame.getContentPane().add(toolBar, BorderLayout.NORTH); // Set up the views, and display it all - JButton refreshButton = new JButton( - new AbstractAction() + JButton refreshButton = new JButton(new AbstractAction() { @Override public void actionPerformed (ActionEvent e) @@ -228,74 +227,6 @@ public void refresh () model.refreshAll(); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean hideEmptyDummies = new Constant.Boolean( - false, - "Should we hide empty dummy containers"); - } - - //-----------------// - // NamedCollection // - //-----------------// - private static class NamedCollection - { - //~ Instance fields ------------------------------------------------------------------------ - - private final String name; - - private final Collection collection; - - //~ Constructors --------------------------------------------------------------------------- - public NamedCollection (String name, - Collection collection) - { - this.name = name; - this.collection = collection; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return name; - } - } - - //-----------// - // NamedData // - //-----------// - private static class NamedData - { - //~ Instance fields ------------------------------------------------------------------------ - - private final String name; - - private final Object data; - - //~ Constructors --------------------------------------------------------------------------- - public NamedData (String name, - Object data) - { - this.name = name; - this.data = data; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return name + ":" + data; - } - } - //-------// // Model // //-------// @@ -303,19 +234,16 @@ public String toString () private class Model implements TreeModel { - //~ Instance fields ------------------------------------------------------------------------ - private final List listeners = new ArrayList(); + private final List listeners = new ArrayList<>(); private final Book book; - //~ Constructors --------------------------------------------------------------------------- - public Model (Book book) + Model (Book book) { this.book = book; } - //~ Methods -------------------------------------------------------------------------------- //----------------------// // addTreeModelListener // //----------------------// @@ -441,35 +369,27 @@ private List getRelevantChildren (Object node) { // First check the cache List relevants = nodeMap.get(node); - if (relevants != null) { return relevants; } - // Not found, so let's build it logger.debug("Retrieving relevants of {} {}", node, node.getClass()); - // Case of Named Collection if (node instanceof NamedCollection) { logger.debug("named collection: " + node); - NamedCollection nc = (NamedCollection) node; - relevants = new ArrayList(); + relevants = new ArrayList<>(); nodeMap.put(node, relevants); - for (Object n : nc.collection) { if (isRelevant(n)) { relevants.add(n); } } - if (logger.isDebugEnabled()) { logger.debug("{} nb={}", node, relevants.size()); } - return relevants; } - // Case of Named Data if (node instanceof NamedData) { logger.debug("named data: " + node); @@ -482,19 +402,16 @@ private List getRelevantChildren (Object node) return relevants; } - ///logger.info("standard node: " + node); Class classe = node.getClass(); - relevants = new ArrayList(); + relevants = new ArrayList<>(); nodeMap.put(node, relevants); - // Walk up the inheritance tree do { // Browse the declared fields of the class at hand for (Field field : classe.getDeclaredFields()) { // Skip field if annotated as non navigable Navigable navigable = field.getAnnotation(Navigable.class); - if ((navigable != null) && (navigable.value() == false)) { if (logger.isDebugEnabled()) { logger.debug("skipping {}", field); @@ -502,7 +419,6 @@ private List getRelevantChildren (Object node) continue; } - ///logger.info("fieldName:" + field.getName()); try { // No static or inner class @@ -547,19 +463,18 @@ private List getRelevantChildren (Object node) ///System.out.println(" ...OK"); relevants.add(new NamedData(field.getName(), object)); - } catch (Exception ex) { + } catch (IllegalAccessException | + IllegalArgumentException | + SecurityException ex) { logger.warn("Error in accessing field", ex); } } - // Walk up the inheritance tree classe = classe.getSuperclass(); } while (filter.isClassRelevant(classe)); - if (logger.isDebugEnabled()) { logger.debug("{} nb={}", node, relevants.size()); } - return relevants; } @@ -589,22 +504,18 @@ private boolean isRelevant (Object node) private class SelectionListener implements TreeSelectionListener { - //~ Methods -------------------------------------------------------------------------------- @Override public void valueChanged (TreeSelectionEvent e) { try { TreePath p = e.getNewLeadSelectionPath(); - if (p != null) { Object obj = p.getLastPathComponent(); - if (obj instanceof NamedData) { NamedData nd = (NamedData) obj; obj = nd.data; } - // Publish selection? if (obj instanceof Inter) { Inter inter = (Inter) obj; @@ -642,9 +553,8 @@ public void valueChanged (TreeSelectionEvent e) null)); } } - if (obj instanceof WeakReference) { - Object o = ((WeakReference) obj).get(); + Object o = ((Reference) obj).get(); htmlPane.setText(new Dumper.Html(filter, o).toString()); } else { htmlPane.setText(new Dumper.Html(filter, obj).toString()); @@ -655,4 +565,65 @@ public void valueChanged (TreeSelectionEvent e) } } } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean hideEmptyDummies = new Constant.Boolean( + false, + "Should we hide empty dummy containers"); + } + + //-----------------// + // NamedCollection // + //-----------------// + private static class NamedCollection + { + + private final String name; + + private final Collection collection; + + NamedCollection (String name, + Collection collection) + { + this.name = name; + this.collection = collection; + } + + @Override + public String toString () + { + return name; + } + } + + //-----------// + // NamedData // + //-----------// + private static class NamedData + { + + private final String name; + + private final Object data; + + NamedData (String name, + Object data) + { + this.name = name; + this.data = data; + } + + @Override + public String toString () + { + return name + ":" + data; + } + } + } diff --git a/src/main/org/audiveris/omr/sheet/ui/DeltaView.java b/src/main/org/audiveris/omr/sheet/ui/DeltaView.java index 13928af59..820a03836 100644 --- a/src/main/org/audiveris/omr/sheet/ui/DeltaView.java +++ b/src/main/org/audiveris/omr/sheet/ui/DeltaView.java @@ -54,18 +54,15 @@ public class DeltaView extends ScrollView implements PropertyChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(DeltaView.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying sheet. */ private final Sheet sheet; /** View parameters. */ private final ViewParameters viewParams = ViewParameters.getInstance(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new {@code DeltaView} instance, dedicated to a sheet. * @@ -90,7 +87,6 @@ public DeltaView (Sheet sheet) setView(view); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // propertyChange // //----------------// @@ -100,21 +96,18 @@ public void propertyChange (PropertyChangeEvent evt) view.repaint(); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // MyView // //--------// private class MyView extends RubberPanel { - //~ Constructors --------------------------------------------------------------------------- - public MyView () + MyView () { setModelSize(new Dimension(sheet.getWidth(), sheet.getHeight())); } - //~ Methods -------------------------------------------------------------------------------- //--------// // render // //--------// diff --git a/src/main/org/audiveris/omr/sheet/ui/DistanceBoard.java b/src/main/org/audiveris/omr/sheet/ui/DistanceBoard.java index 7eeebadf1..d832b7b70 100644 --- a/src/main/org/audiveris/omr/sheet/ui/DistanceBoard.java +++ b/src/main/org/audiveris/omr/sheet/ui/DistanceBoard.java @@ -41,15 +41,12 @@ public class DistanceBoard extends PixelBoard { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(DistanceBoard.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The distance table to browse. */ private final DistanceTable table; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a {@code DistanceBoard} object. * @@ -67,7 +64,6 @@ public DistanceBoard (Sheet sheet, level.getLabel().setToolTipText("Distance to foreground"); } - //~ Methods ------------------------------------------------------------------------------------ //---------------------// // handleLocationEvent // //---------------------// diff --git a/src/main/org/audiveris/omr/sheet/ui/ExtractionMenu.java b/src/main/org/audiveris/omr/sheet/ui/ExtractionMenu.java index 13cbfc6f7..4d3304aab 100644 --- a/src/main/org/audiveris/omr/sheet/ui/ExtractionMenu.java +++ b/src/main/org/audiveris/omr/sheet/ui/ExtractionMenu.java @@ -55,15 +55,12 @@ public class ExtractionMenu extends LocationDependentMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ExtractionMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying sheet. */ private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Create the extraction menu * @@ -78,7 +75,6 @@ public ExtractionMenu (Sheet sheet) add(new JMenuItem(new AreaAction())); // Save just a rectangle of sheet } - //~ Methods ------------------------------------------------------------------------------------ //------// // save // //------// @@ -108,7 +104,6 @@ private void save (BufferedImage img) logger.info("Extraction stored as {}", file); } - //~ Inner Classes ------------------------------------------------------------------------------ //------------// // AreaAction // //------------// @@ -119,26 +114,23 @@ private class AreaAction extends AbstractAction implements LocationDependent { - //~ Instance fields ------------------------------------------------------------------------ /** Clamped area. */ private Rectangle area; - //~ Constructors --------------------------------------------------------------------------- - public AreaAction () + AreaAction () { putValue(SHORT_DESCRIPTION, "Save the selected area to disk"); setEnabled(false); // By default } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { try { // Extract the area selected from initial image save(sheet.getPicture().getImage(area)); - } catch (Exception ex) { + } catch (IOException ex) { logger.warn("Error in area extraction, " + ex, ex); } } @@ -161,6 +153,13 @@ public void updateUserLocation (Rectangle rect) putValue(NAME, "no area selected"); } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } //-------------// @@ -172,24 +171,29 @@ public void updateUserLocation (Rectangle rect) private class WholeAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- - public WholeAction () + WholeAction () { putValue(NAME, "Whole sheet"); putValue(SHORT_DESCRIPTION, "Save the whole sheet to disk"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { try { // Extract the whole initial image save(sheet.getPicture().getImage(null)); - } catch (Exception ex) { + } catch (IOException ex) { logger.warn("Error in sheet extraction, " + ex, ex); } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } } diff --git a/src/main/org/audiveris/omr/sheet/ui/ImageView.java b/src/main/org/audiveris/omr/sheet/ui/ImageView.java index 1ea4ceb5b..fcf8e9bc0 100644 --- a/src/main/org/audiveris/omr/sheet/ui/ImageView.java +++ b/src/main/org/audiveris/omr/sheet/ui/ImageView.java @@ -36,11 +36,9 @@ public class ImageView extends RubberPanel { - //~ Instance fields ---------------------------------------------------------------------------- private final RenderedImage image; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ImageView object. * @@ -56,7 +54,6 @@ public ImageView (RenderedImage image) setModelSize(new Dimension(image.getWidth(), image.getHeight())); } - //~ Methods ------------------------------------------------------------------------------------ @Override public void render (Graphics2D g) { diff --git a/src/main/org/audiveris/omr/sheet/ui/LagController.java b/src/main/org/audiveris/omr/sheet/ui/LagController.java index 34df2d26e..e9006794a 100644 --- a/src/main/org/audiveris/omr/sheet/ui/LagController.java +++ b/src/main/org/audiveris/omr/sheet/ui/LagController.java @@ -23,7 +23,6 @@ import org.audiveris.omr.glyph.GlyphIndex; import org.audiveris.omr.glyph.GlyphsModel; -import org.audiveris.omr.glyph.ui.GlyphService; import org.audiveris.omr.glyph.ui.GlyphsController; import org.audiveris.omr.glyph.ui.NestView; import org.audiveris.omr.glyph.ui.SymbolGlyphBoard; @@ -44,7 +43,6 @@ public class LagController extends GlyphsController { - //~ Instance fields ---------------------------------------------------------------------------- /** The underlying lag. */ private final Lag lag; @@ -55,7 +53,6 @@ public class LagController /** Related user display if any */ private MyView view; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code LagController} object. * @@ -67,12 +64,11 @@ public LagController (Sheet sheet, Lag lag, SheetTab tab) { - super(new GlyphsModel(sheet, (GlyphService) sheet.getGlyphIndex().getEntityService())); + super(new GlyphsModel(sheet, sheet.getGlyphIndex().getEntityService())); this.lag = lag; this.tab = tab; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // refresh // //---------// @@ -83,7 +79,7 @@ public void refresh () { if (view == null) { displayFrame(); - } else if (view != null) { + } else { view.repaint(); } } @@ -105,16 +101,14 @@ private void displayFrame () new SymbolGlyphBoard(this, true, true))); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // MyView // //--------// - private final class MyView + private class MyView extends NestView { - //~ Constructors --------------------------------------------------------------------------- - public MyView (GlyphIndex glyphIndex) + MyView (GlyphIndex glyphIndex) { super(glyphIndex.getEntityService(), Arrays.asList(lag), sheet); diff --git a/src/main/org/audiveris/omr/sheet/ui/PictureView.java b/src/main/org/audiveris/omr/sheet/ui/PictureView.java index 3547e996d..9b1a96947 100644 --- a/src/main/org/audiveris/omr/sheet/ui/PictureView.java +++ b/src/main/org/audiveris/omr/sheet/ui/PictureView.java @@ -62,18 +62,15 @@ public class PictureView extends ScrollView implements PropertyChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(PictureView.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Link with sheet. */ private final Sheet sheet; /** Pop-up page menu. */ private final SheetPopupMenu pageMenu; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new {@code PictureView} instance, dedicated to a sheet. * @@ -101,7 +98,6 @@ public PictureView (Sheet sheet) pageMenu.addMenu(new ExtractionMenu(sheet)); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // propertyChange // //----------------// @@ -111,14 +107,12 @@ public void propertyChange (PropertyChangeEvent evt) view.repaint(); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // MyView // //--------// private class MyView extends RubberPanel { - //~ Methods -------------------------------------------------------------------------------- //-----------------// // contextSelected // diff --git a/src/main/org/audiveris/omr/sheet/ui/PixelBoard.java b/src/main/org/audiveris/omr/sheet/ui/PixelBoard.java index 20e95dd07..1afcda54c 100644 --- a/src/main/org/audiveris/omr/sheet/ui/PixelBoard.java +++ b/src/main/org/audiveris/omr/sheet/ui/PixelBoard.java @@ -56,16 +56,17 @@ public class PixelBoard extends Board { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(PixelBoard.class); /** Events this board is interested in. */ private static final Class[] eventsRead = new Class[]{ - LocationEvent.class, PixelEvent.class - }; + LocationEvent.class, + PixelEvent.class}; + + /** Pixel level. */ + protected final LIntegerField level = new LIntegerField(false, "Level", "Pixel level"); - //~ Instance fields ---------------------------------------------------------------------------- /** Abscissa of upper Left point. */ private final LIntegerField x = new LIntegerField("X", "Abscissa of upper left corner"); @@ -78,10 +79,6 @@ public class PixelBoard /** Height of rectangle. */ private final LIntegerField height = new LIntegerField("Height", "Height of rectangle"); - /** Pixel level. */ - protected final LIntegerField level = new LIntegerField(false, "Level", "Pixel level"); - - //~ Constructors ------------------------------------------------------------------------------- /** * Create a PixelBoard, pre-selected by default * @@ -112,7 +109,6 @@ public PixelBoard (Sheet sheet, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // onEvent // //---------// @@ -223,14 +219,9 @@ private void defineLayout () builder.add(height.getField(), cst.xy(11, r)); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-------------// - // ParamAction // - //-------------// private class ParamAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- // Method run whenever user presses Return/Enter in one of the parameter fields @Override @@ -242,7 +233,18 @@ public void actionPerformed (ActionEvent e) PixelBoard.this, SelectionHint.LOCATION_INIT, MouseMovement.PRESSING, - new Rectangle(x.getValue(), y.getValue(), width.getValue(), height.getValue()))); + new Rectangle( + x.getValue(), + y.getValue(), + width.getValue(), + height.getValue()))); + } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. } } } diff --git a/src/main/org/audiveris/omr/sheet/ui/RunsViewer.java b/src/main/org/audiveris/omr/sheet/ui/RunsViewer.java index 1ba96862c..9c9293c33 100644 --- a/src/main/org/audiveris/omr/sheet/ui/RunsViewer.java +++ b/src/main/org/audiveris/omr/sheet/ui/RunsViewer.java @@ -40,12 +40,10 @@ */ public class RunsViewer { - //~ Instance fields ---------------------------------------------------------------------------- /** The related sheet */ private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new RunsViewer object. * @@ -56,7 +54,6 @@ public RunsViewer (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // display // //---------// @@ -85,7 +82,6 @@ public void display (String name, sheet.getStub().getAssembly().addViewTab(name, new ScrollView(view), boards); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------------// // MyRunsTableView // //-----------------// @@ -96,15 +92,13 @@ public void display (String name, private class MyRunsTableView extends RunTableView { - //~ Constructors --------------------------------------------------------------------------- - public MyRunsTableView (String name, - RunTable table) + MyRunsTableView (String name, + RunTable table) { super(name, table, sheet.getLocationService()); } - //~ Methods -------------------------------------------------------------------------------- @Override protected void renderItems (Graphics2D g) { diff --git a/src/main/org/audiveris/omr/sheet/ui/ScrollImageView.java b/src/main/org/audiveris/omr/sheet/ui/ScrollImageView.java index 20c030b08..57bd978cb 100644 --- a/src/main/org/audiveris/omr/sheet/ui/ScrollImageView.java +++ b/src/main/org/audiveris/omr/sheet/ui/ScrollImageView.java @@ -32,7 +32,6 @@ public class ScrollImageView extends ScrollView { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ScrollImageView object. diff --git a/src/main/org/audiveris/omr/sheet/ui/SelectionPainter.java b/src/main/org/audiveris/omr/sheet/ui/SelectionPainter.java index 274c5bfbc..ef5cc0f8f 100644 --- a/src/main/org/audiveris/omr/sheet/ui/SelectionPainter.java +++ b/src/main/org/audiveris/omr/sheet/ui/SelectionPainter.java @@ -1,178 +1,178 @@ -//------------------------------------------------------------------------------------------------// -// // -// S e l e c t i o n P a i n t e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sheet.ui; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.math.GeoUtil; -import org.audiveris.omr.sheet.Scale; -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.relation.NoExclusion; -import org.audiveris.omr.sig.relation.Relation; -import org.audiveris.omr.sig.relation.Relations; -import org.audiveris.omr.sig.ui.SigPainter; -import static org.audiveris.omr.ui.symbol.Alignment.AREA_CENTER; -import org.audiveris.omr.ui.util.UIUtil; - -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Point; -import java.awt.Stroke; -import java.awt.font.TextLayout; -import java.awt.geom.AffineTransform; -import java.awt.geom.Ellipse2D; -import java.awt.geom.Line2D; - -/** - * Class {@code SelectionPainter} is meant to paint just selected items. - * - * @author Hervé Bitteur - */ -public class SelectionPainter - extends SheetPainter -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SelectionPainter} object. - * - * @param sheet the sheet to paint - * @param g Graphic context - */ - public SelectionPainter (Sheet sheet, - Graphics g) - { - super(sheet, g); - - sigPainter = new SelectionSigPainter(g, sheet.getScale()); - } - - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // drawSupport // - //-------------// - /** - * Draw the support relation between two inters. - * - * @param one first inter - * @param two second inter - * @param supportClass provided support class - * @param potential true if support is just potential - */ - public void drawSupport (Inter one, - Inter two, - Class supportClass, - boolean potential) - { - // Draw support line, using dash and specific color for potential relation - final Stroke oldStroke = potential ? UIUtil.setAbsoluteDashedStroke(g, 1f) - : UIUtil.setAbsoluteStroke(g, 1f); - g.setColor( - potential ? Color.PINK - : (NoExclusion.class.isAssignableFrom(supportClass) ? Color.GRAY : Color.GREEN)); - - final double r = 2; // Radius - final Point oneCenter = one.getRelationCenter(); - Ellipse2D e1 = new Ellipse2D.Double(oneCenter.x - r, oneCenter.y - r, 2 * r, 2 * r); - g.fill(e1); - - final Point twoCenter = two.getRelationCenter(); - Ellipse2D e2 = new Ellipse2D.Double(twoCenter.x - r, twoCenter.y - r, 2 * r, 2 * r); - g.fill(e2); - - final Line2D line = new Line2D.Double(oneCenter, twoCenter); - g.draw(line); - - // Print support name at center of line? - final double zoom = g.getTransform().getScaleX(); - - if (zoom >= constants.minZoomForSupportNames.getValue()) { - final double z = Math.max(0.5, zoom); - final AffineTransform at = AffineTransform.getScaleInstance(0.5 / z, 0.5 / z); - final TextLayout layout = basicLayout(Relations.nameOf(supportClass), at); - paint(layout, GeoUtil.centerOf(line.getBounds()), AREA_CENTER); - } - } - - //--------// - // render // - //--------// - /** - * Render the selected inter, using some highlighting - * - * @param inter the selected inter to render - */ - public void render (Inter inter) - { - inter.accept(sigPainter); - } - - //---------------// - // getSigPainter // - //---------------// - @Override - protected SigPainter getSigPainter () - { - return new SelectionSigPainter(g, sheet.getScale()); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Ratio minZoomForSupportNames = new Constant.Ratio( - 2.0, - "Minimum zoom value to display support names"); - } - - //---------------------// - // SelectionSigPainter // - //---------------------// - private class SelectionSigPainter - extends SigPainter - { - //~ Constructors --------------------------------------------------------------------------- - - public SelectionSigPainter (Graphics g, - Scale scale) - { - super(g, scale); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - protected void setColor (Inter inter) - { - // Use complementary of inter color - g.setColor(UIUtil.complementaryColor(inter.getColor())); - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// S e l e c t i o n P a i n t e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sheet.ui; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.math.GeoUtil; +import org.audiveris.omr.sheet.Scale; +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.relation.NoExclusion; +import org.audiveris.omr.sig.relation.Relation; +import org.audiveris.omr.sig.relation.Relations; +import org.audiveris.omr.sig.ui.SigPainter; +import static org.audiveris.omr.ui.symbol.Alignment.AREA_CENTER; +import org.audiveris.omr.ui.util.UIUtil; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Stroke; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; + +/** + * Class {@code SelectionPainter} is meant to paint just selected items. + * + * @author Hervé Bitteur + */ +public class SelectionPainter + extends SheetPainter +{ + + private static final Constants constants = new Constants(); + + /** + * Creates a new {@code SelectionPainter} object. + * + * @param sheet the sheet to paint + * @param g Graphic context + */ + public SelectionPainter (Sheet sheet, + Graphics g) + { + super(sheet, g); + + sigPainter = new SelectionSigPainter(g, sheet.getScale()); + } + + //-------------// + // drawSupport // + //-------------// + /** + * Draw the support relation between two inters. + * + * @param one first inter + * @param two second inter + * @param supportClass provided support class + * @param potential true if support is just potential + */ + public void drawSupport (Inter one, + Inter two, + Class supportClass, + boolean potential) + { + // Draw support line, using dash and specific color for potential relation + final Stroke oldStroke = potential ? UIUtil.setAbsoluteDashedStroke(g, 1f) + : UIUtil.setAbsoluteStroke(g, 1f); + g.setColor( + potential ? Color.PINK + : (NoExclusion.class.isAssignableFrom(supportClass) ? Color.GRAY + : Color.GREEN)); + + final double r = 2; // Radius + final Point oneCenter = one.getRelationCenter(); + Ellipse2D e1 = new Ellipse2D.Double(oneCenter.x - r, oneCenter.y - r, 2 * r, 2 * r); + g.fill(e1); + + final Point twoCenter = two.getRelationCenter(); + Ellipse2D e2 = new Ellipse2D.Double(twoCenter.x - r, twoCenter.y - r, 2 * r, 2 * r); + g.fill(e2); + + final Line2D line = new Line2D.Double(oneCenter, twoCenter); + g.draw(line); + + // Print support name at center of line? + final double zoom = g.getTransform().getScaleX(); + + if (zoom >= constants.minZoomForSupportNames.getValue()) { + final double z = Math.max(0.5, zoom); + final AffineTransform at = AffineTransform.getScaleInstance(0.5 / z, 0.5 / z); + final TextLayout layout = basicLayout(Relations.nameOf(supportClass), at); + paint(layout, GeoUtil.centerOf(line.getBounds()), AREA_CENTER); + } + } + + //--------// + // render // + //--------// + /** + * Render the selected inter, using some highlighting + * + * @param inter the selected inter to render + */ + public void render (Inter inter) + { + inter.accept(sigPainter); + } + + //---------------// + // getSigPainter // + //---------------// + @Override + protected SigPainter getSigPainter () + { + return new SelectionSigPainter(g, sheet.getScale()); + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Ratio minZoomForSupportNames = new Constant.Ratio( + 2.0, + "Minimum zoom value to display support names"); + } + + //---------------------// + // SelectionSigPainter // + //---------------------// + private static class SelectionSigPainter + extends SigPainter + { + + SelectionSigPainter (Graphics g, + Scale scale) + { + super(g, scale); + } + + @Override + protected void setColor (Inter inter) + { + // Use complementary of inter color + g.setColor(UIUtil.complementaryColor(inter.getColor())); + } + + @Override + protected boolean splitMirrors () + { + return true; + } + } +} diff --git a/src/main/org/audiveris/omr/sheet/ui/SheetAssembly.java b/src/main/org/audiveris/omr/sheet/ui/SheetAssembly.java index da7552de9..09af6d5c2 100644 --- a/src/main/org/audiveris/omr/sheet/ui/SheetAssembly.java +++ b/src/main/org/audiveris/omr/sheet/ui/SheetAssembly.java @@ -47,6 +47,7 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.Rectangle; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -56,7 +57,6 @@ import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JScrollPane; -import javax.swing.JTabbedPane; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; @@ -66,8 +66,9 @@ * Class {@code SheetAssembly} is a UI assembly dedicated to the display of various * views around the same sheet. * All views share the same zoom and the same position within their containing {@link JScrollPane}. - * - * It gathers:
                                                                                                    + *

                                                                                                    + * It gathers: + *

                                                                                                      *
                                                                                                    • a {@link Zoom} with its dedicated graphical {@link LogSlider}
                                                                                                    • *
                                                                                                    • a mouse adapter {@link Rubber}
                                                                                                    • *
                                                                                                    • a tabbed pane of {@link JScrollPane}'s for all views of this sheet
                                                                                                    • @@ -81,12 +82,9 @@ public class SheetAssembly implements ChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SheetAssembly.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Link with sheet stub. */ @Navigable(false) private final SheetStub stub; @@ -97,16 +95,8 @@ public class SheetAssembly /** To manually control the zoom ratio. */ private final LogSlider slider = new LogSlider(2, 5, LogSlider.VERTICAL, -3, 5, 0); - /** Tabbed container for all views of the sheet. */ - private final JTabbedPane viewsPane = new ClosableTabbedPane() - { - @Override - public boolean tabAboutToClose (int tabIndex) - { - return OMR.gui.displayConfirmation( - getTitleAt(tabIndex) + " tab is about to close." + "\nDo you confirm?"); - } - }; + /** Closable tabbed container for all views of the sheet. */ + private final ViewsPane viewsPane = new ViewsPane(); /** Zoom, with default ratio set to 1. */ private final Zoom zoom = new Zoom(slider, 1); @@ -115,7 +105,7 @@ public boolean tabAboutToClose (int tabIndex) private final Rubber rubber = new Rubber(zoom); /** Map: scrollPane -> view tab. */ - private final Map tabs = new HashMap(); + private final Map tabs = new HashMap<>(); /** Previously selected tab. */ private ViewTab previousTab = null; @@ -126,7 +116,6 @@ public boolean tabAboutToClose (int tabIndex) /** Sheet size. */ private Dimension modelSize; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new {@code SheetAssembly} instance dedicated to one sheet stub. * @@ -149,7 +138,6 @@ public SheetAssembly (SheetStub stub) defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // addBoard // //----------// @@ -164,8 +152,7 @@ public void addBoard (final SheetTab tab, { if (!SwingUtilities.isEventDispatchThread()) { try { - SwingUtilities.invokeAndWait( - new Runnable() + SwingUtilities.invokeAndWait(new Runnable() { @Override public void run () @@ -173,7 +160,8 @@ public void run () addBoard(tab, board); } }); - } catch (Exception ex) { + } catch (InterruptedException | + InvocationTargetException ex) { logger.warn("invokeAndWait error", ex); } } else { @@ -219,8 +207,7 @@ public void addViewTab (final String label, { if (!SwingUtilities.isEventDispatchThread()) { try { - SwingUtilities.invokeAndWait( - new Runnable() + SwingUtilities.invokeAndWait(new Runnable() { @Override public void run () @@ -228,7 +215,8 @@ public void run () addViewTab(label, scrollView, boardsPane); } }); - } catch (Exception ex) { + } catch (InterruptedException | + InvocationTargetException ex) { logger.warn("invokeAndWait error", ex); } } else { @@ -342,6 +330,11 @@ public JScrollPane getPane (String title) //--------------------// // getSelectedTabName // //--------------------// + /** + * Report name of selected tab. + * + * @return name of selected tab + */ public String getSelectedTabName () { ViewTab currentTab = getCurrentViewTab(); @@ -388,11 +381,35 @@ public Sheet getSheet () //---------// // getStub // //---------// + /** + * Report the underlying sheet stub. + * + * @return the related sheet stub + */ public SheetStub getStub () { return stub; } + //-------------// + // lockViewTab // + //-------------// + /** + * Make the provided tab non closable. + * + * @param tab the tab to lock + */ + public void lockViewTab (SheetTab tab) + { + for (int i = 0, count = viewsPane.getTabCount(); i < count; i++) { + if (viewsPane.getTitleAt(i).equals(tab.label)) { + viewsPane.removeClosingButton(i); + + return; + } + } + } + //-----------// // renameTab // //-----------// @@ -424,8 +441,7 @@ public void reset () { if (!SwingUtilities.isEventDispatchThread()) { try { - SwingUtilities.invokeAndWait( - new Runnable() + SwingUtilities.invokeAndWait(new Runnable() { @Override public void run () @@ -433,7 +449,8 @@ public void run () reset(); } }); - } catch (Exception ex) { + } catch (InterruptedException | + InvocationTargetException ex) { logger.warn("invokeAndWait error", ex); } } else { @@ -446,7 +463,7 @@ public void run () close(); - for (ViewTab tab : new ArrayList(tabs.values())) { + for (ViewTab tab : new ArrayList<>(tabs.values())) { tab.remove(); } @@ -585,62 +602,6 @@ private ViewTab getCurrentViewTab () } } - //~ Inner Classes ------------------------------------------------------------------------------ - //--------------// - // ScrollValues // - //--------------// - /** - * To preserve and replicate scroll bar values. - */ - private static class ScrollValues - { - //~ Instance fields ------------------------------------------------------------------------ - - final DefaultBoundedRangeModel hori; // Model for horizontal scrollbar - - final DefaultBoundedRangeModel vert; // Model for vertical scrollbar - - //~ Constructors --------------------------------------------------------------------------- - public ScrollValues (ScrollView scrollView) - { - hori = copy(scrollView.getComponent().getHorizontalScrollBar().getModel()); - vert = copy(scrollView.getComponent().getVerticalScrollBar().getModel()); - } - - //~ Methods -------------------------------------------------------------------------------- - public void applyTo (JScrollPane scrollPane) - { - apply(hori, scrollPane.getHorizontalScrollBar().getModel()); - apply(vert, scrollPane.getVerticalScrollBar().getModel()); - } - - @Override - public String toString () - { - return "ScrollValues{hori:" + hori + ", vert:" + vert + "}"; - } - - private void apply (BoundedRangeModel src, - BoundedRangeModel tgt) - { - tgt.setRangeProperties( - src.getValue(), - src.getExtent(), - src.getMinimum(), - src.getMaximum(), - false); - } - - private DefaultBoundedRangeModel copy (BoundedRangeModel m) - { - return new DefaultBoundedRangeModel( - m.getValue(), - m.getExtent(), - m.getMinimum(), - m.getMaximum()); - } - } - //---------// // ViewTab // //---------// @@ -650,7 +611,6 @@ private DefaultBoundedRangeModel copy (BoundedRangeModel m) */ private class ViewTab { - //~ Instance fields ------------------------------------------------------------------------ String title; // Title used for the tab @@ -658,10 +618,9 @@ private class ViewTab ScrollView scrollView; // Component in the JTabbedPane - //~ Constructors --------------------------------------------------------------------------- - public ViewTab (String title, - BoardsPane boardsPane, - ScrollView scrollView) + ViewTab (String title, + BoardsPane boardsPane, + ScrollView scrollView) { this.title = title; this.boardsPane = boardsPane; @@ -697,7 +656,6 @@ public ViewTab (String title, zoom.fireStateChanged(); } - //~ Methods -------------------------------------------------------------------------------- //------------// // deselected // //------------// @@ -834,4 +792,75 @@ private void displayBoards () } } } + + //--------------// + // ScrollValues // + //--------------// + /** + * To preserve and replicate scroll bar values. + */ + private static class ScrollValues + { + + final DefaultBoundedRangeModel hori; // Model for horizontal scrollbar + + final DefaultBoundedRangeModel vert; // Model for vertical scrollbar + + ScrollValues (ScrollView scrollView) + { + hori = copy(scrollView.getComponent().getHorizontalScrollBar().getModel()); + vert = copy(scrollView.getComponent().getVerticalScrollBar().getModel()); + } + + public void applyTo (JScrollPane scrollPane) + { + apply(hori, scrollPane.getHorizontalScrollBar().getModel()); + apply(vert, scrollPane.getVerticalScrollBar().getModel()); + } + + @Override + public String toString () + { + return "ScrollValues{hori:" + hori + ", vert:" + vert + "}"; + } + + private void apply (BoundedRangeModel src, + BoundedRangeModel tgt) + { + tgt.setRangeProperties( + src.getValue(), + src.getExtent(), + src.getMinimum(), + src.getMaximum(), + false); + } + + private DefaultBoundedRangeModel copy (BoundedRangeModel m) + { + return new DefaultBoundedRangeModel( + m.getValue(), + m.getExtent(), + m.getMinimum(), + m.getMaximum()); + } + } + + //-----------// + // ViewsPane // + //-----------// + /** + * Closable tabbed pane, with user confirmation on tab closing. + */ + private static class ViewsPane + extends ClosableTabbedPane + { + + @Override + public boolean tabAboutToClose (int tabIndex) + { + return OMR.gui.displayConfirmation( + getTitleAt(tabIndex) + " tab is about to close." + "\nDo you confirm?"); + } + } + } diff --git a/src/main/org/audiveris/omr/sheet/ui/SheetGradedPainter.java b/src/main/org/audiveris/omr/sheet/ui/SheetGradedPainter.java index 16380951a..491b36822 100644 --- a/src/main/org/audiveris/omr/sheet/ui/SheetGradedPainter.java +++ b/src/main/org/audiveris/omr/sheet/ui/SheetGradedPainter.java @@ -23,13 +23,13 @@ import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.sheet.rhythm.Voice; import org.audiveris.omr.sig.inter.Inter; import org.audiveris.omr.sig.ui.SigPainter; import org.audiveris.omr.ui.ViewParameters; import java.awt.Color; import java.awt.Graphics; -import org.audiveris.omr.sheet.rhythm.Voice; /** * Class {@code SheetGradedPainter} paints a sheet using shape-based colors and @@ -40,7 +40,6 @@ public class SheetGradedPainter extends SheetPainter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SheetGradedPainter} object. @@ -54,7 +53,6 @@ public SheetGradedPainter (Sheet sheet, super(sheet, g); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // getSigPainter // //---------------// @@ -64,26 +62,22 @@ protected SigPainter getSigPainter () return new GradedSigPainter(g, sheet.getScale()); } - //~ Inner Classes ------------------------------------------------------------------------------ //------------------// // GradedSigPainter // //------------------// - private class GradedSigPainter + private static class GradedSigPainter extends SigPainter { - //~ Instance fields ------------------------------------------------------------------------ /** View parameters. */ private final ViewParameters viewParams = ViewParameters.getInstance(); - //~ Constructors --------------------------------------------------------------------------- - public GradedSigPainter (Graphics g, - Scale scale) + GradedSigPainter (Graphics g, + Scale scale) { super(g, scale); } - //~ Methods -------------------------------------------------------------------------------- //----------// // setColor // //----------// @@ -125,5 +119,11 @@ protected void setColor (Inter inter) g.setColor(color); } + + @Override + protected boolean splitMirrors () + { + return viewParams.isVoicePainting(); + } } } diff --git a/src/main/org/audiveris/omr/sheet/ui/SheetPainter.java b/src/main/org/audiveris/omr/sheet/ui/SheetPainter.java index 95228dc58..20c00e552 100644 --- a/src/main/org/audiveris/omr/sheet/ui/SheetPainter.java +++ b/src/main/org/audiveris/omr/sheet/ui/SheetPainter.java @@ -27,6 +27,7 @@ import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sheet.SystemInfo; import org.audiveris.omr.sig.ui.SigPainter; +import org.audiveris.omr.ui.ViewParameters; import org.audiveris.omr.ui.symbol.Alignment; import org.audiveris.omr.ui.symbol.OmrFont; import org.audiveris.omr.ui.util.UIUtil; @@ -47,7 +48,8 @@ /** * Class {@code SheetPainter} provides a basis to paint sheet content. *

                                                                                                      - * It is specialized in:

                                                                                                        + * It is specialized in: + *
                                                                                                          *
                                                                                                        • {@link SheetGradedPainter} which displays all SIG inters with opacity derived from each inter * grade value.
                                                                                                        • *
                                                                                                        • {@link SheetResultPainter} which displays the resulting score (SIG remaining inters, @@ -59,25 +61,26 @@ */ public abstract class SheetPainter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(SheetPainter.class); + /** A transformation to half scale. (used for slot time annotation) */ + protected static final AffineTransform halfAT = AffineTransform.getScaleInstance(0.5, 0.5); + /** Font for annotations. */ protected static final Font basicFont = new Font( "Sans Serif", Font.PLAIN, constants.basicFontSize.getValue()); - /** A transformation to half scale. (used for slot time annotation) */ - protected static final AffineTransform halfAT = AffineTransform.getScaleInstance(0.5, 0.5); - - //~ Instance fields ---------------------------------------------------------------------------- /** Sheet. */ protected final Sheet sheet; + /** View parameters. */ + protected final ViewParameters viewParams = ViewParameters.getInstance(); + /** Graphic context. */ protected final Graphics2D g; @@ -87,7 +90,6 @@ public abstract class SheetPainter /** Painter for Inter instances. */ protected SigPainter sigPainter; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SheetPainter object. * @@ -103,13 +105,11 @@ public SheetPainter (Sheet sheet, clip = g.getClipBounds(); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// /** * Paint the sheet. - * */ public void process () { @@ -150,6 +150,11 @@ protected TextLayout basicLayout (String str, //---------------// // getSigPainter // //---------------// + /** + * Report the concrete sig painter to be used. + * + * @return the sig painter + */ protected abstract SigPainter getSigPainter (); //-------// @@ -173,6 +178,11 @@ protected void paint (TextLayout layout, //---------------// // processSystem // //---------------// + /** + * Process a system. + * + * @param system the system to process + */ protected void processSystem (SystemInfo system) { try { @@ -191,14 +201,12 @@ protected void processSystem (SystemInfo system) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer basicFontSize = new Constant.Integer( "points", diff --git a/src/main/org/audiveris/omr/sheet/ui/SheetResultPainter.java b/src/main/org/audiveris/omr/sheet/ui/SheetResultPainter.java index 586961175..3b652c564 100644 --- a/src/main/org/audiveris/omr/sheet/ui/SheetResultPainter.java +++ b/src/main/org/audiveris/omr/sheet/ui/SheetResultPainter.java @@ -48,14 +48,10 @@ import org.audiveris.omr.sig.relation.Relation; import org.audiveris.omr.sig.ui.SigPainter; import org.audiveris.omr.ui.Colors; -import org.audiveris.omr.ui.ViewParameters; - import static org.audiveris.omr.ui.symbol.Alignment.BOTTOM_CENTER; import static org.audiveris.omr.ui.symbol.Alignment.TOP_LEFT; - import org.audiveris.omr.ui.util.UIUtil; import org.audiveris.omr.util.HorizontalSide; - import static org.audiveris.omr.util.HorizontalSide.LEFT; import org.slf4j.Logger; @@ -85,10 +81,8 @@ public class SheetResultPainter extends SheetPainter { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - SheetResultPainter.class); + private static final Logger logger = LoggerFactory.getLogger(SheetResultPainter.class); /** Abscissa offset, in pixels, for annotation near system. */ protected static final int annotationDx = 15; @@ -96,10 +90,6 @@ public class SheetResultPainter /** Ordinate offset, in pixels, for annotation near staff or system. */ protected static final int annotationDy = 15; - /** View parameters. */ - protected static final ViewParameters viewParams = ViewParameters.getInstance(); - - //~ Instance fields ---------------------------------------------------------------------------- /** For staff lines. */ protected Stroke lineStroke; @@ -115,7 +105,6 @@ public class SheetResultPainter /** Should we draw annotations?. */ protected final boolean annotated; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SheetResultPainter} object. * @@ -143,7 +132,6 @@ public SheetResultPainter (Sheet sheet, g.setFont(basicFont); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // drawSlot // //----------// @@ -301,7 +289,7 @@ protected void processSystem (SystemInfo system) if (scale != null) { lineStroke = new BasicStroke( - (float) sheet.getScale().getFore(), + sheet.getScale().getFore(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); g.setStroke(lineStroke); @@ -357,19 +345,17 @@ private void processStack (MeasureStack stack) // Write the score-based measure id, on first real part only String mid = stack.getPageId(); - if (mid != null) { - g.setColor(Colors.ANNOTATION); - - // Work with top non-dummy staff & measure - SystemInfo system = stack.getSystem(); - Staff staff = system.getFirstStaff(); - Part topRealPart = staff.getPart(); - int stackIndex = system.getStacks().indexOf(stack); - Measure topRealMeasure = topRealPart.getMeasures().get(stackIndex); - int left = topRealMeasure.getAbscissa(HorizontalSide.LEFT, staff); - Point loc = new Point(left, staff.getFirstLine().yAt(left) - annotationDy); - paint(basicLayout(mid, null), loc, BOTTOM_CENTER); - } + g.setColor(Colors.ANNOTATION); + + // Work with top non-dummy staff & measure + SystemInfo system = stack.getSystem(); + Staff staff = system.getFirstStaff(); + Part topRealPart = staff.getPart(); + int stackIndex = system.getStacks().indexOf(stack); + Measure topRealMeasure = topRealPart.getMeasures().get(stackIndex); + int left = topRealMeasure.getAbscissa(HorizontalSide.LEFT, staff); + Point loc = new Point(left, staff.getFirstLine().yAt(left) - annotationDy); + paint(basicLayout(mid, null), loc, BOTTOM_CENTER); // Draw slot vertical lines ? if (viewParams.isSlotPainting() && (stack.getSlots() != null)) { @@ -390,22 +376,19 @@ private void processStack (MeasureStack stack) } } - //~ Inner Classes ------------------------------------------------------------------------------ //------------------// // ResultSigPainter // //------------------// private class ResultSigPainter extends SigPainter { - //~ Constructors --------------------------------------------------------------------------- - public ResultSigPainter (Graphics g, - Scale scale) + ResultSigPainter (Graphics g, + Scale scale) { super(g, scale); } - //~ Methods -------------------------------------------------------------------------------- @Override protected void setColor (Inter inter) { @@ -421,5 +404,11 @@ protected void setColor (Inter inter) g.setColor(defaultColor); } } + + @Override + protected boolean splitMirrors () + { + return coloredVoices; + } } } diff --git a/src/main/org/audiveris/omr/sheet/ui/StubDependent.java b/src/main/org/audiveris/omr/sheet/ui/StubDependent.java index d3aec663c..1fcac124a 100644 --- a/src/main/org/audiveris/omr/sheet/ui/StubDependent.java +++ b/src/main/org/audiveris/omr/sheet/ui/StubDependent.java @@ -45,7 +45,6 @@ public abstract class StubDependent extends AbstractBean implements EventSubscriber { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(StubDependent.class); @@ -67,8 +66,8 @@ public abstract class StubDependent /** Name of property linked to book lack of activity. */ public static final String BOOK_IDLE = "bookIdle"; - /** Name of property linked to book modified. */ - public static final String BOOK_MODIFIED = "bookModified"; + /** Name of property linked to book modified/upgraded. */ + public static final String BOOK_MODIFIED_OR_UPGRADED = "bookModifiedOrUpgraded"; /** Name of property linked to undoable. */ public static final String UNDOABLE = "undoable"; @@ -76,7 +75,6 @@ public abstract class StubDependent /** Name of property linked to redoable. */ public static final String REDOABLE = "redoable"; - //~ Instance fields ---------------------------------------------------------------------------- /** Indicates whether the current sheet stub can be transcribed. */ protected boolean stubTranscribable = false; @@ -95,8 +93,8 @@ public abstract class StubDependent /** Indicates whether current book is idle (all its sheets are idle). */ protected boolean bookIdle = false; - /** Indicates whether current book has been modified. */ - protected boolean bookModified = false; + /** Indicates whether current book has been modified/upgraded. */ + protected boolean bookModifiedOrUpgraded = false; /** Indicates whether we can undo user action. */ protected boolean undoable = false; @@ -104,7 +102,6 @@ public abstract class StubDependent /** Indicates whether we can redo user action. */ protected boolean redoable = false; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StubDependent} object. */ @@ -114,7 +111,6 @@ protected StubDependent () StubsController.getInstance().subscribe(this); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // isBookIdle // //------------// @@ -128,17 +124,53 @@ public boolean isBookIdle () return bookIdle; } - //----------------// - // isBookModified // - //----------------// + //-------------// + // setBookIdle // + //-------------// + /** + * Setter for bookIdle property + * + * @param bookIdle the new property value + */ + public void setBookIdle (boolean bookIdle) + { + boolean oldValue = this.bookIdle; + this.bookIdle = bookIdle; + + if (bookIdle != oldValue) { + firePropertyChange(BOOK_IDLE, oldValue, this.bookIdle); + } + } + + //--------------------------// + // isBookModifiedOrUpgraded // + //--------------------------// /** - * Getter for bookModified property + * Getter for bookModifiedOrUpgraded property * * @return the current property value */ - public boolean isBookModified () + public boolean isBookModifiedOrUpgraded () { - return bookModified; + return bookModifiedOrUpgraded; + } + + //---------------------------// + // setBookModifiedOrUpgraded // + //---------------------------// + /** + * Setter for bookModifiedOrUpgraded property + * + * @param bookModifiedOrUpgraded the new property value + */ + public void setBookModifiedOrUpgraded (boolean bookModifiedOrUpgraded) + { + boolean oldValue = this.bookModifiedOrUpgraded; + this.bookModifiedOrUpgraded = bookModifiedOrUpgraded; + + if (bookModifiedOrUpgraded != oldValue) { + firePropertyChange(BOOK_MODIFIED_OR_UPGRADED, oldValue, this.bookModifiedOrUpgraded); + } } //---------------------// @@ -154,6 +186,24 @@ public boolean isBookTranscribable () return bookTranscribable; } + //----------------------// + // setBookTranscribable // + //----------------------// + /** + * Setter for bookTranscribable property. + * + * @param bookTranscribable the new property value + */ + public void setBookTranscribable (boolean bookTranscribable) + { + boolean oldValue = this.bookTranscribable; + this.bookTranscribable = bookTranscribable; + + if (bookTranscribable != oldValue) { + firePropertyChange(BOOK_TRANSCRIBABLE, oldValue, this.bookTranscribable); + } + } + //------------// // isRedoable // //------------// @@ -167,6 +217,24 @@ public boolean isRedoable () return redoable; } + //-------------// + // setRedoable // + //-------------// + /** + * Setter for redoable property + * + * @param redoable the new property value + */ + public void setRedoable (boolean redoable) + { + boolean oldValue = this.redoable; + this.redoable = redoable; + + if (redoable != oldValue) { + firePropertyChange(REDOABLE, oldValue, this.redoable); + } + } + //-----------------// // isStubAvailable // //-----------------// @@ -180,6 +248,24 @@ public boolean isStubAvailable () return stubAvailable; } + //------------------// + // setStubAvailable // + //------------------// + /** + * Setter for stubAvailable property. + * + * @param stubAvailable the new property value + */ + public void setStubAvailable (boolean stubAvailable) + { + boolean oldValue = this.stubAvailable; + this.stubAvailable = stubAvailable; + + if (stubAvailable != oldValue) { + firePropertyChange(STUB_AVAILABLE, oldValue, this.stubAvailable); + } + } + //------------// // isStubIdle // //------------// @@ -193,6 +279,24 @@ public boolean isStubIdle () return stubIdle; } + //-------------// + // setStubIdle // + //-------------// + /** + * Setter for stubIdle property + * + * @param stubIdle the new property value + */ + public void setStubIdle (boolean stubIdle) + { + boolean oldValue = this.stubIdle; + this.stubIdle = stubIdle; + + if (stubIdle != oldValue) { + firePropertyChange(STUB_IDLE, oldValue, this.stubIdle); + } + } + //---------------------// // isStubTranscribable // //---------------------// @@ -206,6 +310,24 @@ public boolean isStubTranscribable () return stubTranscribable; } + //----------------------// + // setStubTranscribable // + //----------------------// + /** + * Setter for stubTranscribable property. + * + * @param stubTranscribable the new property value + */ + public void setStubTranscribable (boolean stubTranscribable) + { + boolean oldValue = this.stubTranscribable; + this.stubTranscribable = stubTranscribable; + + if (stubTranscribable != oldValue) { + firePropertyChange(STUB_TRANSCRIBABLE, oldValue, this.stubTranscribable); + } + } + //-------------// // isStubValid // //-------------// @@ -219,6 +341,24 @@ public boolean isStubValid () return stubValid; } + //--------------// + // setStubValid // + //--------------// + /** + * Setter for stubValid property. + * + * @param stubValid the new property value + */ + public void setStubValid (boolean stubValid) + { + boolean oldValue = this.stubValid; + this.stubValid = stubValid; + + if (stubValid != oldValue) { + firePropertyChange(STUB_VALID, oldValue, this.stubValid); + } + } + //------------// // isUndoable // //------------// @@ -232,6 +372,24 @@ public boolean isUndoable () return undoable; } + //-------------// + // setUndoable // + //-------------// + /** + * Setter for undoable property + * + * @param undoable the new property value + */ + public void setUndoable (boolean undoable) + { + boolean oldValue = this.undoable; + this.undoable = undoable; + + if (undoable != oldValue) { + firePropertyChange(UNDOABLE, oldValue, this.undoable); + } + } + //---------// // onEvent // //---------// @@ -254,7 +412,7 @@ public void onEvent (StubEvent stubEvent) // logger.info( // "event: {} {}", // stubEvent, - // (stub != null) ? stub.getSheet().getId() : "no stub"); + // (stub != null) ? stub.getId() : "no stub"); // // Update stubAvailable setStubAvailable(stub != null); @@ -283,11 +441,12 @@ public void onEvent (StubEvent stubEvent) setBookTranscribable(false); } - // Update bookModified + // Update bookModifiedOrUpgraded if (stub != null) { - setBookModified(stub.isModified() || stub.getBook().isModified()); + final Book book = stub.getBook(); + setBookModifiedOrUpgraded(book.isModified() || book.isUpgraded()); } else { - setBookModified(false); + setBookModifiedOrUpgraded(false); } // Update undoable/redoable @@ -304,168 +463,6 @@ public void onEvent (StubEvent stubEvent) } } - //-------------// - // setBookIdle // - //-------------// - /** - * Setter for bookIdle property - * - * @param bookIdle the new property value - */ - public void setBookIdle (boolean bookIdle) - { - boolean oldValue = this.bookIdle; - this.bookIdle = bookIdle; - - if (bookIdle != oldValue) { - firePropertyChange(BOOK_IDLE, oldValue, this.bookIdle); - } - } - - //-----------------// - // setBookModified // - //-----------------// - /** - * Setter for bookModified property - * - * @param bookModified the new property value - */ - public void setBookModified (boolean bookModified) - { - boolean oldValue = this.bookModified; - this.bookModified = bookModified; - - if (bookModified != oldValue) { - firePropertyChange(BOOK_MODIFIED, oldValue, this.bookModified); - } - } - - //----------------------// - // setBookTranscribable // - //----------------------// - /** - * Setter for bookTranscribable property. - * - * @param bookTranscribable the new property value - */ - public void setBookTranscribable (boolean bookTranscribable) - { - boolean oldValue = this.bookTranscribable; - this.bookTranscribable = bookTranscribable; - - if (bookTranscribable != oldValue) { - firePropertyChange(BOOK_TRANSCRIBABLE, oldValue, this.bookTranscribable); - } - } - - //-------------// - // setRedoable // - //-------------// - /** - * Setter for redoable property - * - * @param redoable the new property value - */ - public void setRedoable (boolean redoable) - { - boolean oldValue = this.redoable; - this.redoable = redoable; - - if (redoable != oldValue) { - firePropertyChange(REDOABLE, oldValue, this.redoable); - } - } - - //------------------// - // setStubAvailable // - //------------------// - /** - * Setter for stubAvailable property. - * - * @param stubAvailable the new property value - */ - public void setStubAvailable (boolean stubAvailable) - { - boolean oldValue = this.stubAvailable; - this.stubAvailable = stubAvailable; - - if (stubAvailable != oldValue) { - firePropertyChange(STUB_AVAILABLE, oldValue, this.stubAvailable); - } - } - - //-------------// - // setStubIdle // - //-------------// - /** - * Setter for stubIdle property - * - * @param stubIdle the new property value - */ - public void setStubIdle (boolean stubIdle) - { - boolean oldValue = this.stubIdle; - this.stubIdle = stubIdle; - - if (stubIdle != oldValue) { - firePropertyChange(STUB_IDLE, oldValue, this.stubIdle); - } - } - - //----------------------// - // setStubTranscribable // - //----------------------// - /** - * Setter for stubTranscribable property. - * - * @param stubTranscribable the new property value - */ - public void setStubTranscribable (boolean stubTranscribable) - { - boolean oldValue = this.stubTranscribable; - this.stubTranscribable = stubTranscribable; - - if (stubTranscribable != oldValue) { - firePropertyChange(STUB_TRANSCRIBABLE, oldValue, this.stubTranscribable); - } - } - - //--------------// - // setStubValid // - //--------------// - /** - * Setter for stubValid property. - * - * @param stubValid the new property value - */ - public void setStubValid (boolean stubValid) - { - boolean oldValue = this.stubValid; - this.stubValid = stubValid; - - if (stubValid != oldValue) { - firePropertyChange(STUB_VALID, oldValue, this.stubValid); - } - } - - //-------------// - // setUndoable // - //-------------// - /** - * Setter for undoable property - * - * @param undoable the new property value - */ - public void setUndoable (boolean undoable) - { - boolean oldValue = this.undoable; - this.undoable = undoable; - - if (undoable != oldValue) { - firePropertyChange(UNDOABLE, oldValue, this.undoable); - } - } - //------------// // isBookIdle // //------------// diff --git a/src/main/org/audiveris/omr/sheet/ui/StubsController.java b/src/main/org/audiveris/omr/sheet/ui/StubsController.java index 86ed7c16f..f09594707 100644 --- a/src/main/org/audiveris/omr/sheet/ui/StubsController.java +++ b/src/main/org/audiveris/omr/sheet/ui/StubsController.java @@ -45,6 +45,7 @@ import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -91,20 +92,14 @@ public class StubsController implements ChangeListener, PropertyChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - StubsController.class); + private static final Logger logger = LoggerFactory.getLogger(StubsController.class); /** Events that can be published on sheet service. */ private static final Class[] eventsWritten = new Class[]{StubEvent.class}; - /** The single instance of this class. */ - private static volatile StubsController INSTANCE; - - //~ Instance fields ---------------------------------------------------------------------------- /** The concrete tabbed pane, one tab per sheet stub. */ private final JTabbedPane stubsPane; @@ -114,13 +109,12 @@ public class StubsController /** The global event service which publishes the currently selected sheet stub. */ private final SelectionService stubService = new SelectionService("stubService", eventsWritten); - //~ Constructors ------------------------------------------------------------------------------- /** * Create the {@code StubsController} singleton. */ private StubsController () { - stubsMap = new HashMap(); + stubsMap = new HashMap<>(); stubsPane = new JTabbedPane(); stubsPane.setForeground(Colors.SHEET_NOT_LOADED); @@ -129,14 +123,14 @@ private StubsController () stubsPane.addChangeListener(this); // Listener on invalid sheets display - ViewParameters.getInstance() - .addPropertyChangeListener(ViewParameters.INVALID_SHEET_DISPLAY, this); + ViewParameters.getInstance().addPropertyChangeListener( + ViewParameters.INVALID_SHEET_DISPLAY, + this); // Key binding bindKeys(); } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // addAssembly // //-------------// @@ -206,6 +200,11 @@ public void callAboutStub (SheetStub stub) //----------------// // deleteAssembly // //----------------// + /** + * Remove the assembly for the provided stub. + * + * @param stub the provided stub + */ public void deleteAssembly (SheetStub stub) { removeAssembly(stub); // Removed from stubsPane @@ -302,20 +301,16 @@ public void dumpCurrentSheetServices () } } - if (sheet.getFilamentIndex() != null) { - if (sheet.getFilamentIndex().getEntityService() != null) { - sheet.getFilamentIndex().getEntityService().dumpSubscribers(); - } else { - logger.info("No filamentService"); - } + if (sheet.getFilamentIndex().getEntityService() != null) { + sheet.getFilamentIndex().getEntityService().dumpSubscribers(); + } else { + logger.info("No filamentService"); } - if (sheet.getGlyphIndex() != null) { - if (sheet.getGlyphIndex().getEntityService() != null) { - sheet.getGlyphIndex().getEntityService().dumpSubscribers(); - } else { - logger.info("No glyphService"); - } + if (sheet.getGlyphIndex().getEntityService() != null) { + sheet.getGlyphIndex().getEntityService().dumpSubscribers(); + } else { + logger.info("No glyphService"); } if (sheet.getInterIndex() != null) { @@ -341,51 +336,6 @@ public JComponent getComponent () return stubsPane; } - //----------------// - // getCurrentBook // - //----------------// - /** - * Convenient method to get the current book instance, if any. - * - * @return the current book instance, or null - */ - public static Book getCurrentBook () - { - SheetStub stub = getCurrentStub(); - - if (stub == null) { - return null; - } - - return stub.getBook(); - } - - //----------------// - // getCurrentStub // - //----------------// - /** - * Convenient method to get the currently selected sheet stub, if any. - * - * @return the selected stub, or null - */ - public static SheetStub getCurrentStub () - { - return getInstance().getSelectedStub(); - } - - //--------------// - // getEarlyStep // - //--------------// - /** - * Report the step run by default on every new stub displayed. - * - * @return the default target step - */ - public static Step getEarlyStep () - { - return constants.earlyStep.getValue(); - } - //----------// // getIndex // //----------// @@ -400,23 +350,6 @@ public Integer getIndex (SheetStub stub) return stubsPane.indexOfComponent(stub.getAssembly().getComponent()); } - //-------------// - // getInstance // - //-------------// - /** - * Report the single instance of this class. - * - * @return the single instance - */ - public static StubsController getInstance () - { - if (INSTANCE == null) { - INSTANCE = new StubsController(); - } - - return INSTANCE; - } - //--------------// // getLastIndex // //--------------// @@ -445,35 +378,6 @@ public SheetStub getSelectedStub () return (stubEvent != null) ? stubEvent.getData() : null; } - //--------------// - // invokeSelect // - //--------------// - /** - * Delegate to EDT the selection of provided stub. - * - * @param stub the stub to select - */ - public static void invokeSelect (final SheetStub stub) - { - if (!SwingUtilities.isEventDispatchThread()) { - try { - SwingUtilities.invokeAndWait( - new Runnable() - { - @Override - public void run () - { - invokeSelect(stub); - } - }); - } catch (Exception ex) { - logger.warn("invokeAndWait error", ex); - } - } else { - StubsController.getInstance().selectAssembly(stub); - } - } - //---------// // markTab // //---------// @@ -488,8 +392,7 @@ public void markTab (final SheetStub stub, { if (!SwingUtilities.isEventDispatchThread()) { try { - SwingUtilities.invokeAndWait( - new Runnable() + SwingUtilities.invokeAndWait(new Runnable() { @Override public void run () @@ -497,7 +400,8 @@ public void run () markTab(stub, color); } }); - } catch (Exception ex) { + } catch (InterruptedException | + InvocationTargetException ex) { logger.warn("invokeAndWait error", ex); } } else { @@ -614,25 +518,14 @@ public void selectAssembly (SheetStub stub) } } - //--------------// - // setEarlyStep // - //--------------// - /** - * Set the step run by default on every new stub displayed. - * - * @param step the default target step - */ - public static void setEarlyStep (Step step) - { - if (step != getEarlyStep()) { - constants.earlyStep.setValue(step); - logger.info("Early step is now: {}", step); - } - } - //-----------// // reDisplay // //-----------// + /** + * Refresh display for provided stub. + * + * @param stub the provided stub + */ public void reDisplay (final SheetStub stub) { Callable task = new Callable() @@ -647,8 +540,7 @@ public Void call () // Check whether we should run early steps on the sheet checkStubStatus(stub); - SwingUtilities.invokeAndWait( - new Runnable() + SwingUtilities.invokeAndWait(new Runnable() { @Override public void run () @@ -682,8 +574,7 @@ public void selectOtherBook (final Book currentBook) { if (!SwingUtilities.isEventDispatchThread()) { try { - SwingUtilities.invokeAndWait( - new Runnable() + SwingUtilities.invokeAndWait(new Runnable() { @Override public void run () @@ -691,7 +582,8 @@ public void run () selectOtherBook(currentBook); } }); - } catch (Exception ex) { + } catch (InterruptedException | + InvocationTargetException ex) { logger.warn("invokeAndWait error", ex); } } else { @@ -984,32 +876,121 @@ private void insertAssembly (SheetStub stub, index); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet + //----------------// + // getCurrentBook // + //----------------// + /** + * Convenient method to get the current book instance, if any. + * + * @return the current book instance, or null + */ + public static Book getCurrentBook () { - //~ Instance fields ------------------------------------------------------------------------ + SheetStub stub = getCurrentStub(); - private final Constant.Enum earlyStep = new Constant.Enum( - Step.class, - Step.BINARY, - "Early step triggered when an empty stub tab is selected "); + if (stub == null) { + return null; + } - private final Constant.Ratio initialZoomRatio = new Constant.Ratio( - 0.5, - "Initial zoom ratio for displayed sheet pictures"); + return stub.getBook(); + } + + //----------------// + // getCurrentStub // + //----------------// + /** + * Convenient method to get the currently selected sheet stub, if any. + * + * @return the selected stub, or null + */ + public static SheetStub getCurrentStub () + { + return getInstance().getSelectedStub(); + } + + //--------------// + // getEarlyStep // + //--------------// + /** + * Report the step run by default on every new stub displayed. + * + * @return the default target step + */ + public static Step getEarlyStep () + { + return constants.earlyStep.getValue(); + } + + //--------------// + // setEarlyStep // + //--------------// + /** + * Set the step run by default on every new stub displayed. + * + * @param step the default target step + */ + public static void setEarlyStep (Step step) + { + if (step != getEarlyStep()) { + constants.earlyStep.setValue(step); + logger.info("Early step is now: {}", step); + } + } + + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of this class in application. + * + * @return the instance + */ + public static StubsController getInstance () + { + return LazySingleton.INSTANCE; } //---------------// - // CtrlEndAction // + // LazySingleton // //---------------// + private static class LazySingleton + { + + static final StubsController INSTANCE = new StubsController(); + } + + //--------------// + // invokeSelect // + //--------------// + /** + * Delegate to EDT the selection of provided stub. + * + * @param stub the stub to select + */ + public static void invokeSelect (final SheetStub stub) + { + if (!SwingUtilities.isEventDispatchThread()) { + try { + SwingUtilities.invokeAndWait(new Runnable() + { + @Override + public void run () + { + invokeSelect(stub); + } + }); + } catch (InterruptedException | + InvocationTargetException ex) { + logger.warn("invokeAndWait error", ex); + } + } else { + StubsController.getInstance().selectAssembly(stub); + } + } + private class CtrlEndAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -1020,15 +1001,18 @@ public void actionPerformed (ActionEvent e) stubsPane.setSelectedIndex(count - 1); } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } - //----------------// - // CtrlHomeAction // - //----------------// private class CtrlHomeAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -1037,15 +1021,18 @@ public void actionPerformed (ActionEvent e) stubsPane.setSelectedIndex(0); } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } - //----------------// - // PageDownAction // - //----------------// private class PageDownAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -1056,15 +1043,18 @@ public void actionPerformed (ActionEvent e) stubsPane.setSelectedIndex(tabIndex + 1); } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } - //--------------// - // PageUpAction // - //--------------// private class PageUpAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -1075,5 +1065,29 @@ public void actionPerformed (ActionEvent e) stubsPane.setSelectedIndex(tabIndex - 1); } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Enum earlyStep = new Constant.Enum<>( + Step.class, + Step.BINARY, + "Early step triggered when an empty stub tab is selected "); + + private final Constant.Ratio initialZoomRatio = new Constant.Ratio( + 0.5, + "Initial zoom ratio for displayed sheet pictures"); } } diff --git a/src/main/org/audiveris/omr/sheet/ui/TemplateBoard.java b/src/main/org/audiveris/omr/sheet/ui/TemplateBoard.java index 452ddda28..d004efaaa 100644 --- a/src/main/org/audiveris/omr/sheet/ui/TemplateBoard.java +++ b/src/main/org/audiveris/omr/sheet/ui/TemplateBoard.java @@ -35,6 +35,7 @@ import org.audiveris.omr.image.PixelDistance; import org.audiveris.omr.image.Template; import org.audiveris.omr.image.TemplateFactory; +import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sheet.Sheet; import org.audiveris.omr.ui.Board; import org.audiveris.omr.ui.field.LDoubleField; @@ -43,6 +44,7 @@ import org.audiveris.omr.ui.selection.SelectionHint; import org.audiveris.omr.ui.selection.SelectionService; import org.audiveris.omr.ui.selection.UserEvent; +import org.audiveris.omr.ui.symbol.MusicFont; import org.audiveris.omr.ui.util.Panel; import org.slf4j.Logger; @@ -58,8 +60,6 @@ import javax.swing.SpinnerListModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import org.audiveris.omr.sheet.Scale; -import org.audiveris.omr.ui.symbol.MusicFont; /** * Class {@code TemplateBoard} allows to select a template (shape, anchor) and present @@ -72,14 +72,12 @@ public class TemplateBoard implements ChangeListener // For all spinners { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TemplateBoard.class); /** Events this entity is interested in */ private static final Class[] eventsRead = new Class[]{LocationEvent.class}; - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ private final Sheet sheet; @@ -107,7 +105,6 @@ public class TemplateBoard /** Template reference point. */ private Point refPoint; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TemplateBoard} object. * @@ -126,7 +123,7 @@ public TemplateBoard (Sheet sheet, // Shape spinner shapeSpinner = new JSpinner( - new SpinnerListModel(new ArrayList(ShapeSet.getTemplateNotes(sheet)))); + new SpinnerListModel(new ArrayList<>(ShapeSet.getTemplateNotes(sheet)))); shapeSpinner.addChangeListener(this); shapeSpinner.setName("shapeSpinner"); shapeSpinner.setToolTipText("Selection of template shape"); @@ -147,7 +144,6 @@ public TemplateBoard (Sheet sheet, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // onEvent // //---------// @@ -181,9 +177,8 @@ public void stateChanged (ChangeEvent e) if (areCompatible(shape, anchor)) { Scale scale = sheet.getScale(); - Template template = TemplateFactory.getInstance() - .getCatalog(MusicFont.getHeadPointSize(scale, scale.getInterline())) - .getTemplate(shape); + Template template = TemplateFactory.getInstance().getCatalog( + MusicFont.getHeadPointSize(scale, scale.getInterline())).getTemplate(shape); at = new AnchoredTemplate(anchor, template); } diff --git a/src/main/org/audiveris/omr/sheet/ui/TemplateView.java b/src/main/org/audiveris/omr/sheet/ui/TemplateView.java index 1278fe904..ccf2c9c31 100644 --- a/src/main/org/audiveris/omr/sheet/ui/TemplateView.java +++ b/src/main/org/audiveris/omr/sheet/ui/TemplateView.java @@ -70,7 +70,6 @@ public class TemplateView extends ImageView { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -80,7 +79,6 @@ public class TemplateView AlphaComposite.SRC_OVER, 0.25f); - //~ Instance fields ---------------------------------------------------------------------------- private final Sheet sheet; private final DistanceTable table; @@ -91,7 +89,6 @@ public class TemplateView /** Template reference point. */ private Point refPoint; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TemplateView} object. * @@ -113,7 +110,6 @@ public TemplateView (Sheet sheet, templateService.subscribeStrongly(AnchoredTemplateEvent.class, this); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // contextSelected // //-----------------// @@ -237,14 +233,12 @@ protected void renderItems (Graphics2D g) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio minZoomRatio = new Constant.Ratio( 4.0, diff --git a/src/main/org/audiveris/omr/sheet/ui/resources/BookActions.properties b/src/main/org/audiveris/omr/sheet/ui/resources/BookActions.properties index f9354f7e4..eee1264fa 100644 --- a/src/main/org/audiveris/omr/sheet/ui/resources/BookActions.properties +++ b/src/main/org/audiveris/omr/sheet/ui/resources/BookActions.properties @@ -41,10 +41,14 @@ closeBook.Action.shortDescription = Close the current book closeBook.Action.icon=${icons.root}/actions/stop.png closeBook.Action.accelerator = control W -defineParameters.Action.text = Set Parameters... +defineParameters.Action.text = Set Book Parameters... defineParameters.Action.shortDescription = Define specific book parameters defineParameters.Action.icon = ${icons.root}/apps/kmenuedit.png +defineSheetParameters.Action.text = Set Sheet Parameters... +defineSheetParameters.Action.shortDescription = Define specific sheet parameters +defineSheetParameters.Action.icon = ${icons.root}/apps/kmenuedit.png + displayData.Action.text=Display Data displayData.Action.shortDescription=Display the data tab displayData.Action.icon=${icons.root}/actions/tab.png @@ -186,7 +190,7 @@ undo.Action.shortDescription = Undo previous action undo.Action.icon=${icons.root}/actions/undo.png undo.Action.accelerator = control Z -viewBookRepository.Action.text = View Book Repository +viewBookRepository.Action.text = Browse Book Repository viewBookRepository.Action.shortDescription = Open sample browser on book repository viewBookRepository.Action.icon = ${icons.root}/actions/agt_forum.png diff --git a/src/main/org/audiveris/omr/sheet/ui/resources/BookActions_fr.properties b/src/main/org/audiveris/omr/sheet/ui/resources/BookActions_fr.properties index 5d2a56bae..951b70024 100644 --- a/src/main/org/audiveris/omr/sheet/ui/resources/BookActions_fr.properties +++ b/src/main/org/audiveris/omr/sheet/ui/resources/BookActions_fr.properties @@ -36,6 +36,9 @@ closeBook.Action.shortDescription = Fermer le document courant defineParameters.Action.text = D\u00e9finir les Param\u00e8tres... defineParameters.Action.shortDescription = D\u00e9finir les param\u00e8tres du document +defineSheetParameters.Action.text = D\u00e9finir les Param\u00e8tres... +defineSheetParameters.Action.shortDescription = D\u00e9finir les param\u00e8tres de la feuille + displayData.Action.text=Afficher Data displayData.Action.shortDescription=Afficher l'onglet Data @@ -138,8 +141,8 @@ transcribeSheet.Action.shortDescription = Transcrire la feuille courante undo.Action.text = Annuler undo.Action.shortDescription = Annuler l'action pr\u00e9c\u00e9dente -viewBookRepository.Action.text = Ouvrir le Dictionnaire -viewBookRepository.Action.shortDescription = Ouvrir le dictionnaire de ce document +viewBookRepository.Action.text = Inspecter le Dictionnaire +viewBookRepository.Action.shortDescription = Inspecter le dictionnaire de ce document zoomHeight.Action.text=Ajuster en hauteur zoomHeight.Action.shortDescription=Afficher toute la hauteur de la feuille diff --git a/src/main/org/audiveris/omr/sig/AbstractImpacts.java b/src/main/org/audiveris/omr/sig/AbstractImpacts.java deleted file mode 100644 index ff57ed136..000000000 --- a/src/main/org/audiveris/omr/sig/AbstractImpacts.java +++ /dev/null @@ -1,121 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// A b s t r a c t I m p a c t s // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig; - -import org.audiveris.omr.glyph.Grades; - -import javax.xml.bind.annotation.adapters.XmlAdapter; - -/** - * Class {@code AbstractImpacts} is an abstract implementation of {@link GradeImpacts} - * interface. - * - * @author Hervé Bitteur - */ -public abstract class AbstractImpacts - implements GradeImpacts -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Creates a new {@code AbstractImpacts} object. - */ - public AbstractImpacts () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //-------------------// - // getIntrinsicRatio // - //-------------------// - @Override - public double getIntrinsicRatio () - { - return Grades.intrinsicRatio; - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - final StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < getImpactCount(); i++) { - if (sb.length() > 0) { - sb.append(" "); - } - - sb.append(String.format("%s:%.2f", getName(i), getImpact(i))); - } - - return sb.toString(); - } - - //--------------// - // computeGrade // - //--------------// - protected double computeGrade () - { - double global = 1d; - double totalWeight = 0d; - - for (int i = 0; i < getImpactCount(); i++) { - double weight = getWeight(i); - double impact = getImpact(i); - totalWeight += weight; - - if (impact == 0) { - global = 0; - } else if (weight != 0) { - global *= Math.pow(impact, weight); - } - } - - return getIntrinsicRatio() * Math.pow(global, 1 / totalWeight); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //---------// - // Adapter // - //---------// - public static class Adapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public AbstractImpacts marshal (GradeImpacts itf) - throws Exception - { - return (AbstractImpacts) itf; - } - - @Override - public GradeImpacts unmarshal (AbstractImpacts abs) - throws Exception - { - return abs; - } - } -} diff --git a/src/main/org/audiveris/omr/sig/BasicImpacts.java b/src/main/org/audiveris/omr/sig/BasicImpacts.java deleted file mode 100644 index aff8880e1..000000000 --- a/src/main/org/audiveris/omr/sig/BasicImpacts.java +++ /dev/null @@ -1,111 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// B a s i c I m p a c t s // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig; - -/** - * Class {@code BasicImpacts} is an array-based implementation of {@link GradeImpacts} - * interface. - * - * @author Hervé Bitteur - */ -public abstract class BasicImpacts - extends AbstractImpacts -{ - //~ Instance fields ---------------------------------------------------------------------------- - - private final String[] names; - - private final double[] weights; - - protected final double[] impacts; - - protected double grade = -1; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new BasicImpacts object. - * - * @param names array of names - * @param weights array of weights - */ - public BasicImpacts (String[] names, - double[] weights) - { - if (names.length != weights.length) { - throw new IllegalArgumentException("Arrays for names & weights have different lengths"); - } - - this.names = names; - this.weights = weights; - - impacts = new double[names.length]; - } - - //~ Methods ------------------------------------------------------------------------------------ - @Override - public String getDump () - { - final StringBuilder sb = new StringBuilder(); - - return sb.toString(); - } - - @Override - public double getGrade () - { - if (grade == -1) { - grade = computeGrade(); - } - - return grade; - } - - @Override - public double getImpact (int index) - { - return impacts[index]; - } - - @Override - public int getImpactCount () - { - return impacts.length; - } - - @Override - public String getName (int index) - { - return names[index]; - } - - @Override - public double getWeight (int index) - { - return weights[index]; - } - - public void setImpact (int index, - double impact) - { - impacts[index] = GradeUtil.clamp(impact); - } -} diff --git a/src/main/org/audiveris/omr/sig/BeamHeadCleaner.java b/src/main/org/audiveris/omr/sig/BeamHeadCleaner.java index 1150b2275..d3ee7a825 100644 --- a/src/main/org/audiveris/omr/sig/BeamHeadCleaner.java +++ b/src/main/org/audiveris/omr/sig/BeamHeadCleaner.java @@ -1,71 +1,70 @@ -//------------------------------------------------------------------------------------------------// -// // -// B e a m H e a d C l e a n e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig; - -import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sig.relation.BeamHeadRelation; -import org.audiveris.omr.sig.relation.Relation; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Set; - -/** - * Class {@code BeamHeadCleaner} remove all BeamHeadRelation links of a system - * when they are no longer needed. - * - * @author Hervé Bitteur - */ -public class BeamHeadCleaner -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(BeamHeadCleaner.class); - - //~ Instance fields ---------------------------------------------------------------------------- - /** Related system. */ - private final SystemInfo system; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code BeamHeadCleaner} object. - * - * @param system the system to process - */ - public BeamHeadCleaner (SystemInfo system) - { - this.system = system; - } - - //~ Methods ------------------------------------------------------------------------------------ - //---------// - // process // - //---------// - public void process () - { - SIGraph sig = system.getSig(); - Set set = SIGraph.getRelations(sig.edgeSet(), BeamHeadRelation.class); - logger.debug("System#{} BeamHeadRelation instances: {}", system.getId(), set.size()); - sig.removeAllEdges(set); - } -} +//------------------------------------------------------------------------------------------------// +// // +// B e a m H e a d C l e a n e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig; + +import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sig.relation.BeamHeadRelation; +import org.audiveris.omr.sig.relation.Relation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +/** + * Class {@code BeamHeadCleaner} remove all BeamHeadRelation links of a system + * when they are no longer needed. + * + * @author Hervé Bitteur + */ +public class BeamHeadCleaner +{ + + private static final Logger logger = LoggerFactory.getLogger(BeamHeadCleaner.class); + + /** Related system. */ + private final SystemInfo system; + + /** + * Creates a new {@code BeamHeadCleaner} object. + * + * @param system the system to process + */ + public BeamHeadCleaner (SystemInfo system) + { + this.system = system; + } + + //---------// + // process // + //---------// + /** + * Clear BeamHeadRelations. + */ + public void process () + { + SIGraph sig = system.getSig(); + Set set = SIGraph.getRelations(sig.edgeSet(), BeamHeadRelation.class); + logger.debug("System#{} BeamHeadRelation instances: {}", system.getId(), set.size()); + sig.removeAllEdges(set); + } +} diff --git a/src/main/org/audiveris/omr/sig/CrossDetector.java b/src/main/org/audiveris/omr/sig/CrossDetector.java index fbc120ca9..735135941 100644 --- a/src/main/org/audiveris/omr/sig/CrossDetector.java +++ b/src/main/org/audiveris/omr/sig/CrossDetector.java @@ -54,17 +54,14 @@ */ public class CrossDetector { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(CrossDetector.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code CrossDetector} object. * @@ -75,10 +72,12 @@ public CrossDetector (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // process // //---------// + /** + * Resolve conflicts between overlapping inters from different systems. + */ public void process () { final SystemManager manager = sheet.getSystemManager(); @@ -278,14 +277,12 @@ private double staffDistance (Inter inter) return staff.distanceTo(center); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Evaluation.Grade minGradeDiff = new Evaluation.Grade( 0.1, diff --git a/src/main/org/audiveris/omr/sig/GradeImpacts.java b/src/main/org/audiveris/omr/sig/GradeImpacts.java index e82d05acc..9c5f4c9a6 100644 --- a/src/main/org/audiveris/omr/sig/GradeImpacts.java +++ b/src/main/org/audiveris/omr/sig/GradeImpacts.java @@ -21,31 +21,79 @@ // package org.audiveris.omr.sig; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import org.audiveris.omr.glyph.Grades; /** - * Interface {@code GradeImpacts} defines data that impact a resulting grade value. + * Abstract class {@code GradeImpacts} defines data that impact a resulting grade value. + *

                                                                                                          + * It uses 3 parallel arrays for: + *

                                                                                                            + *
                                                                                                          • impact name + *
                                                                                                          • impact value + *
                                                                                                          • impact (relative) weight + *
                                                                                                          * * @author Hervé Bitteur */ -@XmlJavaTypeAdapter(AbstractImpacts.Adapter.class) -public interface GradeImpacts +public abstract class GradeImpacts { - //~ Methods ------------------------------------------------------------------------------------ + + /** Array of actual contributions. */ + protected final double[] impacts; + + /** Array of impacts name. */ + private final String[] names; + + /** Array of impacts weight. */ + private final double[] weights; + + /** Resulting grade. */ + protected double grade = -1; + + /** + * Creates a new BasicImpacts object. + * + * @param names array of names + * @param weights array of weights + */ + public GradeImpacts (String[] names, + double[] weights) + { + if (names.length != weights.length) { + throw new IllegalArgumentException("Arrays for names & weights have different lengths"); + } + + this.names = names; + this.weights = weights; + + impacts = new double[names.length]; + } /** * Report a string about impacts details * * @return string of details */ - String getDump (); + public String getDump () + { + final StringBuilder sb = new StringBuilder(); + + return sb.toString(); + } /** - * Retrieve a global grade value from detailed impacts. + * Report the global grade value from detailed impacts. * * @return the computed grade in range 0 .. 1 */ - double getGrade (); + public double getGrade () + { + if (grade == -1) { + grade = computeGrade(); + } + + return grade; + } /** * Report the value of the grade impact corresponding to index @@ -53,21 +101,42 @@ public interface GradeImpacts * @param index the index of desired impact * @return the impact value */ - double getImpact (int index); + public double getImpact (int index) + { + return impacts[index]; + } + + /** + * Set impact at provided index. + * + * @param index index in impact array + * @param impact value for specific impact + */ + public void setImpact (int index, + double impact) + { + impacts[index] = GradeUtil.clamp(impact); + } /** * Report the number of individual grade impacts. * * @return the count of impacts */ - int getImpactCount (); + public int getImpactCount () + { + return impacts.length; + } /** * Report the reduction ratio to be applied on intrinsic grade * * @return the reduction ratio to be applied */ - double getIntrinsicRatio (); + public double getIntrinsicRatio () + { + return Grades.intrinsicRatio; + } /** * Report the name of the grade impact corresponding to index @@ -75,7 +144,10 @@ public interface GradeImpacts * @param index the index of desired impact * @return the impact name */ - String getName (int index); + public String getName (int index) + { + return names[index]; + } /** * Report the weight of the grade impact corresponding to index @@ -83,5 +155,55 @@ public interface GradeImpacts * @param index the index of desired impact * @return the impact weight */ - double getWeight (int index); + public double getWeight (int index) + { + return weights[index]; + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < getImpactCount(); i++) { + if (sb.length() > 0) { + sb.append(" "); + } + + sb.append(String.format("%s:%.2f", getName(i), getImpact(i))); + } + + return sb.toString(); + } + + //--------------// + // computeGrade // + //--------------// + /** + * Compute resulting grade from all impacts. + * + * @return the resulting grade + */ + protected double computeGrade () + { + double global = 1d; + double totalWeight = 0d; + + for (int i = 0; i < getImpactCount(); i++) { + double weight = getWeight(i); + double impact = getImpact(i); + totalWeight += weight; + + if (impact == 0) { + global = 0; + } else if (weight != 0) { + global *= Math.pow(impact, weight); + } + } + + return getIntrinsicRatio() * Math.pow(global, 1 / totalWeight); + } } diff --git a/src/main/org/audiveris/omr/sig/GradeUtil.java b/src/main/org/audiveris/omr/sig/GradeUtil.java index 8d0e19dd1..ab0783ebb 100644 --- a/src/main/org/audiveris/omr/sig/GradeUtil.java +++ b/src/main/org/audiveris/omr/sig/GradeUtil.java @@ -31,13 +31,16 @@ * * @author Hervé Bitteur */ -public class GradeUtil +public abstract class GradeUtil { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GradeUtil.class); - //~ Methods ------------------------------------------------------------------------------------ + /** Not meant to be instantiated. */ + private GradeUtil () + { + } + //-------// // clamp // //-------// diff --git a/src/main/org/audiveris/omr/sig/InterIndex.java b/src/main/org/audiveris/omr/sig/InterIndex.java index e1eecb14d..35fe0e03e 100644 --- a/src/main/org/audiveris/omr/sig/InterIndex.java +++ b/src/main/org/audiveris/omr/sig/InterIndex.java @@ -51,13 +51,11 @@ public class InterIndex extends BasicIndex { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(InterIndex.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new InterManager object. */ @@ -65,7 +63,6 @@ public InterIndex () { } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // initTransients // //----------------// @@ -129,14 +126,13 @@ public void publish (final Inter inter) final EntityService interService = this.getEntityService(); if (interService != null) { - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () { interService.publish( - new EntityListEvent( + new EntityListEvent<>( this, SelectionHint.ENTITY_INIT, MouseMovement.PRESSING, @@ -146,14 +142,12 @@ public void run () } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String vipInters = new Constant.String( "", diff --git a/src/main/org/audiveris/omr/sig/SIGraph.java b/src/main/org/audiveris/omr/sig/SIGraph.java index 50d33baf2..f4a02409d 100644 --- a/src/main/org/audiveris/omr/sig/SIGraph.java +++ b/src/main/org/audiveris/omr/sig/SIGraph.java @@ -25,6 +25,7 @@ import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sig.inter.AbstractInter; import org.audiveris.omr.sig.inter.HeadInter; import org.audiveris.omr.sig.inter.Inter; import org.audiveris.omr.sig.inter.Inters; @@ -75,11 +76,9 @@ public class SIGraph extends DefaultListenableGraph implements DirectedGraph { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SIGraph.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Dedicated system. */ @Navigable(false) private SystemInfo system; @@ -87,7 +86,6 @@ public class SIGraph /** Content for differed populating after unmarshalling. */ private SigValue sigValue; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SIGraph object at system level. * @@ -120,7 +118,6 @@ private SIGraph () super(new DirectedMultigraph(Relation.class), true /* reuseEvents */); } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // addVertex // //-----------// @@ -172,6 +169,9 @@ public void afterReload (SystemInfo system) // SigValue is no longer useful and can be disposed of sigValue = null; + + upgradeInters(); // Temporary upgrade stuff + } catch (Exception ex) { logger.warn("Error in " + getClass() + " afterReload() " + ex, ex); } @@ -180,6 +180,12 @@ public void afterReload (SystemInfo system) //------------------------// // computeContextualGrade // //------------------------// + /** + * Compute the contextual grade of provided inter. + * + * @param inter provided inter + * @return contextual grade value + */ public double computeContextualGrade (Inter inter) { final List supports = getSupports(inter); @@ -202,7 +208,7 @@ public double computeContextualGrade (Inter inter) */ public List containedInters (Rectangle rect) { - List found = new ArrayList(); + List found = new ArrayList<>(); for (Inter inter : vertexSet()) { final Rectangle box = inter.getBounds(); @@ -229,7 +235,7 @@ public List containedInters (Rectangle rect) */ public List containingInters (Point point) { - List found = new ArrayList(); + List found = new ArrayList<>(); for (Inter inter : vertexSet()) { Rectangle bounds = inter.getBounds(); @@ -263,6 +269,11 @@ public void contextualize () //--------------// // deleteInters // //--------------// + /** + * Remove a collection of inters. + * + * @param inters to delete + */ public void deleteInters (Collection inters) { for (Inter inter : inters) { @@ -281,7 +292,7 @@ public void deleteInters (Collection inters) */ public Set deleteWeakInters () { - Set removed = new LinkedHashSet(); + Set removed = new LinkedHashSet<>(); for (Inter inter : vertexSet()) { // Skip frozen inters @@ -318,7 +329,7 @@ public Set deleteWeakInters () */ public Set exclusions () { - Set exclusions = new LinkedHashSet(); + Set exclusions = new LinkedHashSet<>(); for (Relation rel : edgeSet()) { if (rel instanceof Exclusion) { @@ -339,7 +350,7 @@ public Set exclusions () public Set getClosureOf (final List seeds, final Class... relationClasses) { - final Set closure = new LinkedHashSet(); + final Set closure = new LinkedHashSet<>(); class ClosureBuilder { @@ -441,15 +452,15 @@ public List> getPartitions (Inter focus, final int n = inters.size(); final List stems = (focus instanceof HeadInter) ? stemsOf(inters) : null; - final List> result = new ArrayList>(); + final List> result = new ArrayList<>(); // Map inter -> concurrents of inter (that appear later within the provided list) - List> concurrentSets = new ArrayList>(); + List> concurrentSets = new ArrayList<>(); boolean conflictDetected = false; for (int i = 0; i < n; i++) { Inter inter = inters.get(i); - Set concurrents = new LinkedHashSet(); + Set concurrents = new LinkedHashSet<>(); concurrentSets.add(concurrents); for (Relation rel : getExclusions(inter)) { @@ -489,7 +500,7 @@ public List> getPartitions (Inter focus, } // Define all possible sequences - List seqs = new ArrayList(); + List seqs = new ArrayList<>(); seqs.add(new Sequence(n)); for (int i = 0; i < n; i++) { @@ -518,7 +529,7 @@ public List> getPartitions (Inter focus, // Build resulting partitions for (Sequence seq : seqs) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (int i = 0; i < n; i++) { if (seq.line[i] == 1) { @@ -570,7 +581,7 @@ public Relation getRelation (Inter source, public Set getRelations (Inter inter, Class... classes) { - Set relations = new LinkedHashSet(); + Set relations = new LinkedHashSet<>(); for (Relation rel : edgesOf(inter)) { for (Class classe : classes) { @@ -596,7 +607,7 @@ public Set getRelations (Inter inter, public Set getRelations (Inter inter, Class classe) { - Set relations = new LinkedHashSet(); + Set relations = new LinkedHashSet<>(); for (Relation rel : edgesOf(inter)) { if (classe.isInstance(rel)) { @@ -618,7 +629,7 @@ public Set getRelations (Inter inter, */ public List getSupports (Inter inter) { - List supports = new ArrayList(); + List supports = new ArrayList<>(); for (Relation rel : edgesOf(inter)) { if (rel instanceof Support) { @@ -680,6 +691,12 @@ public final void populateAllInters (Collection inters) } } + @Override + public Object clone () + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //--------------// // getRelations // //--------------// @@ -693,7 +710,7 @@ public final void populateAllInters (Collection inters) public static Set getRelations (Collection relations, Class classe) { - Set found = new LinkedHashSet(); + Set found = new LinkedHashSet<>(); for (Relation rel : relations) { if (classe.isInstance(rel)) { @@ -768,8 +785,8 @@ public Exclusion insertExclusion (Inter inter1, public List insertExclusions (Collection inters, Cause cause) { - List list = new ArrayList(new LinkedHashSet(inters)); - List exclusions = new ArrayList(); + List list = new ArrayList(new LinkedHashSet<>(inters)); + List exclusions = new ArrayList<>(); for (int i = 0, iBreak = list.size(); i < iBreak; i++) { Inter inter = list.get(i); @@ -830,8 +847,9 @@ public Support insertSupport (Inter inter1, if (inter1.isVip() || inter2.isVip()) { logger.info("VIP support {}", sup.toLongString(this)); } - } catch (Exception ex) { - logger.error("Could not instantiate {}", supportClass); + } catch (IllegalAccessException | + InstantiationException ex) { + logger.error("Could not instantiate {} {}", supportClass, ex.toString(), ex); } return sup; @@ -883,7 +901,7 @@ public List inters (Predicate predicate) // inters // //--------// /** - * Lookup for interpretations of the provided class. + * Lookup for interpretations of the provided class (or subclass thereof). * * @param classe the class to search for * @return the interpretations of desired class, perhaps empty but not null @@ -948,7 +966,7 @@ public List inters (final Staff staff, */ public List intersectedInters (Rectangle box) { - List found = new ArrayList(); + List found = new ArrayList<>(); for (Inter inter : vertexSet()) { if (inter.isRemoved()) { @@ -977,7 +995,7 @@ public List intersectedInters (Rectangle box) public boolean noSupport (Inter one, Inter two) { - Set rels = new LinkedHashSet(); + Set rels = new LinkedHashSet<>(); rels.addAll(getAllEdges(one, two)); rels.addAll(getAllEdges(two, one)); @@ -1010,7 +1028,8 @@ public void publish (final Inter inter) * Reduce the provided exclusions as much as possible by removing the source or * target vertex of lower contextual grade. *

                                                                                                          - * Strategy is as follows:

                                                                                                            + * Strategy is as follows: + *
                                                                                                              *
                                                                                                            1. Pick up among all current exclusions the one whose high inter has the highest contextual * grade contribution among all exclusions,
                                                                                                            2. *
                                                                                                            3. Remove the weaker inter in this chosen exclusion relation,
                                                                                                            4. @@ -1023,7 +1042,7 @@ public void publish (final Inter inter) */ public Set reduceExclusions (Collection exclusions) { - final Set removed = new LinkedHashSet(); + final Set removed = new LinkedHashSet<>(); Relation bestRel; do { @@ -1112,7 +1131,7 @@ public Set reduceExclusions () public boolean removeVertex (Inter inter) { if (!inter.isRemoved()) { - logger.error("Do not use removeVertex() directly. Use inter.delete() instead."); + logger.error("Do not use removeVertex() directly. Use inter.remove() instead."); throw new IllegalStateException("Do not use removeVertex() directly"); } @@ -1137,20 +1156,18 @@ public boolean removeVertex (Inter inter) */ public void sortBySource (List rels) { - Collections.sort( - rels, - new Comparator() - { - @Override - public int compare (Relation r1, - Relation r2) - { - Inter s1 = getEdgeSource(r1); - Inter s2 = getEdgeSource(r2); - - return Double.compare(s2.getBestGrade(), s1.getBestGrade()); - } - }); + Collections.sort(rels, new Comparator() + { + @Override + public int compare (Relation r1, + Relation r2) + { + Inter s1 = getEdgeSource(r1); + Inter s2 = getEdgeSource(r2); + + return Double.compare(s2.getBestGrade(), s1.getBestGrade()); + } + }); } //----------// @@ -1169,6 +1186,27 @@ public String toString () return sb.toString(); } + //---------------// + // upgradeInters // + //---------------// + /** + * All just loaded inters are checked and potentially upgraded. + */ + @Deprecated + private void upgradeInters () + { + boolean upgraded = false; + + for (Inter inter : this.vertexSet()) { + AbstractInter abstractInter = (AbstractInter) inter; + upgraded |= abstractInter.upgradeOldStuff(); + } + + if (upgraded) { + system.getSheet().getStub().setUpgraded(true); + } + } + //------------------------// // computeContextualGrade // //------------------------// @@ -1191,10 +1229,10 @@ private Double computeContextualGrade (Inter inter, Collection supports) { /** Collection of partners. */ - final List partners = new ArrayList(); + final List partners = new ArrayList<>(); /** Map: partner -> contribution. */ - final Map partnerContrib = new HashMap(); + final Map partnerContrib = new HashMap<>(); // Check inter involvement for (Support support : supports) { @@ -1239,7 +1277,7 @@ private Double computeContextualGrade (Inter inter, //----------------// private Set involvedInters (Collection relations) { - Set inters = new LinkedHashSet(); + Set inters = new LinkedHashSet<>(); for (Relation rel : relations) { inters.add(getEdgeSource(rel)); @@ -1254,7 +1292,7 @@ private Set involvedInters (Collection relations) //---------// private List stemsOf (List inters) { - List stems = new ArrayList(); + List stems = new ArrayList<>(); for (Inter inter : inters) { if (inter instanceof StemInter) { @@ -1290,48 +1328,14 @@ private String supportsSeenFrom (Inter inter, return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ - //--------------// - // Contribution // - //--------------// - /** - * Meant to sort the actual contributions brought by supporting partners. - */ - private static class Contribution - { - //~ Static fields/initializers ------------------------------------------------------------- - - public static Comparator byReverseValue = new Comparator() - { - @Override - public int compare (Contribution o1, - Contribution o2) - { - return Double.compare(o2.value, o1.value); - } - }; - - //~ Instance fields ------------------------------------------------------------------------ - final Inter partner; // Contributing partner - - final double value; // Concrete contribution brought by the partner - - //~ Constructors --------------------------------------------------------------------------- - public Contribution (Inter partner, - double ratio) - { - this.partner = partner; - value = ratio * partner.getGrade(); - } - } - //----------// // Sequence // //----------// /** * This class lists a sequence of interpretations statuses. *

                                                                                                              - * Possible status values are:

                                                                                                                + * Possible status values are: + *
                                                                                                                  *
                                                                                                                • -1: the related inter is forbidden (because of a conflict with an inter located before in * the sequence)
                                                                                                                • *
                                                                                                                • 0: the related inter is not selected
                                                                                                                • @@ -1340,20 +1344,17 @@ public Contribution (Inter partner, */ private static class Sequence { - //~ Instance fields ------------------------------------------------------------------------ // The sequence of interpretations statuses // This line is parallel to the list of inters considered int[] line; - //~ Constructors --------------------------------------------------------------------------- - public Sequence (int n) + Sequence (int n) { line = new int[n]; Arrays.fill(line, 0); } - //~ Methods -------------------------------------------------------------------------------- public Sequence copy () { Sequence newSeq = new Sequence(line.length); @@ -1369,17 +1370,14 @@ public Sequence copy () private static class ShapePredicate implements Predicate { - //~ Instance fields ------------------------------------------------------------------------ private final Shape shape; - //~ Constructors --------------------------------------------------------------------------- - public ShapePredicate (Shape shape) + ShapePredicate (Shape shape) { this.shape = shape; } - //~ Methods -------------------------------------------------------------------------------- @Override public boolean check (Inter inter) { @@ -1393,17 +1391,14 @@ public boolean check (Inter inter) private static class ShapesPredicate implements Predicate { - //~ Instance fields ------------------------------------------------------------------------ private final Collection shapes; - //~ Constructors --------------------------------------------------------------------------- - public ShapesPredicate (Collection shapes) + ShapesPredicate (Collection shapes) { this.shapes = shapes; } - //~ Methods -------------------------------------------------------------------------------- @Override public boolean check (Inter inter) { @@ -1417,26 +1412,23 @@ public boolean check (Inter inter) private static class StaffClassPredicate implements Predicate { - //~ Instance fields ------------------------------------------------------------------------ private final Staff staff; private final Class classe; - //~ Constructors --------------------------------------------------------------------------- - public StaffClassPredicate (Staff staff, - Class classe) + StaffClassPredicate (Staff staff, + Class classe) { this.staff = staff; this.classe = classe; } - //~ Methods -------------------------------------------------------------------------------- @Override public boolean check (Inter inter) { return !inter.isRemoved() && (inter.getStaff() == staff) - && ((classe == null) || classe.isInstance(inter)); + && ((classe == null) || classe.isInstance(inter)); } } } diff --git a/src/main/org/audiveris/omr/sig/SigAttic.java b/src/main/org/audiveris/omr/sig/SigAttic.java deleted file mode 100644 index 98acf29ab..000000000 --- a/src/main/org/audiveris/omr/sig/SigAttic.java +++ /dev/null @@ -1,150 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// S i g A t t i c // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig; - -import org.audiveris.omr.sig.inter.AbstractChordInter; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.relation.Relation; - -import org.jgrapht.graph.Multigraph; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * Class {@code SigAttic} is a graph used as a temporary storage for some inters of a - * sig, as well as all the relations these inters are involved in. - * - * @author Hervé Bitteur - */ -public class SigAttic - extends Multigraph -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(SigAttic.class); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SigAttic} object. - */ - public SigAttic () - { - super(Relation.class); - } - - //~ Methods ------------------------------------------------------------------------------------ - //---------// - // restore // - //---------// - /** - * Restore from this attic to sig the provided collection of Inter instances, as - * well as the relations they are involved in. - * - * @param sig the sig to partially restore - * @param seeds the collection of primary Inter instances to restore - */ - public void restore (SIGraph sig, - Collection seeds) - { - // Include chord notes as well - Set vertices = new LinkedHashSet(seeds); - - for (Inter inter : seeds) { - if (inter instanceof AbstractChordInter) { - vertices.addAll(((AbstractChordInter) inter).getNotes()); - } - } - - // Restore primary inter instances - for (Inter inter : vertices) { - sig.addVertex(inter); - } - - // Restore relations from seeds - for (Inter inter : seeds) { - for (Relation rel : edgesOf(inter)) { - Inter source = getEdgeSource(rel); - Inter target = getEdgeTarget(rel); - - if (inter == source) { - if (sig.containsVertex(target)) { - sig.addEdge(inter, target, rel); - } - } else if (sig.containsVertex(source)) { - sig.addEdge(source, inter, rel); - } - } - } - } - - //------// - // save // - //------// - /** - * Save from sig to this attic a collection of Inter instances, as well as the - * relations these inters are involved in, which may sometimes also require to - * save secondary inters that are located on the opposite side of a relation. - * - * @param sig the sig to partially backup - * @param seeds the collection of primary Inter instances to save - */ - public void save (SIGraph sig, - Collection seeds) - { - // Save needed vertices - Set vertices = new LinkedHashSet(seeds); - - for (Inter seed : seeds) { - if (seed instanceof AbstractChordInter) { - // Include chord notes as well - vertices.addAll(((AbstractChordInter) seed).getNotes()); - } - } - - for (Inter vertex : vertices) { - addVertex(vertex); - } - - // Save involved relations as edges (including linked vertices) - for (Inter seed : seeds) { - for (Relation rel : sig.edgesOf(seed)) { - Inter source = sig.getEdgeSource(rel); - Inter target = sig.getEdgeTarget(rel); - - // Save secondary Inter if so needed (just to allow the relation) - if (seed == source) { - addVertex(target); - } else { - addVertex(source); - } - - // Save relation - addEdge(source, target, rel); - } - } - } -} diff --git a/src/main/org/audiveris/omr/sig/SigBackup.java b/src/main/org/audiveris/omr/sig/SigBackup.java deleted file mode 100644 index 4542f8763..000000000 --- a/src/main/org/audiveris/omr/sig/SigBackup.java +++ /dev/null @@ -1,96 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// S i g B a c k u p // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig; - -import org.audiveris.omr.sig.inter.Inter; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * Class {@code SigBackup} allows to save and restore inters (with their relations) - * from/to a sig. - * - * @author Hervé Bitteur - */ -public class SigBackup -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** Saved data. */ - protected List seeds; - - /** The attic where data can be saved to and restored from. */ - protected final SigAttic attic = new SigAttic(); - - /** The SIG where work is done. */ - protected final SIGraph sig; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SigBackup} object. - * - * @param sig the related sig - */ - public SigBackup (SIGraph sig) - { - this.sig = sig; - } - - //~ Methods ------------------------------------------------------------------------------------ - //----------// - // getSeeds // - //----------// - public List getSeeds () - { - return seeds; - } - - //---------// - // restore // - //---------// - public void restore (Collection inters) - { - attic.restore(sig, inters); - } - - //------// - // save // - //------// - public void save (Collection inters) - { - // Copy the initial seeds - setSeeds(inters); - - // Save relevant sig inters & relations - attic.save(sig, seeds); - } - - //----------// - // setSeeds // - //----------// - public void setSeeds (Collection seeds) - { - this.seeds = new ArrayList(seeds); - } -} diff --git a/src/main/org/audiveris/omr/sig/SigListener.java b/src/main/org/audiveris/omr/sig/SigListener.java index 12cf40fb0..c73a73f4f 100644 --- a/src/main/org/audiveris/omr/sig/SigListener.java +++ b/src/main/org/audiveris/omr/sig/SigListener.java @@ -1,114 +1,111 @@ -//------------------------------------------------------------------------------------------------// -// // -// S i g L i s t e n e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig; - -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.relation.Relation; - -import org.jgrapht.event.GraphEdgeChangeEvent; -import org.jgrapht.event.GraphListener; -import org.jgrapht.event.GraphVertexChangeEvent; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Class {@code SigListener} listens for SIG modifications. - * - * @author Hervé Bitteur - */ -public class SigListener - implements GraphListener -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(SigListener.class); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SigListener} object. - */ - public SigListener () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - @Override - public void edgeAdded (GraphEdgeChangeEvent e) - { - // logger.info( - // "GRAPH edgeAdded {} src:{} tgt:{}", - // e.getEdge(), - // e.getEdgeSource(), - // e.getEdgeTarget()); - // - e.getEdge().added(e); - } - - @Override - public void edgeRemoved (GraphEdgeChangeEvent e) - { - // logger.info( - // "GRAPH edgeRemoved {} src:{} tgt:{}", - // e.getEdge(), - // e.getEdgeSource(), - // e.getEdgeTarget()); - // - e.getEdge().removed(e); - } - - @Override - public void vertexAdded (GraphVertexChangeEvent e) - { - // logger.info( - // "GRAPH vertexAdded {} source:{} type:{}", - // e.getVertex(), - // e.getSource(), - // e.getType()); - // - // if (e.getVertex().isVip()) { - // logger.info( - // "VIP GRAPH vertexAdded {} source:{} type:{}", - // e.getVertex(), - // e.getSource(), - // e.getType()); - // } - } - - @Override - public void vertexRemoved (GraphVertexChangeEvent e) - { - // logger.info( - // "GRAPH vertexRemoved {} source:{} type:{}", - // e.getVertex(), - // e.getSource(), - // e.getType()); - // - // if (e.getVertex().isVip()) { - // logger.info( - // "VIP GRAPH vertexRemoved {} source:{} type:{}", - // e.getVertex(), - // e.getSource(), - // e.getType()); - // } - } -} +//------------------------------------------------------------------------------------------------// +// // +// S i g L i s t e n e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig; + +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.relation.Relation; + +import org.jgrapht.event.GraphEdgeChangeEvent; +import org.jgrapht.event.GraphListener; +import org.jgrapht.event.GraphVertexChangeEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class {@code SigListener} listens for SIG modifications. + * + * @author Hervé Bitteur + */ +public class SigListener + implements GraphListener +{ + + private static final Logger logger = LoggerFactory.getLogger(SigListener.class); + + /** + * Creates a new {@code SigListener} object. + */ + public SigListener () + { + } + + @Override + public void edgeAdded (GraphEdgeChangeEvent e) + { + // logger.info( + // "GRAPH edgeAdded {} src:{} tgt:{}", + // e.getEdge(), + // e.getEdgeSource(), + // e.getEdgeTarget()); + // + e.getEdge().added(e); + } + + @Override + public void edgeRemoved (GraphEdgeChangeEvent e) + { + // logger.info( + // "GRAPH edgeRemoved {} src:{} tgt:{}", + // e.getEdge(), + // e.getEdgeSource(), + // e.getEdgeTarget()); + // + e.getEdge().removed(e); + } + + @Override + public void vertexAdded (GraphVertexChangeEvent e) + { + // logger.info( + // "GRAPH vertexAdded {} source:{} type:{}", + // e.getVertex(), + // e.getSource(), + // e.getType()); + // + // if (e.getVertex().isVip()) { + // logger.info( + // "VIP GRAPH vertexAdded {} source:{} type:{}", + // e.getVertex(), + // e.getSource(), + // e.getType()); + // } + } + + @Override + public void vertexRemoved (GraphVertexChangeEvent e) + { + // logger.info( + // "GRAPH vertexRemoved {} source:{} type:{}", + // e.getVertex(), + // e.getSource(), + // e.getType()); + // + // if (e.getVertex().isVip()) { + // logger.info( + // "VIP GRAPH vertexRemoved {} source:{} type:{}", + // e.getVertex(), + // e.getSource(), + // e.getType()); + // } + } +} diff --git a/src/main/org/audiveris/omr/sig/SigReducer.java b/src/main/org/audiveris/omr/sig/SigReducer.java index 8a0b3ee68..d2ceca4c6 100644 --- a/src/main/org/audiveris/omr/sig/SigReducer.java +++ b/src/main/org/audiveris/omr/sig/SigReducer.java @@ -35,9 +35,11 @@ import org.audiveris.omr.sheet.SystemInfo; import org.audiveris.omr.sheet.header.StaffHeader; import org.audiveris.omr.sheet.rhythm.Measure; +import org.audiveris.omr.sheet.stem.StemsBuilder; import org.audiveris.omr.sig.inter.AbstractBeamInter; import org.audiveris.omr.sig.inter.AbstractChordInter; import org.audiveris.omr.sig.inter.AbstractNoteInter; +import org.audiveris.omr.sig.inter.AbstractPitchedInter; import org.audiveris.omr.sig.inter.AbstractTimeInter; import org.audiveris.omr.sig.inter.AlterInter; import org.audiveris.omr.sig.inter.AugmentationDotInter; @@ -74,9 +76,7 @@ import org.audiveris.omr.sig.relation.HeadStemRelation; import org.audiveris.omr.sig.relation.Relation; import org.audiveris.omr.sig.relation.StemPortion; -import static org.audiveris.omr.sig.relation.StemPortion.STEM_BOTTOM; -import static org.audiveris.omr.sig.relation.StemPortion.STEM_MIDDLE; -import static org.audiveris.omr.sig.relation.StemPortion.STEM_TOP; +import static org.audiveris.omr.sig.relation.StemPortion.*; import org.audiveris.omr.sig.relation.TimeTopBottomRelation; import org.audiveris.omr.util.HorizontalSide; import static org.audiveris.omr.util.HorizontalSide.LEFT; @@ -95,7 +95,6 @@ import java.util.Collections; import java.util.EnumMap; import java.util.EnumSet; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -114,7 +113,6 @@ */ public class SigReducer { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -152,17 +150,6 @@ public boolean check (Inter inter) private static final EnumSet stemCompShapes = EnumSet.copyOf( Arrays.asList(Shape.SLUR, Shape.CRESCENDO, Shape.DIMINUENDO)); - //~ Enumerations ------------------------------------------------------------------------------- - /** Standard vs Small size. */ - private static enum Size - { - //~ Enumeration constant initializers ------------------------------------------------------ - - STANDARD, - SMALL; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** The dedicated system. */ @Navigable(false) private final SystemInfo system; @@ -177,7 +164,6 @@ private static enum Size /** Should we purge weak inter instances?. */ private final boolean purgeWeaks; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SigReducer} object. * @@ -194,7 +180,6 @@ public SigReducer (SystemInfo system, scale = system.getSheet().getScale(); } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // reduceFoundations // //-------------------// @@ -241,10 +226,10 @@ private void analyzeChords () Collections.sort(stems, Inters.byReverseGrade); // Heads organized by shape (black, void, and small versions) - Map> heads = new HashMap>(); + Map> heads = new EnumMap<>(Shape.class); // Beams organized by size (standard vs small versions) - Map> beams = new EnumMap>(Size.class); + Map> beams = new EnumMap<>(Size.class); for (Inter stem : stems) { if (stem.isVip()) { @@ -267,7 +252,7 @@ private void analyzeChords () Set set = heads.get(shape); if (set == null) { - heads.put(shape, set = new LinkedHashSet()); + heads.put(shape, set = new LinkedHashSet<>()); } set.add(head); @@ -277,7 +262,7 @@ private void analyzeChords () Set set = beams.get(size); if (set == null) { - beams.put(size, set = new LinkedHashSet()); + beams.put(size, set = new LinkedHashSet<>()); } set.add(beam); @@ -287,7 +272,7 @@ private void analyzeChords () // Mutual head exclusion based on head shape // But perhaps the joining stem is the problem, so check the two conflicting heads // are not linked to any other stem than the one at hand. - List headShapes = new ArrayList(heads.keySet()); + List headShapes = new ArrayList<>(heads.keySet()); for (int ic = 0; ic < (headShapes.size() - 1); ic++) { Shape c1 = headShapes.get(ic); @@ -313,8 +298,8 @@ private void analyzeChords () } // Mutual head support within same shape - for (Shape shape : heads.keySet()) { - List list = new ArrayList(heads.get(shape)); + for (Set set : heads.values()) { + List list = new ArrayList<>(set); for (int i = 0; i < list.size(); i++) { HeadInter h1 = (HeadInter) list.get(i); @@ -327,7 +312,7 @@ private void analyzeChords () } // Mutual beam exclusion based on beam size - List beamSizes = new ArrayList(beams.keySet()); + List beamSizes = new ArrayList<>(beams.keySet()); for (int ic = 0; ic < (beamSizes.size() - 1); ic++) { Size c1 = beamSizes.get(ic); @@ -365,7 +350,10 @@ private void analyzeChords () BeamStemRelation.class); for (Inter smallHead : smallHeadSet) { - if (sig.getRelation(smallBeam, smallHead, BeamHeadRelation.class) == null) { + if (sig.getRelation( + smallBeam, + smallHead, + BeamHeadRelation.class) == null) { // Use average of beam-stem and head-stem relation grades HeadStemRelation hs = (HeadStemRelation) sig.getRelation( smallHead, @@ -423,7 +411,7 @@ private void analyzeFrozenInters () { logger.debug("S#{} analyzeFrozenInters", system.getId()); - Set toDelete = new LinkedHashSet(); + Set toDelete = new LinkedHashSet<>(); for (Inter inter : sig.vertexSet()) { if (inter.isFrozen()) { @@ -485,7 +473,7 @@ private void analyzeHeadStems () /** * Perform checks on augmentation dots. *

                                                                                                                  - * An augmentation dot needs a target to augment (note, rest or another augmentation dot). + * An augmentation dot needs a target to augment (note, rest or a first augmentation dot). * * @return the count of modifications done */ @@ -749,7 +737,7 @@ private int checkHeadSide (Inter head) } // Pitch of the note head - int pitch = ((HeadInter) head).getIntegerPitch(); + int pitch = ((AbstractPitchedInter) head).getIntegerPitch(); // Target side and target pitches of other head // Look for presence of head on other side with target pitch @@ -895,9 +883,8 @@ private int checkIsolatedAlters () Part part = staff.getPart(); Measure measure = part.getMeasureAt(center); - if ((measure != null) - && (measure == part.getLastMeasure()) - && (measure.getPartBarlineOn(HorizontalSide.RIGHT) == null)) { + if ((measure != null) && (measure == part.getLastMeasure()) + && (measure.getPartBarlineOn(HorizontalSide.RIGHT) == null)) { // Empty measure? // Measure width? continue; @@ -930,7 +917,7 @@ private int checkLedgers () List allHeads = sig.inters(ShapeSet.Heads); Collections.sort(allHeads, Inters.byAbscissa); - final List toDelete = new ArrayList(); + final List toDelete = new ArrayList<>(); boolean modified; do { @@ -939,17 +926,17 @@ private int checkLedgers () for (Staff staff : system.getStaves()) { SortedMap> map = staff.getLedgerMap(); - // Need a set copy to avoid concurrent modifications - Set>> setCopy; - setCopy = new LinkedHashSet>>(map.entrySet()); + // Need a read-only copy to avoid concurrent modifications + List>> staffLedgers; + staffLedgers = new ArrayList<>(map.entrySet()); - for (Entry> entry : setCopy) { + for (Entry> entry : staffLedgers) { int index = entry.getKey(); // Need a list copy to avoid concurrent modifications - List ledgersCopy = new ArrayList(entry.getValue()); + List lineLedgers = new ArrayList<>(entry.getValue()); - for (LedgerInter ledger : ledgersCopy) { + for (LedgerInter ledger : lineLedgers) { if (!ledgerHasHeadOrLedger(staff, index, ledger, allHeads)) { if (ledger.isVip()) { logger.info("VIP deleting orphan ledger {}", ledger); @@ -984,27 +971,25 @@ private Set checkSlurOnTuplet () { logger.debug("S#{} checkSlurOnTuplet", system.getId()); - Set deleted = new LinkedHashSet(); + Set deleted = new LinkedHashSet<>(); final int maxSlurWidth = scale.toPixels(constants.maxTupletSlurWidth); - final List slurs = sig.inters( - new Predicate() + final List slurs = sig.inters(new Predicate() { @Override public boolean check (Inter inter) { return !inter.isRemoved() && (inter instanceof SlurInter) - && (inter.getBounds().width <= maxSlurWidth); + && (inter.getBounds().width <= maxSlurWidth); } }); - final List tuplets = sig.inters( - new Predicate() + final List tuplets = sig.inters(new Predicate() { @Override public boolean check (Inter inter) { return !inter.isRemoved() && (inter instanceof TupletInter) - && (inter.isContextuallyGood()); + && (inter.isContextuallyGood()); } }); @@ -1109,7 +1094,7 @@ private int checkStemsLengths () { logger.debug("S#{} checkStemsLengths", system.getId()); - final int minStemExtension = scale.toPixels(constants.minStemExtension); + final int minStemExtension = scale.toPixels(StemsBuilder.getMinStemExtension()); final List stems = sig.inters(Shape.STEM); int modifs = 0; @@ -1231,11 +1216,7 @@ private void checkTimeSignatures () if (notePrev != -1) { // Position WRT Bars in staff List bars = staff.getBarlines(); - int barPrev = -2 - - Collections.binarySearch( - bars, - timeSig, - Inters.byAbscissa); + int barPrev = -2 - Collections.binarySearch(bars, timeSig, Inters.byAbscissa); int xMin = (barPrev != -1) ? bars.get(barPrev).getCenter().x : 0; for (int i = notePrev; i >= 0; i--) { @@ -1256,49 +1237,6 @@ private void checkTimeSignatures () } } - //------------// - // compatible // - //------------// - /** - * Check whether the two provided Inter instance can overlap. - * - * @param inters array of exactly 2 instances - * @return true if overlap is accepted, false otherwise - */ - private static boolean compatible (Inter[] inters) - { - for (int i = 0; i <= 1; i++) { - final Inter inter = inters[i]; - final Inter other = inters[1 - i]; - final Shape otherShape = other.getShape(); - - if (inter instanceof AbstractBeamInter) { - if (other instanceof AbstractBeamInter) { - return true; - } - - if (beamCompShapes.contains(otherShape)) { - return true; - } - } else if (inter instanceof SlurInter) { - // Test StaffBarlineInter class, for which shape is always null - if (other instanceof StaffBarlineInter) { - return true; - } - - if (slurCompShapes.contains(otherShape)) { - return true; - } - } else if (inter instanceof StemInter) { - if (stemCompShapes.contains(otherShape)) { - return true; - } - } - } - - return false; - } - //-----------------------// // contextualizeAndPurge // //-----------------------// @@ -1345,25 +1283,26 @@ private void detectOverlaps (List inters, } final Rectangle leftBox = left.getBounds(); - Set mirrors = null; - - final Inter leftMirror = left.getMirror(); - - if (leftMirror != null) { - mirrors = new LinkedHashSet(); - mirrors.add(leftMirror); + final Set mirrors = new LinkedHashSet<>(); - final AbstractChordInter leftChord = (AbstractChordInter) left.getEnsemble(); + if (left instanceof HeadInter) { + HeadInter headMirror = (HeadInter) left.getMirror(); - if (leftChord != null) { - Inter leftChordMirror = leftChord.getMirror(); + if (headMirror != null) { + mirrors.add(headMirror); + HeadChordInter chordMirror = headMirror.getChord(); - if (leftChordMirror != null) { - mirrors.add(leftChordMirror); - mirrors.addAll(((AbstractChordInter) leftChordMirror).getNotes()); + if (chordMirror != null) { + mirrors.add(chordMirror); + mirrors.addAll(chordMirror.getNotes()); } - } else if (leftMirror instanceof AbstractChordInter) { - mirrors.addAll(((AbstractChordInter) leftMirror).getNotes()); + } + } else if (left instanceof AbstractChordInter) { + HeadChordInter chordMirror = (HeadChordInter) left.getMirror(); + + if (chordMirror != null) { + mirrors.add(chordMirror); + mirrors.addAll(chordMirror.getNotes()); } } @@ -1375,7 +1314,7 @@ private void detectOverlaps (List inters, } // Mirror entities do not exclude one another - if ((mirrors != null) && mirrors.contains(right)) { + if (mirrors.contains(right)) { continue; } @@ -1396,12 +1335,16 @@ private void detectOverlaps (List inters, if (left.overlaps(right) && right.overlaps(left)) { // Specific case: Word vs "string" Symbol if (left instanceof WordInter && right instanceof StringSymbolInter) { - if (wordMatchesSymbol((WordInter) left, (StringSymbolInter) right)) { + if (wordMatchesSymbol( + (WordInter) left, + (StringSymbolInter) right)) { left.decrease(0.5); } } else if (left instanceof StringSymbolInter - && right instanceof WordInter) { - if (wordMatchesSymbol((WordInter) right, (StringSymbolInter) left)) { + && right instanceof WordInter) { + if (wordMatchesSymbol( + (WordInter) right, + (StringSymbolInter) left)) { right.decrease(0.5); } } @@ -1419,10 +1362,10 @@ private void detectOverlaps (List inters, } } } - //---------// // exclude // //---------// + /** * Insert exclusion between (the members of) the 2 sets. * @@ -1446,9 +1389,9 @@ private void exclude (Inter left, Inter right) { // Special overlap case between a stem and a standard-size note head - if ((left instanceof StemInter && right instanceof HeadInter - && !right.getShape().isSmall()) - || (right instanceof StemInter && left instanceof HeadInter && !left.getShape().isSmall())) { + if ((left instanceof StemInter && right instanceof HeadInter && !right.getShape().isSmall()) + || (right instanceof StemInter && left instanceof HeadInter + && !left.getShape().isSmall())) { return; } @@ -1470,7 +1413,7 @@ private void exclude (Inter left, */ private List getHeadersInters () { - List inters = new ArrayList(); + List inters = new ArrayList<>(); for (Staff staff : system.getStaves()) { StaffHeader header = staff.getHeader(); @@ -1511,6 +1454,7 @@ private boolean headHasStem (Inter head) { if (head.isVip()) { logger.info("VIP checkHeadHasStem for {}", head); + } // Check if the head has a stem relation @@ -1612,8 +1556,9 @@ private boolean pruneStemHeads (StemInter stem) StemPortion portion = link.getStemPortion(head, stemLine, scale); HorizontalSide headSide = link.getHeadSide(); - if (((portion == STEM_BOTTOM) && (headSide != RIGHT)) - || ((portion == STEM_TOP) && (headSide != LEFT))) { + if (((portion == STEM_BOTTOM) && (headSide != RIGHT)) || ((portion == STEM_TOP) + && (headSide + != LEFT))) { sig.removeEdge(rel); links.remove(rel); modifs++; @@ -1649,7 +1594,7 @@ private boolean pruneStemHeads (StemInter stem) */ private Set reduce (Adapter adapter) { - final Set allRemoved = new LinkedHashSet(); + final Set allRemoved = new LinkedHashSet<>(); logger.debug("S#{} reducing sig ...", system.getId()); @@ -1666,8 +1611,8 @@ private Set reduce (Adapter adapter) adapter.prolog(); - Set reduced = new LinkedHashSet(); // Reduced inters - Set deleted = new LinkedHashSet(); // Deleted inters + Set reduced = new LinkedHashSet<>(); // Reduced inters + Set deleted = new LinkedHashSet<>(); // Deleted inters int epoch = 0; @@ -1759,7 +1704,7 @@ private int reduceHeadAugmentations (HeadInter head, } // Sort related dots by ordinate - final List dots = new ArrayList(); + final List dots = new ArrayList<>(); for (Relation rel : rels) { dots.add(sig.getEdgeSource(rel)); @@ -1777,7 +1722,7 @@ private int reduceHeadAugmentations (HeadInter head, boolean taken = false; for (AbstractNoteInter note : notes) { - if (note instanceof HeadInter && ((note.getIntegerPitch() % 2) == 1)) { + if (note instanceof HeadInter && ((note.getIntegerPitch() % 2) != 0)) { taken = true; break; @@ -1791,13 +1736,12 @@ private int reduceHeadAugmentations (HeadInter head, if (selected == null) { // Try dot below - final AugmentationDotInter dotBelow = (AugmentationDotInter) dots.get( - dots.size() - 1); + final AugmentationDotInter dotBelow = (AugmentationDotInter) dots.get(dots.size() - 1); final List notes = dotBelow.getAugmentedNotes(); boolean taken = false; for (AbstractNoteInter note : notes) { - if (note instanceof HeadInter && ((note.getIntegerPitch() % 2) == 1)) { + if (note instanceof HeadInter && ((note.getIntegerPitch() % 2) != 0)) { taken = true; break; @@ -1872,8 +1816,8 @@ private boolean stemHasSingleHeadEnd (StemInter stem) } final StemPortion forbidden = (stemDir > 0) ? STEM_BOTTOM : STEM_TOP; - final List toRemove = new ArrayList(); - final List toExclude = new ArrayList(); + final List toRemove = new ArrayList<>(); + final List toExclude = new ArrayList<>(); for (Relation rel : sig.getRelations(stem, HeadStemRelation.class)) { HeadStemRelation hsRel = (HeadStemRelation) rel; @@ -1910,6 +1854,49 @@ private boolean stemHasSingleHeadEnd (StemInter stem) return toRemove.isEmpty(); } + //------------// + // compatible // + //------------// + /** + * Check whether the two provided Inter instance can overlap. + * + * @param inters array of exactly 2 instances + * @return true if overlap is accepted, false otherwise + */ + private static boolean compatible (Inter[] inters) + { + for (int i = 0; i <= 1; i++) { + final Inter inter = inters[i]; + final Inter other = inters[1 - i]; + final Shape otherShape = other.getShape(); + + if (inter instanceof AbstractBeamInter) { + if (other instanceof AbstractBeamInter) { + return true; + } + + if (beamCompShapes.contains(otherShape)) { + return true; + } + } else if (inter instanceof SlurInter) { + // Test StaffBarlineInter class, for which shape is always null + if (other instanceof StaffBarlineInter) { + return true; + } + + if (slurCompShapes.contains(otherShape)) { + return true; + } + } else if (inter instanceof StemInter) { + if (stemCompShapes.contains(otherShape)) { + return true; + } + } + } + + return false; + } + //-------------------// // wordMatchesSymbol // //-------------------// @@ -1934,45 +1921,7 @@ private static boolean wordMatchesSymbol (WordInter wordInter, } return false; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //---------// - // Adapter // - //---------// - private abstract static class Adapter - { - //~ Instance fields ------------------------------------------------------------------------ - - Set deleted = new LinkedHashSet(); - - Set reduced = new LinkedHashSet(); - //~ Methods -------------------------------------------------------------------------------- - public int checkConsistencies () - { - return 0; // Void by default - } - - public void checkFrozens () - { - // Void by default - } - - public int checkLateConsistencies () - { - return 0; // Void by default - } - - public Set checkSlurs () - { - return Collections.emptySet(); - } - - public void prolog () - { - // Void by default - } } //-----------------------// @@ -1981,7 +1930,6 @@ public void prolog () private class AdapterForFoundations extends Adapter { - //~ Methods -------------------------------------------------------------------------------- @Override public int checkConsistencies () @@ -2054,7 +2002,6 @@ private void descendStems () private class AdapterForLinks extends Adapter { - //~ Methods -------------------------------------------------------------------------------- @Override public int checkConsistencies () @@ -2106,20 +2053,58 @@ public void prolog () } } + /** Standard vs Small size. */ + private static enum Size + { + STANDARD, + SMALL; + } + + //---------// + // Adapter // + //---------// + private static abstract class Adapter + { + + Set deleted = new LinkedHashSet<>(); + + Set reduced = new LinkedHashSet<>(); + + public int checkConsistencies () + { + return 0; // Void by default + } + + public void checkFrozens () + { + // Void by default + } + + public int checkLateConsistencies () + { + return 0; // Void by default + } + + public Set checkSlurs () + { + return Collections.emptySet(); + } + + public void prolog () + { + // Void by default + } + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxTupletSlurWidth = new Scale.Fraction( 3, "Maximum width for slur around tuplet"); - - private final Scale.Fraction minStemExtension = new Scale.Fraction( - 1.5, - "Minimum vertical extension of a stem beyond last head"); } } diff --git a/src/main/org/audiveris/omr/sig/SigValue.java b/src/main/org/audiveris/omr/sig/SigValue.java index 3a4f53546..220b14401 100644 --- a/src/main/org/audiveris/omr/sig/SigValue.java +++ b/src/main/org/audiveris/omr/sig/SigValue.java @@ -23,7 +23,6 @@ import org.audiveris.omr.sheet.Sheet; import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sig.inter.StaffBarlineInter; import org.audiveris.omr.sheet.StaffManager; import org.audiveris.omr.sig.inter.AbstractInter; import org.audiveris.omr.sig.inter.AlterInter; @@ -70,6 +69,7 @@ import org.audiveris.omr.sig.inter.SmallBeamInter; import org.audiveris.omr.sig.inter.SmallChordInter; import org.audiveris.omr.sig.inter.SmallFlagInter; +import org.audiveris.omr.sig.inter.StaffBarlineInter; import org.audiveris.omr.sig.inter.StemInter; import org.audiveris.omr.sig.inter.TimeNumberInter; import org.audiveris.omr.sig.inter.TimePairInter; @@ -77,12 +77,10 @@ import org.audiveris.omr.sig.inter.TupletInter; import org.audiveris.omr.sig.inter.WedgeInter; import org.audiveris.omr.sig.inter.WordInter; -import org.audiveris.omr.sig.relation.AbstractRelation; import org.audiveris.omr.sig.relation.AlterHeadRelation; import org.audiveris.omr.sig.relation.AugmentationRelation; import org.audiveris.omr.sig.relation.BarConnectionRelation; import org.audiveris.omr.sig.relation.BarGroupRelation; -import org.audiveris.omr.sig.relation.BasicContainment; import org.audiveris.omr.sig.relation.BeamHeadRelation; import org.audiveris.omr.sig.relation.BeamStemRelation; import org.audiveris.omr.sig.relation.ChordArpeggiatoRelation; @@ -97,6 +95,7 @@ import org.audiveris.omr.sig.relation.ChordTupletRelation; import org.audiveris.omr.sig.relation.ChordWedgeRelation; import org.audiveris.omr.sig.relation.ClefKeyRelation; +import org.audiveris.omr.sig.relation.Containment; import org.audiveris.omr.sig.relation.DotFermataRelation; import org.audiveris.omr.sig.relation.DoubleDotRelation; import org.audiveris.omr.sig.relation.EndingBarRelation; @@ -109,6 +108,7 @@ import org.audiveris.omr.sig.relation.HeadStemRelation; import org.audiveris.omr.sig.relation.KeyAltersRelation; import org.audiveris.omr.sig.relation.MarkerBarRelation; +import org.audiveris.omr.sig.relation.MirrorRelation; import org.audiveris.omr.sig.relation.NoExclusion; import org.audiveris.omr.sig.relation.Relation; import org.audiveris.omr.sig.relation.RepeatDotBarRelation; @@ -121,7 +121,6 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.LinkedHashSet; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -145,78 +144,73 @@ @XmlAccessorType(XmlAccessType.NONE) public class SigValue { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - SigValue.class); + private static final Logger logger = LoggerFactory.getLogger(SigValue.class); - //~ Instance fields ---------------------------------------------------------------------------- /** * All CONCRETE inters found in sig, gathered here as true defs. No abstract! * For easier review, class names are listed alphabetically. */ @XmlElementWrapper(name = "inters") @XmlElementRefs({ - @XmlElementRef(type = AlterInter.class) - , @XmlElementRef(type = AugmentationDotInter.class) - , @XmlElementRef(type = ArpeggiatoInter.class) - , @XmlElementRef(type = ArticulationInter.class) - , @XmlElementRef(type = BarConnectorInter.class) - , @XmlElementRef(type = BarlineInter.class) - , @XmlElementRef(type = BeamHookInter.class) - , @XmlElementRef(type = BeamInter.class) - , @XmlElementRef(type = BraceInter.class) - , @XmlElementRef(type = BracketConnectorInter.class) - , @XmlElementRef(type = BracketInter.class) - , @XmlElementRef(type = BreathMarkInter.class) - , @XmlElementRef(type = CaesuraInter.class) - , @XmlElementRef(type = ChordNameInter.class) - , @XmlElementRef(type = ClefInter.class) - , @XmlElementRef(type = DynamicsInter.class) - , @XmlElementRef(type = EndingInter.class) - , @XmlElementRef(type = FermataDotInter.class) - , @XmlElementRef(type = FermataArcInter.class) - , @XmlElementRef(type = FermataInter.class) - , @XmlElementRef(type = FingeringInter.class) - , @XmlElementRef(type = FlagInter.class) - , @XmlElementRef(type = FretInter.class) - , @XmlElementRef(type = HeadChordInter.class) - , @XmlElementRef(type = HeadInter.class) - , @XmlElementRef(type = KeyAlterInter.class) - , @XmlElementRef(type = KeyInter.class) - , @XmlElementRef(type = LedgerInter.class) - , @XmlElementRef(type = LyricItemInter.class) - , @XmlElementRef(type = LyricLineInter.class) - , @XmlElementRef(type = MarkerInter.class) - , @XmlElementRef(type = OrnamentInter.class) - , @XmlElementRef(type = PedalInter.class) - , @XmlElementRef(type = PluckingInter.class) - , @XmlElementRef(type = RepeatDotInter.class) - , @XmlElementRef(type = RestChordInter.class) - , @XmlElementRef(type = RestInter.class) - , @XmlElementRef(type = SegmentInter.class) - , @XmlElementRef(type = SentenceInter.class) - , @XmlElementRef(type = SlurInter.class) - , @XmlElementRef(type = SmallBeamInter.class) - , @XmlElementRef(type = SmallChordInter.class) - , @XmlElementRef(type = SmallFlagInter.class) - , @XmlElementRef(type = StaffBarlineInter.class) - , @XmlElementRef(type = StemInter.class) - , @XmlElementRef(type = TimeNumberInter.class) - , @XmlElementRef(type = TimePairInter.class) - , @XmlElementRef(type = TimeWholeInter.class) - , @XmlElementRef(type = TupletInter.class) - , @XmlElementRef(type = WedgeInter.class) - , @XmlElementRef(type = WordInter.class) - }) - private final ArrayList inters = new ArrayList(); + @XmlElementRef(type = AlterInter.class), + @XmlElementRef(type = AugmentationDotInter.class), + @XmlElementRef(type = ArpeggiatoInter.class), + @XmlElementRef(type = ArticulationInter.class), + @XmlElementRef(type = BarConnectorInter.class), + @XmlElementRef(type = BarlineInter.class), + @XmlElementRef(type = BeamHookInter.class), + @XmlElementRef(type = BeamInter.class), + @XmlElementRef(type = BraceInter.class), + @XmlElementRef(type = BracketConnectorInter.class), + @XmlElementRef(type = BracketInter.class), + @XmlElementRef(type = BreathMarkInter.class), + @XmlElementRef(type = CaesuraInter.class), + @XmlElementRef(type = ChordNameInter.class), + @XmlElementRef(type = ClefInter.class), + @XmlElementRef(type = DynamicsInter.class), + @XmlElementRef(type = EndingInter.class), + @XmlElementRef(type = FermataDotInter.class), + @XmlElementRef(type = FermataArcInter.class), + @XmlElementRef(type = FermataInter.class), + @XmlElementRef(type = FingeringInter.class), + @XmlElementRef(type = FlagInter.class), + @XmlElementRef(type = FretInter.class), + @XmlElementRef(type = HeadChordInter.class), + @XmlElementRef(type = HeadInter.class), + @XmlElementRef(type = KeyAlterInter.class), + @XmlElementRef(type = KeyInter.class), + @XmlElementRef(type = LedgerInter.class), + @XmlElementRef(type = LyricItemInter.class), + @XmlElementRef(type = LyricLineInter.class), + @XmlElementRef(type = MarkerInter.class), + @XmlElementRef(type = OrnamentInter.class), + @XmlElementRef(type = PedalInter.class), + @XmlElementRef(type = PluckingInter.class), + @XmlElementRef(type = RepeatDotInter.class), + @XmlElementRef(type = RestChordInter.class), + @XmlElementRef(type = RestInter.class), + @XmlElementRef(type = SegmentInter.class), + @XmlElementRef(type = SentenceInter.class), + @XmlElementRef(type = SlurInter.class), + @XmlElementRef(type = SmallBeamInter.class), + @XmlElementRef(type = SmallChordInter.class), + @XmlElementRef(type = SmallFlagInter.class), + @XmlElementRef(type = StaffBarlineInter.class), + @XmlElementRef(type = StemInter.class), + @XmlElementRef(type = TimeNumberInter.class), + @XmlElementRef(type = TimePairInter.class), + @XmlElementRef(type = TimeWholeInter.class), + @XmlElementRef(type = TupletInter.class), + @XmlElementRef(type = WedgeInter.class), + @XmlElementRef(type = WordInter.class)}) + private final ArrayList inters = new ArrayList<>(); /** Sig edges: relations between inters. */ @XmlElementWrapper(name = "relations") @XmlElement(name = "relation") - private final ArrayList relations = new ArrayList(); + private final ArrayList relations = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * No-arg constructor meant for JAXB. */ @@ -224,7 +218,6 @@ public SigValue () { } - //~ Methods ------------------------------------------------------------------------------------ /** * Method to be called only when SigValue IDREFs have been fully unmarshalled, * to populate the target SIG. @@ -262,7 +255,24 @@ public void populateSig (SIGraph sig) } } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("SigValue{"); + + if (inters != null) { + sb.append("inters:").append(inters.size()); + } + + if (relations != null) { + sb.append(" relations:").append(relations.size()); + } + + sb.append('}'); + + return sb.toString(); + } + //---------// // Adapter // //---------// @@ -272,7 +282,6 @@ public void populateSig (SIGraph sig) public static class Adapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- /** * Generate a SigValue out of the existing SIG. @@ -315,31 +324,6 @@ public SIGraph unmarshal (SigValue sigValue) } } - //----------// - // InterSet // - //----------// - /** - * Class {@code InterSet} allows to separate Inter instances defined in system - * structure from Inter instances only found in the system SIG. - */ - public static class InterSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final LinkedHashSet defined = new LinkedHashSet(); - - //~ Methods -------------------------------------------------------------------------------- - public void addInter (AbstractInter inter) - { - defined.add(inter); - } - - public LinkedHashSet getInters () - { - return defined; - } - } - //---------------// // RelationValue // //---------------// @@ -348,7 +332,6 @@ public LinkedHashSet getInters () */ private static class RelationValue { - //~ Instance fields ------------------------------------------------------------------------ /** Relation source vertex ID. */ @XmlAttribute(name = "source") @@ -364,47 +347,46 @@ private static class RelationValue * Here we list alphabetically all CONCRETE relation types. No abstract! */ @XmlElementRefs({ - @XmlElementRef(type = AlterHeadRelation.class) - , @XmlElementRef(type = AugmentationRelation.class) - , @XmlElementRef(type = BarConnectionRelation.class) - , @XmlElementRef(type = BarGroupRelation.class) - , @XmlElementRef(type = BasicContainment.class) - , @XmlElementRef(type = Exclusion.class) - , @XmlElementRef(type = BeamHeadRelation.class) - , @XmlElementRef(type = BeamStemRelation.class) - , @XmlElementRef(type = ChordArpeggiatoRelation.class) - , @XmlElementRef(type = ChordArticulationRelation.class) - , @XmlElementRef(type = ChordDynamicsRelation.class) - , @XmlElementRef(type = ChordNameRelation.class) - , @XmlElementRef(type = ChordOrnamentRelation.class) - , @XmlElementRef(type = ChordPedalRelation.class) - , @XmlElementRef(type = ChordSentenceRelation.class) - , @XmlElementRef(type = ChordStemRelation.class) - , @XmlElementRef(type = ChordSyllableRelation.class) - , @XmlElementRef(type = ChordTupletRelation.class) - , @XmlElementRef(type = ChordWedgeRelation.class) - , @XmlElementRef(type = ClefKeyRelation.class) - , @XmlElementRef(type = DotFermataRelation.class) - , @XmlElementRef(type = DoubleDotRelation.class) - , @XmlElementRef(type = EndingBarRelation.class) - , @XmlElementRef(type = EndingSentenceRelation.class) - , @XmlElementRef(type = FermataBarRelation.class) - , @XmlElementRef(type = FermataChordRelation.class) - , @XmlElementRef(type = FlagStemRelation.class) - , @XmlElementRef(type = HeadHeadRelation.class) - , @XmlElementRef(type = HeadStemRelation.class) - , @XmlElementRef(type = KeyAltersRelation.class) - , @XmlElementRef(type = MarkerBarRelation.class) - , @XmlElementRef(type = NoExclusion.class) - , @XmlElementRef(type = RepeatDotBarRelation.class) - , @XmlElementRef(type = RepeatDotPairRelation.class) - , @XmlElementRef(type = SlurHeadRelation.class) - , @XmlElementRef(type = StemAlignmentRelation.class) - , @XmlElementRef(type = TimeTopBottomRelation.class) - }) - public AbstractRelation relation; - - //~ Constructors --------------------------------------------------------------------------- + @XmlElementRef(type = AlterHeadRelation.class), + @XmlElementRef(type = AugmentationRelation.class), + @XmlElementRef(type = BarConnectionRelation.class), + @XmlElementRef(type = BarGroupRelation.class), + @XmlElementRef(type = Containment.class), + @XmlElementRef(type = Exclusion.class), + @XmlElementRef(type = BeamHeadRelation.class), + @XmlElementRef(type = BeamStemRelation.class), + @XmlElementRef(type = ChordArpeggiatoRelation.class), + @XmlElementRef(type = ChordArticulationRelation.class), + @XmlElementRef(type = ChordDynamicsRelation.class), + @XmlElementRef(type = ChordNameRelation.class), + @XmlElementRef(type = ChordOrnamentRelation.class), + @XmlElementRef(type = ChordPedalRelation.class), + @XmlElementRef(type = ChordSentenceRelation.class), + @XmlElementRef(type = ChordStemRelation.class), + @XmlElementRef(type = ChordSyllableRelation.class), + @XmlElementRef(type = ChordTupletRelation.class), + @XmlElementRef(type = ChordWedgeRelation.class), + @XmlElementRef(type = ClefKeyRelation.class), + @XmlElementRef(type = DotFermataRelation.class), + @XmlElementRef(type = DoubleDotRelation.class), + @XmlElementRef(type = EndingBarRelation.class), + @XmlElementRef(type = EndingSentenceRelation.class), + @XmlElementRef(type = FermataBarRelation.class), + @XmlElementRef(type = FermataChordRelation.class), + @XmlElementRef(type = FlagStemRelation.class), + @XmlElementRef(type = HeadHeadRelation.class), + @XmlElementRef(type = HeadStemRelation.class), + @XmlElementRef(type = KeyAltersRelation.class), + @XmlElementRef(type = MarkerBarRelation.class), + @XmlElementRef(type = MirrorRelation.class), + @XmlElementRef(type = NoExclusion.class), + @XmlElementRef(type = RepeatDotBarRelation.class), + @XmlElementRef(type = RepeatDotPairRelation.class), + @XmlElementRef(type = SlurHeadRelation.class), + @XmlElementRef(type = StemAlignmentRelation.class), + @XmlElementRef(type = TimeTopBottomRelation.class)}) + public Relation relation; + /** * Creates a new {@code RelationValue} object. * @@ -412,13 +394,13 @@ private static class RelationValue * @param target target inter * @param relation relation from source to target */ - public RelationValue (Inter source, - Inter target, - Relation relation) + RelationValue (Inter source, + Inter target, + Relation relation) { this.sourceId = source.getId(); this.targetId = target.getId(); - this.relation = (AbstractRelation) relation; + this.relation = relation; } /** @@ -428,7 +410,6 @@ private RelationValue () { } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractBeamInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractBeamInter.java index 3b00b9f4a..5f8e521ca 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractBeamInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractBeamInter.java @@ -32,7 +32,6 @@ import org.audiveris.omr.sheet.SystemInfo; import org.audiveris.omr.sheet.beam.BeamGroup; import org.audiveris.omr.sheet.rhythm.Voice; -import org.audiveris.omr.sig.BasicImpacts; import org.audiveris.omr.sig.GradeImpacts; import org.audiveris.omr.sig.relation.BeamPortion; import org.audiveris.omr.sig.relation.BeamStemRelation; @@ -70,7 +69,8 @@ * The following image shows two beams - a (full) beam and a beam hook: *

                                                                                                                  * Beam image + * src= + * "http://upload.wikimedia.org/wikipedia/commons/thumb/8/8e/Beamed_notes.svg/220px-Beamed_notes.svg.png"> * * @author Hervé Bitteur */ @@ -78,13 +78,9 @@ public abstract class AbstractBeamInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - AbstractBeamInter.class); + private static final Logger logger = LoggerFactory.getLogger(AbstractBeamInter.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -95,6 +91,7 @@ public abstract class AbstractBeamInter /** Median line. */ @XmlElement + @XmlJavaTypeAdapter(Jaxb.Line2DAdapter.class) protected Line2D median; // Transient data @@ -103,7 +100,6 @@ public abstract class AbstractBeamInter /** The containing beam group. */ private BeamGroup group; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AbstractBeamInter object. * Note there is no underlying glyph, cleaning will be based on beam area. @@ -138,13 +134,8 @@ protected AbstractBeamInter (Shape shape, double grade) { super(null, null, shape, grade); - - if (median != null) { - computeArea(); - } } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -290,7 +281,7 @@ public Line2D getBorder (VerticalSide side) */ public List getChords () { - List chords = new ArrayList(); + List chords = new ArrayList<>(); for (StemInter stem : getStems()) { for (AbstractChordInter chord : stem.getChords()) { @@ -333,6 +324,19 @@ public BeamGroup getGroup () return group; } + //----------// + // setGroup // + //----------// + /** + * Assign the containing BeamGroup. + * + * @param group containing group + */ + public void setGroup (BeamGroup group) + { + this.group = group; + } + //-----------// // getHeight // //-----------// @@ -398,7 +402,7 @@ public StemInter getStemOn (BeamPortion portion) */ public Set getStems () { - Set stems = new LinkedHashSet(); + Set stems = new LinkedHashSet<>(); for (Relation bs : sig.getRelations(this, BeamStemRelation.class)) { StemInter stem = (StemInter) sig.getOppositeInter(this, bs); @@ -433,6 +437,11 @@ public boolean isGood () //--------// // isHook // //--------// + /** + * Report whether this beam is a beam hook. + * + * @return true if so + */ public boolean isHook () { return false; @@ -494,14 +503,6 @@ public void setGlyph (Glyph glyph) } } - //----------// - // setGroup // - //----------// - public void setGroup (BeamGroup group) - { - this.group = group; - } - //---------------// // switchToGroup // //---------------// @@ -537,6 +538,9 @@ public void switchToGroup (BeamGroup group) //-------------// // computeArea // //-------------// + /** + * + */ protected void computeArea () { setArea(AreaUtil.horizontalParallelogram(median.getP1(), median.getP2(), height)); @@ -571,7 +575,6 @@ private Area getLookupArea (Scale scale) final Line2D top = getBorder(VerticalSide.TOP); final Line2D bottom = getBorder(VerticalSide.BOTTOM); final int xOut = scale.toPixels(BeamStemRelation.getXOutGapMaximum(manual)); - final int xIn = scale.toPixels(BeamStemRelation.getXInGapMaximum(manual)); final int yGap = scale.toPixels(BeamStemRelation.getYGapMaximum(manual)); final Path2D lu = new Path2D.Double(); @@ -617,7 +620,7 @@ private Collection lookupLinks (List systemStems, logger.info("VIP lookupLinks for {}", this); } - final List links = new ArrayList(); + final List links = new ArrayList<>(); final Scale scale = system.getSheet().getScale(); final Area luArea = getLookupArea(scale); List stems = Inters.intersectedInters(systemStems, GeoOrder.NONE, luArea); @@ -636,24 +639,25 @@ private Collection lookupLinks (List systemStems, return links; } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Impacts // //---------// public static class Impacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{ - "wdth", "minH", "maxH", "core", "belt", "jit" - }; + "wdth", + "minH", + "maxH", + "core", + "belt", + "jit"}; private static final int DIST_INDEX = 5; private static final double[] WEIGHTS = new double[]{0.5, 1, 1, 2, 2, 2}; - //~ Constructors --------------------------------------------------------------------------- public Impacts (double width, double minHeight, double maxHeight, @@ -670,7 +674,6 @@ public Impacts (double width, setImpact(5, dist); } - //~ Methods -------------------------------------------------------------------------------- public double getDistImpact () { return getImpact(DIST_INDEX); diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractChordInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractChordInter.java index 3df914811..2f5021050 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractChordInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractChordInter.java @@ -71,15 +71,11 @@ public abstract class AbstractChordInter extends AbstractInter implements InterEnsemble { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - AbstractChordInter.class); + private static final Logger logger = LoggerFactory.getLogger(AbstractChordInter.class); private static final List NO_BEAM = Collections.emptyList(); - //~ Instance fields ---------------------------------------------------------------------------- - // // Transient data //--------------- // @@ -110,7 +106,6 @@ public abstract class AbstractChordInter /** Voice this chord belongs to. */ protected Voice voice; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code AbstractChordInter} object. * @@ -128,36 +123,6 @@ protected AbstractChordInter () { } - //~ Methods ------------------------------------------------------------------------------------ - //-----------------// - // getClosestChord // - //-----------------// - /** - * From a provided Chord collection, report the chord which has the - * closest abscissa to a provided point. - * - * @param chords the collection of chords to browse - * @param point the reference point - * @return the abscissa-wise closest chord - */ - public static AbstractChordInter getClosestChord (Collection chords, - Point point) - { - AbstractChordInter bestChord = null; - int bestDx = Integer.MAX_VALUE; - - for (AbstractChordInter chord : chords) { - int dx = Math.abs(chord.getHeadLocation().x - point.x); - - if (dx < bestDx) { - bestDx = dx; - bestChord = chord; - } - } - - return bestChord; - } - //--------// // accept // //--------// @@ -210,6 +175,11 @@ public void added () //-------------// // afterReload // //-------------// + /** + * To be called right after unmarshalling. + * + * @param measure containing measure + */ public void afterReload (Measure measure) { try { @@ -323,12 +293,12 @@ public BeamGroup getBeamGroup () * Report the sequence of beams that are attached to this chord, * ordered from the tail to the head of the chord. * - * @return the attached beams + * @return the list of attached beams, perhaps empty */ public List getBeams () { if (beams == null) { - beams = new ArrayList(); + beams = new ArrayList<>(); final StemInter stem = getStem(); @@ -339,28 +309,39 @@ public List getBeams () } // Keep the sequence sorted from chord tail - Collections.sort( - beams, - new Comparator() - { - @Override - public int compare (AbstractBeamInter b1, - AbstractBeamInter b2) - { - int x = getCenter().x; - double y1 = LineUtil.yAtX(b1.getMedian(), x); - double y2 = LineUtil.yAtX(b2.getMedian(), x); - int yHead = getHeadLocation().y; - - return Double.compare(Math.abs(yHead - y2), Math.abs(yHead - y1)); - } - }); + Collections.sort(beams, new Comparator() + { + @Override + public int compare (AbstractBeamInter b1, + AbstractBeamInter b2) + { + int x = getCenter().x; + double y1 = LineUtil.yAtX(b1.getMedian(), x); + double y2 = LineUtil.yAtX(b2.getMedian(), x); + int yHead = getHeadLocation().y; + + return Double.compare(Math.abs(yHead - y2), Math.abs(yHead - y1)); + } + }); } } return beams; } + //-----------------------// + // getBeamsOrFlagsNumber // + //-----------------------// + /** + * Report the number of beams/flags that apply to this chord. + * + * @return beams count + flags count + */ + public int getBeamsOrFlagsNumber () + { + return getBeams().size() + getFlagsNumber(); + } + //----------------// // getBottomStaff // //----------------// @@ -505,8 +486,7 @@ public Rational getDurationSansDotOrTuplet () if (!noteShape.isWholeRest()) { // Apply flags/beams for non-rests if (!noteShape.isRest()) { - final int beamNb = (getBeams() != null) ? getBeams().size() : 0; - final int fbn = getFlagsNumber() + beamNb; + final int fbn = getBeamsOrFlagsNumber(); if (fbn > 0) { /* @@ -618,7 +598,7 @@ public int getFlagsNumber () */ public List getFollowingTiedChords () { - final List tied = new ArrayList(); + final List tied = new ArrayList<>(); for (Inter inter : getMembers()) { AbstractNoteInter note = (AbstractNoteInter) inter; @@ -683,11 +663,29 @@ public AbstractNoteInter getLeadingNote () //------------// // getMeasure // //------------// + /** + * Report the containing measure, if any. + * + * @return containing measure or null + */ public Measure getMeasure () { return measure; } + //------------// + // setMeasure // + //------------// + /** + * Set the containing measure. + * + * @param measure the measure that contains this chord + */ + public void setMeasure (Measure measure) + { + this.measure = measure; + } + //------------// // getMembers // //------------// @@ -745,6 +743,19 @@ public Slot getSlot () return slot; } + //---------// + // setSlot // + //---------// + /** + * Return the containing time slot, if any + * + * @param slot containing slot or null + */ + public void setSlot (Slot slot) + { + this.slot = slot; + } + //----------// // getStaff // //----------// @@ -909,6 +920,73 @@ public Voice getVoice () return voice; } + //----------// + // setVoice // + //----------// + /** + * Assign a voice to this chord, and to the related ones. + * + * @param voice the voice to assign + */ + public void setVoice (Voice voice) + { + // Already done? + if (this.voice == null) { + this.voice = voice; + + // Update the voice entity + if (!isWholeRest()) { + if (slot != null) { + voice.startChord(slot, this); + } + + // Extend this voice to other grouped chords if any + BeamGroup group = getBeamGroup(); + + if (group != null) { + logger.debug( + "{} extending voice#{} to group#{}", + this, + voice.getId(), + group.getId()); + + group.setVoice(voice); + } + + // Extend to the following tied chords as well + List tied = getFollowingTiedChords(); + + for (AbstractChordInter chord : tied) { + logger.debug("{} tied to {}", this, chord); + + // Check the tied chords belong to the same measure + if (this.measure == chord.measure) { + logger.debug( + "{} extending voice#{} to tied chord#{}", + this, + voice.getId(), + chord.getId()); + + chord.setVoice(voice); + } else { + // Chords tied across measure boundary + logger.debug("{} Cross tie -> {}", this, chord); + } + } + } + } else if (this.voice != voice) { + logger.warn( + "{} Attempt to reassign voice from {} to {}", + this, + this.voice.getId(), + voice.getId()); + } else if (!isWholeRest()) { + if (slot != null) { + voice.startChord(slot, this); + } + } + } + //-----------------// // invalidateCache // //-----------------// @@ -1054,6 +1132,9 @@ public void removeMember (Inter member) //-------------// // resetTiming // //-------------// + /** + * Reset cached data related to timing. + */ public void resetTiming () { dotsNumber = 0; @@ -1062,25 +1143,9 @@ public void resetTiming () timeOffset = null; } - //------------// - // setMeasure // - //------------// - public void setMeasure (Measure measure) - { - this.measure = measure; - } - - //---------// - // setSlot // - //---------// - public void setSlot (Slot slot) - { - this.slot = slot; - } - - //----_----------// + //---------------// // setTimeOffset // - //------_--------// + //---------------// /** * Remember the time offset for this chord * @@ -1105,71 +1170,6 @@ public boolean setTimeOffset (Rational timeOffset) return true; } - //----------// - // setVoice // - //----------// - /** - * Assign a voice to this chord, and to the related ones. - * - * @param voice the voice to assign - */ - public void setVoice (Voice voice) - { - // Already done? - if (this.voice == null) { - this.voice = voice; - - // Update the voice entity - if (!isWholeRest()) { - if (slot != null) { - voice.startChord(slot, this); - } - - // Extend this voice to other grouped chords if any - BeamGroup group = getBeamGroup(); - - if (group != null) { - logger.debug( - "{} extending voice#{} to group#{}", - this, - voice.getId(), - group.getId()); - - group.setVoice(voice); - } - - // Extend to the following tied chords as well - List tied = getFollowingTiedChords(); - - for (AbstractChordInter chord : tied) { - logger.debug("{} tied to {}", this, chord); - - // Check the tied chords belong to the same measure - if (this.measure == chord.measure) { - logger.debug( - "{} extending voice#{} to tied chord#{}", - this, - voice.getId(), - chord.getId()); - - chord.setVoice(voice); - } else { - // Chords tied across measure boundary - logger.debug("{} Cross tie -> {}", this, chord); - } - } - } - } else if (this.voice != voice) { - logger.warn( - "{} Attempt to reassign voice from " + this.voice.getId() + " to " + voice.getId(), - this); - } else if (!isWholeRest()) { - if (slot != null) { - voice.startChord(slot, this); - } - } - } - //------------------// // computeLocations // //------------------// @@ -1211,4 +1211,33 @@ protected String internals () return sb.toString(); } + + //-----------------// + // getClosestChord // + //-----------------// + /** + * From a provided Chord collection, report the chord which has the + * closest abscissa to a provided point. + * + * @param chords the collection of chords to browse + * @param point the reference point + * @return the abscissa-wise closest chord + */ + public static AbstractChordInter getClosestChord (Collection chords, + Point point) + { + AbstractChordInter bestChord = null; + int bestDx = Integer.MAX_VALUE; + + for (AbstractChordInter chord : chords) { + int dx = Math.abs(chord.getHeadLocation().x - point.x); + + if (dx < bestDx) { + bestDx = dx; + bestChord = chord; + } + } + + return bestChord; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractDirectionInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractDirectionInter.java index 61c761a62..5087b2bb9 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractDirectionInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractDirectionInter.java @@ -36,7 +36,6 @@ public abstract class AbstractDirectionInter extends AbstractInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code AbstractDirectionInter} object. diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractFlagInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractFlagInter.java index b9d6315b9..cf5e77a01 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractFlagInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractFlagInter.java @@ -23,10 +23,8 @@ import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; - import static org.audiveris.omr.glyph.ShapeSet.FlagsUp; import static org.audiveris.omr.glyph.ShapeSet.SmallFlags; - import org.audiveris.omr.math.GeoOrder; import org.audiveris.omr.math.LineUtil; import org.audiveris.omr.sheet.Scale; @@ -55,17 +53,15 @@ public abstract class AbstractFlagInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(AbstractFlagInter.class); - //~ Instance fields ---------------------------------------------------------------------------- - /** Value of this flag (compound?) in terms of individual flags. + /** + * Value of this flag (compound?) in terms of individual flags. * (Lazily evaluated) */ protected Integer value; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code AbstractFlagInter} object. */ @@ -87,44 +83,6 @@ protected AbstractFlagInter (Glyph glyph, super(glyph, null, shape, grade); } - //~ Methods ------------------------------------------------------------------------------------ - //------------------// - // createValidAdded // - //------------------// - /** - * (Try to) create and add a Flag inter (either standard FlagInter or SmallFlagInter). - *

                                                                                                                  - * At this time note heads have already been validated (with their attached stem). - * So, a flag is created only if it can be related to stems with consistent size head(s), - * and if it is correctly located WRT note heads on the stem. - * - * @param glyph the flag glyph - * @param shape flag shape - * @param grade the interpretation quality - * @param system the related system - * @param systemStems ordered collection of stems in system - * @return the created instance or null - */ - public static AbstractFlagInter createValidAdded (Glyph glyph, - Shape shape, - double grade, - SystemInfo system, - List systemStems) - { - AbstractFlagInter flag = new FlagInter(glyph, shape, grade); - - Link link = flag.lookupLink(systemStems); - - if (link != null) { - system.getSig().addVertex(flag); - link.applyTo(flag); - - return flag; - } - - return null; - } - //--------// // accept // //--------// @@ -188,46 +146,6 @@ public Collection searchLinks (SystemInfo system, return Collections.singleton(link); } - //--------------// - // getFlagValue // - //--------------// - /** - * Report the number of individual flags that corresponds to the flag shape - * - * @param shape the given flag shape - * @return the number of individual flags - */ - private static int getFlagValue (Shape shape) - { - switch (shape) { - case FLAG_1: - case FLAG_1_UP: - case SMALL_FLAG: - case SMALL_FLAG_SLASH: - return 1; - - case FLAG_2: - case FLAG_2_UP: - return 2; - - case FLAG_3: - case FLAG_3_UP: - return 3; - - case FLAG_4: - case FLAG_4_UP: - return 4; - - case FLAG_5: - case FLAG_5_UP: - return 5; - } - - logger.error("Illegal flag shape: {}", shape); - - return 0; - } - //------------// // lookupLink // //------------// @@ -258,10 +176,9 @@ private Link lookupLink (List systemStems) final Point refPt = new Point( flagBox.x, isFlagUp ? ((flagBox.y + flagBox.height) - footHeight) : (flagBox.y + footHeight)); - final int y = isFlagUp ? ((flagBox.y + flagBox.height) - footHeight - - maxStemFlagGapY) : (flagBox.y + maxStemFlagGapY); - final int midFootY = isFlagUp ? (refPt.y + (footHeight / 2)) - : (refPt.y - (footHeight / 2)); + final int y = isFlagUp ? ((flagBox.y + flagBox.height) - footHeight - maxStemFlagGapY) + : (flagBox.y + maxStemFlagGapY); + final int midFootY = isFlagUp ? (refPt.y + (footHeight / 2)) : (refPt.y - (footHeight / 2)); //TODO: -1 is used to cope with stem margin when erased (To be improved) final Rectangle luBox = new Rectangle( @@ -336,4 +253,81 @@ private Link lookupLink (List systemStems) return null; } + + //------------------// + // createValidAdded // + //------------------// + /** + * (Try to) create and add a Flag inter (either standard FlagInter or SmallFlagInter). + *

                                                                                                                  + * At this time note heads have already been validated (with their attached stem). + * So, a flag is created only if it can be related to stems with consistent size head(s), + * and if it is correctly located WRT note heads on the stem. + * + * @param glyph the flag glyph + * @param shape flag shape + * @param grade the interpretation quality + * @param system the related system + * @param systemStems ordered collection of stems in system + * @return the created instance or null + */ + public static AbstractFlagInter createValidAdded (Glyph glyph, + Shape shape, + double grade, + SystemInfo system, + List systemStems) + { + AbstractFlagInter flag = new FlagInter(glyph, shape, grade); + + Link link = flag.lookupLink(systemStems); + + if (link != null) { + system.getSig().addVertex(flag); + link.applyTo(flag); + + return flag; + } + + return null; + } + + //--------------// + // getFlagValue // + //--------------// + /** + * Report the number of individual flags that corresponds to the flag shape + * + * @param shape the given flag shape + * @return the number of individual flags + */ + private static int getFlagValue (Shape shape) + { + switch (shape) { + case FLAG_1: + case FLAG_1_UP: + case SMALL_FLAG: + case SMALL_FLAG_SLASH: + return 1; + + case FLAG_2: + case FLAG_2_UP: + return 2; + + case FLAG_3: + case FLAG_3_UP: + return 3; + + case FLAG_4: + case FLAG_4_UP: + return 4; + + case FLAG_5: + case FLAG_5_UP: + return 5; + } + + logger.error("Illegal flag shape: {}", shape); + + return 0; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractInter.java index 18567b73d..a1b37021d 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractInter.java @@ -33,8 +33,9 @@ import org.audiveris.omr.sheet.rhythm.Voice; import org.audiveris.omr.sig.GradeImpacts; import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.relation.BasicContainment; +import org.audiveris.omr.sig.relation.Containment; import org.audiveris.omr.sig.relation.Link; +import org.audiveris.omr.sig.relation.MirrorRelation; import org.audiveris.omr.sig.relation.Relation; import org.audiveris.omr.ui.Colors; import org.audiveris.omr.ui.symbol.MusicFont; @@ -77,7 +78,8 @@ * interface. *

                                                                                                                  * As a general policy, subclasses can provide convenient creation static methods, which should - * be consistently named as follows:

                                                                                                                    + * be consistently named as follows: + *
                                                                                                                      *
                                                                                                                    • {@code create} for just inter creation. *
                                                                                                                    • {@code createValid} for inter creation and validation (if failed, inter is not created). *
                                                                                                                    • {@code createAdded} for inter creation and addition to SIG. @@ -91,13 +93,9 @@ public abstract class AbstractInter extends AbstractEntity implements Inter { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - AbstractInter.class); + private static final Logger logger = LoggerFactory.getLogger(AbstractInter.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -112,6 +110,7 @@ public abstract class AbstractInter /** Object bounds, perhaps different from glyph bounds. */ @XmlElement(name = "bounds") + @XmlJavaTypeAdapter(Jaxb.RectangleAdapter.class) protected Rectangle bounds; /** The quality of this interpretation. */ @@ -119,10 +118,11 @@ public abstract class AbstractInter @XmlJavaTypeAdapter(type = double.class, value = Jaxb.Double3Adapter.class) protected double grade; - /** Mirror instance, if any. */ + /** Deprecated. Old mirror instance, if any. */ + @Deprecated @XmlIDREF @XmlAttribute(name = "mirror") - protected AbstractInter mirror; + private AbstractInter oldMirror; /** Is it abnormal?. */ @XmlAttribute(name = "abnormal") @@ -171,7 +171,6 @@ public abstract class AbstractInter /** Potential attachments, lazily allocated. */ private AttachmentHolder attachments; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AbstractInter object, with detailed impacts information. * @@ -222,7 +221,6 @@ protected AbstractInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -300,12 +298,7 @@ public boolean contains (Point point) } } - if (isManual()) { - // Here we use bounds only... - return true; // Audaces fortuna juvat! - } - - return false; + return isManual(); } //----------// @@ -376,9 +369,9 @@ public Set getAllEnsembles () Set ensembles = null; for (Relation rel : sig.incomingEdgesOf(this)) { - if (rel instanceof BasicContainment) { + if (rel instanceof Containment) { if (ensembles == null) { - ensembles = new LinkedHashSet(); + ensembles = new LinkedHashSet<>(); } ensembles.add(sig.getOppositeInter(this, rel)); @@ -404,6 +397,19 @@ public Area getArea () return area; } + //---------// + // setArea // + //---------// + /** + * Set the underlying area. + * + * @param area the area to set + */ + public void setArea (Area area) + { + this.area = area; + } + //----------------// // getAttachments // //----------------// @@ -449,6 +455,15 @@ public Rectangle getBounds () return null; } + //-----------// + // setBounds // + //-----------// + @Override + public void setBounds (Rectangle bounds) + { + this.bounds = bounds; + } + //-----------// // getCenter // //-----------// @@ -514,6 +529,15 @@ public Double getContextualGrade () return ctxGrade; } + //--------------------// + // setContextualGrade // + //--------------------// + @Override + public void setContextualGrade (double value) + { + ctxGrade = value; + } + //---------------// // getCoreBounds // //---------------// @@ -553,7 +577,7 @@ public String getDetails () public InterEnsemble getEnsemble () { for (Relation rel : sig.incomingEdgesOf(this)) { - if (rel instanceof BasicContainment) { + if (rel instanceof Containment) { return (InterEnsemble) sig.getOppositeInter(this, rel); } } @@ -570,17 +594,13 @@ public Glyph getGlyph () return glyph; } - //--------------// - // getGoodGrade // - //--------------// - /** - * Report the minimum grade to consider an interpretation as good. - * - * @return the minimum grade value for a good interpretation - */ - public static double getGoodGrade () + //----------// + // setGlyph // + //----------// + @Override + public void setGlyph (Glyph glyph) { - return Grades.goodInterGrade; + this.glyph = glyph; } //----------// @@ -592,6 +612,15 @@ public double getGrade () return grade; } + //----------// + // setGrade // + //----------// + @Override + public void setGrade (double grade) + { + this.grade = grade; + } + //------------// // getImpacts // //------------// @@ -601,26 +630,33 @@ public GradeImpacts getImpacts () return impacts; } - //-------------// - // getMinGrade // - //-------------// - /** - * Report the minimum grade for an acceptable interpretation - * - * @return the minimum grade for keeping an Inter instance - */ - public static double getMinGrade () + //-----------// + // getMirror // + //-----------// + @Override + public Inter getMirror () { - return Grades.minInterGrade; + for (Relation rel : sig.getRelations(this, MirrorRelation.class)) { + return sig.getOppositeInter(this, rel); + } + + return null; } //-----------// - // getMirror // + // setMirror // //-----------// @Override - public Inter getMirror () + public void setMirror (Inter mirror) { - return mirror; + final boolean direct = this.getId() < mirror.getId(); + final Inter source = direct ? this : mirror; + final Inter target = direct ? mirror : this; + Relation rel = sig.getRelation(source, target, MirrorRelation.class); + + if (rel == null) { + sig.addEdge(source, target, new MirrorRelation()); + } } //---------// @@ -639,6 +675,15 @@ public Part getPart () return part; } + //---------// + // setPart // + //---------// + @Override + public void setPart (Part part) + { + this.part = part; + } + //-------------------// // getRelationCenter // //-------------------// @@ -666,6 +711,15 @@ public SIGraph getSig () return sig; } + //--------// + // setSig // + //--------// + @Override + public void setSig (SIGraph sig) + { + this.sig = sig; + } + //----------// // getStaff // //----------// @@ -678,6 +732,18 @@ public Staff getStaff () return staff; } + //----------// + // setStaff // + //----------// + /** + * @param staff the staff to set + */ + @Override + public void setStaff (Staff staff) + { + this.staff = staff; + } + //-----------------// // getSymbolBounds // //-----------------// @@ -752,6 +818,21 @@ public boolean isAbnormal () return abnormal; } + //-------------// + // setAbnormal // + //-------------// + @Override + public void setAbnormal (boolean abnormal) + { + if (this.abnormal != abnormal) { + this.abnormal = abnormal; + + if (sig != null) { + sig.getSystem().getSheet().getStub().setModified(true); + } + } + } + //--------------------// // isContextuallyGood // //--------------------// @@ -792,6 +873,15 @@ public boolean isManual () return manual; } + //-----------// + // setManual // + //-----------// + @Override + public void setManual (boolean manual) + { + this.manual = manual; + } + //-----------// // isRemoved // //-----------// @@ -840,9 +930,8 @@ public boolean overlaps (Inter that) } for (Inter thisMember : members) { - if (thisMember.overlaps(that) - && that.overlaps(thisMember) - && sig.noSupport(thisMember, that)) { + if (thisMember.overlaps(that) && that.overlaps(thisMember) + && sig.noSupport(thisMember, that)) { return true; } } @@ -912,11 +1001,11 @@ public void remove (boolean extensive) if (extensive) { // Handle ensemble - member cases? // Copy is needed to avoid concurrent modification exception - List relsCopy = new ArrayList(sig.incomingEdgesOf(this)); + List relsCopy = new ArrayList<>(sig.incomingEdgesOf(this)); for (Relation rel : relsCopy) { // A member may be contained by several ensembles (case of TimeNumberInter) - if (rel instanceof BasicContainment) { + if (rel instanceof Containment) { InterEnsemble ens = (InterEnsemble) sig.getOppositeInter(this, rel); if (ens.getMembers().size() == 1) { @@ -984,68 +1073,6 @@ public Collection searchLinks (SystemInfo system, return Collections.emptySet(); // By default } - //-------------// - // setAbnormal // - //-------------// - @Override - public void setAbnormal (boolean abnormal) - { - if (this.abnormal != abnormal) { - this.abnormal = abnormal; - - if (sig != null) { - sig.getSystem().getSheet().getStub().setModified(true); - } - } - } - - //---------// - // setArea // - //---------// - /** - * @param area the area to set - */ - public void setArea (Area area) - { - this.area = area; - } - - //-----------// - // setBounds // - //-----------// - @Override - public void setBounds (Rectangle bounds) - { - this.bounds = bounds; - } - - //--------------------// - // setContextualGrade // - //--------------------// - @Override - public void setContextualGrade (double value) - { - ctxGrade = value; - } - - //----------// - // setGlyph // - //----------// - @Override - public void setGlyph (Glyph glyph) - { - this.glyph = glyph; - } - - //----------// - // setGrade // - //----------// - @Override - public void setGrade (double grade) - { - this.grade = grade; - } - //-------// // setId // //-------// @@ -1063,64 +1090,38 @@ public void setId (int id) this.id = id; } - //-----------// - // setManual // - //-----------// - @Override - public void setManual (boolean manual) - { - this.manual = manual; - } - - //-----------// - // setMirror // - //-----------// + //-------------// + // shapeString // + //-------------// @Override - public void setMirror (Inter mirror) + public String shapeString () { - this.mirror = (AbstractInter) mirror; + return shape.toString(); } - //---------// - // setPart // - //---------// + //-----------------// + // upgradeOldStuff // + //-----------------// /** - * @param part the part to set + * Temporary method to upgrade from oldMirror field to MirrorRelation. + * + * @return true if really upgraded */ - @Override - public void setPart (Part part) + @Deprecated + public boolean upgradeOldStuff () { - this.part = part; - } + if (oldMirror != null) { + // We keep only Head mirrors (not HeadChord "mirrors" any more) + if (this instanceof HeadInter) { + setMirror(oldMirror); + } - //--------// - // setSig // - //--------// - @Override - public void setSig (SIGraph sig) - { - this.sig = sig; - } + oldMirror = null; - //----------// - // setStaff // - //----------// - /** - * @param staff the staff to set - */ - @Override - public void setStaff (Staff staff) - { - this.staff = staff; - } + return true; + } - //-------------// - // shapeString // - //-------------// - @Override - public String shapeString () - { - return shape.toString(); + return false; } //---------------// @@ -1180,10 +1181,6 @@ protected String internals () sb.append(")"); - if (mirror != null) { - sb.append(" mirror#").append(mirror.getId()); - } - if (staff != null) { sb.append(" s:").append(staff.getId()); } @@ -1226,7 +1223,32 @@ private void setStaffId (Integer id) } } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------------// + // getGoodGrade // + //--------------// + /** + * Report the minimum grade to consider an interpretation as good. + * + * @return the minimum grade value for a good interpretation + */ + public static double getGoodGrade () + { + return Grades.goodInterGrade; + } + + //-------------// + // getMinGrade // + //-------------// + /** + * Report the minimum grade for an acceptable interpretation + * + * @return the minimum grade for keeping an Inter instance + */ + public static double getMinGrade () + { + return Grades.minInterGrade; + } + //---------// // Adapter // //---------// @@ -1236,7 +1258,6 @@ private void setStaffId (Integer id) public static class Adapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public AbstractInter marshal (Inter inter) diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractInterVisitor.java b/src/main/org/audiveris/omr/sig/inter/AbstractInterVisitor.java index 85bae74fd..1a87731b1 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractInterVisitor.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractInterVisitor.java @@ -32,7 +32,6 @@ public abstract class AbstractInterVisitor implements InterVisitor { - //~ Methods ------------------------------------------------------------------------------------ @Override public void visit (AbstractBeamInter inter) diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractNoteInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractNoteInter.java index e0b602692..167cbb1b5 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractNoteInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractNoteInter.java @@ -48,10 +48,8 @@ public abstract class AbstractNoteInter extends AbstractPitchedInter { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - AbstractNoteInter.class); + private static final Logger logger = LoggerFactory.getLogger(AbstractNoteInter.class); /** The quarter duration value. */ public static final Rational QUARTER_DURATION = new Rational(1, 4); @@ -59,29 +57,6 @@ public abstract class AbstractNoteInter /** All shape-based intrinsic durations. */ private static final Map shapeDurations = buildShapeDurations(); - //~ Enumerations ------------------------------------------------------------------------------- - /** Names of the various note steps. */ - public static enum Step - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** La */ - A, - /** Si */ - B, - /** Do */ - C, - /** Ré */ - D, - /** Mi */ - E, - /** Fa */ - F, - /** Sol */ - G; - } - - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AbstractNoteInter object. * @@ -129,10 +104,14 @@ protected AbstractNoteInter () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getChord // //----------// + /** + * Report the containing chord, if any. + * + * @return containing chord or null + */ public AbstractChordInter getChord () { return (AbstractChordInter) getEnsemble(); @@ -178,21 +157,6 @@ public AugmentationDotInter getFirstAugmentationDot () return null; } - //------------------// - // getShapeDuration // - //------------------// - /** - * Report the duration indicated by the shape of the note or rest - * (regardless of any beam, flag, dot or tuplet). - * - * @param shape the shape of the note / rest - * @return the corresponding intrinsic duration - */ - public static Rational getShapeDuration (Shape shape) - { - return shapeDurations.get(shape); - } - //-------// // added // //-------// @@ -282,6 +246,21 @@ public void remove (boolean extensive) super.remove(extensive); } + //------------------// + // getShapeDuration // + //------------------// + /** + * Report the duration indicated by the shape of the note or rest + * (regardless of any beam, flag, dot or tuplet). + * + * @param shape the shape of the note / rest + * @return the corresponding intrinsic duration + */ + public static Rational getShapeDuration (Shape shape) + { + return shapeDurations.get(shape); + } + //---------------------// // buildShapeDurations // //---------------------// @@ -292,7 +271,7 @@ public void remove (boolean extensive) */ private static EnumMap buildShapeDurations () { - EnumMap map = new EnumMap(Shape.class); + EnumMap map = new EnumMap<>(Shape.class); map.put(Shape.LONG_REST, new Rational(4, 1)); // 4 measures @@ -322,4 +301,23 @@ private static EnumMap buildShapeDurations () return map; } + + /** Names of the various note steps. */ + public static enum Step + { + /** La */ + A, + /** Si */ + B, + /** Do */ + C, + /** Ré */ + D, + /** Mi */ + E, + /** Fa */ + F, + /** Sol */ + G; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractNumberInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractNumberInter.java index 424229482..1384d2bac 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractNumberInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractNumberInter.java @@ -39,13 +39,11 @@ public abstract class AbstractNumberInter extends AbstractInter { - //~ Instance fields ---------------------------------------------------------------------------- /** Integer value for the number. */ @XmlAttribute protected final int value; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AbstractNumberInter object. * @@ -76,7 +74,6 @@ public AbstractNumberInter (Rectangle bounds, this.value = (shape != null) ? valueOf(shape) : (-1); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -99,9 +96,24 @@ public int getValue () return value; } + //-----------// + // internals // + //-----------// + @Override + protected String internals () + { + return super.internals() + " " + value; + } + //---------// // valueOf // //---------// + /** + * Report the integer value for the provided shape + * + * @param shape shape to test + * @return supported integer value or IllegalArgumentException is thrown + */ protected static int valueOf (Shape shape) { switch (shape) { @@ -144,13 +156,4 @@ protected static int valueOf (Shape shape) throw new IllegalArgumentException("No integer value defined for " + shape); } - - //-----------// - // internals // - //-----------// - @Override - protected String internals () - { - return super.internals() + " " + value; - } } diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractPitchedInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractPitchedInter.java index 56a817270..371430bbf 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractPitchedInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractPitchedInter.java @@ -43,10 +43,10 @@ public abstract class AbstractPitchedInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- /** To order from bottom to top. */ - public static Comparator bottomUp = new Comparator() + public static final Comparator bottomUp + = new Comparator() { @Override public int compare (AbstractPitchedInter p1, @@ -62,13 +62,11 @@ public int compare (AbstractPitchedInter p1, } }; - //~ Instance fields ---------------------------------------------------------------------------- /** The assigned pitch. */ @XmlAttribute @XmlJavaTypeAdapter(Jaxb.Double1Adapter.class) protected Double pitch; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AbstractPitchedInter object. * @@ -120,7 +118,6 @@ protected AbstractPitchedInter () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // getIntegerPitch // //-----------------// @@ -147,6 +144,19 @@ public Double getPitch () return pitch; } + //----------// + // setPitch // + //----------// + /** + * Set pitch value. + * + * @param pitch the pitch to set + */ + public void setPitch (double pitch) + { + this.pitch = pitch; + } + //-----------// // setBounds // //-----------// @@ -155,22 +165,11 @@ public void setBounds (Rectangle bounds) { super.setBounds(bounds); - if (pitch == null && staff != null) { + if ((pitch == null) && (staff != null)) { setPitch(staff.pitchPositionOf(GeoUtil.centerOf(bounds))); } } - //----------// - // setPitch // - //----------// - /** - * @param pitch the pitch to set - */ - public void setPitch (double pitch) - { - this.pitch = pitch; - } - //----------// // setStaff // //----------// diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractTimeInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractTimeInter.java index 691b28b21..4c952d819 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractTimeInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractTimeInter.java @@ -56,15 +56,13 @@ public abstract class AbstractTimeInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - AbstractTimeInter.class); + private static final Logger logger = LoggerFactory.getLogger(AbstractTimeInter.class); /** Collection of default num/den combinations. */ - private static final Set defaultTimes = new LinkedHashSet( + private static final Set defaultTimes = new LinkedHashSet<>( Arrays.asList( new TimeRational(2, 2), // Duple simple new TimeRational(3, 2), // Triple simple @@ -84,8 +82,7 @@ public abstract class AbstractTimeInter constants.optionalTimes.getValue()); /** Rational value of each (full) time sig shape. */ - private static final Map rationals = new EnumMap( - Shape.class); + private static final Map rationals = new EnumMap<>(Shape.class); static { for (Shape s : ShapeSet.WholeTimes) { @@ -99,8 +96,6 @@ public abstract class AbstractTimeInter } } - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -109,7 +104,6 @@ public abstract class AbstractTimeInter @XmlJavaTypeAdapter(TimeRational.Adapter.class) protected TimeRational timeRational; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TimeInter object. * @@ -150,7 +144,6 @@ private AbstractTimeInter () super(null, null, null, null); } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // replicate // //-----------// @@ -163,50 +156,6 @@ private AbstractTimeInter () */ public abstract AbstractTimeInter replicate (Staff targetStaff); - //------------// - // rationalOf // - //------------// - /** - * Report the num/den pair of predefined time signature shapes. - * - * @param shape the queried shape - * @return the related num/den or null - */ - public static TimeRational rationalOf (Shape shape) - { - if (shape == null) { - return null; - } - - switch (shape) { - case COMMON_TIME: - case TIME_FOUR_FOUR: - return new TimeRational(4, 4); - - case CUT_TIME: - case TIME_TWO_TWO: - return new TimeRational(2, 2); - - case TIME_TWO_FOUR: - return new TimeRational(2, 4); - - case TIME_THREE_FOUR: - return new TimeRational(3, 4); - - case TIME_FIVE_FOUR: - return new TimeRational(5, 4); - - case TIME_THREE_EIGHT: - return new TimeRational(3, 8); - - case TIME_SIX_EIGHT: - return new TimeRational(6, 8); - - default: - return null; - } - } - //----------------// // getDenominator // //----------------// @@ -268,14 +217,6 @@ public TimeValue getValue () } } - //-------------// - // isSupported // - //-------------// - public static boolean isSupported (TimeRational tr) - { - return defaultTimes.contains(tr) || optionalTimes.contains(tr); - } - //--------// // modify // //--------// @@ -317,6 +258,64 @@ protected String internals () } } + //------------// + // rationalOf // + //------------// + /** + * Report the num/den pair of predefined time signature shapes. + * + * @param shape the queried shape + * @return the related num/den or null + */ + public static TimeRational rationalOf (Shape shape) + { + if (shape == null) { + return null; + } + + switch (shape) { + case COMMON_TIME: + case TIME_FOUR_FOUR: + return new TimeRational(4, 4); + + case CUT_TIME: + case TIME_TWO_TWO: + return new TimeRational(2, 2); + + case TIME_TWO_FOUR: + return new TimeRational(2, 4); + + case TIME_THREE_FOUR: + return new TimeRational(3, 4); + + case TIME_FIVE_FOUR: + return new TimeRational(5, 4); + + case TIME_THREE_EIGHT: + return new TimeRational(3, 8); + + case TIME_SIX_EIGHT: + return new TimeRational(6, 8); + + default: + return null; + } + } + + //-------------// + // isSupported // + //-------------// + /** + * Tell whether the provided TimeRational value is among the supported ones. + * + * @param tr provided value to check + * @return true if so + */ + public static boolean isSupported (TimeRational tr) + { + return defaultTimes.contains(tr) || optionalTimes.contains(tr); + } + //-----------------// // predefinedShape // //-----------------// @@ -343,17 +342,15 @@ private static Shape predefinedShape (TimeRational timeRational) return null; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String optionalTimes = new Constant.String( "6/4, 7/8", - "Time sigs besides " + defaultTimes); + "Optional time sigs"); } } diff --git a/src/main/org/audiveris/omr/sig/inter/AbstractVerticalInter.java b/src/main/org/audiveris/omr/sig/inter/AbstractVerticalInter.java index 5c671add2..b360678cb 100644 --- a/src/main/org/audiveris/omr/sig/inter/AbstractVerticalInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AbstractVerticalInter.java @@ -24,7 +24,6 @@ import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.math.AreaUtil; -import org.audiveris.omr.sig.BasicImpacts; import org.audiveris.omr.sig.GradeImpacts; import org.audiveris.omr.util.Jaxb; @@ -48,7 +47,6 @@ public abstract class AbstractVerticalInter extends AbstractInter { - //~ Instance fields ---------------------------------------------------------------------------- /** Line width. */ @XmlAttribute @@ -57,9 +55,9 @@ public abstract class AbstractVerticalInter /** Median line. */ @XmlElement + @XmlJavaTypeAdapter(Jaxb.Line2DAdapter.class) protected final Line2D median; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code AbstractVerticalInter} object. * @@ -108,7 +106,6 @@ public AbstractVerticalInter (Glyph glyph, } } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -167,20 +164,28 @@ private void computeArea () setBounds(getArea().getBounds()); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Impacts // //---------// + /** + * Grade impacts. + */ public static class Impacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{"core", "gap", "start", "stop"}; private static final double[] WEIGHTS = new double[]{1, 1, 1, 1}; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create an Impacts object. + * + * @param core value of black core impact + * @param gap value of vertical gap impact + * @param start derivative impact at start abscissa + * @param stop derivative impact at stop abscissa + */ public Impacts (double core, double gap, double start, diff --git a/src/main/org/audiveris/omr/sig/inter/AlterInter.java b/src/main/org/audiveris/omr/sig/inter/AlterInter.java index 8474078ed..ef161635e 100644 --- a/src/main/org/audiveris/omr/sig/inter/AlterInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AlterInter.java @@ -60,17 +60,14 @@ public class AlterInter extends AbstractPitchedInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(AlterInter.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Measured pitch value. */ private Double measuredPitch; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AlterInter object. * @@ -122,77 +119,6 @@ private AlterInter () this.measuredPitch = null; } - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // create // - //--------// - /** - * Create an Alter inter, with a grade value, determining pitch WRT provided staff. - * - * @param glyph underlying glyph - * @param shape precise shape - * @param grade evaluation value - * @param staff closest staff (questionable) - * @return the created instance - */ - public static AlterInter create (Glyph glyph, - Shape shape, - double grade, - Staff staff) - { - Pitches pitches = computePitch(glyph, shape, staff); - - return new AlterInter(glyph, shape, grade, staff, pitches.pitch, pitches.measuredPitch); - } - - //--------// - // create // - //--------// - /** - * Create an Alter inter, with impacts data, determining pitch WRT provided staff. - * - * @param glyph underlying glyph - * @param shape precise shape - * @param impacts assignment details - * @param staff related staff - * @return the created instance - */ - public static AlterInter create (Glyph glyph, - Shape shape, - GradeImpacts impacts, - Staff staff) - { - Pitches pitches = computePitch(glyph, shape, staff); - - return new AlterInter(glyph, shape, impacts, staff, pitches.pitch, pitches.measuredPitch); - } - - //-------------------// - // getFlatAreaOffset // - //-------------------// - /** - * Report for a flat sign the center offset WRT area center. - * - * @return height offset of pitch - */ - public static double getFlatAreaOffset () - { - return constants.flatAreaOffset.getValue(); - } - - //--------------------// - // getFlatPitchOffset // - //--------------------// - /** - * Report for a flat sign the vertical offset of pitch ordinate WRT sign top ordinate. - * - * @return height offset of pitch - */ - public static double getFlatPitchOffset () - { - return constants.flatPitchOffset.getValue(); - } - //--------// // accept // //--------// @@ -213,6 +139,47 @@ public void added () setAbnormal(true); // No head linked yet } + //--------------// + // alterationOf // + //--------------// + /** + * Report the pitch alteration that corresponds to the provided accidental. + * + * @param accidental the provided accidental, perhaps null + * @return the pitch impact + */ + public static int alterationOf (AlterInter accidental) + { + if (accidental == null) { + return 0; + } + + switch (accidental.getShape()) { + case SHARP: + return 1; + + case DOUBLE_SHARP: + return 2; + + case FLAT: + return -1; + + case DOUBLE_FLAT: + return -2; + + case NATURAL: + return 0; + + default: + logger.warn( + "Weird shape {} for accidental {}", + accidental.getShape(), + accidental.getId()); + + return 0; // Should not happen + } + } + //---------------// // checkAbnormal // //---------------// @@ -318,59 +285,6 @@ public void setLinks (List systemHeads) } } - //--------------// - // computePitch // - //--------------// - /** - * Compute pitch (integer) and measuredPitch (double) values related to the provided - * staff, according to alteration glyph and shape. - *

                                                                                                                      - * Sharp and natural signs are symmetric, hence their pitch can be directly derived from - * centroid ordinate. - *

                                                                                                                      - * But sharp signs are not symmetric, hence we need a more precise point. - * We use two heuristics:

                                                                                                                        - *
                                                                                                                      • Augment centroid pitch by a fixed pitch offset, around 0.65
                                                                                                                      • - *
                                                                                                                      • Use point located at a fixed ratio of glyph height, around 0.65, to retrieve pitch.
                                                                                                                      • - *
                                                                                                                      - * And we use the average value from these two heuristics. - * - * @param glyph underlying glyph - * @param shape precise shape - * @param staff related staff - * @return the pitch values (assigned, measured) - */ - protected static Pitches computePitch (Glyph glyph, - Shape shape, - Staff staff) - { - Point centroid = glyph.getCentroid(); - double massPitch = staff.pitchPositionOf(centroid); - - // Pitch offset for flat-based alterations - if ((shape == Shape.FLAT) || (shape == Shape.DOUBLE_FLAT)) { - // Heuristic pitch offset WRT centroid pitch - massPitch += getFlatPitchOffset(); - - // Heuristic center WRT glyph box - Rectangle box = glyph.getBounds(); - Point center = glyph.getCenter(); - double geoPitch = staff.pitchPositionOf( - new Point2D.Double(center.x, center.y + (getFlatAreaOffset() * box.height))); - - // Average value of both heuristics - double mix = 0.5 * (massPitch + geoPitch); - - // logger.info( - // "G#{} {}", - // glyph.getId(), - // String.format("mass:%+.2f geo:%+.2f mix:%+.2f", massPitch, geoPitch, mix)); - return new Pitches((int) Math.rint(mix), mix); - } else { - return new Pitches((int) Math.rint(massPitch), massPitch); - } - } - //-----------// // internals // //-----------// @@ -410,8 +324,9 @@ private Collection lookupLinks (List systemHeads) Rectangle accidBox = getBounds(); Point accidPt = new Point( accidBox.x + accidBox.width, - ((shape != Shape.FLAT) && (shape != Shape.DOUBLE_FLAT)) - ? (accidBox.y + (accidBox.height / 2)) + ((shape != Shape.FLAT) && (shape != Shape.DOUBLE_FLAT)) ? (accidBox.y + + (accidBox.height + / 2)) : (accidBox.y + ((3 * accidBox.height) / 4))); Rectangle luBox = new Rectangle(accidPt.x, accidPt.y - yGapMax, xGapMax, 2 * yGapMax); List notes = Inters.intersectedInters(systemHeads, GeoOrder.BY_ABSCISSA, luBox); @@ -444,7 +359,7 @@ private Collection lookupLinks (List systemHeads) } if (bestRel != null) { - Set set = new LinkedHashSet(); + Set set = new LinkedHashSet<>(); set.add(new Link(bestHead, bestRel, true)); // If any, include head mirror as well @@ -459,21 +374,153 @@ private Collection lookupLinks (List systemHeads) return Collections.emptySet(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------// + // create // + //--------// + /** + * Create an Alter inter, with a grade value, determining pitch WRT provided staff. + * + * @param glyph underlying glyph + * @param shape precise shape + * @param grade evaluation value + * @param staff closest staff (questionable) + * @return the created instance + */ + public static AlterInter create (Glyph glyph, + Shape shape, + double grade, + Staff staff) + { + Pitches pitches = computePitch(glyph, shape, staff); + + return new AlterInter(glyph, shape, grade, staff, pitches.pitch, pitches.measuredPitch); + } + + //--------// + // create // + //--------// + /** + * Create an Alter inter, with impacts data, determining pitch WRT provided staff. + * + * @param glyph underlying glyph + * @param shape precise shape + * @param impacts assignment details + * @param staff related staff + * @return the created instance + */ + public static AlterInter create (Glyph glyph, + Shape shape, + GradeImpacts impacts, + Staff staff) + { + Pitches pitches = computePitch(glyph, shape, staff); + + return new AlterInter(glyph, shape, impacts, staff, pitches.pitch, pitches.measuredPitch); + } + + //-------------------// + // getFlatAreaOffset // + //-------------------// + /** + * Report for a flat sign the center offset WRT area center. + * + * @return height offset of pitch + */ + public static double getFlatAreaOffset () + { + return constants.flatAreaOffset.getValue(); + } + + //--------------------// + // getFlatPitchOffset // + //--------------------// + /** + * Report for a flat sign the vertical offset of pitch ordinate WRT sign top ordinate. + * + * @return height offset of pitch + */ + public static double getFlatPitchOffset () + { + return constants.flatPitchOffset.getValue(); + } + + //--------------// + // computePitch // + //--------------// + /** + * Compute pitch (integer) and measuredPitch (double) values related to the provided + * staff, according to alteration glyph and shape. + *

                                                                                                                      + * Sharp and natural signs are symmetric, hence their pitch can be directly derived from + * centroid ordinate. + *

                                                                                                                      + * But sharp signs are not symmetric, hence we need a more precise point. + * We use two heuristics: + *

                                                                                                                        + *
                                                                                                                      • Augment centroid pitch by a fixed pitch offset, around 0.65
                                                                                                                      • + *
                                                                                                                      • Use point located at a fixed ratio of glyph height, around 0.65, to retrieve pitch.
                                                                                                                      • + *
                                                                                                                      + * And we use the average value from these two heuristics. + * + * @param glyph underlying glyph + * @param shape precise shape + * @param staff related staff + * @return the pitch values (assigned, measured) + */ + protected static Pitches computePitch (Glyph glyph, + Shape shape, + Staff staff) + { + Point centroid = glyph.getCentroid(); + double massPitch = staff.pitchPositionOf(centroid); + + // Pitch offset for flat-based alterations + if ((shape == Shape.FLAT) || (shape == Shape.DOUBLE_FLAT)) { + // Heuristic pitch offset WRT centroid pitch + massPitch += getFlatPitchOffset(); + + // Heuristic center WRT glyph box + Rectangle box = glyph.getBounds(); + Point center = glyph.getCenter(); + double geoPitch = staff.pitchPositionOf( + new Point2D.Double(center.x, center.y + (getFlatAreaOffset() * box.height))); + + // Average value of both heuristics + double mix = 0.5 * (massPitch + geoPitch); + + // logger.info( + // "G#{} {}", + // glyph.getId(), + // String.format("mass:%+.2f geo:%+.2f mix:%+.2f", massPitch, geoPitch, mix)); + return new Pitches((int) Math.rint(mix), mix); + } else { + return new Pitches((int) Math.rint(massPitch), massPitch); + } + } + //---------// // Pitches // //---------// + /** + * Gather both rounded and precise pitch values. + */ protected static class Pitches { - //~ Instance fields ------------------------------------------------------------------------ + /** Rounded to integer value. */ public final double pitch; + /** Precise value. */ public final double measuredPitch; - //~ Constructors --------------------------------------------------------------------------- - public Pitches (double pitch, - double measuredPitch) + /** + * Create a Pitches object + * + * @param pitch rounded value + * @param measuredPitch precise value + */ + Pitches (double pitch, + double measuredPitch) { this.pitch = pitch; this.measuredPitch = measuredPitch; @@ -483,10 +530,9 @@ public Pitches (double pitch, //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Double flatPitchOffset = new Constant.Double( "pitch", diff --git a/src/main/org/audiveris/omr/sig/inter/ArpeggiatoInter.java b/src/main/org/audiveris/omr/sig/inter/ArpeggiatoInter.java index a480d6fad..48394c545 100644 --- a/src/main/org/audiveris/omr/sig/inter/ArpeggiatoInter.java +++ b/src/main/org/audiveris/omr/sig/inter/ArpeggiatoInter.java @@ -1,249 +1,232 @@ -//------------------------------------------------------------------------------------------------// -// // -// A r p e g g i a t o I n t e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.inter; - -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.math.GeoOrder; -import org.audiveris.omr.math.GeoUtil; -import org.audiveris.omr.sheet.Scale; -import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sheet.rhythm.Voice; -import org.audiveris.omr.sig.relation.ChordArpeggiatoRelation; -import org.audiveris.omr.sig.relation.Link; -import org.audiveris.omr.sig.relation.Relation; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Rectangle; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code ArpeggiatoInter} represents the arpeggiato notation along the heads - * of a chord. - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "arpeggiato") -public class ArpeggiatoInter - extends AbstractInter -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger(ArpeggiatoInter.class); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code ArpeggiatoInter} object. - * - * @param glyph the arpeggiato glyph - * @param grade the interpretation quality - */ - public ArpeggiatoInter (Glyph glyph, - double grade) - { - super(glyph, (glyph != null) ? glyph.getBounds() : null, Shape.ARPEGGIATO, grade); - } - - /** - * No-arg constructor meant for JAXB. - */ - private ArpeggiatoInter () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // accept // - //--------// - @Override - public void accept (InterVisitor visitor) - { - visitor.visit(this); - } - - //-------// - // added // - //-------// - @Override - public void added () - { - super.added(); - - setAbnormal(true); // No chord linked yet - } - - //---------------// - // checkAbnormal // - //---------------// - @Override - public boolean checkAbnormal () - { - // Check if a chord is connected - setAbnormal(!sig.hasRelation(this, ChordArpeggiatoRelation.class)); - - return isAbnormal(); - } - - //------------------// - // createValidAdded // - //------------------// - /** - * (Try to) create and add an arpeggiato inter. - * - * @param glyph the arpeggiato glyph - * @param grade the interpretation quality - * @param system the related system - * @param systemHeadChords abscissa-ordered list of head-chords in this system - * @return the created arpeggiato or null - */ - public static ArpeggiatoInter createValidAdded (Glyph glyph, - double grade, - SystemInfo system, - List systemHeadChords) - { - ArpeggiatoInter arpeggiato = new ArpeggiatoInter(glyph, grade); - - Link link = arpeggiato.lookupLink(systemHeadChords, system); - - if (link != null) { - system.getSig().addVertex(arpeggiato); - link.applyTo(arpeggiato); - - return arpeggiato; - } - - return null; - } - - //----------// - // getVoice // - //----------// - @Override - public Voice getVoice () - { - for (Relation rel : sig.getRelations(this, ChordArpeggiatoRelation.class)) { - return sig.getOppositeInter(this, rel).getVoice(); - } - - return null; - } - - //-------------// - // searchLinks // - //-------------// - @Override - public Collection searchLinks (SystemInfo system, - boolean doit) - { - // Not very optimized! - List systemHeadChords = system.getSig().inters(HeadChordInter.class); - Collections.sort(systemHeadChords, Inters.byAbscissa); - - Link link = lookupLink(systemHeadChords, system); - - if (link == null) { - return Collections.emptyList(); - } - - if (doit) { - link.applyTo(this); - } - - return Collections.singleton(link); - } - - //------------// - // lookupLink // - //------------// - /** - * Try to detect a link between this arpeggiato instance and a HeadChord nearby. - * - * @param systemHeadChords ordered collection of head chords in system - * @return the link found or null - */ - private Link lookupLink (List systemHeadChords, - SystemInfo system) - { - // Look for a head-chord on right side of this symbol - // Use a lookup box (glyph height, predefined width) - // For intersected head-chords, measure y overlap WRT glyph height - Rectangle luBox = getBounds(); - luBox.x += luBox.width; - luBox.width = system.getSheet().getScale().toPixels(constants.areaDx); - - final List chords = Inters.intersectedInters( - systemHeadChords, - GeoOrder.BY_ABSCISSA, - luBox); - - int bestOverlap = 0; - HeadChordInter bestChord = null; - - for (Inter chord : chords) { - HeadChordInter hc = (HeadChordInter) chord; - Rectangle headsBox = hc.getHeadsBounds(); - - if (headsBox.intersects(luBox)) { - int overlap = GeoUtil.yOverlap(headsBox, luBox); - - if (bestOverlap < overlap) { - bestOverlap = overlap; - bestChord = hc; - } - } - } - - if (bestChord == null) { - return null; - } - - double relGrade = (double) bestOverlap / luBox.height; - ChordArpeggiatoRelation rel = new ChordArpeggiatoRelation(relGrade); - - if (relGrade < rel.getMinGrade()) { - return null; - } - - return new Link(bestChord, rel, false); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - Scale.Fraction areaDx = new Scale.Fraction(1.5, "Width of lookup area for embraced notes"); - } -} +//------------------------------------------------------------------------------------------------// +// // +// A r p e g g i a t o I n t e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.inter; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.math.GeoOrder; +import org.audiveris.omr.math.GeoUtil; +import org.audiveris.omr.sheet.Scale; +import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sheet.rhythm.Voice; +import org.audiveris.omr.sig.relation.ChordArpeggiatoRelation; +import org.audiveris.omr.sig.relation.Link; +import org.audiveris.omr.sig.relation.Relation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Rectangle; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code ArpeggiatoInter} represents the arpeggiato notation along the heads + * of a chord. + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "arpeggiato") +public class ArpeggiatoInter + extends AbstractInter +{ + + private static final Logger logger = LoggerFactory.getLogger(ArpeggiatoInter.class); + + /** + * Creates a new {@code ArpeggiatoInter} object. + * + * @param glyph the arpeggiato glyph + * @param grade the interpretation quality + */ + public ArpeggiatoInter (Glyph glyph, + double grade) + { + super(glyph, (glyph != null) ? glyph.getBounds() : null, Shape.ARPEGGIATO, grade); + } + + /** + * No-arg constructor meant for JAXB. + */ + private ArpeggiatoInter () + { + } + + //--------// + // accept // + //--------// + @Override + public void accept (InterVisitor visitor) + { + visitor.visit(this); + } + + //-------// + // added // + //-------// + @Override + public void added () + { + super.added(); + + setAbnormal(true); // No chord linked yet + } + + //---------------// + // checkAbnormal // + //---------------// + @Override + public boolean checkAbnormal () + { + // Check if a chord is connected + setAbnormal(!sig.hasRelation(this, ChordArpeggiatoRelation.class)); + + return isAbnormal(); + } + + //----------// + // getVoice // + //----------// + @Override + public Voice getVoice () + { + for (Relation rel : sig.getRelations(this, ChordArpeggiatoRelation.class)) { + return sig.getOppositeInter(this, rel).getVoice(); + } + + return null; + } + + //-------------// + // searchLinks // + //-------------// + @Override + public Collection searchLinks (SystemInfo system, + boolean doit) + { + // Not very optimized! + List systemHeadChords = system.getSig().inters(HeadChordInter.class); + Collections.sort(systemHeadChords, Inters.byAbscissa); + + Link link = lookupLink(systemHeadChords, system); + + if (link == null) { + return Collections.emptyList(); + } + + if (doit) { + link.applyTo(this); + } + + return Collections.singleton(link); + } + + //------------// + // lookupLink // + //------------// + /** + * Try to detect a link between this arpeggiato instance and a HeadChord nearby. + * + * @param systemHeadChords ordered collection of head chords in system + * @return the link found or null + */ + private Link lookupLink (List systemHeadChords, + SystemInfo system) + { + // Look for a head-chord on right side of this symbol + // Use a lookup box (glyph height, predefined width) + // For intersected head-chords, measure y overlap WRT glyph height + final Scale scale = system.getSheet().getScale(); + final Rectangle luBox = getBounds(); + luBox.x += luBox.width; + luBox.width = scale.toPixels(ChordArpeggiatoRelation.getXGapMaximum(manual)); + + final List chords = Inters.intersectedInters( + systemHeadChords, + GeoOrder.BY_ABSCISSA, + luBox); + + int bestOverlap = 0; + HeadChordInter bestChord = null; + + for (Inter chord : chords) { + HeadChordInter hc = (HeadChordInter) chord; + Rectangle headsBox = hc.getHeadsBounds(); + + if (headsBox.intersects(luBox)) { + int overlap = GeoUtil.yOverlap(headsBox, luBox); + + if (bestOverlap < overlap) { + bestOverlap = overlap; + bestChord = hc; + } + } + } + + if (bestChord == null) { + return null; + } + + double relGrade = (double) bestOverlap / luBox.height; + ChordArpeggiatoRelation rel = new ChordArpeggiatoRelation(relGrade); + + if (relGrade < rel.getMinGrade()) { + return null; + } + + return new Link(bestChord, rel, false); + } + + //------------------// + // createValidAdded // + //------------------// + /** + * (Try to) create and add an arpeggiato inter. + * + * @param glyph the arpeggiato glyph + * @param grade the interpretation quality + * @param system the related system + * @param systemHeadChords abscissa-ordered list of head-chords in this system + * @return the created arpeggiato or null + */ + public static ArpeggiatoInter createValidAdded (Glyph glyph, + double grade, + SystemInfo system, + List systemHeadChords) + { + ArpeggiatoInter arpeggiato = new ArpeggiatoInter(glyph, grade); + + Link link = arpeggiato.lookupLink(systemHeadChords, system); + + if (link != null) { + system.getSig().addVertex(arpeggiato); + link.applyTo(arpeggiato); + + return arpeggiato; + } + + return null; + } +} diff --git a/src/main/org/audiveris/omr/sig/inter/ArticulationInter.java b/src/main/org/audiveris/omr/sig/inter/ArticulationInter.java index 3458b325d..0c69d3d28 100644 --- a/src/main/org/audiveris/omr/sig/inter/ArticulationInter.java +++ b/src/main/org/audiveris/omr/sig/inter/ArticulationInter.java @@ -1,292 +1,288 @@ -//------------------------------------------------------------------------------------------------// -// // -// A r t i c u l a t i o n I n t e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.inter; - -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.math.GeoOrder; -import org.audiveris.omr.sheet.Scale; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sheet.rhythm.Voice; -import org.audiveris.omr.sig.relation.ChordArticulationRelation; -import org.audiveris.omr.sig.relation.Link; -import org.audiveris.omr.sig.relation.Relation; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Point; -import java.awt.Rectangle; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code ArticulationInter} represents an articulation sign - * (TENUTO, ACCENT, STACCATO, STACCATISSIMO, MARCATO). - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "articulation") -public class ArticulationInter - extends AbstractInter -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(ArticulationInter.class); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new ArticulationInter object. - * - * @param glyph underlying glyph - * @param shape precise shape (TENUTO, ACCENT, STACCATO, STACCATISSIMO, MARCATO) - * @param grade evaluation value - */ - public ArticulationInter (Glyph glyph, - Shape shape, - double grade) - { - super(glyph, (glyph != null) ? glyph.getBounds() : null, shape, grade); - } - - /** - * No-arg constructor meant for JAXB. - */ - private ArticulationInter () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // accept // - //--------// - @Override - public void accept (InterVisitor visitor) - { - visitor.visit(this); - } - - //-------// - // added // - //-------// - @Override - public void added () - { - super.added(); - - setAbnormal(true); // No chord linked yet - } - - //---------------// - // checkAbnormal // - //---------------// - @Override - public boolean checkAbnormal () - { - // Check if a chord is connected - setAbnormal(!sig.hasRelation(this, ChordArticulationRelation.class)); - - return isAbnormal(); - } - - //------------------// - // createValidAdded // - //------------------// - /** - * (Try to) create an ArticulationInter. - * - * @param glyph underlying glyph - * @param shape detected shape - * @param grade assigned grade - * @param system containing system - * @param systemHeadChords system head chords, ordered by abscissa - * @return the created articulation or null - */ - public static ArticulationInter createValidAdded (Glyph glyph, - Shape shape, - double grade, - SystemInfo system, - List systemHeadChords) - { - if (glyph.isVip()) { - logger.info("VIP ArticulationInter create {} as {}", glyph, shape); - } - - ArticulationInter artic = new ArticulationInter(glyph, shape, grade); - Link link = artic.lookupLink(systemHeadChords); - - if (link != null) { - system.getSig().addVertex(artic); - link.applyTo(artic); - - return artic; - } - - return null; - } - - //----------// - // getStaff // - //----------// - @Override - public Staff getStaff () - { - if (staff == null) { - for (Relation rel : sig.getRelations(this, ChordArticulationRelation.class)) { - HeadChordInter chord = (HeadChordInter) sig.getOppositeInter(this, rel); - - return staff = chord.getStaff(); - } - } - - return staff; - } - - //----------// - // getVoice // - //----------// - @Override - public Voice getVoice () - { - for (Relation rel : sig.getRelations(this, ChordArticulationRelation.class)) { - return sig.getOppositeInter(this, rel).getVoice(); - } - - return null; - } - - //-------------// - // searchLinks // - //-------------// - @Override - public Collection searchLinks (SystemInfo system, - boolean doit) - { - // Not very optimized! - List systemHeadChords = system.getSig().inters(HeadChordInter.class); - Collections.sort(systemHeadChords, Inters.byAbscissa); - - Link link = lookupLink(systemHeadChords); - - if (link == null) { - return Collections.emptyList(); - } - - if (doit) { - link.applyTo(this); - } - - return Collections.singleton(link); - } - - //-----------// - // internals // - //-----------// - @Override - protected String internals () - { - return super.internals() + " " + shape; - } - - //------------// - // lookupLink // - //------------// - /** - * Try to detect a link between this articulation instance and a HeadChord nearby. - * - * @param systemHeadChords ordered collection of head chords in system - * @return the link found or null - */ - private Link lookupLink (List systemHeadChords) - { - if (systemHeadChords.isEmpty()) { - return null; - } - - final SystemInfo system = systemHeadChords.get(0).getSig().getSystem(); - final Scale scale = system.getSheet().getScale(); - final int maxDx = scale.toPixels( - ChordArticulationRelation.getXOutGapMaximum(manual)); - final int maxDy = scale.toPixels(ChordArticulationRelation.getYGapMaximum(manual)); - final int minDy = scale.toPixels(ChordArticulationRelation.getYGapMinimum(manual)); - final Rectangle articBox = getBounds(); - final Point arcticCenter = getCenter(); - final Rectangle luBox = new Rectangle(arcticCenter); - luBox.grow(maxDx, maxDy); - - final List chords = Inters.intersectedInters( - systemHeadChords, - GeoOrder.BY_ABSCISSA, - luBox); - - if (chords.isEmpty()) { - return null; - } - - ChordArticulationRelation bestRel = null; - Inter bestChord = null; - double bestYGap = Double.MAX_VALUE; - - for (Inter chord : chords) { - Rectangle chordBox = chord.getBounds(); - - // The articulation cannot intersect the chord - if (chordBox.intersects(articBox)) { - continue; - } - - Point center = chord.getCenter(); - - // Select proper chord reference point (top or bottom) - int yRef = (arcticCenter.y > center.y) ? (chordBox.y + chordBox.height) : chordBox.y; - double absXGap = Math.abs(center.x - arcticCenter.x); - double yGap = (arcticCenter.y > center.y) ? (arcticCenter.y - yRef) - : (yRef - arcticCenter.y); - - if (yGap < minDy) { - continue; - } - - double absYGap = Math.abs(yGap); - ChordArticulationRelation rel = new ChordArticulationRelation(); - rel.setOutGaps(scale.pixelsToFrac(absXGap), scale.pixelsToFrac(absYGap), manual); - - if (rel.getGrade() >= rel.getMinGrade()) { - if ((bestRel == null) || (bestYGap > absYGap)) { - bestRel = rel; - bestChord = chord; - bestYGap = absYGap; - } - } - } - - if (bestRel != null) { - return new Link(bestChord, bestRel, false); - } - - return null; - } -} +//------------------------------------------------------------------------------------------------// +// // +// A r t i c u l a t i o n I n t e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.inter; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.math.GeoOrder; +import org.audiveris.omr.sheet.Scale; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sheet.rhythm.Voice; +import org.audiveris.omr.sig.relation.ChordArticulationRelation; +import org.audiveris.omr.sig.relation.Link; +import org.audiveris.omr.sig.relation.Relation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Point; +import java.awt.Rectangle; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code ArticulationInter} represents an articulation sign + * (TENUTO, ACCENT, STACCATO, STACCATISSIMO, MARCATO). + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "articulation") +public class ArticulationInter + extends AbstractInter +{ + + private static final Logger logger = LoggerFactory.getLogger(ArticulationInter.class); + + /** + * Creates a new ArticulationInter object. + * + * @param glyph underlying glyph + * @param shape precise shape (TENUTO, ACCENT, STACCATO, STACCATISSIMO, MARCATO) + * @param grade evaluation value + */ + public ArticulationInter (Glyph glyph, + Shape shape, + double grade) + { + super(glyph, (glyph != null) ? glyph.getBounds() : null, shape, grade); + } + + /** + * No-arg constructor meant for JAXB. + */ + private ArticulationInter () + { + } + + //--------// + // accept // + //--------// + @Override + public void accept (InterVisitor visitor) + { + visitor.visit(this); + } + + //-------// + // added // + //-------// + @Override + public void added () + { + super.added(); + + setAbnormal(true); // No chord linked yet + } + + //---------------// + // checkAbnormal // + //---------------// + @Override + public boolean checkAbnormal () + { + // Check if a chord is connected + setAbnormal(!sig.hasRelation(this, ChordArticulationRelation.class)); + + return isAbnormal(); + } + + //----------// + // getStaff // + //----------// + @Override + public Staff getStaff () + { + if (staff == null) { + for (Relation rel : sig.getRelations(this, ChordArticulationRelation.class)) { + HeadChordInter chord = (HeadChordInter) sig.getOppositeInter(this, rel); + + return staff = chord.getStaff(); + } + } + + return staff; + } + + //----------// + // getVoice // + //----------// + @Override + public Voice getVoice () + { + for (Relation rel : sig.getRelations(this, ChordArticulationRelation.class)) { + return sig.getOppositeInter(this, rel).getVoice(); + } + + return null; + } + + //-------------// + // searchLinks // + //-------------// + @Override + public Collection searchLinks (SystemInfo system, + boolean doit) + { + // Not very optimized! + List systemHeadChords = system.getSig().inters(HeadChordInter.class); + Collections.sort(systemHeadChords, Inters.byAbscissa); + + Link link = lookupLink(systemHeadChords); + + if (link == null) { + return Collections.emptyList(); + } + + if (doit) { + link.applyTo(this); + } + + return Collections.singleton(link); + } + + //-----------// + // internals // + //-----------// + @Override + protected String internals () + { + return super.internals() + " " + shape; + } + + //------------// + // lookupLink // + //------------// + /** + * Try to detect a link between this articulation instance and a HeadChord nearby. + * + * @param systemHeadChords ordered collection of head chords in system + * @return the link found or null + */ + private Link lookupLink (List systemHeadChords) + { + if (systemHeadChords.isEmpty()) { + return null; + } + + final SystemInfo system = systemHeadChords.get(0).getSig().getSystem(); + final Scale scale = system.getSheet().getScale(); + final int maxDx = scale.toPixels(ChordArticulationRelation.getXOutGapMaximum(manual)); + final int maxDy = scale.toPixels(ChordArticulationRelation.getYGapMaximum(manual)); + final int minDy = scale.toPixels(ChordArticulationRelation.getYGapMinimum(manual)); + final Rectangle articBox = getBounds(); + final Point arcticCenter = getCenter(); + final Rectangle luBox = new Rectangle(arcticCenter); + luBox.grow(maxDx, maxDy); + + final List chords = Inters.intersectedInters( + systemHeadChords, + GeoOrder.BY_ABSCISSA, + luBox); + + if (chords.isEmpty()) { + return null; + } + + ChordArticulationRelation bestRel = null; + Inter bestChord = null; + double bestYGap = Double.MAX_VALUE; + + for (Inter chord : chords) { + Rectangle chordBox = chord.getBounds(); + + // The articulation cannot intersect the chord + if (chordBox.intersects(articBox)) { + continue; + } + + Point center = chord.getCenter(); + + // Select proper chord reference point (top or bottom) + int yRef = (arcticCenter.y > center.y) ? (chordBox.y + chordBox.height) : chordBox.y; + double absXGap = Math.abs(center.x - arcticCenter.x); + double yGap = (arcticCenter.y > center.y) ? (arcticCenter.y - yRef) + : (yRef - arcticCenter.y); + + if (yGap < minDy) { + continue; + } + + double absYGap = Math.abs(yGap); + ChordArticulationRelation rel = new ChordArticulationRelation(); + rel.setOutGaps(scale.pixelsToFrac(absXGap), scale.pixelsToFrac(absYGap), manual); + + if (rel.getGrade() >= rel.getMinGrade()) { + if ((bestRel == null) || (bestYGap > absYGap)) { + bestRel = rel; + bestChord = chord; + bestYGap = absYGap; + } + } + } + + if (bestRel != null) { + return new Link(bestChord, bestRel, false); + } + + return null; + } + + //------------------// + // createValidAdded // + //------------------// + /** + * (Try to) create an ArticulationInter. + * + * @param glyph underlying glyph + * @param shape detected shape + * @param grade assigned grade + * @param system containing system + * @param systemHeadChords system head chords, ordered by abscissa + * @return the created articulation or null + */ + public static ArticulationInter createValidAdded (Glyph glyph, + Shape shape, + double grade, + SystemInfo system, + List systemHeadChords) + { + if (glyph.isVip()) { + logger.info("VIP ArticulationInter create {} as {}", glyph, shape); + } + + ArticulationInter artic = new ArticulationInter(glyph, shape, grade); + Link link = artic.lookupLink(systemHeadChords); + + if (link != null) { + system.getSig().addVertex(artic); + link.applyTo(artic); + + return artic; + } + + return null; + } +} diff --git a/src/main/org/audiveris/omr/sig/inter/AugmentationDotInter.java b/src/main/org/audiveris/omr/sig/inter/AugmentationDotInter.java index 6e2770d5c..241faeb18 100644 --- a/src/main/org/audiveris/omr/sig/inter/AugmentationDotInter.java +++ b/src/main/org/audiveris/omr/sig/inter/AugmentationDotInter.java @@ -50,7 +50,8 @@ import javax.xml.bind.annotation.XmlRootElement; /** - * Class {@code AugmentationDotInter} represent an augmentation dot for a note or a rest. + * Class {@code AugmentationDotInter} represents an augmentation dot for + * a note (head or rest) or another dot. * * @author Hervé Bitteur */ @@ -58,11 +59,9 @@ public class AugmentationDotInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(AugmentationDotInter.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code AugmentationDotInter} object. * @@ -82,7 +81,6 @@ private AugmentationDotInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -153,7 +151,7 @@ public List getAugmentedNotes () for (Relation rel : sig.getRelations(this, AugmentationRelation.class)) { if (notes == null) { - notes = new ArrayList(); + notes = new ArrayList<>(); } notes.add((AbstractNoteInter) sig.getEdgeTarget(rel)); @@ -255,7 +253,7 @@ public List lookupDotLinks (List systemDots, SystemInfo system) { // Need getCenter() - final List links = new ArrayList(); + final List links = new ArrayList<>(); final Scale scale = system.getSheet().getScale(); final Point dotCenter = getCenter(); final MeasureStack dotStack = system.getStackAt(dotCenter); @@ -306,7 +304,9 @@ public List lookupDotLinks (List systemDots, /** * Look up for a possible link with a head. *

                                                                                                                      - * Assumption: System dots are already in place or they are processed bottom up. + * Even in the case of a shared head, at most one head link is returned. + *

                                                                                                                      + * Assumption: System dots are already in place or they are processed top down. * * @param systemHeadChords system head chords, sorted by abscissa * @param system containing system @@ -316,7 +316,7 @@ public Link lookupHeadLink (List systemHeadChords, SystemInfo system) { // Need sig and getCenter() - final List links = new ArrayList(); + final List links = new ArrayList<>(); final Scale scale = system.getSheet().getScale(); final Point dotCenter = getCenter(); final MeasureStack dotStack = system.getStackAt(dotCenter); @@ -335,13 +335,17 @@ public Link lookupHeadLink (List systemHeadChords, for (Inter ic : chords) { HeadChordInter chord = (HeadChordInter) ic; - // Heads are processed bottom up within their chord - for (Inter ih : chord.getNotes()) { + // Heads are reported bottom up within their chord + // So, we need to sort the list top down + List chordHeads = chord.getNotes(); + Collections.sort(chordHeads, Inters.byCenterOrdinate); + + for (Inter ih : chordHeads) { HeadInter head = (HeadInter) ih; // Check head is within reach and not yet augmented if (GeoUtil.yEmbraces(luBox, head.getCenter().y) - && (head.getFirstAugmentationDot() == null)) { + && (head.getFirstAugmentationDot() == null)) { Point refPt = head.getCenterRight(); double xGap = dotCenter.x - refPt.x; @@ -351,7 +355,10 @@ public Link lookupHeadLink (List systemHeadChords, } // When this method is called, there is at most one stem per head - for (Relation rel : system.getSig().getRelations(head, HeadStemRelation.class)) { + // (including the case of shared heads) + for (Relation rel : system.getSig().getRelations( + head, + HeadStemRelation.class)) { HeadStemRelation hsRel = (HeadStemRelation) rel; if (hsRel.getHeadSide() == RIGHT) { @@ -388,7 +395,7 @@ public Link lookupHeadLink (List systemHeadChords, for (Link link : links) { HeadInter head = (HeadInter) link.partner; - if ((head.getIntegerPitch() % 2) == 1) { + if ((head.getIntegerPitch() % 2) != 0) { return link; } } @@ -410,7 +417,7 @@ public List lookupRestLinks (List systemRests, SystemInfo system) { // Need getCenter() - final List links = new ArrayList(); + final List links = new ArrayList<>(); final Scale scale = system.getSheet().getScale(); final Point dotCenter = getCenter(); final MeasureStack dotStack = system.getStackAt(dotCenter); @@ -472,10 +479,12 @@ public void remove (boolean extensive) //-------------// /** * Try to find a link with a note or another dot on the left. + *

                                                                                                                      + * In case of a shared head, a pair of links can be returned. * * @param system containing system * @param doit true to apply - * @return the link found or null + * @return a collection of 0, 1 or 2 best link(s) found */ @Override public Collection searchLinks (SystemInfo system, @@ -497,11 +506,95 @@ public Collection searchLinks (SystemInfo system, return Collections.emptyList(); } + final Collection links; + + if (link.partner instanceof HeadInter) { + links = sharedHeadLinks(link); + } else { + links = Collections.singleton(link); + } + if (doit) { - link.applyTo(this); + for (Link lnk : links) { + lnk.applyTo(this); + } + } + + return links; + } + + //-----------------// + // sharedHeadLinks // + //-----------------// + /** + * Modify the provided head link when the target head is a shared head. + *

                                                                                                                      + * There is a very specific case for shared heads. + * See some cases in Dichterliebe01 example. + *

                                                                                                                        + *
                                                                                                                      • If head is located on staff line or ledger, use dot relative location. + *
                                                                                                                      • If head is located between staff lines or ledgers, check chords durations: + *
                                                                                                                          + *
                                                                                                                        • If durations are different, assign the dot only to the longer + * (which means lower number of beams or flags). + *
                                                                                                                        • If they are identical, assign the dot to both. + *
                                                                                                                        + *
                                                                                                                      + * + * @param link the provided (head) link, perhaps null + * @return a collection of (head) links, the provided link for a non-shared head, but one or two + * links for shared heads + */ + public Collection sharedHeadLinks (Link link) + { + if (link == null) { + return Collections.EMPTY_LIST; } - return Collections.singleton(link); + final Collection links = new ArrayList<>(); + final HeadInter h1 = (HeadInter) link.partner; + final HeadInter h2 = (HeadInter) h1.getMirror(); + + if (h2 == null) { + links.add(link); + } else { + // Head on or between line(s)? + final int p = h1.getIntegerPitch(); + + if (p % 2 == 0) { + // On line + final int yHead = h1.getCenter().y; + final int yAug = getCenter().y; + final int yCh1 = h1.getChord().getCenter().y; + final HeadInter head; + + if (yAug < yHead) { + // Link to upper + head = yCh1 < yHead ? h1 : h2; + } else { + // Link to lower + head = yCh1 > yHead ? h1 : h2; + } + + links.add(new Link(head, new AugmentationRelation(), true)); + } else { + // Between lines + final int bf1 = h1.getChord().getBeamsOrFlagsNumber(); + final int bf2 = h2.getChord().getBeamsOrFlagsNumber(); + + if (bf1 == bf2) { + // Link to both + links.add(new Link(h1, new AugmentationRelation(), true)); + links.add(new Link(h2, new AugmentationRelation(), true)); + } else { + // Link to longer + HeadInter head = (bf1 < bf2) ? h1 : h2; + links.add(new Link(head, new AugmentationRelation(), true)); + } + } + } + + return links; } //--------------// @@ -524,24 +617,6 @@ private Rectangle getDotsLuBox (Point dotCenter, return getLuBox(dotCenter, maxDx, maxDy); } - //----------// - // getLuBox // - //----------// - /** - * Report proper lookup box based on provided dot center - * - * @param dotCenter center of dot candidate - * @param maxDx maximum dx between entity left side and dot center - * @param maxDy maximum dy between entity center and dot center - * @return proper lookup box - */ - private static Rectangle getLuBox (Point dotCenter, - int maxDx, - int maxDy) - { - return new Rectangle(dotCenter.x - maxDx, dotCenter.y - maxDy, maxDx, 2 * maxDy); - } - //---------------// // getNotesLuBox // //---------------// @@ -562,6 +637,24 @@ private Rectangle getNotesLuBox (Point dotCenter, return getLuBox(dotCenter, maxDx, maxDy); } + //----------// + // getLuBox // + //----------// + /** + * Report proper lookup box based on provided dot center + * + * @param dotCenter center of dot candidate + * @param maxDx maximum dx between entity left side and dot center + * @param maxDy maximum dy between entity center and dot center + * @return proper lookup box + */ + private static Rectangle getLuBox (Point dotCenter, + int maxDx, + int maxDy) + { + return new Rectangle(dotCenter.x - maxDx, dotCenter.y - maxDy, maxDx, 2 * maxDy); + } + //------------// // lookupLink // //------------// @@ -580,7 +673,7 @@ private Link lookupLink (List systemRests, List systemDots, SystemInfo system) { - List links = new ArrayList(); + List links = new ArrayList<>(); Link headLink = lookupHeadLink(systemHeadChords, system); if (headLink != null) { diff --git a/src/main/org/audiveris/omr/sig/inter/BarConnectorInter.java b/src/main/org/audiveris/omr/sig/inter/BarConnectorInter.java index 6d715b8fc..6b3da3d41 100644 --- a/src/main/org/audiveris/omr/sig/inter/BarConnectorInter.java +++ b/src/main/org/audiveris/omr/sig/inter/BarConnectorInter.java @@ -38,11 +38,9 @@ public class BarConnectorInter extends AbstractVerticalInter { - //~ Instance fields ---------------------------------------------------------------------------- private final BarConnection connection; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code BarConnectorInter} object. * @@ -67,7 +65,6 @@ private BarConnectorInter () this.connection = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/BarlineInter.java b/src/main/org/audiveris/omr/sig/inter/BarlineInter.java index b8bab2f30..3b8a6de31 100644 --- a/src/main/org/audiveris/omr/sig/inter/BarlineInter.java +++ b/src/main/org/audiveris/omr/sig/inter/BarlineInter.java @@ -55,13 +55,11 @@ public class BarlineInter extends AbstractVerticalInter { - //~ Instance fields ---------------------------------------------------------------------------- /** Does this bar line define a staff side?. */ @XmlAttribute(name = "staff-end") private HorizontalSide staffEnd; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BarlineInter object. * @@ -106,7 +104,6 @@ private BarlineInter () super(null, null, null, null, null); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -135,35 +132,6 @@ public void added () } } - //-------------------// - // getClosestBarline // - //-------------------// - /** - * From a provided Barline collection, report the one which has the closest abscissa - * to a provided point. - * - * @param bars the collection of bars to browse - * @param point the reference point - * @return the abscissa-wise closest barline - */ - public static BarlineInter getClosestBarline (Collection bars, - Point point) - { - BarlineInter bestBar = null; - int bestDx = Integer.MAX_VALUE; - - for (BarlineInter bar : bars) { - int dx = Math.abs(bar.getCenter().x - point.x); - - if (dx < bestDx) { - bestDx = dx; - bestBar = bar; - } - } - - return bestBar; - } - //------------// // getDetails // //------------// @@ -187,7 +155,7 @@ public String getDetails () */ public SortedSet getGroupItems () { - SortedSet items = new TreeSet(Inters.byFullAbscissa); + SortedSet items = new TreeSet<>(Inters.byFullAbscissa); items.add(this); browseGroup(this, items); @@ -197,6 +165,11 @@ public SortedSet getGroupItems () //------------// // getMeasure // //------------// + /** + * Report the containing measure. + * + * @return related measure + */ public Measure getMeasure () { StaffBarlineInter sb = getStaffBarline(); @@ -273,7 +246,7 @@ public StaffBarlineInter getStaffBarline () */ public List getSystemBarline () { - final List systemBarline = new ArrayList(); + final List systemBarline = new ArrayList<>(); final StaffBarlineInter staffBarline = getStaffBarline(); if (staffBarline != null) { @@ -295,6 +268,12 @@ public boolean isGood () //------------// // isStaffEnd // //------------// + /** + * Tell whether this barline ends the staff on provided side. + * + * @param side provided side + * @return true if so + */ public boolean isStaffEnd (HorizontalSide side) { return staffEnd == side; @@ -323,6 +302,11 @@ public void remove (boolean extensive) //-------------// // setStaffEnd // //-------------// + /** + * Set this barline as a staff end. + * + * @param side which side for the end. + */ public void setStaffEnd (HorizontalSide side) { staffEnd = side; @@ -358,4 +342,33 @@ private void browseGroup (BarlineInter bar, } } } + + //-------------------// + // getClosestBarline // + //-------------------// + /** + * From a provided Barline collection, report the one which has the closest abscissa + * to a provided point. + * + * @param bars the collection of bars to browse + * @param point the reference point + * @return the abscissa-wise closest barline + */ + public static BarlineInter getClosestBarline (Collection bars, + Point point) + { + BarlineInter bestBar = null; + int bestDx = Integer.MAX_VALUE; + + for (BarlineInter bar : bars) { + int dx = Math.abs(bar.getCenter().x - point.x); + + if (dx < bestDx) { + bestDx = dx; + bestBar = bar; + } + } + + return bestBar; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/BeamHookInter.java b/src/main/org/audiveris/omr/sig/inter/BeamHookInter.java index af08cd846..3231f60db 100644 --- a/src/main/org/audiveris/omr/sig/inter/BeamHookInter.java +++ b/src/main/org/audiveris/omr/sig/inter/BeamHookInter.java @@ -50,18 +50,15 @@ * Class {@code BeamHookInter} represents a beam hook interpretation. * * @see BeamInter - * * @author Hervé Bitteur */ @XmlRootElement(name = "beam-hook") public class BeamHookInter extends AbstractBeamInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new HookInter object. * @@ -94,7 +91,6 @@ private BeamHookInter () super(null, null, null, 0); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -224,14 +220,12 @@ private Link lookupLink (List systemStems, return new Link(bestStem, bestRel, true); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ Scale.Fraction xMargin = new Scale.Fraction(0.5, "Width of lookup area for stem"); diff --git a/src/main/org/audiveris/omr/sig/inter/BeamInter.java b/src/main/org/audiveris/omr/sig/inter/BeamInter.java index baf30efe3..96daa6a4d 100644 --- a/src/main/org/audiveris/omr/sig/inter/BeamInter.java +++ b/src/main/org/audiveris/omr/sig/inter/BeamInter.java @@ -33,14 +33,12 @@ * to a beam hook interpretation. * * @see BeamHookInter - * * @author Hervé Bitteur */ @XmlRootElement(name = "beam") public class BeamInter extends AbstractBeamInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BeamInter object. @@ -74,7 +72,6 @@ private BeamInter () super(null, null, null, 0); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/BraceInter.java b/src/main/org/audiveris/omr/sig/inter/BraceInter.java index 9b8ff18fd..ab9ed7859 100644 --- a/src/main/org/audiveris/omr/sig/inter/BraceInter.java +++ b/src/main/org/audiveris/omr/sig/inter/BraceInter.java @@ -43,11 +43,9 @@ public class BraceInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(BraceInter.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BraceInter object. * @@ -68,7 +66,6 @@ private BraceInter () super(null, null, null, null); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/BracketConnectorInter.java b/src/main/org/audiveris/omr/sig/inter/BracketConnectorInter.java index 56821c0e3..898a58d3c 100644 --- a/src/main/org/audiveris/omr/sig/inter/BracketConnectorInter.java +++ b/src/main/org/audiveris/omr/sig/inter/BracketConnectorInter.java @@ -37,11 +37,9 @@ public class BracketConnectorInter extends AbstractVerticalInter { - //~ Instance fields ---------------------------------------------------------------------------- private final BarConnection connection; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BracketConnectorInter object. * @@ -64,7 +62,6 @@ private BracketConnectorInter () this.connection = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/BracketInter.java b/src/main/org/audiveris/omr/sig/inter/BracketInter.java index dae57dbdb..cc250a6d5 100644 --- a/src/main/org/audiveris/omr/sig/inter/BracketInter.java +++ b/src/main/org/audiveris/omr/sig/inter/BracketInter.java @@ -39,24 +39,11 @@ public class BracketInter extends AbstractVerticalInter { - //~ Enumerations ------------------------------------------------------------------------------- - public static enum BracketKind - { - //~ Enumeration constant initializers ------------------------------------------------------ - - TOP, - BOTH, - BOTTOM, - NONE; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Bracket kind. */ @XmlAttribute(name = "kind") private final BracketKind kind; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code BracketInter} object. * @@ -85,7 +72,6 @@ private BracketInter () this.kind = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -111,10 +97,23 @@ public String getDetails () // getKind // //---------// /** + * Report the bracket kind. + * * @return the kind */ public BracketKind getKind () { return kind; } + + /** + * Kind of bracket. + */ + public static enum BracketKind + { + TOP, + BOTH, + BOTTOM, + NONE + } } diff --git a/src/main/org/audiveris/omr/sig/inter/BreathMarkInter.java b/src/main/org/audiveris/omr/sig/inter/BreathMarkInter.java index b28dd24e1..d0ae0226b 100644 --- a/src/main/org/audiveris/omr/sig/inter/BreathMarkInter.java +++ b/src/main/org/audiveris/omr/sig/inter/BreathMarkInter.java @@ -1,101 +1,99 @@ -//------------------------------------------------------------------------------------------------// -// // -// B r e a t h M a r k I n t e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.inter; - -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sheet.SystemInfo; - -import java.awt.Point; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code BreathMarkInter} represents a comma-shaped break mark above a staff. - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "breath-mark") -public class BreathMarkInter - extends AbstractInter -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Creates a new {@code BreathMarkInter} object. - * - * @param glyph the breathMark glyph - * @param grade the interpretation quality - */ - public BreathMarkInter (Glyph glyph, - double grade) - { - super(glyph, (glyph != null) ? glyph.getBounds() : null, Shape.BREATH_MARK, grade); - } - - /** - * No-arg constructor meant for JAXB. - */ - private BreathMarkInter () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // accept // - //--------// - @Override - public void accept (InterVisitor visitor) - { - visitor.visit(this); - } - - //--------// - // create // - //--------// - /** - * (Try to) create a breathMark inter. - * - * @param glyph the breathMark glyph - * @param grade the interpretation quality - * @param system the related system - * @return the created breathMark or null - */ - public static BreathMarkInter create (Glyph glyph, - double grade, - SystemInfo system) - { - // Look for staff below - final Point center = glyph.getCenter(); - final Staff staff = system.getStaffAtOrBelow(center); - - if (staff == null) { - return null; - } - - final BreathMarkInter breathMark = new BreathMarkInter(glyph, grade); - breathMark.setStaff(staff); - - return breathMark; - } -} +//------------------------------------------------------------------------------------------------// +// // +// B r e a t h M a r k I n t e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.inter; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.SystemInfo; + +import java.awt.Point; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code BreathMarkInter} represents a comma-shaped break mark above a staff. + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "breath-mark") +public class BreathMarkInter + extends AbstractInter +{ + + /** + * Creates a new {@code BreathMarkInter} object. + * + * @param glyph the breathMark glyph + * @param grade the interpretation quality + */ + public BreathMarkInter (Glyph glyph, + double grade) + { + super(glyph, (glyph != null) ? glyph.getBounds() : null, Shape.BREATH_MARK, grade); + } + + /** + * No-arg constructor meant for JAXB. + */ + private BreathMarkInter () + { + } + + //--------// + // accept // + //--------// + @Override + public void accept (InterVisitor visitor) + { + visitor.visit(this); + } + + //--------// + // create // + //--------// + /** + * (Try to) create a breathMark inter. + * + * @param glyph the breathMark glyph + * @param grade the interpretation quality + * @param system the related system + * @return the created breathMark or null + */ + public static BreathMarkInter create (Glyph glyph, + double grade, + SystemInfo system) + { + // Look for staff below + final Point center = glyph.getCenter(); + final Staff staff = system.getStaffAtOrBelow(center); + + if (staff == null) { + return null; + } + + final BreathMarkInter breathMark = new BreathMarkInter(glyph, grade); + breathMark.setStaff(staff); + + return breathMark; + } +} diff --git a/src/main/org/audiveris/omr/sig/inter/CaesuraInter.java b/src/main/org/audiveris/omr/sig/inter/CaesuraInter.java index 1c2c46619..423503741 100644 --- a/src/main/org/audiveris/omr/sig/inter/CaesuraInter.java +++ b/src/main/org/audiveris/omr/sig/inter/CaesuraInter.java @@ -1,101 +1,99 @@ -//------------------------------------------------------------------------------------------------// -// // -// C a e s u r a I n t e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.inter; - -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sheet.SystemInfo; - -import java.awt.Point; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code CaesuraInter} represents a caesura sign above a staff. - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "caesura") -public class CaesuraInter - extends AbstractInter -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Creates a new {@code CaesuraInter} object. - * - * @param glyph the caesura glyph - * @param grade the interpretation quality - */ - public CaesuraInter (Glyph glyph, - double grade) - { - super(glyph, (glyph != null) ? glyph.getBounds() : null, Shape.CAESURA, grade); - } - - /** - * No-arg constructor meant for JAXB. - */ - private CaesuraInter () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // accept // - //--------// - @Override - public void accept (InterVisitor visitor) - { - visitor.visit(this); - } - - //--------// - // create // - //--------// - /** - * (Try to) create a caesura inter. - * - * @param glyph the caesura glyph - * @param grade the interpretation quality - * @param system the related system - * @return the created caesura or null - */ - public static CaesuraInter create (Glyph glyph, - double grade, - SystemInfo system) - { - // Look for staff below - final Point center = glyph.getCenter(); - final Staff staff = system.getStaffAtOrBelow(center); - - if (staff == null) { - return null; - } - - final CaesuraInter caesura = new CaesuraInter(glyph, grade); - caesura.setStaff(staff); - - return caesura; - } -} +//------------------------------------------------------------------------------------------------// +// // +// C a e s u r a I n t e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.inter; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.SystemInfo; + +import java.awt.Point; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code CaesuraInter} represents a caesura sign above a staff. + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "caesura") +public class CaesuraInter + extends AbstractInter +{ + + /** + * Creates a new {@code CaesuraInter} object. + * + * @param glyph the caesura glyph + * @param grade the interpretation quality + */ + public CaesuraInter (Glyph glyph, + double grade) + { + super(glyph, (glyph != null) ? glyph.getBounds() : null, Shape.CAESURA, grade); + } + + /** + * No-arg constructor meant for JAXB. + */ + private CaesuraInter () + { + } + + //--------// + // accept // + //--------// + @Override + public void accept (InterVisitor visitor) + { + visitor.visit(this); + } + + //--------// + // create // + //--------// + /** + * (Try to) create a caesura inter. + * + * @param glyph the caesura glyph + * @param grade the interpretation quality + * @param system the related system + * @return the created caesura or null + */ + public static CaesuraInter create (Glyph glyph, + double grade, + SystemInfo system) + { + // Look for staff below + final Point center = glyph.getCenter(); + final Staff staff = system.getStaffAtOrBelow(center); + + if (staff == null) { + return null; + } + + final CaesuraInter caesura = new CaesuraInter(glyph, grade); + caesura.setStaff(staff); + + return caesura; + } +} diff --git a/src/main/org/audiveris/omr/sig/inter/ChordNameInter.java b/src/main/org/audiveris/omr/sig/inter/ChordNameInter.java index fa5b793b1..305d97374 100644 --- a/src/main/org/audiveris/omr/sig/inter/ChordNameInter.java +++ b/src/main/org/audiveris/omr/sig/inter/ChordNameInter.java @@ -26,6 +26,7 @@ import static org.audiveris.omr.sig.inter.ChordNameInter.Kind.Type.*; import org.audiveris.omr.text.TextLine; import org.audiveris.omr.text.TextWord; +import org.audiveris.omr.util.Jaxb; import static org.audiveris.omr.util.RegexUtil.*; import org.slf4j.Logger; @@ -42,17 +43,18 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * Class {@code ChordNameInter} is a formatted piece of text that * describes a chord symbol such as F#m7, A(9) or BMaj7/D#. * This class is organized according to the target MusicXML harmony element. *

                                                                                                                      - * TODO: Add support for degree subtract (besides add and alter)

                                                                                                                      + * TODO: Add support for degree subtract (besides add and alter) *

                                                                                                                      - * TODO: Add support for classical functions (besides root)

                                                                                                                      + * TODO: Add support for classical functions (besides root) *

                                                                                                                      - * TODO: Add support for French steps: Do, Ré, Mi, etc.

                                                                                                                      + * TODO: Add support for French steps: Do, Ré, Mi, etc. * * @author Hervé Bitteur */ @@ -61,10 +63,8 @@ public class ChordNameInter extends WordInter { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - ChordNameInter.class); + private static final Logger logger = LoggerFactory.getLogger(ChordNameInter.class); /** Unicode value for flat sign: {@value}. */ public static final String FLAT = "\u266D"; @@ -116,12 +116,15 @@ public class ChordNameInter private static final String STEP_CLASS = "[A-G]"; /** Pattern for root value. A, A# or Ab */ - private static final String rootPat = group(ROOT_STEP, STEP_CLASS) - + group(ROOT_ALTER, Alter.CLASS) + "?"; + private static final String rootPat = group(ROOT_STEP, STEP_CLASS) + group( + ROOT_ALTER, + Alter.CLASS) + "?"; /** Pattern for bass value, if any. /A, /A# or /Ab */ private static final String bassPat = "(/" + group(BASS_STEP, STEP_CLASS) - + group(BASS_ALTER, Alter.CLASS) + "?" + ")"; + + group(BASS_ALTER, Alter.CLASS) + + "?" + + ")"; /** Pattern for major indication. M, maj or DELTA */ private static final String majPat = group(MAJ, "(M|[Mm][Aa][Jj]|" + DELTA + ")"); @@ -139,13 +142,20 @@ public class ChordNameInter private static final String hdimPat = group(HDIM, "\u00F8"); /** Pattern for any of the indication alternatives. (except sus) */ - private static final String modePat = "(" + majPat + "|" + minPat + "|" + augPat + "|" - + dimPat + "|" + hdimPat + ")"; + private static final String modePat = "(" + majPat + + "|" + + minPat + + "|" + + augPat + + "|" + + dimPat + + "|" + + hdimPat + + ")"; /** Pattern for (maj7) in min(maj7) = MAJOR_MINOR. */ - private static final String parMajPat = "(\\(" - + group(PMAJ7, "(M|[Mm][Aa][Jj]|" + DELTA + ")7") - + "\\))"; + private static final String parMajPat = "(\\(" + group(PMAJ7, "(M|[Mm][Aa][Jj]|" + DELTA + ")7") + + "\\))"; /** Pattern for any degree value. 5, 6, 7, 9, 11 or 13 */ private static final String DEG_CLASS = "(5|6|7|9|11|13)"; @@ -164,16 +174,13 @@ public class ChordNameInter modePat + "?" + parMajPat + "?" + degsPat + "?" + susPat + "?"); /** Pattern for parenthesized degrees if any. (6), (#9), (#11b13) */ - private static final String parPat = "(\\(" - + group( - PARS, - Alter.CLASS + "?" + DEG_CLASS + "(" + Alter.CLASS + DEG_CLASS + ")*") + "\\))"; + private static final String parPat = "(\\(" + group( + PARS, + Alter.CLASS + "?" + DEG_CLASS + "(" + Alter.CLASS + DEG_CLASS + ")*") + "\\))"; /** Un-compiled patterns for whole chord symbol. */ private static final String[] raws = new String[]{ - rootPat + kindPat + "?" + parPat + "?" + bassPat - + "?" - // TODO: add a pattern for functions + rootPat + kindPat + "?" + parPat + "?" + bassPat + "?" // TODO: add a pattern for functions }; /** Compiled patterns for whole chord symbol. */ @@ -181,12 +188,11 @@ public class ChordNameInter /** Pattern for one degree. (in a sequence of degrees) */ private static final String degPat = group(DEG_ALTER, Alter.CLASS) + "?" - + group(DEG_VALUE, DEG_CLASS); + + group(DEG_VALUE, DEG_CLASS); /** Compiled pattern for one degree. */ private static final Pattern degPattern = Pattern.compile(degPat); - //~ Instance fields ---------------------------------------------------------------------------- /** Root. */ @XmlElement private final Pitch root; @@ -203,7 +209,6 @@ public class ChordNameInter @XmlElement(name = "degree") private final List degrees; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ChordInfo object, with all parameters. * @@ -265,14 +270,12 @@ public ChordNameInter (TextWord textWord, */ private ChordNameInter () { - super(null); this.root = null; this.kind = null; this.bass = null; this.degrees = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -282,6 +285,75 @@ public void accept (InterVisitor visitor) visitor.visit(this); } + /** + * @return the bass + */ + public Pitch getBass () + { + return bass; + } + + /** + * @return the degrees + */ + public List getDegrees () + { + return degrees; + } + + /** + * @return the kind + */ + public Kind getKind () + { + return kind; + } + + /** + * @return the root + */ + public Pitch getRoot () + { + return root; + } + + //-------------// + // shapeString // + //-------------// + @Override + public String shapeString () + { + return "CHORD_NAME_\"" + value + "\""; + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + sb.append("{"); + + sb.append(" '").append(value).append("'"); + + sb.append(" root:").append(root); + + sb.append(" kind:").append(kind); + + if (bass != null) { + sb.append(" bass:").append(bass); + } + + for (Degree degree : degrees) { + sb.append(" deg:").append(degree); + } + + sb.append("}"); + + return sb.toString(); + } + //--------// // create // //--------// @@ -328,8 +400,8 @@ public static ChordNameInter createValid (TextWord textWord) String degStr = getGroup(matcher, DEGS); List degrees = Degree.createList(degStr, null); Degree firstDeg = (!degrees.isEmpty()) ? degrees.get(0) : null; - String firstDegStr = (firstDeg != null) - ? Integer.toString(degrees.get(0).value) : ""; + String firstDegStr = (firstDeg != null) ? Integer.toString(degrees.get(0).value) + : ""; // (maj7) special stuff String pmaj7 = standard(matcher, PMAJ7); @@ -342,9 +414,8 @@ public static ChordNameInter createValid (TextWord textWord) getGroup(matcher, BASS_STEP), getGroup(matcher, BASS_ALTER)); - if ((firstDeg != null) - && (kind.type != SUSPENDED_FOURTH) - && (kind.type != SUSPENDED_SECOND)) { + if ((firstDeg != null) && (kind.type != SUSPENDED_FOURTH) + && (kind.type != SUSPENDED_SECOND)) { // Remove first degree degrees.remove(firstDeg); } @@ -362,75 +433,6 @@ public static ChordNameInter createValid (TextWord textWord) return null; } - /** - * @return the bass - */ - public Pitch getBass () - { - return bass; - } - - /** - * @return the degrees - */ - public List getDegrees () - { - return degrees; - } - - /** - * @return the kind - */ - public Kind getKind () - { - return kind; - } - - /** - * @return the root - */ - public Pitch getRoot () - { - return root; - } - - //-------------// - // shapeString // - //-------------// - @Override - public String shapeString () - { - return "CHORD_NAME_\"" + value + "\""; - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()); - sb.append("{"); - - sb.append(" '").append(value).append("'"); - - sb.append(" root:").append(root); - - sb.append(" kind:").append(kind); - - if (bass != null) { - sb.append(" bass:").append(bass); - } - - for (Degree degree : degrees) { - sb.append(" deg:").append(degree); - } - - sb.append("}"); - - return sb.toString(); - } - //-------------// // getPatterns // //-------------// @@ -442,11 +444,13 @@ public String toString () private static List getPatterns () { if (patterns == null) { - patterns = new ArrayList(); + List ps = new ArrayList<>(); for (String raw : raws) { - patterns.add(Pattern.compile(raw)); + ps.add(Pattern.compile(raw)); } + + patterns = ps; } return patterns; @@ -471,23 +475,12 @@ private static String standard (Matcher matcher, return token.isEmpty() ? "" : name; } - //~ Inner Classes ------------------------------------------------------------------------------ - // - //-------// - // Alter // - //-------// - /** - * Handling of alteration indication (flat, sharp or nothing). - * The class accepts both number (#) and real sharp sign, as well as both (b) and real flat sign - */ public static class Alter { - //~ Static fields/initializers ------------------------------------------------------------- /** Alter class. */ private static final String CLASS = "[" + FLAT + "b" + SHARP + "#" + "]"; - //~ Methods -------------------------------------------------------------------------------- /** * Convert sharp/flat/empty sign to Integer. * @@ -527,30 +520,15 @@ private static String toString (Integer alter) return (alter == 1) ? "#" : ((alter == -1) ? "b" : ""); } } + + private Alter () + { + } } - //--------// - // Degree // - //--------// - /** - * Handling of degree information. - *

                                                                                                                      - * TODO: subtraction is not yet handled - */ public static class Degree { - //~ Enumerations --------------------------------------------------------------------------- - - public static enum DegreeType - { - //~ Enumeration constant initializers -------------------------------------------------- - - ADD, - ALTER, - SUBTRACT; - } - //~ Instance fields ------------------------------------------------------------------------ // /** nth value of the degree, wrt the chord root. */ @XmlAttribute @@ -568,8 +546,6 @@ public static enum DegreeType @XmlAttribute public final String text; - //~ Constructors --------------------------------------------------------------------------- - // public Degree (int value, Integer alter, DegreeType type) @@ -588,7 +564,26 @@ public Degree (int value, this.text = text; } - //~ Methods -------------------------------------------------------------------------------- + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("("); + + sb.append(value); + + sb.append(Alter.toString(alter)); + + sb.append(" ").append(type); + + if (!text.isEmpty()) { + sb.append(" '").append(text).append("'"); + } + + sb.append(")"); + + return sb.toString(); + } + // /** * Build a sequence of Degree instances from the provided string @@ -600,15 +595,12 @@ public Degree (int value, public static List createList (String str, Degree dominant) { - List degrees = new ArrayList(); - + List degrees = new ArrayList<>(); if ((str == null) || str.isEmpty()) { return degrees; } - // Loop on occurrences of the one-degree pattern Matcher matcher = degPattern.matcher(str); - while (matcher.find()) { // Deg value String degStr = getGroup(matcher, DEG_VALUE); @@ -628,126 +620,64 @@ public static List createList (String str, // Deg alter final String altStr = getGroup(matcher, DEG_ALTER); - final Integer alter = (altStr != null) ? Alter.toAlter(altStr) : null; + final Integer alter = Alter.toAlter(altStr); degrees.add(new Degree(deg, alter, type, "")); } - return degrees; } - @Override - public String toString () + public static enum DegreeType { - StringBuilder sb = new StringBuilder("("); - - sb.append(value); - - sb.append(Alter.toString(alter)); - - sb.append(" ").append(type); - - if (!text.isEmpty()) { - sb.append(" '").append(text).append("'"); - } - - sb.append(")"); - - return sb.toString(); + ADD, + ALTER, + SUBTRACT; } } - //------// - // Kind // - //------// - /** - * Handling of kind (aka quality) chord information. - */ public static class Kind { - //~ Enumerations --------------------------------------------------------------------------- - public static enum Type - { - //~ Enumeration constant initializers -------------------------------------------------- - - MAJOR, - MINOR, - AUGMENTED, - DIMINISHED, - DOMINANT, - MAJOR_SEVENTH, - MINOR_SEVENTH, - DIMINISHED_SEVENTH, - AUGMENTED_SEVENTH, - HALF_DIMINISHED, - MAJOR_MINOR, - MAJOR_SIXTH, - MINOR_SIXTH, - DOMINANT_NINTH, - MAJOR_NINTH, - MINOR_NINTH, - DOMINANT_11_TH, - MAJOR_11_TH, - MINOR_11_TH, - DOMINANT_13_TH, - MAJOR_13_TH, - MINOR_13_TH, - SUSPENDED_SECOND, - SUSPENDED_FOURTH, - - // NEAPOLITAN, - // ITALIAN, - // FRENCH, - // GERMAN, - // PEDAL, - // POWER, - // TRISTAN, - OTHER, - NONE; - } - - //~ Instance fields ------------------------------------------------------------------------ - // /** Precise type of kind. (subset of the 33 Music XML values) */ @XmlAttribute public final Type type; /** Flag to signal parentheses, if any. */ @XmlAttribute - public final Boolean parentheses; + @XmlJavaTypeAdapter(type = boolean.class, value = Jaxb.BooleanPositiveAdapter.class) + public final boolean parentheses; /** Flag to signal use of symbol, if any. */ @XmlAttribute - public final Boolean symbol; + @XmlJavaTypeAdapter(type = boolean.class, value = Jaxb.BooleanPositiveAdapter.class) + public final boolean symbol; /** Exact display text for the chord kind. (For example min vs m) */ @XmlAttribute public final String text; - //~ Constructors --------------------------------------------------------------------------- public Kind (Type type) { - this(type, "", null, null); + this(type, "", false, false); } public Kind (Type type, String text) { - this(type, text, null, null); + this(type, text, false, false); } public Kind (Type type, String text, - Boolean symbol) + boolean symbol) { - this(type, text, symbol, null); + this(type, text, symbol, false); } public Kind (Type type, String text, - Boolean symbol, - Boolean parentheses) + boolean symbol, + boolean parentheses) { this.type = type; this.parentheses = parentheses; @@ -759,12 +689,11 @@ public Kind (Type type, private Kind () { this.type = null; - this.parentheses = null; - this.symbol = null; + this.parentheses = false; + this.symbol = false; this.text = null; } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { @@ -777,11 +706,11 @@ public String toString () sb.append(" '").append(text).append("'"); } - if ((parentheses != null) && parentheses) { + if (parentheses) { sb.append(" parens"); } - if ((symbol != null) && symbol) { + if (symbol) { sb.append(" symbol"); } @@ -811,10 +740,10 @@ private static Kind create (Matcher matcher, switch (susStr.toLowerCase()) { case "sus2": - return new Kind(SUSPENDED_SECOND, kindStr, null, parentheses); + return new Kind(SUSPENDED_SECOND, kindStr, false, parentheses); case "sus4": - return new Kind(SUSPENDED_FOURTH, kindStr, null, parentheses); + return new Kind(SUSPENDED_FOURTH, kindStr, false, parentheses); case "": // Fall through @@ -822,8 +751,10 @@ private static Kind create (Matcher matcher, // Then check for other combinations final String str = standard(matcher, MIN) + standard(matcher, MAJ) - + standard(matcher, AUG) + standard(matcher, DIM) - + standard(matcher, HDIM) + dominant; + + standard(matcher, AUG) + + standard(matcher, DIM) + + standard(matcher, HDIM) + + dominant; Type type = typeOf(str); // Special case for Triangle sign => maj7 rather than major @@ -832,9 +763,8 @@ private static Kind create (Matcher matcher, } // Use of symbol? - Boolean symbol = (getGroup(matcher, MAJ).equals(DELTA) - || getGroup(matcher, MIN).equals("-") - || getGroup(matcher, AUG).equals("+")) ? Boolean.TRUE : null; + Boolean symbol = (getGroup(matcher, MAJ).equals(DELTA) || getGroup(matcher, MIN).equals( + "-") || getGroup(matcher, AUG).equals("+")) ? Boolean.TRUE : null; return (type != null) ? new Kind(type, kindStr, symbol, parentheses) : null; } @@ -923,18 +853,49 @@ private static Type typeOf (String str) return null; } } + + public static enum Type + { + MAJOR, + MINOR, + AUGMENTED, + DIMINISHED, + DOMINANT, + MAJOR_SEVENTH, + MINOR_SEVENTH, + DIMINISHED_SEVENTH, + AUGMENTED_SEVENTH, + HALF_DIMINISHED, + MAJOR_MINOR, + MAJOR_SIXTH, + MINOR_SIXTH, + DOMINANT_NINTH, + MAJOR_NINTH, + MINOR_NINTH, + DOMINANT_11_TH, + MAJOR_11_TH, + MINOR_11_TH, + DOMINANT_13_TH, + MAJOR_13_TH, + MINOR_13_TH, + SUSPENDED_SECOND, + SUSPENDED_FOURTH, + + // NEAPOLITAN, + // ITALIAN, + // FRENCH, + // GERMAN, + // PEDAL, + // POWER, + // TRISTAN, + OTHER, + NONE; + } } - //-------// - // Pitch // - //-------// - /** - * General handling of pitch information, used by root and bass. - */ @XmlAccessorType(XmlAccessType.NONE) public static class Pitch { - //~ Instance fields ------------------------------------------------------------------------ /** Related step. */ @XmlAttribute @@ -944,7 +905,6 @@ public static class Pitch @XmlAttribute public final Integer alter; - //~ Constructors --------------------------------------------------------------------------- public Pitch (AbstractNoteInter.Step step, Integer alter) { @@ -964,7 +924,18 @@ private Pitch () this.alter = null; } - //~ Methods -------------------------------------------------------------------------------- + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(); + + sb.append(step); + + sb.append(Alter.toString(alter)); + + return sb.toString(); + } + /** * Create a Pitch object from provided step and alter strings * @@ -984,54 +955,5 @@ public static Pitch create (String stepStr, return null; } } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(); - - sb.append(step); - - sb.append(Alter.toString(alter)); - - return sb.toString(); - } } - - /* - * major : X|maj ................ : 1 - 3 - 5 - * minor : m|min ................ : 1 - b3 - 5 - * augmented : +|aug............. : 1 - 3 - #5 - * diminished : °|dim............ : 1 - b3 - b5 - * dominant : 7 ................. : 1 - 3 - 5 - b7 - * major-seventh : M7|maj7....... : 1 - 3 - 5 - 7 - * minor-seventh : m7|min7....... : 1 - b3 - 5 - b7 - * diminished-seventh : dim7..... : 1 - b3 - b5 -bb7 - * augmented-seventh :+7|7+5|aug7 : 1 - 3 - #5 - b7 - * half-diminished :-7b5|m7b5.... : 1 - b3 - b5 - b7 - * major-minor : min(maj7)....... : 1 - b3 - 5 - 7 - * major-sixth : 6 .............. : 1 - 3 - 5 - 6 - * minor-sixth : m6|min6......... : 1 - b3 - 5 - 6 - * dominant-ninth : 9............ : 1 - 3 - 5 - b7 - 9 - * major-ninth : M9|maj9......... : 1 - 3 - 5 - 7 - 9 - * minor-ninth : m9|min9......... : 1 - b3 - 5 - b7 - 9 - * dominant-11th : 11............ : 1 - 3 - 5 - b7 - 9 - 11 - * major-11th : M11|maj11........ : 1 - 3 - 5 - 7 - 9 - 11 - * minor-11th : m11|min11........ : 1 - b3 - 5 - b7 - 9 - 11 - * dominant-13th : 13............ : 1 - 3 - 5 - b7 - 9 - 11 - 13 - * major-13th : M13|maj13........ : 1 - 3 - 5 - 7 - 9 - 11 - 13 - * minor-13th : m13|min13........ : 1 - b3 - 5 - b7 - 9 - 11 - 13 - * suspended-second : sus2....... : 1 - 2 - 5 - * suspended-fourth : sus4....... : 1 - 4 - 5 - * suspended-ninth : sus9........ : 1 - 4 - 5 - b7 - 9 - * Neapolitan : ................. : - * Italian : .................... : - * French : ..................... : - * German : ..................... : - * pedal : ...................... : - * power : ...................... : - * Tristan : .................... : - * other : ...................... : - * none : ....................... : - */ } diff --git a/src/main/org/audiveris/omr/sig/inter/ClefInter.java b/src/main/org/audiveris/omr/sig/inter/ClefInter.java index 5276112d6..587658aff 100644 --- a/src/main/org/audiveris/omr/sig/inter/ClefInter.java +++ b/src/main/org/audiveris/omr/sig/inter/ClefInter.java @@ -24,6 +24,7 @@ import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.rhythm.MeasureStack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,7 +33,6 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; -import org.audiveris.omr.sheet.rhythm.MeasureStack; /** * Class {@code ClefInter} handles a Clef interpretation. @@ -41,7 +41,8 @@ * (Treble, Alto, Tenor and Bass) and for each presents where the "Middle C" note (C4) would take * place. *

                                                                                                                      - * Middle C in four clefs *

                                                                                                                      * Step line of the clef : -4 for top line (Baritone), -2 for Bass and Tenor, @@ -53,7 +54,6 @@ public class ClefInter extends AbstractPitchedInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ClefInter.class); @@ -66,39 +66,10 @@ public class ClefInter +2.0, ClefKind.TREBLE); - //~ Enumerations ------------------------------------------------------------------------------- - public static enum ClefKind - { - //~ Enumeration constant initializers ------------------------------------------------------ - - TREBLE(Shape.G_CLEF, 2), - BASS(Shape.F_CLEF, -2), - ALTO(Shape.C_CLEF, 0), - TENOR(Shape.C_CLEF, -2), - PERCUSSION(Shape.PERCUSSION_CLEF, 0); - - //~ Instance fields ------------------------------------------------------------------------ - /** Symbol shape class. (regardless of ottava mark if any) */ - public final Shape shape; - - /** Pitch of reference line. */ - public final int pitch; - - //~ Constructors --------------------------------------------------------------------------- - ClefKind (Shape shape, - int pitch) - { - this.shape = shape; - this.pitch = pitch; - } - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Kind of the clef. */ @XmlAttribute - private final ClefKind kind; + private ClefKind kind; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a ghost ClefInter object. * @@ -138,134 +109,26 @@ private ClefInter (Glyph glyph, private ClefInter () { super(null, null, null, null, null, null); - this.kind = null; } - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // create // - //--------// - /** - * Create a Clef inter. - * - * @param glyph underlying glyph - * @param shape precise shape - * @param grade evaluation value - * @param staff related staff - * @return the created instance or null if failed - */ - public static ClefInter create (Glyph glyph, - Shape shape, - double grade, - Staff staff) - { - switch (shape) { - case G_CLEF: - case G_CLEF_SMALL: - case G_CLEF_8VA: - case G_CLEF_8VB: - return new ClefInter(glyph, shape, grade, staff, 2.0, ClefKind.TREBLE); - - case C_CLEF: - - // Depending on precise clef position, we can have - // an Alto C-clef (pp=0) or a Tenor C-clef (pp=-2) - Point center = glyph.getCenter(); - double pp = Math.rint(staff.pitchPositionOf(center)); - ClefKind kind = (pp >= -1) ? ClefKind.ALTO : ClefKind.TENOR; - - return new ClefInter(glyph, shape, grade, staff, pp, kind); - - case F_CLEF: - case F_CLEF_SMALL: - case F_CLEF_8VA: - case F_CLEF_8VB: - return new ClefInter(glyph, shape, grade, staff, -2.0, ClefKind.BASS); - - case PERCUSSION_CLEF: - return new ClefInter(glyph, shape, grade, staff, 0.0, ClefKind.PERCUSSION); - - default: - return null; - } - } - - //--------// - // kindOf // - //--------// - public static ClefKind kindOf (Glyph glyph, - Shape shape, - Staff staff) + //-------// + // added // + //-------// + @Override + public void added () { - switch (shape) { - case G_CLEF: - case G_CLEF_SMALL: - case G_CLEF_8VA: - case G_CLEF_8VB: - return ClefKind.TREBLE; - - case C_CLEF: - - // Disambiguate between Alto C-clef (pp=0) and Tenor C-clef (pp=-2) - Point center = glyph.getCenter(); - int pp = (int) Math.rint(staff.pitchPositionOf(center)); - - return (pp >= -1) ? ClefKind.ALTO : ClefKind.TENOR; - - case F_CLEF: - case F_CLEF_SMALL: - case F_CLEF_8VA: - case F_CLEF_8VB: - return ClefKind.BASS; + super.added(); - case PERCUSSION_CLEF: - return ClefKind.PERCUSSION; - - default: - return null; - } - } + // Add it to containing measure stack + MeasureStack stack = sig.getSystem().getStackAt(getCenter()); - //------------// - // noteStepOf // - //------------// - /** - * Report the note step that corresponds to a note in the provided pitch position, - * using the current clef if any, otherwise using the default clef (G_CLEF) - * - * @param clef the provided current clef - * @param pitchPosition the pitch position of the provided note - * @return the corresponding note step - */ - public static HeadInter.Step noteStepOf (ClefInter clef, - int pitchPosition) - { - if (clef == null) { - return defaultClef.noteStepOf(pitchPosition); - } else { - return clef.noteStepOf(pitchPosition); - } - } + if (stack != null) { + stack.addInter(this); - //----------// - // octaveOf // - //----------// - /** - * Report the octave corresponding to a note at the provided pitch position, - * assuming we are governed by the provided clef, otherwise (if clef is null) - * we use the default clef (G_CLEF) - * - * @param clef the current clef if any - * @param pitch the pitch position of the note - * @return the corresponding octave - */ - public static int octaveOf (ClefInter clef, - double pitch) - { - if (clef == null) { - return defaultClef.octaveOf(pitch); - } else { - return clef.octaveOf(pitch); + if (kind == null) { + kind = kindOf(getCenter(), shape, staff); + pitch = Double.valueOf(kind.pitch); + } } } @@ -424,4 +287,163 @@ private int octaveOf (double pitchPosition) return 0; // To keep compiler happy } } + + //--------// + // create // + //--------// + /** + * Create a Clef inter. + * + * @param glyph underlying glyph + * @param shape precise shape + * @param grade evaluation value + * @param staff related staff + * @return the created instance or null if failed + */ + public static ClefInter create (Glyph glyph, + Shape shape, + double grade, + Staff staff) + { + switch (shape) { + case G_CLEF: + case G_CLEF_SMALL: + case G_CLEF_8VA: + case G_CLEF_8VB: + return new ClefInter(glyph, shape, grade, staff, 2.0, ClefKind.TREBLE); + + case C_CLEF: + + // Depending on precise clef position, we can have + // an Alto C-clef (pp=0) or a Tenor C-clef (pp=-2) + Point center = glyph.getCenter(); + double pp = Math.rint(staff.pitchPositionOf(center)); + ClefKind kind = (pp >= -1) ? ClefKind.ALTO : ClefKind.TENOR; + + return new ClefInter(glyph, shape, grade, staff, pp, kind); + + case F_CLEF: + case F_CLEF_SMALL: + case F_CLEF_8VA: + case F_CLEF_8VB: + return new ClefInter(glyph, shape, grade, staff, -2.0, ClefKind.BASS); + + case PERCUSSION_CLEF: + return new ClefInter(glyph, shape, grade, staff, 0.0, ClefKind.PERCUSSION); + + default: + return null; + } + } + + //--------// + // kindOf // + //--------// + /** + * Guess the clef kind, based on shape and location. + * + * @param center area center of the clef + * @param shape clef shape + * @param staff the containing shape + * @return the precise clef kind + */ + public static ClefKind kindOf (Point center, + Shape shape, + Staff staff) + { + switch (shape) { + case G_CLEF: + case G_CLEF_SMALL: + case G_CLEF_8VA: + case G_CLEF_8VB: + return ClefKind.TREBLE; + + case C_CLEF: + + // Disambiguate between Alto C-clef (pp=0) and Tenor C-clef (pp=-2) + int pp = (int) Math.rint(staff.pitchPositionOf(center)); + + return (pp >= -1) ? ClefKind.ALTO : ClefKind.TENOR; + + case F_CLEF: + case F_CLEF_SMALL: + case F_CLEF_8VA: + case F_CLEF_8VB: + return ClefKind.BASS; + + case PERCUSSION_CLEF: + return ClefKind.PERCUSSION; + + default: + return null; + } + } + + //------------// + // noteStepOf // + //------------// + /** + * Report the note step that corresponds to a note in the provided pitch position, + * using the current clef if any, otherwise using the default clef (G_CLEF) + * + * @param clef the provided current clef + * @param pitchPosition the pitch position of the provided note + * @return the corresponding note step + */ + public static HeadInter.Step noteStepOf (ClefInter clef, + int pitchPosition) + { + if (clef == null) { + return defaultClef.noteStepOf(pitchPosition); + } else { + return clef.noteStepOf(pitchPosition); + } + } + + //----------// + // octaveOf // + //----------// + /** + * Report the octave corresponding to a note at the provided pitch position, + * assuming we are governed by the provided clef, otherwise (if clef is null) + * we use the default clef (G_CLEF) + * + * @param clef the current clef if any + * @param pitch the pitch position of the note + * @return the corresponding octave + */ + public static int octaveOf (ClefInter clef, + double pitch) + { + if (clef == null) { + return defaultClef.octaveOf(pitch); + } else { + return clef.octaveOf(pitch); + } + } + + /** + * Clef kind, based on shape and pitch. + */ + public static enum ClefKind + { + TREBLE(Shape.G_CLEF, 2), + BASS(Shape.F_CLEF, -2), + ALTO(Shape.C_CLEF, 0), + TENOR(Shape.C_CLEF, -2), + PERCUSSION(Shape.PERCUSSION_CLEF, 0); + + /** Symbol shape class. (regardless of ottava mark if any) */ + public final Shape shape; + + /** Pitch of reference line. */ + public final int pitch; + + ClefKind (Shape shape, + int pitch) + { + this.shape = shape; + this.pitch = pitch; + } + } } diff --git a/src/main/org/audiveris/omr/sig/inter/DeletedInterException.java b/src/main/org/audiveris/omr/sig/inter/DeletedInterException.java index f405d5bc7..abb8afb64 100644 --- a/src/main/org/audiveris/omr/sig/inter/DeletedInterException.java +++ b/src/main/org/audiveris/omr/sig/inter/DeletedInterException.java @@ -30,11 +30,10 @@ public class DeletedInterException extends Exception { - //~ Instance fields ---------------------------------------------------------------------------- + /** Inter involved in deletion. */ public final Inter inter; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code DeletedInterException} object. * diff --git a/src/main/org/audiveris/omr/sig/inter/DynamicsInter.java b/src/main/org/audiveris/omr/sig/inter/DynamicsInter.java index b5ce040f1..f8589fc37 100644 --- a/src/main/org/audiveris/omr/sig/inter/DynamicsInter.java +++ b/src/main/org/audiveris/omr/sig/inter/DynamicsInter.java @@ -60,14 +60,19 @@ public class DynamicsInter extends AbstractInter implements StringSymbolInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(DynamicsInter.class); /** Map Shape -> Signature. */ - private static final Map sigs = new EnumMap(Shape.class); + private static final Map sigs = new EnumMap<>(Shape.class); + + /** Map Signature -> Shape. */ + private static final Map shapes = new HashMap<>(); + + /** Map Shape -> Sound. (TODO: complete the table) */ + private static final Map sounds = new EnumMap<>(Shape.class); static { // // Additional characters : m, r, s & z @@ -99,9 +104,6 @@ public class DynamicsInter // sigs.put(Shape.DYNAMICS_SFPP, "sfpp"); } - /** Map Signature -> Shape. */ - private static final Map shapes = new HashMap(); - static { shapes.put("pp", Shape.DYNAMICS_PP); shapes.put("p", Shape.DYNAMICS_P); @@ -123,9 +125,6 @@ public class DynamicsInter // shapes.put("sfpp", Shape.DYNAMICS_SFPP); } - /** Map Shape -> Sound. (TODO: complete the table) */ - private static final Map sounds = new HashMap(); - static { sounds.put(Shape.DYNAMICS_PP, 45); sounds.put(Shape.DYNAMICS_P, 56); @@ -152,7 +151,6 @@ public class DynamicsInter // sounds.put(Shape.DYNAMICS_SFZ, "sfz"); } - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new DynamicsInter object. * @@ -174,7 +172,6 @@ private DynamicsInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -187,6 +184,11 @@ public void accept (InterVisitor visitor) //---------------// // getSoundLevel // //---------------// + /** + * Report the sound level, if any, based on dynamics shape. + * + * @return sound level or null + */ public Integer getSoundLevel () { Shape shape = getShape(); @@ -243,6 +245,12 @@ public boolean linkWithChord () //------------// // lookupLink // //------------// + /** + * Look up system for a potential link. + * + * @param system containing system + * @return link or null + */ public Link lookupLink (SystemInfo system) { if (isVip()) { @@ -266,9 +274,9 @@ public Link lookupLink (SystemInfo system) for (VerticalSide side : VerticalSide.values()) { final boolean lookAbove = side == VerticalSide.TOP; - AbstractChordInter chord = lookAbove - ? stack.getStandardChordAbove(center, widenedBounds) - : stack.getStandardChordBelow(center, widenedBounds); + AbstractChordInter chord = lookAbove ? stack.getStandardChordAbove( + center, + widenedBounds) : stack.getStandardChordBelow(center, widenedBounds); if ((chord == null) || chord instanceof RestChordInter) { continue; @@ -355,18 +363,15 @@ public void swallowShorterDynamics (List dynamics) final DynamicsInter shorter = (DynamicsInter) inter; final String shortString = shorter.getSymbolString(); - if ((shorter == this) - || (shortString.length() >= cplLength) - || !cplString.contains(shortString)) { + if ((shorter == this) || (shortString.length() >= cplLength) + || !cplString.contains(shortString)) { continue; } // Measure area of intersection over area of shorter box Rectangle shortBox = shorter.getBounds(); double shortArea = shortBox.width * shortBox.height; - int intArea = GeoUtil.xOverlap(shortBox, cplBox) * GeoUtil.yOverlap( - shortBox, - cplBox); + int intArea = GeoUtil.xOverlap(shortBox, cplBox) * GeoUtil.yOverlap(shortBox, cplBox); double ios = intArea / shortArea; logger.debug("ios:{} {} intersects {}", ios, shorter, this); @@ -391,14 +396,12 @@ protected String internals () return super.internals() + " " + shape; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxDy = new Scale.Fraction( 7, diff --git a/src/main/org/audiveris/omr/sig/inter/EndingInter.java b/src/main/org/audiveris/omr/sig/inter/EndingInter.java index ba6cf642c..161819ca4 100644 --- a/src/main/org/audiveris/omr/sig/inter/EndingInter.java +++ b/src/main/org/audiveris/omr/sig/inter/EndingInter.java @@ -22,17 +22,18 @@ package org.audiveris.omr.sig.inter; import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.sig.BasicImpacts; import org.audiveris.omr.sig.GradeImpacts; import org.audiveris.omr.sig.relation.EndingSentenceRelation; import org.audiveris.omr.sig.relation.Relation; import org.audiveris.omr.text.TextRole; +import org.audiveris.omr.util.Jaxb; import java.awt.Rectangle; import java.awt.geom.Line2D; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * Class {@code EndingInter} represents an ending. @@ -49,23 +50,30 @@ public class EndingInter extends AbstractInter { - //~ Instance fields ---------------------------------------------------------------------------- + // Persistent Data + //---------------- + // /** Mandatory left leg. */ @XmlElement(name = "left-leg") + @XmlJavaTypeAdapter(Jaxb.Line2DAdapter.class) private final Line2D leftLeg; /** Horizontal line. */ @XmlElement + @XmlJavaTypeAdapter(Jaxb.Line2DAdapter.class) private final Line2D line; /** Optional right leg, if any. */ @XmlElement(name = "right-leg") + @XmlJavaTypeAdapter(Jaxb.Line2DAdapter.class) private final Line2D rightLeg; + // Transient Data + //--------------- + // private final SegmentInter segment; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new EndingInter object. * @@ -101,7 +109,6 @@ private EndingInter () this.rightLeg = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -188,6 +195,11 @@ public Line2D getLine () //-----------// // getNumber // //-----------// + /** + * Report the ending number clause, if any. + * + * @return ending number clause or null + */ public String getNumber () { for (Relation r : sig.getRelations(this, EndingSentenceRelation.class)) { @@ -217,6 +229,13 @@ public Line2D getRightLeg () //----------// // getValue // //----------// + /** + * The raw ending text, only if different from normalized number. + *

                                                                                                                      + * For instance, the actual text could be: "1., 2." and the normalized number: "1, 2" + * + * @return the raw ending text or null + */ public String getValue () { final String number = getNumber(); @@ -233,23 +252,22 @@ public String getValue () return null; } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Impacts // //---------// public static class Impacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{ - "straight", "slope", "length", "leftBar", - "rightBar" - }; + "straight", + "slope", + "length", + "leftBar", + "rightBar"}; private static final double[] WEIGHTS = new double[]{1, 1, 1, 1, 1}; - //~ Constructors --------------------------------------------------------------------------- public Impacts (double straight, double slope, double length, diff --git a/src/main/org/audiveris/omr/sig/inter/EnsembleHelper.java b/src/main/org/audiveris/omr/sig/inter/EnsembleHelper.java index 8c4138157..1bcc225c2 100644 --- a/src/main/org/audiveris/omr/sig/inter/EnsembleHelper.java +++ b/src/main/org/audiveris/omr/sig/inter/EnsembleHelper.java @@ -1,150 +1,153 @@ -//------------------------------------------------------------------------------------------------// -// // -// E n s e m b l e H e l p e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.inter; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.SigListener; -import org.audiveris.omr.sig.relation.BasicContainment; -import org.audiveris.omr.sig.relation.Relation; - -import org.jgrapht.event.GraphEdgeChangeEvent; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * Class {@code EnsembleHelper} gathers helping methods for ensembles. - * - * @author Hervé Bitteur - */ -public abstract class EnsembleHelper -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(EnsembleHelper.class); - - //~ Methods ------------------------------------------------------------------------------------ - //-----------// - // addMember // - //-----------// - /** - * Set the containment relationship between an (ensemble) instance and a (member) - * instance. - *

                                                                                                                      - * Both instances must already exist in SIG. - *

                                                                                                                      - * This will trigger {@link SigListener#edgeAdded(GraphEdgeChangeEvent) } in - * any SIG listener. - * - * @param ensemble the containing inter - * @param member the contained inter - */ - public static void addMember (InterEnsemble ensemble, - Inter member) - { - SIGraph sig = ensemble.getSig(); - sig.addEdge(ensemble, member, new BasicContainment()); - } - - //------------// - // getMembers // - //------------// - /** - * Report the sorted list of current member instances for a provided ensemble. - * - * @param ensemble the containing inter - * @param comparator the comparator to sort the list of members - * @return the members list, perhaps empty but not null - */ - public static List getMembers (InterEnsemble ensemble, - Comparator comparator) - { - SIGraph sig = ensemble.getSig(); - - if (sig == null) { - logger.debug("Ensemble#{} not in sig", ensemble.getId()); - - return Collections.EMPTY_LIST; - } - - List members = new ArrayList(); - - if (sig.containsVertex(ensemble)) { - for (Relation rel : sig.getRelations(ensemble, BasicContainment.class)) { - members.add(sig.getOppositeInter(ensemble, rel)); - } - - if (!members.isEmpty()) { - Collections.sort(members, comparator); - } - } - - return members; - } - - //----------------// - // linkOldMembers // - //----------------// - /** - * Convert old containment implementation (based on nesting) to new implementation - * based on explicit BasicContainment in SIG. - * - * @param ensemble the containing inter - * @param oldMembers the (unmarshalled) old list of nested members - */ - @Deprecated - public static void linkOldMembers (InterEnsemble ensemble, - List oldMembers) - { - if ((oldMembers != null) && !oldMembers.isEmpty()) { - for (Inter member : oldMembers) { - ensemble.addMember(member); - } - } - } - - //--------------// - // removeMember // - //--------------// - /** - * Remove the containment relationship between an (ensemble) instance and a (member) - * instance. - *

                                                                                                                      - * Both instances must exist in SIG. - *

                                                                                                                      - * This will trigger {@link SigListener#edgeRemoved(GraphEdgeChangeEvent)} in any SIG listener. - * - * @param ensemble the containing inter - * @param member the contained inter - */ - public static void removeMember (InterEnsemble ensemble, - Inter member) - { - SIGraph sig = ensemble.getSig(); - sig.removeEdge(ensemble, member); - } -} +//------------------------------------------------------------------------------------------------// +// // +// E n s e m b l e H e l p e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.inter; + +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.SigListener; +import org.audiveris.omr.sig.relation.Containment; +import org.audiveris.omr.sig.relation.Relation; + +import org.jgrapht.event.GraphEdgeChangeEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Class {@code EnsembleHelper} gathers helping methods for ensembles. + * + * @author Hervé Bitteur + */ +public abstract class EnsembleHelper +{ + + private static final Logger logger = LoggerFactory.getLogger(EnsembleHelper.class); + + /** Not meant to be instantiated. */ + private EnsembleHelper () + { + } + + //-----------// + // addMember // + //-----------// + /** + * Set the containment relationship between an (ensemble) instance and a (member) + * instance. + *

                                                                                                                      + * Both instances must already exist in SIG. + *

                                                                                                                      + * This will trigger {@link SigListener#edgeAdded(GraphEdgeChangeEvent) } in + * any SIG listener. + * + * @param ensemble the containing inter + * @param member the contained inter + */ + public static void addMember (InterEnsemble ensemble, + Inter member) + { + SIGraph sig = ensemble.getSig(); + sig.addEdge(ensemble, member, new Containment()); + } + + //------------// + // getMembers // + //------------// + /** + * Report the sorted list of current member instances for a provided ensemble. + * + * @param ensemble the containing inter + * @param comparator the comparator to sort the list of members + * @return the members list, perhaps empty but not null + */ + public static List getMembers (InterEnsemble ensemble, + Comparator comparator) + { + SIGraph sig = ensemble.getSig(); + + if (sig == null) { + logger.debug("Ensemble#{} not in sig", ensemble.getId()); + + return Collections.EMPTY_LIST; + } + + List members = new ArrayList<>(); + + if (sig.containsVertex(ensemble)) { + for (Relation rel : sig.getRelations(ensemble, Containment.class)) { + members.add(sig.getOppositeInter(ensemble, rel)); + } + + if (!members.isEmpty()) { + Collections.sort(members, comparator); + } + } + + return members; + } + + //----------------// + // linkOldMembers // + //----------------// + /** + * Convert old containment implementation (based on nesting) to new implementation + * based on explicit Containment in SIG. + * + * @param ensemble the containing inter + * @param oldMembers the (unmarshalled) old list of nested members + */ + @Deprecated + public static void linkOldMembers (InterEnsemble ensemble, + List oldMembers) + { + if ((oldMembers != null) && !oldMembers.isEmpty()) { + for (Inter member : oldMembers) { + ensemble.addMember(member); + } + } + } + + //--------------// + // removeMember // + //--------------// + /** + * Remove the containment relationship between an (ensemble) instance and a (member) + * instance. + *

                                                                                                                      + * Both instances must exist in SIG. + *

                                                                                                                      + * This will trigger {@link SigListener#edgeRemoved(GraphEdgeChangeEvent)} in any SIG listener. + * + * @param ensemble the containing inter + * @param member the contained inter + */ + public static void removeMember (InterEnsemble ensemble, + Inter member) + { + SIGraph sig = ensemble.getSig(); + sig.removeEdge(ensemble, member); + } +} diff --git a/src/main/org/audiveris/omr/sig/inter/FermataArcInter.java b/src/main/org/audiveris/omr/sig/inter/FermataArcInter.java index 6d815f7fa..e593a9f06 100644 --- a/src/main/org/audiveris/omr/sig/inter/FermataArcInter.java +++ b/src/main/org/audiveris/omr/sig/inter/FermataArcInter.java @@ -1,109 +1,107 @@ -//------------------------------------------------------------------------------------------------// -// // -// F e r m a t a A r c I n t e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.inter; - -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sheet.SystemInfo; - -import java.awt.Point; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code FermataArcInter} represents the arc part of a fermata, either upright or - * inverted. - *

                                                                                                                      - * Combined with a FermataDotInter, it can lead to a (full) FermataInter. - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "fermata-arc") -public class FermataArcInter - extends AbstractInter -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Creates a new {@code FermataArcInter} object. - * - * @param glyph the fermata arc glyph - * @param shape FERMATA_ARC or FERMATA_ARC_BELOW - * @param grade the interpretation quality - */ - private FermataArcInter (Glyph glyph, - Shape shape, - double grade) - { - super(glyph, glyph.getBounds(), shape, grade); - } - - /** - * No-arg constructor meant for JAXB. - */ - private FermataArcInter () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // accept // - //--------// - @Override - public void accept (InterVisitor visitor) - { - visitor.visit(this); - } - - //--------// - // create // - //--------// - /** - * (Try to) create a fermata arc inter. - * - * @param glyph the fermata arc glyph - * @param shape FERMATA_ARC or FERMATA_ARC_BELOW - * @param grade the interpretation quality - * @param system the related system - * @return the created fermata arc or null - */ - public static FermataArcInter create (Glyph glyph, - Shape shape, - double grade, - SystemInfo system) - { - // Look for proper staff - final Point center = glyph.getCenter(); - final Staff staff = (shape == Shape.FERMATA_ARC) ? system.getStaffAtOrBelow(center) - : system.getStaffAtOrAbove(center); - - if (staff == null) { - return null; - } - - final FermataArcInter arc = new FermataArcInter(glyph, shape, grade); - arc.setStaff(staff); - - return arc; - } -} +//------------------------------------------------------------------------------------------------// +// // +// F e r m a t a A r c I n t e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.inter; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.SystemInfo; + +import java.awt.Point; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code FermataArcInter} represents the arc part of a fermata, either upright or + * inverted. + *

                                                                                                                      + * Combined with a FermataDotInter, it can lead to a (full) FermataInter. + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "fermata-arc") +public class FermataArcInter + extends AbstractInter +{ + + /** + * Creates a new {@code FermataArcInter} object. + * + * @param glyph the fermata arc glyph + * @param shape FERMATA_ARC or FERMATA_ARC_BELOW + * @param grade the interpretation quality + */ + private FermataArcInter (Glyph glyph, + Shape shape, + double grade) + { + super(glyph, glyph.getBounds(), shape, grade); + } + + /** + * No-arg constructor meant for JAXB. + */ + private FermataArcInter () + { + } + + //--------// + // accept // + //--------// + @Override + public void accept (InterVisitor visitor) + { + visitor.visit(this); + } + + //--------// + // create // + //--------// + /** + * (Try to) create a fermata arc inter. + * + * @param glyph the fermata arc glyph + * @param shape FERMATA_ARC or FERMATA_ARC_BELOW + * @param grade the interpretation quality + * @param system the related system + * @return the created fermata arc or null + */ + public static FermataArcInter create (Glyph glyph, + Shape shape, + double grade, + SystemInfo system) + { + // Look for proper staff + final Point center = glyph.getCenter(); + final Staff staff = (shape == Shape.FERMATA_ARC) ? system.getStaffAtOrBelow(center) + : system.getStaffAtOrAbove(center); + + if (staff == null) { + return null; + } + + final FermataArcInter arc = new FermataArcInter(glyph, shape, grade); + arc.setStaff(staff); + + return arc; + } +} diff --git a/src/main/org/audiveris/omr/sig/inter/FermataDotInter.java b/src/main/org/audiveris/omr/sig/inter/FermataDotInter.java index 5ad7d7afd..9b3d5bc50 100644 --- a/src/main/org/audiveris/omr/sig/inter/FermataDotInter.java +++ b/src/main/org/audiveris/omr/sig/inter/FermataDotInter.java @@ -23,6 +23,7 @@ import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.sheet.Staff; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -38,7 +39,6 @@ public class FermataDotInter extends AbstractInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code FermataDotInter} object. @@ -59,7 +59,6 @@ private FermataDotInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -68,4 +67,22 @@ public void accept (InterVisitor visitor) { visitor.visit(this); } + + //----------// + // getStaff // + //----------// + @Override + public Staff getStaff () + { + if (staff == null) { + // Use staff information of containing FermataInter ensemble + InterEnsemble ens = getEnsemble(); + + if (ens != null) { + staff = ens.getStaff(); + } + } + + return staff; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/FermataInter.java b/src/main/org/audiveris/omr/sig/inter/FermataInter.java index c02b0d988..4c6f3f5b5 100644 --- a/src/main/org/audiveris/omr/sig/inter/FermataInter.java +++ b/src/main/org/audiveris/omr/sig/inter/FermataInter.java @@ -27,9 +27,11 @@ import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sheet.rhythm.MeasureStack; import org.audiveris.omr.sig.SIGraph; import org.audiveris.omr.sig.relation.FermataBarRelation; import org.audiveris.omr.sig.relation.FermataChordRelation; +import org.audiveris.omr.sig.relation.Link; import org.audiveris.omr.util.Entities; import org.slf4j.Logger; @@ -38,6 +40,7 @@ import java.awt.Point; import java.awt.Rectangle; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -47,7 +50,8 @@ * Class {@code FermataInter} represents a full fermata interpretation, either upright * or inverted, combining an arc and a dot. *

                                                                                                                      - * Urlinie in G with fermata on penultimate note *

                                                                                                                      * An upright fermata refers to the chord in the staff right below in the containing part. @@ -64,7 +68,6 @@ public class FermataInter extends AbstractInter implements InterEnsemble { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -80,7 +83,6 @@ public int compare (Inter o1, } }; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code FermataInter} object. * @@ -100,7 +102,6 @@ private FermataInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -110,47 +111,6 @@ public void accept (InterVisitor visitor) visitor.visit(this); } - //-------------// - // createAdded // - //-------------// - /** - * (Try to) create a fermata inter. - * - * @param arc the fermata arc (shape: FERMATA_ARC or FERMATA_ARC_BELOW) - * @param dot the fermata dot - * @param system the related system - * @return the created instance or null - */ - public static FermataInter createAdded (FermataArcInter arc, - FermataDotInter dot, - SystemInfo system) - { - // Look for proper staff - final Point center = arc.getGlyph().getCenter(); - final Shape arcShape = arc.getShape(); - final Staff staff = (arcShape == Shape.FERMATA_ARC) ? system.getStaffAtOrBelow(center) - : system.getStaffAtOrAbove(center); - - if (staff == null) { - return null; - } - - final SIGraph sig = arc.getSig(); - sig.computeContextualGrade(arc); - sig.computeContextualGrade(dot); - - final double grade = 0.5 * (arc.getContextualGrade() + dot.getContextualGrade()); - final Shape shape = (arcShape == Shape.FERMATA_ARC) ? Shape.FERMATA - : Shape.FERMATA_BELOW; - final FermataInter fermata = new FermataInter(shape, grade); - fermata.setStaff(staff); - sig.addVertex(fermata); - fermata.addMember(arc); - fermata.addMember(dot); - - return fermata; - } - //-----------// // addMember // //-----------// @@ -181,6 +141,11 @@ public void addMember (Inter member) //--------// // getArc // //--------// + /** + * Report the arc part. + * + * @return the arc + */ public FermataArcInter getArc () { final List members = getMembers(); @@ -210,6 +175,11 @@ public Rectangle getBounds () //--------// // getDot // //--------// + /** + * Report the dot part. + * + * @return the dot + */ public FermataDotInter getDot () { final List members = getMembers(); @@ -251,24 +221,10 @@ public void invalidateCache () */ public boolean linkWithBarline () { - Point center = getCenter(); - List staffBars = getStaff().getStaffBarlines(); - StaffBarlineInter staffBar = StaffBarlineInter.getClosestStaffBarline(staffBars, center); - - if ((staffBar != null) && (GeoUtil.xOverlap(getBounds(), staffBar.getBounds()) > 0)) { - // Check vertical distance to bar/staff - final Scale scale = sig.getSystem().getSheet().getScale(); - final int maxDy = scale.toPixels(constants.maxFermataDy); - final int dyStaff = staff.distanceTo(center); + Link link = lookupBarlineLink(sig.getSystem()); - if (dyStaff > maxDy) { - logger.debug("{} too far from barline: {}", this, staffBar); - - return false; - } - - // For fermata & for staffbar - sig.addEdge(this, staffBar, new FermataBarRelation()); + if (link != null) { + link.applyTo(this); return true; } @@ -276,55 +232,20 @@ public boolean linkWithBarline () return false; } - //----------------// - // linkWithChords // - //----------------// + //---------------// + // linkWithChord // + //---------------// /** * (Try to) connect this fermata with suitable chord. * - * @param chords the chords in fermata related staff, constrained by fermata x range * @return true if successful */ - public boolean linkWithChords (Collection chords) + public boolean linkWithChord () { - // Look for a suitable chord related to this fermata - Point center = getCenter(); - AbstractChordInter chord = AbstractChordInter.getClosestChord(chords, center); + Link link = lookupChordLink(sig.getSystem()); - if (chord != null) { - double dyChord = Math.sqrt(GeoUtil.ptDistanceSq(chord.getBounds(), center.x, center.y)); - - // If chord is mirrored, select the closest vertically - if (chord.getMirror() != null) { - double dyMirror = Math.sqrt( - GeoUtil.ptDistanceSq(chord.getMirror().getBounds(), center.x, center.y)); - - if (dyMirror < dyChord) { - dyChord = dyMirror; - chord = (AbstractChordInter) chord.getMirror(); - logger.debug("{} selecting mirror {}", this, chord); - } - } - - // Check vertical distance between fermata and chord - final Scale scale = sig.getSystem().getSheet().getScale(); - final int maxDy = scale.toPixels(constants.maxFermataDy); - - if (dyChord > maxDy) { - // Check vertical distance between fermata and staff - final Staff chordStaff = (shape == Shape.FERMATA) ? chord.getTopStaff() - : chord.getBottomStaff(); - final int dyStaff = chordStaff.distanceTo(center); - - if (dyStaff > maxDy) { - logger.debug("{} too far from staff/chord: {}", this, dyStaff); - - return false; - } - } - - // For fermata & for chord - sig.addEdge(this, chord, new FermataChordRelation()); + if (link != null) { + link.applyTo(this); return true; } @@ -346,6 +267,34 @@ public void removeMember (Inter member) EnsembleHelper.removeMember(this, member); } + //-------------// + // searchLinks // + //-------------// + @Override + public Collection searchLinks (SystemInfo system, + boolean doit) + { + // Not very optimized! + List systemHeadChords = system.getSig().inters(HeadChordInter.class); + Collections.sort(systemHeadChords, Inters.byAbscissa); + + Link link = lookupBarlineLink(system); + + if (link == null) { + link = lookupChordLink(system); + } + + if (link == null) { + return Collections.emptyList(); + } + + if (doit) { + link.applyTo(this); + } + + return Collections.singleton(link); + } + //-----------// // internals // //-----------// @@ -355,14 +304,155 @@ protected String internals () return super.internals() + " " + shape; } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------------// + // lookupBarlineLink // + //-------------------// + /** + * Look for a suitable link with a staff barline. + * + * @param system containing system + * @return suitable link or null + */ + private Link lookupBarlineLink (SystemInfo system) + { + final Point center = getCenter(); + + if (staff == null) { + staff = (shape == Shape.FERMATA_BELOW) ? system.getStaffAtOrAbove(center) + : system.getStaffAtOrBelow(center); + } + + if (staff == null) { + return null; + } + + List staffBars = staff.getStaffBarlines(); + StaffBarlineInter staffBar = StaffBarlineInter.getClosestStaffBarline(staffBars, center); + + // Fermata and staff bar bounds must intersect horizontally + if (staffBar == null || GeoUtil.xOverlap(getBounds(), staffBar.getBounds()) <= 0) { + return null; + } + + // Check vertical distance to bar/staff + final Scale scale = system.getSheet().getScale(); + final int maxDy = scale.toPixels(constants.maxFermataDy); + final int dyStaff = staff.distanceTo(center); + + if (dyStaff > maxDy) { + logger.debug("{} too far from barline: {}", this, staffBar); + + return null; + } + + return new Link(staffBar, new FermataBarRelation(), true); + } + + //-----------------// + // lookupChordLink // + //-----------------// + /** + * Look for a suitable link with a standard chord (head or rest). + * + * @param system containing system + * @return suitable link or null + */ + private Link lookupChordLink (SystemInfo system) + { + getBounds(); + final Point center = getCenter(); + final MeasureStack stack = system.getStackAt(center); + final Collection chords = (shape == Shape.FERMATA_BELOW) ? stack + .getStandardChordsAbove(center, bounds) + : stack.getStandardChordsBelow(center, bounds); + + // Look for a suitable chord related to this fermata + AbstractChordInter chord = AbstractChordInter.getClosestChord(chords, center); + + if (chord == null) { + return null; + } + + double dyChord = Math.sqrt(GeoUtil.ptDistanceSq(chord.getBounds(), center.x, center.y)); + + // If chord is mirrored, select the closest vertically + if (chord.getMirror() != null) { + double dyMirror = Math.sqrt( + GeoUtil.ptDistanceSq(chord.getMirror().getBounds(), center.x, center.y)); + + if (dyMirror < dyChord) { + dyChord = dyMirror; + chord = (AbstractChordInter) chord.getMirror(); + logger.debug("{} selecting mirror {}", this, chord); + } + } + + // Check vertical distance between fermata and chord + final Scale scale = system.getSheet().getScale(); + final int maxDy = scale.toPixels(constants.maxFermataDy); + + if (dyChord > maxDy) { + // Check vertical distance between fermata and staff + final Staff chordStaff = (shape == Shape.FERMATA) ? chord.getTopStaff() + : chord.getBottomStaff(); + final int dyStaff = chordStaff.distanceTo(center); + + if (dyStaff > maxDy) { + logger.debug("{} too far from staff/chord: {}", this, dyStaff); + + return null; + } + } + + return new Link(chord, new FermataChordRelation(), true); + } + + //-------------// + // createAdded // + //-------------// + /** + * (Try to) create a fermata inter. + * + * @param arc the fermata arc (shape: FERMATA_ARC or FERMATA_ARC_BELOW) + * @param dot the fermata dot + * @param system the related system + * @return the created instance or null + */ + public static FermataInter createAdded (FermataArcInter arc, + FermataDotInter dot, + SystemInfo system) + { + // Look for proper staff + final Point center = arc.getGlyph().getCenter(); + final Shape arcShape = arc.getShape(); + final Staff staff = (arcShape == Shape.FERMATA_ARC) ? system.getStaffAtOrBelow(center) + : system.getStaffAtOrAbove(center); + + if (staff == null) { + return null; + } + + final SIGraph sig = arc.getSig(); + sig.computeContextualGrade(arc); + sig.computeContextualGrade(dot); + + final double grade = 0.5 * (arc.getContextualGrade() + dot.getContextualGrade()); + final Shape shape = (arcShape == Shape.FERMATA_ARC) ? Shape.FERMATA : Shape.FERMATA_BELOW; + final FermataInter fermata = new FermataInter(shape, grade); + fermata.setStaff(staff); + sig.addVertex(fermata); + fermata.addMember(arc); + fermata.addMember(dot); + + return fermata; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxFermataDy = new Scale.Fraction( 2.5, diff --git a/src/main/org/audiveris/omr/sig/inter/FingeringInter.java b/src/main/org/audiveris/omr/sig/inter/FingeringInter.java index 1d1f81c63..459b03756 100644 --- a/src/main/org/audiveris/omr/sig/inter/FingeringInter.java +++ b/src/main/org/audiveris/omr/sig/inter/FingeringInter.java @@ -36,12 +36,10 @@ public class FingeringInter extends AbstractInter implements StringSymbolInter { - //~ Instance fields ---------------------------------------------------------------------------- /** Integer value for the number. (0, 1, 2, 3, 4) */ private final int value; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new FingeringInter object. * @@ -65,7 +63,6 @@ private FingeringInter () this.value = 0; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/FlagInter.java b/src/main/org/audiveris/omr/sig/inter/FlagInter.java index 43df917d3..8ec741c17 100644 --- a/src/main/org/audiveris/omr/sig/inter/FlagInter.java +++ b/src/main/org/audiveris/omr/sig/inter/FlagInter.java @@ -41,7 +41,6 @@ public class FlagInter extends AbstractFlagInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new FlagInter object. @@ -64,7 +63,6 @@ protected FlagInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/FretInter.java b/src/main/org/audiveris/omr/sig/inter/FretInter.java index 67dc530bb..6110297d7 100644 --- a/src/main/org/audiveris/omr/sig/inter/FretInter.java +++ b/src/main/org/audiveris/omr/sig/inter/FretInter.java @@ -36,12 +36,10 @@ public class FretInter extends AbstractInter implements StringSymbolInter { - //~ Instance fields ---------------------------------------------------------------------------- /** Fret value. */ private final int value; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code FretInter} object. * @@ -65,7 +63,6 @@ private FretInter () this.value = 0; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/HeadChordInter.java b/src/main/org/audiveris/omr/sig/inter/HeadChordInter.java index 635d7474e..021843661 100644 --- a/src/main/org/audiveris/omr/sig/inter/HeadChordInter.java +++ b/src/main/org/audiveris/omr/sig/inter/HeadChordInter.java @@ -22,11 +22,14 @@ package org.audiveris.omr.sig.inter; import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.sig.relation.ChordArpeggiatoRelation; +import org.audiveris.omr.sig.relation.ChordArticulationRelation; import org.audiveris.omr.sig.relation.ChordStemRelation; -import org.audiveris.omr.sig.relation.BasicContainment; +import org.audiveris.omr.sig.relation.Containment; import org.audiveris.omr.sig.relation.FlagStemRelation; import org.audiveris.omr.sig.relation.HeadStemRelation; import org.audiveris.omr.sig.relation.Relation; +import org.audiveris.omr.sig.relation.SlurHeadRelation; import org.audiveris.omr.util.Entities; import org.audiveris.omr.util.HorizontalSide; @@ -35,6 +38,8 @@ import java.awt.Point; import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -46,7 +51,7 @@ * Class {@code HeadChordInter} is a AbstractChordInter composed of heads and possibly * a stem. *

                                                                                                                      - * Heads are linked via {@link BasicContainment} relation and stem via {@link ChordStemRelation}. + * Heads are linked via {@link Containment} relation and stem via {@link ChordStemRelation}. * * @author Hervé Bitteur */ @@ -54,14 +59,13 @@ public class HeadChordInter extends AbstractChordInter { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - HeadChordInter.class); + private static final Logger logger = LoggerFactory.getLogger(HeadChordInter.class); /** - * Compare two heads (assumed to be) of the same chord, ordered by - * increasing distance from chord head ordinate. + * Compare two heads (assumed to be) of the same chord, ordered by increasing + * distance from chord head ordinate. + * It implements total ordering. */ public static final Comparator headComparator = new Comparator() { @@ -73,13 +77,21 @@ public int compare (HeadInter n1, return 0; } - AbstractChordInter c1 = n1.getChord(); + final Point p1 = n1.getCenter(); + final Point p2 = n2.getCenter(); + final int yCmp = Integer.compare(p1.y, p2.y); - return c1.getStemDir() * (n1.getCenter().y - n2.getCenter().y); + if (yCmp != 0) { + final AbstractChordInter chord = n1.getChord(); + + return chord.getStemDir() * yCmp; + } + + // Total ordering: use abscissa to separate heads with identical ordinates (rare case) + return Integer.compare(p1.x, p2.x); } }; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code HeadChordInter} object. * @@ -97,7 +109,6 @@ protected HeadChordInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -137,9 +148,7 @@ public HeadChordInter duplicate (boolean toBlack) { // Beams are not copied HeadChordInter clone = new HeadChordInter(getGrade()); - clone.setMirror(this); sig.addVertex(clone); - setMirror(clone); clone.setStaff(staff); @@ -151,11 +160,15 @@ public HeadChordInter duplicate (boolean toBlack) switch (head.getShape()) { case NOTEHEAD_BLACK: newHead = head.duplicate(); + sig.addVertex(newHead); + newHead.setMirror(head); break; case NOTEHEAD_VOID: newHead = toBlack ? head.duplicateAs(Shape.NOTEHEAD_BLACK) : head.duplicate(); + sig.addVertex(newHead); + newHead.setMirror(head); break; @@ -173,6 +186,50 @@ public HeadChordInter duplicate (boolean toBlack) return clone; } + //---------------// + // getArpeggiato // + //---------------// + /** + * Report the chord arpeggiato if any. + * + * @return related arpeggiato or null + */ + public ArpeggiatoInter getArpeggiato () + { + for (Relation rel : sig.getRelations(this, ChordArpeggiatoRelation.class)) { + return ((ArpeggiatoInter) sig.getOppositeInter(this, rel)); + } + + return null; + } + + //------------------// + // getArticulations // + //------------------// + /** + * Report the chord articulations if any. + * + * @return list of articulations, perhaps empty + */ + public List getArticulations () + { + List found = null; + + for (Relation rel : sig.getRelations(this, ChordArticulationRelation.class)) { + if (found == null) { + found = new ArrayList<>(); + } + + found.add((ArticulationInter) sig.getOppositeInter(this, rel)); + } + + if (found == null) { + return Collections.EMPTY_LIST; + } + + return found; + } + //-----------// // getBounds // //-----------// @@ -244,6 +301,30 @@ public int getFlagsNumber () return count; } + //---------------// + // getGraceChord // + //---------------// + /** + * Report the grace chord, if any, which is linked on left side of this chord. + * + * @return the linked grace chord if any + */ + public SmallChordInter getGraceChord () + { + for (Inter interNote : getNotes()) { + for (Relation rel : sig.getRelations(interNote, SlurHeadRelation.class)) { + SlurInter slur = (SlurInter) sig.getOppositeInter(interNote, rel); + HeadInter head = slur.getHead(HorizontalSide.LEFT); + + if ((head != null) && head.getShape().isSmall()) { + return (SmallChordInter) head.getChord(); + } + } + } + + return null; + } + //----------------// // getHeadsBounds // //----------------// @@ -340,6 +421,30 @@ public HeadInter getLeadingNote () } } + //-----------// + // getMirror // + //-----------// + /** + * {@inheritDoc} + *

                                                                                                                      + * For a HeadChord, we check for a head member with a mirror. + * + * @return the "mirrored" chord if any + */ + @Override + public HeadChordInter getMirror () + { + for (Inter inter : getNotes()) { + HeadInter mirrorHead = (HeadInter) inter.getMirror(); + + if (mirrorHead != null) { + return mirrorHead.getChord(); + } + } + + return null; + } + //---------// // getStem // //---------// @@ -364,6 +469,25 @@ public StemInter getStem () return null; } + //---------// + // setStem // + //---------// + /** + * Set the stem of this head chord. + * + * @param stem the stem to set + */ + public final void setStem (StemInter stem) + { + Objects.requireNonNull(sig, "Chord not in SIG."); + + Relation rel = sig.getRelation(this, stem, ChordStemRelation.class); + + if (rel == null) { + sig.addEdge(this, stem, new ChordStemRelation()); + } + } + //------------// // getStemDir // //------------// @@ -382,19 +506,6 @@ public int getStemDir () } } - //---------// - // setStem // - //---------// - /** - * @param stem the stem to set - */ - public final void setStem (StemInter stem) - { - Objects.requireNonNull(sig, "Chord not in SIG."); - - sig.addEdge(this, stem, new ChordStemRelation()); - } - //-------------// // shapeString // //-------------// diff --git a/src/main/org/audiveris/omr/sig/inter/HeadInter.java b/src/main/org/audiveris/omr/sig/inter/HeadInter.java index ff66cc370..27408b7a0 100644 --- a/src/main/org/audiveris/omr/sig/inter/HeadInter.java +++ b/src/main/org/audiveris/omr/sig/inter/HeadInter.java @@ -25,7 +25,6 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.glyph.BasicGlyph; import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.glyph.ShapeSet; @@ -44,7 +43,6 @@ import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sheet.SystemInfo; import org.audiveris.omr.sheet.rhythm.Measure; -import org.audiveris.omr.sig.BasicImpacts; import org.audiveris.omr.sig.GradeImpacts; import org.audiveris.omr.sig.relation.AlterHeadRelation; import org.audiveris.omr.sig.relation.HeadStemRelation; @@ -56,12 +54,14 @@ import org.audiveris.omr.util.HorizontalSide; import static org.audiveris.omr.util.HorizontalSide.*; import static org.audiveris.omr.util.VerticalSide.*; +import org.audiveris.omr.util.Jaxb; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Point; import java.awt.Rectangle; +import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; @@ -78,6 +78,7 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * Class {@code HeadInter} represents a note head, that is any head shape including @@ -92,19 +93,17 @@ public class HeadInter extends AbstractNoteInter { - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(HeadInter.class); private static final Constants constants = new Constants(); - //~ Instance fields ---------------------------------------------------------------------------- - // + private static final Logger logger = LoggerFactory.getLogger(HeadInter.class); + // Persistent data //---------------- // /** Absolute location of head template pivot. */ @XmlElement + @XmlJavaTypeAdapter(Jaxb.PointAdapter.class) private final Point pivot; /** Relative pivot position WRT head. */ @@ -117,7 +116,6 @@ public class HeadInter /** Shape template descriptor. */ private ShapeDescriptor descriptor; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code HeadInter} object. * @@ -173,7 +171,6 @@ private HeadInter () this.anchor = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -210,9 +207,33 @@ public boolean checkAbnormal () return isAbnormal(); } + //----------// + // contains // + //----------// + @Override + public boolean contains (Point point) + { + if (!super.contains(point)) { + return false; + } + + Line2D midLine = getMidLine(); + + if (midLine != null) { + return midLine.relativeCCW(point) < 0; + } + + return true; + } + //-----------// // duplicate // //-----------// + /** + * Duplicate this head, keeping the same head shape (black vs void). + * + * @return a duplicate head + */ public HeadInter duplicate () { return duplicateAs(shape); @@ -221,19 +242,24 @@ public HeadInter duplicate () //-------------// // duplicateAs // //-------------// + /** + * Build a duplicate of this head, perhaps with a different shape. + *

                                                                                                                      + * Used when say a half head is shared, with flag/beam on one side. + * + * @param shape precise shape for the duplicate + * @return duplicate + */ public HeadInter duplicateAs (Shape shape) { HeadInter clone = new HeadInter(pivot, anchor, bounds, shape, impacts, staff, pitch); + clone.setGlyph(this.glyph); - clone.setMirror(this); if (impacts == null) { - clone.setGrade(this.grade); + clone.setGrade(grade); } - sig.addVertex(clone); - setMirror(clone); - return clone; } @@ -254,9 +280,9 @@ public AlterInter getAccidental () return null; } - //----------// - // getAlter // - //----------// + //---------------// + // getAlteration // + //---------------// /** * Report the actual alteration of this note, taking into account the accidental of * this note if any, the accidental of previous note with same step within the same @@ -265,14 +291,130 @@ public AlterInter getAccidental () * @param fifths fifths value for current key signature * @return the actual alteration */ - public int getAlter (Integer fifths) + public int getAlteration (Integer fifths) + { + return HeadInter.this.getAlteration(fifths, true); + } + + //---------------// + // getAlteration // + //---------------// + /** + * Report the actual alteration of this note, taking into account the accidental of + * this note if any, the accidental of previous note with same step within the same + * measure, a tie from previous measure and finally the current key signature. + * + * @param fifths fifths value for current key signature + * @param useTie true to use tie for check + * @return the actual alteration + */ + public int getAlteration (Integer fifths, + boolean useTie) + { + // Look for local/measure accidental + AlterInter accidental = getMeasureAccidental(); + + if (accidental != null) { + return AlterInter.alterationOf(accidental); + } + + if (useTie) { + // Look for tie from previous measure (same system or previous system) + for (Relation rel : sig.getRelations(this, SlurHeadRelation.class)) { + SlurInter slur = (SlurInter) sig.getOppositeInter(this, rel); + + if (slur.isTie() && (slur.getHead(HorizontalSide.RIGHT) == this)) { + // Is the starting head in same system? + HeadInter startHead = slur.getHead(HorizontalSide.LEFT); + + if (startHead != null) { + // Use start head alter + return startHead.getAlteration(fifths); + } + + // Use slur extension to look into previous system + SlurInter prevSlur = slur.getExtension(HorizontalSide.LEFT); + + if (prevSlur != null) { + startHead = prevSlur.getHead(HorizontalSide.LEFT); + + if (startHead != null) { + // Use start head alter + return startHead.getAlteration(fifths); + } + } + + // TODO: Here we should look in previous sheet/page... + } + } + } + + // Finally, use the current key signature + if (fifths != null) { + return KeyInter.getAlterFor(getStep(), fifths); + } + + // Nothing found, so... + return 0; + } + + //----------------------// + // getMeasureAccidental // + //----------------------// + /** + * Report the accidental (if any) which applies to this head or to a previous + * compatible one in the same measure. + * + * @return the measure scoped accidental found or null + */ + public AlterInter getMeasureAccidental () { - return getAlter(fifths, true); + // Look for local accidental + AlterInter accidental = getAccidental(); + + if (accidental != null) { + return accidental; + } + + // Look for previous accidental with same note step in the same measure + // Let's avoid the use of time slots (which would require RHYTHMS step to be done!) + Measure measure = getChord().getMeasure(); + List heads = new ArrayList<>(); + + for (HeadChordInter headChord : measure.getHeadChords()) { + heads.addAll(headChord.getMembers()); + } + + boolean started = false; + Collections.sort(heads, Inters.byReverseCenterAbscissa); + + for (Inter inter : heads) { + HeadInter head = (HeadInter) inter; + + if (head == this) { + started = true; + } else if (started && (head.getStep() == getStep()) + && (head.getOctave() == getOctave()) + && (head.getStaff() == getStaff())) { + accidental = head.getAccidental(); + + if (accidental != null) { + return accidental; + } + } + } + + return null; } //----------// // getChord // //----------// + /** + * Report the containing (head) chord, if any. + * + * @return containing chord or null + */ @Override public HeadChordInter getChord () { @@ -291,6 +433,11 @@ public Rectangle2D getCoreBounds () //---------------// // getDescriptor // //---------------// + /** + * Report the descriptor used to generate this head shape with proper size. + * + * @return related template descriptor + */ public ShapeDescriptor getDescriptor () { if (descriptor == null) { @@ -301,20 +448,78 @@ public ShapeDescriptor getDescriptor () return descriptor; } - //--------------------// - // getShrinkHoriRatio // - //--------------------// - public static double getShrinkHoriRatio () + //------------// + // getMidLine // + //------------// + /** + * Report the separating line for shared heads. + *

                                                                                                                      + * The line is nearly vertical, oriented from head to stem. + * Thus, the relativeCCW is negative for points located on proper head half. + * + * @return oriented middle line or null if not mirrored + */ + public Line2D getMidLine () { - return constants.shrinkHoriRatio.getValue(); + if (getMirror() == null) { + return null; + } + + final Rectangle box = getBounds(); + + for (Relation relation : sig.getRelations(this, HeadStemRelation.class)) { + final HeadStemRelation rel = (HeadStemRelation) relation; + + if (rel.getHeadSide() == HorizontalSide.LEFT) { + return LineUtil.bisector( + new Point(box.x, box.y + box.height), + new Point(box.x + box.width, box.y)); + } else { + return LineUtil.bisector( + new Point(box.x + box.width, box.y), + new Point(box.x, box.y + box.height)); + } + } + + return null; } - //--------------------// - // getShrinkVertRatio // - //--------------------// - public static double getShrinkVertRatio () + //-------------------// + // getRelationCenter // + //-------------------// + /** + * {@inheritDoc} + *

                                                                                                                      + * For shared heads, the relation center is slightly shifted to the containing chord. + * + * @return the head relation center, shifted for a shared head + */ + @Override + public Point getRelationCenter () { - return constants.shrinkVertRatio.getValue(); + final Point center = getCenter(); + + if (getMirror() == null) { + return center; + } + + final Rectangle box = getBounds(); + final int dx = box.width / 5; + final int dy = box.height / 5; + + for (Relation relation : sig.getRelations(this, HeadStemRelation.class)) { + final HeadStemRelation rel = (HeadStemRelation) relation; + + if (rel.getHeadSide() == HorizontalSide.LEFT) { + center.translate(-dx, +dy); + } else { + center.translate(+dx, -dy); + } + + return center; + } + + return center; // Should not occur... } //--------------// @@ -329,8 +534,7 @@ public static double getShrinkVertRatio () public Map> getSideStems () { // Split connected stems into left and right sides - final Map> map = new EnumMap>( - HorizontalSide.class); + final Map> map = new EnumMap<>(HorizontalSide.class); for (Relation relation : sig.getRelations(this, HeadStemRelation.class)) { HeadStemRelation rel = (HeadStemRelation) relation; @@ -338,7 +542,7 @@ public Map> getSideStems () Set set = map.get(side); if (set == null) { - map.put(side, set = new LinkedHashSet()); + map.put(side, set = new LinkedHashSet<>()); } set.add((StemInter) sig.getEdgeTarget(rel)); @@ -380,7 +584,7 @@ public Point2D getStemReferencePoint (Anchor anchor, */ public Set getStems () { - final Set set = new LinkedHashSet(); + final Set set = new LinkedHashSet<>(); for (Relation relation : sig.getRelations(this, HeadStemRelation.class)) { set.add((StemInter) sig.getEdgeTarget(relation)); @@ -389,56 +593,6 @@ public Set getStems () return set; } - //----------------// - // haveSameHeight // - //----------------// - /** - * Check whether two heads represent the same height - * (same octave, same step, same alteration). - * - * @param h1 first head - * @param h2 second head, down the score - * @return true if the heads are equivalent. - */ - public static boolean haveSameHeight (HeadInter h1, - HeadInter h2) - { - if ((h1 == null) || (h2 == null)) { - return false; - } - - // Step - if (h1.getStep() != h2.getStep()) { - return false; - } - - // Octave - if (h1.getOctave() != h2.getOctave()) { - return false; - } - - // Alteration - Staff s1 = h1.getStaff(); - Measure m1 = s1.getPart().getMeasureAt(h1.getCenter()); - KeyInter k1 = m1.getKeyBefore(s1); - int f1 = (k1 != null) ? k1.getFifths() : 0; - - Staff s2 = h2.getStaff(); - Measure m2 = s2.getPart().getMeasureAt(h2.getCenter()); - KeyInter k2 = m2.getKeyBefore(s2); - int f2 = (k2 != null) ? k2.getFifths() : 0; - - if (m1 == m2) { - // Both heads are in same measure - return (f1 == f2) && (h1.getStaff() == h2.staff); - } else { - int a1 = h1.getAlter(f1, false); - int a2 = h2.getAlter(f2, false); - - return a1 == a2; - } - } - //----------// // overlaps // //----------// @@ -459,7 +613,7 @@ public boolean overlaps (Inter that) // Specific between notes if (that instanceof HeadInter) { if (this.isVip() && ((HeadInter) that).isVip()) { - //logger.info("AbstractHeadInter checking overlaps between {} and {}", this, that); + logger.info("HeadInter checking overlaps between {} and {}", this, that); } HeadInter thatHead = (HeadInter) that; @@ -490,8 +644,10 @@ public boolean overlaps (Inter that) int minArea = Math.min(thisArea, thatArea); int commonArea = common.width * common.height; double areaRatio = (double) commonArea / minArea; - boolean res = (common.width > (constants.maxOverlapDxRatio.getValue() * thisBounds.width)) - && (areaRatio > constants.maxOverlapAreaRatio.getValue()); + boolean res = (common.width > (constants.maxOverlapDxRatio.getValue() + * thisBounds.width)) && (areaRatio + > constants.maxOverlapAreaRatio + .getValue()); return res; @@ -559,7 +715,7 @@ public Glyph retrieveGlyph (ByteProcessor image) // Glyph glyph = sheet.getGlyphIndex().registerOriginal( - new BasicGlyph(descBox.x + foreBox.x, descBox.y + foreBox.y, runTable)); + new Glyph(descBox.x + foreBox.x, descBox.y + foreBox.y, runTable)); // Use glyph bounds as inter bounds bounds = glyph.getBounds(); @@ -600,27 +756,6 @@ public Collection searchLinks (SystemInfo system, return Collections.emptyList(); } - //--------// - // shrink // - //--------// - /** - * Shrink a bit a bounding bounds when checking for note overlap. - * - * @param box the bounding bounds - * @return the shrunk bounds - */ - public static Rectangle2D shrink (Rectangle box) - { - double newWidth = constants.shrinkHoriRatio.getValue() * box.width; - double newHeight = constants.shrinkVertRatio.getValue() * box.height; - - return new Rectangle2D.Double( - box.getCenterX() - (newWidth / 2.0), - box.getCenterY() - (newHeight / 2.0), - newWidth, - newHeight); - } - //-----------// // internals // //-----------// @@ -630,43 +765,6 @@ protected String internals () return super.internals() + " " + shape; } - //--------------// - // alterationOf // - //--------------// - /** - * Report the pitch alteration that corresponds to the provided accidental. - * - * @param accidental the provided accidental - * @return the pitch impact - */ - private int alterationOf (AlterInter accidental) - { - switch (accidental.getShape()) { - case SHARP: - return 1; - - case DOUBLE_SHARP: - return 2; - - case FLAT: - return -1; - - case DOUBLE_FLAT: - return -2; - - case NATURAL: - return 0; - - default: - logger.warn( - "Weird shape {} for accidental {}", - accidental.getShape(), - accidental.getId()); - - return 0; // Should not happen - } - } - //------------------// // fixDuplicateWith // //------------------// @@ -700,97 +798,6 @@ private void fixDuplicateWith (HeadInter that) // Perhaps check for a weak ledger, tangent to the note towards staff } - //----------// - // getAlter // - //----------// - /** - * Report the actual alteration of this note, taking into account the accidental of - * this note if any, the accidental of previous note with same step within the same - * measure, a tie from previous measure and finally the current key signature. - * - * @param fifths fifths value for current key signature - * @param useTie true to use tie for check - * @return the actual alteration - */ - private int getAlter (Integer fifths, - boolean useTie) - { - // Look for local accidental - AlterInter accidental = getAccidental(); - - if (accidental != null) { - return alterationOf(accidental); - } - - // Look for previous accidental with same note step in the same measure - // Let's avoid the use of time slots (which would require RHYTHMS step to be done!) - Measure measure = getChord().getMeasure(); - List heads = new ArrayList(); - - for (HeadChordInter headChord : measure.getHeadChords()) { - heads.addAll(headChord.getMembers()); - } - - boolean started = false; - Collections.sort(heads, Inters.byReverseCenterAbscissa); - - for (Inter inter : heads) { - HeadInter head = (HeadInter) inter; - - if (head == this) { - started = true; - } else if (started - && (head.getStep() == getStep()) - && (head.getOctave() == getOctave()) - && (head.getStaff() == getStaff())) { - AlterInter accid = head.getAccidental(); - - if (accid != null) { - return alterationOf(accid); - } - } - } - - if (useTie) { - // Look for tie from previous measure (same system or previous system) - for (Relation rel : sig.getRelations(this, SlurHeadRelation.class)) { - SlurInter slur = (SlurInter) sig.getOppositeInter(this, rel); - - if (slur.isTie() && (slur.getHead(HorizontalSide.RIGHT) == this)) { - // Is the starting head in same system? - HeadInter startHead = slur.getHead(HorizontalSide.LEFT); - - if (startHead != null) { - // Use start head alter - return startHead.getAlter(fifths); - } - - // Use slur extension to look into previous system - SlurInter prevSlur = slur.getExtension(HorizontalSide.LEFT); - - if (prevSlur != null) { - startHead = prevSlur.getHead(HorizontalSide.LEFT); - - if (startHead != null) { - // Use start head alter - return startHead.getAlter(fifths); - } - } - - // TODO: Here we should look in previous sheet/page... - } - } - } - - // Finally, use the current key signature - if (fifths != null) { - return KeyInter.getAlterFor(getStep(), fifths); - } - - // Nothing found, so... - return 0; - } - //------------// // lookupLink // //------------// @@ -820,8 +827,7 @@ private Link lookupLink (List systemStems) double bestGrade = 0; for (Corner corner : Corner.values) { - Point refPt = PointUtil.rounded( - getStemReferencePoint(corner.stemAnchor(), interline)); + Point refPt = PointUtil.rounded(getStemReferencePoint(corner.stemAnchor(), interline)); int xMin = refPt.x - ((corner.hSide == RIGHT) ? maxHeadInDx : maxHeadOutDx); int yMin = refPt.y - ((corner.vSide == TOP) ? maxYGap : 0); Rectangle luBox = new Rectangle(xMin, yMin, maxHeadInDx + maxHeadOutDx, maxYGap); @@ -861,20 +867,64 @@ private Link lookupLink (List systemStems) return bestLink; } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------------------// + // getShrinkHoriRatio // + //--------------------// + /** + * Report horizontal ratio to check overlap + * + * @return horizontal ratio + */ + public static double getShrinkHoriRatio () + { + return constants.shrinkHoriRatio.getValue(); + } + + //--------------------// + // getShrinkVertRatio // + //--------------------// + /** + * Report vertical ratio to check overlap + * + * @return vertical ratio + */ + public static double getShrinkVertRatio () + { + return constants.shrinkVertRatio.getValue(); + } + + //--------// + // shrink // + //--------// + /** + * Shrink a bit a bounding bounds when checking for note overlap. + * + * @param box the bounding bounds + * @return the shrunk bounds + */ + public static Rectangle2D shrink (Rectangle box) + { + double newWidth = constants.shrinkHoriRatio.getValue() * box.width; + double newHeight = constants.shrinkVertRatio.getValue() * box.height; + + return new Rectangle2D.Double( + box.getCenterX() - (newWidth / 2.0), + box.getCenterY() - (newHeight / 2.0), + newWidth, + newHeight); + } + //---------// // Impacts // //---------// public static class Impacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{"dist"}; private static final double[] WEIGHTS = new double[]{1}; - //~ Constructors --------------------------------------------------------------------------- public Impacts (double dist) { super(NAMES, WEIGHTS); @@ -885,10 +935,9 @@ public Impacts (double dist) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio shrinkHoriRatio = new Constant.Ratio( 0.5, diff --git a/src/main/org/audiveris/omr/sig/inter/Inter.java b/src/main/org/audiveris/omr/sig/inter/Inter.java index 134febd7c..a75738fef 100644 --- a/src/main/org/audiveris/omr/sig/inter/Inter.java +++ b/src/main/org/audiveris/omr/sig/inter/Inter.java @@ -47,7 +47,8 @@ * Interface {@code Inter} defines a possible interpretation. *

                                                                                                                      * Every Inter instance is assigned an intrinsic grade in range [0..1]. - * There usually exist two thresholds on grade value:

                                                                                                                        + * There usually exist two thresholds on grade value: + *
                                                                                                                          *
                                                                                                                        1. Minimum grade: this is the minimum value required to actually create (or keep) any * interpretation instance.
                                                                                                                        2. *
                                                                                                                        3. Good grade: this is the value which designates a really reliable interpretation, which @@ -70,7 +71,6 @@ public interface Inter extends Entity, VisitableInter, AttachmentHolder { - //~ Methods ------------------------------------------------------------------------------------ /** * Call-back when this instance has just been added to SIG. @@ -156,6 +156,13 @@ public interface Inter */ Double getContextualGrade (); + /** + * Assign the contextual grade, (0..1 probability) computed for interpretation. + * + * @param value the contextual grade value + */ + void setContextualGrade (double value); + /** * Report the core bounds for this interpretation. * @@ -186,6 +193,13 @@ public interface Inter */ Glyph getGlyph (); + /** + * Assign the glyph which is concerned by this interpretation. + * + * @param glyph the underlying glyph (non null) + */ + void setGlyph (Glyph glyph); + /** * Report the intrinsic grade (0..1 probability) assigned to interpretation * @@ -193,6 +207,13 @@ public interface Inter */ double getGrade (); + /** + * Assign an intrinsic grade (0..1 probability) to interpretation + * + * @param grade the new value for intrinsic grade + */ + void setGrade (double grade); + /** * Report details about the final grade * @@ -201,12 +222,21 @@ public interface Inter GradeImpacts getImpacts (); /** - * Report the inter, if any, this instance is a duplicate of. + * Report the inter, if any, this instance is a mirror of. + *

                                                                                                                          + * This is used only for HeadInter and HeadChordInter classes. * * @return the mirror instance or null */ Inter getMirror (); + /** + * Assign the mirror instance. + * + * @param mirror the mirrored instance + */ + void setMirror (Inter mirror); + /** * Report the containing part, if any. * @@ -214,6 +244,13 @@ public interface Inter */ Part getPart (); + /** + * Assign the related part, if any. + * + * @param part the part to set + */ + void setPart (Part part); + /** * Report the inter center for relation drawing. * @@ -235,6 +272,13 @@ public interface Inter */ SIGraph getSig (); + /** + * Assign the containing SIG + * + * @param sig the containing SIG + */ + void setSig (SIGraph sig); + /** * Report the related staff, if any. * @@ -242,6 +286,13 @@ public interface Inter */ Staff getStaff (); + /** + * Assign the related staff, if any. + * + * @param staff the staff to set + */ + void setStaff (Staff staff); + /** * Report a COPY of the bounding box based on MusicFont symbol. * @@ -285,6 +336,13 @@ public interface Inter */ boolean isAbnormal (); + /** + * Set this inter as an abnormal one. + * + * @param abnormal new value + */ + void setAbnormal (boolean abnormal); + /** * Report whether the interpretation has a good contextual grade. * @@ -313,6 +371,13 @@ public interface Inter */ boolean isManual (); + /** + * Set this inter as a manual one. + * + * @param manual new value + */ + void setManual (boolean manual); + /** * Report whether this instance has been removed from SIG. * @@ -365,7 +430,10 @@ boolean overlaps (Inter that) void remove (boolean extensive); /** - * Look for partners around this inter instance. + * Look for potential partners around this inter instance. + *

                                                                                                                          + * NOTA: Since this method can be used with a not-yet-settled candidate, implementations cannot + * assume that Inter instance already has a related staff or sig. *

                                                                                                                          * Relationships that are searched for inters (which cannot survive without such link): *

                                                                                                                            @@ -379,6 +447,7 @@ boolean overlaps (Inter that) *
                                                                                                                          • For a dynamic: 1 chord (really?) *
                                                                                                                          • For a slur: 1 or 2 heads (or connection across system/page break) *
                                                                                                                          • For a tuplet: 3 or 6 chords (approximately) + *
                                                                                                                          • For a fermata: 1 barline of 1 chord *
                                                                                                                          * Manual inters survive but are displayed in red, to show they are not yet in normal status. * @@ -389,13 +458,6 @@ boolean overlaps (Inter that) Collection searchLinks (SystemInfo system, boolean doit); - /** - * Set this inter as an abnormal one. - * - * @param abnormal new value - */ - void setAbnormal (boolean abnormal); - /** * Assign the bounding box for this interpretation. * The assigned bounds may be different from the underlying glyph bounds. @@ -404,62 +466,6 @@ Collection searchLinks (SystemInfo system, */ void setBounds (Rectangle box); - /** - * Assign the contextual grade, (0..1 probability) computed for interpretation. - * - * @param value the contextual grade value - */ - void setContextualGrade (double value); - - /** - * Assign the glyph which is concerned by this interpretation. - * - * @param glyph the underlying glyph (non null) - */ - void setGlyph (Glyph glyph); - - /** - * Assign an intrinsic grade (0..1 probability) to interpretation - * - * @param grade the new value for intrinsic grade - */ - void setGrade (double grade); - - /** - * Set this inter as a manual one. - * - * @param manual new value - */ - void setManual (boolean manual); - - /** - * Assign the mirror instance. - * - * @param mirror the mirrored instance - */ - void setMirror (Inter mirror); - - /** - * Assign the related part, if any. - * - * @param part the part to set - */ - void setPart (Part part); - - /** - * Assign the containing SIG - * - * @param sig the containing SIG - */ - void setSig (SIGraph sig); - - /** - * Assign the related staff, if any. - * - * @param staff the staff to set - */ - void setStaff (Staff staff); - /** * Report a shape-based string. * diff --git a/src/main/org/audiveris/omr/sig/inter/InterEnsemble.java b/src/main/org/audiveris/omr/sig/inter/InterEnsemble.java index 962abbc0f..6f1859117 100644 --- a/src/main/org/audiveris/omr/sig/inter/InterEnsemble.java +++ b/src/main/org/audiveris/omr/sig/inter/InterEnsemble.java @@ -27,7 +27,8 @@ * Interface {@code InterEnsemble} refers to an inter that is composed of other inters, * with the ability to add or remove members. *

                                                                                                                          - * Examples are:

                                                                                                                            + * Examples are: + *
                                                                                                                              *
                                                                                                                            • Sentence vs words (and LyricLine vs LyricItems)
                                                                                                                            • *
                                                                                                                            • TimePairInter vs num and den
                                                                                                                            • *
                                                                                                                            • KeyInter vs its alterations
                                                                                                                            • @@ -39,7 +40,8 @@ * {@link SentenceInter} instance cannot exist without at least one member {@link WordInter} * instance. *

                                                                                                                              - * Hence, care must be taken to avoid such "empty ensembles":

                                                                                                                                + * Hence, care must be taken to avoid such "empty ensembles": + *
                                                                                                                                  *
                                                                                                                                • Sentence creation must be followed by inclusion of a word. *
                                                                                                                                • Deletion of sole word of a sentence must be followed by sentence deletion. *
                                                                                                                                @@ -49,7 +51,6 @@ public interface InterEnsemble extends Inter { - //~ Methods ------------------------------------------------------------------------------------ /** * Add a member to the ensemble. diff --git a/src/main/org/audiveris/omr/sig/inter/InterVisitor.java b/src/main/org/audiveris/omr/sig/inter/InterVisitor.java index b78c6a4de..32fbdc504 100644 --- a/src/main/org/audiveris/omr/sig/inter/InterVisitor.java +++ b/src/main/org/audiveris/omr/sig/inter/InterVisitor.java @@ -30,7 +30,6 @@ */ public interface InterVisitor { - //~ Methods ------------------------------------------------------------------------------------ void visit (AbstractBeamInter inter); // BeamHook, Beam, SmallBeam diff --git a/src/main/org/audiveris/omr/sig/inter/Inters.java b/src/main/org/audiveris/omr/sig/inter/Inters.java index b174df472..dec32da13 100644 --- a/src/main/org/audiveris/omr/sig/inter/Inters.java +++ b/src/main/org/audiveris/omr/sig/inter/Inters.java @@ -43,7 +43,6 @@ */ public abstract class Inters { - //~ Static fields/initializers ----------------------------------------------------------------- /** * Comparator to put members first and ensembles last. @@ -306,7 +305,8 @@ public int compare (Inter i1, /** * For comparing inter instances by decreasing mean grade. */ - public static final Comparator> byReverseMeanGrade = new Comparator>() + public static final Comparator> byReverseMeanGrade + = new Comparator>() { @Override public int compare (Collection c1, @@ -319,7 +319,8 @@ public int compare (Collection c1, /** * For comparing inter instances by decreasing mean contextual grade. */ - public static final Comparator> byReverseMeanContextualGrade = new Comparator>() + public static final Comparator> byReverseMeanContextualGrade + = new Comparator>() { @Override public int compare (Collection c1, @@ -329,7 +330,11 @@ public int compare (Collection c1, } }; - //~ Methods ------------------------------------------------------------------------------------ + /** Not meant to be instantiated. */ + private Inters () + { + } + //-----------// // getBounds // //-----------// @@ -389,6 +394,12 @@ public static Inter getClosestInter (Collection inters, //------------------// // getMeanBestGrade // //------------------// + /** + * Report the average of inters best grade value. + * + * @param col inters collection + * @return average best grade + */ public static double getMeanBestGrade (Collection col) { if (col.isEmpty()) { @@ -407,6 +418,12 @@ public static double getMeanBestGrade (Collection col) //--------------// // getMeanGrade // //--------------// + /** + * Report the average grade of provided inters. + * + * @param col inters collection + * @return average grade + */ public static double getMeanGrade (Collection col) { if (col.isEmpty()) { @@ -446,6 +463,12 @@ public static boolean hasGoodMember (Collection inters) //-----// // ids // //-----// + /** + * Report a string of inter IDs. + * + * @param inters collection of inters + * @return string of IDs + */ public static String ids (Collection inters) { if (inters == null) { @@ -478,7 +501,7 @@ public static String ids (Collection inters) public static List inters (Collection collection, Predicate predicate) { - List found = new ArrayList(); + List found = new ArrayList<>(); for (Inter inter : collection) { if ((predicate == null) || predicate.check(inter)) { @@ -518,7 +541,7 @@ public static List inters (Collection collection, public static List inters (Staff staff, Collection inters) { - List filtered = new ArrayList(); + List filtered = new ArrayList<>(); for (Inter inter : inters) { if (inter.getStaff() == staff) { @@ -545,7 +568,7 @@ public static List intersectedInters (List inters, GeoOrder order, Area area) { - List found = new ArrayList(); + List found = new ArrayList<>(); Rectangle bounds = area.getBounds(); double xMax = bounds.getMaxX(); double yMax = bounds.getMaxY(); @@ -601,7 +624,7 @@ public static List intersectedInters (List inters, GeoOrder order, Rectangle box) { - List found = new ArrayList(); + List found = new ArrayList<>(); int xMax = (box.x + box.width) - 1; int yMax = (box.y + box.height) - 1; @@ -624,24 +647,28 @@ public static List intersectedInters (List inters, return found; } - //~ Inner Classes ------------------------------------------------------------------------------ //----------------// // ClassPredicate // //----------------// + /** + * Predicate to filter Inter instance of a certain class (or subclass thereof). + */ public static class ClassPredicate implements Predicate { - //~ Instance fields ------------------------------------------------------------------------ private final Class classe; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create class predicate + * + * @param classe Filtering class + */ public ClassPredicate (Class classe) { this.classe = classe; } - //~ Methods -------------------------------------------------------------------------------- @Override public boolean check (Inter inter) { @@ -652,20 +679,25 @@ public boolean check (Inter inter) //------------------// // ClassesPredicate // //------------------// + /** + * Predicate to filter Inter instance of provided classes. + */ public static class ClassesPredicate implements Predicate { - //~ Instance fields ------------------------------------------------------------------------ private final Class[] classes; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create classes predicate + * + * @param classes filtering classes + */ public ClassesPredicate (Class[] classes) { this.classes = classes; } - //~ Methods -------------------------------------------------------------------------------- @Override public boolean check (Inter inter) { diff --git a/src/main/org/audiveris/omr/sig/inter/KeyAlterInter.java b/src/main/org/audiveris/omr/sig/inter/KeyAlterInter.java index d7bb41941..cbbf710ab 100644 --- a/src/main/org/audiveris/omr/sig/inter/KeyAlterInter.java +++ b/src/main/org/audiveris/omr/sig/inter/KeyAlterInter.java @@ -37,7 +37,6 @@ public class KeyAlterInter extends AlterInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new KeyAlterInter object. @@ -67,7 +66,6 @@ private KeyAlterInter () super(null, null, 0, null, null, null); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/KeyInter.java b/src/main/org/audiveris/omr/sig/inter/KeyInter.java index 637d95e69..1898e9189 100644 --- a/src/main/org/audiveris/omr/sig/inter/KeyInter.java +++ b/src/main/org/audiveris/omr/sig/inter/KeyInter.java @@ -52,18 +52,34 @@ public class KeyInter extends AbstractInter implements InterEnsemble { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(KeyInter.class); + /** Sharp pitches per clef kind. */ + public static final Map SHARP_PITCHES_MAP = new EnumMap<>(ClefKind.class); + + /** Flat pitches per clef kind. */ + public static final Map FLAT_PITCHES_MAP = new EnumMap<>(ClefKind.class); + /** Sharp keys note steps. */ private static final AbstractNoteInter.Step[] SHARP_STEPS = new AbstractNoteInter.Step[]{ - F, C, G, D, A, E, B - }; + F, + C, + G, + D, + A, + E, + B}; - /** Sharp pitches per clef kind. */ - public static final Map SHARP_PITCHES_MAP = new EnumMap( - ClefKind.class); + /** Flat keys note steps. */ + private static final AbstractNoteInter.Step[] FLAT_STEPS = new AbstractNoteInter.Step[]{ + B, + E, + A, + D, + G, + C, + F}; static { SHARP_PITCHES_MAP.put(TREBLE, new int[]{-4, -1, -5, -2, 1, -3, 0}); @@ -72,15 +88,6 @@ public class KeyInter SHARP_PITCHES_MAP.put(TENOR, new int[]{2, -2, 1, -3, 0, -4, -1}); } - /** Flat keys note steps. */ - private static final AbstractNoteInter.Step[] FLAT_STEPS = new AbstractNoteInter.Step[]{ - B, E, A, D, G, C, F - }; - - /** Flat pitches per clef kind. */ - public static final Map FLAT_PITCHES_MAP = new EnumMap( - ClefKind.class); - static { FLAT_PITCHES_MAP.put(TREBLE, new int[]{0, -3, 1, -2, 2, -1, 3}); FLAT_PITCHES_MAP.put(ALTO, new int[]{1, -2, 2, -1, 3, 0, 4}); @@ -88,12 +95,10 @@ public class KeyInter FLAT_PITCHES_MAP.put(TENOR, new int[]{-1, -4, 0, -3, 1, -2, 2}); } - //~ Instance fields ---------------------------------------------------------------------------- /** Numerical value for signature. */ @XmlAttribute private int fifths; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new KeyInter object. * @@ -129,7 +134,6 @@ private KeyInter () super(null, null, null, null); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -158,70 +162,6 @@ public void addMember (Inter member) EnsembleHelper.addMember(this, member); } - //-------------// - // createAdded // - //-------------// - /** - * Create and add a new KeyInter object. - * - * @param staff the containing staff - * @param alters sequence of alteration inters - * @return the created KeyInter - */ - public static KeyInter createAdded (Staff staff, - List alters) - { - SIGraph sig = staff.getSystem().getSig(); - double grade = 0; - - for (KeyAlterInter alter : alters) { - grade += sig.computeContextualGrade(alter); - } - - grade /= alters.size(); - - KeyInter keyInter = new KeyInter(grade, 0); - keyInter.setStaff(staff); - sig.addVertex(keyInter); - - for (Inter member : alters) { - keyInter.addMember(member); - } - - return keyInter; - } - - //-------------// - // getAlterFor // - //-------------// - /** - * Report the alteration to apply to the provided note step, under the provided - * active key signature. - * - * @param step note step - * @param signature key signature - * @return the key-based alteration (either -1, 0 or +1) - */ - public static int getAlterFor (AbstractNoteInter.Step step, - int signature) - { - if (signature > 0) { - for (int k = 0; k < signature; k++) { - if (step == SHARP_STEPS[k]) { - return 1; - } - } - } else { - for (int k = 0; k < -signature; k++) { - if (step == FLAT_STEPS[k]) { - return -1; - } - } - } - - return 0; - } - //-------------// // getAlterFor // //-------------// @@ -304,6 +244,168 @@ public int getFifths () return fifths; } + //-----------// + // setFifths // + //-----------// + /** + * (method currently not used) Adjust the signature integer value. + * + * @param fifths the fifths to set + */ + public void setFifths (int fifths) + { + this.fifths = fifths; + } + + //------------// + // getMembers // + //------------// + @Override + public List getMembers () + { + return EnsembleHelper.getMembers(this, Inters.byCenterAbscissa); + } + + //-----------------// + // invalidateCache // + //-----------------// + @Override + public void invalidateCache () + { + bounds = null; + fifths = 0; + } + + //--------------// + // removeMember // + //--------------// + @Override + public void removeMember (Inter member) + { + if (!(member instanceof KeyAlterInter)) { + throw new IllegalArgumentException("Only KeyAlterInter can be removed from Key"); + } + + EnsembleHelper.removeMember(this, member); + } + + //-----------// + // replicate // + //-----------// + /** + * Replicate this key in a target staff. + * + * @param targetStaff the target staff + * @return the replicated key, whose bounds may need an update + */ + public KeyInter replicate (Staff targetStaff) + { + KeyInter inter = new KeyInter(0, getFifths()); + inter.setStaff(targetStaff); + + return inter; + } + + //-------------// + // shapeString // + //-------------// + @Override + public String shapeString () + { + return "KEY_SIG:" + getFifths(); + } + + //--------// + // shrink // + //--------// + /** + * Discard the last alter item in this key. + */ + public void shrink () + { + final List alters = getMembers(); + + // Discard last alter + Inter lastAlter = alters.get(alters.size() - 1); + lastAlter.remove(); + } + + //-----------// + // internals // + //-----------// + @Override + protected String internals () + { + StringBuilder sb = new StringBuilder(super.internals()); + sb.append(" fifths:").append(getFifths()); + + return sb.toString(); + } + + //-------------// + // createAdded // + //-------------// + /** + * Create and add a new KeyInter object. + * + * @param staff the containing staff + * @param alters sequence of alteration inters + * @return the created KeyInter + */ + public static KeyInter createAdded (Staff staff, + List alters) + { + SIGraph sig = staff.getSystem().getSig(); + double grade = 0; + + for (KeyAlterInter alter : alters) { + grade += sig.computeContextualGrade(alter); + } + + grade /= alters.size(); + + KeyInter keyInter = new KeyInter(grade, 0); + keyInter.setStaff(staff); + sig.addVertex(keyInter); + + for (Inter member : alters) { + keyInter.addMember(member); + } + + return keyInter; + } + + //-------------// + // getAlterFor // + //-------------// + /** + * Report the alteration to apply to the provided note step, under the provided + * active key signature. + * + * @param step note step + * @param signature key signature + * @return the key-based alteration (either -1, 0 or +1) + */ + public static int getAlterFor (AbstractNoteInter.Step step, + int signature) + { + if (signature > 0) { + for (int k = 0; k < signature; k++) { + if (step == SHARP_STEPS[k]) { + return 1; + } + } + } else { + for (int k = 0; k < -signature; k++) { + if (step == FLAT_STEPS[k]) { + return -1; + } + } + } + + return 0; + } + //--------------// // getItemPitch // //--------------// @@ -332,15 +434,6 @@ public static int getItemPitch (int n, return pitches[Math.abs(n) - 1]; } - //------------// - // getMembers // - //------------// - @Override - public List getMembers () - { - return EnsembleHelper.getMembers(this, Inters.byCenterAbscissa); - } - //------------// // getPitches // //------------// @@ -415,8 +508,18 @@ public static double getStandardPosition (int k) } //-----------// - // guessKind // Not used! + // guessKind // //-----------// + /** + * Try to guess the clef kind, based only on key shape and pitches. + *

                                                                                                                                + * This method is not used. + * + * @param shape the shape of key alter signs (FLAT vs SHARP) + * @param measuredPitches precise pitches of the alter signs + * @param results (output) map to receive results per clef kind. Allocated if null. + * @return the guessed clef kind + */ public static ClefKind guessKind (Shape shape, Double[] measuredPitches, Map results) @@ -424,7 +527,7 @@ public static ClefKind guessKind (Shape shape, Map map = (shape == Shape.FLAT) ? FLAT_PITCHES_MAP : SHARP_PITCHES_MAP; if (results == null) { - results = new EnumMap(ClefKind.class); + results = new EnumMap<>(ClefKind.class); } ClefKind bestKind = null; @@ -464,95 +567,6 @@ public static ClefKind guessKind (Shape shape, return bestKind; } - //-----------------// - // invalidateCache // - //-----------------// - @Override - public void invalidateCache () - { - bounds = null; - fifths = 0; - } - - //--------------// - // removeMember // - //--------------// - @Override - public void removeMember (Inter member) - { - if (!(member instanceof KeyAlterInter)) { - throw new IllegalArgumentException("Only KeyAlterInter can be removed from Key"); - } - - EnsembleHelper.removeMember(this, member); - } - - //-----------// - // replicate // - //-----------// - /** - * Replicate this key in a target staff. - * - * @param targetStaff the target staff - * @return the replicated key, whose bounds may need an update - */ - public KeyInter replicate (Staff targetStaff) - { - KeyInter inter = new KeyInter(0, getFifths()); - inter.setStaff(targetStaff); - - return inter; - } - - //-----------// - // setFifths // - //-----------// - /** - * (method currently not used) Adjust the signature integer value. - * - * @param fifths the fifths to set - */ - public void setFifths (int fifths) - { - this.fifths = fifths; - } - - //-------------// - // shapeString // - //-------------// - @Override - public String shapeString () - { - return "KEY_SIG:" + getFifths(); - } - - //--------// - // shrink // - //--------// - /** - * Discard the last alter item in this key. - */ - public void shrink () - { - final List alters = getMembers(); - - // Discard last alter - Inter lastAlter = alters.get(alters.size() - 1); - lastAlter.remove(); - } - - //-----------// - // internals // - //-----------// - @Override - protected String internals () - { - StringBuilder sb = new StringBuilder(super.internals()); - sb.append(" fifths:").append(getFifths()); - - return sb.toString(); - } - //---------// // valueOf // //---------// diff --git a/src/main/org/audiveris/omr/sig/inter/LedgerInter.java b/src/main/org/audiveris/omr/sig/inter/LedgerInter.java index fd97b6da3..281fe5e1b 100644 --- a/src/main/org/audiveris/omr/sig/inter/LedgerInter.java +++ b/src/main/org/audiveris/omr/sig/inter/LedgerInter.java @@ -36,12 +36,11 @@ public class LedgerInter extends AbstractInter { - //~ Instance fields ---------------------------------------------------------------------------- /** * Index of virtual line relative to staff. * Above staff if index is negative (-1, -2, etc) - * + *

                                                                                                                                * -2 - * -1 - * --------------------------------- @@ -51,12 +50,11 @@ public class LedgerInter * --------------------------------- * +1 - * +2 - - * + *

                                                                                                                                * Below staff if index is positive (+1, +2, etc) */ private Integer index; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LedgerInter object. * @@ -89,7 +87,6 @@ private LedgerInter () super(null, null, null, null); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -141,6 +138,16 @@ public Integer getIndex () return index; } + /** + * Set the ledger index, with respect to staff. + * + * @param index the index to set + */ + public void setIndex (int index) + { + this.index = index; + } + //--------// // remove // //--------// @@ -160,12 +167,4 @@ public void remove (boolean extensive) super.remove(extensive); } - - /** - * @param index the index to set - */ - public void setIndex (int index) - { - this.index = index; - } } diff --git a/src/main/org/audiveris/omr/sig/inter/LyricItemInter.java b/src/main/org/audiveris/omr/sig/inter/LyricItemInter.java index 7e06028ca..dfab01523 100644 --- a/src/main/org/audiveris/omr/sig/inter/LyricItemInter.java +++ b/src/main/org/audiveris/omr/sig/inter/LyricItemInter.java @@ -50,14 +50,13 @@ public class LyricItemInter extends WordInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(LyricItemInter.class); /** String equivalent of Character used for elision. (undertie) */ - public static final String ELISION_STRING = new String(Character.toChars(8255)); + public static final String ELISION_STRING = new String(Character.toChars(8_255)); /** String equivalent of Character used for extension. (underscore) */ public static final String EXTENSION_STRING = "_"; @@ -65,42 +64,6 @@ public class LyricItemInter /** String equivalent of Character used for hyphen. */ public static final String HYPHEN_STRING = "-"; - //~ Enumerations ------------------------------------------------------------------------------- - /** - * Describes the kind of this lyrics item. - */ - public static enum ItemKind - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Just an elision */ - Elision, - /** Just an extension */ - Extension, - /** A hyphen between syllables */ - Hyphen, - /** A real syllable */ - Syllable; - } - - /** - * Describes more precisely a syllable inside a word. - */ - public static enum SyllabicType - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Single-syllable word */ - SINGLE, - /** Syllable that begins a word */ - BEGIN, - /** Syllable at the middle of a word */ - MIDDLE, - /** Syllable that ends a word */ - END; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Lyrics kind. */ @XmlAttribute(name = "kind") private ItemKind itemKind; @@ -109,7 +72,6 @@ public static enum SyllabicType @XmlAttribute(name = "syllabic") private SyllabicType syllabicType; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LyricItemInter object. * @@ -137,7 +99,6 @@ private LyricItemInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -150,6 +111,13 @@ public void accept (InterVisitor visitor) //--------------------// // defineSyllabicType // //--------------------// + /** + * Define proper syllabic type for this lyric syllable item, based on previous and + * next items. + * + * @param prevItem previous item + * @param nextItem next item + */ public void defineSyllabicType (LyricItemInter prevItem, LyricItemInter nextItem) { @@ -169,14 +137,37 @@ public void defineSyllabicType (LyricItemInter prevItem, //-------------// // getItemKind // //-------------// + /** + * Report the kind of this lyric item + * + * @return item kind (SYLLABLE, HYPHEN, ...) + */ public ItemKind getItemKind () { return itemKind; } + //-------------// + // setItemKind // + //-------------// + /** + * Set the item kind. + * + * @param itemKind item kind + */ + public void setItemKind (ItemKind itemKind) + { + this.itemKind = itemKind; + } + //--------------// // getLyricLine // //--------------// + /** + * Report the containing lyric line. + * + * @return containing line + */ public LyricLineInter getLyricLine () { return (LyricLineInter) getEnsemble(); @@ -185,11 +176,29 @@ public LyricLineInter getLyricLine () //-----------------// // getSyllabicType // //-----------------// + /** + * Report the syllabic type. + * + * @return syllabic type + */ public SyllabicType getSyllabicType () { return syllabicType; } + //-----------------// + // setSyllabicType // + //-----------------// + /** + * Set the syllabic type. + * + * @param syllabicType the syllabic type for this item + */ + public void setSyllabicType (SyllabicType syllabicType) + { + this.syllabicType = syllabicType; + } + //----------// // getVoice // //----------// @@ -203,25 +212,12 @@ public Voice getVoice () return null; } - //-------------// - // isSeparator // - //-------------// - /** - * Predicate to detect a separator. - * - * @param str the character to check - * - * @return true if this is a separator - */ - public static boolean isSeparator (String str) - { - return str.equals(EXTENSION_STRING) || str.equals(ELISION_STRING) - || str.equals(HYPHEN_STRING); - } - //------------// // mapToChord // //------------// + /** + * Set a ChordSyllableRelation between this lyric item and proper chord. + */ public void mapToChord () { // We map only syllables @@ -248,8 +244,7 @@ public void mapToChord () setStaff(relatedStaff); Part part = relatedStaff.getPart(); - int maxDx = part.getSystem().getSheet().getScale() - .toPixels(constants.maxItemDx); + int maxDx = part.getSystem().getSheet().getScale().toPixels(constants.maxItemDx); // A word can start in a measure and finish in the next measure // Look for best aligned head-chord in proper staff @@ -268,8 +263,8 @@ public void mapToChord () if (lookAbove) { for (AbstractChordInter chord : measure.getHeadChordsAbove(getLocation())) { - if (chord instanceof HeadChordInter - && (chord.getBottomStaff() == relatedStaff)) { + if (chord instanceof HeadChordInter && (chord + .getBottomStaff() == relatedStaff)) { int dx = Math.abs(chord.getHeadLocation().x - centerX); if (bestDx > dx) { @@ -301,22 +296,6 @@ public void mapToChord () logger.info("No head-chord for {}", this); } - //-------------// - // setItemKind // - //-------------// - public void setItemKind (ItemKind itemKind) - { - this.itemKind = itemKind; - } - - //-----------------// - // setSyllabicType // - //-----------------// - public void setSyllabicType (SyllabicType syllabicType) - { - this.syllabicType = syllabicType; - } - //-----------// // internals // //-----------// @@ -336,14 +315,57 @@ protected String internals () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------// + // isSeparator // + //-------------// + /** + * Predicate to detect a separator. + * + * @param str the character to check + * @return true if this is a separator + */ + public static boolean isSeparator (String str) + { + return str.equals(EXTENSION_STRING) || str.equals(ELISION_STRING) + || str.equals(HYPHEN_STRING); + } + + /** + * Describes the kind of this lyrics item. + */ + public static enum ItemKind + { + /** Just an elision */ + Elision, + /** Just an extension */ + Extension, + /** A hyphen between syllables */ + Hyphen, + /** A real syllable */ + Syllable; + } + + /** + * Describes more precisely a syllable inside a word. + */ + public static enum SyllabicType + { + /** Single-syllable word */ + SINGLE, + /** Syllable that begins a word */ + BEGIN, + /** Syllable at the middle of a word */ + MIDDLE, + /** Syllable that ends a word */ + END; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxItemDx = new Scale.Fraction( 5, diff --git a/src/main/org/audiveris/omr/sig/inter/LyricLineInter.java b/src/main/org/audiveris/omr/sig/inter/LyricLineInter.java index f6deba4ca..ef70212e1 100644 --- a/src/main/org/audiveris/omr/sig/inter/LyricLineInter.java +++ b/src/main/org/audiveris/omr/sig/inter/LyricLineInter.java @@ -23,7 +23,6 @@ import org.audiveris.omr.glyph.Grades; import org.audiveris.omr.sheet.Part; -import org.audiveris.omr.sheet.SystemInfo; import org.audiveris.omr.text.FontInfo; import org.audiveris.omr.text.TextLine; import org.audiveris.omr.text.TextRole; @@ -48,16 +47,13 @@ public class LyricLineInter extends SentenceInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(LyricLineInter.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The line number. */ @XmlAttribute private int number; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LyricLine object. * @@ -80,7 +76,6 @@ private LyricLineInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -103,25 +98,6 @@ public void added () } } - //--------// - // create // - //--------// - /** - * Create a {@code LyricLineInter} from a TextLine. - * - * @param line the OCR'ed text line - * @return the LyricLine inter - */ - public static LyricLineInter create (TextLine line) - { - LyricLineInter lyricLine = new LyricLineInter( - line.getBounds(), - line.getConfidence() * Grades.intrinsicRatio, - line.getMeanFont()); - - return lyricLine; - } - //------------------// // getFollowingLine // //------------------// @@ -135,13 +111,12 @@ public LyricLineInter getFollowingLine () LyricLineInter nextLine = null; // Check existence of similar line in following system part (within the same page) - SystemInfo system = sig.getSystem(); Part nextPart = staff.getPart().getFollowingInPage(); if (nextPart != null) { // Retrieve the same lyrics line in the next (system) part if (nextPart.getLyrics().size() >= number) { - nextLine = (LyricLineInter) nextPart.getLyrics().get(number - 1); + nextLine = nextPart.getLyrics().get(number - 1); } } @@ -161,6 +136,19 @@ public int getNumber () return number; } + //-----------// + // setNumber // + //-----------// + /** + * Set the number of this item within the containing lyrics line. + * + * @param number 1-based number + */ + public void setNumber (int number) + { + this.number = number; + } + //------------------// // getPrecedingLine // //------------------// @@ -172,11 +160,10 @@ public int getNumber () public LyricLineInter getPrecedingLine () { // Check existence of similar line in preceding system part - SystemInfo system = sig.getSystem(); Part prevPart = staff.getPart().getPrecedingInPage(); if ((prevPart != null) && (prevPart.getLyrics().size() >= number)) { - return (LyricLineInter) prevPart.getLyrics().get(number - 1); + return prevPart.getLyrics().get(number - 1); } return null; @@ -216,6 +203,9 @@ public void invalidateCache () //----------------------// // refineLyricSyllables // //----------------------// + /** + * Refine items syllabic types in this lyric line. + */ public void refineLyricSyllables () { // Last item of preceding line is any @@ -267,14 +257,6 @@ public void remove (boolean extensive) super.remove(extensive); } - //-----------// - // setNumber // - //-----------// - public void setNumber (int number) - { - this.number = number; - } - //-------------// // shapeString // //-------------// @@ -283,4 +265,23 @@ public String shapeString () { return "LYRICS"; } + + //--------// + // create // + //--------// + /** + * Create a {@code LyricLineInter} from a TextLine. + * + * @param line the OCR'ed text line + * @return the LyricLine inter + */ + public static LyricLineInter create (TextLine line) + { + LyricLineInter lyricLine = new LyricLineInter( + line.getBounds(), + line.getConfidence() * Grades.intrinsicRatio, + line.getMeanFont()); + + return lyricLine; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/MarkerInter.java b/src/main/org/audiveris/omr/sig/inter/MarkerInter.java index 0dad6308e..45db62428 100644 --- a/src/main/org/audiveris/omr/sig/inter/MarkerInter.java +++ b/src/main/org/audiveris/omr/sig/inter/MarkerInter.java @@ -1,123 +1,121 @@ -//------------------------------------------------------------------------------------------------// -// // -// M a r k e r I n t e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.inter; - -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.math.GeoUtil; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sig.relation.MarkerBarRelation; - -import java.awt.Point; -import java.util.List; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code MarkerInter} represents a navigation marker. - * Shape can be coda, segno, dacapo (D.C.), dal segno (D.S.). - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "marker") -public class MarkerInter - extends AbstractInter -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Creates a new {@code MarkerInter} object. - * - * @param glyph underlying glyph if any - * @param shape precise shape (CODA, SEGNO, DA_CAPO, DAL_SEGNO) - * @param grade quality - */ - public MarkerInter (Glyph glyph, - Shape shape, - double grade) - { - super(glyph, (glyph != null) ? glyph.getBounds() : null, shape, grade); - } - - /** - * No-arg constructor meant for JAXB. - */ - private MarkerInter () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // accept // - //--------// - @Override - public void accept (InterVisitor visitor) - { - visitor.visit(this); - } - - //--------// - // create // - //--------// - /** - * Create a MarkerInter. - * - * @param glyph underlying glyph - * @param shape precise shape - * @param grade evaluation value - * @param staff related staff - * @return the created instance - */ - public static MarkerInter create (Glyph glyph, - Shape shape, - double grade, - Staff staff) - { - MarkerInter marker = new MarkerInter(glyph, shape, grade); - marker.setStaff(staff); - - return marker; - } - - //----------------------// - // linkWithStaffBarline // - //----------------------// - /** - * (Try to) connect this marker with a suitable StaffBarline. - * - * @return true if successful - */ - public boolean linkWithStaffBarline () - { - Point center = getCenter(); - List staffBars = getStaff().getStaffBarlines(); - StaffBarlineInter staffBar = StaffBarlineInter.getClosestStaffBarline(staffBars, center); - - if ((staffBar != null) && (GeoUtil.xOverlap(getBounds(), staffBar.getBounds()) > 0)) { - sig.addEdge(this, staffBar, new MarkerBarRelation()); - - return true; - } - - return false; - } -} +//------------------------------------------------------------------------------------------------// +// // +// M a r k e r I n t e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.inter; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.math.GeoUtil; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sig.relation.MarkerBarRelation; + +import java.awt.Point; +import java.util.List; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code MarkerInter} represents a navigation marker. + * Shape can be coda, segno, dacapo (D.C.), dal segno (D.S.). + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "marker") +public class MarkerInter + extends AbstractInter +{ + + /** + * Creates a new {@code MarkerInter} object. + * + * @param glyph underlying glyph if any + * @param shape precise shape (CODA, SEGNO, DA_CAPO, DAL_SEGNO) + * @param grade quality + */ + public MarkerInter (Glyph glyph, + Shape shape, + double grade) + { + super(glyph, (glyph != null) ? glyph.getBounds() : null, shape, grade); + } + + /** + * No-arg constructor meant for JAXB. + */ + private MarkerInter () + { + } + + //--------// + // accept // + //--------// + @Override + public void accept (InterVisitor visitor) + { + visitor.visit(this); + } + + //----------------------// + // linkWithStaffBarline // + //----------------------// + /** + * (Try to) connect this marker with a suitable StaffBarline. + * + * @return true if successful + */ + public boolean linkWithStaffBarline () + { + Point center = getCenter(); + List staffBars = getStaff().getStaffBarlines(); + StaffBarlineInter staffBar = StaffBarlineInter.getClosestStaffBarline(staffBars, center); + + if ((staffBar != null) && (GeoUtil.xOverlap(getBounds(), staffBar.getBounds()) > 0)) { + sig.addEdge(this, staffBar, new MarkerBarRelation()); + + return true; + } + + return false; + } + + //--------// + // create // + //--------// + /** + * Create a MarkerInter. + * + * @param glyph underlying glyph + * @param shape precise shape + * @param grade evaluation value + * @param staff related staff + * @return the created instance + */ + public static MarkerInter create (Glyph glyph, + Shape shape, + double grade, + Staff staff) + { + MarkerInter marker = new MarkerInter(glyph, shape, grade); + marker.setStaff(staff); + + return marker; + } +} diff --git a/src/main/org/audiveris/omr/sig/inter/OrnamentInter.java b/src/main/org/audiveris/omr/sig/inter/OrnamentInter.java index f07bc5108..48f2da4e1 100644 --- a/src/main/org/audiveris/omr/sig/inter/OrnamentInter.java +++ b/src/main/org/audiveris/omr/sig/inter/OrnamentInter.java @@ -1,265 +1,273 @@ -//------------------------------------------------------------------------------------------------// -// // -// O r n a m e n t I n t e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.inter; - -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.math.GeoOrder; -import org.audiveris.omr.sheet.Scale; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sheet.rhythm.Voice; -import org.audiveris.omr.sig.relation.ChordOrnamentRelation; -import org.audiveris.omr.sig.relation.Link; -import org.audiveris.omr.sig.relation.Relation; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Point; -import java.awt.Rectangle; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code OrnamentInter} represents an ornament interpretation. - * (TR, TURN, TURN_INVERTED, TURN_UP, TURN_SLASH, MORDENT, MORDENT_INVERTED, GRACE_NOTE_SLASH, - * GRACE_NOTE) - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "ornament") -public class OrnamentInter - extends AbstractInter -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(OrnamentInter.class); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new OrnamentInter object. - * - * @param glyph underlying glyph - * @param shape precise shape (TR, TURN, TURN_INVERTED, TURN_UP, TURN_SLASH, MORDENT, - * MORDENT_INVERTED, GRACE_NOTE_SLASH, GRACE_NOTE) - * @param grade evaluation value - */ - public OrnamentInter (Glyph glyph, - Shape shape, - double grade) - { - super(glyph, (glyph != null) ? glyph.getBounds() : null, shape, grade); - } - - /** - * No-arg constructor meant for JAXB. - */ - private OrnamentInter () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // accept // - //--------// - @Override - public void accept (InterVisitor visitor) - { - visitor.visit(this); - } - - //------------------// - // createValidAdded // - //------------------// - /** - * (Try to) create and add a valid OrnamentInter. - *

                                                                                                                                - * TODO: this is to be refined for GRACE ornaments which are located on left side of chord. - * - * @param glyph underlying glyph - * @param shape detected shape - * @param grade assigned grade - * @param system containing system - * @param systemHeadChords system head chords, ordered by abscissa - * @return the created articulation or null - */ - public static OrnamentInter createValidAdded (Glyph glyph, - Shape shape, - double grade, - SystemInfo system, - List systemHeadChords) - { - if (glyph.isVip()) { - logger.info("VIP OrnamentInter create {} as {}", glyph, shape); - } - - OrnamentInter orn = new OrnamentInter(glyph, shape, grade); - Link link = orn.lookupLink(systemHeadChords); - - if (link != null) { - system.getSig().addVertex(orn); - link.applyTo(orn); - - return orn; - } - - return null; - } - - //----------// - // getStaff // - //----------// - @Override - public Staff getStaff () - { - if (staff == null) { - for (Relation rel : sig.getRelations(this, ChordOrnamentRelation.class)) { - HeadChordInter chord = (HeadChordInter) sig.getOppositeInter(this, rel); - - return staff = chord.getStaff(); - } - } - - return staff; - } - - //----------// - // getVoice // - //----------// - @Override - public Voice getVoice () - { - for (Relation rel : sig.getRelations(this, ChordOrnamentRelation.class)) { - return sig.getOppositeInter(this, rel).getVoice(); - } - - return null; - } - - //-------------// - // searchLinks // - //-------------// - @Override - public Collection searchLinks (SystemInfo system, - boolean doit) - { - // Not very optimized! - List systemHeadChords = system.getSig().inters(HeadChordInter.class); - Collections.sort(systemHeadChords, Inters.byAbscissa); - - Link link = lookupLink(systemHeadChords); - - if (link == null) { - return Collections.emptyList(); - } - - if (doit) { - link.applyTo(this); - } - - return Collections.singleton(link); - } - - //-----------// - // internals // - //-----------// - @Override - protected String internals () - { - return super.internals() + " " + shape; - } - - //------------// - // lookupLink // - //------------// - /** - * Try to detect a link between this ornament instance and a HeadChord nearby. - * - * @param systemHeadChords ordered collection of head chords in system - * @return the link found or null - */ - private Link lookupLink (List systemHeadChords) - { - if (systemHeadChords.isEmpty()) { - return null; - } - - final SystemInfo system = systemHeadChords.get(0).getSig().getSystem(); - final Scale scale = system.getSheet().getScale(); - final int maxDx = scale.toPixels(ChordOrnamentRelation.getXOutGapMaximum(manual)); - final int maxDy = scale.toPixels(ChordOrnamentRelation.getYGapMaximum(manual)); - final Rectangle ornamentBox = getBounds(); - final Point ornamentCenter = getCenter(); - final Rectangle luBox = new Rectangle(ornamentCenter); - luBox.grow(maxDx, maxDy); - - final List chords = Inters.intersectedInters( - systemHeadChords, - GeoOrder.BY_ABSCISSA, - luBox); - - if (chords.isEmpty()) { - return null; - } - - ChordOrnamentRelation bestRel = null; - Inter bestChord = null; - double bestYGap = Double.MAX_VALUE; - - for (Inter chord : chords) { - Rectangle chordBox = chord.getBounds(); - - // The ornament cannot intersect the chord - if (chordBox.intersects(ornamentBox)) { - continue; - } - - Point center = chord.getCenter(); - - // Select proper chord reference point (top or bottom) - int yRef = (ornamentCenter.y > center.y) - ? (chordBox.y + chordBox.height) : chordBox.y; - double xGap = Math.abs(center.x - ornamentCenter.x); - double yGap = Math.abs(yRef - ornamentCenter.y); - ChordOrnamentRelation rel = new ChordOrnamentRelation(); - rel.setOutGaps(scale.pixelsToFrac(xGap), scale.pixelsToFrac(yGap), manual); - - if (rel.getGrade() >= rel.getMinGrade()) { - if ((bestRel == null) || (bestYGap > yGap)) { - bestRel = rel; - bestChord = chord; - bestYGap = yGap; - } - } - } - - if (bestRel != null) { - return new Link(bestChord, bestRel, false); - } - - return null; - } -} +//------------------------------------------------------------------------------------------------// +// // +// O r n a m e n t I n t e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.inter; + +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.math.GeoOrder; +import org.audiveris.omr.sheet.Scale; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sheet.rhythm.Voice; +import org.audiveris.omr.sig.relation.ChordOrnamentRelation; +import org.audiveris.omr.sig.relation.Link; +import org.audiveris.omr.sig.relation.Relation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Point; +import java.awt.Rectangle; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code OrnamentInter} represents an ornament interpretation. + * (TR, TURN, TURN_INVERTED, TURN_UP, TURN_SLASH, MORDENT, MORDENT_INVERTED) + * and perhaps GRACE_NOTE_SLASH, GRACE_NOTE + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "ornament") +public class OrnamentInter + extends AbstractInter +{ + + private static final Logger logger = LoggerFactory.getLogger(OrnamentInter.class); + + /** + * Creates a new OrnamentInter object. + * + * @param glyph underlying glyph + * @param shape precise shape (TR, TURN, TURN_INVERTED, TURN_UP, TURN_SLASH, MORDENT, + * MORDENT_INVERTED) and perhaps GRACE_NOTE_SLASH, GRACE_NOTE + * @param grade evaluation value + */ + public OrnamentInter (Glyph glyph, + Shape shape, + double grade) + { + super(glyph, (glyph != null) ? glyph.getBounds() : null, shape, grade); + } + + /** + * No-arg constructor meant for JAXB. + */ + private OrnamentInter () + { + } + + //--------// + // accept // + //--------// + @Override + public void accept (InterVisitor visitor) + { + visitor.visit(this); + } + + //---------------// + // checkAbnormal // + //---------------// + @Override + public boolean checkAbnormal () + { + // Check if a chord is connected + setAbnormal(!sig.hasRelation(this, ChordOrnamentRelation.class)); + + return isAbnormal(); + } + + //----------// + // getStaff // + //----------// + @Override + public Staff getStaff () + { + if (staff == null) { + for (Relation rel : sig.getRelations(this, ChordOrnamentRelation.class)) { + HeadChordInter chord = (HeadChordInter) sig.getOppositeInter(this, rel); + + return staff = chord.getStaff(); + } + } + + return staff; + } + + //----------// + // getVoice // + //----------// + @Override + public Voice getVoice () + { + for (Relation rel : sig.getRelations(this, ChordOrnamentRelation.class)) { + return sig.getOppositeInter(this, rel).getVoice(); + } + + return null; + } + + //-------------// + // searchLinks // + //-------------// + @Override + public Collection searchLinks (SystemInfo system, + boolean doit) + { + // Not very optimized! + List systemHeadChords = system.getSig().inters(HeadChordInter.class); + Collections.sort(systemHeadChords, Inters.byAbscissa); + + Link link = lookupLink(systemHeadChords); + + if (link == null) { + return Collections.emptyList(); + } + + if (doit) { + link.applyTo(this); + } + + return Collections.singleton(link); + } + + //-----------// + // internals // + //-----------// + @Override + protected String internals () + { + return super.internals() + " " + shape; + } + + //------------// + // lookupLink // + //------------// + /** + * Try to detect a link between this ornament instance and a HeadChord nearby. + * + * @param systemHeadChords ordered collection of head chords in system + * @return the link found or null + */ + private Link lookupLink (List systemHeadChords) + { + if (systemHeadChords.isEmpty()) { + return null; + } + + final SystemInfo system = systemHeadChords.get(0).getSig().getSystem(); + final Scale scale = system.getSheet().getScale(); + final int maxDx = scale.toPixels(ChordOrnamentRelation.getXOutGapMaximum(manual)); + final int maxDy = scale.toPixels(ChordOrnamentRelation.getYGapMaximum(manual)); + final Rectangle ornamentBox = getBounds(); + final Point ornamentCenter = getCenter(); + final Rectangle luBox = new Rectangle(ornamentCenter); + luBox.grow(maxDx, maxDy); + + final List chords = Inters.intersectedInters( + systemHeadChords, + GeoOrder.BY_ABSCISSA, + luBox); + + if (chords.isEmpty()) { + return null; + } + + ChordOrnamentRelation bestRel = null; + Inter bestChord = null; + double bestYGap = Double.MAX_VALUE; + + for (Inter chord : chords) { + Rectangle chordBox = chord.getBounds(); + + // The ornament cannot intersect the chord + if (chordBox.intersects(ornamentBox)) { + continue; + } + + Point center = chord.getCenter(); + + // Select proper chord reference point (top or bottom) + int yRef = (ornamentCenter.y > center.y) ? (chordBox.y + chordBox.height) : chordBox.y; + double xGap = Math.abs(center.x - ornamentCenter.x); + double yGap = Math.abs(yRef - ornamentCenter.y); + ChordOrnamentRelation rel = new ChordOrnamentRelation(); + rel.setOutGaps(scale.pixelsToFrac(xGap), scale.pixelsToFrac(yGap), manual); + + if (rel.getGrade() >= rel.getMinGrade()) { + if ((bestRel == null) || (bestYGap > yGap)) { + bestRel = rel; + bestChord = chord; + bestYGap = yGap; + } + } + } + + if (bestRel != null) { + return new Link(bestChord, bestRel, false); + } + + return null; + } + + //------------------// + // createValidAdded // + //------------------// + /** + * (Try to) create and add a valid OrnamentInter. + *

                                                                                                                                + * TODO: this is to be refined for GRACE ornaments which are located on left side of chord. + * + * @param glyph underlying glyph + * @param shape detected shape + * @param grade assigned grade + * @param system containing system + * @param systemHeadChords system head chords, ordered by abscissa + * @return the created articulation or null + */ + public static OrnamentInter createValidAdded (Glyph glyph, + Shape shape, + double grade, + SystemInfo system, + List systemHeadChords) + { + if (glyph.isVip()) { + logger.info("VIP OrnamentInter create {} as {}", glyph, shape); + } + + OrnamentInter orn = new OrnamentInter(glyph, shape, grade); + Link link = orn.lookupLink(systemHeadChords); + + if (link != null) { + system.getSig().addVertex(orn); + link.applyTo(orn); + + return orn; + } + + return null; + } +} diff --git a/src/main/org/audiveris/omr/sig/inter/PedalInter.java b/src/main/org/audiveris/omr/sig/inter/PedalInter.java index 8575735c2..b5a482548 100644 --- a/src/main/org/audiveris/omr/sig/inter/PedalInter.java +++ b/src/main/org/audiveris/omr/sig/inter/PedalInter.java @@ -23,6 +23,7 @@ import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sig.relation.ChordPedalRelation; import org.audiveris.omr.sig.relation.Relation; @@ -37,7 +38,6 @@ public class PedalInter extends AbstractDirectionInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PedalInter} object. @@ -61,7 +61,6 @@ private PedalInter () super(null, null, null, 0); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -92,6 +91,21 @@ public AbstractChordInter getChord () return null; } + //----------// + // getStaff // + //----------// + @Override + public Staff getStaff () + { + if (staff == null) { + if (sig != null) { + staff = sig.getSystem().getStaffAtOrAbove(getCenter()); + } + } + + return staff; + } + //-----------// // internals // //-----------// diff --git a/src/main/org/audiveris/omr/sig/inter/PluckingInter.java b/src/main/org/audiveris/omr/sig/inter/PluckingInter.java index 26bcb26dc..046c2064c 100644 --- a/src/main/org/audiveris/omr/sig/inter/PluckingInter.java +++ b/src/main/org/audiveris/omr/sig/inter/PluckingInter.java @@ -37,13 +37,11 @@ public class PluckingInter extends AbstractInter implements StringSymbolInter { - //~ Instance fields ---------------------------------------------------------------------------- /** Letter for the finger. (p, i, m, a) */ @XmlAttribute private final char letter; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PluckingInter} object. * @@ -67,7 +65,6 @@ private PluckingInter () this.letter = 0; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/RepeatDotInter.java b/src/main/org/audiveris/omr/sig/inter/RepeatDotInter.java index 828d86799..c67696f2f 100644 --- a/src/main/org/audiveris/omr/sig/inter/RepeatDotInter.java +++ b/src/main/org/audiveris/omr/sig/inter/RepeatDotInter.java @@ -36,7 +36,6 @@ public class RepeatDotInter extends AbstractPitchedInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new RepeatDotInter object. @@ -61,7 +60,6 @@ private RepeatDotInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/RestChordInter.java b/src/main/org/audiveris/omr/sig/inter/RestChordInter.java index bac09ab81..515ec38dd 100644 --- a/src/main/org/audiveris/omr/sig/inter/RestChordInter.java +++ b/src/main/org/audiveris/omr/sig/inter/RestChordInter.java @@ -32,7 +32,6 @@ public class RestChordInter extends AbstractChordInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code RestChordInter} object. @@ -45,13 +44,12 @@ public RestChordInter (double grade) } /** - * No-arg constructor meant for JAXB (and for DummyWholeRestChordInter subclass). + * No-arg constructor meant for JAXB (and for FakeChord subclass). */ protected RestChordInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/RestInter.java b/src/main/org/audiveris/omr/sig/inter/RestInter.java index 416af432e..0c0662eed 100644 --- a/src/main/org/audiveris/omr/sig/inter/RestInter.java +++ b/src/main/org/audiveris/omr/sig/inter/RestInter.java @@ -43,8 +43,7 @@ import javax.xml.bind.annotation.XmlRootElement; /** - * Class {@code RestInter} represents a rest. - * TODO: Should be closer to AbstractNoteInter? + * Class {@code RestInter} represents a rest note. * * @author Hervé Bitteur */ @@ -52,13 +51,11 @@ public class RestInter extends AbstractNoteInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(RestInter.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new RestInter object. * @@ -84,7 +81,6 @@ protected RestInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -94,6 +90,29 @@ public void accept (InterVisitor visitor) visitor.visit(this); } + //----------// + // getChord // + //----------// + /** + * Report the containing rest chord. + * + * @return containing rest chord + */ + @Override + public RestChordInter getChord () + { + return (RestChordInter) getEnsemble(); + } + + //-----------// + // internals // + //-----------// + @Override + protected String internals () + { + return super.internals() + " " + shape; + } + //-------------// // createValid // //-------------// @@ -238,40 +257,18 @@ public boolean check (Inter inter) return restInter; } - //----------// - // getChord // - //----------// - @Override - public RestChordInter getChord () - { - return (RestChordInter) getEnsemble(); - } - - //-----------// - // internals // - //-----------// - @Override - protected String internals () - { - return super.internals() + " " + shape; - } - - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ - private final Constant.Double suspiciousPitchPosition = new Constant.Double( - "PitchPosition", - 2.0, - "Maximum absolute pitch position for a rest to avoid additional checks"); + private final Constant.Double suspiciousPitchPosition = new Constant.Double("PitchPosition", + 2.0, + "Maximum absolute pitch position for a rest to avoid additional checks"); - private final Scale.Fraction minInterChordDx = new Scale.Fraction( - 0.5, - "Minimum horizontal delta between two chords"); + private final Scale.Fraction minInterChordDx = new Scale.Fraction(0.5, + "Minimum horizontal delta between two chords"); } } diff --git a/src/main/org/audiveris/omr/sig/inter/SegmentInter.java b/src/main/org/audiveris/omr/sig/inter/SegmentInter.java index d1817eae1..6b1d7e17b 100644 --- a/src/main/org/audiveris/omr/sig/inter/SegmentInter.java +++ b/src/main/org/audiveris/omr/sig/inter/SegmentInter.java @@ -23,7 +23,6 @@ import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.sheet.curve.SegmentInfo; -import org.audiveris.omr.sig.BasicImpacts; import org.audiveris.omr.sig.GradeImpacts; import javax.xml.bind.annotation.XmlRootElement; @@ -37,11 +36,9 @@ public class SegmentInter extends AbstractInter { - //~ Instance fields ---------------------------------------------------------------------------- private final SegmentInfo info; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SegmentInter object. * @@ -64,7 +61,6 @@ private SegmentInter () this.info = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -77,25 +73,27 @@ public void accept (InterVisitor visitor) //---------// // getInfo // //---------// + /** + * Report the related building information. + * + * @return segment building info + */ public SegmentInfo getInfo () { return info; } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Impacts // //---------// public static class Impacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{"dist"}; private static final double[] WEIGHTS = new double[]{1}; - //~ Constructors --------------------------------------------------------------------------- public Impacts (double dist) { super(NAMES, WEIGHTS); diff --git a/src/main/org/audiveris/omr/sig/inter/SentenceInter.java b/src/main/org/audiveris/omr/sig/inter/SentenceInter.java index 3b5d7c96f..f8b4a4b52 100644 --- a/src/main/org/audiveris/omr/sig/inter/SentenceInter.java +++ b/src/main/org/audiveris/omr/sig/inter/SentenceInter.java @@ -22,6 +22,7 @@ package org.audiveris.omr.sig.inter; import org.audiveris.omr.glyph.Grades; +import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.sheet.Skew; import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sheet.SystemInfo; @@ -42,7 +43,6 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import org.audiveris.omr.glyph.Shape; /** * Class {@code SentenceInter} represents a full sentence of words. @@ -59,13 +59,11 @@ public class SentenceInter extends AbstractInter implements InterEnsemble { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - SentenceInter.class); + private static final Logger logger = LoggerFactory.getLogger(SentenceInter.class); /** For ordering sentences by their de-skewed ordinate. */ - public static Comparator byOrdinate = new Comparator() + public static final Comparator byOrdinate = new Comparator() { @Override public int compare (SentenceInter s1, @@ -79,8 +77,6 @@ public int compare (SentenceInter s1, } }; - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -93,7 +89,6 @@ public int compare (SentenceInter s1, @XmlAttribute protected TextRole role; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SentenceInter} object. * @@ -136,7 +131,6 @@ protected SentenceInter () this.role = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -183,26 +177,6 @@ public Staff assignStaff (SystemInfo system, return staff; } - //--------// - // create // - //--------// - /** - * Create a {@code SentenceInter} from a TextLine. - * - * @param line the OCR'ed text line - * @return the sentence inter - */ - public static SentenceInter create (TextLine line) - { - SentenceInter sentence = new SentenceInter( - line.getBounds(), - line.getConfidence() * Grades.intrinsicRatio, - line.getMeanFont(), - line.getRole()); - - return sentence; - } - //-----------// // getBounds // //-----------// @@ -232,6 +206,11 @@ public int getExportedFontSize () //--------------// // getFirstWord // //--------------// + /** + * Report the first word in this sentence. + * + * @return first word or null if sentence is empty + */ public WordInter getFirstWord () { final List words = getMembers(); @@ -246,6 +225,11 @@ public WordInter getFirstWord () //-------------// // getLastWord // //-------------// + /** + * Report the last word in this sentence + * + * @return last word or null if empty + */ public WordInter getLastWord () { final List words = getMembers(); @@ -260,6 +244,11 @@ public WordInter getLastWord () //-------------// // getLocation // //-------------// + /** + * Return the starting point of the first word. + * + * @return starting point + */ public Point getLocation () { final Inter first = getFirstWord(); @@ -306,9 +295,27 @@ public TextRole getRole () return role; } + //---------// + // setRole // + //---------// + /** + * Assign a new role. + * + * @param role the new role + */ + public void setRole (TextRole role) + { + this.role = role; + } + //----------// // getValue // //----------// + /** + * Report sentence text content, built out of contained words. + * + * @return text content + */ public String getValue () { StringBuilder sb = null; @@ -356,19 +363,6 @@ public void removeMember (Inter member) EnsembleHelper.removeMember(this, member); } - //---------// - // setRole // - //---------// - /** - * Assign a new role. - * - * @param role the new role - */ - public void setRole (TextRole role) - { - this.role = role; - } - //-------------// // shapeString // //-------------// @@ -391,4 +385,24 @@ protected String internals () return sb.toString(); } + + //--------// + // create // + //--------// + /** + * Create a {@code SentenceInter} from a TextLine. + * + * @param line the OCR'ed text line + * @return the sentence inter + */ + public static SentenceInter create (TextLine line) + { + SentenceInter sentence = new SentenceInter( + line.getBounds(), + line.getConfidence() * Grades.intrinsicRatio, + line.getMeanFont(), + line.getRole()); + + return sentence; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/SlurInter.java b/src/main/org/audiveris/omr/sig/inter/SlurInter.java index 7075af7e2..2c86bc6f8 100644 --- a/src/main/org/audiveris/omr/sig/inter/SlurInter.java +++ b/src/main/org/audiveris/omr/sig/inter/SlurInter.java @@ -40,7 +40,6 @@ import org.audiveris.omr.sheet.rhythm.Measure; import org.audiveris.omr.sheet.rhythm.MeasureStack; import org.audiveris.omr.sheet.rhythm.Voice; -import org.audiveris.omr.sig.BasicImpacts; import org.audiveris.omr.sig.GradeImpacts; import org.audiveris.omr.sig.relation.Link; import org.audiveris.omr.sig.relation.Relation; @@ -87,12 +86,10 @@ public class SlurInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - SlurInter.class); + private static final Logger logger = LoggerFactory.getLogger(SlurInter.class); /** To sort slurs vertically within a measure. */ public static final Comparator verticalComparator = new Comparator() @@ -168,7 +165,7 @@ public boolean check (SlurInter slur) Staff staff = system.getClosestStaff(end); Measure measure = stack.getMeasureAt(staff); int middle = (staff.getHeaderStop() + measure.getAbscissa(LEFT, staff) - + measure.getWidth()) / 2; + + measure.getWidth()) / 2; if (end.getX() < middle) { return true; @@ -180,8 +177,6 @@ public boolean check (SlurInter slur) } }; - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -215,7 +210,6 @@ public boolean check (SlurInter slur) /** Physical characteristics. */ private SlurInfo info; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SlurInter} object. * @@ -257,7 +251,6 @@ private SlurInter () info = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -373,12 +366,26 @@ public boolean checkAbnormal () //---------------// /** * Check whether the cross-system slur connection is a tie. + *

                                                                                                                                + * This method assumes that 'this' and 'prevSlur' belong to the same logical part but in + * separate systems. * * @param prevSlur slur at the end of previous system (perhaps in previous sheet) */ public void checkCrossTie (SlurInter prevSlur) { - boolean result = HeadInter.haveSameHeight(prevSlur.getHead(LEFT), this.getHead(RIGHT)); + final HeadInter h1 = prevSlur.getHead(LEFT); + final HeadInter h2 = prevSlur.getHead(RIGHT); + final boolean result; + + if (!areTieCompatible(h1, h2)) { + result = false; + } else { + // Check staff continuity (we are within the same logical part) + Staff s1 = h1.getStaff(); + Staff s2 = h2.getStaff(); + result = s1.getIndexInPart() == s2.getIndexInPart(); + } prevSlur.setTie(result); this.setTie(result); @@ -405,9 +412,10 @@ public void checkStaffTie (List systemHeadChords) HeadInter h1 = getHead(LEFT); HeadInter h2 = getHead(RIGHT); - boolean result = (h1 != null) && (h2 != null) && (h1.getStaff() == h2.getStaff()) - && HeadInter.haveSameHeight(h1, h2) - && isSpaceClear(h1, h2, systemHeadChords); + boolean result = (h1 != null) && (h2 != null) + && (h1.getStaff() == h2.getStaff()) + && areTieCompatible(h1, h2) + && isSpaceClear(h1, h2, systemHeadChords); setTie(result); if (isVip()) { @@ -415,29 +423,6 @@ public void checkStaffTie (List systemHeadChords) } } - //----------------// - // discardOrphans // - //----------------// - /** - * Discard every orphan left over, unless it's a manual one. - * - * @param orphans the orphan slurs left over - * @param side side of missing connection - */ - public static void discardOrphans (List orphans, - HorizontalSide side) - { - for (SlurInter slur : orphans) { - if (slur.isVip()) { - logger.info("VIP could not {}-connect {}", side, slur); - } - - if (!slur.isManual()) { - slur.remove(); - } - } - } - //----------// // getCurve // //----------// @@ -540,6 +525,11 @@ public SlurHeadRelation getHeadRelation (HorizontalSide side) //---------// // getInfo // //---------// + /** + * Report the related physical information. + * + * @return build info + */ public SlurInfo getInfo () { return info; @@ -699,6 +689,25 @@ public boolean isTie () return tie; } + //--------// + // setTie // + //--------// + /** + * Set this slur as being a tie. + * + * @param tie new tie value + */ + public void setTie (boolean tie) + { + if (this.tie != tie) { + this.tie = tie; + + if (sig != null) { + sig.getSystem().getSheet().getStub().setModified(true); + } + } + } + //--------// // remove // //--------// @@ -792,23 +801,51 @@ public void setGlyph (Glyph glyph) } } - //--------// - // setTie // - //--------// + //------------------// + // areTieCompatible // + //------------------// /** - * Set this slur as being a tie. + * Check whether two heads represent the same height + * (same octave, same step, same key fifths) + * but accidental alteration is NOT considered. + *

                                                                                                                                + * Potential accidental alterations must be taken care of by the caller, if so needed. * - * @param tie new tie value + * @param h1 first head + * @param h2 second head, down the score + * @return true if the heads are equivalent. */ - public void setTie (boolean tie) + private static boolean areTieCompatible (HeadInter h1, + HeadInter h2) { - if (this.tie != tie) { - this.tie = tie; + if ((h1 == null) || (h2 == null)) { + return false; + } - if (sig != null) { - sig.getSystem().getSheet().getStub().setModified(true); + // Step + if (h1.getStep() != h2.getStep()) { + return false; + } + + // Octave + if (h1.getOctave() != h2.getOctave()) { + return false; + } + + // Accidental + AlterInter a1 = h1.getMeasureAccidental(); + AlterInter a2 = h2.getMeasureAccidental(); + + if (a1 == null) { + if (a2 != null) { + return false; } + } else if (a2 != null && a2.getShape() != a1.getShape()) { + return false; } + + // Let's caller handle staff / part / system compatibility + return true; } //-------------// @@ -839,10 +876,8 @@ private Collection lookupLinks (List systemHeads, Map sideAreas = slurLinker.defineAreaPair(this); // Retrieve candidate chords - Map> chords = new EnumMap>( - HorizontalSide.class); - List systemChords = system.getSig().inters( - HeadChordInter.class); + Map> chords = new EnumMap<>(HorizontalSide.class); + List systemChords = system.getSig().inters(HeadChordInter.class); for (HorizontalSide side : HorizontalSide.values()) { Rectangle box = sideAreas.get(side).getBounds(); @@ -860,7 +895,7 @@ private Collection lookupLinks (List systemHeads, return Collections.emptySet(); } - List links = new ArrayList(); + List links = new ArrayList<>(); for (Link link : linkPair.values()) { if (link != null) { @@ -871,22 +906,45 @@ private Collection lookupLinks (List systemHeads, return links; } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------------// + // discardOrphans // + //----------------// + /** + * Discard every orphan left over, unless it's a manual one. + * + * @param orphans the orphan slurs left over + * @param side side of missing connection + */ + public static void discardOrphans (List orphans, + HorizontalSide side) + { + for (SlurInter slur : orphans) { + if (slur.isVip()) { + logger.info("VIP could not {}-connect {}", side, slur); + } + + if (!slur.isManual()) { + slur.remove(); + } + } + } + //---------// // Impacts // //---------// public static class Impacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{ - "dist", "angle", "width", "height", "vert" - }; + "dist", + "angle", + "width", + "height", + "vert"}; private static final double[] WEIGHTS = new double[]{3, 1, 1, 1, 1}; - //~ Constructors --------------------------------------------------------------------------- public Impacts (double dist, double angle, double width, @@ -905,10 +963,9 @@ public Impacts (double dist, //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxDeltaY = new Scale.Fraction( 4, diff --git a/src/main/org/audiveris/omr/sig/inter/SmallBeamInter.java b/src/main/org/audiveris/omr/sig/inter/SmallBeamInter.java index b481b444a..f1d1d4a64 100644 --- a/src/main/org/audiveris/omr/sig/inter/SmallBeamInter.java +++ b/src/main/org/audiveris/omr/sig/inter/SmallBeamInter.java @@ -37,7 +37,6 @@ public class SmallBeamInter extends AbstractBeamInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SmallBeamInter object. @@ -61,7 +60,6 @@ private SmallBeamInter () super(null, null, null, 0); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/SmallChordInter.java b/src/main/org/audiveris/omr/sig/inter/SmallChordInter.java index 5e9a01c14..87715abcb 100644 --- a/src/main/org/audiveris/omr/sig/inter/SmallChordInter.java +++ b/src/main/org/audiveris/omr/sig/inter/SmallChordInter.java @@ -33,7 +33,6 @@ public class SmallChordInter extends HeadChordInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SmallChordInter} object. @@ -52,7 +51,6 @@ private SmallChordInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/sig/inter/SmallFlagInter.java b/src/main/org/audiveris/omr/sig/inter/SmallFlagInter.java index 67c3eb069..f43068903 100644 --- a/src/main/org/audiveris/omr/sig/inter/SmallFlagInter.java +++ b/src/main/org/audiveris/omr/sig/inter/SmallFlagInter.java @@ -23,6 +23,7 @@ import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.sig.relation.FlagStemRelation; import javax.xml.bind.annotation.XmlRootElement; @@ -35,7 +36,6 @@ public class SmallFlagInter extends AbstractFlagInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SmallFlagInter object. @@ -58,7 +58,6 @@ private SmallFlagInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -68,6 +67,29 @@ public void accept (InterVisitor visitor) visitor.visit(this); } + //-------// + // added // + //-------// + @Override + public void added () + { + super.added(); + + setAbnormal(true); // No stem linked yet + } + + //---------------// + // checkAbnormal // + //---------------// + @Override + public boolean checkAbnormal () + { + // Check if flag is connected to a stem + setAbnormal(!sig.hasRelation(this, FlagStemRelation.class)); + + return isAbnormal(); + } + //-----------// // internals // //-----------// diff --git a/src/main/org/audiveris/omr/sig/inter/StaffBarlineInter.java b/src/main/org/audiveris/omr/sig/inter/StaffBarlineInter.java index 63fb571fb..622f68357 100644 --- a/src/main/org/audiveris/omr/sig/inter/StaffBarlineInter.java +++ b/src/main/org/audiveris/omr/sig/inter/StaffBarlineInter.java @@ -39,6 +39,9 @@ import org.audiveris.omr.util.Entities; import org.audiveris.omr.util.HorizontalSide; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; @@ -73,18 +76,18 @@ */ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "staff-barline") -public class StaffBarlineInter +public final class StaffBarlineInter extends AbstractInter implements InterEnsemble { - //~ Instance fields ---------------------------------------------------------------------------- + + private static final Logger logger = LoggerFactory.getLogger(StaffBarlineInter.class); // Transient data //--------------- // private Style style; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code StaffBarlineInter} object from a shape. * @@ -144,7 +147,6 @@ private StaffBarlineInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -171,6 +173,12 @@ public void addMember (Inter member) //----------// // contains // //----------// + /** + * Tell whether this StaffBarlineInter contains the provided barline. + * + * @param barline provided barline + * @return true if so + */ public boolean contains (BarlineInter barline) { return sig.getRelation(this, barline, Containment.class) != null; @@ -189,35 +197,6 @@ public Rectangle getBounds () return new Rectangle(bounds); } - //------------------------// - // getClosestStaffBarline // - //------------------------// - /** - * From a provided StaffBarline collection, report the one which has the closest - * abscissa to a provided point. - * - * @param bars the collection of StaffBarlineInter to browse - * @param point the reference point - * @return the abscissa-wise closest barline - */ - public static StaffBarlineInter getClosestStaffBarline (Collection bars, - Point point) - { - StaffBarlineInter bestBar = null; - int bestDx = Integer.MAX_VALUE; - - for (StaffBarlineInter bar : bars) { - int dx = Math.abs(bar.getCenter().x - point.x); - - if (dx < bestDx) { - bestDx = dx; - bestBar = bar; - } - } - - return bestBar; - } - //-----------// // getEnding // //-----------// @@ -273,7 +252,7 @@ public Set getFermatas () for (Relation rel : sig.getRelations(bar, FermataBarRelation.class)) { if (fermatas == null) { - fermatas = new LinkedHashSet(); + fermatas = new LinkedHashSet<>(); } fermatas.add((FermataInter) sig.getOppositeInter(bar, rel)); @@ -284,7 +263,7 @@ public Set getFermatas () // Use of direct relation for (Relation rel : sig.getRelations(this, FermataBarRelation.class)) { if (fermatas == null) { - fermatas = new LinkedHashSet(); + fermatas = new LinkedHashSet<>(); } fermatas.add((FermataInter) sig.getOppositeInter(this, rel)); @@ -301,6 +280,11 @@ public Set getFermatas () //------------// // getLeftBar // //------------// + /** + * Report the starting Barline in this StaffBarlineInter. + * + * @return first barline + */ public BarlineInter getLeftBar () { final List bars = getMembers(); @@ -337,6 +321,11 @@ public int getLeftX () //------------// // getMeasure // //------------// + /** + * Report the containing measure. + * + * @return containing measure + */ public Measure getMeasure () { getPart(); @@ -367,6 +356,25 @@ public List getMembers () return EnsembleHelper.getMembers(this, Inters.byCenterAbscissa); } + //--------------// + // getMiddleBar // + //--------------// + /** + * Report the middle barline if any. + * + * @return middle barline or null + */ + public BarlineInter getMiddleBar () + { + final List bars = getMembers(); + + if (bars.size() != 3) { + return null; + } + + return (BarlineInter) bars.get(1); + } + //----------------// // getPartBarline // //----------------// @@ -425,14 +433,12 @@ public Point getReferenceCenter () return GeoUtil.centerOf(bounds); case DOUBLE_BARLINE: + case REVERSE_FINAL_BARLINE: return new Point(bounds.x + (int) Math.rint(0.9 * bounds.width), y); case FINAL_BARLINE: return new Point(bounds.x + (int) Math.rint(0.7 * bounds.width), y); - case REVERSE_FINAL_BARLINE: - return new Point(bounds.x + (int) Math.rint(0.9 * bounds.width), y); - case LEFT_REPEAT_SIGN: case RIGHT_REPEAT_SIGN: case BACK_TO_BACK_REPEAT_SIGN: @@ -462,7 +468,7 @@ public List getRelatedInters (Class relationClass) for (Inter bar : bars) { for (Relation rel : sig.getRelations(bar, relationClass)) { if (related == null) { - related = new ArrayList(); + related = new ArrayList<>(); } related.add(sig.getOppositeInter(bar, rel)); @@ -472,7 +478,7 @@ public List getRelatedInters (Class relationClass) if (bars.isEmpty()) { for (Relation rel : sig.getRelations(this, relationClass)) { if (related == null) { - related = new ArrayList(); + related = new ArrayList<>(); } related.add(sig.getOppositeInter(this, rel)); @@ -489,6 +495,11 @@ public List getRelatedInters (Class relationClass) //-------------// // getRightBar // //-------------// + /** + * report the stopping barline + * + * @return last barline in StaffBarline + */ public BarlineInter getRightBar () { final List bars = getMembers(); @@ -525,6 +536,11 @@ public int getRightX () //----------// // getStyle // //----------// + /** + * Report the StaffBarline style. + * + * @return style (like LIGHT_HEAVY, ...) in line with MusicXML definitions + */ public Style getStyle () { if (style == null) { @@ -544,7 +560,7 @@ public Style getStyle () */ public List getSystemBarline () { - final List systemBarline = new ArrayList(); + final List systemBarline = new ArrayList<>(); Measure measure = getMeasure(); // Measure barline? @@ -595,6 +611,11 @@ public List getSystemBarline () //---------------// // hasDotsOnLeft // //---------------// + /** + * Tell whether there are repeat dots on the left side of this bar. + * + * @return true if so + */ public boolean hasDotsOnLeft () { if ((shape == Shape.RIGHT_REPEAT_SIGN) || (shape == Shape.BACK_TO_BACK_REPEAT_SIGN)) { @@ -620,6 +641,11 @@ public boolean hasDotsOnLeft () //----------------// // hasDotsOnRight // //----------------// + /** + * Tell whether there are repeat dots on the right side of this bar. + * + * @return true if so + */ public boolean hasDotsOnRight () { if ((shape == Shape.LEFT_REPEAT_SIGN) || (shape == Shape.BACK_TO_BACK_REPEAT_SIGN)) { @@ -670,17 +696,29 @@ public void invalidateCache () //--------------// // isLeftRepeat // //--------------// + /** + * Tell whether this barline is a measure left repeat (dots on right of barline). + * + * @return true if so + */ public boolean isLeftRepeat () { - return (getStyle() == HEAVY_LIGHT) && hasDotsOnRight(); + return ((getStyle() == HEAVY_LIGHT) || (getStyle() == LIGHT_HEAVY_LIGHT)) + && hasDotsOnRight(); } //---------------// // isRightRepeat // //---------------// + /** + * Tell whether this barline is a measure right repeat (dots on left of barline). + * + * @return true if so + */ public boolean isRightRepeat () { - return (getStyle() == LIGHT_HEAVY) && hasDotsOnLeft(); + return ((getStyle() == LIGHT_HEAVY) || (getStyle() == LIGHT_HEAVY_LIGHT)) + && hasDotsOnLeft(); } //--------------// @@ -730,15 +768,25 @@ private Style computeStyle () case 1: return (getLeftBar().getShape() == Shape.THIN_BARLINE) ? REGULAR : HEAVY; - case 2: { + case 2: + if (getLeftBar().getShape() == Shape.THIN_BARLINE) { return (getRightBar().getShape() == Shape.THIN_BARLINE) ? LIGHT_LIGHT : LIGHT_HEAVY; } else { return (getRightBar().getShape() == Shape.THIN_BARLINE) ? HEAVY_LIGHT : HEAVY_HEAVY; } - } + + case 3: + + if ((getLeftBar().getShape() == Shape.THIN_BARLINE) && (getMiddleBar() + .getShape() == Shape.THICK_BARLINE) + && (getRightBar().getShape() == Shape.THIN_BARLINE)) { + return LIGHT_HEAVY_LIGHT; + } default: + logger.warn("Unknown style for {}", this); + return null; } } @@ -774,7 +822,38 @@ private Style toStyle (Shape shape) return Style.LIGHT_HEAVY; // Bof! + dots on both sides default: + logger.warn("No style for barline shape {}", shape); + return null; } } + + //------------------------// + // getClosestStaffBarline // + //------------------------// + /** + * From a provided StaffBarline collection, report the one which has the closest + * abscissa to a provided point. + * + * @param bars the collection of StaffBarlineInter to browse + * @param point the reference point + * @return the abscissa-wise closest barline + */ + public static StaffBarlineInter getClosestStaffBarline (Collection bars, + Point point) + { + StaffBarlineInter bestBar = null; + int bestDx = Integer.MAX_VALUE; + + for (StaffBarlineInter bar : bars) { + int dx = Math.abs(bar.getCenter().x - point.x); + + if (dx < bestDx) { + bestDx = dx; + bestBar = bar; + } + } + + return bestBar; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/StemInter.java b/src/main/org/audiveris/omr/sig/inter/StemInter.java index 08e501760..2ad5f3a48 100644 --- a/src/main/org/audiveris/omr/sig/inter/StemInter.java +++ b/src/main/org/audiveris/omr/sig/inter/StemInter.java @@ -23,7 +23,6 @@ import ij.process.ByteProcessor; -import org.audiveris.omr.glyph.BasicGlyph; import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; import static org.audiveris.omr.run.Orientation.VERTICAL; @@ -31,6 +30,7 @@ import org.audiveris.omr.run.RunTableFactory; import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.sheet.Staff; import org.audiveris.omr.sheet.rhythm.Voice; import org.audiveris.omr.sig.GradeImpacts; import org.audiveris.omr.sig.relation.AbstractStemConnection; @@ -46,6 +46,7 @@ import org.audiveris.omr.util.HorizontalSide; import static org.audiveris.omr.util.HorizontalSide.LEFT; import static org.audiveris.omr.util.HorizontalSide.RIGHT; +import org.audiveris.omr.util.Jaxb; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,6 +63,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * Class {@code StemInter} represents Stem interpretations. @@ -72,27 +74,25 @@ public class StemInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(StemInter.class); /** Anchor vertical margin, relative to head height. */ private static final double ANCHOR_MARGIN_RATIO = 0.67; - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // /** Top point. */ @XmlElement + @XmlJavaTypeAdapter(Jaxb.Point2DAdapter.class) private Point2D top; /** Bottom point. */ @XmlElement + @XmlJavaTypeAdapter(Jaxb.Point2DAdapter.class) private Point2D bottom; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new StemInter object. * @@ -132,7 +132,6 @@ private StemInter () super(null, null, null, null); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -238,7 +237,7 @@ public int computeDirection () { Scale scale = sig.getSystem().getSheet().getScale(); final Line2D stemLine = computeExtendedLine(); - final List links = new ArrayList( + final List links = new ArrayList<>( sig.getRelations(this, AbstractStemConnection.class)); sig.sortBySource(links); @@ -315,25 +314,6 @@ public Line2D computeExtendedLine () return new Line2D.Double(extTop, extBottom); } - //-----------// - // duplicate // - //-----------// - public StemInter duplicate () - { - StemInter clone = new StemInter(glyph, impacts); - clone.setGlyph(this.glyph); - clone.setMirror(this); - - if (impacts == null) { - clone.setGrade(this.grade); - } - - sig.addVertex(clone); - setMirror(clone); - - return clone; - } - //----------------// // extractSubStem // //----------------// @@ -367,8 +347,7 @@ public StemInter extractSubStem (int y1, final RunTable table = factory.createTable(buffer, roi).trim(stemOffset); final int x = glyph.getLeft() + stemOffset.x; final int y = glyph.getTop() + roi.y + stemOffset.y; - final Glyph g = sheet.getGlyphIndex().registerOriginal( - new BasicGlyph(x, y, table)); + final Glyph g = sheet.getGlyphIndex().registerOriginal(new Glyph(x, y, table)); // Create sub-stem final StemInter subStem = new StemInter(g, getGrade()); @@ -388,7 +367,7 @@ public StemInter extractSubStem (int y1, */ public Set getBeams () { - final Set set = new LinkedHashSet(); + final Set set = new LinkedHashSet<>(); for (Relation relation : sig.getRelations(this, BeamStemRelation.class)) { set.add((AbstractBeamInter) sig.getEdgeSource(relation)); @@ -414,7 +393,8 @@ public Point2D getBottom () /** * Report the chord(s) currently attached to the provided stem. *

                                                                                                                                - * We can have:

                                                                                                                                  + * We can have: + *
                                                                                                                                    *
                                                                                                                                  • No chord found, simply because this stem has not yet been processed.
                                                                                                                                  • *
                                                                                                                                  • One chord found, this is the normal case.
                                                                                                                                  • *
                                                                                                                                  • Two chords found, when the same stem is "shared" by two chords (as in complex structures @@ -429,7 +409,7 @@ public List getChords () for (Relation rel : sig.getRelations(this, ChordStemRelation.class)) { if (chords == null) { - chords = new ArrayList(); + chords = new ArrayList<>(); } chords.add((HeadChordInter) sig.getOppositeInter(this, rel)); @@ -452,7 +432,7 @@ public List getChords () */ public Set getHeads () { - final Set set = new LinkedHashSet(); + final Set set = new LinkedHashSet<>(); for (Relation relation : sig.getRelations(this, HeadStemRelation.class)) { set.add((HeadInter) sig.getEdgeSource(relation)); @@ -474,14 +454,6 @@ public Line2D getMedian () return new Line2D.Double(top, bottom); } - //-------------// - // getMinGrade // - //-------------// - public static double getMinGrade () - { - return AbstractInter.getMinGrade(); - } - //--------// // getTop // //--------// @@ -521,7 +493,7 @@ public boolean isGraceStem () // First head tested is enough. return (headShape == Shape.NOTEHEAD_BLACK_SMALL) - || (headShape == Shape.NOTEHEAD_VOID_SMALL); + || (headShape == Shape.NOTEHEAD_VOID_SMALL); } return false; @@ -634,4 +606,47 @@ public void setGlyph (Glyph glyph) top = glyph.getStartPoint(VERTICAL); bottom = glyph.getStopPoint(VERTICAL); } + + //----------// + // getStaff // + //----------// + @Override + public Staff getStaff () + { + if (staff != null) { + return staff; + } + + // Check related chord(s) + Staff stemStaff = null; + + for (HeadChordInter chord : getChords()) { + Staff chordStaff = chord.getStaff(); + + if (chordStaff == null) { + return null; + } + + if (stemStaff != null && stemStaff != chordStaff) { + return null; + } + + stemStaff = chordStaff; + } + + return staff = stemStaff; + } + + //-------------// + // getMinGrade // + //-------------// + /** + * Report the minimum acceptable grade + * + * @return minimum grade + */ + public static double getMinGrade () + { + return AbstractInter.getMinGrade(); + } } diff --git a/src/main/org/audiveris/omr/sig/inter/StringSymbolInter.java b/src/main/org/audiveris/omr/sig/inter/StringSymbolInter.java index 3f1dd5d2d..217b34b19 100644 --- a/src/main/org/audiveris/omr/sig/inter/StringSymbolInter.java +++ b/src/main/org/audiveris/omr/sig/inter/StringSymbolInter.java @@ -57,7 +57,6 @@ public interface StringSymbolInter extends Inter { - //~ Methods ------------------------------------------------------------------------------------ /** * Report the string that resembles the symbol. diff --git a/src/main/org/audiveris/omr/sig/inter/TimeNumberInter.java b/src/main/org/audiveris/omr/sig/inter/TimeNumberInter.java index a04f0a9b1..6dd020ef0 100644 --- a/src/main/org/audiveris/omr/sig/inter/TimeNumberInter.java +++ b/src/main/org/audiveris/omr/sig/inter/TimeNumberInter.java @@ -35,7 +35,8 @@ import javax.xml.bind.annotation.XmlRootElement; /** - * Class {@code TimeNumberInter} represents a top or bottom number in a time signature. + * Class {@code TimeNumberInter} represents a top or bottom number + * (gathering one or several digits) in a time signature. * * @author Hervé Bitteur */ @@ -43,16 +44,13 @@ public class TimeNumberInter extends AbstractNumberInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Instance fields ---------------------------------------------------------------------------- /** Top or bottom. */ @XmlAttribute protected VerticalSide side; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TimeNumberInter object. * @@ -83,7 +81,6 @@ private TimeNumberInter () super((Glyph) null, null, 0); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -93,6 +90,41 @@ public void accept (InterVisitor visitor) visitor.visit(this); } + //---------// + // getSide // + //---------// + /** + * Report vertical position with respect to the staff time signature. + * + * @return TOP or BOTTOM + */ + public VerticalSide getSide () + { + return side; + } + + //---------// + // setSide // + //---------// + /** + * Set the vertical position with respect to the staff time signature. + * + * @param side the side to set + */ + public void setSide (VerticalSide side) + { + this.side = side; + } + + //-----------// + // internals // + //-----------// + @Override + protected String internals () + { + return super.internals() + " " + shape; + } + /** * (Try to) create a top or bottom number for time signature. * @@ -113,7 +145,7 @@ public static TimeNumberInter create (Glyph glyph, double absPitch = Math.abs(pitch); if ((absPitch < constants.minAbsolutePitch.getValue()) - || (absPitch > constants.maxAbsolutePitch.getValue())) { + || (absPitch > constants.maxAbsolutePitch.getValue())) { return null; } @@ -125,42 +157,12 @@ public static TimeNumberInter create (Glyph glyph, return inter; } - //---------// - // getSide // - //---------// - public VerticalSide getSide () - { - return side; - } - - //---------// - // setSide // - //---------// - /** - * @param side the side to set - */ - public void setSide (VerticalSide side) - { - this.side = side; - } - - //-----------// - // internals // - //-----------// - @Override - protected String internals () - { - return super.internals() + " " + shape; - } - - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Double minAbsolutePitch = new Constant.Double( "pitch", diff --git a/src/main/org/audiveris/omr/sig/inter/TimePairInter.java b/src/main/org/audiveris/omr/sig/inter/TimePairInter.java index 4f1a9528e..20c8fdf65 100644 --- a/src/main/org/audiveris/omr/sig/inter/TimePairInter.java +++ b/src/main/org/audiveris/omr/sig/inter/TimePairInter.java @@ -49,11 +49,9 @@ public class TimePairInter extends AbstractTimeInter implements InterEnsemble { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TimePairInter.class); - //~ Constructors ------------------------------------------------------------------------------- /** * (Private) constructor. * @@ -74,7 +72,6 @@ private TimePairInter () super(null, null, 0); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -84,33 +81,6 @@ public void accept (InterVisitor visitor) visitor.visit(this); } - //-------------// - // createAdded // - //-------------// - /** - * Create and add a {@code TimePairInter} object from its two halves. - * - * @param num numerator: non-null, registered in sig - * @param den denominator: non-null, registered in sig - * @return the created instance, already added to sig - */ - public static TimePairInter createAdded (TimeNumberInter num, - TimeNumberInter den) - { - double grade = 0.5 * (num.getGrade() + den.getGrade()); - TimePairInter pair = new TimePairInter(null, grade); - SIGraph sig = num.getSig(); - sig.addVertex(pair); - pair.addMember(num); - pair.addMember(den); - - if (pair.isVip()) { - logger.info("VIP created {} from num:{} den:{}", pair, num, den); - } - - return pair; - } - //-----------// // addMember // //-----------// @@ -251,8 +221,8 @@ public TimeRational getTimeRational () if (members.size() == 2) { timeRational = new TimeRational( - ((TimeNumberInter) members.get(0)).getValue(), - ((TimeNumberInter) members.get(1)).getValue()); + ((AbstractNumberInter) members.get(0)).getValue(), + ((AbstractNumberInter) members.get(1)).getValue()); } } @@ -302,4 +272,31 @@ public String shapeString () { return "TIME_SIG_" + getTimeRational(); } + + //-------------// + // createAdded // + //-------------// + /** + * Create and add a {@code TimePairInter} object from its two halves. + * + * @param num numerator: non-null, registered in sig + * @param den denominator: non-null, registered in sig + * @return the created instance, already added to sig + */ + public static TimePairInter createAdded (TimeNumberInter num, + TimeNumberInter den) + { + double grade = 0.5 * (num.getGrade() + den.getGrade()); + TimePairInter pair = new TimePairInter(null, grade); + SIGraph sig = num.getSig(); + sig.addVertex(pair); + pair.addMember(num); + pair.addMember(den); + + if (pair.isVip()) { + logger.info("VIP created {} from num:{} den:{}", pair, num, den); + } + + return pair; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/TimeWholeInter.java b/src/main/org/audiveris/omr/sig/inter/TimeWholeInter.java index e9f32c65b..24fc3ea36 100644 --- a/src/main/org/audiveris/omr/sig/inter/TimeWholeInter.java +++ b/src/main/org/audiveris/omr/sig/inter/TimeWholeInter.java @@ -45,7 +45,6 @@ public class TimeWholeInter extends AbstractTimeInter { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TimeWholeInter} object. @@ -73,7 +72,6 @@ private TimeWholeInter () super(null, null, 0); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -83,29 +81,6 @@ public void accept (InterVisitor visitor) visitor.visit(this); } - //--------// - // create // - //--------// - /** - * Create a TimeWholeInter. - * - * @param glyph underlying glyph - * @param shape precise shape - * @param grade evaluation value - * @param staff related staff - * @return the created instance or null if failed - */ - public static TimeWholeInter create (Glyph glyph, - Shape shape, - double grade, - Staff staff) - { - TimeWholeInter time = new TimeWholeInter(glyph, shape, grade); - time.setStaff(staff); - - return time; - } - //-----------------// // getSymbolBounds // //-----------------// @@ -150,4 +125,27 @@ public TimeWholeInter replicate (Staff targetStaff) return inter; } + + //--------// + // create // + //--------// + /** + * Create a TimeWholeInter. + * + * @param glyph underlying glyph + * @param shape precise shape + * @param grade evaluation value + * @param staff related staff + * @return the created instance or null if failed + */ + public static TimeWholeInter create (Glyph glyph, + Shape shape, + double grade, + Staff staff) + { + TimeWholeInter time = new TimeWholeInter(glyph, shape, grade); + time.setStaff(staff); + + return time; + } } diff --git a/src/main/org/audiveris/omr/sig/inter/TupletInter.java b/src/main/org/audiveris/omr/sig/inter/TupletInter.java index aab06ad04..ffd2939be 100644 --- a/src/main/org/audiveris/omr/sig/inter/TupletInter.java +++ b/src/main/org/audiveris/omr/sig/inter/TupletInter.java @@ -61,20 +61,17 @@ public class TupletInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(TupletInter.class); - //~ Instance fields ---------------------------------------------------------------------------- // Factor lazily computed private DurationFactor durationFactor; /** Base duration. Lazily computed. */ private Rational baseDuration; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TupletInter} object. * @@ -96,7 +93,6 @@ private TupletInter () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -142,46 +138,14 @@ public boolean checkAbnormal () return isAbnormal(); } - //-------------// - // createValid // - //-------------// - /** - * (Try to) create a tuplet inter, checking that there is at least one (head) chord - * nearby. - * - * @param glyph the candidate tuplet glyph - * @param shape TUPLET_THREE or TUPLET_SIX - * @param grade the interpretation quality - * @param system the related system - * @param systemChords abscissa-ordered list of chords in this system - * @return the create TupletInter or null - */ - public static TupletInter createValid (Glyph glyph, - Shape shape, - double grade, - SystemInfo system, - List systemChords) - { - Rectangle luBox = glyph.getBounds(); - Scale scale = system.getSheet().getScale(); - luBox.grow( - scale.toPixels(constants.maxTupletChordDx), - scale.toPixels(constants.maxTupletChordDy)); - - List nearby = Inters.intersectedInters(systemChords, GeoOrder.BY_ABSCISSA, luBox); - - if (nearby.isEmpty()) { - logger.debug("Discarding isolated tuplet candidate glyph#{}", glyph.getId()); - - return null; - } - - return new TupletInter(glyph, shape, grade); - } - //-----------------// // getBaseDuration // //-----------------// + /** + * Report the chord duration (without dot) on which tuplet modification applies. + * + * @return base duration + */ public Rational getBaseDuration () { if (baseDuration == null) { @@ -208,7 +172,7 @@ public Rational getBaseDuration () */ public List getChords () { - List list = new ArrayList(); + List list = new ArrayList<>(); for (Relation tcRel : sig.getRelations(this, ChordTupletRelation.class)) { list.add((AbstractChordInter) sig.getOppositeInter(this, tcRel)); @@ -324,6 +288,43 @@ protected String internals () return super.internals() + " " + shape; } + //-------------// + // createValid // + //-------------// + /** + * (Try to) create a tuplet inter, checking that there is at least one (head) chord + * nearby. + * + * @param glyph the candidate tuplet glyph + * @param shape TUPLET_THREE or TUPLET_SIX + * @param grade the interpretation quality + * @param system the related system + * @param systemChords abscissa-ordered list of chords in this system + * @return the create TupletInter or null + */ + public static TupletInter createValid (Glyph glyph, + Shape shape, + double grade, + SystemInfo system, + List systemChords) + { + Rectangle luBox = glyph.getBounds(); + Scale scale = system.getSheet().getScale(); + luBox.grow( + scale.toPixels(constants.maxTupletChordDx), + scale.toPixels(constants.maxTupletChordDy)); + + List nearby = Inters.intersectedInters(systemChords, GeoOrder.BY_ABSCISSA, luBox); + + if (nearby.isEmpty()) { + logger.debug("Discarding isolated tuplet candidate glyph#{}", glyph.getId()); + + return null; + } + + return new TupletInter(glyph, shape, grade); + } + //-----------// // getFactor // //-----------// @@ -349,14 +350,12 @@ private static DurationFactor getFactor (Shape shape) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction maxTupletChordDx = new Scale.Fraction( 3, diff --git a/src/main/org/audiveris/omr/sig/inter/VisitableInter.java b/src/main/org/audiveris/omr/sig/inter/VisitableInter.java index ec117ce3d..74e6633da 100644 --- a/src/main/org/audiveris/omr/sig/inter/VisitableInter.java +++ b/src/main/org/audiveris/omr/sig/inter/VisitableInter.java @@ -29,13 +29,11 @@ */ public interface VisitableInter { - //~ Methods ------------------------------------------------------------------------------------ /** - * General entry for any visiting + * General entry for any visiting. * - * @param visitor the concrete visitor object which defines the actual - * processing + * @param visitor the concrete visitor object which defines the actual processing */ void accept (InterVisitor visitor); } diff --git a/src/main/org/audiveris/omr/sig/inter/WedgeInter.java b/src/main/org/audiveris/omr/sig/inter/WedgeInter.java index c12f89823..44f94d094 100644 --- a/src/main/org/audiveris/omr/sig/inter/WedgeInter.java +++ b/src/main/org/audiveris/omr/sig/inter/WedgeInter.java @@ -25,9 +25,12 @@ import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.sheet.Scale; -import org.audiveris.omr.sig.BasicImpacts; import org.audiveris.omr.sig.GradeImpacts; import org.audiveris.omr.util.HorizontalSide; +import org.audiveris.omr.util.Jaxb; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.awt.Rectangle; import java.awt.geom.Line2D; @@ -36,12 +39,14 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * Class {@code WedgeInter} represents a wedge (crescendo or diminuendo). *

                                                                                                                                    * Wedge image + * src= + * "http://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Music_hairpins.svg/296px-Music_hairpins.svg.png"> * * @author Hervé Bitteur */ @@ -50,20 +55,21 @@ public class WedgeInter extends AbstractDirectionInter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Instance fields ---------------------------------------------------------------------------- + private static final Logger logger = LoggerFactory.getLogger(WedgeInter.class); + /** Top line. */ @XmlElement + @XmlJavaTypeAdapter(Jaxb.Line2DAdapter.class) private Line2D l1; /** Bottom line. */ @XmlElement + @XmlJavaTypeAdapter(Jaxb.Line2DAdapter.class) private Line2D l2; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new WedgeInter object. * @@ -112,7 +118,6 @@ private WedgeInter () this.l2 = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -140,6 +145,11 @@ public Rectangle getBounds () //----------// // getLine1 // //----------// + /** + * Report wedge top line. + * + * @return top line + */ public Line2D getLine1 () { return l1; @@ -148,6 +158,11 @@ public Line2D getLine1 () //----------// // getLine2 // //----------// + /** + * Report wedge bottom line. + * + * @return bottom line + */ public Line2D getLine2 () { return l2; @@ -156,6 +171,12 @@ public Line2D getLine2 () //-----------// // getSpread // //-----------// + /** + * Report vertical gap between ending points or provided side. + * + * @param side provided horizontal side + * @return vertical gap in pixels + */ public double getSpread (HorizontalSide side) { if (side == HorizontalSide.LEFT) { @@ -165,14 +186,6 @@ public double getSpread (HorizontalSide side) } } - //------------------------// - // getStackAbscissaMargin // - //------------------------// - public static Scale.Fraction getStackAbscissaMargin () - { - return constants.stackAbscissaMargin; - } - //----------// // setGlyph // //----------// @@ -199,6 +212,11 @@ public void setGlyph (Glyph glyph) l1 = new Line2D.Double(b.x, b.y, b.x + b.width, b.y + (b.height / 2)); l2 = new Line2D.Double(b.x, b.y + b.height, b.x + b.width, b.y + (b.height / 2)); + break; + + default: + logger.warn("Unknown wedge shape: {}", shape); + break; } } @@ -213,22 +231,35 @@ protected String internals () return super.internals() + " " + shape; } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------------------// + // getStackAbscissaMargin // + //------------------------// + /** + * Report margin beyond stack abscissa limits. + * + * @return margin constant + */ + public static Scale.Fraction getStackAbscissaMargin () + { + return constants.stackAbscissaMargin; + } + //---------// // Impacts // //---------// public static class Impacts - extends BasicImpacts + extends GradeImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{ - "s1", "s2", "closedDy", "openDy", "openBias" - }; + "s1", + "s2", + "closedDy", + "openDy", + "openBias"}; private static final double[] WEIGHTS = new double[]{1, 1, 1, 1, 1}; - //~ Constructors --------------------------------------------------------------------------- public Impacts (double s1, double s2, double closedDy, @@ -247,10 +278,9 @@ public Impacts (double s1, //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction stackAbscissaMargin = new Scale.Fraction( 1.0, diff --git a/src/main/org/audiveris/omr/sig/inter/WordInter.java b/src/main/org/audiveris/omr/sig/inter/WordInter.java index ef0afb3b7..adf0dd053 100644 --- a/src/main/org/audiveris/omr/sig/inter/WordInter.java +++ b/src/main/org/audiveris/omr/sig/inter/WordInter.java @@ -26,6 +26,7 @@ import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.text.FontInfo; import org.audiveris.omr.text.TextWord; +import org.audiveris.omr.util.Jaxb; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,12 +47,9 @@ public class WordInter extends AbstractInter { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - WordInter.class); + private static final Logger logger = LoggerFactory.getLogger(WordInter.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Word text content. */ @XmlAttribute protected String value; @@ -63,9 +61,9 @@ public class WordInter /** Precise word starting point. */ @XmlElement + @XmlJavaTypeAdapter(Jaxb.PointAdapter.class) protected Point location; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code WordInter} object, with TEXT shape. * @@ -118,7 +116,6 @@ protected WordInter () this.fontInfo = null; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// @@ -163,18 +160,6 @@ public String getValue () return value; } - //----------// - // setGlyph // - //----------// - @Override - public void setGlyph (Glyph glyph) - { - super.setGlyph(glyph); - - // Location? - // FontInfo? - } - //----------// // setValue // //----------// @@ -188,6 +173,18 @@ public void setValue (String value) this.value = value; } + //----------// + // setGlyph // + //----------// + @Override + public void setGlyph (Glyph glyph) + { + super.setGlyph(glyph); + + // Location? + // FontInfo? + } + //-------------// // shapeString // //-------------// diff --git a/src/main/org/audiveris/omr/sig/inter/package-info.java b/src/main/org/audiveris/omr/sig/inter/package-info.java index a7d53e2fe..1d88025c5 100644 --- a/src/main/org/audiveris/omr/sig/inter/package-info.java +++ b/src/main/org/audiveris/omr/sig/inter/package-info.java @@ -1,22 +1,4 @@ /** * Package for all interpretations used by SIG. */ -@XmlJavaTypeAdapters({ - @XmlJavaTypeAdapter(value = Jaxb.PathAdapter.class, type = Path.class), - @XmlJavaTypeAdapter(value = Jaxb.Line2DAdapter.class, type = Line2D.class), - @XmlJavaTypeAdapter(value = Jaxb.PointAdapter.class, type = Point.class), - @XmlJavaTypeAdapter(value = Jaxb.Point2DAdapter.class, type = Point2D.class), - @XmlJavaTypeAdapter(value = Jaxb.RectangleAdapter.class, type = Rectangle.class) -}) package org.audiveris.omr.sig.inter; - -import org.audiveris.omr.util.Jaxb; - -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.Line2D; -import java.awt.geom.Point2D; -import java.nio.file.Path; - -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; diff --git a/src/main/org/audiveris/omr/sig/relation/AbstractConnection.java b/src/main/org/audiveris/omr/sig/relation/AbstractConnection.java index 29a8a7454..15abb0ecf 100644 --- a/src/main/org/audiveris/omr/sig/relation/AbstractConnection.java +++ b/src/main/org/audiveris/omr/sig/relation/AbstractConnection.java @@ -44,16 +44,13 @@ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "connection") public abstract class AbstractConnection - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - AbstractConnection.class); + private static final Logger logger = LoggerFactory.getLogger(AbstractConnection.class); - //~ Instance fields ---------------------------------------------------------------------------- /** * Horizontal gap at connection (specified in interline fraction). * Positive value for an 'out' distance (true gap). @@ -71,7 +68,6 @@ public abstract class AbstractConnection @XmlJavaTypeAdapter(Jaxb.Double3Adapter.class) protected Double dy; - //~ Methods ------------------------------------------------------------------------------------ /** * Report the horizontal gap (positive or negative) in interline fraction * @@ -160,12 +156,25 @@ protected double[] getOutWeights () return OutImpacts.WEIGHTS; } + /** + * Report the maximum acceptable for outer abscissa gap. + * + * @param manual true for user-driven action + * @return maximum x out gap + */ protected abstract Scale.Fraction getXOutGapMax (boolean manual); + /** + * Report the maximum acceptable ordinate gap. + * + * @param manual true for user-driven action + * @return max y gap + */ protected abstract Scale.Fraction getYGapMax (boolean manual); /** - * Report maximum acceptable overlap. + * Report maximum acceptable abscissa overlap. + *

                                                                                                                                    * This method is disabled by default, to be overridden if overlap is possible * * @param manual true for user-driven action @@ -185,31 +194,44 @@ protected String internals () StringBuilder sb = new StringBuilder(super.internals()); if ((dx != null) && (dy != null)) { - sb.append("@(").append(String.format("%.2f", dx)).append(",") - .append(String.format("%.2f", dy)).append(")"); + sb.append("@(").append(String.format("%.2f", dx)).append(",").append( + String.format("%.2f", dy)).append(")"); } return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-----------// // InImpacts // //-----------// + /** + * Grade impacts for abscissa overlap. + */ public static class InImpacts extends SupportImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{"xInGap", "yGap"}; // Default weights private static final double[] WEIGHTS = new double[]{ constants.xInWeight.getValue(), - constants.yWeight.getValue() - }; - - //~ Constructors --------------------------------------------------------------------------- + constants.yWeight.getValue()}; + + /** + * Create an InImpacts object. + * + * @param xInGap horizontal overlap + * @param yGap vertical gap + * @param weights array of impacts weight + */ public InImpacts (double xInGap, double yGap, double[] weights) @@ -223,20 +245,27 @@ public InImpacts (double xInGap, //------------// // OutImpacts // //------------// + /** + * Grade impacts for abscissa gap. + */ public static class OutImpacts extends SupportImpacts { - //~ Static fields/initializers ------------------------------------------------------------- private static final String[] NAMES = new String[]{"xOutGap", "yGap"}; // Defaults weights private static final double[] WEIGHTS = new double[]{ constants.xOutWeight.getValue(), - constants.yWeight.getValue() - }; - - //~ Constructors --------------------------------------------------------------------------- + constants.yWeight.getValue()}; + + /** + * Create an OutImpacts object. + * + * @param xOutGap horizontal gap + * @param yGap vertical gap + * @param weights array of impacts weight + */ public OutImpacts (double xOutGap, double yGap, double[] weights) @@ -250,10 +279,9 @@ public OutImpacts (double xOutGap, //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio xInWeight = new Constant.Ratio( 1, diff --git a/src/main/org/audiveris/omr/sig/relation/AbstractRelation.java b/src/main/org/audiveris/omr/sig/relation/AbstractRelation.java deleted file mode 100644 index 90593fef6..000000000 --- a/src/main/org/audiveris/omr/sig/relation/AbstractRelation.java +++ /dev/null @@ -1,191 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// A b s t r a c t R e l a t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.util.Jaxb; - -import org.jgrapht.event.GraphEdgeChangeEvent; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - -/** - * Class {@code AbstractRelation} is the abstract basis for any Relation implementation. - * - * @author Hervé Bitteur - */ -@XmlAccessorType(XmlAccessType.NONE) -public abstract class AbstractRelation - implements Relation, Cloneable -{ - //~ Instance fields ---------------------------------------------------------------------------- - - // Persistent data - //---------------- - // - /** Indicates that this relation was set manually. */ - @XmlAttribute(name = "manual") - @XmlJavaTypeAdapter(type = boolean.class, value = Jaxb.BooleanPositiveAdapter.class) - private boolean manual; - - //~ Methods ------------------------------------------------------------------------------------ - //-------// - // added // - //-------// - @Override - public void added (GraphEdgeChangeEvent e) - { - // No-op by default - } - - //-----------// - // duplicate // - //-----------// - @Override - public Relation duplicate () - { - try { - return (Relation) super.clone(); - } catch (CloneNotSupportedException ex) { - return null; - } - } - - //------------// - // getDetails // - //------------// - @Override - public String getDetails () - { - return internals(); - } - - //---------// - // getName // - //---------// - @Override - public String getName () - { - return Relations.nameOf(getClass()); - } - - //----------// - // isManual // - //----------// - /** - * @return the manual - */ - @Override - public boolean isManual () - { - return manual; - } - - //---------// - // removed // - //---------// - @Override - public void removed (GraphEdgeChangeEvent e) - { - // No-op by default - } - - //----------// - // seenFrom // - //----------// - @Override - public String seenFrom (Inter inter) - { - final StringBuilder sb = new StringBuilder(toString()); - final SIGraph sig = inter.getSig(); - - if (sig != null) { - final Inter source = sig.getEdgeSource(this); - - if (source != inter) { - sb.append("<-").append(source); - } else { - final Inter target = sig.getEdgeTarget(this); - - if (target != inter) { - sb.append("->").append(target); - } - } - } - - return sb.toString(); - } - - //-----------// - // setManual // - //-----------// - /** - * @param manual the manual to set - */ - @Override - public void setManual (boolean manual) - { - this.manual = manual; - } - - //--------------// - // toLongString // - //--------------// - @Override - public String toLongString (SIGraph sig) - { - final Inter source = sig.getEdgeSource(this); - final Inter target = sig.getEdgeTarget(this); - final StringBuilder sb = new StringBuilder(); - - sb.append(source); - sb.append("-"); - sb.append(getName()); - sb.append(":"); - sb.append(getDetails()); - sb.append("-"); - sb.append(target); - - return sb.toString(); - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - return getName(); - } - - //-----------// - // internals // - //-----------// - protected String internals () - { - return isManual() ? "MANUAL" : ""; - } -} diff --git a/src/main/org/audiveris/omr/sig/relation/AbstractStemConnection.java b/src/main/org/audiveris/omr/sig/relation/AbstractStemConnection.java index f8c2d6835..539d1773f 100644 --- a/src/main/org/audiveris/omr/sig/relation/AbstractStemConnection.java +++ b/src/main/org/audiveris/omr/sig/relation/AbstractStemConnection.java @@ -23,27 +23,30 @@ import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.util.Jaxb; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * Class {@code AbstractStemConnection} is the basis for connections to a stem. * * @author Hervé Bitteur */ + + public abstract class AbstractStemConnection extends AbstractConnection { - //~ Instance fields ---------------------------------------------------------------------------- /** Logical extension point. */ @XmlElement(name = "extension-point") + @XmlJavaTypeAdapter(Jaxb.Point2DAdapter.class) protected Point2D extensionPoint; - //~ Methods ------------------------------------------------------------------------------------ //----------------// // getStemPortion // //----------------// @@ -98,9 +101,19 @@ protected String internals () if (extensionPoint != null) { sb.append( - String.format(" [x:%.0f,y:%.0f]", extensionPoint.getX(), extensionPoint.getY())); + String.format( + " [x:%.0f,y:%.0f]", + extensionPoint.getX(), + extensionPoint.getY())); } return sb.toString(); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/AbstractSupport.java b/src/main/org/audiveris/omr/sig/relation/AbstractSupport.java deleted file mode 100644 index d6c7bd980..000000000 --- a/src/main/org/audiveris/omr/sig/relation/AbstractSupport.java +++ /dev/null @@ -1,189 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// A b s t r a c t S u p p o r t // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.sig.GradeImpacts; -import org.audiveris.omr.util.Jaxb; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - -/** - * Class {@code AbstractSupport} is the base implementation of {@link Support} interface. - * - * @author Hervé Bitteur - */ -@XmlAccessorType(XmlAccessType.NONE) -@XmlRootElement(name = "support") -public abstract class AbstractSupport - extends AbstractRelation - implements Support -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - //~ Instance fields ---------------------------------------------------------------------------- - /** Quality of the geometric junction. */ - @XmlAttribute - @XmlJavaTypeAdapter(type = double.class, value = Jaxb.Double3Adapter.class) - protected double grade; - - /** Details about grade (for debugging). */ - protected GradeImpacts impacts; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new BasicSupport object, with a grade set to 1. - */ - public AbstractSupport () - { - this(1.0); - } - - /** - * Creates a new BasicSupport object. - * - * @param grade the grade evaluated for this relation - */ - public AbstractSupport (double grade) - { - this.grade = grade; - } - - //~ Methods ------------------------------------------------------------------------------------ - //----------------// - // getSourceRatio // - //----------------// - @Override - public final double getSourceRatio () - { - return 1.0 + (getSourceCoeff() * grade); - } - - //----------------// - // getTargetRatio // - //----------------// - @Override - public final double getTargetRatio () - { - return 1.0 + (getTargetCoeff() * grade); - } - - //----------// - // getGrade // - //----------// - /** - * @return the grade - */ - public double getGrade () - { - return grade; - } - - //------------// - // getImpacts // - //------------// - @Override - public GradeImpacts getImpacts () - { - return impacts; - } - - //-------------// - // getMinGrade // - //-------------// - public double getMinGrade () - { - return constants.minGrade.getValue(); - } - - //----------// - // setGrade // - //----------// - /** - * @param grade the grade to set - */ - public void setGrade (double grade) - { - this.grade = grade; - } - - //------------// - // setImpacts // - //------------// - @Override - public void setImpacts (GradeImpacts impacts) - { - this.impacts = impacts; - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - return String.format("%.2f~%s", grade, super.toString()); - } - - //----------------// - // getSourceCoeff // - //----------------// - /** - * @return the coefficient used to compute source support ratio (default: 0) - */ - protected double getSourceCoeff () - { - return 0; - } - - //----------------// - // getTargetCoeff // - //----------------// - /** - * @return the coefficient used to compute target support ratio (default: 0) - */ - protected double getTargetCoeff () - { - return 0; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Ratio minGrade = new Constant.Ratio( - 0.1, - "Minimum support relation grade"); - } -} diff --git a/src/main/org/audiveris/omr/sig/relation/AlterHeadRelation.java b/src/main/org/audiveris/omr/sig/relation/AlterHeadRelation.java index 767b335ce..2e13525e1 100644 --- a/src/main/org/audiveris/omr/sig/relation/AlterHeadRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/AlterHeadRelation.java @@ -44,7 +44,6 @@ public class AlterHeadRelation extends AbstractConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -52,15 +51,12 @@ public class AlterHeadRelation private static final double[] IN_WEIGHTS = new double[]{ constants.xInWeight.getValue(), - constants.yWeight.getValue() - }; + constants.yWeight.getValue()}; private static final double[] OUT_WEIGHTS = new double[]{ constants.xOutWeight.getValue(), - constants.yWeight.getValue() - }; + constants.yWeight.getValue()}; - //~ Methods ------------------------------------------------------------------------------------ //-------// // added // //-------// @@ -71,6 +67,13 @@ public void added (GraphEdgeChangeEvent e) alter.checkAbnormal(); } + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //------------------// // getXInGapMaximum // //------------------// @@ -120,7 +123,10 @@ public boolean isSingleTarget () public void removed (GraphEdgeChangeEvent e) { final AlterInter alter = (AlterInter) e.getEdgeSource(); - alter.checkAbnormal(); + + if (!alter.isRemoved()) { + alter.checkAbnormal(); + } } //--------------// @@ -173,14 +179,12 @@ protected Scale.Fraction getYGapMax (boolean manual) return getYGapMaximum(manual); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio AccidentalCoeff = new Constant.Ratio( 3, diff --git a/src/main/org/audiveris/omr/sig/relation/AugmentationRelation.java b/src/main/org/audiveris/omr/sig/relation/AugmentationRelation.java index 5a5f23375..5f2bbba20 100644 --- a/src/main/org/audiveris/omr/sig/relation/AugmentationRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/AugmentationRelation.java @@ -26,6 +26,7 @@ import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sig.inter.AugmentationDotInter; import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.ui.InterController; import org.jgrapht.event.GraphEdgeChangeEvent; @@ -37,6 +38,10 @@ /** * Class {@code AugmentationRelation} represents the relation between an augmentation * dot and the related note (head or rest) instance. + *

                                                                                                                                    + * NOTA: An augmentation can be considered as linked to at most one note (single target) + * since the case of pair of mirrored heads (which can indeed be targeted by the same dot) + * is addressed specifically within {@link InterController#link} method. * * @author Hervé Bitteur */ @@ -44,7 +49,6 @@ public class AugmentationRelation extends AbstractConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -52,10 +56,8 @@ public class AugmentationRelation private static final double[] OUT_WEIGHTS = new double[]{ constants.xOutWeight.getValue(), - constants.yWeight.getValue() - }; + constants.yWeight.getValue()}; - //~ Methods ------------------------------------------------------------------------------------ //-------// // added // //-------// @@ -66,6 +68,13 @@ public void added (GraphEdgeChangeEvent e) dot.checkAbnormal(); } + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-------------------// // getXOutGapMaximum // //-------------------// @@ -105,7 +114,7 @@ public boolean isSingleSource () @Override public boolean isSingleTarget () { - return true; + return true; // See explanation in class header } //---------// @@ -115,7 +124,10 @@ public boolean isSingleTarget () public void removed (GraphEdgeChangeEvent e) { final AugmentationDotInter dot = (AugmentationDotInter) e.getEdgeSource(); - dot.checkAbnormal(); + + if (!dot.isRemoved()) { + dot.checkAbnormal(); + } } //---------------// @@ -151,14 +163,12 @@ protected Scale.Fraction getYGapMax (boolean manual) return getYGapMaximum(manual); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio dotSupportCoeff = new Constant.Ratio( 0.5, diff --git a/src/main/org/audiveris/omr/sig/relation/BarConnectionRelation.java b/src/main/org/audiveris/omr/sig/relation/BarConnectionRelation.java index 60de21998..b3f1d4274 100644 --- a/src/main/org/audiveris/omr/sig/relation/BarConnectionRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/BarConnectionRelation.java @@ -35,13 +35,11 @@ */ @XmlRootElement(name = "bar-connection") public class BarConnectionRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BarConnectionRelation object. * @@ -58,7 +56,6 @@ private BarConnectionRelation () { } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -95,14 +92,19 @@ protected double getTargetCoeff () return constants.barSupportCoeff.getValue(); } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio barSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/BarGroupRelation.java b/src/main/org/audiveris/omr/sig/relation/BarGroupRelation.java index b2c3850d7..7ae1840cb 100644 --- a/src/main/org/audiveris/omr/sig/relation/BarGroupRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/BarGroupRelation.java @@ -30,14 +30,12 @@ */ @XmlRootElement(name = "bar-group") public class BarGroupRelation - extends AbstractRelation + extends Relation { - //~ Instance fields ---------------------------------------------------------------------------- /** Horizontal white gap (in interline) between the two bar lines. */ private final double xGap; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BarGroupRelation object. * @@ -54,7 +52,6 @@ private BarGroupRelation () this.xGap = 0; } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -85,4 +82,11 @@ protected String internals () return sb.toString(); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/BeamHeadRelation.java b/src/main/org/audiveris/omr/sig/relation/BeamHeadRelation.java index c40b3e129..fcc0c844d 100644 --- a/src/main/org/audiveris/omr/sig/relation/BeamHeadRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/BeamHeadRelation.java @@ -24,6 +24,9 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import javax.xml.bind.annotation.XmlRootElement; /** @@ -33,13 +36,13 @@ */ @XmlRootElement(name = "beam-head") public class BeamHeadRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Constructors ------------------------------------------------------------------------------- + private static final Logger logger = LoggerFactory.getLogger(BeamHeadRelation.class); + /** * Creates a new {@code BeamHeadRelation} object. * @@ -57,7 +60,6 @@ private BeamHeadRelation () { } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -85,14 +87,19 @@ protected double getTargetCoeff () return constants.headSupportCoeff.getValue(); } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio headSupportCoeff = new Constant.Ratio( 0.75, diff --git a/src/main/org/audiveris/omr/sig/relation/BeamStemRelation.java b/src/main/org/audiveris/omr/sig/relation/BeamStemRelation.java index 40a6d1531..95ff3c800 100644 --- a/src/main/org/audiveris/omr/sig/relation/BeamStemRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/BeamStemRelation.java @@ -62,24 +62,19 @@ public class BeamStemRelation extends AbstractStemConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - BeamStemRelation.class); + private static final Logger logger = LoggerFactory.getLogger(BeamStemRelation.class); private static final double[] OUT_WEIGHTS = new double[]{ constants.xOutWeight.getValue(), - constants.yWeight.getValue() - }; + constants.yWeight.getValue()}; - //~ Instance fields ---------------------------------------------------------------------------- /** Which portion of beam is used?. */ @XmlAttribute(name = "beam-portion") private BeamPortion beamPortion; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code BeamStemRelation} object. */ @@ -87,63 +82,11 @@ public BeamStemRelation () { } - //~ Methods ------------------------------------------------------------------------------------ - //-------// - // added // - //-------// - /** - * Populate beam portion if needed and update the chord(s) if any that use this stem. - *

                                                                                                                                    - * In the rare case where a stem is shared by several chords, the beam connection applies to - * all these chords. - * - * @param e edge change event - */ @Override - public void added (GraphEdgeChangeEvent e) + public Object clone () + throws CloneNotSupportedException { - final AbstractBeamInter beam = (AbstractBeamInter) e.getEdgeSource(); - final StemInter stem = (StemInter) e.getEdgeTarget(); - - if (extensionPoint == null) { - extensionPoint = computeExtensionPoint(beam, stem); - } - - if (beamPortion == null) { - if (beam instanceof BeamHookInter) { - beamPortion = (beam.getCenter().x < stem.getCenter().x) ? RIGHT : LEFT; - } else { - int xStem = stem.getCenter().x; - Scale scale = stem.getSig().getSystem().getSheet().getScale(); - int maxDx = scale.toPixels(getXInGapMaximum(false)); - double left = beam.getMedian().getX1(); - double right = beam.getMedian().getX2(); - - if (xStem < (left + maxDx)) { - beamPortion = LEFT; - } else if (xStem > (right - maxDx)) { - beamPortion = RIGHT; - } else { - beamPortion = CENTER; - } - } - } - - for (HeadChordInter chord : stem.getChords()) { - chord.invalidateCache(); - - // Include in proper BeamGroup set within containing Measure? - SystemInfo system = stem.getSig().getSystem(); - Staff staff1 = chord.getTopStaff(); - MeasureStack stack = system.getStackAt(stem.getCenter()); - - if (stack != null) { - Measure measure = stack.getMeasureAt(staff1); - BeamGroup.includeBeam(beam, measure); - } - } - - beam.checkAbnormal(); + return super.clone(); //To change body of generated methods, choose Tools | Templates. } //-----------------------// @@ -172,6 +115,30 @@ public static Point2D computeExtensionPoint (AbstractBeamInter beam, return LineUtil.intersection(stemMedian, beamBorder); } + //----------------// + // getBeamPortion // + //----------------// + /** + * @return the beamPortion + */ + public BeamPortion getBeamPortion () + { + return beamPortion; + } + + //----------------// + // getStemPortion // + //----------------// + @Override + public StemPortion getStemPortion (Inter source, + Line2D stemLine, + Scale scale) + { + double midStem = (stemLine.getY1() + stemLine.getY2()) / 2; + + return (extensionPoint.getY() < midStem) ? StemPortion.STEM_TOP : StemPortion.STEM_BOTTOM; + } + //------------------// // getXInGapMaximum // //------------------// @@ -196,28 +163,62 @@ public static Scale.Fraction getYGapMaximum (boolean manual) return manual ? constants.yGapMaxManual : constants.yGapMax; } - //----------------// - // getBeamPortion // - //----------------// + //-------// + // added // + //-------// /** - * @return the beamPortion + * Populate beam portion if needed and update the chord(s) if any that use this stem. + *

                                                                                                                                    + * In the rare case where a stem is shared by several chords, the beam connection applies to + * all these chords. + * + * @param e edge change event */ - public BeamPortion getBeamPortion () - { - return beamPortion; - } - - //----------------// - // getStemPortion // - //----------------// @Override - public StemPortion getStemPortion (Inter source, - Line2D stemLine, - Scale scale) + public void added (GraphEdgeChangeEvent e) { - double midStem = (stemLine.getY1() + stemLine.getY2()) / 2; + final AbstractBeamInter beam = (AbstractBeamInter) e.getEdgeSource(); + final StemInter stem = (StemInter) e.getEdgeTarget(); - return (extensionPoint.getY() < midStem) ? StemPortion.STEM_TOP : StemPortion.STEM_BOTTOM; + if (extensionPoint == null) { + extensionPoint = computeExtensionPoint(beam, stem); + } + + if (beamPortion == null) { + if (beam instanceof BeamHookInter) { + beamPortion = (beam.getCenter().x < stem.getCenter().x) ? RIGHT : LEFT; + } else { + int xStem = stem.getCenter().x; + Scale scale = stem.getSig().getSystem().getSheet().getScale(); + int maxDx = scale.toPixels(getXInGapMaximum(false)); + double left = beam.getMedian().getX1(); + double right = beam.getMedian().getX2(); + + if (xStem < (left + maxDx)) { + beamPortion = LEFT; + } else if (xStem > (right - maxDx)) { + beamPortion = RIGHT; + } else { + beamPortion = CENTER; + } + } + } + + for (HeadChordInter chord : stem.getChords()) { + chord.invalidateCache(); + + // Include in proper BeamGroup set within containing Measure? + SystemInfo system = stem.getSig().getSystem(); + Staff staff1 = chord.getTopStaff(); + MeasureStack stack = system.getStackAt(stem.getCenter()); + + if (stack != null) { + Measure measure = stack.getMeasureAt(staff1); + BeamGroup.includeBeam(beam, measure); + } + } + + beam.checkAbnormal(); } //----------------// @@ -247,12 +248,20 @@ public void removed (GraphEdgeChangeEvent e) // If stem has a chord with heads, remove all beam-head relations final AbstractBeamInter beam = (AbstractBeamInter) e.getEdgeSource(); final StemInter stem = (StemInter) e.getEdgeTarget(); - final SIGraph sig = stem.getSig(); - for (HeadChordInter headChord : stem.getChords()) { - for (Inter inter : headChord.getNotes()) { - HeadInter head = (HeadInter) inter; - sig.removeEdge(beam, head); + /** + * CAVEAT: if a beam (with beam-stem and beam-head relations) is removed, + * the graph will automatically remove these relations, so also removing here + * the beam-head relation might lead to NPE in graph... + */ + if (!beam.isRemoved() && !stem.isRemoved()) { + final SIGraph sig = stem.getSig(); + + for (HeadChordInter headChord : stem.getChords()) { + for (Inter inter : headChord.getNotes()) { + HeadInter head = (HeadInter) inter; + sig.removeEdge(beam, head); + } } } } @@ -331,14 +340,12 @@ protected String internals () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio beamSupportCoeff = new Constant.Ratio( 4, diff --git a/src/main/org/audiveris/omr/sig/relation/ChordArpeggiatoRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordArpeggiatoRelation.java index f2a288bdd..1541840f5 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordArpeggiatoRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordArpeggiatoRelation.java @@ -1,123 +1,139 @@ -//------------------------------------------------------------------------------------------------// -// // -// C h o r d A r p e g g i a t o R e l a t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.sig.inter.ArpeggiatoInter; -import org.audiveris.omr.sig.inter.Inter; - -import org.jgrapht.event.GraphEdgeChangeEvent; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code ChordArpeggiatoRelation} - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "chord-arpeggiato") -public class ChordArpeggiatoRelation - extends AbstractSupport -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code ArpeggiatoChordRelation} object. - * - * @param grade relation quality - */ - public ChordArpeggiatoRelation (double grade) - { - super(grade); - } - - /** - * Creates a new {@code ArpeggiatoChordRelation} object. - */ - public ChordArpeggiatoRelation () - { - super(); - } - - //~ Methods ------------------------------------------------------------------------------------ - //-------// - // added // - //-------// - @Override - public void added (GraphEdgeChangeEvent e) - { - final ArpeggiatoInter arpeggiato = (ArpeggiatoInter) e.getEdgeTarget(); - arpeggiato.checkAbnormal(); - } - - //----------------// - // isSingleSource // - //----------------// - @Override - public boolean isSingleSource () - { - return true; - } - - //----------------// - // isSingleTarget // - //----------------// - @Override - public boolean isSingleTarget () - { - return true; - } - - //---------// - // removed // - //---------// - @Override - public void removed (GraphEdgeChangeEvent e) - { - final ArpeggiatoInter arpeggiato = (ArpeggiatoInter) e.getEdgeTarget(); - arpeggiato.checkAbnormal(); - } - - @Override - protected double getSourceCoeff () - { - return constants.arpeggiatoSupportCoeff.getValue(); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Ratio arpeggiatoSupportCoeff = new Constant.Ratio( - 0.5, - "Supporting coeff for (source) arpeggiato"); - } -} +//------------------------------------------------------------------------------------------------// +// // +// C h o r d A r p e g g i a t o R e l a t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.sheet.Scale; +import org.audiveris.omr.sig.inter.ArpeggiatoInter; +import org.audiveris.omr.sig.inter.Inter; + +import org.jgrapht.event.GraphEdgeChangeEvent; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code ChordArpeggiatoRelation} represents a relation between a (head) chord + * and an arpeggiato. + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "chord-arpeggiato") +public class ChordArpeggiatoRelation + extends Support +{ + + private static final Constants constants = new Constants(); + + /** + * Creates a new {@code ArpeggiatoChordRelation} object. + * + * @param grade relation quality + */ + public ChordArpeggiatoRelation (double grade) + { + super(grade); + } + + /** + * Creates a new {@code ArpeggiatoChordRelation} object. + */ + public ChordArpeggiatoRelation () + { + super(); + } + + //-------// + // added // + //-------// + @Override + public void added (GraphEdgeChangeEvent e) + { + final ArpeggiatoInter arpeggiato = (ArpeggiatoInter) e.getEdgeTarget(); + arpeggiato.checkAbnormal(); + } + + //----------------// + // isSingleSource // + //----------------// + @Override + public boolean isSingleSource () + { + return true; + } + + //----------------// + // isSingleTarget // + //----------------// + @Override + public boolean isSingleTarget () + { + return true; + } + + //---------// + // removed // + //---------// + @Override + public void removed (GraphEdgeChangeEvent e) + { + final ArpeggiatoInter arpeggiato = (ArpeggiatoInter) e.getEdgeTarget(); + + if (!arpeggiato.isRemoved()) { + arpeggiato.checkAbnormal(); + } + } + + @Override + protected double getSourceCoeff () + { + return constants.arpeggiatoSupportCoeff.getValue(); + } + + //----------------// + // getXGapMaximum // + //----------------// + public static Scale.Fraction getXGapMaximum (boolean manual) + { + return manual ? constants.xGapMaxManual : constants.xGapMax; + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Ratio arpeggiatoSupportCoeff = new Constant.Ratio( + 0.5, + "Supporting coeff for (source) arpeggiato"); + + private final Scale.Fraction xGapMax = new Scale.Fraction( + 1.5, + "Maximum horizontal gap between arpeggiato & chord"); + + private final Scale.Fraction xGapMaxManual = new Scale.Fraction( + 2.5, + "Maximum manual horizontal gap between arpeggiato & chord"); + } +} diff --git a/src/main/org/audiveris/omr/sig/relation/ChordArticulationRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordArticulationRelation.java index 77113de63..68ad226d0 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordArticulationRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordArticulationRelation.java @@ -1,206 +1,207 @@ -//------------------------------------------------------------------------------------------------// -// // -// C h o r d A r t i c u l a t i o n R e l a t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.sheet.Scale; -import org.audiveris.omr.sig.inter.ArticulationInter; -import org.audiveris.omr.sig.inter.Inter; - -import org.jgrapht.event.GraphEdgeChangeEvent; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code ChordArticulationRelation} is a connection between a head-based chord - * and an articulation sign (tenuto, accent, staccato, staccatissimo, marcato). - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "chord-articulation") -public class ChordArticulationRelation - extends AbstractConnection -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger( - ChordArticulationRelation.class); - - private static final double[] WEIGHTS = new double[]{ - constants.xWeight.getValue(), - constants.yWeight.getValue() - }; - - //~ Methods ------------------------------------------------------------------------------------ - //-------// - // added // - //-------// - @Override - public void added (GraphEdgeChangeEvent e) - { - final ArticulationInter articulation = (ArticulationInter) e.getEdgeTarget(); - articulation.checkAbnormal(); - } - - //-------------------// - // getXOutGapMaximum // - //-------------------// - public static Scale.Fraction getXOutGapMaximum (boolean manual) - { - return manual ? constants.xGapMaxManual : constants.xGapMax; - } - - //----------------// - // getYGapMaximum // - //----------------// - public static Scale.Fraction getYGapMaximum (boolean manual) - { - return manual ? constants.yGapMaxManual : constants.yGapMax; - } - - //----------------// - // getYGapMinimum // - //----------------// - public static Scale.Fraction getYGapMinimum (boolean manual) - { - return manual ? constants.yGapMinManual : constants.yGapMin; - } - - //----------------// - // isSingleSource // - //----------------// - @Override - public boolean isSingleSource () - { - return true; - } - - //----------------// - // isSingleTarget // - //----------------// - @Override - public boolean isSingleTarget () - { - return true; - } - - //---------// - // removed // - //---------// - @Override - public void removed (GraphEdgeChangeEvent e) - { - final ArticulationInter articulation = (ArticulationInter) e.getEdgeTarget(); - articulation.checkAbnormal(); - } - - //---------------// - // getOutWeights // - //---------------// - @Override - protected double[] getOutWeights () - { - return WEIGHTS; - } - - //----------------// - // getTargetCoeff // - //----------------// - /** - * @return the supporting coefficient for (target) articulation - */ - @Override - protected double getTargetCoeff () - { - return constants.articulationSupportCoeff.getValue(); - } - - //---------------// - // getXOutGapMax // - //---------------// - @Override - protected Scale.Fraction getXOutGapMax (boolean manual) - { - return getXOutGapMaximum(manual); - } - - //------------// - // getYGapMax // - //------------// - @Override - protected Scale.Fraction getYGapMax (boolean manual) - { - return getYGapMaximum(manual); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Ratio articulationSupportCoeff = new Constant.Ratio( - 3, - "Supporting coeff for (target) articulation"); - - private final Scale.Fraction xGapMax = new Scale.Fraction( - 0.75, - "Maximum horizontal gap between articulation center & chord"); - - private final Scale.Fraction xGapMaxManual = new Scale.Fraction( - 1.0, - "Maximum manual horizontal gap between articulation center & chord"); - - private final Scale.Fraction yGapMax = new Scale.Fraction( - 2.0, - "Maximum vertical gap between articulation center & chord"); - - private final Scale.Fraction yGapMaxManual = new Scale.Fraction( - 3.0, - "Maximum manual vertical gap between articulation center & chord"); - - private final Scale.Fraction yGapMin = new Scale.Fraction( - 0.1, - "Minimum vertical gap between articulation center & chord"); - - private final Scale.Fraction yGapMinManual = new Scale.Fraction( - 0.05, - "Minimum manual vertical gap between articulation center & chord"); - - private final Constant.Ratio xWeight = new Constant.Ratio( - 3, - "Relative impact weight for xGap (in or out)"); - - private final Constant.Ratio yWeight = new Constant.Ratio( - 1, - "Relative impact weight for yGap"); - } -} +//------------------------------------------------------------------------------------------------// +// // +// C h o r d A r t i c u l a t i o n R e l a t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.sheet.Scale; +import org.audiveris.omr.sig.inter.ArticulationInter; +import org.audiveris.omr.sig.inter.Inter; + +import org.jgrapht.event.GraphEdgeChangeEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code ChordArticulationRelation} is a connection between a head-based chord + * and an articulation sign (tenuto, accent, staccato, staccatissimo, marcato). + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "chord-articulation") +public class ChordArticulationRelation + extends AbstractConnection +{ + + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(ChordArticulationRelation.class); + + private static final double[] WEIGHTS = new double[]{ + constants.xWeight.getValue(), + constants.yWeight.getValue()}; + + //-------// + // added // + //-------// + @Override + public void added (GraphEdgeChangeEvent e) + { + final ArticulationInter articulation = (ArticulationInter) e.getEdgeTarget(); + articulation.checkAbnormal(); + } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + + //-------------------// + // getXOutGapMaximum // + //-------------------// + public static Scale.Fraction getXOutGapMaximum (boolean manual) + { + return manual ? constants.xGapMaxManual : constants.xGapMax; + } + + //----------------// + // getYGapMaximum // + //----------------// + public static Scale.Fraction getYGapMaximum (boolean manual) + { + return manual ? constants.yGapMaxManual : constants.yGapMax; + } + + //----------------// + // getYGapMinimum // + //----------------// + public static Scale.Fraction getYGapMinimum (boolean manual) + { + return manual ? constants.yGapMinManual : constants.yGapMin; + } + + //----------------// + // isSingleSource // + //----------------// + @Override + public boolean isSingleSource () + { + return true; + } + + //----------------// + // isSingleTarget // + //----------------// + @Override + public boolean isSingleTarget () + { + return true; + } + + //---------// + // removed // + //---------// + @Override + public void removed (GraphEdgeChangeEvent e) + { + final ArticulationInter articulation = (ArticulationInter) e.getEdgeTarget(); + + if (!articulation.isRemoved()) { + articulation.checkAbnormal(); + } + } + + //---------------// + // getOutWeights // + //---------------// + @Override + protected double[] getOutWeights () + { + return WEIGHTS; + } + + //----------------// + // getTargetCoeff // + //----------------// + @Override + protected double getTargetCoeff () + { + return constants.articulationSupportCoeff.getValue(); + } + + //---------------// + // getXOutGapMax // + //---------------// + @Override + protected Scale.Fraction getXOutGapMax (boolean manual) + { + return getXOutGapMaximum(manual); + } + + //------------// + // getYGapMax // + //------------// + @Override + protected Scale.Fraction getYGapMax (boolean manual) + { + return getYGapMaximum(manual); + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Ratio articulationSupportCoeff = new Constant.Ratio( + 3, + "Supporting coeff for (target) articulation"); + + private final Scale.Fraction xGapMax = new Scale.Fraction( + 0.75, + "Maximum horizontal gap between articulation center & chord"); + + private final Scale.Fraction xGapMaxManual = new Scale.Fraction( + 1.0, + "Maximum manual horizontal gap between articulation center & chord"); + + private final Scale.Fraction yGapMax = new Scale.Fraction( + 2.0, + "Maximum vertical gap between articulation center & chord"); + + private final Scale.Fraction yGapMaxManual = new Scale.Fraction( + 3.0, + "Maximum manual vertical gap between articulation center & chord"); + + private final Scale.Fraction yGapMin = new Scale.Fraction( + 0.1, + "Minimum vertical gap between articulation center & chord"); + + private final Scale.Fraction yGapMinManual = new Scale.Fraction( + 0.05, + "Minimum manual vertical gap between articulation center & chord"); + + private final Constant.Ratio xWeight = new Constant.Ratio( + 3, + "Relative impact weight for xGap (in or out)"); + + private final Constant.Ratio yWeight = new Constant.Ratio( + 1, + "Relative impact weight for yGap"); + } +} diff --git a/src/main/org/audiveris/omr/sig/relation/ChordDynamicsRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordDynamicsRelation.java index 0d83dc218..ee5155f3f 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordDynamicsRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordDynamicsRelation.java @@ -31,9 +31,8 @@ */ @XmlRootElement(name = "chord-dynamics") public class ChordDynamicsRelation - extends AbstractSupport + extends Support { - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // @@ -52,4 +51,11 @@ public boolean isSingleTarget () { return true; } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/ChordNameRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordNameRelation.java index d6251b7b5..7ce73b5f6 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordNameRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordNameRelation.java @@ -31,9 +31,8 @@ */ @XmlRootElement(name = "chord-name") public class ChordNameRelation - extends AbstractSupport + extends Support { - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // @@ -52,4 +51,11 @@ public boolean isSingleTarget () { return true; } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/ChordOrnamentRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordOrnamentRelation.java index 8cbf9ab86..2f9c4f3cb 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordOrnamentRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordOrnamentRelation.java @@ -1,135 +1,131 @@ -//------------------------------------------------------------------------------------------------// -// // -// C h o r d O r n a m e n t R e l a t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.sheet.Scale; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code ChordOrnamentRelation} represents the relation between a head-chord and - * an ornament. - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "chord-ornament") -public class ChordOrnamentRelation - extends AbstractConnection -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - //~ Methods ------------------------------------------------------------------------------------ - //-------------------// - // getXOutGapMaximum // - //-------------------// - public static Scale.Fraction getXOutGapMaximum (boolean manual) - { - return manual ? constants.xGapMaxManual : constants.xGapMax; - } - - //----------------// - // getYGapMaximum // - //----------------// - public static Scale.Fraction getYGapMaximum (boolean manual) - { - return manual ? constants.yGapMaxManual : constants.yGapMax; - } - - //----------------// - // isSingleSource // - //----------------// - @Override - public boolean isSingleSource () - { - return true; - } - - //----------------// - // isSingleTarget // - //----------------// - @Override - public boolean isSingleTarget () - { - return true; - } - - //----------------// - // getTargetCoeff // - //----------------// - @Override - protected double getTargetCoeff () - { - return constants.ornamentTargetCoeff.getValue(); - } - - //---------------// - // getXOutGapMax // - //---------------// - @Override - protected Scale.Fraction getXOutGapMax (boolean manual) - { - return getXOutGapMaximum(manual); - } - - //------------// - // getYGapMax // - //------------// - @Override - protected Scale.Fraction getYGapMax (boolean manual) - { - return getYGapMaximum(manual); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Ratio ornamentTargetCoeff = new Constant.Ratio( - 0.5, - "Supporting coeff for (target) ornament"); - - private final Scale.Fraction xGapMax = new Scale.Fraction( - 0.75, - "Maximum horizontal gap between ornament center & chord"); - - private final Scale.Fraction xGapMaxManual = new Scale.Fraction( - 1.2, - "Maximum manual horizontal gap between ornament center & chord"); - - private final Scale.Fraction yGapMax = new Scale.Fraction( - 2.0, - "Maximum vertical gap between ornament center & chord"); - - private final Scale.Fraction yGapMaxManual = new Scale.Fraction( - 3.0, - "Maximum manual vertical gap between ornament center & chord"); - } -} +//------------------------------------------------------------------------------------------------// +// // +// C h o r d O r n a m e n t R e l a t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.sheet.Scale; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code ChordOrnamentRelation} represents the relation between a head-chord and + * an ornament. + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "chord-ornament") +public class ChordOrnamentRelation + extends AbstractConnection +{ + + private static final Constants constants = new Constants(); + + //----------------// + // isSingleSource // + //----------------// + @Override + public boolean isSingleSource () + { + return true; + } + + //----------------// + // isSingleTarget // + //----------------// + @Override + public boolean isSingleTarget () + { + return true; + } + + //----------------// + // getTargetCoeff // + //----------------// + @Override + protected double getTargetCoeff () + { + return constants.ornamentTargetCoeff.getValue(); + } + + //---------------// + // getXOutGapMax // + //---------------// + @Override + protected Scale.Fraction getXOutGapMax (boolean manual) + { + return getXOutGapMaximum(manual); + } + + //------------// + // getYGapMax // + //------------// + @Override + protected Scale.Fraction getYGapMax (boolean manual) + { + return getYGapMaximum(manual); + } + + //-------------------// + // getXOutGapMaximum // + //-------------------// + public static Scale.Fraction getXOutGapMaximum (boolean manual) + { + return manual ? constants.xGapMaxManual : constants.xGapMax; + } + + //----------------// + // getYGapMaximum // + //----------------// + public static Scale.Fraction getYGapMaximum (boolean manual) + { + return manual ? constants.yGapMaxManual : constants.yGapMax; + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Ratio ornamentTargetCoeff = new Constant.Ratio( + 0.5, + "Supporting coeff for (target) ornament"); + + private final Scale.Fraction xGapMax = new Scale.Fraction( + 0.75, + "Maximum horizontal gap between ornament center & chord"); + + private final Scale.Fraction xGapMaxManual = new Scale.Fraction( + 1.2, + "Maximum manual horizontal gap between ornament center & chord"); + + private final Scale.Fraction yGapMax = new Scale.Fraction( + 2.0, + "Maximum vertical gap between ornament center & chord"); + + private final Scale.Fraction yGapMaxManual = new Scale.Fraction( + 3.0, + "Maximum manual vertical gap between ornament center & chord"); + } +} diff --git a/src/main/org/audiveris/omr/sig/relation/ChordPedalRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordPedalRelation.java index 52a92ffed..c086bc8ce 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordPedalRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordPedalRelation.java @@ -31,9 +31,8 @@ */ @XmlRootElement(name = "chord-pedal") public class ChordPedalRelation - extends AbstractSupport + extends Support { - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // @@ -52,4 +51,11 @@ public boolean isSingleTarget () { return true; } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/ChordSentenceRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordSentenceRelation.java index f9df64c41..51f57b6bb 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordSentenceRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordSentenceRelation.java @@ -34,21 +34,11 @@ */ @XmlRootElement(name = "chord-sentence") public class ChordSentenceRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // getXGapMax // - //------------// - public static Scale.Fraction getXGapMax () - { - return constants.xGapMax; - } - //----------------// // isSingleSource // //----------------// @@ -67,14 +57,20 @@ public boolean isSingleTarget () return true; } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------// + // getXGapMax // + //------------// + public static Scale.Fraction getXGapMax () + { + return constants.xGapMax; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Scale.Fraction xGapMax = new Scale.Fraction( 1.0, diff --git a/src/main/org/audiveris/omr/sig/relation/ChordStemRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordStemRelation.java index 19ba1a490..92ff069a1 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordStemRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordStemRelation.java @@ -1,82 +1,91 @@ -//------------------------------------------------------------------------------------------------// -// // -// C h o r d S t e m R e l a t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.sig.inter.HeadChordInter; -import org.audiveris.omr.sig.inter.Inter; - -import org.jgrapht.event.GraphEdgeChangeEvent; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code ChordStemRelation} represents a HeadChord - Stem relation. - * - * @author Hervé Bitteur - */ -@XmlAccessorType(XmlAccessType.NONE) -@XmlRootElement(name = "chord-stem") -public class ChordStemRelation - extends AbstractRelation -{ - //~ Methods ------------------------------------------------------------------------------------ - - //-------// - // added // - //-------// - @Override - public void added (GraphEdgeChangeEvent e) - { - HeadChordInter headChord = (HeadChordInter) e.getEdgeSource(); - headChord.invalidateCache(); - } - - //----------------// - // isSingleSource // - //----------------// - @Override - public boolean isSingleSource () - { - return true; - } - - //----------------// - // isSingleTarget // - //----------------// - @Override - public boolean isSingleTarget () - { - return true; - } - - //---------// - // removed // - //---------// - @Override - public void removed (GraphEdgeChangeEvent e) - { - HeadChordInter headChord = (HeadChordInter) e.getEdgeSource(); - headChord.invalidateCache(); - } -} +//------------------------------------------------------------------------------------------------// +// // +// C h o r d S t e m R e l a t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import org.audiveris.omr.sig.inter.HeadChordInter; +import org.audiveris.omr.sig.inter.Inter; + +import org.jgrapht.event.GraphEdgeChangeEvent; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code ChordStemRelation} represents a HeadChord - Stem relation. + * + * @author Hervé Bitteur + */ +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = "chord-stem") +public class ChordStemRelation + extends Relation +{ + + //-------// + // added // + //-------// + @Override + public void added (GraphEdgeChangeEvent e) + { + HeadChordInter headChord = (HeadChordInter) e.getEdgeSource(); + headChord.invalidateCache(); + } + + //----------------// + // isSingleSource // + //----------------// + @Override + public boolean isSingleSource () + { + return true; + } + + //----------------// + // isSingleTarget // + //----------------// + @Override + public boolean isSingleTarget () + { + return true; + } + + //---------// + // removed // + //---------// + @Override + public void removed (GraphEdgeChangeEvent e) + { + HeadChordInter headChord = (HeadChordInter) e.getEdgeSource(); + + if (!headChord.isRemoved()) { + headChord.invalidateCache(); + } + } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } +} diff --git a/src/main/org/audiveris/omr/sig/relation/ChordSyllableRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordSyllableRelation.java index 9a2fc8184..f90a2a1e8 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordSyllableRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordSyllableRelation.java @@ -31,9 +31,8 @@ */ @XmlRootElement(name = "chord-syllable") public class ChordSyllableRelation - extends AbstractSupport + extends Support { - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // @@ -52,4 +51,11 @@ public boolean isSingleTarget () { return true; } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/ChordTupletRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordTupletRelation.java index 2524140e1..108ea063f 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordTupletRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordTupletRelation.java @@ -39,17 +39,14 @@ */ @XmlRootElement(name = "chord-tuplet") public class ChordTupletRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Instance fields ---------------------------------------------------------------------------- /** Assigned tuplet support coefficient. */ private final double tupletCoeff; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TupletChordRelation} object. * @@ -68,7 +65,6 @@ public ChordTupletRelation () this.tupletCoeff = 0; } - //~ Methods ------------------------------------------------------------------------------------ //-------// // added // //-------// @@ -104,7 +100,10 @@ public boolean isSingleTarget () public void removed (GraphEdgeChangeEvent e) { final TupletInter tuplet = (TupletInter) e.getEdgeTarget(); - tuplet.checkAbnormal(); + + if (!tuplet.isRemoved()) { + tuplet.checkAbnormal(); + } } //----------------// @@ -116,6 +115,13 @@ protected double getTargetCoeff () return tupletCoeff; } + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //----------------// // getTupletCoeff // //----------------// @@ -133,14 +139,12 @@ private double getTupletCoeff (Shape shape) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio tupletThreeSupportCoeff = new Constant.Ratio( 2 * 0.33, diff --git a/src/main/org/audiveris/omr/sig/relation/ChordWedgeRelation.java b/src/main/org/audiveris/omr/sig/relation/ChordWedgeRelation.java index bd837d8db..fcbfe98fb 100644 --- a/src/main/org/audiveris/omr/sig/relation/ChordWedgeRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ChordWedgeRelation.java @@ -42,15 +42,13 @@ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "chord-wedge") public class ChordWedgeRelation - extends AbstractSupport + extends Support { - //~ Instance fields ---------------------------------------------------------------------------- /** Left or right side of the wedge. */ @XmlAttribute private HorizontalSide side; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ChordWedgeRelation} object. * @@ -68,7 +66,6 @@ public ChordWedgeRelation () { } - //~ Methods ------------------------------------------------------------------------------------ //-------// // added // //-------// @@ -124,4 +121,11 @@ public String toString () { return super.toString() + "/" + side; } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/ClefKeyRelation.java b/src/main/org/audiveris/omr/sig/relation/ClefKeyRelation.java index 8760b489f..4174235e9 100644 --- a/src/main/org/audiveris/omr/sig/relation/ClefKeyRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/ClefKeyRelation.java @@ -35,13 +35,11 @@ */ @XmlRootElement(name = "clef-key") public class ClefKeyRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ClefKeyRelation} object. */ @@ -49,7 +47,6 @@ public ClefKeyRelation () { } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -68,20 +65,6 @@ public boolean isSingleTarget () return true; } - //------------------------// - // maxContributionForClef // - //------------------------// - /** - * Report the maximum contribution for the clef, brought by the following key. - * - * @return maximum contribution a clef can expect (from the following key) - */ - public static double maxContributionForClef () - { - // Maximum key grade value is Grades.intrinsicRatio - return Grades.intrinsicRatio * constants.clefSupportCoeff.getValue(); - } - //----------------// // getSourceCoeff // //----------------// @@ -100,14 +83,26 @@ protected double getTargetCoeff () return constants.keySupportCoeff.getValue(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------------------// + // maxContributionForClef // + //------------------------// + /** + * Report the maximum contribution for the clef, brought by the following key. + * + * @return maximum contribution a clef can expect (from the following key) + */ + public static double maxContributionForClef () + { + // Maximum key grade value is Grades.intrinsicRatio + return Grades.intrinsicRatio * constants.clefSupportCoeff.getValue(); + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio clefSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/Containment.java b/src/main/org/audiveris/omr/sig/relation/Containment.java old mode 100755 new mode 100644 index cd919064b..1e6577d97 --- a/src/main/org/audiveris/omr/sig/relation/Containment.java +++ b/src/main/org/audiveris/omr/sig/relation/Containment.java @@ -1,31 +1,91 @@ -//------------------------------------------------------------------------------------------------// -// // -// C o n t a i n m e n t // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -/** - * Interface {@code Containment} defines ensemble - member relation. - * - * @author Hervé Bitteur - */ -public interface Containment -{ -} +//------------------------------------------------------------------------------------------------// +// // +// C o n t a i n m e n t // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.inter.InterEnsemble; + +import org.jgrapht.event.GraphEdgeChangeEvent; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code Containment} represents an ensemble - member relation. + * + * @author Hervé Bitteur + */ +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = "containment") +public class Containment + extends Relation +{ + + //-------// + // added // + //-------// + @Override + public void added (GraphEdgeChangeEvent e) + { + InterEnsemble ensemble = (InterEnsemble) e.getEdgeSource(); + ensemble.invalidateCache(); + } + + //----------------// + // isSingleSource // + //----------------// + @Override + public boolean isSingleSource () + { + return true; + } + + //----------------// + // isSingleTarget // + //----------------// + @Override + public boolean isSingleTarget () + { + return false; + } + + //---------// + // removed // + //---------// + @Override + public void removed (GraphEdgeChangeEvent e) + { + InterEnsemble ensemble = (InterEnsemble) e.getEdgeSource(); + + if (!ensemble.isRemoved()) { + ensemble.invalidateCache(); + } + } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } +} diff --git a/src/main/org/audiveris/omr/sig/relation/DotFermataRelation.java b/src/main/org/audiveris/omr/sig/relation/DotFermataRelation.java index e150bdce8..d38a56755 100644 --- a/src/main/org/audiveris/omr/sig/relation/DotFermataRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/DotFermataRelation.java @@ -48,7 +48,6 @@ public class DotFermataRelation extends AbstractConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -56,10 +55,8 @@ public class DotFermataRelation private static final double[] OUT_WEIGHTS = new double[]{ constants.xOutWeight.getValue(), - constants.yWeight.getValue() - }; + constants.yWeight.getValue()}; - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -127,14 +124,19 @@ protected Scale.Fraction getYGapMax (boolean manual) return constants.yGapMax; } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio dotSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/DoubleDotRelation.java b/src/main/org/audiveris/omr/sig/relation/DoubleDotRelation.java index 8623101e1..f7a209186 100644 --- a/src/main/org/audiveris/omr/sig/relation/DoubleDotRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/DoubleDotRelation.java @@ -44,7 +44,6 @@ public class DoubleDotRelation extends AbstractConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -52,10 +51,15 @@ public class DoubleDotRelation private static final double[] OUT_WEIGHTS = new double[]{ constants.xOutWeight.getValue(), - constants.yWeight.getValue() - }; + constants.yWeight.getValue()}; + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // getXOutGapMaximum // //-------------------// @@ -115,7 +119,10 @@ public boolean isSingleTarget () public void removed (GraphEdgeChangeEvent e) { final AugmentationDotInter secondDot = (AugmentationDotInter) e.getEdgeSource(); - secondDot.checkAbnormal(); + + if (!secondDot.isRemoved()) { + secondDot.checkAbnormal(); + } } //---------------// @@ -151,14 +158,12 @@ protected Scale.Fraction getYGapMax (boolean manual) return getYGapMaximum(manual); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio secondDotSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/EndingBarRelation.java b/src/main/org/audiveris/omr/sig/relation/EndingBarRelation.java index a7fb28c7b..19b9f4a8f 100644 --- a/src/main/org/audiveris/omr/sig/relation/EndingBarRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/EndingBarRelation.java @@ -44,16 +44,13 @@ public class EndingBarRelation extends AbstractConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final double[] WEIGHTS = new double[]{ constants.xWeight.getValue(), - constants.yWeight.getValue() - }; + constants.yWeight.getValue()}; - //~ Instance fields ---------------------------------------------------------------------------- /** Which side of ending is used?. */ @XmlAttribute(name = "side") private HorizontalSide endingSide; @@ -61,7 +58,6 @@ public class EndingBarRelation /** Horizontal delta (in interline) between bar line and ending side. */ private final double xDistance; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new EndingBarRelation object. * @@ -83,7 +79,13 @@ public EndingBarRelation () this.xDistance = 0; } - //~ Methods ------------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //----------------// // getXGapMaximum // //----------------// @@ -219,14 +221,12 @@ protected String internals () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio endingSupportCoeff = new Constant.Ratio( 3, diff --git a/src/main/org/audiveris/omr/sig/relation/EndingSentenceRelation.java b/src/main/org/audiveris/omr/sig/relation/EndingSentenceRelation.java index b8f71aa07..0930429a2 100644 --- a/src/main/org/audiveris/omr/sig/relation/EndingSentenceRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/EndingSentenceRelation.java @@ -31,9 +31,8 @@ */ @XmlRootElement(name = "ending-sentence") public class EndingSentenceRelation - extends AbstractSupport + extends Support { - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // @@ -52,4 +51,11 @@ public boolean isSingleTarget () { return true; } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/Exclusion.java b/src/main/org/audiveris/omr/sig/relation/Exclusion.java index cd181bfb4..28f73450d 100644 --- a/src/main/org/audiveris/omr/sig/relation/Exclusion.java +++ b/src/main/org/audiveris/omr/sig/relation/Exclusion.java @@ -32,23 +32,12 @@ */ @XmlRootElement(name = "exclusion") public class Exclusion - extends AbstractRelation + extends Relation { - //~ Enumerations ------------------------------------------------------------------------------- - public enum Cause - { - //~ Enumeration constant initializers ------------------------------------------------------ - - OVERLAP, - INCOMPATIBLE; - } - - //~ Instance fields ---------------------------------------------------------------------------- @XmlAttribute public final Cause cause; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Exclusion object. * @@ -67,7 +56,6 @@ private Exclusion () this.cause = null; } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -94,4 +82,17 @@ protected String internals () { return super.internals() + cause; } + + /** + * Cause of exclusion. + */ + public enum Cause + { + + /** Physical overlap. */ + OVERLAP, + + /** Some logical exclusion. */ + INCOMPATIBLE + } } diff --git a/src/main/org/audiveris/omr/sig/relation/FermataBarRelation.java b/src/main/org/audiveris/omr/sig/relation/FermataBarRelation.java index 8dabd59f3..2390f6570 100644 --- a/src/main/org/audiveris/omr/sig/relation/FermataBarRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/FermataBarRelation.java @@ -34,13 +34,11 @@ */ @XmlRootElement(name = "fermata-bar") public class FermataBarRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -68,14 +66,19 @@ protected double getSourceCoeff () return constants.fermataSupportCoeff.getValue(); } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio fermataSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/FermataChordRelation.java b/src/main/org/audiveris/omr/sig/relation/FermataChordRelation.java index 8d85ddb0e..ed3ddab02 100644 --- a/src/main/org/audiveris/omr/sig/relation/FermataChordRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/FermataChordRelation.java @@ -34,13 +34,11 @@ */ @XmlRootElement(name = "fermata-chord") public class FermataChordRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -77,14 +75,19 @@ protected double getTargetCoeff () return constants.chordSupportCoeff.getValue(); } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio fermataSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/FlagStemRelation.java b/src/main/org/audiveris/omr/sig/relation/FlagStemRelation.java index 3e3faf3df..576df8e06 100644 --- a/src/main/org/audiveris/omr/sig/relation/FlagStemRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/FlagStemRelation.java @@ -25,7 +25,7 @@ import org.audiveris.omr.constant.ConstantSet; import static org.audiveris.omr.glyph.ShapeSet.FlagsUp; import org.audiveris.omr.sheet.Scale; -import org.audiveris.omr.sig.inter.FlagInter; +import org.audiveris.omr.sig.inter.AbstractFlagInter; import org.audiveris.omr.sig.inter.Inter; import static org.audiveris.omr.sig.relation.StemPortion.*; @@ -48,20 +48,18 @@ public class FlagStemRelation extends AbstractStemConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(FlagStemRelation.class); - //~ Methods ------------------------------------------------------------------------------------ //-------// // added // //-------// @Override public void added (GraphEdgeChangeEvent e) { - final FlagInter flag = (FlagInter) e.getEdgeSource(); + final AbstractFlagInter flag = (AbstractFlagInter) e.getEdgeSource(); flag.checkAbnormal(); } @@ -76,36 +74,13 @@ public StemPortion getStemPortion (Inter source, final double margin = scale.getInterline(); // TODO: use a constant instead? if (FlagsUp.contains(source.getShape())) { - return (extensionPoint.getY() > (stemLine.getY2() - margin)) ? STEM_BOTTOM : STEM_MIDDLE; + return (extensionPoint.getY() > (stemLine.getY2() - margin)) ? STEM_BOTTOM + : STEM_MIDDLE; } else { return (extensionPoint.getY() < (stemLine.getY1() + margin)) ? STEM_TOP : STEM_MIDDLE; } } - //------------------// - // getXInGapMaximum // - //------------------// - public static Scale.Fraction getXInGapMaximum (boolean manual) - { - return manual ? constants.xInGapMaxManual : constants.xInGapMax; - } - - //-------------------// - // getXOutGapMaximum // - //-------------------// - public static Scale.Fraction getXOutGapMaximum (boolean manual) - { - return manual ? constants.xOutGapMaxManual : constants.xOutGapMax; - } - - //----------------// - // getYGapMaximum // - //----------------// - public static Scale.Fraction getYGapMaximum (boolean manual) - { - return manual ? constants.yGapMaxManual : constants.yGapMax; - } - //----------------// // isSingleSource // //----------------// @@ -130,8 +105,11 @@ public boolean isSingleTarget () @Override public void removed (GraphEdgeChangeEvent e) { - final FlagInter flag = (FlagInter) e.getEdgeSource(); - flag.checkAbnormal(); + final AbstractFlagInter flag = (AbstractFlagInter) e.getEdgeSource(); + + if (!flag.isRemoved()) { + flag.checkAbnormal(); + } } //----------------// @@ -179,14 +157,36 @@ protected Scale.Fraction getYGapMax (boolean manual) return getYGapMaximum(manual); } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------------// + // getXInGapMaximum // + //------------------// + public static Scale.Fraction getXInGapMaximum (boolean manual) + { + return manual ? constants.xInGapMaxManual : constants.xInGapMax; + } + + //-------------------// + // getXOutGapMaximum // + //-------------------// + public static Scale.Fraction getXOutGapMaximum (boolean manual) + { + return manual ? constants.xOutGapMaxManual : constants.xOutGapMax; + } + + //----------------// + // getYGapMaximum // + //----------------// + public static Scale.Fraction getYGapMaximum (boolean manual) + { + return manual ? constants.yGapMaxManual : constants.yGapMax; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio flagSupportCoeff = new Constant.Ratio( 3, diff --git a/src/main/org/audiveris/omr/sig/relation/HeadHeadRelation.java b/src/main/org/audiveris/omr/sig/relation/HeadHeadRelation.java index ddabe8356..307261488 100644 --- a/src/main/org/audiveris/omr/sig/relation/HeadHeadRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/HeadHeadRelation.java @@ -1,93 +1,96 @@ -//------------------------------------------------------------------------------------------------// -// // -// H e a d H e a d R e l a t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code HeadHeadRelation} represents the relation support between two heads - * (of the same kind) on a shared stem. - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "head-head") -public class HeadHeadRelation - extends AbstractSupport -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - //~ Methods ------------------------------------------------------------------------------------ - //----------------// - // isSingleSource // - //----------------// - @Override - public boolean isSingleSource () - { - return false; - } - - //----------------// - // isSingleTarget // - //----------------// - @Override - public boolean isSingleTarget () - { - return false; - } - - //----------------// - // getSourceCoeff // - //----------------// - @Override - protected double getSourceCoeff () - { - return constants.headSupportCoeff.getValue(); - } - - //----------------// - // getTargetCoeff // - //----------------// - @Override - protected double getTargetCoeff () - { - return constants.headSupportCoeff.getValue(); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Ratio headSupportCoeff = new Constant.Ratio( - 1, - "Value for (source or target) head coeff in head-head support formula"); - } -} +//------------------------------------------------------------------------------------------------// +// // +// H e a d H e a d R e l a t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code HeadHeadRelation} represents the relation support between two heads + * (of the same kind) on a shared stem. + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "head-head") +public class HeadHeadRelation + extends Support +{ + + private static final Constants constants = new Constants(); + + //----------------// + // isSingleSource // + //----------------// + @Override + public boolean isSingleSource () + { + return false; + } + + //----------------// + // isSingleTarget // + //----------------// + @Override + public boolean isSingleTarget () + { + return false; + } + + //----------------// + // getSourceCoeff // + //----------------// + @Override + protected double getSourceCoeff () + { + return constants.headSupportCoeff.getValue(); + } + + //----------------// + // getTargetCoeff // + //----------------// + @Override + protected double getTargetCoeff () + { + return constants.headSupportCoeff.getValue(); + } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Ratio headSupportCoeff = new Constant.Ratio( + 1, + "Value for (source or target) head coeff in head-head support formula"); + } +} diff --git a/src/main/org/audiveris/omr/sig/relation/HeadStemRelation.java b/src/main/org/audiveris/omr/sig/relation/HeadStemRelation.java index f9f876c29..3d7083ee5 100644 --- a/src/main/org/audiveris/omr/sig/relation/HeadStemRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/HeadStemRelation.java @@ -27,6 +27,7 @@ import org.audiveris.omr.sheet.Scale; import org.audiveris.omr.sheet.beam.BeamGroup; import org.audiveris.omr.sheet.rhythm.Measure; +import org.audiveris.omr.sig.SIGraph; import org.audiveris.omr.sig.inter.AbstractBeamInter; import org.audiveris.omr.sig.inter.HeadChordInter; import org.audiveris.omr.sig.inter.HeadInter; @@ -41,6 +42,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.awt.Point; +import java.awt.Rectangle; import java.awt.geom.Line2D; import java.awt.geom.Point2D; @@ -50,6 +53,21 @@ /** * Class {@code HeadStemRelation} represents the relation support between a head and a * stem. + *

                                                                                                                                    + * A special configuration is known as the canonical "shared" configuration. + * It gathers a head with two stems: + *

                                                                                                                                      + *
                                                                                                                                    • STEM_TOP on head LEFT side + *
                                                                                                                                    • STEM_BOTTOM on head RIGHT side + *
                                                                                                                                    + * + *
                                                                                                                                    + *    |
                                                                                                                                    + *    |
                                                                                                                                    + *  +O+
                                                                                                                                    + *  |
                                                                                                                                    + *  |
                                                                                                                                    + * 
                                                                                                                                    * * @author Hervé Bitteur */ @@ -57,19 +75,15 @@ public class HeadStemRelation extends AbstractStemConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - HeadStemRelation.class); + private static final Logger logger = LoggerFactory.getLogger(HeadStemRelation.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Which side of head is used?. */ @XmlAttribute(name = "head-side") private HorizontalSide headSide; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code HeadStemRelation} object. */ @@ -77,7 +91,6 @@ public HeadStemRelation () { } - //~ Methods ------------------------------------------------------------------------------------ //-------// // added // //-------// @@ -108,7 +121,17 @@ public void added (GraphEdgeChangeEvent e) HeadChordInter ch = head.getChord(); if (ch != null) { - ch.setStem(stem); + StemInter existingStem = ch.getStem(); + + if (existingStem != stem) { + if (existingStem != null) { + SIGraph sig = stem.getSig(); + Relation rel = sig.getRelation(ch, existingStem, ChordStemRelation.class); + sig.removeEdge(rel); + } + + ch.setStem(stem); + } // Propagate to beam if any Measure measure = ch.getMeasure(); @@ -125,6 +148,13 @@ public void added (GraphEdgeChangeEvent e) stem.checkAbnormal(); } + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //------------------// // getXInGapMaximum // //------------------// @@ -165,14 +195,31 @@ public StemPortion getStemPortion (Inter source, Line2D stemLine, Scale scale) { - final double margin = source.getBounds().height * constants.anchorHeightRatio.getValue(); - final double midStem = (stemLine.getY1() + stemLine.getY2()) / 2; - final double anchor = extensionPoint.getY(); + return getStemPortion((HeadInter) source, stemLine, extensionPoint.getY()); + } + + //----------------// + // getStemPortion // + //----------------// + /** + * Helper method to retrieve StemPortion of the connection. + * + * @param head the item connected to the stem (head) + * @param stemLine logical range of the stem + * @param yExtension ordinate of head-stem extension point + * @return the stem Portion + */ + public static StemPortion getStemPortion (HeadInter head, + Line2D stemLine, + double yExtension) + { + final double margin = head.getBounds().height * constants.anchorHeightRatio.getValue(); + final double yMidStem = (stemLine.getY1() + stemLine.getY2()) / 2; - if (anchor >= midStem) { - return (anchor > (stemLine.getY2() - margin)) ? STEM_BOTTOM : STEM_MIDDLE; + if (yExtension >= yMidStem) { + return (yExtension > (stemLine.getY2() - margin)) ? STEM_BOTTOM : STEM_MIDDLE; } else { - return (anchor < (stemLine.getY1() + margin)) ? STEM_TOP : STEM_MIDDLE; + return (yExtension < (stemLine.getY1() + margin)) ? STEM_TOP : STEM_MIDDLE; } } @@ -187,8 +234,8 @@ public StemPortion getStemPortion (Inter source, */ public boolean isInvading () { - return (dy <= constants.maxInvadingDy.getValue()) - && (dx <= constants.maxInvadingDx.getValue()); + return (dy <= constants.maxInvadingDy.getValue()) && (dx <= constants.maxInvadingDx + .getValue()); } //----------------// @@ -218,8 +265,13 @@ public void removed (GraphEdgeChangeEvent e) final HeadInter head = (HeadInter) e.getEdgeSource(); final StemInter stem = (StemInter) e.getEdgeTarget(); - head.checkAbnormal(); - stem.checkAbnormal(); + if (!head.isRemoved()) { + head.checkAbnormal(); + } + + if (!stem.isRemoved()) { + stem.checkAbnormal(); + } } //-------------// @@ -287,14 +339,101 @@ protected String internals () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ + /** + * Check whether this is the canonical "shared" configuration. + * It uses stems. + * + * @param leftStem stem on left + * @param head head in the middle + * @param rightStem stem on right + * @return true if canonical + */ + public static boolean isCanonicalShare (StemInter leftStem, + HeadInter head, + StemInter rightStem) + { + return isCanonicalShare(leftStem, null, head, null, rightStem); + } + + /** + * Check whether this is the canonical "shared" configuration. + * It uses relations to stems. + * + * @param leftRel head-stem relation on left + * @param head head in the middle + * @param rightRel head-stem relation on right + * @return true if canonical + */ + public static boolean isCanonicalShare (HeadStemRelation leftRel, + HeadInter head, + HeadStemRelation rightRel) + { + return isCanonicalShare(null, leftRel, head, rightRel, null); + } + + /** + * Check whether this is the canonical "shared" configuration. + * It uses stems and/or relations to stems. + * + * @param leftStem stem on left (can be null if leftRel is not) + * @param leftRel head-stem relation on left (can be null if leftStem is not) + * @param head head in the middle + * @param rightRel head-stem relation on right (can be null if rightStem is not) + * @param rightStem stem on right (can be null if rightRel is not) + * @return true if canonical + */ + public static boolean isCanonicalShare (StemInter leftStem, + HeadStemRelation leftRel, + HeadInter head, + HeadStemRelation rightRel, + StemInter rightStem) + { + final SIGraph sig = head.getSig(); + + if (leftStem == null) { + if (leftRel == null) { + return false; + } + + leftStem = (StemInter) sig.getOppositeInter(head, leftRel); + } + + if (rightStem == null) { + if (rightRel == null) { + return false; + } + + rightStem = (StemInter) sig.getOppositeInter(head, rightRel); + } + + // Prefer use of relation extension points over stem physical limits + Line2D leftLine = leftStem.computeExtendedLine(); + Line2D rightLine = rightStem.computeExtendedLine(); + + Rectangle headBox = head.getBounds(); + Point headCenter = head.getCenter(); + double yMidLeft = (leftLine.getY1() + leftLine.getY2()) / 2; + double yMidRight = (rightLine.getY1() + rightLine.getY2()) / 2; + if (headCenter.y >= yMidLeft || headCenter.y <= yMidRight) { + return false; + } + + double yLeftExt = leftRel != null ? leftRel.getExtensionPoint().getY() : headBox.y; + double yRightExt = rightRel != null ? rightRel.getExtensionPoint().getY() + : headBox.y + headBox.height - 1; + + StemPortion leftPortion = getStemPortion(head, leftLine, yLeftExt); + StemPortion rightPortion = getStemPortion(head, rightLine, yRightExt); + + return leftPortion == STEM_TOP && rightPortion == STEM_BOTTOM; + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio headSupportCoeff = new Constant.Ratio( 1, @@ -313,12 +452,12 @@ private static final class Constants "Maximum manual horizontal overlap between stem & head"); private final Scale.Fraction xOutGapMax = new Scale.Fraction( - 0.25, + 0.275, "Maximum horizontal gap between stem & head"); private final Scale.Fraction xOutGapMaxManual = new Scale.Fraction( 0.35, - "Maximum manualhorizontal gap between stem & head"); + "Maximum manual horizontal gap between stem & head"); private final Scale.Fraction yGapMax = new Scale.Fraction( 0.8, diff --git a/src/main/org/audiveris/omr/sig/relation/KeyAltersRelation.java b/src/main/org/audiveris/omr/sig/relation/KeyAltersRelation.java index 8a5bb0cb9..26e6adf3d 100644 --- a/src/main/org/audiveris/omr/sig/relation/KeyAltersRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/KeyAltersRelation.java @@ -34,13 +34,11 @@ */ @XmlRootElement(name = "key-alters") public class KeyAltersRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code KeyAltersRelation} object. */ @@ -48,7 +46,6 @@ public KeyAltersRelation () { } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -85,14 +82,19 @@ protected double getTargetCoeff () return constants.targetSupportCoeff.getValue(); } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio sourceSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/Link.java b/src/main/org/audiveris/omr/sig/relation/Link.java index 652657564..c582a5d13 100644 --- a/src/main/org/audiveris/omr/sig/relation/Link.java +++ b/src/main/org/audiveris/omr/sig/relation/Link.java @@ -1,117 +1,136 @@ -//------------------------------------------------------------------------------------------------// -// // -// L i n k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.Inter; - -import java.util.Collections; -import java.util.List; - -/** - * Seen from an Inter instance, class {@code Link} describes a potential relation with - * another Inter instance (the partner). - *

                                                                                                                                    - * This is meant to deal with a potential relation between the inter instance and the partner, - * before perhaps recording the relation as an edge within the SIG. - * - * @author Hervé Bitteur - */ -public class Link - implements Comparable -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** The other Inter instance, the one to be linked with. */ - public Inter partner; - - /** The concrete relation. */ - public final Relation relation; - - /** True for Inter as source and Partner as target, false for the reverse. */ - public final boolean outgoing; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code Link} object. - * - * @param partner the partnering inter instance - * @param relation the relation with this partner - * @param outgoing true if partner is target, false if it is source - */ - public Link (Inter partner, - Relation relation, - boolean outgoing) - { - this.partner = partner; - this.relation = relation; - this.outgoing = outgoing; - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Add the relation between the provided inter and the partner. - * - * @param inter the provided inter - */ - public void applyTo (Inter inter) - { - final SIGraph sig = inter.getSig(); - final Inter source = outgoing ? inter : partner; - final Inter target = outgoing ? partner : inter; - - sig.addEdge(source, target, relation); - } - - //--------// - // bestOf // - //--------// - public static Link bestOf (List links) - { - if (links.size() > 1) { - Collections.sort(links); - } - - return links.isEmpty() ? null : links.get(0); - } - - @Override - public int compareTo (Link that) - { - AbstractSupport s1 = (AbstractSupport) this.relation; - AbstractSupport s2 = (AbstractSupport) that.relation; - - return Double.compare(s1.getGrade(), s2.getGrade()); - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("Link{"); - sb.append(partner); - sb.append(" ").append(relation); - sb.append(" ").append(outgoing ? "OUTGOING" : "INCOMING"); - sb.append("}"); - - return sb.toString(); - } -} +//------------------------------------------------------------------------------------------------// +// // +// L i n k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.Inter; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Seen from an Inter instance, class {@code Link} describes a potential relation with + * another Inter instance (the partner). + *

                                                                                                                                    + * This is meant to deal with a potential relation between the inter instance and the + * partner, before perhaps recording the relation as an edge within the SIG. + * + * @author Hervé Bitteur + */ +public class Link +{ + + /** + * For comparing Link instances by decreasing grade. + */ + public static final Comparator byReverseGrade = new Comparator() + { + @Override + public int compare (Link l1, + Link l2) + { + Support s1 = (Support) l1.relation; + Support s2 = (Support) l2.relation; + + return Double.compare(s2.getGrade(), s1.getGrade()); + } + }; + + /** The other Inter instance, the one to be linked with. */ + public Inter partner; + + /** The concrete relation. */ + public final Relation relation; + + /** True for Inter as source and Partner as target, false for the reverse. */ + public final boolean outgoing; + + /** + * Creates a new {@code Link} object. + * + * @param partner the partnering inter instance + * @param relation the relation with this partner + * @param outgoing true if partner is target, false if it is source + */ + public Link (Inter partner, + Relation relation, + boolean outgoing) + { + this.partner = partner; + this.relation = relation; + this.outgoing = outgoing; + } + + //---------// + // applyTo // + //---------// + /** + * Add the relation between the provided inter and the partner, unless an instance + * of the same relation class already exists between them. + * + * @param inter the provided inter + */ + public void applyTo (Inter inter) + { + final SIGraph sig = inter.getSig(); + final Inter source = outgoing ? inter : partner; + final Inter target = outgoing ? partner : inter; + + if (null == sig.getRelation(source, target, relation.getClass())) { + sig.addEdge(source, target, relation); + } + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("Link{"); + sb.append(partner); + sb.append(" ").append(relation); + sb.append(" ").append(outgoing ? "OUTGOING" : "INCOMING"); + sb.append("}"); + + return sb.toString(); + } + + //--------// + // bestOf // + //--------// + /** + * Report the best of provided links. + * + * @param links provided links + * @return the best link or null if empty + */ + public static Link bestOf (List links) + { + if (links.size() > 1) { + Collections.sort(links, byReverseGrade); + } + + return links.isEmpty() ? null : links.get(0); + } +} diff --git a/src/main/org/audiveris/omr/sig/relation/MarkerBarRelation.java b/src/main/org/audiveris/omr/sig/relation/MarkerBarRelation.java index d87caf341..7a78d9106 100644 --- a/src/main/org/audiveris/omr/sig/relation/MarkerBarRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/MarkerBarRelation.java @@ -1,84 +1,87 @@ -//------------------------------------------------------------------------------------------------// -// // -// M a r k e r B a r R e l a t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code MarkerBarRelation} represents the geographical relation between a marker - * and a StaffBarline. - * - * @author Hervé Bitteur - */ -@XmlRootElement(name = "marker-bar") -public class MarkerBarRelation - extends AbstractSupport -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - //~ Methods ------------------------------------------------------------------------------------ - //----------------// - // isSingleSource // - //----------------// - @Override - public boolean isSingleSource () - { - return true; - } - - //----------------// - // isSingleTarget // - //----------------// - @Override - public boolean isSingleTarget () - { - return true; - } - - //----------------// - // getSourceCoeff // - //----------------// - @Override - protected double getSourceCoeff () - { - return constants.markerSupportCoeff.getValue(); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Ratio markerSupportCoeff = new Constant.Ratio( - 3, - "Supporting coeff for (source) marker"); - } -} +//------------------------------------------------------------------------------------------------// +// // +// M a r k e r B a r R e l a t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code MarkerBarRelation} represents the geographical relation between a marker + * and a StaffBarline. + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "marker-bar") +public class MarkerBarRelation + extends Support +{ + + private static final Constants constants = new Constants(); + + //----------------// + // isSingleSource // + //----------------// + @Override + public boolean isSingleSource () + { + return true; + } + + //----------------// + // isSingleTarget // + //----------------// + @Override + public boolean isSingleTarget () + { + return true; + } + + //----------------// + // getSourceCoeff // + //----------------// + @Override + protected double getSourceCoeff () + { + return constants.markerSupportCoeff.getValue(); + } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Ratio markerSupportCoeff = new Constant.Ratio( + 3, + "Supporting coeff for (source) marker"); + } +} diff --git a/src/main/org/audiveris/omr/sig/relation/BasicContainment.java b/src/main/org/audiveris/omr/sig/relation/MirrorRelation.java similarity index 60% rename from src/main/org/audiveris/omr/sig/relation/BasicContainment.java rename to src/main/org/audiveris/omr/sig/relation/MirrorRelation.java index d901f117d..d5b6f8735 100644 --- a/src/main/org/audiveris/omr/sig/relation/BasicContainment.java +++ b/src/main/org/audiveris/omr/sig/relation/MirrorRelation.java @@ -1,83 +1,63 @@ -//------------------------------------------------------------------------------------------------// -// // -// B a s i c C o n t a i n m e n t // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.inter.InterEnsemble; - -import org.jgrapht.event.GraphEdgeChangeEvent; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - -/** - * Class {@code BasicContainment} represents an ensemble - member relation. - * - * @author Hervé Bitteur - */ -@XmlAccessorType(XmlAccessType.NONE) -@XmlRootElement(name = "containment") -public class BasicContainment - extends AbstractRelation - implements Containment -{ - //~ Methods ------------------------------------------------------------------------------------ - - //-------// - // added // - //-------// - @Override - public void added (GraphEdgeChangeEvent e) - { - InterEnsemble ensemble = (InterEnsemble) e.getEdgeSource(); - ensemble.invalidateCache(); - } - - //----------------// - // isSingleSource // - //----------------// - @Override - public boolean isSingleSource () - { - return true; - } - - //----------------// - // isSingleTarget // - //----------------// - @Override - public boolean isSingleTarget () - { - return false; - } - - //---------// - // removed // - //---------// - @Override - public void removed (GraphEdgeChangeEvent e) - { - InterEnsemble ensemble = (InterEnsemble) e.getEdgeSource(); - ensemble.invalidateCache(); - } -} +//------------------------------------------------------------------------------------------------// +// // +// M i r r o r R e l a t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Class {@code MirrorRelation} formalizes the relation between two mirrored inters. + *

                                                                                                                                    + * This relation replaced the deprecated mirror field member in {@link AbstractInter} + * + * @author Hervé Bitteur + */ +@XmlRootElement(name = "mirror") +public class MirrorRelation + extends Support +{ + + //----------------// + // isSingleSource // + //----------------// + @Override + public boolean isSingleSource () + { + return true; + } + + //----------------// + // isSingleTarget // + //----------------// + @Override + public boolean isSingleTarget () + { + return true; + } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/src/main/org/audiveris/omr/sig/relation/NoExclusion.java b/src/main/org/audiveris/omr/sig/relation/NoExclusion.java index 568d77966..66ae5862d 100644 --- a/src/main/org/audiveris/omr/sig/relation/NoExclusion.java +++ b/src/main/org/audiveris/omr/sig/relation/NoExclusion.java @@ -32,9 +32,8 @@ */ @XmlRootElement(name = "no-exclusion") public class NoExclusion - extends AbstractSupport + extends Support { - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // @@ -53,4 +52,11 @@ public boolean isSingleTarget () { return false; } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/Relation.java b/src/main/org/audiveris/omr/sig/relation/Relation.java index f763d144c..3a6749e36 100644 --- a/src/main/org/audiveris/omr/sig/relation/Relation.java +++ b/src/main/org/audiveris/omr/sig/relation/Relation.java @@ -23,94 +23,219 @@ import org.audiveris.omr.sig.SIGraph; import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.util.Jaxb; import org.jgrapht.event.GraphEdgeChangeEvent; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + /** - * Interface {@code Relation} describes a relation between two Interpretation instances. + * Abstract class {@code Relation} describes a relation between two Inter instances + * within the same SIG. * * @author Hervé Bitteur */ -public interface Relation +@XmlAccessorType(XmlAccessType.NONE) +public abstract class Relation + implements Cloneable { - //~ Methods ------------------------------------------------------------------------------------ + // Persistent data + //---------------- + // + /** Indicates that this relation was set manually. */ + @XmlAttribute(name = "manual") + @XmlJavaTypeAdapter(type = boolean.class, value = Jaxb.BooleanPositiveAdapter.class) + private boolean manual; + + //-------// + // added // + //-------// /** * Notifies that this relation has been added to the sig. * * @param e the relation event. */ - void added (GraphEdgeChangeEvent e); + public void added (GraphEdgeChangeEvent e) + { + // No-op by default + } + //-----------// + // duplicate // + //-----------// /** * Clone a relation. * * @return the cloned relation */ - Relation duplicate (); + public Relation duplicate () + { + try { + return (Relation) super.clone(); + } catch (CloneNotSupportedException ex) { + return null; + } + } + //------------// + // getDetails // + //------------// /** * Details for tip. * * @return relation details */ - String getDetails (); + public String getDetails () + { + return internals(); + } + //---------// + // getName // + //---------// /** * Short name. * * @return the relation short name */ - String getName (); + public String getName () + { + return Relations.nameOf(getClass()); + } + //----------// + // isManual // + //----------// /** * Report whether this relation has been set manually. * * @return true if manual */ - boolean isManual (); + public boolean isManual () + { + return manual; + } + + //-----------// + // setManual // + //-----------// + /** + * Set this relation as a manual one. + * + * @param manual new value + */ + public void setManual (boolean manual) + { + this.manual = manual; + } /** * Tell if, seen from a given target, there can be at most one source. * * @return true if source number is limited to 1, false by default */ - boolean isSingleSource (); + public abstract boolean isSingleSource (); /** * Tell if, seen from a given source, there can be at most one target. * * @return true if target number is limited to 1, false by default */ - boolean isSingleTarget (); + public abstract boolean isSingleTarget (); + //---------// + // removed // + //---------// /** * Notifies that this relation has been removed from the sig. * * @param e the relation event. */ - void removed (GraphEdgeChangeEvent e); + public void removed (GraphEdgeChangeEvent e) + { + // No-op by default + } + //----------// + // seenFrom // + //----------// /** * Relation description when seen from one of its involved inters * * @param inter the interpretation point of view * @return the inter-based description */ - String seenFrom (Inter inter); + public String seenFrom (Inter inter) + { + final StringBuilder sb = new StringBuilder(toString()); + final SIGraph sig = inter.getSig(); - /** - * Set this relation as a manual one. - * - * @param manual new value - */ - void setManual (boolean manual); + if (sig != null) { + final Inter source = sig.getEdgeSource(this); + + if (source != inter) { + sb.append("<-").append(source); + } else { + final Inter target = sig.getEdgeTarget(this); + + if (target != inter) { + sb.append("->").append(target); + } + } + } + return sb.toString(); + } + + //--------------// + // toLongString // + //--------------// /** * Report a long description of the relation * * @param sig the containing sig * @return long description */ - String toLongString (SIGraph sig); + public String toLongString (SIGraph sig) + { + final Inter source = sig.getEdgeSource(this); + final Inter target = sig.getEdgeTarget(this); + final StringBuilder sb = new StringBuilder(); + + sb.append(source); + sb.append("-"); + sb.append(getName()); + sb.append(":"); + sb.append(getDetails()); + sb.append("-"); + sb.append(target); + + return sb.toString(); + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + return getName(); + } + + //-----------// + // internals // + //-----------// + /** + * Report a description string of class internals. + * + * @return description string of internals + */ + protected String internals () + { + return isManual() ? "MANUAL" : ""; + } } diff --git a/src/main/org/audiveris/omr/sig/relation/Relations.java b/src/main/org/audiveris/omr/sig/relation/Relations.java index 4973e5714..3c25ba031 100644 --- a/src/main/org/audiveris/omr/sig/relation/Relations.java +++ b/src/main/org/audiveris/omr/sig/relation/Relations.java @@ -1,319 +1,326 @@ -//------------------------------------------------------------------------------------------------// -// // -// R e l a t i o n s // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.AbstractBeamInter; -import org.audiveris.omr.sig.inter.AbstractChordInter; -import org.audiveris.omr.sig.inter.AbstractNoteInter; -import org.audiveris.omr.sig.inter.AlterInter; -import org.audiveris.omr.sig.inter.ArpeggiatoInter; -import org.audiveris.omr.sig.inter.ArticulationInter; -import org.audiveris.omr.sig.inter.AugmentationDotInter; -import org.audiveris.omr.sig.inter.BarlineInter; -import org.audiveris.omr.sig.inter.ChordNameInter; -import org.audiveris.omr.sig.inter.DynamicsInter; -import org.audiveris.omr.sig.inter.EndingInter; -import org.audiveris.omr.sig.inter.FermataArcInter; -import org.audiveris.omr.sig.inter.FermataDotInter; -import org.audiveris.omr.sig.inter.FermataInter; -import org.audiveris.omr.sig.inter.FlagInter; -import org.audiveris.omr.sig.inter.HeadChordInter; -import org.audiveris.omr.sig.inter.HeadInter; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.inter.LyricItemInter; -import org.audiveris.omr.sig.inter.MarkerInter; -import org.audiveris.omr.sig.inter.OrnamentInter; -import org.audiveris.omr.sig.inter.PedalInter; -import org.audiveris.omr.sig.inter.RepeatDotInter; -import org.audiveris.omr.sig.inter.SentenceInter; -import org.audiveris.omr.sig.inter.SlurInter; -import org.audiveris.omr.sig.inter.StaffBarlineInter; -import org.audiveris.omr.sig.inter.StemInter; -import org.audiveris.omr.sig.inter.TimeNumberInter; -import org.audiveris.omr.sig.inter.TupletInter; -import org.audiveris.omr.sig.inter.WedgeInter; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * Class {@code Relations} gathers utilities for Relation classes and instances. - * - * @author Hervé Bitteur - */ -public abstract class Relations -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger( - Relations.class); - - private static final Map, Set>> src = new LinkedHashMap, Set>>(); - - private static final Map, Set>> tgt = new LinkedHashMap, Set>>(); - - static { - buildMaps(); - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Report the defined relation classes between the provided source and target - * inter classes. - * - * @param sourceClass provided inter class as source - * @param targetClass provided inter class as target - * @return the list of defined relation classes, perhaps empty - */ - public static Set> definedRelationsBetween ( - Class sourceClass, - Class targetClass) - { - final Set> defined = new LinkedHashSet>(); - Set> from = definedRelationsFrom(sourceClass); - Set> to = definedRelationsTo(targetClass); - defined.addAll(from); - defined.retainAll(to); - - if (defined.isEmpty()) { - return Collections.emptySet(); - } else { - return defined; - } - } - - /** - * Report the defined relation classes from the provided source inter class. - * - * @param sourceClass provided inter class as source - * @return the list of defined relation classes, perhaps empty - */ - public static Set> definedRelationsFrom ( - Class sourceClass) - { - Objects.requireNonNull(sourceClass, "Source class is null"); - - final Set> defined = new LinkedHashSet>(); - Class classe = sourceClass; - - while (true) { - if ((classe == null) || !Inter.class.isAssignableFrom(classe)) { - break; - } - - Set> set = src.get(classe); - - if (set != null) { - defined.addAll(set); - } - - classe = classe.getSuperclass(); - } - - if (!defined.isEmpty()) { - return Collections.unmodifiableSet(defined); - } else { - return Collections.emptySet(); - } - } - - /** - * Report the defined relation classes to the provided target inter class. - * - * @param targetClass provided inter class as target - * @return the list of defined relation classes, perhaps empty - */ - public static Set> definedRelationsTo ( - Class targetClass) - { - Objects.requireNonNull(targetClass, "Target class is null"); - - final Set> defined = new LinkedHashSet>(); - Class classe = targetClass; - - while (true) { - if ((classe == null) || !Inter.class.isAssignableFrom(classe)) { - break; - } - - Set> set = tgt.get(classe); - - if (set != null) { - defined.addAll(set); - } - - classe = classe.getSuperclass(); - } - - if (!defined.isEmpty()) { - return Collections.unmodifiableSet(defined); - } else { - return Collections.emptySet(); - } - } - - /** - * Report a simple name for the provided relation class. - * - * @param relationClass provided relation class - * @return simple name - */ - public static String nameOf (Class relationClass) - { - return relationClass.getSimpleName().replaceFirst("Relation", ""); - } - - /** - * Report the suggested relation classes between the provided source and target - * inters. - * - * @param source provided inter as source - * @param target provided inter as target - * @return the possible relation classes - */ - public static Set> suggestedRelationsBetween (Inter source, - Inter target) - { - // Check inputs - Objects.requireNonNull(source, "Source is null"); - Objects.requireNonNull(target, "Target is null"); - - SIGraph sig = source.getSig(); - Objects.requireNonNull(sig, "Source has no sig"); - - if (target.getSig() != sig) { - logger.info("Source and Target do not share the same sig"); - - return Collections.emptySet(); - } - - // Suggestions - Set> suggestions = new LinkedHashSet>( - definedRelationsBetween(source.getClass(), target.getClass())); - - // Let's not remove existing relation, to allow cleaning of relations - // // NO (Skip existing relation, if any, between source & target) NO - // Relation edge = sig.getEdge(source, target); - // - // if (edge != null) { - // suggestions.remove(edge.getClass()); - // } - // - // Return what we got - if (suggestions.isEmpty()) { - return Collections.emptySet(); - } else { - return suggestions; - } - } - - /** - * Build the maps of possible support classes for a source inter class and for a - * target inter class. - *

                                                                                                                                    - * A few relations are used only for support during reduction, rather than symbolic relation. - * They are thus excluded for lack of usefulness at UI level:

                                                                                                                                      - *
                                                                                                                                    • BarConnectionRelation - *
                                                                                                                                    • BeamHeadRelation - *
                                                                                                                                    • ClefKeyRelation - *
                                                                                                                                    • HeadHeadRelation - *
                                                                                                                                    • KeyAltersRelation - *
                                                                                                                                    • NoExclusion - *
                                                                                                                                    • StemAlignmentRelation - *
                                                                                                                                    - */ - private static void buildMaps () - { - map(AbstractBeamInter.class, BeamStemRelation.class, StemInter.class); - - map(AbstractChordInter.class, ChordDynamicsRelation.class, DynamicsInter.class); - map(AbstractChordInter.class, ChordPedalRelation.class, PedalInter.class); - map(AbstractChordInter.class, ChordTupletRelation.class, TupletInter.class); - map(AbstractChordInter.class, ChordWedgeRelation.class, WedgeInter.class); - - map(AlterInter.class, AlterHeadRelation.class, HeadInter.class); - - map(AugmentationDotInter.class, AugmentationRelation.class, AbstractNoteInter.class); - map(AugmentationDotInter.class, DoubleDotRelation.class, AugmentationDotInter.class); - - map(EndingInter.class, EndingBarRelation.class, BarlineInter.class); // Old - map(EndingInter.class, EndingBarRelation.class, StaffBarlineInter.class); - map(EndingInter.class, EndingSentenceRelation.class, SentenceInter.class); - - map(FermataDotInter.class, DotFermataRelation.class, FermataArcInter.class); // Temporary! - - map(FermataInter.class, FermataBarRelation.class, BarlineInter.class); // Old - map(FermataInter.class, FermataBarRelation.class, StaffBarlineInter.class); - map(FermataInter.class, FermataChordRelation.class, AbstractChordInter.class); - - map(FlagInter.class, FlagStemRelation.class, StemInter.class); - - map(HeadChordInter.class, ChordArpeggiatoRelation.class, ArpeggiatoInter.class); - map(HeadChordInter.class, ChordArticulationRelation.class, ArticulationInter.class); - map(HeadChordInter.class, ChordNameRelation.class, ChordNameInter.class); - map(HeadChordInter.class, ChordOrnamentRelation.class, OrnamentInter.class); - map(HeadChordInter.class, ChordSentenceRelation.class, SentenceInter.class); - map(HeadChordInter.class, ChordStemRelation.class, StemInter.class); - map(HeadChordInter.class, ChordSyllableRelation.class, LyricItemInter.class); - - map(HeadInter.class, HeadStemRelation.class, StemInter.class); - - map(MarkerInter.class, MarkerBarRelation.class, BarlineInter.class); // Old - map(MarkerInter.class, MarkerBarRelation.class, StaffBarlineInter.class); - - map(RepeatDotInter.class, RepeatDotBarRelation.class, BarlineInter.class); - map(RepeatDotInter.class, RepeatDotPairRelation.class, RepeatDotInter.class); - - map(SlurInter.class, SlurHeadRelation.class, HeadInter.class); - - map(TimeNumberInter.class, TimeTopBottomRelation.class, TimeNumberInter.class); - } - - private static Set> getSet ( - Map, Set>> map, - Class classe) - { - Set> set = map.get(classe); - - if (set == null) { - map.put(classe, set = new LinkedHashSet>()); - } - - return set; - } - - private static void map (Class sourceClass, - Class relationClass, - Class targetClass) - { - getSet(src, sourceClass).add(relationClass); - getSet(tgt, targetClass).add(relationClass); - } -} +//------------------------------------------------------------------------------------------------// +// // +// R e l a t i o n s // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.relation; + +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.AbstractBeamInter; +import org.audiveris.omr.sig.inter.AbstractChordInter; +import org.audiveris.omr.sig.inter.AbstractNoteInter; +import org.audiveris.omr.sig.inter.AlterInter; +import org.audiveris.omr.sig.inter.ArpeggiatoInter; +import org.audiveris.omr.sig.inter.ArticulationInter; +import org.audiveris.omr.sig.inter.AugmentationDotInter; +import org.audiveris.omr.sig.inter.BarlineInter; +import org.audiveris.omr.sig.inter.ChordNameInter; +import org.audiveris.omr.sig.inter.DynamicsInter; +import org.audiveris.omr.sig.inter.EndingInter; +import org.audiveris.omr.sig.inter.FermataArcInter; +import org.audiveris.omr.sig.inter.FermataDotInter; +import org.audiveris.omr.sig.inter.FermataInter; +import org.audiveris.omr.sig.inter.FlagInter; +import org.audiveris.omr.sig.inter.HeadChordInter; +import org.audiveris.omr.sig.inter.HeadInter; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.inter.LyricItemInter; +import org.audiveris.omr.sig.inter.MarkerInter; +import org.audiveris.omr.sig.inter.OrnamentInter; +import org.audiveris.omr.sig.inter.PedalInter; +import org.audiveris.omr.sig.inter.RepeatDotInter; +import org.audiveris.omr.sig.inter.SentenceInter; +import org.audiveris.omr.sig.inter.SlurInter; +import org.audiveris.omr.sig.inter.SmallFlagInter; +import org.audiveris.omr.sig.inter.StaffBarlineInter; +import org.audiveris.omr.sig.inter.StemInter; +import org.audiveris.omr.sig.inter.TimeNumberInter; +import org.audiveris.omr.sig.inter.TupletInter; +import org.audiveris.omr.sig.inter.WedgeInter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Class {@code Relations} gathers utilities for Relation classes and instances. + * + * @author Hervé Bitteur + */ +public abstract class Relations +{ + + private static final Logger logger = LoggerFactory.getLogger(Relations.class); + + private static final Map, Set>> src + = new LinkedHashMap<>(); + + private static final Map, Set>> tgt + = new LinkedHashMap<>(); + + static { + buildMaps(); + } + + /** Not meant to be instantiated. */ + private Relations () + { + } + + /** + * Report the defined relation classes between the provided source and target + * inter classes. + * + * @param sourceClass provided inter class as source + * @param targetClass provided inter class as target + * @return the list of defined relation classes, perhaps empty + */ + public static Set> definedRelationsBetween ( + Class sourceClass, + Class targetClass) + { + final Set> defined = new LinkedHashSet<>(); + Set> from = definedRelationsFrom(sourceClass); + Set> to = definedRelationsTo(targetClass); + defined.addAll(from); + defined.retainAll(to); + + if (defined.isEmpty()) { + return Collections.emptySet(); + } else { + return defined; + } + } + + /** + * Report the defined relation classes from the provided source inter class. + * + * @param sourceClass provided inter class as source + * @return the list of defined relation classes, perhaps empty + */ + public static Set> definedRelationsFrom ( + Class sourceClass) + { + Objects.requireNonNull(sourceClass, "Source class is null"); + + final Set> defined = new LinkedHashSet<>(); + Class classe = sourceClass; + + while (true) { + if ((classe == null) || !Inter.class.isAssignableFrom(classe)) { + break; + } + + Set> set = src.get(classe); + + if (set != null) { + defined.addAll(set); + } + + classe = classe.getSuperclass(); + } + + if (!defined.isEmpty()) { + return Collections.unmodifiableSet(defined); + } else { + return Collections.emptySet(); + } + } + + /** + * Report the defined relation classes to the provided target inter class. + * + * @param targetClass provided inter class as target + * @return the list of defined relation classes, perhaps empty + */ + public static Set> definedRelationsTo ( + Class targetClass) + { + Objects.requireNonNull(targetClass, "Target class is null"); + + final Set> defined = new LinkedHashSet<>(); + Class classe = targetClass; + + while (true) { + if ((classe == null) || !Inter.class.isAssignableFrom(classe)) { + break; + } + + Set> set = tgt.get(classe); + + if (set != null) { + defined.addAll(set); + } + + classe = classe.getSuperclass(); + } + + if (!defined.isEmpty()) { + return Collections.unmodifiableSet(defined); + } else { + return Collections.emptySet(); + } + } + + /** + * Report a simple name for the provided relation class. + * + * @param relationClass provided relation class + * @return simple name + */ + public static String nameOf (Class relationClass) + { + return relationClass.getSimpleName().replaceFirst("Relation", ""); + } + + /** + * Report the suggested relation classes between the provided source and target + * inters. + * + * @param source provided inter as source + * @param target provided inter as target + * @return the possible relation classes + */ + public static Set> suggestedRelationsBetween (Inter source, + Inter target) + { + // Check inputs + Objects.requireNonNull(source, "Source is null"); + Objects.requireNonNull(target, "Target is null"); + + SIGraph sig = source.getSig(); + Objects.requireNonNull(sig, "Source has no sig"); + + if (target.getSig() != sig) { + logger.info("Source and Target do not share the same sig"); + + return Collections.emptySet(); + } + + // Suggestions + Set> suggestions = new LinkedHashSet<>( + definedRelationsBetween(source.getClass(), target.getClass())); + + // Let's not remove existing relation, to allow cleaning of relations + // // NO (Skip existing relation, if any, between source & target) NO + // Relation edge = sig.getEdge(source, target); + // + // if (edge != null) { + // suggestions.remove(edge.getClass()); + // } + // + // Return what we got + if (suggestions.isEmpty()) { + return Collections.emptySet(); + } else { + return suggestions; + } + } + + /** + * Build the maps of possible support classes for a source inter class and for a + * target inter class. + *

                                                                                                                                    + * A few relations are used only for support during reduction, rather than symbolic relation. + * They are thus excluded for lack of usefulness at UI level: + *

                                                                                                                                      + *
                                                                                                                                    • BarConnectionRelation + *
                                                                                                                                    • BeamHeadRelation + *
                                                                                                                                    • ClefKeyRelation + *
                                                                                                                                    • HeadHeadRelation + *
                                                                                                                                    • KeyAltersRelation + *
                                                                                                                                    • NoExclusion + *
                                                                                                                                    • StemAlignmentRelation + *
                                                                                                                                    + */ + private static void buildMaps () + { + map(AbstractBeamInter.class, BeamStemRelation.class, StemInter.class); + + map(AbstractChordInter.class, ChordDynamicsRelation.class, DynamicsInter.class); + map(AbstractChordInter.class, ChordPedalRelation.class, PedalInter.class); + map(AbstractChordInter.class, ChordTupletRelation.class, TupletInter.class); + map(AbstractChordInter.class, ChordWedgeRelation.class, WedgeInter.class); + + map(AlterInter.class, AlterHeadRelation.class, HeadInter.class); + + map(AugmentationDotInter.class, AugmentationRelation.class, AbstractNoteInter.class); + map(AugmentationDotInter.class, DoubleDotRelation.class, AugmentationDotInter.class); + + map(EndingInter.class, EndingBarRelation.class, BarlineInter.class); // Old + map(EndingInter.class, EndingBarRelation.class, StaffBarlineInter.class); + map(EndingInter.class, EndingSentenceRelation.class, SentenceInter.class); + + map(FermataDotInter.class, DotFermataRelation.class, FermataArcInter.class); // Temporary! + + map(FermataInter.class, FermataBarRelation.class, BarlineInter.class); // Old + map(FermataInter.class, FermataBarRelation.class, StaffBarlineInter.class); + map(FermataInter.class, FermataChordRelation.class, AbstractChordInter.class); + + map(FlagInter.class, FlagStemRelation.class, StemInter.class); + map(SmallFlagInter.class, FlagStemRelation.class, StemInter.class); + + map(HeadChordInter.class, ChordArpeggiatoRelation.class, ArpeggiatoInter.class); + map(HeadChordInter.class, ChordArticulationRelation.class, ArticulationInter.class); + map(HeadChordInter.class, ChordNameRelation.class, ChordNameInter.class); + map(HeadChordInter.class, ChordOrnamentRelation.class, OrnamentInter.class); + map(HeadChordInter.class, ChordSentenceRelation.class, SentenceInter.class); + map(HeadChordInter.class, ChordStemRelation.class, StemInter.class); + map(HeadChordInter.class, ChordSyllableRelation.class, LyricItemInter.class); + + map(HeadInter.class, HeadStemRelation.class, StemInter.class); + + map(MarkerInter.class, MarkerBarRelation.class, BarlineInter.class); // Old + map(MarkerInter.class, MarkerBarRelation.class, StaffBarlineInter.class); + + map(RepeatDotInter.class, RepeatDotBarRelation.class, BarlineInter.class); + map(RepeatDotInter.class, RepeatDotPairRelation.class, RepeatDotInter.class); + + map(SlurInter.class, SlurHeadRelation.class, HeadInter.class); + + map(TimeNumberInter.class, TimeTopBottomRelation.class, TimeNumberInter.class); + } + + private static Set> getSet ( + Map, Set>> map, + Class classe) + { + Set> set = map.get(classe); + + if (set == null) { + map.put(classe, set = new LinkedHashSet<>()); + } + + return set; + } + + private static void map (Class sourceClass, + Class relationClass, + Class targetClass) + { + getSet(src, sourceClass).add(relationClass); + getSet(tgt, targetClass).add(relationClass); + } +} diff --git a/src/main/org/audiveris/omr/sig/relation/RepeatDotBarRelation.java b/src/main/org/audiveris/omr/sig/relation/RepeatDotBarRelation.java index 78a7d95c8..23f88998e 100644 --- a/src/main/org/audiveris/omr/sig/relation/RepeatDotBarRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/RepeatDotBarRelation.java @@ -40,7 +40,6 @@ public class RepeatDotBarRelation extends AbstractConnection { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -48,10 +47,15 @@ public class RepeatDotBarRelation private static final double[] OUT_WEIGHTS = new double[]{ constants.xOutWeight.getValue(), - constants.yWeight.getValue() - }; + constants.yWeight.getValue()}; + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // getXOutGapMaximum // //-------------------// @@ -125,14 +129,12 @@ protected Scale.Fraction getYGapMax (boolean manual) return getYGapMaximum(manual); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio repeatDotSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/RepeatDotPairRelation.java b/src/main/org/audiveris/omr/sig/relation/RepeatDotPairRelation.java index 7e737e19e..814660b00 100644 --- a/src/main/org/audiveris/omr/sig/relation/RepeatDotPairRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/RepeatDotPairRelation.java @@ -34,13 +34,11 @@ */ @XmlRootElement(name = "repeat-dot-pair") public class RepeatDotPairRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -77,14 +75,19 @@ protected double getTargetCoeff () return constants.dotSupportCoeff.getValue(); } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio dotSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/SlurHeadRelation.java b/src/main/org/audiveris/omr/sig/relation/SlurHeadRelation.java index c05e8579f..e383b6560 100644 --- a/src/main/org/audiveris/omr/sig/relation/SlurHeadRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/SlurHeadRelation.java @@ -50,17 +50,13 @@ */ @XmlRootElement(name = "slur-head") public class SlurHeadRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - SlurHeadRelation.class); + private static final Logger logger = LoggerFactory.getLogger(SlurHeadRelation.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -74,7 +70,6 @@ public class SlurHeadRelation /** Euclidean distance from slur end to chord middle vertical. */ private double euclidean; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SlurNoteRelation} object. * @@ -92,7 +87,6 @@ public SlurHeadRelation () { } - //~ Methods ------------------------------------------------------------------------------------ //-------// // added // //-------// @@ -129,6 +123,8 @@ public void added (GraphEdgeChangeEvent e) // getEuclidean // //--------------// /** + * Report the euclidean distance between head and slur end. + * * @return the euclidean distance */ public double getEuclidean () @@ -136,6 +132,19 @@ public double getEuclidean () return euclidean; } + //--------------// + // setEuclidean // + //--------------// + /** + * Set the euclidean distance between head and slur end. + * + * @param euclidean the euclidean distance to set + */ + public void setEuclidean (double euclidean) + { + this.euclidean = euclidean; + } + //---------// // getSide // //---------// @@ -172,18 +181,10 @@ public boolean isSingleTarget () public void removed (GraphEdgeChangeEvent e) { final SlurInter slur = (SlurInter) e.getEdgeSource(); - slur.checkAbnormal(); - } - //--------------// - // setEuclidean // - //--------------// - /** - * @param euclidean the euclidean distance to set - */ - public void setEuclidean (double euclidean) - { - this.euclidean = euclidean; + if (!slur.isRemoved()) { + slur.checkAbnormal(); + } } //----------// @@ -204,14 +205,12 @@ protected double getSourceCoeff () return constants.slurSupportCoeff.getValue(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio slurSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/StemAlignmentRelation.java b/src/main/org/audiveris/omr/sig/relation/StemAlignmentRelation.java index e9f6e0566..5cd511815 100644 --- a/src/main/org/audiveris/omr/sig/relation/StemAlignmentRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/StemAlignmentRelation.java @@ -32,9 +32,8 @@ */ @XmlRootElement(name = "stem-alignment") public class StemAlignmentRelation - extends AbstractSupport + extends Support { - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // @@ -53,4 +52,11 @@ public boolean isSingleTarget () { return false; } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/sig/relation/Support.java b/src/main/org/audiveris/omr/sig/relation/Support.java index 162fcba35..325e01f5f 100644 --- a/src/main/org/audiveris/omr/sig/relation/Support.java +++ b/src/main/org/audiveris/omr/sig/relation/Support.java @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------------------------// // // -// S u p p o r t // +// S u p p o r t // // // //------------------------------------------------------------------------------------------------// // @@ -21,46 +21,190 @@ // package org.audiveris.omr.sig.relation; +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; import org.audiveris.omr.sig.GradeImpacts; +import org.audiveris.omr.util.Jaxb; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** - * Interface {@code Support} is a relation between interpretation instances that support - * one another. + * Abstract class {@code Support} is a relation between interpretation instances that + * support one another. *

                                                                                                                                    * Typical example is a mutual support between a stem and a note head, or between a stem and a beam. * * @author Hervé Bitteur */ -public interface Support +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = "support") +public abstract class Support extends Relation { - //~ Methods ------------------------------------------------------------------------------------ + + private static final Constants constants = new Constants(); + + /** Quality of the geometric junction. */ + @XmlAttribute + @XmlJavaTypeAdapter(type = double.class, value = Jaxb.Double3Adapter.class) + protected double grade; + + /** Details about grade (for debugging). */ + protected GradeImpacts impacts; /** - * Report details about the final relation grade + * Creates a new BasicSupport object, with a grade set to 1. + */ + public Support () + { + this(1.0); + } + + /** + * Creates a new BasicSupport object. * - * @return the grade details + * @param grade the grade evaluated for this relation */ - GradeImpacts getImpacts (); + public Support (double grade) + { + this.grade = grade; + } + //----------------// + // getSourceRatio // + //----------------// /** * Report the support ratio for source inter * * @return support ratio for source (value is always ≥ 1) */ - double getSourceRatio (); + public final double getSourceRatio () + { + return 1.0 + (getSourceCoeff() * grade); + } + //----------------// + // getTargetRatio // + //----------------// /** * Report the support ratio for target inter * * @return support ratio for target (value is always ≥ 1) */ - double getTargetRatio (); + public final double getTargetRatio () + { + return 1.0 + (getTargetCoeff() * grade); + } + + //----------// + // getGrade // + //----------// + /** + * Report the support grade. + * + * @return the grade + */ + public double getGrade () + { + return grade; + } + + //----------// + // setGrade // + //----------// + /** + * Set the support grade value. + * + * @param grade the grade to set + */ + public void setGrade (double grade) + { + this.grade = grade; + } + + //------------// + // getImpacts // + //------------// + /** + * Report details about the final relation grade + * + * @return the grade details + */ + public GradeImpacts getImpacts () + { + return impacts; + } + //------------// + // setImpacts // + //------------// /** * Assign details about the relation grade * * @param impacts the grade impacts */ - void setImpacts (GradeImpacts impacts); + public void setImpacts (GradeImpacts impacts) + { + this.impacts = impacts; + } + + //-------------// + // getMinGrade // + //-------------// + /** + * Report the minimum grade for this relation. + * + * @return minimum grade + */ + public double getMinGrade () + { + return constants.minGrade.getValue(); + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + return String.format("%.2f~%s", grade, super.toString()); + } + + //----------------// + // getSourceCoeff // + //----------------// + /** + * @return the coefficient used to compute source support ratio (default: 0) + */ + protected double getSourceCoeff () + { + return 0; + } + + //----------------// + // getTargetCoeff // + //----------------// + /** + * @return the coefficient used to compute target support ratio (default: 0) + */ + protected double getTargetCoeff () + { + return 0; + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Ratio minGrade = new Constant.Ratio( + 0.1, + "Minimum support relation grade"); + } } diff --git a/src/main/org/audiveris/omr/sig/relation/SupportImpacts.java b/src/main/org/audiveris/omr/sig/relation/SupportImpacts.java index 922a4d21d..23791bedf 100644 --- a/src/main/org/audiveris/omr/sig/relation/SupportImpacts.java +++ b/src/main/org/audiveris/omr/sig/relation/SupportImpacts.java @@ -21,7 +21,7 @@ // package org.audiveris.omr.sig.relation; -import org.audiveris.omr.sig.BasicImpacts; +import org.audiveris.omr.sig.GradeImpacts; /** * Class {@code SupportImpacts} handles impacts for a supporting relation. @@ -29,9 +29,8 @@ * @author Hervé Bitteur */ public class SupportImpacts - extends BasicImpacts + extends GradeImpacts { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new RelationImpacts object. @@ -45,7 +44,6 @@ public SupportImpacts (String[] names, super(names, weights); } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // getIntrinsicRatio // //-------------------// diff --git a/src/main/org/audiveris/omr/sig/relation/TimeTopBottomRelation.java b/src/main/org/audiveris/omr/sig/relation/TimeTopBottomRelation.java index b82396403..26303a5f6 100644 --- a/src/main/org/audiveris/omr/sig/relation/TimeTopBottomRelation.java +++ b/src/main/org/audiveris/omr/sig/relation/TimeTopBottomRelation.java @@ -34,13 +34,11 @@ */ @XmlRootElement(name = "time-top-bottom") public class TimeTopBottomRelation - extends AbstractSupport + extends Support { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TimeTopBottomRelation} object. */ @@ -48,7 +46,6 @@ public TimeTopBottomRelation () { } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // isSingleSource // //----------------// @@ -85,14 +82,19 @@ protected double getTargetCoeff () return constants.numberSupportCoeff.getValue(); } - //~ Inner Classes ------------------------------------------------------------------------------ + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio numberSupportCoeff = new Constant.Ratio( 5, diff --git a/src/main/org/audiveris/omr/sig/relation/package-info.java b/src/main/org/audiveris/omr/sig/relation/package-info.java index 5022cbfc9..3d1e02c97 100644 --- a/src/main/org/audiveris/omr/sig/relation/package-info.java +++ b/src/main/org/audiveris/omr/sig/relation/package-info.java @@ -1,20 +1,4 @@ /** * Package for all relations between interpretations used by SIG. */ -@XmlJavaTypeAdapters({ - @XmlJavaTypeAdapter(value = Jaxb.PathAdapter.class, type = Path.class), - @XmlJavaTypeAdapter(value = Jaxb.PointAdapter.class, type = Point.class), - @XmlJavaTypeAdapter(value = Jaxb.Point2DAdapter.class, type = Point2D.class), - @XmlJavaTypeAdapter(value = Jaxb.RectangleAdapter.class, type = Rectangle.class) -}) package org.audiveris.omr.sig.relation; - -import org.audiveris.omr.util.Jaxb; - -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.Point2D; -import java.nio.file.Path; - -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; diff --git a/src/main/org/audiveris/omr/sig/ui/AdditionTask.java b/src/main/org/audiveris/omr/sig/ui/AdditionTask.java index 49e5e4542..738072b19 100644 --- a/src/main/org/audiveris/omr/sig/ui/AdditionTask.java +++ b/src/main/org/audiveris/omr/sig/ui/AdditionTask.java @@ -1,80 +1,78 @@ -//------------------------------------------------------------------------------------------------// -// // -// A d d i t i o n T a s k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.relation.Link; - -import java.awt.Rectangle; -import java.util.Collection; - -/** - * Class {@code AdditionTask} adds an Inter instance, together with its relations. - * - * @author Hervé Bitteur - */ -public class AdditionTask - extends InterTask -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Add an inter instance with its provided relations. - * - * @param sig the underlying sig - * @param inter the inter to add - * @param initialBounds the initial bounds for this inter - * @param links the provided relations around inter - */ - public AdditionTask (SIGraph sig, - Inter inter, - Rectangle initialBounds, - Collection links) - { - super(sig, inter, initialBounds, links); - } - - //~ Methods ------------------------------------------------------------------------------------ - @Override - public void performDo () - { - inter.setBounds(initialBounds); - sig.addVertex(inter); - - for (Link link : links) { - link.applyTo(inter); - } - } - - @Override - public void performUndo () - { - inter.remove(); - } - - @Override - protected String actionName () - { - return "add"; - } -} +//------------------------------------------------------------------------------------------------// +// // +// A d d i t i o n T a s k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.relation.Link; + +import java.awt.Rectangle; +import java.util.Collection; + +/** + * Class {@code AdditionTask} adds an Inter instance, together with its relations. + * + * @author Hervé Bitteur + */ +public class AdditionTask + extends InterTask +{ + + /** + * Add an inter instance with its provided relations. + * + * @param sig the underlying sig + * @param inter the inter to add + * @param initialBounds the initial bounds for this inter + * @param links the provided relations around inter + */ + public AdditionTask (SIGraph sig, + Inter inter, + Rectangle initialBounds, + Collection links) + { + super(sig, inter, initialBounds, links); + } + + @Override + public void performDo () + { + inter.setBounds(initialBounds); + sig.addVertex(inter); + + for (Link link : links) { + link.applyTo(inter); + } + } + + @Override + public void performUndo () + { + inter.remove(); + } + + @Override + protected String actionName () + { + return "add"; + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/DndOperation.java b/src/main/org/audiveris/omr/sig/ui/DndOperation.java index 86e60a0e0..860c1aac8 100644 --- a/src/main/org/audiveris/omr/sig/ui/DndOperation.java +++ b/src/main/org/audiveris/omr/sig/ui/DndOperation.java @@ -1,273 +1,257 @@ -//------------------------------------------------------------------------------------------------// -// // -// D n d O p e r a t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.OMR; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.glyph.ShapeSet; -import org.audiveris.omr.math.PointUtil; -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sheet.grid.LineInfo; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.ui.OmrGlassPane; -import org.audiveris.omr.ui.symbol.MusicFont; -import org.audiveris.omr.ui.symbol.ShapeSymbol; -import org.audiveris.omr.ui.symbol.Symbols; -import org.audiveris.omr.ui.view.Zoom; -import org.audiveris.omr.util.HorizontalSide; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Dimension; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; -import java.util.Arrays; - -/** - * Class {@code DndOperation} handles one DnD operation with a moving inter. - * - * @author Hervé Bitteur - */ -public class DndOperation -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger(DndOperation.class); - - //~ Instance fields ---------------------------------------------------------------------------- - /** Related sheet. */ - private final Sheet sheet; - - /** GlassPane. */ - private final OmrGlassPane glass = OMR.gui.getGlassPane(); - - private final Zoom zoom; - - /** Pay-load: the Inter instance being "moved". */ - private final Inter ghost; - - /** Staff currently related. */ - private Staff staff; - - /** System currently related. */ - private SystemInfo system; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code DndOperation} object. - * - * @param sheet the related sheet - * @param zoom zoom applied on display - * @param ghost the inter being "moved" - */ - public DndOperation (Sheet sheet, - Zoom zoom, - Inter ghost) - { - this.sheet = sheet; - this.zoom = zoom; - this.ghost = ghost; - } - - //~ Methods ------------------------------------------------------------------------------------ - //------// - // drop // - //------// - /** - * Drop the ghost inter at provided location. - *

                                                                                                                                    - * Finalize ghost info (staff and bounds), insert into proper SIG - * and link to partners if any. - * - * @param center provided location - */ - public void drop (Point center) - { - if (staff == null) { - logger.warn("No staff selected for drop"); - - return; - } - - // Staff - ghost.setStaff(staff); - - // Bounds - final int staffInterline = staff.getSpecificInterline(); - final MusicFont font = (ShapeSet.Heads.contains(ghost.getShape())) - ? MusicFont.getHeadFont(sheet.getScale(), staffInterline) - : MusicFont.getBaseFont(staffInterline); - final ShapeSymbol symbol = Symbols.getSymbol(ghost.getShape()); - final Dimension dim = symbol.getDimension(font); - final Rectangle bounds = new Rectangle( - center.x - (dim.width / 2), - center.y - (dim.height / 2), - dim.width, - dim.height); - ghost.setBounds(bounds); - - sheet.getInterController().addInters(Arrays.asList(ghost)); - - logger.debug("Dropped {} at {}", this, center); - } - - //----------------// - // enteringTarget // - //----------------// - /** - * Call-back when mouse is entering the target component. - */ - public void enteringTarget () - { - updateImage(sheet.getScale().getInterline()); - } - - //----------// - // getGhost // - //----------// - public Inter getGhost () - { - return ghost; - } - - //--------------// - // getReference // - //--------------// - /** - * Report the reference point for the moving inter located on 'center' point. - *

                                                                                                                                    - * Staff above - * Staff above or below - * Staff below: coda, ... all markers? - * Note head on right: alteration - * HeadChord above or below: articulation - * etc - * - * @param center current inter center - * @return the location of reference entity - */ - public Point getReference (Point center) - { - // By default, use a "sticky staff" approach... - Staff closestStaff = sheet.getStaffManager().getClosestStaff(center); - - if (closestStaff == null) { - staff = null; - system = null; - } else { - double pp = closestStaff.pitchPositionOf(center); - - if (Math.abs(pp) <= 4) { - // We are within staff height, so let's pick up this staff - if (staff != closestStaff) { - if (system != closestStaff.getSystem()) { - system = closestStaff.getSystem(); - - // Retrieve system heads???????????????????????? - } - - // Adjust image size WRT new interline - if ((staff == null) - || (staff.getSpecificInterline() != closestStaff.getSpecificInterline())) { - updateImage(closestStaff.getSpecificInterline()); - } - - staff = closestStaff; - system = staff.getSystem(); - } - } - } - - if (staff == null) { - return null; - } - - LineInfo line = staff.getLines().get(2); - - if (center.x < line.getEndPoint(HorizontalSide.LEFT).getX()) { - return PointUtil.rounded(line.getEndPoint(HorizontalSide.LEFT)); - } - - if (center.x > line.getEndPoint(HorizontalSide.RIGHT).getX()) { - return PointUtil.rounded(line.getEndPoint(HorizontalSide.RIGHT)); - } - - return new Point(center.x, line.yAt(center.x)); - } - - //----------// - // toString // - //----------// - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("DnDOperation{"); - - if (staff != null) { - sb.append(" staff:").append(staff.getId()); - } - - if (ghost != null) { - sb.append(" ghost:").append(ghost); - } - - sb.append("}"); - - return sb.toString(); - } - - //-------------// - // updateImage // - //-------------// - /** - * Build ghost image based on provided interline value. - * - * @param interline provided interline - */ - private void updateImage (int interline) - { - // Adapt image to current zoom and interline - int zoomedInterline = (int) Math.rint(zoom.getRatio() * interline); - Shape shape = ghost.getShape(); - BufferedImage image = MusicFont.buildImage(shape, zoomedInterline, true); // Decorated - - if (image != null) { - glass.setImage(image); - } - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - } -} +//------------------------------------------------------------------------------------------------// +// // +// D n d O p e r a t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.OMR; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.glyph.ShapeSet; +import org.audiveris.omr.math.PointUtil; +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sheet.grid.LineInfo; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.ui.OmrGlassPane; +import org.audiveris.omr.ui.symbol.MusicFont; +import org.audiveris.omr.ui.symbol.ShapeSymbol; +import org.audiveris.omr.ui.symbol.Symbols; +import org.audiveris.omr.ui.view.Zoom; +import org.audiveris.omr.util.HorizontalSide; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.util.Arrays; + +/** + * Class {@code DndOperation} handles one DnD operation with a moving inter. + * + * @author Hervé Bitteur + */ +public class DndOperation +{ + + private static final Logger logger = LoggerFactory.getLogger(DndOperation.class); + + /** Related sheet. */ + private final Sheet sheet; + + /** GlassPane. */ + private final OmrGlassPane glass = OMR.gui.getGlassPane(); + + private final Zoom zoom; + + /** Pay-load: the Inter instance being "moved". */ + private final Inter ghost; + + /** Staff currently related. */ + private Staff staff; + + /** System currently related. */ + private SystemInfo system; + + /** + * Creates a new {@code DndOperation} object. + * + * @param sheet the related sheet + * @param zoom zoom applied on display + * @param ghost the inter being "moved" + */ + public DndOperation (Sheet sheet, + Zoom zoom, + Inter ghost) + { + this.sheet = sheet; + this.zoom = zoom; + this.ghost = ghost; + } + + //------// + // drop // + //------// + /** + * Drop the ghost inter at provided location. + *

                                                                                                                                    + * Finalize ghost info (staff and bounds), insert into proper SIG + * and link to partners if any. + * + * @param center provided location + */ + public void drop (Point center) + { + if (staff == null) { + logger.warn("No staff selected for drop"); + + return; + } + + // Staff + ghost.setStaff(staff); + + // Bounds + final int staffInterline = staff.getSpecificInterline(); + final MusicFont font = (ShapeSet.Heads.contains(ghost.getShape())) ? MusicFont.getHeadFont( + sheet.getScale(), + staffInterline) : MusicFont.getBaseFont(staffInterline); + final ShapeSymbol symbol = Symbols.getSymbol(ghost.getShape()); + final Dimension dim = symbol.getDimension(font); + final Rectangle bounds = new Rectangle( + center.x - (dim.width / 2), + center.y - (dim.height / 2), + dim.width, + dim.height); + ghost.setBounds(bounds); + + sheet.getInterController().addInters(Arrays.asList(ghost)); + + logger.debug("Dropped {} at {}", this, center); + } + + //----------------// + // enteringTarget // + //----------------// + /** + * Call-back when mouse is entering the target component. + */ + public void enteringTarget () + { + updateImage(sheet.getScale().getInterline()); + } + + //----------// + // getGhost // + //----------// + public Inter getGhost () + { + return ghost; + } + + //--------------// + // getReference // + //--------------// + /** + * Report the reference point for the moving inter located on 'center' point. + *

                                                                                                                                    + * Staff above + * Staff above or below + * Staff below: coda, ... all markers? + * Note head on right: alteration + * HeadChord above or below: articulation + * etc + * + * @param center current inter center + * @return the location of reference entity + */ + public Point getReference (Point center) + { + // By default, use a "sticky staff" approach... + Staff closestStaff = sheet.getStaffManager().getClosestStaff(center); + + if (closestStaff == null) { + staff = null; + system = null; + } else { + double pp = closestStaff.pitchPositionOf(center); + + if (Math.abs(pp) <= 4) { + // We are within staff height, so let's pick up this staff + if (staff != closestStaff) { + if (system != closestStaff.getSystem()) { + system = closestStaff.getSystem(); + + // Retrieve system heads???????????????????????? + } + + // Adjust image size WRT new interline + if ((staff == null) || (staff.getSpecificInterline() != closestStaff + .getSpecificInterline())) { + updateImage(closestStaff.getSpecificInterline()); + } + + staff = closestStaff; + system = staff.getSystem(); + } + } + } + + if (staff == null) { + return null; + } + + LineInfo line = staff.getLines().get(2); + + if (center.x < line.getEndPoint(HorizontalSide.LEFT).getX()) { + return PointUtil.rounded(line.getEndPoint(HorizontalSide.LEFT)); + } + + if (center.x > line.getEndPoint(HorizontalSide.RIGHT).getX()) { + return PointUtil.rounded(line.getEndPoint(HorizontalSide.RIGHT)); + } + + return new Point(center.x, line.yAt(center.x)); + } + + //----------// + // toString // + //----------// + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("DnDOperation{"); + + if (staff != null) { + sb.append(" staff:").append(staff.getId()); + } + + if (ghost != null) { + sb.append(" ghost:").append(ghost); + } + + sb.append("}"); + + return sb.toString(); + } + + //-------------// + // updateImage // + //-------------// + /** + * Build ghost image based on provided interline value. + * + * @param interline provided interline + */ + private void updateImage (int interline) + { + // Adapt image to current zoom and interline + int zoomedInterline = (int) Math.rint(zoom.getRatio() * interline); + Shape shape = ghost.getShape(); + BufferedImage image = MusicFont.buildImage(shape, zoomedInterline, true); // Decorated + + if (image != null) { + glass.setImage(image); + } + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/GlyphAction.java b/src/main/org/audiveris/omr/sig/ui/GlyphAction.java index 83ad1a0db..5f4b7cd44 100644 --- a/src/main/org/audiveris/omr/sig/ui/GlyphAction.java +++ b/src/main/org/audiveris/omr/sig/ui/GlyphAction.java @@ -42,15 +42,12 @@ public class GlyphAction extends AbstractAction { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GlyphAction.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The underlying glyph. */ private final Glyph glyph; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new GlyphAction object. * @@ -75,7 +72,6 @@ public GlyphAction (Glyph glyph, putValue(SHORT_DESCRIPTION, tipOf(glyph)); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // actionPerformed // //-----------------// @@ -96,7 +92,7 @@ public void publish () logger.warn("No index for {}", glyph); } else { glyphIndex.getEntityService().publish( - new EntityListEvent( + new EntityListEvent<>( this, SelectionHint.ENTITY_INIT, MouseMovement.PRESSING, diff --git a/src/main/org/audiveris/omr/sig/ui/GlyphListMenu.java b/src/main/org/audiveris/omr/sig/ui/GlyphListMenu.java index 7d13263ba..00ec3fb85 100644 --- a/src/main/org/audiveris/omr/sig/ui/GlyphListMenu.java +++ b/src/main/org/audiveris/omr/sig/ui/GlyphListMenu.java @@ -48,16 +48,13 @@ public class GlyphListMenu extends LocationDependentMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GlyphListMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- private final GlyphListener glyphListener = new GlyphListener(); private final Sheet sheet; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code GlyphMenu} object. * @@ -69,7 +66,6 @@ public GlyphListMenu (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ @Override public void updateUserLocation (Rectangle rect) { @@ -101,7 +97,6 @@ public void updateUserLocation (Rectangle rect) super.updateUserLocation(rect); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------------// // GlyphListener // //---------------// @@ -111,7 +106,6 @@ public void updateUserLocation (Rectangle rect) private class GlyphListener extends AbstractMouseListener { - //~ Methods -------------------------------------------------------------------------------- @Override public void mouseEntered (MouseEvent e) @@ -120,7 +114,7 @@ public void mouseEntered (MouseEvent e) Glyph glyph = shapeMenu.getGlyph(); sheet.getGlyphIndex().getEntityService().publish( - new EntityListEvent( + new EntityListEvent<>( this, SelectionHint.ENTITY_INIT, MouseMovement.PRESSING, diff --git a/src/main/org/audiveris/omr/sig/ui/InterAction.java b/src/main/org/audiveris/omr/sig/ui/InterAction.java index bbc9870e6..6d39e9165 100644 --- a/src/main/org/audiveris/omr/sig/ui/InterAction.java +++ b/src/main/org/audiveris/omr/sig/ui/InterAction.java @@ -41,15 +41,12 @@ public class InterAction extends AbstractAction { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(InterAction.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The underlying interpretation. */ private final Inter inter; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new InterAction object. * @@ -85,7 +82,6 @@ public InterAction (Inter inter, } } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // actionPerformed // //-----------------// diff --git a/src/main/org/audiveris/omr/sig/ui/InterBoard.java b/src/main/org/audiveris/omr/sig/ui/InterBoard.java index 4906a9cc8..0c8ab27a9 100644 --- a/src/main/org/audiveris/omr/sig/ui/InterBoard.java +++ b/src/main/org/audiveris/omr/sig/ui/InterBoard.java @@ -64,13 +64,11 @@ public class InterBoard extends EntityBoard { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(InterBoard.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ private final Sheet sheet; @@ -89,7 +87,6 @@ public class InterBoard /** To delete/deassign. */ private final DeassignAction deassignAction = new DeassignAction(); - // // /** Numerator of time signature */ // private final LIntegerField timeNum; // @@ -97,9 +94,9 @@ public class InterBoard // private final LIntegerField timeDen; // /** ComboBox for text role. */ - private final LComboBox roleCombo = new LComboBox( + private final LComboBox roleCombo = new LComboBox<>( "Role", - "Role of the Text", + "Role of the Sentence", TextRole.values()); /** Input/Output : textual content. */ @@ -111,7 +108,6 @@ public class InterBoard /** To avoid unwanted events. */ private boolean selfUpdatingText; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new InterBoard object, pre-selected by default. * @@ -154,7 +150,6 @@ public InterBoard (Sheet sheet, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //---------------------// // dumpActionPerformed // //---------------------// @@ -315,14 +310,12 @@ private void defineLayout () getComponent().getActionMap().put("TextAction", paramAction); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final PixelCount shapeIconHeight = new PixelCount( 70, @@ -339,7 +332,6 @@ private static final class Constants private class DeassignAction extends AbstractAction { - //~ Constructors --------------------------------------------------------------------------- public DeassignAction () { @@ -347,7 +339,6 @@ public DeassignAction () this.putValue(Action.SHORT_DESCRIPTION, "Deassign inter"); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -365,7 +356,6 @@ public void actionPerformed (ActionEvent e) private class ParamAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- /** * Method run whenever user presses Return/Enter in one of the parameter fields diff --git a/src/main/org/audiveris/omr/sig/ui/InterController.java b/src/main/org/audiveris/omr/sig/ui/InterController.java index 5f0608e42..779aaee5f 100644 --- a/src/main/org/audiveris/omr/sig/ui/InterController.java +++ b/src/main/org/audiveris/omr/sig/ui/InterController.java @@ -1,1468 +1,1557 @@ -//------------------------------------------------------------------------------------------------// -// // -// I n t e r C o n t r o l l e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import ij.process.ByteProcessor; - -import org.audiveris.omr.OMR; -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.glyph.Glyph; -import org.audiveris.omr.glyph.Shape; -import org.audiveris.omr.glyph.ui.NestView; -import org.audiveris.omr.glyph.ui.SymbolsEditor; -import org.audiveris.omr.math.GeoUtil; -import org.audiveris.omr.sheet.PartBarline; -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.sheet.Skew; -import org.audiveris.omr.sheet.Staff; -import org.audiveris.omr.sheet.SystemInfo; -import org.audiveris.omr.sheet.rhythm.MeasureStack; -import org.audiveris.omr.sheet.symbol.SymbolFactory; -import org.audiveris.omr.sheet.ui.BookActions; -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.BarlineInter; -import org.audiveris.omr.sig.inter.ChordNameInter; -import org.audiveris.omr.sig.inter.HeadChordInter; -import org.audiveris.omr.sig.inter.HeadInter; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.inter.InterEnsemble; -import org.audiveris.omr.sig.inter.Inters; -import org.audiveris.omr.sig.inter.LyricItemInter; -import org.audiveris.omr.sig.inter.LyricLineInter; -import org.audiveris.omr.sig.inter.RestChordInter; -import org.audiveris.omr.sig.inter.RestInter; -import org.audiveris.omr.sig.inter.SentenceInter; -import org.audiveris.omr.sig.inter.SlurInter; -import org.audiveris.omr.sig.inter.StaffBarlineInter; -import org.audiveris.omr.sig.inter.StemInter; -import org.audiveris.omr.sig.inter.WordInter; -import org.audiveris.omr.sig.relation.BasicContainment; -import org.audiveris.omr.sig.relation.ChordStemRelation; -import org.audiveris.omr.sig.relation.HeadStemRelation; -import org.audiveris.omr.sig.relation.Link; -import org.audiveris.omr.sig.relation.Relation; -import org.audiveris.omr.sig.relation.SlurHeadRelation; -import org.audiveris.omr.sig.ui.UITask.OpKind; -import static org.audiveris.omr.sig.ui.UITask.OpKind.*; -import org.audiveris.omr.sig.ui.UITaskList.Option; -import org.audiveris.omr.step.Step; -import org.audiveris.omr.text.BlockScanner; -import org.audiveris.omr.text.TextBuilder; -import org.audiveris.omr.text.TextLine; -import org.audiveris.omr.text.TextRole; -import org.audiveris.omr.text.TextWord; -import org.audiveris.omr.ui.selection.EntityListEvent; -import org.audiveris.omr.ui.selection.MouseMovement; -import org.audiveris.omr.ui.selection.SelectionHint; -import org.audiveris.omr.ui.util.UIThread; -import org.audiveris.omr.util.HorizontalSide; -import static org.audiveris.omr.util.HorizontalSide.*; -import org.audiveris.omr.util.VoidTask; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.geom.Area; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import javax.swing.AbstractAction; -import javax.swing.InputMap; -import javax.swing.JComponent; -import javax.swing.KeyStroke; - -/** - * Class {@code InterController} is the UI in charge of dealing with Inter and - * Relation instances (addition, removal, modifications) to correct OMR output, - * with the ability to undo and redo at will. - *

                                                                                                                                    - * It works at sheet level. - *

                                                                                                                                    - * When adding or dropping an inter, the instance is allocated in proper system (and staff if - * relevant) together with its relations with existing partners nearby. - * It is not always obvious to select the proper staff, various techniques are used, and if - * all have failed, the user is prompted for staff indication. - *

                                                                                                                                    - * Finally, a proper {@link UITaskList} is allocated, inserted in controller's history, and run. - * Undo and Redo actions operate on this history. - *

                                                                                                                                    - * User actions are processed asynchronously in background. - * - * @author Hervé Bitteur - */ -public class InterController -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger(InterController.class); - - //~ Instance fields ---------------------------------------------------------------------------- - /** Underlying sheet. */ - private final Sheet sheet; - - /** History of tasks. */ - private final TaskHistory history = new TaskHistory(); - - /** User pane. Lazily assigned */ - private SymbolsEditor editor; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code IntersController} object. - * - * @param sheet the underlying sheet - */ - public InterController (Sheet sheet) - { - this.sheet = sheet; - } - - //~ Methods ------------------------------------------------------------------------------------ - //-----------// - // addInters // - //-----------// - /** - * Add one or several inters. - * - * @param inters the populated inters (staff & bounds are already set) - * @param options additional options - */ - @UIThread - public void addInters (final List inters, - final Option... options) - { - if ((inters == null) || inters.isEmpty()) { - return; - } - - logger.debug("addInters {} {}", inters, options); - - if ((options == null) || !Arrays.asList(options).contains(Option.VALIDATED)) { - if (sheet.getStub().getLatestStep().compareTo(Step.MEASURES) >= 0) { - List staffBarlines = staffBarlinesOf(inters); - - if (!staffBarlines.isEmpty()) { - final StaffBarlineInter oneBar = (StaffBarlineInter) staffBarlines.get(0); - final List closure = buildStaffBarlineClosure(oneBar); - - if (!closure.isEmpty()) { - addInters(closure, Option.VALIDATED, Option.UPDATE_MEASURES); - } - - return; - } - } - } - - CtrlTask ctrlTask = new CtrlTask(DO) - { - @Override - protected Void doInBackground () - { - try { - List lgs = new ArrayList(); - - for (Inter inter : inters) { - SystemInfo system = inter.getStaff().getSystem(); - - if (inter instanceof BarlineInter) { - BarlineInter b = (BarlineInter) inter; - - if (b.getArea() == null) { - b.setArea(new Area(b.getBounds())); - } - } - - // If glyph used by another inter, delete this other inter - ///removeCompetitors(inter, inter.getGlyph(), system, seq); - // - lgs.add(new LinkedGhost(inter, inter.searchLinks(system, false))); - } - - addGhosts(seq, lgs); - epilog(seq); - } catch (Throwable ex) { - logger.warn("Exception in addInters {}", ex.toString(), ex); - } - - return null; - } - }; - - ctrlTask.seq.setOptions(options); - ctrlTask.execute(); - } - - //-------------// - // assignGlyph // - //-------------// - /** - * Add a shape interpretation based on a provided glyph. - * - * @param aGlyph the glyph to interpret - * @param shape the shape to be assigned - */ - @UIThread - public void assignGlyph (Glyph aGlyph, - final Shape shape) - { - logger.debug("addInter {} as {}", aGlyph, shape); - - if ((shape == Shape.TEXT) || (shape == Shape.LYRICS)) { - addText(aGlyph, shape); - - return; - } - - final Glyph glyph = sheet.getGlyphIndex().registerOriginal(aGlyph); - final Inter ghost = SymbolFactory.createManual(shape, sheet); - ghost.setBounds(glyph.getBounds()); - ghost.setGlyph(glyph); - - // While interacting with user, make sure we have the target staff - final Collection links = new ArrayList(); - final Staff staff = determineStaff(glyph, ghost, links); - - if (staff == null) { - logger.info("No staff, abandonned."); - - return; - } - - // For barlines, make sure length is only one-staff high - if (ghost instanceof BarlineInter || ghost instanceof StaffBarlineInter) { - Rectangle box = ghost.getBounds(); - int y1 = staff.getFirstLine().yAt(box.x); - int y2 = staff.getLastLine().yAt(box.x); - ghost.setBounds(new Rectangle(box.x, y1, box.width, y2 - y1 + 1)); - ghost.setGlyph(null); - } - - ghost.setStaff(staff); - addInters(Arrays.asList(ghost)); - } - - //---------// - // canRedo // - //---------// - /** - * Is a redo possible? - * - * @return true if so - */ - @UIThread - public boolean canRedo () - { - return history.canRedo(); - } - - //---------// - // canUndo // - //---------// - /** - * Is an undo possible? - * - * @return true if so - */ - @UIThread - public boolean canUndo () - { - return history.canUndo(); - } - - //----------------// - // changeSentence // - //----------------// - /** - * Change the role of a sentence. - * - * @param sentence the sentence to modify - * @param newRole the new role for the sentence - */ - @UIThread - public void changeSentence (final SentenceInter sentence, - final TextRole newRole) - { - logger.debug("changeSentence {} for {}", sentence, newRole); - - new CtrlTask(DO) - { - @Override - protected Void doInBackground () - { - try { - seq.add(new SentenceRoleTask(sentence, newRole)); - seq.performDo(); - sheet.getInterIndex().publish(sentence); - epilog(seq); - } catch (Throwable ex) { - logger.warn("Exception in changeSentence {}", ex.toString(), ex); - } - - return null; - } - }.execute(); - } - - //------------// - // changeWord // - //------------// - /** - * Change the text content of a word. - * - * @param word the word to modify - * @param newValue the new word content - */ - @UIThread - public void changeWord (final WordInter word, - final String newValue) - { - logger.debug("changeWord {} for {}", word, newValue); - - new CtrlTask(DO) - { - @Override - protected Void doInBackground () - { - try { - seq.add(new WordValueTask(word, newValue)); - seq.performDo(); - sheet.getInterIndex().publish(word); - epilog(seq); - } catch (Throwable ex) { - logger.warn("Exception in changeWord {}", ex.toString(), ex); - } - - return null; - } - }.execute(); - } - - //--------------// - // clearHistory // - //--------------// - /** - * Clear history of user actions. - */ - @UIThread - public void clearHistory () - { - history.clear(); - - if (editor != null) { - refreshUI(); - } - } - - //------// - // link // - //------// - /** - * Add a relation between inters. - * - * @param sig the containing SIG - * @param source the source inter - * @param target the target inter - * @param relation the relation to add - */ - @UIThread - public void link (final SIGraph sig, - final Inter source, - final Inter target, - final Relation relation) - { - new CtrlTask(DO) - { - @Override - protected Void doInBackground () - { - try { - if (relation instanceof HeadStemRelation) { - HeadInter head = (HeadInter) source; - StemInter stem = (StemInter) target; - HeadChordInter headChord = (HeadChordInter) head.getChord(); - - if (headChord != null) { - List stemChords = stem.getChords(); - HeadChordInter stemChord = (!stemChords.isEmpty()) - ? stemChords.get(0) : null; - - if ((stemChords.isEmpty() && (headChord.getStem() != null)) - || (!stemChords.isEmpty() && !stemChords.contains(headChord))) { - // Extract head from headChord - seq.add( - new UnlinkTask( - sig, - sig.getRelation( - headChord, - head, - BasicContainment.class))); - - if (headChord.getNotes().size() <= 1) { - // Remove headChord getting empty - seq.add(new RemovalTask(headChord)); - } - - if (stemChord == null) { - // Create a HeadChord on the fly based on stem - stemChord = new HeadChordInter(-1); - seq.add( - new AdditionTask( - sig, - stemChord, - stem.getBounds(), - Collections.EMPTY_SET)); - seq.add( - new LinkTask( - sig, - stemChord, - stem, - new ChordStemRelation())); - } - - // Insert head to stem chord - seq.add( - new LinkTask(sig, stemChord, head, new BasicContainment())); - } - } - } - - // Remove conflicting relations if any - Set toRemove = new LinkedHashSet(); - - if (relation instanceof SlurHeadRelation) { - // This relation is declared multi-source & multi-target - // But is single target (head) for each given side - SlurInter slur = (SlurInter) source; - HeadInter head = (HeadInter) target; - HorizontalSide side = (head.getCenter().x < slur.getCenter().x) - ? LEFT : RIGHT; - SlurHeadRelation existingRel = slur.getHeadRelation(side); - - if (existingRel != null) { - toRemove.add(existingRel); - } - } - - if (relation.isSingleSource()) { - for (Relation rel : sig.getRelations(target, relation.getClass())) { - toRemove.add(rel); - } - } - - if (relation.isSingleTarget()) { - for (Relation rel : sig.getRelations(source, relation.getClass())) { - toRemove.add(rel); - } - } - - for (Relation rel : toRemove) { - seq.add(new UnlinkTask(sig, rel)); - } - - // Finally, add relation - seq.add(new LinkTask(sig, source, target, relation)); - seq.performDo(); - epilog(seq); - } catch (Throwable ex) { - logger.warn("Exception in process {}", ex.toString(), ex); - } - - return null; - } - }.execute(); - } - - //------// - // redo // - //------// - /** - * Redo last user (undone) action sequence. - */ - @UIThread - public void redo () - { - new CtrlTask(REDO) - { - @Override - protected Void doInBackground () - { - try { - seq = history.toRedo(); - seq.performDo(); - - sheet.getInterIndex().publish(null); - - epilog(seq); - } catch (Throwable ex) { - logger.warn("Exception in redo {}", ex.toString(), ex); - } - - return null; - } - }.execute(); - } - - //--------------// - // removeInters // - //--------------// - /** - * Remove the provided collection of inter (with their relations) - * - * @param inters the inters to remove - * @param options added options if any - */ - @UIThread - public void removeInters (final List inters, - final Option... options) - { - if ((options == null) || !Arrays.asList(options).contains(Option.VALIDATED)) { - if (sheet.getStub().getLatestStep().compareTo(Step.MEASURES) >= 0) { - // Now that measures exist, it's whole system height or nothing - List staffBarlines = new ArrayList(staffBarlinesOf(inters)); - - if (staffBarlines.isEmpty()) { - for (Inter inter : barlinesOf(inters)) { - StaffBarlineInter sb = ((BarlineInter) inter).getStaffBarline(); - - if ((sb != null) && !staffBarlines.contains(sb)) { - staffBarlines.add(sb); - } - } - } - - if (!staffBarlines.isEmpty()) { - final StaffBarlineInter oneBar = (StaffBarlineInter) staffBarlines.get(0); - final List closure = getStaffBarlineClosure(oneBar); - - if (!closure.isEmpty()) { - // Remove full system height - for (Inter inter : closure) { - inter.getBounds(); - } - - removeInters(closure, Option.VALIDATED, Option.UPDATE_MEASURES); - } - - return; - } - } - } - - CtrlTask ctrlTask = new CtrlTask(DO) - { - @Override - protected Void doInBackground () - { - try { - populateRemovals(inters, seq); - - seq.performDo(); - sheet.getInterIndex().publish(null); - epilog(seq); - } catch (Throwable ex) { - logger.warn("Exception in removeInters {}", ex.toString(), ex); - } - - return null; - } - }; - - ctrlTask.seq.setOptions(options); - ctrlTask.execute(); - } - - //-----------------// - // reprocessRhythm // - //-----------------// - /** - * Reprocess the rhythm on the provided measure stack. - * - * @param stack measure stack to reprocess - */ - @UIThread - public void reprocessRhythm (final MeasureStack stack) - { - new CtrlTask(DO) - { - @Override - protected Void doInBackground () - { - try { - sheet.getStub().setModified(true); - - seq = null; // So that history is not modified - - // Re-process impacted steps - final UITaskList tempSeq = new UITaskList(new StackTask(stack)); - final Step latestStep = sheet.getStub().getLatestStep(); - final Step firstStep = Step.RHYTHMS; - - final EnumSet steps = EnumSet.range(firstStep, latestStep); - - for (Step step : steps) { - logger.debug("Impact {}", step); - step.impact(tempSeq, OpKind.DO); - } - } catch (Throwable ex) { - logger.warn("Exception in reprocessRhythm {}", ex.toString(), ex); - } - - return null; - } - }.execute(); - } - - //------------------// - // setSymbolsEditor // - //------------------// - /** - * Late assignment of editor, to avoid circularities in elaboration, and to allow - * handling of specific keys. - * - * @param symbolsEditor the user pane - */ - public void setSymbolsEditor (SymbolsEditor symbolsEditor) - { - editor = symbolsEditor; - - NestView view = editor.getView(); - InputMap inputMap = view.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - - // Support for delete key - inputMap.put(KeyStroke.getKeyStroke("DELETE"), "RemoveAction"); - view.getActionMap().put("RemoveAction", new RemoveAction()); - } - - //------// - // undo // - //------// - /** - * Undo last user action. - */ - @UIThread - public void undo () - { - new CtrlTask(UNDO) - { - @Override - protected Void doInBackground () - { - try { - UITaskList seq = history.toUndo(); - seq.performUndo(); - - sheet.getInterIndex().publish(null); - - epilog(seq); - } catch (Throwable ex) { - logger.warn("Exception in undo {}", ex.toString(), ex); - } - - return null; - } - }.execute(); - } - - //--------// - // unlink // - //--------// - /** - * Remove a relation between inters. - * - * @param sig the containing SIG - * @param relation the relation to remove - */ - @UIThread - public void unlink (final SIGraph sig, - final Relation relation) - { - new CtrlTask(DO) - { - @Override - protected Void doInBackground () - { - try { - logger.debug("unlink on {}", relation); - - seq.add(new UnlinkTask(sig, relation)); - - Inter source = sig.getEdgeSource(relation); - - seq.performDo(); - - sheet.getInterIndex().publish(source); - - epilog(seq); - } catch (Throwable ex) { - logger.warn("Exception in unlink {}", ex.toString(), ex); - } - - return null; - } - }.execute(); - } - - //-----------// - // addGhosts // - //-----------// - /** - * Perform ghosts addition. - * It completes {@link #addInters}. - * - * @param seq (output) the UITaskList to populate - * @param linkedGhosts the ghost inters to add/drop with their links - */ - private void addGhosts (UITaskList seq, - List linkedGhosts) - { - for (LinkedGhost linkedGhost : linkedGhosts) { - final Inter ghost = linkedGhost.ghost; - final Collection links = linkedGhost.links; - final Rectangle ghostBounds = ghost.getBounds(); - final Staff staff = ghost.getStaff(); - final SystemInfo system = staff.getSystem(); - final SIGraph sig = system.getSig(); - - // Inter addition - seq.add(new AdditionTask(sig, ghost, ghostBounds, links)); - - // Related additions if any - if (ghost instanceof RestInter) { - // Wrap this rest within a rest chord - RestChordInter restChord = new RestChordInter(-1); - restChord.setStaff(staff); - seq.add( - new AdditionTask( - sig, - restChord, - ghostBounds, - Arrays.asList(new Link(ghost, new BasicContainment(), true)))); - } else if (ghost instanceof HeadInter) { - // If we link head to a stem, create/update the related head chord - boolean stemFound = false; - - for (Link link : links) { - if (link.relation instanceof HeadStemRelation) { - final StemInter stem = (StemInter) link.partner; - final HeadChordInter headChord; - final List stemChords = stem.getChords(); - - if (stemChords.isEmpty()) { - // Create a chord based on stem - headChord = new HeadChordInter(-1); - seq.add( - new AdditionTask( - sig, - headChord, - stem.getBounds(), - Collections.EMPTY_SET)); - seq.add(new LinkTask(sig, headChord, stem, new ChordStemRelation())); - } else { - if (stemChords.size() > 1) { - logger.warn("Stem shared by several chords, picked one"); - } - - headChord = stemChords.get(0); - } - - // Declare head part of head-chord - seq.add(new LinkTask(sig, headChord, ghost, new BasicContainment())); - stemFound = true; - - break; - } - } - - if (!stemFound) { - // Head without stem - HeadChordInter headChord = new HeadChordInter(-1); - seq.add(new AdditionTask(sig, headChord, ghostBounds, Collections.EMPTY_SET)); - seq.add(new LinkTask(sig, headChord, ghost, new BasicContainment())); - } - } - } - - seq.performDo(); - - sheet.getInterIndex().publish(linkedGhosts.get(0).ghost); - sheet.getGlyphIndex().publish(null); - } - - //---------// - // addText // - //---------// - /** - * Special addition of glyph text - * - * @param glyph to be OCR'ed to text lines and words - * @param shape either TEXT or LYRICS - */ - @UIThread - private void addText (final Glyph glyph, - final Shape shape) - { - new CtrlTask(DO) - { - @Override - protected Void doInBackground () - { - try { - final Point centroid = glyph.getCentroid(); - final SystemInfo system = sheet.getSystemManager().getClosestSystem( - centroid); - - if (system == null) { - return null; - } - - final SIGraph sig = system.getSig(); - - // Retrieve lines relative to glyph origin - ByteProcessor buffer = glyph.getBuffer(); - List relativeLines = new BlockScanner(sheet).scanBuffer( - buffer, - sheet.getStub().getOcrLanguages().getValue(), - glyph.getId()); - - // Retrieve absolute lines (and the underlying word glyphs) - boolean lyrics = shape == Shape.LYRICS; - List lines = new TextBuilder(system, lyrics).retrieveGlyphLines( - buffer, - relativeLines, - glyph.getTopLeft()); - - // Generate the sequence of word/line Inter additions - for (TextLine line : lines) { - logger.debug("line {}", line); - - TextRole role = line.getRole(); - SentenceInter sentence = null; - Staff staff = null; - - for (TextWord textWord : line.getWords()) { - logger.debug("word {}", textWord); - - WordInter word = lyrics ? new LyricItemInter(textWord) - : new WordInter(textWord); - - if (sentence != null) { - seq.add( - new AdditionTask( - sig, - word, - textWord.getBounds(), - Arrays.asList( - new Link(sentence, new BasicContainment(), false)))); - } else { - sentence = lyrics ? LyricLineInter.create(line) - : ((role == TextRole.ChordName) - ? ChordNameInter.create(line) - : SentenceInter.create(line)); - staff = sentence.assignStaff(system, line.getLocation()); - seq.add( - new AdditionTask( - sig, - word, - textWord.getBounds(), - Collections.EMPTY_SET)); - seq.add( - new AdditionTask( - sig, - sentence, - line.getBounds(), - Arrays.asList( - new Link(word, new BasicContainment(), true)))); - } - - word.setStaff(staff); - } - } - - seq.performDo(); - - sheet.getInterIndex().publish(null); - sheet.getGlyphIndex().publish(null); - - epilog(seq); - } catch (Throwable ex) { - logger.warn("Exception in addText {}", ex.toString(), ex); - } - - return null; - } - }.execute(); - } - - //------------// - // barlinesOf // - //------------// - private List barlinesOf (Collection inters) - { - return Inters.inters(inters, new Inters.ClassPredicate(BarlineInter.class)); - } - - //--------------------------// - // buildStaffBarlineClosure // - //--------------------------// - private List buildStaffBarlineClosure (StaffBarlineInter oneBar) - { - final Rectangle oneBounds = oneBar.getBounds(); - final Staff staff = oneBar.getStaff(); - final SystemInfo system = staff.getSystem(); - - // Include a staffBarline per system staff, properly positioned in abscissa - final Skew skew = sheet.getSkew(); - final Point center = GeoUtil.centerOf(oneBounds); - final double slope = skew.getSlope(); - final List closure = new ArrayList(); - - for (Staff st : system.getStaves()) { - if (st == staff) { - closure.add(oneBar); - } else { - double y1 = st.getFirstLine().yAt(center.getX()); - double y2 = st.getLastLine().yAt(center.getX()); - double y = (y1 + y2) / 2; - double x = center.x - ((y - center.y) * slope); - Rectangle box = new Rectangle((int) Math.rint(x), (int) Math.rint(y), 0, 0); - box.grow(oneBounds.width / 2, oneBounds.height / 2); - - StaffBarlineInter g = new StaffBarlineInter(oneBar.getShape(), 1); - g.setManual(true); - g.setStaff(st); - g.setBounds(box); - closure.add(g); - } - } - - // Display closure staff barlines to user - sheet.getInterIndex().getEntityService().publish( - new EntityListEvent( - this, - SelectionHint.ENTITY_INIT, - MouseMovement.PRESSING, - closure)); - - if (OMR.gui.displayConfirmation( - "Do you confirm whole system-height addition?", - "Insertion of " + closure.size() + " barline(s)")) { - return closure; - } else { - return Collections.EMPTY_LIST; - } - } - - //----------------// - // determineStaff // - //----------------// - /** - * Determine the target staff for the provided glyph. - * - * @param glyph provided glyph - * @param ghost glyph-based ghost - * @param links (output) to be populated by links - * @return the staff found or null - */ - private Staff determineStaff (Glyph glyph, - Inter ghost, - Collection links) - { - Staff staff = null; - SystemInfo system; - final Point center = glyph.getCenter(); - final List staves = sheet.getStaffManager().getStavesOf(center); - - if (staves.isEmpty()) { - throw new IllegalStateException("No staff for " + center); - } - - if ((staves.size() == 1) - || ghost instanceof BarlineInter - || ghost instanceof StaffBarlineInter) { - // Staff is uniquely defined - staff = staves.get(0); - system = staff.getSystem(); - links.addAll(ghost.searchLinks(system, false)); - - return staff; - } - - // Sort the 2 staves by increasing distance from glyph center - Collections.sort( - staves, - new Comparator() - { - @Override - public int compare (Staff s1, - Staff s2) - { - return Double.compare(s1.distanceTo(center), s2.distanceTo(center)); - } - }); - - if ((staff == null) && constants.useStaffLink.isSet()) { - // Try to use link - SystemInfo prevSystem = null; - StaffLoop: - for (Staff stf : staves) { - system = stf.getSystem(); - - if (system != prevSystem) { - links.addAll(ghost.searchLinks(system, false)); - - for (Link p : links) { - if (p.partner.getStaff() != null) { - staff = p.partner.getStaff(); - - // We stop on first link found (we check closest staff first) - break StaffLoop; - } - } - - links.clear(); - } - - prevSystem = system; - } - } - - if ((staff == null) && constants.useStaffProximity.isSet()) { - // Use proximity to staff (vertical margin defined as ratio of gutter) - final double bestDist = staves.get(0).distanceTo(center); - final double otherDist = staves.get(1).distanceTo(center); - final double gutter = bestDist + otherDist; - - if (bestDist <= (gutter * constants.gutterRatio.getValue())) { - staff = staves.get(0); - } - } - - if (staff == null) { - // Finally, prompt user... - int option = StaffSelection.getInstance().prompt(); - - if (option >= 0) { - staff = staves.get(option); - } - } - - return staff; - } - - //-------------------// - // firstImpactedStep // - //-------------------// - /** - * Report the first step impacted by the provided task sequence - * - * @param seq the provided task sequence - * @return the first step impacted, perhaps null - */ - private Step firstImpactedStep (UITaskList seq) - { - // Classes of inter and relation instances involved - final Set classes = new HashSet(); - - for (UITask task : seq.getTasks()) { - if (task instanceof InterTask) { - InterTask interTask = (InterTask) task; - classes.add(interTask.getInter().getClass()); - } else if (task instanceof RelationTask) { - RelationTask relationTask = (RelationTask) task; - classes.add(relationTask.getRelation().getClass()); - } - } - - for (Step step : Step.values()) { - for (Class classe : classes) { - if (step.isImpactedBy(classe)) { - return step; // First step impacted - } - } - } - - return null; // No impact detected - } - - //------------------------// - // getStaffBarlineClosure // - //------------------------// - private List getStaffBarlineClosure (StaffBarlineInter oneBar) - { - final List closure = new ArrayList(); - - for (PartBarline pb : oneBar.getSystemBarline()) { - closure.addAll(pb.getStaffBarlines()); - } - - // Display closure staff barlines to user - sheet.getInterIndex().getEntityService().publish( - new EntityListEvent( - this, - SelectionHint.ENTITY_INIT, - MouseMovement.PRESSING, - closure)); - - if (OMR.gui.displayConfirmation( - "Do you confirm whole system-height removal?", - "Removal of " + closure.size() + " barline(s)")) { - return closure; - } else { - return Collections.EMPTY_LIST; - } - } - - //------------------// - // populateRemovals // - //------------------// - /** - * Prepare removal of the provided inters (with their relations) - * - * @param inters the inters to remove - * @param seq the task sequence to append to - */ - private void populateRemovals (Collection inters, - UITaskList seq) - { - // Dry run - final Removal removal = new Removal(); - - for (Inter inter : inters) { - if (inter.isRemoved()) { - continue; - } - - if (inter.isVip()) { - logger.info("VIP removeInter {}", inter); - } - - removal.include(inter); - } - - // Now set the removal tasks - removal.populateTaskList(seq); - } - - //-----------// - // refreshUI // - //-----------// - /** - * Refresh UI after any user action sequence. - */ - @UIThread - private void refreshUI () - { - // Update editor display - editor.refresh(); - - // Update status of undo/redo actions - final BookActions bookActions = BookActions.getInstance(); - bookActions.setUndoable(canUndo()); - bookActions.setRedoable(canRedo()); - } - - //-------------------// - // removeCompetitors // - //-------------------// - /** - * Discard any existing Inter with the same underlying glyph. - * - * @param glyph underlying glyph - * @param system containing system - */ - private void removeCompetitors (Inter ghost, - Glyph glyph, - SystemInfo system, - UITaskList seq) - { - if (glyph == null) { - return; - } - - final List intersected = system.getSig().intersectedInters(glyph.getBounds()); - final List competitors = new ArrayList(); - - for (Inter inter : intersected) { - if ((inter != ghost) && (inter.getGlyph() == glyph)) { - competitors.add(inter); - } - } - - populateRemovals(competitors, seq); - } - - //-----------------// - // staffBarlinesOf // - //-----------------// - private List staffBarlinesOf (Collection inters) - { - return Inters.inters(inters, new Inters.ClassPredicate(StaffBarlineInter.class)); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean useStaffLink = new Constant.Boolean( - true, - "Should we use link for staff selection"); - - private final Constant.Boolean useStaffProximity = new Constant.Boolean( - true, - "Should we use proximity for staff selection"); - - private final Constant.Ratio gutterRatio = new Constant.Ratio( - 0.33, - "Vertical margin as ratio of inter-staff gutter"); - } - - //-------------// - // LinkedGhost // - //-------------// - private static class LinkedGhost - { - //~ Instance fields ------------------------------------------------------------------------ - - final Inter ghost; - - final Collection links; - - //~ Constructors --------------------------------------------------------------------------- - public LinkedGhost (Inter ghost, - Collection links) - { - this.ghost = ghost; - this.links = links; - } - - public LinkedGhost (Inter ghost) - { - this(ghost, Collections.EMPTY_LIST); - } - } - - //---------// - // Removal // - //---------// - /** - * Removal scenario used for dry-run before actual operations. - */ - private static class Removal - { - //~ Instance fields ------------------------------------------------------------------------ - - /** Non-ensemble inters to be removed. */ - LinkedHashSet inters = new LinkedHashSet(); - - /** Ensemble inters to be removed. */ - LinkedHashSet ensembles = new LinkedHashSet(); - - /** Ensemble inters to be watched for potential removal. */ - LinkedHashSet watched = new LinkedHashSet(); - - //~ Methods -------------------------------------------------------------------------------- - public void include (Inter inter) - { - if (inter instanceof InterEnsemble) { - // Include the ensemble and its members - final InterEnsemble ens = (InterEnsemble) inter; - ensembles.add(ens); - inters.addAll(ens.getMembers()); - - if (inter instanceof HeadChordInter) { - // Remove the chord stem as well - final HeadChordInter chord = (HeadChordInter) inter; - final StemInter stem = chord.getStem(); - - if (stem != null) { - inters.add(stem); - } - } - } else { - inters.add(inter); - - // Watch the containing ensemble (if not already to be removed) - final SIGraph sig = inter.getSig(); - - for (Relation rel : sig.getRelations(inter, BasicContainment.class)) { - final InterEnsemble ens = (InterEnsemble) sig.getOppositeInter(inter, rel); - - if (!ensembles.contains(ens)) { - watched.add(ens); - } - } - } - } - - /** - * Populate the operational task list - * - * @param seq the task list to populate - */ - public void populateTaskList (UITaskList seq) - { - // Examine watched ensembles - for (InterEnsemble ens : watched) { - List members = new ArrayList(ens.getMembers()); - members.removeAll(inters); - - if (members.isEmpty()) { - ensembles.add(ens); - } - } - - // Ensembles to remove first - for (InterEnsemble ens : ensembles) { - seq.add(new RemovalTask(ens)); - } - - // Simple inters to remove second - for (Inter inter : inters) { - seq.add(new RemovalTask(inter)); - } - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("Removal{"); - sb.append("ensembles:").append(ensembles); - sb.append(" inters:").append(inters); - sb.append("}"); - - return sb.toString(); - } - } - - //----------// - // CtrlTask // - //----------// - /** - * Task class to run user-initiated processing asynchronously. - */ - private abstract class CtrlTask - extends VoidTask - { - //~ Instance fields ------------------------------------------------------------------------ - - protected UITaskList seq = new UITaskList(); - - protected final OpKind opKind; - - //~ Constructors --------------------------------------------------------------------------- - public CtrlTask (OpKind opKind) - { - this.opKind = opKind; - } - - //~ Methods -------------------------------------------------------------------------------- - /** - * Background epilog for any user action sequence. - * - * @param seq sequence of user tasks - */ - protected void epilog (UITaskList seq) - { - if (opKind == OpKind.DO) { - sheet.getStub().setModified(true); - } - - // Re-process impacted steps - final Step latestStep = sheet.getStub().getLatestStep(); - final Step firstStep = firstImpactedStep(seq); - logger.debug("firstStep: {}", firstStep); - - if ((firstStep != null) && (firstStep.compareTo(latestStep) <= 0)) { - final EnumSet steps = EnumSet.range(firstStep, latestStep); - - for (Step step : steps) { - logger.debug("Impact {}", step); - step.impact(seq, opKind); - } - } - } - - @Override - @UIThread - protected void finished () - { - // This method runs on EDT - - // Append to history? - if ((opKind == DO) && (seq != null)) { - history.add(seq); - } - - // Refresh user display - refreshUI(); - } - } - - //--------------// - // RemoveAction // - //--------------// - /** - * Action to remove the selected inter. (Bound to DELETE key) - */ - private class RemoveAction - extends AbstractAction - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public void actionPerformed (ActionEvent e) - { - List inters = sheet.getInterIndex().getEntityService().getSelectedEntityList(); - - if ((inters == null) || inters.isEmpty()) { - return; - } - - if ((inters.size() == 1) - || OMR.gui.displayConfirmation( - "Do you confirm this multiple deletion?", - "Deletion of " + inters.size() + " inters")) { - removeInters(inters); - } - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// I n t e r C o n t r o l l e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import ij.process.ByteProcessor; + +import org.audiveris.omr.OMR; +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.glyph.Glyph; +import org.audiveris.omr.glyph.Shape; +import org.audiveris.omr.glyph.ui.NestView; +import org.audiveris.omr.glyph.ui.SymbolsEditor; +import org.audiveris.omr.math.GeoUtil; +import org.audiveris.omr.sheet.PartBarline; +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.sheet.Skew; +import org.audiveris.omr.sheet.Staff; +import org.audiveris.omr.sheet.SystemInfo; +import org.audiveris.omr.sheet.rhythm.MeasureStack; +import org.audiveris.omr.sheet.symbol.InterFactory; +import org.audiveris.omr.sheet.ui.BookActions; +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.BarlineInter; +import org.audiveris.omr.sig.inter.ChordNameInter; +import org.audiveris.omr.sig.inter.HeadChordInter; +import org.audiveris.omr.sig.inter.HeadInter; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.inter.InterEnsemble; +import org.audiveris.omr.sig.inter.Inters; +import org.audiveris.omr.sig.inter.LyricItemInter; +import org.audiveris.omr.sig.inter.LyricLineInter; +import org.audiveris.omr.sig.inter.RestChordInter; +import org.audiveris.omr.sig.inter.RestInter; +import org.audiveris.omr.sig.inter.SentenceInter; +import org.audiveris.omr.sig.inter.SlurInter; +import org.audiveris.omr.sig.inter.StaffBarlineInter; +import org.audiveris.omr.sig.inter.StemInter; +import org.audiveris.omr.sig.inter.WordInter; +import org.audiveris.omr.sig.relation.AugmentationRelation; +import org.audiveris.omr.sig.relation.ChordStemRelation; +import org.audiveris.omr.sig.relation.Containment; +import org.audiveris.omr.sig.relation.HeadStemRelation; +import org.audiveris.omr.sig.relation.Link; +import org.audiveris.omr.sig.relation.MirrorRelation; +import org.audiveris.omr.sig.relation.Relation; +import org.audiveris.omr.sig.relation.SlurHeadRelation; +import org.audiveris.omr.sig.ui.UITask.OpKind; +import static org.audiveris.omr.sig.ui.UITask.OpKind.*; +import org.audiveris.omr.sig.ui.UITaskList.Option; +import org.audiveris.omr.step.Step; +import org.audiveris.omr.text.BlockScanner; +import org.audiveris.omr.text.OCR; +import org.audiveris.omr.text.OcrUtil; +import org.audiveris.omr.text.TextBuilder; +import org.audiveris.omr.text.TextLine; +import org.audiveris.omr.text.TextRole; +import org.audiveris.omr.text.TextWord; +import org.audiveris.omr.ui.selection.EntityListEvent; +import org.audiveris.omr.ui.selection.MouseMovement; +import org.audiveris.omr.ui.selection.SelectionHint; +import org.audiveris.omr.ui.util.UIThread; +import org.audiveris.omr.util.HorizontalSide; +import static org.audiveris.omr.util.HorizontalSide.*; +import org.audiveris.omr.util.VoidTask; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.geom.Area; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.InputMap; +import javax.swing.JComponent; +import javax.swing.KeyStroke; + +/** + * Class {@code InterController} is the UI in charge of dealing with Inter and + * Relation instances (addition, removal, modifications) to correct OMR output, + * with the ability to undo and redo at will. + *

                                                                                                                                    + * It works at sheet level. + *

                                                                                                                                    + * When adding or dropping an inter, the instance is allocated in proper system (and staff if + * relevant) together with its relations with existing partners nearby. + * It is not always obvious to select the proper staff, various techniques are used, and if + * all have failed, the user is prompted for staff indication. + *

                                                                                                                                    + * Finally, a proper {@link UITaskList} is allocated, inserted in controller's history, and run. + * Undo and Redo actions operate on this history. + *

                                                                                                                                    + * User actions are processed asynchronously in background. + * + * @author Hervé Bitteur + */ +public class InterController +{ + + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(InterController.class); + + /** Underlying sheet. */ + private final Sheet sheet; + + /** History of tasks. */ + private final TaskHistory history = new TaskHistory(); + + /** User pane. Lazily assigned */ + private SymbolsEditor editor; + + /** + * Creates a new {@code IntersController} object. + * + * @param sheet the underlying sheet + */ + public InterController (Sheet sheet) + { + this.sheet = sheet; + } + + //-----------// + // addInters // + //-----------// + /** + * Add one or several inters. + * + * @param inters the populated inters (staff and bounds are already set) + * @param options additional options + */ + @UIThread + public void addInters (final List inters, + final Option... options) + { + if ((inters == null) || inters.isEmpty()) { + return; + } + + logger.debug("addInters {} {}", inters, options); + + if ((options == null) || !Arrays.asList(options).contains(Option.VALIDATED)) { + if (sheet.getStub().getLatestStep().compareTo(Step.MEASURES) >= 0) { + List staffBarlines = staffBarlinesOf(inters); + + if (!staffBarlines.isEmpty()) { + final StaffBarlineInter oneBar = (StaffBarlineInter) staffBarlines.get(0); + final List closure = buildStaffBarlineClosure(oneBar); + + if (!closure.isEmpty()) { + addInters(closure, Option.VALIDATED, Option.UPDATE_MEASURES); + } + + return; + } + } + } + + CtrlTask ctrlTask = new CtrlTask(DO) + { + @Override + protected Void doInBackground () + { + try { + List lgs = new ArrayList<>(); + + for (Inter inter : inters) { + SystemInfo system = inter.getStaff().getSystem(); + + if (inter instanceof BarlineInter) { + BarlineInter b = (BarlineInter) inter; + + if (b.getArea() == null) { + b.setArea(new Area(b.getBounds())); + } + } + + // If glyph is used by another inter, delete this other inter + removeCompetitors(inter, inter.getGlyph(), system, seq); + + lgs.add(new LinkedGhost(inter, inter.searchLinks(system, false))); + } + + addGhosts(seq, lgs); + epilog(seq); + } catch (Throwable ex) { + logger.warn("Exception in addInters {}", ex.toString(), ex); + } + + return null; + } + }; + + ctrlTask.seq.setOptions(options); + ctrlTask.execute(); + } + + //-------------// + // assignGlyph // + //-------------// + /** + * Add a shape interpretation based on a provided glyph. + * + * @param aGlyph the glyph to interpret + * @param shape the shape to be assigned + */ + @UIThread + public void assignGlyph (Glyph aGlyph, + final Shape shape) + { + logger.debug("addInter {} as {}", aGlyph, shape); + + if ((shape == Shape.TEXT) || (shape == Shape.LYRICS)) { + addText(aGlyph, shape); + + return; + } + + final Glyph glyph = sheet.getGlyphIndex().registerOriginal(aGlyph); + final Inter ghost = InterFactory.createManual(shape, sheet); + ghost.setBounds(glyph.getBounds()); + ghost.setGlyph(glyph); + + // While we are still interacting with the user, make sure we have the target staff + final Collection links = new ArrayList<>(); + final Staff staff = determineStaff(glyph, ghost, links); + + if (staff == null) { + logger.info("No staff, abandonned."); + + return; + } + + // For barlines, make sure length is only one-staff high + if (ghost instanceof BarlineInter || ghost instanceof StaffBarlineInter) { + Rectangle box = ghost.getBounds(); + int y1 = staff.getFirstLine().yAt(box.x); + int y2 = staff.getLastLine().yAt(box.x); + ghost.setBounds(new Rectangle(box.x, y1, box.width, y2 - y1 + 1)); + ghost.setGlyph(null); + } + + ghost.setStaff(staff); + addInters(Arrays.asList(ghost)); + } + + //---------// + // canRedo // + //---------// + /** + * Is a redo possible? + * + * @return true if so + */ + @UIThread + public boolean canRedo () + { + return history.canRedo(); + } + + //---------// + // canUndo // + //---------// + /** + * Is an undo possible? + * + * @return true if so + */ + @UIThread + public boolean canUndo () + { + return history.canUndo(); + } + + //----------------// + // changeSentence // + //----------------// + /** + * Change the role of a sentence. + * + * @param sentence the sentence to modify + * @param newRole the new role for the sentence + */ + @UIThread + public void changeSentence (final SentenceInter sentence, + final TextRole newRole) + { + logger.debug("changeSentence {} for {}", sentence, newRole); + + new CtrlTask(DO) + { + @Override + protected Void doInBackground () + { + try { + seq.add(new SentenceRoleTask(sentence, newRole)); + seq.performDo(); + sheet.getInterIndex().publish(sentence); + epilog(seq); + } catch (Throwable ex) { + logger.warn("Exception in changeSentence {}", ex.toString(), ex); + } + + return null; + } + }.execute(); + } + + //------------// + // changeWord // + //------------// + /** + * Change the text content of a word. + * + * @param word the word to modify + * @param newValue the new word content + */ + @UIThread + public void changeWord (final WordInter word, + final String newValue) + { + logger.debug("changeWord {} for {}", word, newValue); + + new CtrlTask(DO) + { + @Override + protected Void doInBackground () + { + try { + seq.add(new WordValueTask(word, newValue)); + seq.performDo(); + sheet.getInterIndex().publish(word); + epilog(seq); + } catch (Throwable ex) { + logger.warn("Exception in changeWord {}", ex.toString(), ex); + } + + return null; + } + }.execute(); + } + + //--------------// + // clearHistory // + //--------------// + /** + * Clear history of user actions. + */ + @UIThread + public void clearHistory () + { + history.clear(); + + if (editor != null) { + refreshUI(); + } + } + + //------// + // link // + //------// + /** + * Add a relation between inters. + * + * @param sig the containing SIG + * @param src the source inter + * @param target the target inter + * @param relation the relation to add + */ + @UIThread + public void link (final SIGraph sig, + final Inter src, + final Inter target, + final Relation relation) + { + new CtrlTask(DO) + { + @Override + protected Void doInBackground () + { + Inter source = src; // To allow use of a new source + + try { + if (relation instanceof HeadStemRelation) { + HeadInter head = (HeadInter) source; + + if (head.getChord() != null) { + source = preHeadStemLink(seq, head, (StemInter) target); + } + } + + // Remove conflicting relations if any + removeConflictingRelations(seq, sig, src, source, target, relation); + + // Finally, add relation + seq.add(new LinkTask(sig, source, target, relation)); + + seq.performDo(); + epilog(seq); + } catch (Throwable ex) { + logger.warn("Exception in process {}", ex.toString(), ex); + } + + return null; + } + }.execute(); + } + + //------// + // redo // + //------// + /** + * Redo last user (undone) action sequence. + */ + @UIThread + public void redo () + { + new CtrlTask(REDO) + { + @Override + protected Void doInBackground () + { + try { + seq = history.toRedo(); + seq.performDo(); + + sheet.getInterIndex().publish(null); + + epilog(seq); + } catch (Throwable ex) { + logger.warn("Exception in redo {}", ex.toString(), ex); + } + + return null; + } + }.execute(); + } + + //--------------// + // removeInters // + //--------------// + /** + * Remove the provided collection of inter (with their relations) + * + * @param inters the inters to remove + * @param options added options if any + */ + @UIThread + public void removeInters (final List inters, + final Option... options) + { + if ((options == null) || !Arrays.asList(options).contains(Option.VALIDATED)) { + if (sheet.getStub().getLatestStep().compareTo(Step.MEASURES) >= 0) { + // Now that measures exist, it's whole system height or nothing + List staffBarlines = new ArrayList<>(staffBarlinesOf(inters)); + + if (staffBarlines.isEmpty()) { + for (Inter inter : barlinesOf(inters)) { + StaffBarlineInter sb = ((BarlineInter) inter).getStaffBarline(); + + if ((sb != null) && !staffBarlines.contains(sb)) { + staffBarlines.add(sb); + } + } + } + + if (!staffBarlines.isEmpty()) { + final StaffBarlineInter oneBar = (StaffBarlineInter) staffBarlines.get(0); + final List closure = getStaffBarlineClosure(oneBar); + + if (!closure.isEmpty()) { + // Remove full system height + for (Inter inter : closure) { + inter.getBounds(); + } + + removeInters(closure, Option.VALIDATED, Option.UPDATE_MEASURES); + } + + return; + } + } + } + + CtrlTask ctrlTask = new CtrlTask(DO) + { + @Override + protected Void doInBackground () + { + try { + populateRemovals(inters, seq); + + seq.performDo(); + sheet.getInterIndex().publish(null); + epilog(seq); + } catch (Throwable ex) { + logger.warn("Exception in removeInters {}", ex.toString(), ex); + } + + return null; + } + }; + + ctrlTask.seq.setOptions(options); + ctrlTask.execute(); + } + + //-----------------// + // reprocessRhythm // + //-----------------// + /** + * Reprocess the rhythm on the provided measure stack. + * + * @param stack measure stack to reprocess + */ + @UIThread + public void reprocessRhythm (final MeasureStack stack) + { + new CtrlTask(DO) + { + @Override + protected Void doInBackground () + { + try { + sheet.getStub().setModified(true); + + seq = null; // So that history is not modified + + // Re-process impacted steps + final UITaskList tempSeq = new UITaskList(new StackTask(stack)); + final Step latestStep = sheet.getStub().getLatestStep(); + final Step firstStep = Step.RHYTHMS; + + final EnumSet steps = EnumSet.range(firstStep, latestStep); + + for (Step step : steps) { + logger.debug("Impact {}", step); + step.impact(tempSeq, OpKind.DO); + } + } catch (Throwable ex) { + logger.warn("Exception in reprocessRhythm {}", ex.toString(), ex); + } + + return null; + } + }.execute(); + } + + //------------------// + // setSymbolsEditor // + //------------------// + /** + * Late assignment of editor, to avoid circularities in elaboration, and to allow + * handling of specific keys. + * + * @param symbolsEditor the user pane + */ + public void setSymbolsEditor (SymbolsEditor symbolsEditor) + { + editor = symbolsEditor; + + NestView view = editor.getView(); + InputMap inputMap = view.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + + // Support for delete key + inputMap.put(KeyStroke.getKeyStroke("DELETE"), "RemoveAction"); + view.getActionMap().put("RemoveAction", new RemoveAction()); + } + + //------// + // undo // + //------// + /** + * Undo last user action. + */ + @UIThread + public void undo () + { + new CtrlTask(UNDO) + { + @Override + protected Void doInBackground () + { + try { + UITaskList seq = history.toUndo(); + seq.performUndo(); + + sheet.getInterIndex().publish(null); + + epilog(seq); + } catch (Throwable ex) { + logger.warn("Exception in undo {}", ex.toString(), ex); + } + + return null; + } + }.execute(); + } + + //--------// + // unlink // + //--------// + /** + * Remove a relation between inters. + * + * @param sig the containing SIG + * @param relation the relation to remove + */ + @UIThread + public void unlink (final SIGraph sig, + final Relation relation) + { + new CtrlTask(DO) + { + @Override + protected Void doInBackground () + { + try { + logger.debug("unlink on {}", relation); + + seq.add(new UnlinkTask(sig, relation)); + + Inter source = sig.getEdgeSource(relation); + + seq.performDo(); + + sheet.getInterIndex().publish(source); + + epilog(seq); + } catch (Throwable ex) { + logger.warn("Exception in unlink {}", ex.toString(), ex); + } + + return null; + } + }.execute(); + } + + //-----------// + // addGhosts // + //-----------// + /** + * Perform ghosts addition. + * It completes {@link #addInters}. + * + * @param seq (output) the UITaskList to populate + * @param linkedGhosts the ghost inters to add/drop with their links + */ + private void addGhosts (UITaskList seq, + List linkedGhosts) + { + for (LinkedGhost linkedGhost : linkedGhosts) { + final Inter ghost = linkedGhost.ghost; + final Collection links = linkedGhost.links; + final Rectangle ghostBounds = ghost.getBounds(); + final Staff staff = ghost.getStaff(); + final SystemInfo system = staff.getSystem(); + final SIGraph sig = system.getSig(); + + // Inter addition + seq.add(new AdditionTask(sig, ghost, ghostBounds, links)); + sheet.getSymbolsEditor().getShapeBoard().addToHistory(ghost.getShape()); + + // Related additions if any + if (ghost instanceof RestInter) { + // Wrap this rest within a rest chord + RestChordInter restChord = new RestChordInter(-1); + restChord.setStaff(staff); + seq.add( + new AdditionTask( + sig, + restChord, + ghostBounds, + Arrays.asList(new Link(ghost, new Containment(), true)))); + } else if (ghost instanceof HeadInter) { + // If we link head to a stem, create/update the related head chord + boolean stemFound = false; + + for (Link link : links) { + if (link.relation instanceof HeadStemRelation) { + final StemInter stem = (StemInter) link.partner; + final HeadChordInter headChord; + final List stemChords = stem.getChords(); + + if (stemChords.isEmpty()) { + // Create a chord based on stem + headChord = new HeadChordInter(-1); + seq.add( + new AdditionTask( + sig, + headChord, + stem.getBounds(), + Collections.EMPTY_SET)); + seq.add(new LinkTask(sig, headChord, stem, new ChordStemRelation())); + } else { + if (stemChords.size() > 1) { + logger.warn("Stem shared by several chords, picked one"); + } + + headChord = stemChords.get(0); + } + + // Declare head part of head-chord + seq.add(new LinkTask(sig, headChord, ghost, new Containment())); + stemFound = true; + + break; + } + } + + if (!stemFound) { + // Head without stem + HeadChordInter headChord = new HeadChordInter(-1); + seq.add(new AdditionTask(sig, headChord, ghostBounds, Collections.EMPTY_SET)); + seq.add(new LinkTask(sig, headChord, ghost, new Containment())); + } + } + } + + seq.performDo(); + + sheet.getInterIndex().publish(linkedGhosts.get(0).ghost); + sheet.getGlyphIndex().publish(null); + } + + //---------// + // addText // + //---------// + /** + * Special addition of glyph text + * + * @param glyph to be OCR'ed to text lines and words + * @param shape either TEXT or LYRICS + */ + @UIThread + private void addText (final Glyph glyph, + final Shape shape) + { + new CtrlTask(DO) + { + @Override + protected Void doInBackground () + { + try { + if (!OcrUtil.getOcr().isAvailable()) { + logger.info(OCR.NO_OCR); + + return null; + } + + final Point centroid = glyph.getCentroid(); + final SystemInfo system = sheet.getSystemManager().getClosestSystem(centroid); + + if (system == null) { + return null; + } + + final SIGraph sig = system.getSig(); + + // Retrieve lines relative to glyph origin + ByteProcessor buffer = glyph.getBuffer(); + List relativeLines = new BlockScanner(sheet).scanBuffer( + buffer, + sheet.getStub().getOcrLanguages().getValue(), + glyph.getId()); + + // Retrieve absolute lines (and the underlying word glyphs) + boolean lyrics = shape == Shape.LYRICS; + List lines = new TextBuilder(system, lyrics).retrieveGlyphLines( + buffer, + relativeLines, + glyph.getTopLeft()); + + // Generate the sequence of word/line Inter additions + for (TextLine line : lines) { + logger.debug("line {}", line); + + TextRole role = line.getRole(); + SentenceInter sentence = null; + Staff staff = null; + + for (TextWord textWord : line.getWords()) { + logger.debug("word {}", textWord); + + WordInter word = lyrics ? new LyricItemInter(textWord) + : new WordInter(textWord); + + if (sentence != null) { + seq.add( + new AdditionTask( + sig, + word, + textWord.getBounds(), + Arrays.asList( + new Link( + sentence, + new Containment(), + false)))); + } else { + sentence = lyrics ? LyricLineInter.create(line) + : ((role == TextRole.ChordName) ? ChordNameInter.create( + line) : SentenceInter.create(line)); + staff = sentence.assignStaff(system, line.getLocation()); + seq.add( + new AdditionTask( + sig, + word, + textWord.getBounds(), + Collections.EMPTY_SET)); + seq.add( + new AdditionTask( + sig, + sentence, + line.getBounds(), + Arrays.asList( + new Link(word, new Containment(), true)))); + } + + word.setStaff(staff); + } + } + + seq.performDo(); + + sheet.getInterIndex().publish(null); + sheet.getGlyphIndex().publish(null); + sheet.getSymbolsEditor().getShapeBoard().addToHistory(shape); + + epilog(seq); + } catch (Throwable ex) { + logger.warn("Exception in addText {}", ex.toString(), ex); + } + + return null; + } + }.execute(); + } + + //------------// + // barlinesOf // + //------------// + private List barlinesOf (Collection inters) + { + return Inters.inters(inters, new Inters.ClassPredicate(BarlineInter.class)); + } + + //--------------------------// + // buildStaffBarlineClosure // + //--------------------------// + private List buildStaffBarlineClosure (StaffBarlineInter oneBar) + { + final Rectangle oneBounds = oneBar.getBounds(); + final Staff staff = oneBar.getStaff(); + final SystemInfo system = staff.getSystem(); + + // Include a staffBarline per system staff, properly positioned in abscissa + final Skew skew = sheet.getSkew(); + final Point center = GeoUtil.centerOf(oneBounds); + final double slope = skew.getSlope(); + final List closure = new ArrayList<>(); + + for (Staff st : system.getStaves()) { + if (st == staff) { + closure.add(oneBar); + } else { + double y1 = st.getFirstLine().yAt(center.getX()); + double y2 = st.getLastLine().yAt(center.getX()); + double y = (y1 + y2) / 2; + double x = center.x - ((y - center.y) * slope); + Rectangle box = new Rectangle((int) Math.rint(x), (int) Math.rint(y), 0, 0); + box.grow(oneBounds.width / 2, oneBounds.height / 2); + + StaffBarlineInter g = new StaffBarlineInter(oneBar.getShape(), 1); + g.setManual(true); + g.setStaff(st); + g.setBounds(box); + closure.add(g); + } + } + + // Display closure staff barlines to user + sheet.getInterIndex().getEntityService().publish( + new EntityListEvent<>( + this, + SelectionHint.ENTITY_INIT, + MouseMovement.PRESSING, + closure)); + + if (OMR.gui.displayConfirmation( + "Do you confirm whole system-height addition?", + "Insertion of " + closure.size() + " barline(s)")) { + return closure; + } else { + return Collections.EMPTY_LIST; + } + } + + //----------------// + // determineStaff // + //----------------// + /** + * Determine the target staff for the provided glyph. + * + * @param glyph provided glyph + * @param ghost glyph-based ghost + * @param links (output) to be populated by links + * @return the staff found or null + */ + private Staff determineStaff (Glyph glyph, + Inter ghost, + Collection links) + { + Staff staff = null; + SystemInfo system; + final Point center = glyph.getCenter(); + final List staves = sheet.getStaffManager().getStavesOf(center); + + if (staves.isEmpty()) { + throw new IllegalStateException("No staff for " + center); + } + + if ((staves.size() == 1) || ghost instanceof BarlineInter + || ghost instanceof StaffBarlineInter) { + // Staff is uniquely defined + staff = staves.get(0); + system = staff.getSystem(); + links.addAll(ghost.searchLinks(system, false)); + + return staff; + } + + // Sort the 2 staves by increasing distance from glyph center + Collections.sort(staves, new Comparator() + { + @Override + public int compare (Staff s1, + Staff s2) + { + return Double.compare(s1.distanceTo(center), s2.distanceTo(center)); + } + }); + + if (constants.useStaffLink.isSet()) { + // Try to use link + SystemInfo prevSystem = null; + StaffLoop: + for (Staff stf : staves) { + system = stf.getSystem(); + + if (system != prevSystem) { + links.addAll(ghost.searchLinks(system, false)); + + for (Link p : links) { + if (p.partner.getStaff() != null) { + staff = p.partner.getStaff(); + + // We stop on first link found (we check closest staff first) + break StaffLoop; + } + } + + links.clear(); + } + + prevSystem = system; + } + } + + if ((staff == null) && constants.useStaffProximity.isSet()) { + // Use proximity to staff (vertical margin defined as ratio of gutter) + final double bestDist = staves.get(0).distanceTo(center); + final double otherDist = staves.get(1).distanceTo(center); + final double gutter = bestDist + otherDist; + + if (bestDist <= (gutter * constants.gutterRatio.getValue())) { + staff = staves.get(0); + } + } + + if (staff == null) { + // Finally, prompt user... + int option = StaffSelection.getInstance().prompt(); + + if (option >= 0) { + staff = staves.get(option); + } + } + + return staff; + } + + //-------------------// + // firstImpactedStep // + //-------------------// + /** + * Report the first step impacted by the provided task sequence + * + * @param seq the provided task sequence + * @return the first step impacted, perhaps null + */ + private Step firstImpactedStep (UITaskList seq) + { + // Classes of inter and relation instances involved + final Set classes = new HashSet<>(); + + for (UITask task : seq.getTasks()) { + if (task instanceof InterTask) { + InterTask interTask = (InterTask) task; + classes.add(interTask.getInter().getClass()); + } else if (task instanceof RelationTask) { + RelationTask relationTask = (RelationTask) task; + classes.add(relationTask.getRelation().getClass()); + } + } + + for (Step step : Step.values()) { + for (Class classe : classes) { + if (step.isImpactedBy(classe)) { + return step; // First step impacted + } + } + } + + return null; // No impact detected + } + + //------------------------// + // getStaffBarlineClosure // + //------------------------// + private List getStaffBarlineClosure (StaffBarlineInter oneBar) + { + final List closure = new ArrayList<>(); + + for (PartBarline pb : oneBar.getSystemBarline()) { + closure.addAll(pb.getStaffBarlines()); + } + + // Display closure staff barlines to user + sheet.getInterIndex().getEntityService().publish( + new EntityListEvent<>( + this, + SelectionHint.ENTITY_INIT, + MouseMovement.PRESSING, + closure)); + + if (OMR.gui.displayConfirmation( + "Do you confirm whole system-height removal?", + "Removal of " + closure.size() + " barline(s)")) { + return closure; + } else { + return Collections.EMPTY_LIST; + } + } + + //------------------// + // populateRemovals // + //------------------// + /** + * Prepare removal of the provided inters (with their relations) + * + * @param inters the inters to remove + * @param seq the task sequence to append to + */ + private void populateRemovals (Collection inters, + UITaskList seq) + { + // Dry run + final Removal removal = new Removal(); + + for (Inter inter : inters) { + if (inter.isRemoved()) { + continue; + } + + if (inter.isVip()) { + logger.info("VIP removeInter {}", inter); + } + + removal.include(inter); + } + + // Now set the removal tasks + removal.populateTaskList(seq); + } + + //-----------------// + // preHeadStemLink // + //-----------------// + /** + * Specific actions before linking a head and a stem. + *

                                                                                                                                      + *
                                                                                                                                    • If head is not yet part of a chord, no specific preparation is needed. + *
                                                                                                                                    • If the link is to result in a canonical share (down stem + head + up stem) then the + * head must be "mirrored" between left and right chords. + *
                                                                                                                                    • Otherwise, if link is to result in a non compatible configuration between head, stem + * and existing chords, then the head must migrate away from its chord to incoming stem chord. + *
                                                                                                                                    + * + * @param seq action sequence to populate + * @param head head inter being linked + * @param stem stem inter being linked + * @return the (perhaps new) head + */ + private Inter preHeadStemLink (UITaskList seq, + HeadInter head, + StemInter stem) + { + final HeadChordInter headChord = head.getChord(); // Not null + final SIGraph sig = head.getSig(); + final List stemChords = stem.getChords(); + HeadChordInter stemChord = (!stemChords.isEmpty()) ? stemChords.get(0) : null; + + // Check for a canonical head share, to share head + final HorizontalSide headSide = (stem.getCenter().x < head.getCenter().x) ? LEFT : RIGHT; + final StemInter headStem = headChord.getStem(); + + final boolean sharing; + if (headSide == LEFT) { + sharing = HeadStemRelation.isCanonicalShare(stem, head, headStem); + } else { + sharing = HeadStemRelation.isCanonicalShare(headStem, head, stem); + } + + if (sharing) { + // Duplicate head and link as mirror + HeadInter newHead = head.duplicate(); + newHead.setManual(true); + seq.add( + new AdditionTask( + sig, + newHead, + newHead.getBounds(), + Arrays.asList(new Link(head, new MirrorRelation(), false)))); + + // Insert newHead to stem chord + if (stemChord == null) { + stemChord = buildStemChord(seq, stem); + } + + seq.add(new LinkTask(sig, stemChord, newHead, new Containment())); + + return newHead; + } + + // If resulting chords are not compatible, move head to stemChord + if ((stemChords.isEmpty() && (headChord.getStem() != null)) || (!stemChords.isEmpty() + && !stemChords + .contains(headChord))) { + // Extract head from headChord + seq.add(new UnlinkTask(sig, sig.getRelation(headChord, head, Containment.class))); + + if (headChord.getNotes().size() <= 1) { + // Remove headChord getting empty + seq.add(new RemovalTask(headChord)); + } + + if (stemChord == null) { + stemChord = buildStemChord(seq, stem); + } + + // Insert head to stem chord + seq.add(new LinkTask(sig, stemChord, head, new Containment())); + } + + return head; + } + + //----------------// + // buildStemChord // + //----------------// + /** + * Create a HeadChord on the fly based on provided stem. + * + * @param seq action sequence to populate + * @param stem the provided stem + * @return a HeadChord around this stem + */ + private HeadChordInter buildStemChord (UITaskList seq, + StemInter stem) + { + final SIGraph sig = stem.getSig(); + final HeadChordInter stemChord = new HeadChordInter(-1); + seq.add(new AdditionTask(sig, stemChord, stem.getBounds(), Collections.EMPTY_SET)); + seq.add(new LinkTask(sig, stemChord, stem, new ChordStemRelation())); + + return stemChord; + } + + //-----------// + // refreshUI // + //-----------// + /** + * Refresh UI after any user action sequence. + */ + @UIThread + private void refreshUI () + { + // Update editor display + editor.refresh(); + + // Update status of undo/redo actions + final BookActions bookActions = BookActions.getInstance(); + bookActions.setUndoable(canUndo()); + bookActions.setRedoable(canRedo()); + } + + //-------------------// + // removeCompetitors // + //-------------------// + /** + * Discard any existing Inter with the same underlying glyph. + * + * @param glyph underlying glyph + * @param system containing system + */ + private void removeCompetitors (Inter ghost, + Glyph glyph, + SystemInfo system, + UITaskList seq) + { + if (glyph == null) { + return; + } + + final List intersected = system.getSig().intersectedInters(glyph.getBounds()); + final List competitors = new ArrayList<>(); + + for (Inter inter : intersected) { + if ((inter != ghost) && (inter.getGlyph() == glyph)) { + competitors.add(inter); + } + } + + populateRemovals(competitors, seq); + } + + //----------------------------// + // removeConflictingRelations // + //----------------------------// + private void removeConflictingRelations (UITaskList seq, + SIGraph sig, + Inter src, + Inter source, + Inter target, + Relation relation) + { + Set toRemove = new LinkedHashSet<>(); + + if (relation instanceof SlurHeadRelation) { + // This relation is declared multi-source & multi-target + // But is single target (head) for each given side + SlurInter slur = (SlurInter) source; + HeadInter head = (HeadInter) target; + HorizontalSide side = (head.getCenter().x < slur.getCenter().x) ? LEFT : RIGHT; + SlurHeadRelation existingRel = slur.getHeadRelation(side); + + if (existingRel != null) { + toRemove.add(existingRel); + } + } + + // Conflict on sources + if (relation.isSingleSource()) { + for (Relation rel : sig.getRelations(target, relation.getClass())) { + toRemove.add(rel); + } + } + + // Conflict on targets + if (relation.isSingleTarget()) { + if (source == src) { + for (Relation rel : sig.getRelations(source, relation.getClass())) { + toRemove.add(rel); + } + + // Specific case of (single target) augmentation dot to shared head: + // We allow a dot source to augment both mirrored head targets + if (relation instanceof AugmentationRelation && target instanceof HeadInter) { + HeadInter mirror = (HeadInter) target.getMirror(); + + if (mirror != null) { + Relation mirrorRel = sig.getRelation(source, mirror, relation.getClass()); + + if (mirrorRel != null) { + toRemove.remove(mirrorRel); + } + } + } + } + } + + for (Relation rel : toRemove) { + seq.add(new UnlinkTask(sig, rel)); + } + } + + //-----------------// + // staffBarlinesOf // + //-----------------// + private List staffBarlinesOf (Collection inters) + { + return Inters.inters(inters, new Inters.ClassPredicate(StaffBarlineInter.class)); + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean useStaffLink = new Constant.Boolean( + true, + "Should we use link for staff selection"); + + private final Constant.Boolean useStaffProximity = new Constant.Boolean( + true, + "Should we use proximity for staff selection"); + + private final Constant.Ratio gutterRatio = new Constant.Ratio( + 0.33, + "Vertical margin as ratio of inter-staff gutter"); + } + + //-------------// + // LinkedGhost // + //-------------// + private static class LinkedGhost + { + + final Inter ghost; + + final Collection links; + + public LinkedGhost (Inter ghost, + Collection links) + { + this.ghost = ghost; + this.links = links; + } + + public LinkedGhost (Inter ghost) + { + this(ghost, Collections.EMPTY_LIST); + } + } + + //---------// + // Removal // + //---------// + /** + * Removal scenario used for dry-run before actual operations. + */ + private static class Removal + { + + /** Non-ensemble inters to be removed. */ + LinkedHashSet inters = new LinkedHashSet<>(); + + /** Ensemble inters to be removed. */ + LinkedHashSet ensembles = new LinkedHashSet<>(); + + /** Ensemble inters to be watched for potential removal. */ + LinkedHashSet watched = new LinkedHashSet<>(); + + public void include (Inter inter) + { + if (inter instanceof InterEnsemble) { + // Include the ensemble and its members + final InterEnsemble ens = (InterEnsemble) inter; + ensembles.add(ens); + inters.addAll(ens.getMembers()); + + if (inter instanceof HeadChordInter) { + // Remove the chord stem as well + final HeadChordInter chord = (HeadChordInter) inter; + final StemInter stem = chord.getStem(); + + if (stem != null) { + inters.add(stem); + } + } + } else { + inters.add(inter); + + // Watch the containing ensemble (if not already to be removed) + final SIGraph sig = inter.getSig(); + + for (Relation rel : sig.getRelations(inter, Containment.class)) { + final InterEnsemble ens = (InterEnsemble) sig.getOppositeInter(inter, rel); + + if (!ensembles.contains(ens)) { + watched.add(ens); + } + } + } + } + + /** + * Populate the operational task list + * + * @param seq the task list to populate + */ + public void populateTaskList (UITaskList seq) + { + // Examine watched ensembles + for (InterEnsemble ens : watched) { + List members = new ArrayList<>(ens.getMembers()); + members.removeAll(inters); + + if (members.isEmpty()) { + ensembles.add(ens); + } + } + + // Ensembles to remove first + for (InterEnsemble ens : ensembles) { + seq.add(new RemovalTask(ens)); + } + + // Simple inters to remove second + for (Inter inter : inters) { + seq.add(new RemovalTask(inter)); + } + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("Removal{"); + sb.append("ensembles:").append(ensembles); + sb.append(" inters:").append(inters); + sb.append("}"); + + return sb.toString(); + } + } + + //----------// + // CtrlTask // + //----------// + /** + * Task class to run user-initiated processing asynchronously. + */ + private abstract class CtrlTask + extends VoidTask + { + + protected UITaskList seq = new UITaskList(); + + protected final OpKind opKind; + + public CtrlTask (OpKind opKind) + { + this.opKind = opKind; + } + + /** + * Background epilog for any user action sequence. + * + * @param seq sequence of user tasks + */ + protected void epilog (UITaskList seq) + { + if (opKind == OpKind.DO) { + sheet.getStub().setModified(true); + } + + // Re-process impacted steps + final Step latestStep = sheet.getStub().getLatestStep(); + final Step firstStep = firstImpactedStep(seq); + logger.debug("firstStep: {}", firstStep); + + if ((firstStep != null) && (firstStep.compareTo(latestStep) <= 0)) { + final EnumSet steps = EnumSet.range(firstStep, latestStep); + + for (Step step : steps) { + logger.debug("Impact {}", step); + step.impact(seq, opKind); + } + } + } + + @Override + @UIThread + protected void finished () + { + // This method runs on EDT + + // Append to history? + if ((opKind == DO) && (seq != null)) { + history.add(seq); + } + + // Refresh user display + refreshUI(); + } + } + + //--------------// + // RemoveAction // + //--------------// + /** + * Action to remove the selected inter. (Bound to DELETE key) + */ + private class RemoveAction + extends AbstractAction + { + + @Override + public void actionPerformed (ActionEvent e) + { + List inters = sheet.getInterIndex().getEntityService().getSelectedEntityList(); + + if ((inters == null) || inters.isEmpty()) { + return; + } + + if ((inters.size() == 1) || OMR.gui.displayConfirmation( + "Do you confirm this multiple deletion?", + "Deletion of " + inters.size() + " inters")) { + removeInters(inters); + } + } + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/InterListMenu.java b/src/main/org/audiveris/omr/sig/ui/InterListMenu.java index fad134118..6e1614fed 100644 --- a/src/main/org/audiveris/omr/sig/ui/InterListMenu.java +++ b/src/main/org/audiveris/omr/sig/ui/InterListMenu.java @@ -27,6 +27,7 @@ import org.audiveris.omr.sig.SIGraph; import org.audiveris.omr.sig.inter.Inter; import org.audiveris.omr.sig.inter.Inters; +import org.audiveris.omr.sig.relation.Relation; import org.audiveris.omr.ui.selection.EntityListEvent; import org.audiveris.omr.ui.selection.MouseMovement; import org.audiveris.omr.ui.selection.SelectionHint; @@ -46,9 +47,9 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.TreeMap; -import javax.swing.JMenu; import javax.swing.JMenuItem; /** @@ -59,16 +60,13 @@ public class InterListMenu extends LocationDependentMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(InterListMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- private final Sheet sheet; private final InterListener interListener = new InterListener(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code InterListMenu} object. * @@ -80,7 +78,6 @@ public InterListMenu (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //--------------------// // updateUserLocation // //--------------------// @@ -102,8 +99,7 @@ private void insertDeletionItem (final SystemInfo system, "Delete " + sysInters.size() + " inters for System #" + system.getId() + ":"); // To delete all listed inters when item is clicked upon - item.addActionListener( - new ActionListener() + item.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) @@ -116,14 +112,13 @@ public void actionPerformed (ActionEvent e) }); // To (re)focus on all the listed inters when moving the mouse on the item - item.addMouseListener( - new AbstractMouseListener() + item.addMouseListener(new AbstractMouseListener() { @Override public void mouseEntered (MouseEvent e) { system.getSheet().getInterIndex().getEntityService().publish( - new EntityListEvent( + new EntityListEvent<>( this, SelectionHint.ENTITY_INIT, MouseMovement.PRESSING, @@ -140,7 +135,7 @@ public void mouseEntered (MouseEvent e) private void updateMenu (Collection inters) { // Sort the inters, first by containing system, then by decreasing contextual grade - Map> interMap = new TreeMap>(); + Map> interMap = new TreeMap<>(); if (inters != null) { for (Inter inter : inters) { @@ -153,7 +148,7 @@ private void updateMenu (Collection inters) List list = interMap.get(system); if (list == null) { - interMap.put(system, list = new ArrayList()); + interMap.put(system, list = new ArrayList<>()); } list.add(inter); @@ -178,17 +173,22 @@ private void updateMenu (Collection inters) addSeparator(); } - // UIUtil.insertTitle( - // this, - // sysInters.size() + " inters for System #" + system.getId() + ":"); List sysInters = entry.getValue(); insertDeletionItem(system, sysInters); for (Inter inter : sysInters) { - // A menu dedicated to this inter instance - JMenu relMenu = new InterMenu(inter).getMenu(); - relMenu.addMouseListener(interListener); - add(relMenu); + // A menu (or simple item) dedicated to this inter instance + final JMenuItem item; + final Set relations = inter.getSig().edgesOf(inter); + + if (!relations.isEmpty()) { + item = new InterMenu(inter, relations).getMenu(); + } else { + item = new JMenuItem(new InterAction(inter, null)); + } + + item.addMouseListener(interListener); + add(item); } } @@ -203,7 +203,6 @@ private void updateMenu (Collection inters) } } - //~ Inner Classes ------------------------------------------------------------------------------ //---------------// // InterListener // //---------------// @@ -213,7 +212,6 @@ private void updateMenu (Collection inters) private static class InterListener extends AbstractMouseListener { - //~ Methods -------------------------------------------------------------------------------- @Override public void mouseEntered (MouseEvent e) diff --git a/src/main/org/audiveris/omr/sig/ui/InterMenu.java b/src/main/org/audiveris/omr/sig/ui/InterMenu.java index 0d95d886a..cd5d3f7df 100644 --- a/src/main/org/audiveris/omr/sig/ui/InterMenu.java +++ b/src/main/org/audiveris/omr/sig/ui/InterMenu.java @@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory; import java.awt.event.MouseEvent; +import java.util.Set; import javax.swing.JMenu; import javax.swing.JMenuItem; @@ -47,26 +48,25 @@ */ public class InterMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(InterMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- private final SeparableMenu menu; private final Inter inter; private final RelationListener relationListener = new RelationListener(); - private final InterController interController; + private InterController interController; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code InterMenu} object. * - * @param inter originating inter + * @param inter originating inter + * @param relations (non empty) set of inter relations */ - public InterMenu (final Inter inter) + public InterMenu (final Inter inter, + final Set relations) { this.inter = inter; @@ -79,8 +79,7 @@ public InterMenu (final Inter inter) interController = sheet.getInterController(); - // Existing relations (available for unlinking) - for (Relation relation : inter.getSig().edgesOf(inter)) { + for (Relation relation : relations) { JMenuItem item = new JMenuItem(new RelationAction(inter, relation)); item.addMouseListener(relationListener); menu.add(item); @@ -89,7 +88,6 @@ public InterMenu (final Inter inter) menu.trimSeparator(); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getMenu // //---------// @@ -109,14 +107,13 @@ private JMenuItem buildTitle (final Sheet sheet, { JMenuItem title = new JMenuItem("Relations:"); title.setEnabled(false); - title.addMouseListener( - new AbstractMouseListener() + title.addMouseListener(new AbstractMouseListener() { @Override public void mouseEntered (MouseEvent e) { sheet.getInterIndex().getEntityService().publish( - new EntityListEvent( + new EntityListEvent<>( this, SelectionHint.ENTITY_INIT, MouseMovement.PRESSING, @@ -127,14 +124,12 @@ public void mouseEntered (MouseEvent e) return title; } - //~ Inner Classes ------------------------------------------------------------------------------ //------------------// // RelationListener // //------------------// private class RelationListener extends AbstractMouseListener { - //~ Methods -------------------------------------------------------------------------------- @Override public void mouseEntered (MouseEvent e) diff --git a/src/main/org/audiveris/omr/sig/ui/InterService.java b/src/main/org/audiveris/omr/sig/ui/InterService.java index d07737357..880585c26 100644 --- a/src/main/org/audiveris/omr/sig/ui/InterService.java +++ b/src/main/org/audiveris/omr/sig/ui/InterService.java @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; /** @@ -50,16 +51,14 @@ public class InterService extends EntityService { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(InterService.class); /** Events that can be published on inter service. */ private static final Class[] eventsAllowed = new Class[]{ - EntityListEvent.class, IdEvent.class - }; + EntityListEvent.class, + IdEvent.class}; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code InterService} object. * @@ -72,7 +71,6 @@ public InterService (EntityIndex index, super(index, locationService, eventsAllowed); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // getMostRelevant // //-----------------// @@ -87,8 +85,7 @@ protected Inter getMostRelevant (List list) return list.get(0); default: - - List copy = new ArrayList(list); + List copy = new ArrayList<>(list); Collections.sort(copy, Inters.membersFirst); return copy.get(0); @@ -138,8 +135,23 @@ protected void handleEntityListEvent (EntityListEvent listEvent) protected void handleLocationEvent (LocationEvent locationEvent) { // Search only when in MODE_INTER or MODE_GLYPH - if (ViewParameters.getInstance().getSelectionMode() != ViewParameters.SelectionMode.MODE_SECTION) { + if (ViewParameters.getInstance() + .getSelectionMode() != ViewParameters.SelectionMode.MODE_SECTION) { super.handleLocationEvent(locationEvent); } } + + //-------------// + // purgeBasket // + //-------------// + @Override + protected void purgeBasket () + { + // Purge basket of Inter instances that are flagged as removed + for (Iterator it = basket.iterator(); it.hasNext();) { + if (it.next().isRemoved()) { + it.remove(); + } + } + } } diff --git a/src/main/org/audiveris/omr/sig/ui/InterTask.java b/src/main/org/audiveris/omr/sig/ui/InterTask.java index 9f605cf36..6ab66d182 100644 --- a/src/main/org/audiveris/omr/sig/ui/InterTask.java +++ b/src/main/org/audiveris/omr/sig/ui/InterTask.java @@ -1,131 +1,128 @@ -//------------------------------------------------------------------------------------------------// -// // -// I n t e r T a s k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.relation.Link; -import org.audiveris.omr.sig.relation.Relation; - -import java.awt.Rectangle; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * Class {@code InterTask} is the elementary task (focused on an Inter) that can be - * done, undone and redone by the {@link InterController}. - * - * @author Hervé Bitteur - */ -public abstract class InterTask - extends UITask -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** Task focus. */ - protected final Inter inter; - - /** Initial bounds of inter. */ - protected final Rectangle initialBounds; - - /** Relations inter is involved in. */ - protected Collection links; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code InterTask} object. - * - * @param sig the underlying sig - * @param inter the inter task is focused upon - * @param initialBounds the inter initial bounds - * @param links the relations around inter - */ - protected InterTask (SIGraph sig, - Inter inter, - Rectangle initialBounds, - Collection links) - { - super(sig); - this.inter = inter; - this.initialBounds = (initialBounds != null) ? new Rectangle(initialBounds) : null; - this.links = links; - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Getter for involved inter. - * - * @return the inter involved - */ - public Inter getInter () - { - return inter; - } - - public Collection getLinks () - { - return links; - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(actionName()); - sb.append(" ").append(inter); - - return sb.toString(); - } - - //---------// - // linksOf // - //---------// - /** - * Retrieve the current links around the provided inter. - * - * @param inter the provided inter - * @return its links, perhaps empty - */ - protected static Collection linksOf (Inter inter) - { - final SIGraph sig = inter.getSig(); - Set links = null; - - for (Relation rel : sig.edgesOf(inter)) { - if (links == null) { - links = new LinkedHashSet(); - } - - Inter partner = sig.getOppositeInter(inter, rel); - - links.add( - new Link(sig.getOppositeInter(inter, rel), rel, sig.getEdgeTarget(rel) == partner)); - } - - if (links == null) { - return Collections.emptySet(); - } - - return links; - } -} +//------------------------------------------------------------------------------------------------// +// // +// I n t e r T a s k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.relation.Link; +import org.audiveris.omr.sig.relation.Relation; + +import java.awt.Rectangle; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Class {@code InterTask} is the elementary task (focused on an Inter) that can be + * done, undone and redone by the {@link InterController}. + * + * @author Hervé Bitteur + */ +public abstract class InterTask + extends UITask +{ + + /** Task focus. */ + protected final Inter inter; + + /** Initial bounds of inter. */ + protected final Rectangle initialBounds; + + /** Relations inter is involved in. */ + protected Collection links; + + /** + * Creates a new {@code InterTask} object. + * + * @param sig the underlying sig + * @param inter the inter task is focused upon + * @param initialBounds the inter initial bounds + * @param links the relations around inter + */ + protected InterTask (SIGraph sig, + Inter inter, + Rectangle initialBounds, + Collection links) + { + super(sig); + this.inter = inter; + this.initialBounds = (initialBounds != null) ? new Rectangle(initialBounds) : null; + this.links = links; + } + + /** + * Getter for involved inter. + * + * @return the inter involved + */ + public Inter getInter () + { + return inter; + } + + public Collection getLinks () + { + return links; + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(actionName()); + sb.append(" ").append(inter); + + return sb.toString(); + } + + //---------// + // linksOf // + //---------// + /** + * Retrieve the current links around the provided inter. + * + * @param inter the provided inter + * @return its links, perhaps empty + */ + protected static Collection linksOf (Inter inter) + { + final SIGraph sig = inter.getSig(); + Set links = null; + + for (Relation rel : sig.edgesOf(inter)) { + if (links == null) { + links = new LinkedHashSet<>(); + } + + Inter partner = sig.getOppositeInter(inter, rel); + + links.add(new Link(sig.getOppositeInter(inter, rel), rel, sig.getEdgeTarget(rel) + == partner)); + } + + if (links == null) { + return Collections.emptySet(); + } + + return links; + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/LinkTask.java b/src/main/org/audiveris/omr/sig/ui/LinkTask.java index 6f569cea9..469d7abb1 100644 --- a/src/main/org/audiveris/omr/sig/ui/LinkTask.java +++ b/src/main/org/audiveris/omr/sig/ui/LinkTask.java @@ -1,81 +1,79 @@ -//------------------------------------------------------------------------------------------------// -// // -// L i n k T a s k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.relation.Relation; - -/** - * Class {@code LinkTask} - * - * @author Hervé Bitteur - */ -public class LinkTask - extends RelationTask -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Creates a new {@code LinkTask} object. - * - * @param sig the underlying sig - * @param source the source inter - * @param target the target inter - * @param relation the relation that task is focused upon - */ - public LinkTask (SIGraph sig, - Inter source, - Inter target, - Relation relation) - { - super(sig, relation); - this.source = source; - this.target = target; - } - - //~ Methods ------------------------------------------------------------------------------------ - @Override - public void performDo () - { - sig.addEdge(getSource(), getTarget(), getRelation()); - - sheet.getInterIndex().publish(source); - } - - @Override - public void performUndo () - { - sig.removeEdge(getRelation()); - -// // Source inter may have been removed when publication is seen on UI... -// if (!source.isRemoved()) { -// sheet.getInterIndex().publish(source); -// } - } - - @Override - protected String actionName () - { - return "link"; - } -} +//------------------------------------------------------------------------------------------------// +// // +// L i n k T a s k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.relation.Relation; + +/** + * Class {@code LinkTask} + * + * @author Hervé Bitteur + */ +public class LinkTask + extends RelationTask +{ + + /** + * Creates a new {@code LinkTask} object. + * + * @param sig the underlying sig + * @param source the source inter + * @param target the target inter + * @param relation the relation that task is focused upon + */ + public LinkTask (SIGraph sig, + Inter source, + Inter target, + Relation relation) + { + super(sig, relation); + this.source = source; + this.target = target; + } + + @Override + public void performDo () + { + sig.addEdge(getSource(), getTarget(), getRelation()); + + sheet.getInterIndex().publish(source); + } + + @Override + public void performUndo () + { + sig.removeEdge(getRelation()); + + // // Source inter may have been removed when publication is seen on UI... + // if (!source.isRemoved()) { + // sheet.getInterIndex().publish(source); + // } + } + + @Override + protected String actionName () + { + return "link"; + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/RelationAction.java b/src/main/org/audiveris/omr/sig/ui/RelationAction.java index 6eb46645b..7c48b2b5e 100644 --- a/src/main/org/audiveris/omr/sig/ui/RelationAction.java +++ b/src/main/org/audiveris/omr/sig/ui/RelationAction.java @@ -39,7 +39,6 @@ public class RelationAction extends AbstractAction { - //~ Instance fields ---------------------------------------------------------------------------- /** Originating inter. */ private final Inter inter; @@ -50,7 +49,6 @@ public class RelationAction /** The other inter, if any. */ private final Inter other; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new RelationAction object. * @@ -84,7 +82,6 @@ public RelationAction (Inter inter, } } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // actionPerformed // //-----------------// @@ -94,16 +91,6 @@ public void actionPerformed (ActionEvent e) publish(); } - //---------// - // publish // - //---------// - public void publish () - { - if (other != null) { - other.getSig().publish(other); - } - } - /** * @return the inter */ @@ -112,6 +99,14 @@ public Inter getInter () return inter; } + /** + * @return the other + */ + public Inter getOther () + { + return other; + } + /** * @return the relation */ @@ -120,11 +115,13 @@ public Relation getRelation () return relation; } - /** - * @return the other - */ - public Inter getOther () + //---------// + // publish // + //---------// + public void publish () { - return other; + if (other != null) { + other.getSig().publish(other); + } } } diff --git a/src/main/org/audiveris/omr/sig/ui/RelationTask.java b/src/main/org/audiveris/omr/sig/ui/RelationTask.java index 81f1a562d..232169707 100644 --- a/src/main/org/audiveris/omr/sig/ui/RelationTask.java +++ b/src/main/org/audiveris/omr/sig/ui/RelationTask.java @@ -1,93 +1,90 @@ -//------------------------------------------------------------------------------------------------// -// // -// R e l a t i o n T a s k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.relation.Relation; - -/** - * Class {@code RelationTask} acts on relations, by linking or unlinking inters. - * - * @author Hervé Bitteur - */ -public abstract class RelationTask - extends UITask -{ - //~ Instance fields ---------------------------------------------------------------------------- - - protected final Relation relation; - - protected Inter source; - - protected Inter target; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code RelationTask} object. - * - * @param sig the underlying sig - * @param relation the relation task is focused upon - */ - public RelationTask (SIGraph sig, - Relation relation) - { - super(sig); - this.relation = relation; - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * @return the relation - */ - public Relation getRelation () - { - return relation; - } - - /** - * @return the source - */ - public Inter getSource () - { - return source; - } - - /** - * @return the target - */ - public Inter getTarget () - { - return target; - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(actionName()); - sb.append(" ").append(relation); - sb.append(" src:").append(source); - sb.append(" tgt:").append(target); - - return sb.toString(); - } -} +//------------------------------------------------------------------------------------------------// +// // +// R e l a t i o n T a s k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.relation.Relation; + +/** + * Class {@code RelationTask} acts on relations, by linking or unlinking inters. + * + * @author Hervé Bitteur + */ +public abstract class RelationTask + extends UITask +{ + + protected final Relation relation; + + protected Inter source; + + protected Inter target; + + /** + * Creates a new {@code RelationTask} object. + * + * @param sig the underlying sig + * @param relation the relation task is focused upon + */ + public RelationTask (SIGraph sig, + Relation relation) + { + super(sig); + this.relation = relation; + } + + /** + * @return the relation + */ + public Relation getRelation () + { + return relation; + } + + /** + * @return the source + */ + public Inter getSource () + { + return source; + } + + /** + * @return the target + */ + public Inter getTarget () + { + return target; + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(actionName()); + sb.append(" ").append(relation); + sb.append(" src:").append(source); + sb.append(" tgt:").append(target); + + return sb.toString(); + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/RemovalTask.java b/src/main/org/audiveris/omr/sig/ui/RemovalTask.java index 5be19d08c..c88ec9965 100644 --- a/src/main/org/audiveris/omr/sig/ui/RemovalTask.java +++ b/src/main/org/audiveris/omr/sig/ui/RemovalTask.java @@ -1,71 +1,69 @@ -//------------------------------------------------------------------------------------------------// -// // -// R e m o v a l T a s k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.relation.Link; - -/** - * Class {@code RemovalTask} removes an inter (with its relations). - * - * @author Hervé Bitteur - */ -public class RemovalTask - extends InterTask -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Creates a new {@code RemovalTask} object. - * - * @param inter the inter to remove - */ - public RemovalTask (Inter inter) - { - super(inter.getSig(), inter, inter.getBounds(), null); - } - - //~ Methods ------------------------------------------------------------------------------------ - @Override - public void performDo () - { - links = linksOf(inter); - inter.remove(false); - } - - @Override - public void performUndo () - { - inter.setBounds(initialBounds); - sig.addVertex(inter); - - for (Link link : links) { - link.applyTo(inter); - } - } - - @Override - protected String actionName () - { - return "del"; - } -} +//------------------------------------------------------------------------------------------------// +// // +// R e m o v a l T a s k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.sig.inter.Inter; +import org.audiveris.omr.sig.relation.Link; + +/** + * Class {@code RemovalTask} removes an inter (with its relations). + * + * @author Hervé Bitteur + */ +public class RemovalTask + extends InterTask +{ + + /** + * Creates a new {@code RemovalTask} object. + * + * @param inter the inter to remove + */ + public RemovalTask (Inter inter) + { + super(inter.getSig(), inter, inter.getBounds(), null); + } + + @Override + public void performDo () + { + links = linksOf(inter); + inter.remove(false); + } + + @Override + public void performUndo () + { + inter.setBounds(initialBounds); + sig.addVertex(inter); + + for (Link link : links) { + link.applyTo(inter); + } + } + + @Override + protected String actionName () + { + return "del"; + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/SentenceRoleTask.java b/src/main/org/audiveris/omr/sig/ui/SentenceRoleTask.java index 825e512d2..bd75a31cd 100644 --- a/src/main/org/audiveris/omr/sig/ui/SentenceRoleTask.java +++ b/src/main/org/audiveris/omr/sig/ui/SentenceRoleTask.java @@ -1,114 +1,111 @@ -//------------------------------------------------------------------------------------------------// -// // -// S e n t e n c e R o l e T a s k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sig.inter.SentenceInter; -import org.audiveris.omr.text.TextRole; - -/** - * Class {@code SentenceRoleTask} change the role of a sentence. - * - * @author Hervé Bitteur - */ -public class SentenceRoleTask - extends InterTask -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** Old sentence role. */ - final TextRole oldRole; - - /** New sentence role. */ - final TextRole newRole; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SentenceTask} object. - * - * @param sentence the sentence to modify - * @param newRole the new role for the sentence - */ - public SentenceRoleTask (SentenceInter sentence, - TextRole newRole) - { - super(sentence.getSig(), sentence, sentence.getBounds(), null); - this.newRole = newRole; - - oldRole = sentence.getRole(); - } - - //~ Methods ------------------------------------------------------------------------------------ - @Override - public SentenceInter getInter () - { - return (SentenceInter) inter; - } - - /** - * @return the newRole - */ - public TextRole getNewRole () - { - return newRole; - } - - /** - * @return the oldRole - */ - public TextRole getOldRole () - { - return oldRole; - } - - @Override - public void performDo () - { - getInter().setRole(newRole); - - sheet.getInterIndex().publish(getInter()); - } - - @Override - public void performUndo () - { - getInter().setRole(oldRole); - - sheet.getInterIndex().publish(getInter()); - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder(actionName()); - sb.append(" ").append(inter); - sb.append(" from ").append(oldRole); - sb.append(" to ").append(newRole); - - return sb.toString(); - } - - @Override - protected String actionName () - { - return "role"; - } -} +//------------------------------------------------------------------------------------------------// +// // +// S e n t e n c e R o l e T a s k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.sig.inter.SentenceInter; +import org.audiveris.omr.text.TextRole; + +/** + * Class {@code SentenceRoleTask} change the role of a sentence. + * + * @author Hervé Bitteur + */ +public class SentenceRoleTask + extends InterTask +{ + + /** Old sentence role. */ + final TextRole oldRole; + + /** New sentence role. */ + final TextRole newRole; + + /** + * Creates a new {@code SentenceTask} object. + * + * @param sentence the sentence to modify + * @param newRole the new role for the sentence + */ + public SentenceRoleTask (SentenceInter sentence, + TextRole newRole) + { + super(sentence.getSig(), sentence, sentence.getBounds(), null); + this.newRole = newRole; + + oldRole = sentence.getRole(); + } + + @Override + public SentenceInter getInter () + { + return (SentenceInter) inter; + } + + /** + * @return the newRole + */ + public TextRole getNewRole () + { + return newRole; + } + + /** + * @return the oldRole + */ + public TextRole getOldRole () + { + return oldRole; + } + + @Override + public void performDo () + { + getInter().setRole(newRole); + + sheet.getInterIndex().publish(getInter()); + } + + @Override + public void performUndo () + { + getInter().setRole(oldRole); + + sheet.getInterIndex().publish(getInter()); + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder(actionName()); + sb.append(" ").append(inter); + sb.append(" from ").append(oldRole); + sb.append(" to ").append(newRole); + + return sb.toString(); + } + + @Override + protected String actionName () + { + return "role"; + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/ShapeBoard.java b/src/main/org/audiveris/omr/sig/ui/ShapeBoard.java index f63d1f07f..f6523a8ae 100644 --- a/src/main/org/audiveris/omr/sig/ui/ShapeBoard.java +++ b/src/main/org/audiveris/omr/sig/ui/ShapeBoard.java @@ -33,7 +33,7 @@ import org.audiveris.omr.glyph.ShapeSet; import org.audiveris.omr.glyph.ui.SymbolsEditor; import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.sheet.symbol.SymbolFactory; +import org.audiveris.omr.sheet.symbol.InterFactory; import org.audiveris.omr.ui.Board; import org.audiveris.omr.ui.OmrGlassPane; import org.audiveris.omr.ui.dnd.AbstractGhostDropListener; @@ -70,11 +70,13 @@ import java.awt.image.BufferedImage; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JButton; +import javax.swing.SwingUtilities; /** * Class {@code ShapeBoard} hosts a palette of shapes for insertion and assignment of @@ -96,12 +98,10 @@ public class ShapeBoard extends Board { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - ShapeBoard.class); + private static final Logger logger = LoggerFactory.getLogger(ShapeBoard.class); /** To force the width of the various panels. */ private static final int BOARD_WIDTH = 317; @@ -114,16 +114,15 @@ public class ShapeBoard private static final Map heights = buildHeightMap(); /** Map first typed char to selected shape set. */ - private static final Map setMap = new HashMap(); + private static final Map setMap = new HashMap<>(); /** Map 2-char typed string to selected shape. */ - private static final Map shapeMap = new HashMap(); + private static final Map shapeMap = new HashMap<>(); static { populateCharMaps(); } - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ @Navigable(false) private final Sheet sheet; @@ -173,7 +172,6 @@ public void mouseClicked (MouseEvent e) if (glyph != null) { ShapeButton button = (ShapeButton) e.getSource(); - shapeHistory.add(button.shape); assignGlyph(glyph, button.shape); } } @@ -184,7 +182,7 @@ public void mouseClicked (MouseEvent e) private final Panel setsPanel; /** Map of shape panels, indexed by shapeSet. */ - private final Map shapesPanels = new HashMap(); + private final Map shapesPanels = new HashMap<>(); /** History of recently used shapes. */ private final ShapeHistory shapeHistory; @@ -204,9 +202,9 @@ public void mouseClicked (MouseEvent e) /** When mouse is pressed (start) and released (stop). */ private final MyDropAdapter dropAdapter = new MyDropAdapter(); - private MyKeyListener keyListener = new MyKeyListener(); + /** To handle sequence of keys typed. */ + private final MyKeyListener keyListener = new MyKeyListener(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new ShapeBoard object. * @@ -232,7 +230,32 @@ public ShapeBoard (Sheet sheet, getComponent().addKeyListener(keyListener); } - //~ Methods ------------------------------------------------------------------------------------ + //--------------// + // addToHistory // + //--------------// + /** + * Add a shape to recent history. + * + * @param shape the shape just used + */ + public void addToHistory (Shape shape) + { + shapeHistory.add(shape); + } + + //------------// + // getHistory // + //------------// + /** + * Report the recent shapes. + * + * @return list of most recent shapes + */ + public List getHistory () + { + return shapeHistory.getShapes(); + } + //---------// // onEvent // //---------// @@ -299,7 +322,7 @@ private void addButtons (Panel panel, List shapes) { for (Shape shape : shapes) { - ShapeButton button = new ShapeButton(shape); + ShapeButton button = new ShapeButton(shape, keyListener); button.addMouseListener(mouseListener); // For double-click button.addMouseListener(dropAdapter); // For DnD transfer and double-click button.addMouseMotionListener(motionAdapter); // For dragging @@ -323,7 +346,7 @@ private void assignGlyph (Glyph glyph, //----------------// private static Map buildHeightMap () { - Map map = new HashMap(); + Map map = new HashMap<>(); map.put(ShapeSet.Accidentals, 40); map.put(ShapeSet.Articulations, 40); map.put(ShapeSet.Attributes, 60); @@ -444,9 +467,7 @@ private void closeShapeSet () private void defineLayout () { CellConstraints cst = new CellConstraints(); - FormLayout layout = new FormLayout( - "190dlu", - "pref," + Panel.getFieldInterline() + ",pref"); + FormLayout layout = new FormLayout("190dlu", "pref," + Panel.getFieldInterline() + ",pref"); PanelBuilder builder = new PanelBuilder(layout, getBody()); builder.add(shapeHistory.panel, cst.xy(1, 1)); @@ -527,22 +548,20 @@ private void selectShapeSet (ShapeSet set) shapesPanel.requestFocusInWindow(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-------------// // ShapeButton // //-------------// /** * A button dedicated to a shape. */ - public class ShapeButton + public static class ShapeButton extends JButton { - //~ Instance fields ------------------------------------------------------------------------ final Shape shape; - //~ Constructors --------------------------------------------------------------------------- - public ShapeButton (Shape shape) + public ShapeButton (Shape shape, + MyKeyListener keyListener) { this.shape = shape; setIcon(shape.getDecoratedSymbol()); @@ -557,10 +576,9 @@ public ShapeButton (Shape shape) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean publishLocationWhileDragging = new Constant.Boolean( false, @@ -581,14 +599,12 @@ private static final class Constants private class MyDropAdapter extends GhostDropAdapter { - //~ Constructors --------------------------------------------------------------------------- public MyDropAdapter () { super(ShapeBoard.this.glassPane, null); } - //~ Methods -------------------------------------------------------------------------------- public Shape getAction () { return action; @@ -639,7 +655,6 @@ public void mouseReleased (MouseEvent e) private class MyDropListener extends AbstractGhostDropListener { - //~ Constructors --------------------------------------------------------------------------- public MyDropListener () { @@ -647,7 +662,6 @@ public MyDropListener () super(null); } - //~ Methods -------------------------------------------------------------------------------- @Override public void dropped (GhostDropEvent e) { @@ -668,7 +682,7 @@ public void dropped (GhostDropEvent e) dndOperation.drop(localPt); // Update history - shapeHistory.add(dndOperation.getGhost().getShape()); + addToHistory(dndOperation.getGhost().getShape()); } } } @@ -686,11 +700,9 @@ public void dropped (GhostDropEvent e) private class MyKeyListener implements KeyListener { - //~ Instance fields ------------------------------------------------------------------------ Character c1 = null; - //~ Methods -------------------------------------------------------------------------------- @Override public void keyPressed (KeyEvent e) { @@ -723,14 +735,13 @@ public void keyTyped (KeyEvent e) logger.debug("shape:{}", shape); if (shape != null) { - shapeHistory.add(shape); - Glyph glyph = sheet.getGlyphIndex().getSelectedGlyph(); if (glyph != null) { assignGlyph(glyph, shape); } else { // Set focus on proper shape button + addToHistory(shape); shapeHistory.setFocus(); } } else { @@ -758,22 +769,19 @@ public void reset () private class MyMotionAdapter extends GhostMotionAdapter { - //~ Instance fields ------------------------------------------------------------------------ // Optimization: remember the latest component on target private WeakReference prevComponent; - //~ Constructors --------------------------------------------------------------------------- public MyMotionAdapter () { super(ShapeBoard.this.glassPane); reset(); } - //~ Methods -------------------------------------------------------------------------------- public final void reset () { - prevComponent = new WeakReference(null); + prevComponent = new WeakReference<>(null); } /** @@ -809,7 +817,7 @@ public void mouseDragged (MouseEvent e) dndOperation = new DndOperation( sheet, zoom, - SymbolFactory.createManual(shape, sheet)); + InterFactory.createManual(shape, sheet)); } dndOperation.enteringTarget(); @@ -818,14 +826,15 @@ public void mouseDragged (MouseEvent e) glass.setReference(null); } - prevComponent = new WeakReference(component); + prevComponent = new WeakReference<>(component); } if (shape.isDraggable()) { // Update reference point Point localRef = dndOperation.getReference(localPt); glass.setReference( - (localRef != null) ? new ScreenPoint(view, zoom.scaled(localRef)) : null); + (localRef != null) ? new ScreenPoint(view, zoom.scaled(localRef)) + : null); } } else if (prevComponent.get() != null) { // No longer on a droppable target, reuse initial image & size @@ -848,18 +857,16 @@ public void mouseDragged (MouseEvent e) */ private class ShapeHistory { - //~ Instance fields ------------------------------------------------------------------------ /** Shapes recently used, ordered from most to less recent. */ - private final List shapes = new ArrayList(); + private final List shapes = new ArrayList<>(); private final Panel panel = new Panel(); - //~ Constructors --------------------------------------------------------------------------- public ShapeHistory () { panel.setNoInsets(); - panel.setPreferredSize(new Dimension(BOARD_WIDTH, 55)); + panel.setPreferredSize(new Dimension(BOARD_WIDTH, 80)); panel.setVisible(false); FlowLayout layout = new FlowLayout(); @@ -867,23 +874,50 @@ public ShapeHistory () panel.setLayout(layout); } - //~ Methods -------------------------------------------------------------------------------- - public void add (Shape shape) + /** + * Insert a shape in history. + *

                                                                                                                                    + * This dynamically modifies the display of recently used shapes, and thus must be performed + * from EDT (and not from a background thread). + * + * @param shape the most recent shape + */ + public void add (final Shape shape) { - shapes.remove(shape); // Remove duplicate if any - shapes.add(0, shape); // Insert at beginning of the list + if (!SwingUtilities.isEventDispatchThread()) { + try { + SwingUtilities.invokeAndWait(new Runnable() + { + @Override + public void run () + { + add(shape); + } + }); + } catch (Exception ex) { + logger.warn("invokeAndWait error", ex); + } + } else { + shapes.remove(shape); // Remove duplicate if any + shapes.add(0, shape); // Insert at beginning of the list - // Check for maximum length - while (shapes.size() > constants.maxHistoryLength.getValue()) { - shapes.remove(shapes.size() - 1); - } + // Check for maximum length + while (shapes.size() > constants.maxHistoryLength.getValue()) { + shapes.remove(shapes.size() - 1); + } - // Regenerate the buttons - panel.removeAll(); - addButtons(panel, shapes); + // Regenerate the buttons + panel.removeAll(); + addButtons(panel, shapes); - panel.setVisible(true); - resizeBoard(); + panel.setVisible(true); + resizeBoard(); + } + } + + public List getShapes () + { + return Collections.unmodifiableList(shapes); } /** diff --git a/src/main/org/audiveris/omr/sig/ui/SigPainter.java b/src/main/org/audiveris/omr/sig/ui/SigPainter.java index 9ba74a4fa..ea876fbee 100644 --- a/src/main/org/audiveris/omr/sig/ui/SigPainter.java +++ b/src/main/org/audiveris/omr/sig/ui/SigPainter.java @@ -35,9 +35,12 @@ import org.audiveris.omr.sheet.rhythm.Voice; import org.audiveris.omr.sig.SIGraph; import org.audiveris.omr.sig.inter.AbstractBeamInter; +import org.audiveris.omr.sig.inter.AbstractChordInter; import org.audiveris.omr.sig.inter.AbstractFlagInter; import org.audiveris.omr.sig.inter.AbstractInterVisitor; +import org.audiveris.omr.sig.inter.AlterInter; import org.audiveris.omr.sig.inter.ArpeggiatoInter; +import org.audiveris.omr.sig.inter.AugmentationDotInter; import org.audiveris.omr.sig.inter.BarConnectorInter; import org.audiveris.omr.sig.inter.BarlineInter; import org.audiveris.omr.sig.inter.BraceInter; @@ -57,6 +60,8 @@ import org.audiveris.omr.sig.inter.TimeWholeInter; import org.audiveris.omr.sig.inter.WedgeInter; import org.audiveris.omr.sig.inter.WordInter; +import org.audiveris.omr.sig.relation.AlterHeadRelation; +import org.audiveris.omr.sig.relation.AugmentationRelation; import org.audiveris.omr.sig.relation.FlagStemRelation; import org.audiveris.omr.sig.relation.Relation; import org.audiveris.omr.text.FontInfo; @@ -66,11 +71,11 @@ import org.audiveris.omr.ui.symbol.OmrFont; import org.audiveris.omr.ui.symbol.ShapeSymbol; import org.audiveris.omr.ui.symbol.Symbols; +import org.audiveris.omr.ui.symbol.TextFont; import static org.audiveris.omr.ui.symbol.Symbols.SYMBOL_BRACE_LOWER_HALF; import static org.audiveris.omr.ui.symbol.Symbols.SYMBOL_BRACE_UPPER_HALF; import static org.audiveris.omr.ui.symbol.Symbols.SYMBOL_BRACKET_LOWER_SERIF; import static org.audiveris.omr.ui.symbol.Symbols.SYMBOL_BRACKET_UPPER_SERIF; -import org.audiveris.omr.ui.symbol.TextFont; import org.audiveris.omr.ui.util.Panel; import org.audiveris.omr.ui.util.UIUtil; @@ -89,6 +94,9 @@ import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.CubicCurve2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -101,7 +109,8 @@ *

                                                                                                                                    * Its life cycle ends with the painting of a sheet. *

                                                                                                                                    - * Remarks on no-op visit() for:

                                                                                                                                      + * Remarks on no-op visit() for: + *
                                                                                                                                        *
                                                                                                                                      • AbstractChordInter: Notes and stem are painted on their own *
                                                                                                                                      • KeyInter: Each key item is painted on its own *
                                                                                                                                      • WordInter: Painting is handled from sentence @@ -112,7 +121,6 @@ public abstract class SigPainter extends AbstractInterVisitor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SigPainter.class); @@ -135,10 +143,8 @@ public abstract class SigPainter /** 7 Pink */ new Color(255, 150, 150, alpha), /** 8 BlueGreen */ - new Color(0, 128, 128, alpha) - }; + new Color(0, 128, 128, alpha)}; - //~ Instance fields ---------------------------------------------------------------------------- /** Graphic context. */ protected final Graphics2D g; @@ -166,7 +172,6 @@ public abstract class SigPainter /** Global stroke for ledgers, with no glyph. */ private final Stroke ledgerStroke; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SigPainter} object. * @@ -204,12 +209,13 @@ public SigPainter (Graphics g, ledgerStroke = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // getVoicePanel // //---------------// /** * Build a panel which displays all defined voice ID colors. + *

                                                                                                                                        + * Separate numbers for first staff and second staff as: 1234 - 5678 * * @return the populated voice panel */ @@ -217,8 +223,18 @@ public static JPanel getVoicePanel () { final int length = voiceColors.length; final Font font = new Font("SansSerif", Font.BOLD, 22); - final Color background = Color.WHITE; //new Color(220, 220, 220); - final FormLayout layout = Panel.makeLabelsLayout(1, length, "0dlu", "10dlu"); + final Color background = Color.WHITE; + final StringBuilder sbc = new StringBuilder(); + + for (int i = 0; i <= length; i++) { + if (i != 0) { + sbc.append(","); + } + + sbc.append("10dlu"); + } + + final FormLayout layout = new FormLayout(sbc.toString(), "pref"); final Panel panel = new Panel(); final PanelBuilder builder = new PanelBuilder(layout, panel); final CellConstraints cst = new CellConstraints(); @@ -227,6 +243,8 @@ public static JPanel getVoicePanel () final Dimension cellDim = new Dimension(5, 22); panel.setInsets(3, 0, 0, 3); // TLBR + final int mid = length / 2; + for (int c = 1; c <= length; c++) { final Color color = new Color(voiceColors[c - 1].getRGB()); // Remove alpha final JLabel label = new JLabel("" + c, JLabel.CENTER); @@ -235,7 +253,20 @@ public static JPanel getVoicePanel () label.setOpaque(true); label.setBackground(background); label.setForeground(color); - builder.add(label, cst.xy((2 * c) - 1, 1)); + + int col = (c <= mid) ? c : (c + 1); + builder.add(label, cst.xy(col, 1)); + } + // Separation between staves + { + final Color color = Color.BLACK; + final JLabel label = new JLabel("="); + label.setPreferredSize(cellDim); + label.setFont(font); + label.setOpaque(true); + label.setBackground(background); + label.setForeground(color); + builder.add(label, cst.xy(mid + 1, 1)); } return panel; @@ -249,7 +280,7 @@ public void process (SIGraph sig) final int bracketGrowth = 2 * sig.getSystem().getSheet().getInterline(); // Use a COPY of vertices, to reduce risks of concurrent modifications (but not all...) - Set copy = new LinkedHashSet(sig.vertexSet()); + Set copy = new LinkedHashSet<>(sig.vertexSet()); for (Inter inter : copy) { if (!inter.isRemoved()) { @@ -313,6 +344,15 @@ public void visit (AbstractFlagInter flag) } } + //-------// + // visit // + //-------// + @Override + public void visit (AlterInter inter) + { + paintHalf(inter, AlterHeadRelation.class); + } + //-------// // visit // //-------// @@ -341,6 +381,15 @@ public void visit (ArpeggiatoInter arpeggiato) g.setClip(clip); } + //-------// + // visit // + //-------// + @Override + public void visit (AugmentationDotInter inter) + { + paintHalf(inter, AugmentationRelation.class); + } + //-------// // visit // //-------// @@ -490,17 +539,35 @@ public void visit (EndingInter ending) @Override public void visit (HeadInter head) { - // Consider it as a plain inter - visit((Inter) head); + final Line2D midLine = head.getMidLine(); + + if (midLine != null) { + if (splitMirrors()) { + // Draw head proper half + int width = head.getBounds().width; + int xDir = midLine.getY2() > midLine.getY1() ? -1 : +1; + Path2D p = new Path2D.Double(); + p.append(midLine, false); + p.lineTo(midLine.getX2() + xDir * width, midLine.getY2()); + p.lineTo(midLine.getX1() + xDir * width, midLine.getY1()); + p.closePath(); + + java.awt.Shape oldClip = g.getClip(); + g.clip(p); + visit((Inter) head); + g.setClip(oldClip); + } else { + visit((Inter) head); + } - if (head.getMirror() != null) { - // Draw a sign using complementary color of head + // Draw midLine using complementary color of head Color compColor = UIUtil.complementaryColor(g.getColor()); Stroke oldStroke = UIUtil.setAbsoluteStroke(g, 1f); - Rectangle box = head.getBounds(); g.setColor(compColor); - g.drawLine(box.x, box.y, box.x + box.width, box.y + box.height); + g.draw(midLine); g.setStroke(oldStroke); + } else { + visit((Inter) head); } } @@ -690,6 +757,26 @@ public void visit (WedgeInter wedge) g.draw(wedge.getLine2()); } + //----------// + // setColor // + //----------// + /** + * Use color adapted to current inter and global viewing parameters. + * + * @param inter the interpretation to colorize + */ + protected abstract void setColor (Inter inter); + + //--------------// + // splitMirrors // + //--------------// + /** + * Tell whether shared heads are split. + * + * @return true if so + */ + protected abstract boolean splitMirrors (); + //---------// // colorOf // //---------// @@ -798,15 +885,75 @@ protected void paint (TextLayout layout, OmrFont.paint(g, layout, location, alignment); } - //----------// - // setColor // - //----------// + //-----------// + // paintHalf // + //-----------// /** - * Use color adapted to current inter and global viewing parameters. + * Paint upper and lower parts of a symbol, if it is linked to two shared heads. + * Otherwise, paint it normally as a whole. * - * @param inter the interpretation to colorize + * @param inter the inter to paint + * (AugmentationDotInter or AlterInter) + * @param classe the relation class to search between inter and head + * (AugmentationRelation or AlterHeadRelation) */ - protected abstract void setColor (Inter inter); + private void paintHalf (Inter inter, + Class classe) + { + if (!splitMirrors()) { + visit(inter); + + return; + } + + final SIGraph sig = inter.getSig(); + final List heads = new ArrayList<>(); + + for (Relation rel : sig.getRelations(inter, classe)) { + Inter opposite = sig.getOppositeInter(inter, rel); + + if (opposite instanceof HeadInter) { + heads.add((HeadInter) opposite); + } + } + + if ((heads.size() != 2) || (heads.get(0).getMirror() != heads.get(1))) { + // Standard case where symbol is painted as a whole + visit(inter); + } else { + // Split according to linked shared heads + final Rectangle box = inter.getBounds(); + final int height = box.height; + final Point center = inter.getCenter(); + final Point ref = inter.getRelationCenter(); // Not always the area center + final Shape shape = inter.getShape(); + final Staff staff = inter.getStaff(); + final ShapeSymbol symbol = Symbols.getSymbol(shape); + final MusicFont font = getMusicFont(staff); + final Dimension dim = symbol.getDimension(font); + final int w = dim.width; + final Line2D line = new Line2D.Double(ref.x - w, ref.y, ref.x + w, ref.y); + + // Draw each inter half + for (HeadInter h : heads) { + final AbstractChordInter ch = h.getChord(); + final int yDir = ch.getCenter().y > h.getCenter().y ? +1 : -1; + final Path2D p = new Path2D.Double(); + p.append(line, false); + p.lineTo(line.getX2(), line.getY2() + yDir * height); + p.lineTo(line.getX1(), line.getY1() + yDir * height); + p.closePath(); + + final java.awt.Shape oldClip = g.getClip(); + g.clip(p); + + setColor(ch); + symbol.paintSymbol(g, font, center, Alignment.AREA_CENTER); + + g.setClip(oldClip); + } + } + } //-----------// // paintWord // diff --git a/src/main/org/audiveris/omr/sig/ui/StackTask.java b/src/main/org/audiveris/omr/sig/ui/StackTask.java index d8dd98a08..b250ad632 100644 --- a/src/main/org/audiveris/omr/sig/ui/StackTask.java +++ b/src/main/org/audiveris/omr/sig/ui/StackTask.java @@ -1,74 +1,71 @@ -//------------------------------------------------------------------------------------------------// -// // -// S t a c k T a s k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sheet.rhythm.MeasureStack; - -/** - * Class {@code StackTask} implements the on demand re-processing of a stack. - * - * @author Hervé Bitteur - */ -public class StackTask - extends UITask -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** Impacted stack. */ - private final MeasureStack stack; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code StackTask} object. - * - * @param stack the impacted stack - */ - public StackTask (MeasureStack stack) - { - super(stack.getSystem().getSig()); - this.stack = stack; - } - - //~ Methods ------------------------------------------------------------------------------------ - public MeasureStack getStack () - { - return stack; - } - - @Override - public void performDo () - { - // Void - } - - @Override - public void performUndo () - { - // Void - } - - @Override - protected String actionName () - { - return "reprocess"; - } -} +//------------------------------------------------------------------------------------------------// +// // +// S t a c k T a s k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.sheet.rhythm.MeasureStack; + +/** + * Class {@code StackTask} implements the on demand re-processing of a stack. + * + * @author Hervé Bitteur + */ +public class StackTask + extends UITask +{ + + /** Impacted stack. */ + private final MeasureStack stack; + + /** + * Creates a new {@code StackTask} object. + * + * @param stack the impacted stack + */ + public StackTask (MeasureStack stack) + { + super(stack.getSystem().getSig()); + this.stack = stack; + } + + public MeasureStack getStack () + { + return stack; + } + + @Override + public void performDo () + { + // Void + } + + @Override + public void performUndo () + { + // Void + } + + @Override + protected String actionName () + { + return "reprocess"; + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/StaffSelection.java b/src/main/org/audiveris/omr/sig/ui/StaffSelection.java index c16413e4f..0de7d22a4 100644 --- a/src/main/org/audiveris/omr/sig/ui/StaffSelection.java +++ b/src/main/org/audiveris/omr/sig/ui/StaffSelection.java @@ -1,119 +1,111 @@ -//------------------------------------------------------------------------------------------------// -// // -// S t a f f S e l e c t i o n // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.OMR; - -import org.jdesktop.application.Application; -import org.jdesktop.application.ResourceMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.JDialog; -import javax.swing.JOptionPane; -import static javax.swing.JOptionPane.CLOSED_OPTION; - -/** - * Class {@code StaffSelection} aims at selecting proper staff, based on user location. - * - * @author Hervé Bitteur - */ -public class StaffSelection -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(StaffSelection.class); - - /** Singleton. */ - private static volatile StaffSelection INSTANCE; - - //~ Instance fields ---------------------------------------------------------------------------- - /** Resource injection. */ - private final ResourceMap resources = Application.getInstance().getContext().getResourceMap( - StaffSelection.class); - - private final String className = getClass().getSimpleName(); - - /** Options objects for dialog. */ - private final Object[] options = new Object[]{ - resources.getImageIcon(className + ".UP.icon"), - resources.getImageIcon(className + ".DOWN.icon"), - resources.getString(className + ".cancel") - }; - - /** Option pane. */ - private final JOptionPane pane = new JOptionPane( - resources.getString(className + ".message"), - JOptionPane.QUESTION_MESSAGE, - JOptionPane.DEFAULT_OPTION, - null, - options); - - /** Reusable dialog. */ - private final JDialog dialog = pane.createDialog( - OMR.gui.getFrame(), - resources.getString(className + ".title")); - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Report the singleton - * - * @return the unique instance of this class - */ - public static StaffSelection getInstance () - { - if (INSTANCE == null) { - synchronized (StaffSelection.class) { - if (INSTANCE == null) { - INSTANCE = new StaffSelection(); - } - } - } - - return INSTANCE; - } - - /** - * Prompt the user for staff selection. - * - * @return the chosen option: 0 for UP, 1 for down, else -1 - */ - public int prompt () - { - dialog.setVisible(true); - - Object selectedValue = pane.getValue(); - - if (selectedValue == null) { - return CLOSED_OPTION; - } - - for (int counter = 0; counter < 2; counter++) { - if (options[counter].equals(selectedValue)) { - return counter; - } - } - - return CLOSED_OPTION; // Either closed or cancelled - } -} +//------------------------------------------------------------------------------------------------// +// // +// S t a f f S e l e c t i o n // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.OMR; + +import org.jdesktop.application.Application; +import org.jdesktop.application.ResourceMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import static javax.swing.JOptionPane.CLOSED_OPTION; + +/** + * Class {@code StaffSelection} aims at selecting proper staff, based on user location. + * + * @author Hervé Bitteur + */ +public class StaffSelection +{ + + private static final Logger logger = LoggerFactory.getLogger(StaffSelection.class); + + /** Resource injection. */ + private final ResourceMap resources = Application.getInstance().getContext().getResourceMap( + StaffSelection.class); + + private final String className = getClass().getSimpleName(); + + /** Options objects for dialog. */ + private final Object[] options = new Object[]{resources.getImageIcon(className + ".UP.icon"), + resources.getImageIcon(className + ".DOWN.icon"), + resources.getString(className + ".cancel")}; + + /** Option pane. */ + private final JOptionPane pane = new JOptionPane(resources.getString(className + ".message"), + JOptionPane.QUESTION_MESSAGE, + JOptionPane.DEFAULT_OPTION, null, options); + + /** Reusable dialog. */ + private final JDialog dialog = pane.createDialog(OMR.gui.getFrame(), resources.getString( + className + ".title")); + + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of this class in application. + * + * @return the instance + */ + public static StaffSelection getInstance () + { + return LazySingleton.INSTANCE; + } + + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { + + static final StaffSelection INSTANCE = new StaffSelection(); + } + + /** + * Prompt the user for staff selection. + * + * @return the chosen option: 0 for UP, 1 for down, else -1 + */ + public int prompt () + { + dialog.setVisible(true); + + Object selectedValue = pane.getValue(); + + if (selectedValue == null) { + return CLOSED_OPTION; + } + + for (int counter = 0; counter < 2; counter++) { + if (options[counter].equals(selectedValue)) { + return counter; + } + } + + return CLOSED_OPTION; // Either closed or cancelled + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/TaskHistory.java b/src/main/org/audiveris/omr/sig/ui/TaskHistory.java index 311421f70..e2476e7e0 100644 --- a/src/main/org/audiveris/omr/sig/ui/TaskHistory.java +++ b/src/main/org/audiveris/omr/sig/ui/TaskHistory.java @@ -1,138 +1,135 @@ -//------------------------------------------------------------------------------------------------// -// // -// T a s k H i s t o r y // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -/** - * Class {@code TaskHistory} handles a history of UITaskList instances, with the - * ability to add, undo and redo. - *

                                                                                                                                        - * Within an UITaskList, all tasks are handled as a whole, to cope with dependent tasks. - * - * @author Hervé Bitteur - */ -class TaskHistory -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(TaskHistory.class); - - //~ Instance fields ---------------------------------------------------------------------------- - /** History of action sequences. */ - private final List sequences = new ArrayList(); - - /** Current position in history, always pointing to sequence just done. */ - private int cursor = -1; - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Register an action sequence. - * - * @param tasks one (or several related) task(s) - * @return the action sequence - */ - public UITaskList add (UITaskList seq) - { - sequences.add(cursor + 1, seq); - cursor++; - - // Delete trailing sequences if any - for (int i = cursor + 1; i < sequences.size(); i++) { - sequences.remove(i); - } - - return seq; - } - - /** - * Tell if a redo is possible. - * - * @return true if OK - */ - public boolean canRedo () - { - return cursor < (sequences.size() - 1); - } - - /** - * Tell if an undo is possible. - * - * @return true if OK - */ - public boolean canUndo () - { - return cursor >= 0; - } - - /** - * Clear history. - */ - public void clear () - { - sequences.clear(); - cursor = -1; - } - - /** - * Report the cancelled action sequence. - * - * @return the task sequence to redo - */ - public UITaskList toRedo () - { - UITaskList seq = sequences.get(cursor + 1); - cursor++; - - return seq; - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("TaskHistory{"); - sb.append("c:").append(cursor); - - sb.append(" ").append(sequences); - - sb.append("}"); - - return sb.toString(); - } - - /** - * Report the action sequence to cancel. - * - * @return the task sequence to undo - */ - public UITaskList toUndo () - { - UITaskList seq = sequences.get(cursor); - cursor--; - - return seq; - } -} +//------------------------------------------------------------------------------------------------// +// // +// T a s k H i s t o r y // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class {@code TaskHistory} handles a history of UITaskList instances, with the + * ability to add, undo and redo. + *

                                                                                                                                        + * Within an UITaskList, all tasks are handled as a whole, to cope with dependent tasks. + * + * @author Hervé Bitteur + */ +class TaskHistory +{ + + private static final Logger logger = LoggerFactory.getLogger(TaskHistory.class); + + /** History of action sequences. */ + private final List sequences = new ArrayList<>(); + + /** Current position in history, always pointing to sequence just done. */ + private int cursor = -1; + + /** + * Register an action sequence. + * + * @param tasks one (or several related) task(s) + * @return the action sequence + */ + public UITaskList add (UITaskList seq) + { + sequences.add(cursor + 1, seq); + cursor++; + + // Delete trailing sequences if any + for (int i = cursor + 1; i < sequences.size(); i++) { + sequences.remove(i); + } + + return seq; + } + + /** + * Tell if a redo is possible. + * + * @return true if OK + */ + public boolean canRedo () + { + return cursor < (sequences.size() - 1); + } + + /** + * Tell if an undo is possible. + * + * @return true if OK + */ + public boolean canUndo () + { + return cursor >= 0; + } + + /** + * Clear history. + */ + public void clear () + { + sequences.clear(); + cursor = -1; + } + + /** + * Report the cancelled action sequence. + * + * @return the task sequence to redo + */ + public UITaskList toRedo () + { + UITaskList seq = sequences.get(cursor + 1); + cursor++; + + return seq; + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("TaskHistory{"); + sb.append("c:").append(cursor); + + sb.append(" ").append(sequences); + + sb.append("}"); + + return sb.toString(); + } + + /** + * Report the action sequence to cancel. + * + * @return the task sequence to undo + */ + public UITaskList toUndo () + { + UITaskList seq = sequences.get(cursor); + cursor--; + + return seq; + } +} diff --git a/src/main/org/audiveris/omr/sig/ui/TextTask.java b/src/main/org/audiveris/omr/sig/ui/TextTask.java index 680b37774..4f036f323 100644 --- a/src/main/org/audiveris/omr/sig/ui/TextTask.java +++ b/src/main/org/audiveris/omr/sig/ui/TextTask.java @@ -1,54 +1,52 @@ -//------------------------------------------------------------------------------------------------// -// // -// T e x t T a s k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.Inter; - -/** - * Class {@code TextTask} acts on textual items (words and sentences). - * - * @author Hervé Bitteur - */ -public abstract class TextTask - extends UITask -{ - //~ Constructors ------------------------------------------------------------------------------- - - /** - * Creates a new {@code TextTask} object. - * - * @param sig the underlying sig - */ - public TextTask (SIGraph sig) - { - super(sig); - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Report the underlying inter (word or sentence) - * - * @return the underlying inter - */ - public abstract Inter getInter (); -} +//------------------------------------------------------------------------------------------------// +// // +// T e x t T a s k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.sig.SIGraph; +import org.audiveris.omr.sig.inter.Inter; + +/** + * Class {@code TextTask} acts on textual items (words and sentences). + * + * @author Hervé Bitteur + */ +public abstract class TextTask + extends UITask +{ + + /** + * Creates a new {@code TextTask} object. + * + * @param sig the underlying sig + */ + public TextTask (SIGraph sig) + { + super(sig); + } + + /** + * Report the underlying inter (word or sentence) + * + * @return the underlying inter + */ + public abstract Inter getInter (); +} diff --git a/src/main/org/audiveris/omr/sig/ui/UITask.java b/src/main/org/audiveris/omr/sig/ui/UITask.java index a964889d5..4577f3a4c 100644 --- a/src/main/org/audiveris/omr/sig/ui/UITask.java +++ b/src/main/org/audiveris/omr/sig/ui/UITask.java @@ -1,81 +1,75 @@ -//------------------------------------------------------------------------------------------------// -// // -// U I T a s k // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.sig.SIGraph; - -/** - * Class {@code UITask} is the basis for user interactive task - * - * @author Hervé Bitteur - */ -public abstract class UITask -{ - //~ Enumerations ------------------------------------------------------------------------------- - - /** Operation kind performed on a UITask. */ - public static enum OpKind - { - //~ Enumeration constant initializers ------------------------------------------------------ - - DO, - UNDO, - REDO; - } - - //~ Instance fields ---------------------------------------------------------------------------- - /** Underlying sheet. */ - protected final Sheet sheet; - - /** Underlying SIG. */ - protected final SIGraph sig; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code UITask} object. - * - * @param sig the underlying sig - */ - public UITask (SIGraph sig) - { - this.sig = sig; - sheet = sig.getSystem().getSheet(); - } - - //~ Methods ------------------------------------------------------------------------------------ - public SIGraph getSig () - { - return sig; - } - - public abstract void performDo (); - - public abstract void performUndo (); - - /** - * Report a name for task action. - * - * @return task name - */ - protected abstract String actionName (); -} +//------------------------------------------------------------------------------------------------// +// // +// U I T a s k // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.sig.ui; + +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.sig.SIGraph; + +/** + * Class {@code UITask} is the basis for user interactive task + * + * @author Hervé Bitteur + */ +public abstract class UITask +{ + + /** Operation kind performed on a UITask. */ + public static enum OpKind + { + DO, + UNDO, + REDO; + } + + /** Underlying sheet. */ + protected final Sheet sheet; + + /** Underlying SIG. */ + protected final SIGraph sig; + + /** + * Creates a new {@code UITask} object. + * + * @param sig the underlying sig + */ + public UITask (SIGraph sig) + { + this.sig = sig; + sheet = sig.getSystem().getSheet(); + } + + public SIGraph getSig () + { + return sig; + } + + public abstract void performDo (); + + public abstract void performUndo (); + + /** + * Report a name for task action. + * + * @return task name + */ + protected abstract String actionName (); +} diff --git a/src/main/org/audiveris/omr/sig/ui/UITaskList.java b/src/main/org/audiveris/omr/sig/ui/UITaskList.java index bd42a20a1..e5b8bb327 100644 --- a/src/main/org/audiveris/omr/sig/ui/UITaskList.java +++ b/src/main/org/audiveris/omr/sig/ui/UITaskList.java @@ -1,258 +1,251 @@ -//------------------------------------------------------------------------------------------------// -// // -// U I T a s k L i s t // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.sig.ui; - -import org.audiveris.omr.sig.SIGraph; -import org.audiveris.omr.sig.inter.Inter; -import org.audiveris.omr.sig.relation.Relation; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Set; - -/** - * Class {@code UITaskList} is a sequence of {@link UITask} instances, meant to - * be handled as a whole. - * - * @author Hervé Bitteur - */ -public class UITaskList -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(UITaskList.class); - - //~ Enumerations ------------------------------------------------------------------------------- - /** Possible options. */ - public static enum Option - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** User has validated the choice. */ - VALIDATED, - /** Measures are to be updated. */ - UPDATE_MEASURES; - } - - //~ Instance fields ---------------------------------------------------------------------------- - /** Sequence of related actions. */ - private final List list = new ArrayList(); - - /** Options for the actions list. */ - private final Set

                                                                                                                                        - * Sequence of steps:
                                                                                                                                        + * Sequence of steps: + *
                                                                                                                                        * step sequence - *

                                                                                                                                        */ package org.audiveris.omr.step; diff --git a/src/main/org/audiveris/omr/step/ui/StepMenu.java b/src/main/org/audiveris/omr/step/ui/StepMenu.java index 3f69f8e81..36ad53953 100644 --- a/src/main/org/audiveris/omr/step/ui/StepMenu.java +++ b/src/main/org/audiveris/omr/step/ui/StepMenu.java @@ -52,16 +52,12 @@ */ public class StepMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(StepMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** The concrete UI menu. */ private final JMenu menu; - //~ Constructors ------------------------------------------------------------------------------- /** * Generates the menu to be inserted in the application pull-down menus. * @@ -82,7 +78,6 @@ public StepMenu (JMenu menu) menu.addMenuListener(new MyMenuListener()); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getMenu // //---------// @@ -112,30 +107,50 @@ public final void updateMenu () } } - //~ Inner Classes ------------------------------------------------------------------------------ - //------------// - // StepAction // - //------------// + //----------------// + // MyMenuListener // + //----------------// /** - * Action to be performed when the related step item is selected. + * Class {@code MyMenuListener} is triggered when the whole sub-menu is entered. + * This is done with respect to currently displayed sheet. + * The steps already done are flagged as such. */ + private class MyMenuListener + extends AbstractMenuListener + { + + @Override + public void menuSelected (MenuEvent e) + { + SheetStub stub = StubsController.getCurrentStub(); + boolean isIdle = (stub != null) && (stub.getCurrentStep() == null); + + for (int i = 0; i < menu.getItemCount(); i++) { + JMenuItem menuItem = menu.getItem(i); + + // Adjust the status for each step + if (menuItem instanceof StepItem) { + StepItem item = (StepItem) menuItem; + item.displayState(stub, isIdle); + } + } + } + } + private static class StepAction extends AbstractAction { - //~ Instance fields ------------------------------------------------------------------------ // The related step final Step step; - //~ Constructors --------------------------------------------------------------------------- - public StepAction (Step step) + StepAction (Step step) { super(step.toString()); this.step = step; putValue(SHORT_DESCRIPTION, step.getDescription()); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { @@ -149,11 +164,12 @@ protected Void doInBackground () try { Step sofar = stub.getLatestStep(); - if ((sofar != null) & (sofar.compareTo(step) >= 0)) { + if ((sofar != null) && (sofar.compareTo(step) >= 0)) { int answer = JOptionPane.showConfirmDialog( OMR.gui.getFrame(), - "About to re-perform step " + step + " from scratch." - + "\nDo you confirm?", + "About to re-perform step " + step + + " from scratch." + + "\nDo you confirm?", "Redo confirmation", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); @@ -187,6 +203,13 @@ protected void finished () } }.execute(); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } //----------// @@ -198,14 +221,12 @@ protected void finished () private static class StepItem extends JCheckBoxMenuItem { - //~ Constructors --------------------------------------------------------------------------- - public StepItem (Step step) + StepItem (Step step) { super(new StepAction(step)); } - //~ Methods -------------------------------------------------------------------------------- public void displayState (SheetStub stub, boolean isIdle) { @@ -226,35 +247,4 @@ public void displayState (SheetStub stub, } } } - - //----------------// - // MyMenuListener // - //----------------// - /** - * Class {@code MyMenuListener} is triggered when the whole sub-menu is entered. - * This is done with respect to currently displayed sheet. - * The steps already done are flagged as such. - */ - private class MyMenuListener - extends AbstractMenuListener - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public void menuSelected (MenuEvent e) - { - SheetStub stub = StubsController.getCurrentStub(); - boolean isIdle = (stub != null) && (stub.getCurrentStep() == null); - - for (int i = 0; i < menu.getItemCount(); i++) { - JMenuItem menuItem = menu.getItem(i); - - // Adjust the status for each step - if (menuItem instanceof StepItem) { - StepItem item = (StepItem) menuItem; - item.displayState(stub, isIdle); - } - } - } - } } diff --git a/src/main/org/audiveris/omr/step/ui/StepMonitor.java b/src/main/org/audiveris/omr/step/ui/StepMonitor.java index 4d3f663bf..df3a0d5b0 100644 --- a/src/main/org/audiveris/omr/step/ui/StepMonitor.java +++ b/src/main/org/audiveris/omr/step/ui/StepMonitor.java @@ -43,20 +43,17 @@ */ public class StepMonitor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(StepMonitor.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Progress bar for actions performed on sheet. */ private final JProgressBar bar = new MyJProgressBar(); /** Total active actions. */ private int actives = 0; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a user monitor on step processing. * There is exactly one instance of this class (and no instance when running in batch mode) @@ -70,8 +67,6 @@ public StepMonitor () bar.setForeground(Colors.PROGRESS_BAR); } - //~ Methods ------------------------------------------------------------------------------------ - // //--------------// // getComponent // //--------------// @@ -96,8 +91,7 @@ public JProgressBar getComponent () public void notifyMsg (final String msg) { logger.debug("notifyMsg '{}'", msg); - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () @@ -107,6 +101,32 @@ public void run () }); } + //--------// + // setBar // + //--------// + /** + * Sets the progress bar to show a percentage. + * + * @param amount percentage, in decimal form, from 0.0 to 1.0 + */ + private void setBar (final double amount) + { + logger.debug("setBar amount:{}", amount); + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run () + { + int divisions = constants.divisions.getValue(); + bar.setMinimum(0); + bar.setMaximum(divisions); + + int val = (int) Math.round(divisions * amount); + bar.setValue(val); + } + }); + } + //------------------// // displayAnimation // //------------------// @@ -151,8 +171,7 @@ void animate () { if (!constants.useIndeterminate.isSet()) { logger.debug("animate"); - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () @@ -170,45 +189,16 @@ public void run () } } - //--------// - // setBar // - //--------// - /** - * Sets the progress bar to show a percentage. - * - * @param amount percentage, in decimal form, from 0.0 to 1.0 - */ - private void setBar (final double amount) - { - logger.debug("setBar amount:{}", amount); - SwingUtilities.invokeLater( - new Runnable() - { - @Override - public void run () - { - int divisions = constants.divisions.getValue(); - bar.setMinimum(0); - bar.setMaximum(divisions); - - int val = (int) Math.round(divisions * amount); - bar.setValue(val); - } - }); - } - - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer divisions = new Constant.Integer( "divisions", - 1000, + 1_000, "Number of divisions (amount of precision) of step monitor, minimum 10"); private final Ratio ratio = new Ratio( @@ -226,7 +216,6 @@ private static final class Constants private static class MyJProgressBar extends JProgressBar { - //~ Methods -------------------------------------------------------------------------------- @Override public void paintComponent (Graphics g) diff --git a/src/main/org/audiveris/omr/step/ui/StepMonitoring.java b/src/main/org/audiveris/omr/step/ui/StepMonitoring.java index d246aa693..9dc53edf5 100644 --- a/src/main/org/audiveris/omr/step/ui/StepMonitoring.java +++ b/src/main/org/audiveris/omr/step/ui/StepMonitoring.java @@ -38,14 +38,12 @@ */ public abstract class StepMonitoring { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(StepMonitoring.class); /** Related progress monitor when used in interactive mode. */ private static volatile StepMonitor monitor; - //~ Constructors ------------------------------------------------------------------------------- /** * Not meant to be instantiated. */ @@ -53,7 +51,6 @@ private StepMonitoring () { } - //~ Methods ------------------------------------------------------------------------------------ //---------// // animate // //---------// @@ -126,8 +123,7 @@ public static void notifyStep (final SheetStub stub, { if (monitor != null) { final boolean finished = stub.getCurrentStep() == null; - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () diff --git a/src/main/org/audiveris/omr/text/BlockScanner.java b/src/main/org/audiveris/omr/text/BlockScanner.java index 4226298ec..a6b7b9823 100644 --- a/src/main/org/audiveris/omr/text/BlockScanner.java +++ b/src/main/org/audiveris/omr/text/BlockScanner.java @@ -1,115 +1,109 @@ -//------------------------------------------------------------------------------------------------// -// // -// B l o c k S c a n n e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.text; - -import ij.process.ByteProcessor; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.sheet.Sheet; -import org.audiveris.omr.util.Navigable; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * Class {@code BlockScanner} launches the OCR on a buffer, typically a glyph buffer, - * to retrieve the TextLine instance(s) this buffer represents. - *

                                                                                                                                        - * As opposed to [@link SheetScanner}, here Tesseract is used in SINGLE_BLOCK layout mode, - * since the buffer, as complex as it can be with many lines and words, is considered as a single - * block of text. - *

                                                                                                                                        - * The raw OCR output will later be processed at system level by dedicated TextBuilder instances. - * - * @author Hervé Bitteur - */ -public class BlockScanner -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger(BlockScanner.class); - - //~ Instance fields ---------------------------------------------------------------------------- - /** Related sheet. */ - @Navigable(false) - private final Sheet sheet; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code GlyphScanner} object that can work on a glyph or a buffer. - * - * @param sheet underlying sheet - */ - public BlockScanner (Sheet sheet) - { - this.sheet = sheet; - } - - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // scanBuffer // - //------------// - /** - * Launch the OCR on the provided buffer, to retrieve the TextLine instance(s) with - * coordinates relative to buffer origin. - *

                                                                                                                                        - * Tesseract OCR generally gives better results if the processed image exhibits white pixels - * on the image contour, so here we (transparently) add a white margin around the buffer. - * - * @param buffer the ByteProcessor buffer - * @param language the probable language spec - * @param id an arbitrary id, used only when keeping the image on disk - * @return a list, not null but perhaps empty, of raw TextLine's with relative coordinates. - */ - public List scanBuffer (ByteProcessor buffer, - String language, - int id) - { - return OcrUtil.scan( - buffer.getBufferedImage(), - constants.whiteMarginAdded.getValue(), - OCR.LayoutMode.SINGLE_BLOCK, - language, - sheet.getScale().getInterline(), - sheet.getId() + "-b" + id); - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Integer whiteMarginAdded = new Constant.Integer( - "pixels", - 10, - "Margin of white pixels added around block image"); - } -} +//------------------------------------------------------------------------------------------------// +// // +// B l o c k S c a n n e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.text; + +import ij.process.ByteProcessor; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.sheet.Sheet; +import org.audiveris.omr.util.Navigable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Class {@code BlockScanner} launches the OCR on a buffer, typically a glyph buffer, + * to retrieve the TextLine instance(s) this buffer represents. + *

                                                                                                                                        + * As opposed to [@link SheetScanner}, here Tesseract is used in SINGLE_BLOCK layout mode, + * since the buffer, as complex as it can be with many lines and words, is considered as a single + * block of text. + *

                                                                                                                                        + * The raw OCR output will later be processed at system level by dedicated TextBuilder instances. + * + * @author Hervé Bitteur + */ +public class BlockScanner +{ + + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(BlockScanner.class); + + /** Related sheet. */ + @Navigable(false) + private final Sheet sheet; + + /** + * Creates a new {@code GlyphScanner} object that can work on a glyph or a buffer. + * + * @param sheet underlying sheet + */ + public BlockScanner (Sheet sheet) + { + this.sheet = sheet; + } + + //------------// + // scanBuffer // + //------------// + /** + * Launch the OCR on the provided buffer, to retrieve the TextLine instance(s) with + * coordinates relative to buffer origin. + *

                                                                                                                                        + * Tesseract OCR generally gives better results if the processed image exhibits white pixels + * on the image contour, so here we (transparently) add a white margin around the buffer. + * + * @param buffer the ByteProcessor buffer + * @param language the probable language spec + * @param id an arbitrary id, used only when keeping the image on disk + * @return a list, not null but perhaps empty, of raw TextLine's with relative coordinates. + */ + public List scanBuffer (ByteProcessor buffer, + String language, + int id) + { + return OcrUtil.scan( + buffer.getBufferedImage(), + constants.whiteMarginAdded.getValue(), + OCR.LayoutMode.SINGLE_BLOCK, + language, + sheet.getScale().getInterline(), + sheet.getId() + "-b" + id); + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Integer whiteMarginAdded = new Constant.Integer( + "pixels", + 10, + "Margin of white pixels added around block image"); + } +} diff --git a/src/main/org/audiveris/omr/text/FontInfo.java b/src/main/org/audiveris/omr/text/FontInfo.java index 125594fa5..55f9a6380 100644 --- a/src/main/org/audiveris/omr/text/FontInfo.java +++ b/src/main/org/audiveris/omr/text/FontInfo.java @@ -28,10 +28,11 @@ /** * Non-mutable font attributes (generally for a word). + * + * @author Hervé Bitteur */ public class FontInfo { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(FontInfo.class); @@ -41,7 +42,6 @@ public class FontInfo /** Separator in memo between attributes (if any) and point size. */ private static final char SEPARATOR = '-'; - //~ Instance fields ---------------------------------------------------------------------------- /** True if bold. */ public final boolean isBold; @@ -66,7 +66,6 @@ public class FontInfo /** Font name. */ public final String fontName; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new FontInfo object. * @@ -118,42 +117,6 @@ public FontInfo (FontInfo org, org.fontName); } - //~ Methods ------------------------------------------------------------------------------------ - // - //---------------// - // createDefault // - //---------------// - /** - * Create a default font using provided fontSize. - * - * @param fontSize the size to use - * @return the FontInfo instance - */ - public static FontInfo createDefault (int fontSize) - { - return new FontInfo(false, false, false, false, true, false, fontSize, "Serif"); - } - - //--------// - // decode // - //--------// - public static FontInfo decode (String str) - { - final int sep = str.indexOf(SEPARATOR); - final String sizeStr = (sep != -1) ? str.substring(sep + 1) : str; - final int size = Integer.decode(sizeStr); - - return new FontInfo( - str.indexOf('B') != -1, - str.indexOf('I') != -1, - str.indexOf('U') != -1, - str.indexOf('M') != -1, - str.indexOf('S') != -1, - str.indexOf('C') != -1, - size, - "generic"); - } - //----------// // getMnemo // //----------// @@ -218,14 +181,55 @@ public String toString () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //---------------// + // createDefault // + //---------------// + /** + * Create a default font using provided fontSize. + * + * @param fontSize the size to use + * @return the FontInfo instance + */ + public static FontInfo createDefault (int fontSize) + { + return new FontInfo(false, false, false, false, true, false, fontSize, "Serif"); + } + + //--------// + // decode // + //--------// + /** + * Decode a FontInfo out of the provided string. + * + * @param str input string + * @return decoded FontInfo + */ + public static FontInfo decode (String str) + { + final int sep = str.indexOf(SEPARATOR); + final String sizeStr = (sep != -1) ? str.substring(sep + 1) : str; + final int size = Integer.decode(sizeStr); + + return new FontInfo( + str.indexOf('B') != -1, + str.indexOf('I') != -1, + str.indexOf('U') != -1, + str.indexOf('M') != -1, + str.indexOf('S') != -1, + str.indexOf('C') != -1, + size, + "generic"); + } + //---------// // Adapter // //---------// + /** + * JAXB adapter for FontInfo. + */ public static class Adapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public String marshal (FontInfo info) diff --git a/src/main/org/audiveris/omr/text/Language.java b/src/main/org/audiveris/omr/text/Language.java index bf8c94622..fb344b96d 100644 --- a/src/main/org/audiveris/omr/text/Language.java +++ b/src/main/org/audiveris/omr/text/Language.java @@ -21,6 +21,7 @@ // package org.audiveris.omr.text; +import java.io.IOException; import org.audiveris.omr.WellKnowns; import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; @@ -59,35 +60,32 @@ */ public class Language { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(Language.class); - /** Languages file name. */ - private static final String LANG_FILE_NAME = "ISO639-3.xml"; - /** Separator in a specification. */ public static final String SEP_CHAR = "+"; /** Default language specification (such as deu+eng+fra). */ - public static final Param ocrDefaultLanguages = new ConstantBasedParam( - constants.defaultSpecification); + public static final Param ocrDefaultLanguages + = new ConstantBasedParam(constants.defaultSpecification); + + /** Languages file name. */ + private static final String LANG_FILE_NAME = "ISO639-3.xml"; /** Language used when specification is empty. */ private static final String NO_SPEC = "eng"; /** Collection of supported languages, lazily created. */ - private static SupportedLanguages supportedLanguages; + private static volatile SupportedLanguages supportedLanguages; - //~ Constructors ------------------------------------------------------------------------------- /** Not meant to be instantiated. */ private Language () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------------------// // getSupportedLanguages // //-----------------------// @@ -100,7 +98,6 @@ private static SupportedLanguages getSupportedLanguages () return supportedLanguages; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // ListModel // //-----------// @@ -110,7 +107,6 @@ private static SupportedLanguages getSupportedLanguages () public static class ListModel extends AbstractListModel { - //~ Methods -------------------------------------------------------------------------------- @Override public String getElementAt (int index) @@ -152,10 +148,9 @@ public String specOf (Collection list) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String defaultSpecification = new Constant.String( "deu+eng+fra", @@ -168,11 +163,9 @@ private static final class Constants private static class OcrDefaultLanguages extends StringParam { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String constant = constants.defaultSpecification; - //~ Methods -------------------------------------------------------------------------------- @Override public String getSourceValue () { @@ -230,37 +223,26 @@ public boolean setSpecific (String specific) */ private static class SupportedLanguages { - //~ Instance fields ------------------------------------------------------------------------ /** Map of language code -> language full name. */ - private final SortedMap codes = new TreeMap(); + private final SortedMap codes = new TreeMap<>(); /** Convenient sequence of codes, parallel to sorted map. */ private final List codesList; - //~ Constructors --------------------------------------------------------------------------- - public SupportedLanguages () + SupportedLanguages () { // Build the map of all possible codes Properties langNames = new Properties(); URI uri = UriUtil.toURI(WellKnowns.RES_URI, LANG_FILE_NAME); - try { - InputStream input = null; - - try { - input = uri.toURL().openStream(); - langNames.loadFromXML(input); + try (InputStream input = uri.toURL().openStream()) { + langNames.loadFromXML(input); - for (String code : langNames.stringPropertyNames()) { - codes.put(code, langNames.getProperty(code, code)); - } - } finally { - if (input != null) { - input.close(); - } + for (String code : langNames.stringPropertyNames()) { + codes.put(code, langNames.getProperty(code, code)); } - } catch (Throwable ex) { + } catch (IOException ex) { logger.error("Error loading " + uri, ex); } @@ -270,10 +252,9 @@ public SupportedLanguages () codes.keySet().retainAll(supported); // Create parallel list of codes - codesList = new ArrayList(codes.keySet()); + codesList = new ArrayList<>(codes.keySet()); } - //~ Methods -------------------------------------------------------------------------------- /** * Report the code out of a list item. * diff --git a/src/main/org/audiveris/omr/text/OCR.java b/src/main/org/audiveris/omr/text/OCR.java index 4709f8662..c943a2b4e 100644 --- a/src/main/org/audiveris/omr/text/OCR.java +++ b/src/main/org/audiveris/omr/text/OCR.java @@ -33,21 +33,19 @@ */ public interface OCR { - //~ Enumerations ------------------------------------------------------------------------------- + + /** Standard NO_OCR message: {@value}. */ + static String NO_OCR = "No OCR is available!"; /** Handling of image layout. */ enum LayoutMode { - //~ Enumeration constant initializers ------------------------------------------------------ - /** Automatic discovery of multi block layout */ MULTI_BLOCK, /** No layout processing, a single block is assumed */ SINGLE_BLOCK; } - //~ Methods ------------------------------------------------------------------------------------ - // /** * Report the set of supported language codes * @@ -56,18 +54,18 @@ enum LayoutMode Set getLanguages (); /** - * Report whether the OCR engine is available. + * Return OCR engine identification. * - * @return true if OCR is OK + * @return string containing the name and the version of the OCR engine. */ - boolean isAvailable (); + String identify (); /** - * Return OCR engine identification. + * Report whether the OCR engine is available. * - * @return string containing the name and the version of the OCR engine. + * @return true if OCR is OK */ - String identify(); + boolean isAvailable (); /** * Launch the recognition of the provided image, whose language is specified. @@ -80,7 +78,8 @@ enum LayoutMode * @param label an optional label related to the image, null otherwise. * This is meant for keeping track of the temporary image files. * @return a list of TextLine instances, or null. - * The coordinates of any returned TextLine are absolute coordinates thanks to the topLeft + * The coordinates of any returned TextLine are absolute coordinates thanks to the + * topLeft * parameter. */ List recognize (int interline, @@ -90,12 +89,33 @@ List recognize (int interline, LayoutMode layoutMode, String label); - //~ Inner Classes ------------------------------------------------------------------------------ /** * Exception used to signal that no OCR is actually available. */ static class UnavailableOcrException extends RuntimeException { + + /** + * Create a UnavailableOcrException. + * + * @param msg related message + */ + public UnavailableOcrException (String msg) + { + super(msg); + } + + /** + * Create a UnavailableOcrException. + * + * @param msg related message + * @param cause exception cause + */ + public UnavailableOcrException (String msg, + Throwable cause) + { + super(msg, cause); + } } } diff --git a/src/main/org/audiveris/omr/text/OcrUtil.java b/src/main/org/audiveris/omr/text/OcrUtil.java index 4668e1873..1ee8bed88 100644 --- a/src/main/org/audiveris/omr/text/OcrUtil.java +++ b/src/main/org/audiveris/omr/text/OcrUtil.java @@ -1,113 +1,110 @@ -//------------------------------------------------------------------------------------------------// -// // -// O c r U t i l // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.text; - -import org.audiveris.omr.text.OCR.LayoutMode; -import org.audiveris.omr.text.tesseract.TesseractOCR; - -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Point; -import java.awt.image.BufferedImage; -import java.util.List; - -/** - * Class {@code OcrUtil} gathers utility features related to OCR. - * - * @author Hervé Bitteur - */ -public abstract class OcrUtil -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - /** The related OCR. */ - private static final OCR ocr = TesseractOCR.getInstance(); - - //~ Constructors ------------------------------------------------------------------------------- - /** Not meant to be instantiated. */ - private OcrUtil () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------// - // getOcr // - //--------// - /** - * Report the related OCR engine, if one is available. - * - * @return the available OCR engine, or null - */ - public static OCR getOcr () - { - return ocr; - } - - //------// - // scan // - //------// - /** - * Scan the provided image for lines of text. - * - * @param image the provided image - * @param margin amount of white pixels added around the image (can be zero) - * @param layoutMode MULTI_BLOCK or SINGLE_BLOCK - * @param language language specification - * @param interline scaling interline - * @param label some label meant for debugging - * @return the raw lines of text found, with coordinates relative to image origin - */ - public static List scan (BufferedImage image, - int margin, - LayoutMode layoutMode, - String language, - int interline, - String label) - { - final int width = image.getWidth(); - final int height = image.getHeight(); - final Point origin = new Point(0, 0); - - // Add some white some white margin around the image? - final BufferedImage bi; - - if (margin > 0) { - bi = new BufferedImage( - width + (2 * margin), - height + (2 * margin), - BufferedImage.TYPE_BYTE_GRAY); - - // Background filled with white - Graphics g = bi.createGraphics(); - g.setColor(Color.WHITE); - g.fillRect(0, 0, bi.getWidth(), bi.getHeight()); - - // Draw image with margins shift - g.drawImage(image, margin, margin, null); - origin.translate(-margin, -margin); - } else { - bi = image; - } - - return ocr.recognize(interline, bi, origin, language, layoutMode, label); - } -} +//------------------------------------------------------------------------------------------------// +// // +// O c r U t i l // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.text; + +import org.audiveris.omr.text.OCR.LayoutMode; +import org.audiveris.omr.text.tesseract.TesseractOCR; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.util.List; + +/** + * Class {@code OcrUtil} gathers utility features related to OCR. + * + * @author Hervé Bitteur + */ +public abstract class OcrUtil +{ + + /** The related OCR. */ + private static final OCR ocr = TesseractOCR.getInstance(); + + /** Not meant to be instantiated. */ + private OcrUtil () + { + } + + //--------// + // getOcr // + //--------// + /** + * Report the related OCR engine, if one is available. + * + * @return the available OCR engine, or null + */ + public static OCR getOcr () + { + return ocr; + } + + //------// + // scan // + //------// + /** + * Scan the provided image for lines of text. + * + * @param image the provided image + * @param margin amount of white pixels added around the image (can be zero) + * @param layoutMode MULTI_BLOCK or SINGLE_BLOCK + * @param language language specification + * @param interline scaling interline + * @param label some label meant for debugging + * @return the raw lines of text found, with coordinates relative to image origin + */ + public static List scan (BufferedImage image, + int margin, + LayoutMode layoutMode, + String language, + int interline, + String label) + { + final int width = image.getWidth(); + final int height = image.getHeight(); + final Point origin = new Point(0, 0); + + // Add some white some white margin around the image? + final BufferedImage bi; + + if (margin > 0) { + bi = new BufferedImage( + width + (2 * margin), + height + (2 * margin), + BufferedImage.TYPE_BYTE_GRAY); + + // Background filled with white + Graphics g = bi.createGraphics(); + g.setColor(Color.WHITE); + g.fillRect(0, 0, bi.getWidth(), bi.getHeight()); + + // Draw image with margins shift + g.drawImage(image, margin, margin, null); + origin.translate(-margin, -margin); + } else { + bi = image; + } + + return ocr.recognize(interline, bi, origin, language, layoutMode, label); + } +} diff --git a/src/main/org/audiveris/omr/text/SheetScanner.java b/src/main/org/audiveris/omr/text/SheetScanner.java index fce762c64..116d88d31 100644 --- a/src/main/org/audiveris/omr/text/SheetScanner.java +++ b/src/main/org/audiveris/omr/text/SheetScanner.java @@ -78,20 +78,17 @@ */ public class SheetScanner { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(SheetScanner.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet. */ private final Sheet sheet; /** Buffer used by OCR. */ private ByteProcessor buffer; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code TextPageScanner} object. * @@ -102,7 +99,6 @@ public SheetScanner (Sheet sheet) this.sheet = sheet; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getBuffer // //-----------// @@ -171,16 +167,14 @@ private BufferedImage getCleanImage () if (constants.displayTexts.isSet() && (OMR.gui != null)) { sheet.getStub().getAssembly().addViewTab( "Texts", - new ScrollImageView( - sheet, - new ImageView(img) - { - @Override - protected void renderItems (Graphics2D g) - { - sheet.renderItems(g); // Apply registered sheet renderers - } - }), + new ScrollImageView(sheet, new ImageView(img) + { + @Override + protected void renderItems (Graphics2D g) + { + sheet.renderItems(g); // Apply registered sheet renderers + } + }), new BoardsPane(new PixelBoard(sheet))); } @@ -192,14 +186,12 @@ protected void renderItems (Graphics2D g) return img; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, @@ -236,12 +228,10 @@ private static final class Constants private static class TextsCleaner extends PageCleaner { - //~ Instance fields ------------------------------------------------------------------------ /** Scale-dependent parameters. */ private final Parameters params; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new {@code TextsCleaner} object. * @@ -249,15 +239,14 @@ private static class TextsCleaner * @param g graphics context on buffer * @param sheet related sheet */ - public TextsCleaner (ByteProcessor buffer, - Graphics2D g, - Sheet sheet) + TextsCleaner (ByteProcessor buffer, + Graphics2D g, + Sheet sheet) { super(buffer, g, sheet); params = new Parameters(sheet.getScale()); } - //~ Methods -------------------------------------------------------------------------------- //-------------// // eraseInters // //-------------// @@ -266,11 +255,11 @@ public TextsCleaner (ByteProcessor buffer, */ public void eraseInters () { - List cores = new ArrayList(); + List cores = new ArrayList<>(); for (SystemInfo system : sheet.getSystems()) { final SIGraph sig = system.getSig(); - final List erased = new ArrayList(); + final List erased = new ArrayList<>(); for (Inter inter : sig.vertexSet()) { if (!inter.isRemoved()) { @@ -333,8 +322,7 @@ public void visit (LedgerInter ledger) // Thicken the ledgerline 1 pixel above & 1 pixel below final Stroke oldStroke = g.getStroke(); final Glyph glyph = ledger.getGlyph(); - float thickness = (float) ledger.getGlyph() - .getMeanThickness(Orientation.HORIZONTAL); + float thickness = (float) ledger.getGlyph().getMeanThickness(Orientation.HORIZONTAL); thickness += 2; g.setStroke(new BasicStroke(thickness, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND)); glyph.renderLine(g); @@ -370,20 +358,17 @@ private void eraseBorderGlyphs (List glyphs, } } - //~ Inner Classes -------------------------------------------------------------------------- //------------// // Parameters // //------------// - private class Parameters + private static class Parameters { - //~ Instance fields -------------------------------------------------------------------- final int hMargin; final int vMargin; - //~ Constructors ----------------------------------------------------------------------- - public Parameters (Scale scale) + Parameters (Scale scale) { hMargin = scale.toPixels(constants.staffHorizontalMargin); vMargin = scale.toPixels(constants.staffVerticalMargin); diff --git a/src/main/org/audiveris/omr/text/TextBasedItem.java b/src/main/org/audiveris/omr/text/TextBasedItem.java index dc75c172a..16bb8fbf2 100644 --- a/src/main/org/audiveris/omr/text/TextBasedItem.java +++ b/src/main/org/audiveris/omr/text/TextBasedItem.java @@ -41,7 +41,6 @@ public abstract class TextBasedItem extends TextItem implements Vip { - //~ Instance fields ---------------------------------------------------------------------------- /** Baseline. */ private Line2D baseline; @@ -52,7 +51,6 @@ public abstract class TextBasedItem /** (Debug) flag this object as VIP. */ private boolean vip; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TextBasedItem object. * @@ -72,33 +70,6 @@ public TextBasedItem (Rectangle bounds, this.confidence = confidence; } - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // baselineOf // - //------------// - public static Line2D baselineOf (List items) - { - Point2D first = items.get(0).getBaseline().getP1(); - Point2D last = items.get(items.size() - 1).getBaseline().getP2(); - - return new Line2D.Double(first, last); - } - - //--------------// - // confidenceOf // - //--------------// - public static double confidenceOf (Collection items) - { - // Use average confidence - double total = 0; - - for (TextBasedItem item : items) { - total += item.getConfidence(); - } - - return total / items.size(); - } - //-------------// // getBaseline // //-------------// @@ -112,6 +83,19 @@ public Line2D getBaseline () return baseline; } + //-------------// + // setBaseline // + //-------------// + /** + * Assign the word baseline + * + * @param baseline the new item baseline + */ + public void setBaseline (Line2D baseline) + { + this.baseline = baseline; + } + //---------------// // getConfidence // //---------------// @@ -125,9 +109,27 @@ public Double getConfidence () return confidence; } + //---------------// + // setConfidence // + //---------------// + /** + * Assign the item confidence level + * + * @param confidence the confidence or null + */ + public void setConfidence (Double confidence) + { + this.confidence = confidence; + } + //-------------// // getLocation // //-------------// + /** + * Report the text starting location, if any. + * + * @return starting location or null + */ public Point getLocation () { Line2D bl = getBaseline(); @@ -148,33 +150,6 @@ public boolean isVip () return vip; } - // - //-------------// - // setBaseline // - //-------------// - /** - * Assign the word baseline - * - * @param baseline the new item baseline - */ - public void setBaseline (Line2D baseline) - { - this.baseline = baseline; - } - - //---------------// - // setConfidence // - //---------------// - /** - * Assign the item confidence level - * - * @param confidence the confidence or null - */ - public void setConfidence (Double confidence) - { - this.confidence = confidence; - } - //--------// // setVip // //--------// @@ -234,4 +209,42 @@ protected String internals () return sb.toString(); } + + //------------// + // baselineOf // + //------------// + /** + * Report the baseline defined by the provided sequence of items. + * + * @param items provided sequence + * @return baseline from first to last + */ + public static Line2D baselineOf (List items) + { + Point2D first = items.get(0).getBaseline().getP1(); + Point2D last = items.get(items.size() - 1).getBaseline().getP2(); + + return new Line2D.Double(first, last); + } + + //--------------// + // confidenceOf // + //--------------// + /** + * Report a global confidence value for the provided collection of test items. + * + * @param items provided items + * @return global confidence value + */ + public static double confidenceOf (Collection items) + { + // Use average confidence + double total = 0; + + for (TextBasedItem item : items) { + total += item.getConfidence(); + } + + return total / items.size(); + } } diff --git a/src/main/org/audiveris/omr/text/TextBuilder.java b/src/main/org/audiveris/omr/text/TextBuilder.java index 4bce569d1..05ea1a801 100644 --- a/src/main/org/audiveris/omr/text/TextBuilder.java +++ b/src/main/org/audiveris/omr/text/TextBuilder.java @@ -25,7 +25,6 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.glyph.BasicGlyph; import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.glyph.GlyphIndex; import org.audiveris.omr.glyph.dynamic.CompoundFactory; @@ -84,7 +83,8 @@ * Class {@code TextBuilder} works at system level, to provide features to check, build * and reorganize text items, including interacting with the OCR engine. *

                                                                                                                                        - * This builder can operate in 3 different modes:

                                                                                                                                          + * This builder can operate in 3 different modes: + *
                                                                                                                                            *
                                                                                                                                          1. Free mode: Engine mode, text role can be any role, determined by heuristics. * manualLyrics == null; *
                                                                                                                                          2. Manual as lyrics: Manual mode, for which text role is imposed as lyrics. @@ -97,22 +97,20 @@ */ public class TextBuilder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(TextBuilder.class); + /** Needed for font size computation. */ + protected static final FontRenderContext frc = new FontRenderContext(null, true, true); + /** Abnormal characters. */ private static final char[] ABNORMAL_CHARS = new char[]{'\\'}; /** Regexp for abnormal words. */ private static final Pattern ABNORMAL_WORDS = getAbnormalWords(); - /** Needed for font size computation. */ - protected static final FontRenderContext frc = new FontRenderContext(null, true, true); - - //~ Instance fields ---------------------------------------------------------------------------- /** Related system. */ @Navigable(false) private final SystemInfo system; @@ -128,15 +126,14 @@ public class TextBuilder private final Parameters params; /** Set of text lines. */ - private final Set textLines = new LinkedHashSet(); + private final Set textLines = new LinkedHashSet<>(); /** Processed sections. true/false */ - private final Set
                                                                                                                                            processedSections = new LinkedHashSet
                                                                                                                                            (); + private final Set
                                                                                                                                            processedSections = new LinkedHashSet<>(); /** Manual mode. */ private final Boolean manualLyrics; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TextBuilder object. * @@ -169,40 +166,6 @@ public TextBuilder (SystemInfo system, params = new Parameters(sheet.getScale(), true); } - //~ Methods ------------------------------------------------------------------------------------ - //----------------// - // isMainlyItalic // - //----------------// - /** - * Check whether the (majority of) line is in italic font. - * - * @param line the line to check - * @return true if mainly italics - */ - public static boolean isMainlyItalic (TextLine line) - { - int reliableWords = 0; - int italicWords = 0; - - for (TextWord word : line.getWords()) { - if ((word.getConfidence() >= constants.minConfidence.getValue()) - && (word.getLength() > 1)) { - reliableWords++; - - if (word.getFontInfo().isItalic) { - italicWords++; - } - } - } - - // Check for majority among reliable words - if (reliableWords != 0) { - return (italicWords * 2) >= reliableWords; - } else { - return false; - } - } - //--------------------// // retrieveGlyphLines // //--------------------// @@ -253,7 +216,7 @@ public void retrieveSystemLines (ByteProcessor buffer, StopWatch watch = new StopWatch("Texts retrieveLines system#" + system.getId()); watch.start("Pickup system lines"); - List systemLines = new ArrayList(); + List systemLines = new ArrayList<>(); // We pick up the words that are contained by system area // Beware: a text located between two systems must be deep copied to each system! @@ -347,7 +310,7 @@ private String checkValidity (TextLine line) { // Discard really invalid words final double lowConf = constants.lowConfidence.getValue(); - final List toRemove = new ArrayList(); + final List toRemove = new ArrayList<>(); for (TextWord word : line.getWords()) { if (word.getConfidence() < lowConf) { @@ -513,8 +476,7 @@ private void createInters () for (TextLine line : textLines) { final TextRole role = line.getRole(); final SentenceInter sentence = (role == TextRole.Lyrics) ? LyricLineInter.create(line) - : ((role == TextRole.ChordName) - ? ChordNameInter.create(line) + : ((role == TextRole.ChordName) ? ChordNameInter.create(line) : SentenceInter.create(line)); // Related staff (can still be modified later) @@ -595,25 +557,6 @@ private TextWord findNewWord (TextWord oldWord, return null; } - //------------------// - // getAbnormalWords // - //------------------// - /** - * Compile the provided regexp to detect abnormal words - * - * @return the pattern for abnormal words, if successful - */ - private static Pattern getAbnormalWords () - { - try { - return Pattern.compile(constants.abnormalWordRegexp.getValue()); - } catch (PatternSyntaxException pse) { - logger.warn("Error in regexp for abnormal words", pse); - - return null; - } - } - //-----------------// // getDeskewedCore // //-----------------// @@ -670,7 +613,7 @@ private List
                                                                                                                                            getSections (ByteProcessor buffer, List lines) { SectionFactory factory = new SectionFactory(VERTICAL, JunctionRatioPolicy.DEFAULT); - List
                                                                                                                                            allSections = new ArrayList
                                                                                                                                            (); + List
                                                                                                                                            allSections = new ArrayList<>(); for (TextLine line : lines) { for (TextWord word : line.getWords()) { @@ -699,7 +642,7 @@ private List getSubWords (TextWord word, TextLine line, WordScanner scanner) { - final List subWords = new ArrayList(); + final List subWords = new ArrayList<>(); final int contentLength = word.getValue().length(); while (scanner.hasNext()) { @@ -851,21 +794,14 @@ private void mapGlyphs (List lines, final int dy = (offset != null) ? offset.y : 0; final GlyphIndex glyphIndex = sheet.getGlyphIndex(); final int interline = sheet.getInterline(); - final CompoundConstructor constructor = new CompoundConstructor() - { - @Override - public SectionCompound newInstance () - { - return new SectionCompound(interline); - } - }; + final CompoundConstructor constructor = new SectionCompound.Constructor(interline); for (TextLine line : lines) { logger.debug(" mapping {}", line); // Browse all words, starting by shorter ones - List toRemove = new ArrayList(); - List sortedWords = new ArrayList(line.getWords()); + List toRemove = new ArrayList<>(); + List sortedWords = new ArrayList<>(line.getWords()); Collections.sort(sortedWords, TextWord.bySize); for (TextWord word : sortedWords) { @@ -881,7 +817,7 @@ public SectionCompound newInstance () constructor); Glyph rel = compound.toGlyph(null); Glyph wordGlyph = glyphIndex.registerOriginal( - new BasicGlyph(rel.getLeft() + dx, rel.getTop() + dy, rel.getRunTable())); + new Glyph(rel.getLeft() + dx, rel.getTop() + dy, rel.getRunTable())); // Link TextWord -> Glyph word.setGlyph(wordGlyph); @@ -953,7 +889,7 @@ private TextLine mergeChunks (List chunks) */ private TextLine mergeLines (List lines) { - List words = new ArrayList(); + List words = new ArrayList<>(); for (TextLine line : lines) { line.setProcessed(true); @@ -978,10 +914,10 @@ private List mergeLyricLines (List oldLyrics) { logger.debug("mergeLyricsLines"); - List newLyrics = new ArrayList(); + List newLyrics = new ArrayList<>(); Collections.sort(oldLyrics, TextLine.byOrdinate(skew)); - List chunks = new ArrayList(); + List chunks = new ArrayList<>(); double lastY = 0; for (TextLine line : oldLyrics) { @@ -1071,7 +1007,7 @@ private List mergeStandardLines (List oldStandards) candidate.setProcessed(true); candidate = head; - break HeadsLoop; + break; } } } @@ -1079,7 +1015,7 @@ private List mergeStandardLines (List oldStandards) } // Remove unavailable lines - List newStandards = new ArrayList(); + List newStandards = new ArrayList<>(); for (TextLine line : oldStandards) { if (!line.isProcessed()) { @@ -1099,8 +1035,8 @@ private void mergeStandardWords (TextLine line) ///logger.debug(" mergeStandardWords for {}", line); final int minWordDx = (int) Math.rint( line.getMeanFont().pointsize * params.minWordDxFontRatio); - List toAdd = new ArrayList(); - List toRemove = new ArrayList(); + List toAdd = new ArrayList<>(); + List toRemove = new ArrayList<>(); TextWord prevWord = null; for (TextWord word : line.getWords()) { @@ -1130,7 +1066,7 @@ private void mergeStandardWords (TextLine line) if (!toAdd.isEmpty()) { // No use to add & remove the same words - List common = new ArrayList(toAdd); + List common = new ArrayList<>(toAdd); common.retainAll(toRemove); toAdd.removeAll(common); toRemove.removeAll(common); @@ -1158,7 +1094,7 @@ private void numberLyricLines () return; } - List lines = new ArrayList(); + List lines = new ArrayList<>(); for (Inter inter : lyricInters) { lines.add((LyricLineInter) inter); @@ -1198,7 +1134,7 @@ private List purgeInvalidLines (String kind, { logger.debug("purgeInvalidLines for {}", kind); - List newLines = new ArrayList(); + List newLines = new ArrayList<>(); for (TextLine line : lines) { String reason = checkValidity(line); @@ -1242,8 +1178,8 @@ private List recomposeLines (Collection rawLines) logger.debug("System#{} recomposeLines", system.getId()); // Separate lyrics and standard lines, based on their roles - List standards = new ArrayList(); - List lyrics = new ArrayList(); + List standards = new ArrayList<>(); + List lyrics = new ArrayList<>(); separatePopulations(rawLines, standards, lyrics); // Process lyrics @@ -1283,7 +1219,7 @@ private List recomposeLines (Collection rawLines) } // Gather and sort all lines (standard & lyrics) - List allLines = new ArrayList(); + List allLines = new ArrayList<>(); allLines.addAll(lyrics); allLines.addAll(standards); Collections.sort(allLines, TextLine.byOrdinate(skew)); @@ -1321,22 +1257,16 @@ private SortedSet
                                                                                                                                            retrieveSections (List chars, Collection
                                                                                                                                            allSections, Point offset) { - final SortedSet
                                                                                                                                            wordSections = new TreeSet
                                                                                                                                            (Section.byFullAbscissa); - final CompoundConstructor constructor = new CompoundConstructor() - { - @Override - public SectionCompound newInstance () - { - return new SectionCompound(sheet.getInterline()); - } - }; + final SortedSet
                                                                                                                                            wordSections = new TreeSet<>(Section.byFullAbscissa); + final CompoundConstructor constructor = new SectionCompound.Constructor( + sheet.getInterline()); final int dx = (offset != null) ? offset.x : 0; final int dy = (offset != null) ? offset.y : 0; for (TextChar charDesc : chars) { final Rectangle charBox = charDesc.getBounds(); - final SortedSet
                                                                                                                                            charSections = new TreeSet
                                                                                                                                            (Section.byFullAbscissa); + final SortedSet
                                                                                                                                            charSections = new TreeSet<>(Section.byFullAbscissa); for (Section section : allSections) { // Do we contain a section not (yet) assigned? @@ -1352,7 +1282,7 @@ public SectionCompound newInstance () // Register char underlying glyph SectionCompound compound = CompoundFactory.buildCompound(charSections, constructor); Glyph relGlyph = compound.toGlyph(null); - Glyph absGlyph = new BasicGlyph( + Glyph absGlyph = new Glyph( relGlyph.getLeft() + dx, relGlyph.getTop() + dy, relGlyph.getRunTable()); @@ -1427,7 +1357,7 @@ private List splitStandardLines (List oldStandards) Collections.sort(oldStandards, TextLine.byOrdinate(skew)); - List newStandards = new ArrayList(); + List newStandards = new ArrayList<>(); for (TextLine line : oldStandards) { final int maxAbscissaGap = getWordGap(line); // TODO: should gap depend on font size? @@ -1505,8 +1435,8 @@ private void splitWords (Collection words, TextLine line) { // To avoid concurrent modification errors - Collection toAdd = new ArrayList(); - Collection toRemove = new ArrayList(); + Collection toAdd = new ArrayList<>(); + Collection toRemove = new ArrayList<>(); for (TextWord word : words) { List subWords = null; // Results of split @@ -1555,7 +1485,7 @@ private void splitWords (Collection words, word.getChars())); // } - if ((subWords != null) && !subWords.isEmpty()) { + if (!subWords.isEmpty()) { toRemove.add(word); toAdd.addAll(subWords); } @@ -1569,14 +1499,64 @@ private void splitWords (Collection words, } } - //~ Inner Classes ------------------------------------------------------------------------------ + //----------------// + // isMainlyItalic // + //----------------// + /** + * Check whether the (majority of) line is in italic font. + * + * @param line the line to check + * @return true if mainly italics + */ + public static boolean isMainlyItalic (TextLine line) + { + int reliableWords = 0; + int italicWords = 0; + + for (TextWord word : line.getWords()) { + if ((word.getConfidence() >= constants.minConfidence.getValue()) && (word + .getLength() > 1)) { + reliableWords++; + + if (word.getFontInfo().isItalic) { + italicWords++; + } + } + } + + // Check for majority among reliable words + if (reliableWords != 0) { + return (italicWords * 2) >= reliableWords; + } else { + return false; + } + } + + //------------------// + // getAbnormalWords // + //------------------// + /** + * Compile the provided regexp to detect abnormal words + * + * @return the pattern for abnormal words, if successful + */ + private static Pattern getAbnormalWords () + { + try { + return Pattern.compile(constants.abnormalWordRegexp.getValue()); + } catch (PatternSyntaxException pse) { + logger.warn("Error in regexp for abnormal words", pse); + + return null; + } + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printWatch = new Constant.Boolean( false, @@ -1654,7 +1634,6 @@ private static final class Constants //------------// private static class Parameters { - //~ Instance fields ------------------------------------------------------------------------ final int minFontSize; @@ -1682,9 +1661,8 @@ private static class Parameters final double maxDiagRatio; - //~ Constructors --------------------------------------------------------------------------- - public Parameters (Scale scale, - boolean isManual) + Parameters (Scale scale, + boolean isManual) { // TODO: check all these constant for specific manual work... minFontSize = scale.toPixels(constants.minFontSize); diff --git a/src/main/org/audiveris/omr/text/TextChar.java b/src/main/org/audiveris/omr/text/TextChar.java index 8d8970505..716bd9096 100644 --- a/src/main/org/audiveris/omr/text/TextChar.java +++ b/src/main/org/audiveris/omr/text/TextChar.java @@ -31,7 +31,6 @@ public class TextChar extends TextItem { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TextChar object. @@ -45,7 +44,6 @@ public TextChar (Rectangle bounds, super(bounds, value); } - //~ Methods ------------------------------------------------------------------------------------ @Override public void setValue (String value) { diff --git a/src/main/org/audiveris/omr/text/TextItem.java b/src/main/org/audiveris/omr/text/TextItem.java index a5a25ab01..e6bf3fa3a 100644 --- a/src/main/org/audiveris/omr/text/TextItem.java +++ b/src/main/org/audiveris/omr/text/TextItem.java @@ -32,7 +32,6 @@ */ public abstract class TextItem { - //~ Instance fields ---------------------------------------------------------------------------- /** Item bounds. */ private Rectangle bounds; @@ -40,8 +39,6 @@ public abstract class TextItem /** Item value. */ private String value; - //~ Constructors ------------------------------------------------------------------------------- - // //----------// // TextItem // //----------// @@ -61,31 +58,6 @@ public TextItem (Rectangle bounds, this.value = value; } - //~ Methods ------------------------------------------------------------------------------------ - //----------// - // boundsOf // - //----------// - /** - * Compute the bounding box of a collection of TextItem instances. - * - * @param items the provided collection of TextItem instances - * @return the global bounding box - */ - public static Rectangle boundsOf (Collection items) - { - Rectangle bounds = null; - - for (TextItem item : items) { - if (bounds == null) { - bounds = item.getBounds(); - } else { - bounds.add(item.getBounds()); - } - } - - return bounds; - } - //-----------// // getBounds // //-----------// @@ -103,6 +75,19 @@ public Rectangle getBounds () } } + //-----------// + // setBounds // + //-----------// + /** + * Set a new bounding box of the item. + * + * @param bounds the new bounding box + */ + public void setBounds (Rectangle bounds) + { + this.bounds = bounds; + } + //----------// // getValue // //----------// @@ -116,17 +101,17 @@ public String getValue () return value; } - //-----------// - // setBounds // - //-----------// + //----------// + // setValue // + //----------// /** - * Set a new bounding box of the item. + * Modify the item value. * - * @param bounds the new bounding box + * @param value the new item value */ - public void setBounds (Rectangle bounds) + protected void setValue (String value) { - this.bounds = bounds; + this.value = value; } //----------// @@ -162,24 +147,6 @@ public void translate (int dx, } } - //---------// - // valueOf // - //---------// - public static String valueOf (List items) - { - StringBuilder sb = new StringBuilder(); - - for (TextItem item : items) { - if (sb.length() > 0) { - sb.append(" "); - } - - sb.append(item.getValue()); - } - - return sb.toString(); - } - //-----------// // internals // //-----------// @@ -208,15 +175,50 @@ protected String internals () } //----------// - // setValue // + // boundsOf // //----------// /** - * Modify the item value. + * Compute the bounding box of a collection of TextItem instances. * - * @param value the new item value + * @param items the provided collection of TextItem instances + * @return the global bounding box */ - protected void setValue (String value) + public static Rectangle boundsOf (Collection items) { - this.value = value; + Rectangle bounds = null; + + for (TextItem item : items) { + if (bounds == null) { + bounds = item.getBounds(); + } else { + bounds.add(item.getBounds()); + } + } + + return bounds; + } + + //---------// + // valueOf // + //---------// + /** + * Report the string content of a sequence of TextItem. + * + * @param items provided sequence + * @return string value + */ + public static String valueOf (List items) + { + StringBuilder sb = new StringBuilder(); + + for (TextItem item : items) { + if (sb.length() > 0) { + sb.append(" "); + } + + sb.append(item.getValue()); + } + + return sb.toString(); } } diff --git a/src/main/org/audiveris/omr/text/TextLine.java b/src/main/org/audiveris/omr/text/TextLine.java index 0ee802917..48772d49f 100644 --- a/src/main/org/audiveris/omr/text/TextLine.java +++ b/src/main/org/audiveris/omr/text/TextLine.java @@ -45,14 +45,11 @@ public class TextLine extends TextBasedItem { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TextLine.class); - //~ Instance fields ---------------------------------------------------------------------------- - // /** Words that compose this line. */ - private final List words = new ArrayList(); + private final List words = new ArrayList<>(); /** Average font for the line. */ private FontInfo meanFont; @@ -63,7 +60,6 @@ public class TextLine /** Temporary processed flag. */ private boolean processed; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TextLine object from a sequence of words. * @@ -89,7 +85,6 @@ public TextLine () super(null, null, null, null); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // addWords // //----------// @@ -113,7 +108,6 @@ public void addWords (Collection words) } } - // //------------// // appendWord // //------------// @@ -129,48 +123,6 @@ public void appendWord (TextWord word) invalidateCache(); } - /** - * Give a Line comparator by de-skewed abscissa. - * - * @param skew the global sheet skew - * @return the skew-based abscissa comparator - */ - public static Comparator byAbscissa (final Skew skew) - { - return new Comparator() - { - @Override - public int compare (TextLine line1, - TextLine line2) - { - return Double.compare( - line1.getDskOrigin(skew).getX(), - line2.getDskOrigin(skew).getX()); - } - }; - } - - /** - * Give a Line comparator by de-skewed ordinate. - * - * @param skew the global sheet skew - * @return the skew-based ordinate comparator - */ - public static Comparator byOrdinate (final Skew skew) - { - return new Comparator() - { - @Override - public int compare (TextLine line1, - TextLine line2) - { - return Double.compare( - line1.getDskOrigin(skew).getY(), - line2.getDskOrigin(skew).getY()); - } - }; - } - //------// // dump // //------// @@ -236,7 +188,7 @@ public Rectangle getBounds () */ public List getChars () { - List chars = new ArrayList(); + List chars = new ArrayList<>(); for (TextWord word : words) { chars.addAll(word.getChars()); @@ -393,6 +345,19 @@ public TextRole getRole () return role; } + //---------// + // setRole // + //---------// + /** + * Assign role information. + * + * @param role the role to set + */ + public void setRole (TextRole role) + { + this.role = role; + } + //----------// // getValue // //----------// @@ -434,7 +399,7 @@ public String getValue () */ public List getWordGlyphs () { - List glyphs = new ArrayList(words.size()); + List glyphs = new ArrayList<>(words.size()); for (TextWord word : words) { Glyph glyph = word.getGlyph(); @@ -491,11 +456,29 @@ public boolean isLyrics () //-------------// // isProcessed // //-------------// + /** + * Tell whether this line has already beet processed. + * + * @return true if so + */ public boolean isProcessed () { return processed; } + //--------------// + // setProcessed // + //--------------// + /** + * Set the processed flag for this line + * + * @param processed true if processed + */ + public void setProcessed (boolean processed) + { + this.processed = processed; + } + //-------------// // removeWords // //-------------// @@ -512,27 +495,6 @@ public void removeWords (Collection words) } } - //--------------// - // setProcessed // - //--------------// - public void setProcessed (boolean processed) - { - this.processed = processed; - } - - //---------// - // setRole // - //---------// - /** - * Assign role information. - * - * @param role the role to set - */ - public void setRole (TextRole role) - { - this.role = role; - } - //-----------// // translate // //-----------// @@ -583,4 +545,46 @@ private void invalidateCache () role = null; meanFont = null; } + + /** + * Give a Line comparator by de-skewed abscissa. + * + * @param skew the global sheet skew + * @return the skew-based abscissa comparator + */ + public static Comparator byAbscissa (final Skew skew) + { + return new Comparator() + { + @Override + public int compare (TextLine line1, + TextLine line2) + { + return Double.compare( + line1.getDskOrigin(skew).getX(), + line2.getDskOrigin(skew).getX()); + } + }; + } + + /** + * Give a Line comparator by de-skewed ordinate. + * + * @param skew the global sheet skew + * @return the skew-based ordinate comparator + */ + public static Comparator byOrdinate (final Skew skew) + { + return new Comparator() + { + @Override + public int compare (TextLine line1, + TextLine line2) + { + return Double.compare( + line1.getDskOrigin(skew).getY(), + line2.getDskOrigin(skew).getY()); + } + }; + } } diff --git a/src/main/org/audiveris/omr/text/TextRole.java b/src/main/org/audiveris/omr/text/TextRole.java index 0645ce489..6043c025b 100644 --- a/src/main/org/audiveris/omr/text/TextRole.java +++ b/src/main/org/audiveris/omr/text/TextRole.java @@ -89,7 +89,7 @@ public enum TextRole public boolean isCreator () { return (this == CreatorArranger) || (this == CreatorComposer) - || ((this == CreatorLyricist) || (this == Creator)); + || ((this == CreatorLyricist) || (this == Creator)); } //-----------// @@ -182,8 +182,9 @@ public static TextRole guessRole (TextLine line, logger.debug( "{} firstSystem={} lastSystem={} systemPosition={}" - + " partPosition={} closeToStaff={} leftOfStaves={}" - + " pageCentered={} rightAligned={} shortSentence={}" + " highText={10}", + + " partPosition={} closeToStaff={} leftOfStaves={}" + + " pageCentered={} rightAligned={} shortSentence={}" + + " highText={}", box, firstSystem, lastSystem, @@ -239,13 +240,13 @@ public static TextRole guessRole (TextLine line, if (leftOfStaves) { return PartName; - } else if (lyricsAllowed - && (switches.getValue(Switch.lyrics) - || switches.getValue(Switch.lyricsAboveStaff)) - && ((partPosition == StaffPosition.BELOW_STAVES) - || ((partPosition == StaffPosition.ABOVE_STAVES) - && switches.getValue(Switch.lyricsAboveStaff))) - && !isMainlyItalic) { + } else if (lyricsAllowed && (switches.getValue(Switch.lyrics) || switches.getValue( + Switch.lyricsAboveStaff)) + && ((partPosition == StaffPosition.BELOW_STAVES) + || ((partPosition == StaffPosition.ABOVE_STAVES) + && switches.getValue( + Switch.lyricsAboveStaff))) + && !isMainlyItalic) { return Lyrics; } else if (!tinySentence) { return Direction; @@ -266,11 +267,10 @@ public static TextRole guessRole (TextLine line, } if (part.getStaves().size() == 1) { - if (lyricsAllowed - && (switches.getValue(Switch.lyrics) - || switches.getValue(Switch.lyricsAboveStaff)) - && (partPosition == StaffPosition.BELOW_STAVES) - && !isMainlyItalic) { + if (lyricsAllowed && (switches.getValue(Switch.lyrics) || switches.getValue( + Switch.lyricsAboveStaff)) + && (partPosition == StaffPosition.BELOW_STAVES) + && !isMainlyItalic) { return Lyrics; } } @@ -283,7 +283,7 @@ public static TextRole guessRole (TextLine line, //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { diff --git a/src/main/org/audiveris/omr/text/TextWord.java b/src/main/org/audiveris/omr/text/TextWord.java index 8017fe3e7..8f526183c 100644 --- a/src/main/org/audiveris/omr/text/TextWord.java +++ b/src/main/org/audiveris/omr/text/TextWord.java @@ -44,7 +44,6 @@ public class TextWord extends TextBasedItem { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TextWord.class); @@ -70,8 +69,6 @@ public int compare (TextWord o1, } }; - //~ Instance fields ---------------------------------------------------------------------------- - // /** Containing TextLine. */ @Navigable(false) private TextLine textLine; @@ -80,7 +77,7 @@ public int compare (TextWord o1, private final FontInfo fontInfo; /** Chars that compose this word. */ - private final List chars = new ArrayList(); + private final List chars = new ArrayList<>(); /** Underlying glyph, if known. */ private Glyph glyph; @@ -88,8 +85,6 @@ public int compare (TextWord o1, /** Precise font size, lazily computed. */ private Float preciseFontSize; - //~ Constructors ------------------------------------------------------------------------------- - // //----------// // TextWord // //----------// @@ -148,69 +143,6 @@ public TextWord (Rectangle bounds, this.fontInfo = new FontInfo(fontInfo, (int) Math.rint(size)); } - //~ Methods ------------------------------------------------------------------------------------ - //------------------// - // createManualWord // - //------------------// - /** - * Create a TextWord instance manually, out of a given glyph and value. - * TODO: Perhaps we could improve the baseline, according to the precise string value provided. - * - * @param glyph the underlying glyph - * @param value the provided string value - * @return the TextWord created - */ - public static TextWord createManualWord (Glyph glyph, - String value) - { - Rectangle box = glyph.getBounds(); - int fontSize = (int) Math.rint( - TextFont.computeFontSize(value, FontInfo.DEFAULT, box.getSize())); - TextWord word = new TextWord( - box, - value, - new Line2D.Double(box.x, box.y + box.height, box.x + box.width, box.y + box.height), - 1.0, // Confidence - FontInfo.createDefault(fontSize), - null); - - word.setGlyph(glyph); - - return word; - } - - //---------// - // mergeOf // - //---------// - /** - * Return a word which results from the merge of the provided words. - * - * @param words the words to merge - * @return the compound word - */ - public static TextWord mergeOf (TextWord... words) - { - List chars = new ArrayList(); - StringBuilder sb = new StringBuilder(); - - for (TextWord word : words) { - chars.addAll(word.getChars()); - sb.append(word.getValue()); - } - - // Use font info of first word. What else? - FontInfo fontInfo = words[0].getFontInfo(); - TextLine line = words[0].textLine; - - return new TextWord( - baselineOf(Arrays.asList(words)), - sb.toString(), - fontInfo, - confidenceOf(Arrays.asList(words)), - chars, - line); - } - //---------// // addChar // //---------// @@ -302,9 +234,31 @@ public Glyph getGlyph () return glyph; } + //----------// + // setGlyph // + //----------// + /** + * Assign the underlying glyph. + * + * @param glyph the glyph to set + */ + public void setGlyph (Glyph glyph) + { + this.glyph = glyph; + + if (glyph.isVip()) { + setVip(true); + } + } + //------------------// // getInternalValue // //------------------// + /** + * Report the word content. + * + * @return text value + */ public String getInternalValue () { return super.getValue(); @@ -329,7 +283,8 @@ public int getLength () * word bounds. *

                                                                                                                                            * The size appears to be a bit larger than OCR detected side, by a factor in the range 1.1 - - * 1.2. TODO: To be improved, using font attributes for better font selection

                                                                                                                                            + * 1.2. TODO: To be improved, using font attributes for better font selection + *

                                                                                                                                            * * @return the computed font size */ @@ -342,50 +297,6 @@ public float getPreciseFontSize () return preciseFontSize; } - //-------------// - // getTextLine // - //-------------// - /** - * Report the containing TextLine instance - * - * @return the containing line - */ - public TextLine getTextLine () - { - return textLine; - } - - //----------// - // getValue // - //----------// - /** - * Overridden to use glyph manual value if any or fall back using initial value. - * - * @return the value to be used - */ - @Override - public String getValue () - { - return getInternalValue(); - } - - //----------// - // setGlyph // - //----------// - /** - * Assign the underlying glyph. - * - * @param glyph the glyph to set - */ - public void setGlyph (Glyph glyph) - { - this.glyph = glyph; - - if (glyph.isVip()) { - setVip(true); - } - } - //--------------------// // setPreciseFontSize // //--------------------// @@ -400,6 +311,19 @@ public void setPreciseFontSize (Float preciseFontSize) this.preciseFontSize = preciseFontSize; } + //-------------// + // getTextLine // + //-------------// + /** + * Report the containing TextLine instance + * + * @return the containing line + */ + public TextLine getTextLine () + { + return textLine; + } + //-------------// // setTextLine // //-------------// @@ -417,6 +341,20 @@ public void setTextLine (TextLine textLine) } } + //----------// + // getValue // + //----------// + /** + * Overridden to use glyph manual value if any or fall back using initial value. + * + * @return the value to be used + */ + @Override + public String getValue () + { + return getInternalValue(); + } + //-----------// // translate // //-----------// @@ -460,4 +398,64 @@ protected String internals () return sb.toString(); } + + //------------------// + // createManualWord // + //------------------// + /** + * Create a TextWord instance manually, out of a given glyph and value. + *

                                                                                                                                            + * TODO: Perhaps we could improve the baseline, according to the precise string value provided. + * + * @param glyph the underlying glyph + * @param value the provided string value + * @return the TextWord created + */ + public static TextWord createManualWord (Glyph glyph, + String value) + { + Rectangle box = glyph.getBounds(); + int fontSize = (int) Math.rint( + TextFont.computeFontSize(value, FontInfo.DEFAULT, box.getSize())); + TextWord word = new TextWord( + box, + value, + new Line2D.Double(box.x, box.y + box.height, box.x + box.width, box.y + box.height), + 1.0, // Confidence + FontInfo.createDefault(fontSize), + null); + + word.setGlyph(glyph); + + return word; + } + + //---------// + // mergeOf // + //---------// + /** + * Return a word which results from the merge of the provided words. + * + * @param words the words to merge + * @return the compound word + */ + public static TextWord mergeOf (TextWord... words) + { + List chars = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + for (TextWord word : words) { + chars.addAll(word.getChars()); + sb.append(word.getValue()); + } + // Use font info of first word. What else? + FontInfo fontInfo = words[0].getFontInfo(); + TextLine line = words[0].textLine; + return new TextWord( + baselineOf(Arrays.asList(words)), + sb.toString(), + fontInfo, + confidenceOf(Arrays.asList(words)), + chars, + line); + } } diff --git a/src/main/org/audiveris/omr/text/TextsStep.java b/src/main/org/audiveris/omr/text/TextsStep.java index 4f6f56e10..1cb7b424c 100644 --- a/src/main/org/audiveris/omr/text/TextsStep.java +++ b/src/main/org/audiveris/omr/text/TextsStep.java @@ -31,6 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.List; /** @@ -41,11 +42,9 @@ public class TextsStep extends AbstractSystemStep { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TextsStep.class); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a TextsStep instance. */ @@ -53,7 +52,6 @@ public TextsStep () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // doSystem // //----------// @@ -73,29 +71,44 @@ public void doSystem (SystemInfo system, protected Context doProlog (Sheet sheet) throws StepException { + List lines = new ArrayList<>(); + // Launch OCR on the whole sheet SheetScanner scanner = new SheetScanner(sheet); - List lines = scanner.scanSheet(); + + if (OcrUtil.getOcr().isAvailable()) { + lines.addAll(scanner.scanSheet()); + } else { + logger.warn("TEXTS step: {}", OCR.NO_OCR); + } // Make all this available for system-level processing return new Context(scanner.getBuffer(), lines); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------// // Context // //---------// + /** + * Context data for this step. + */ protected static class Context { - //~ Instance fields ------------------------------------------------------------------------ + /** The sheet buffer handed to OCR. */ public final ByteProcessor buffer; + /** The raw text lines OCR'ed. */ public final List textLines; - //~ Constructors --------------------------------------------------------------------------- - public Context (ByteProcessor buffer, - List textLines) + /** + * Create a Context object. + * + * @param buffer + * @param textLines + */ + Context (ByteProcessor buffer, + List textLines) { this.buffer = buffer; this.textLines = textLines; diff --git a/src/main/org/audiveris/omr/text/WordScanner.java b/src/main/org/audiveris/omr/text/WordScanner.java index 008e3611d..a9e2a0b21 100644 --- a/src/main/org/audiveris/omr/text/WordScanner.java +++ b/src/main/org/audiveris/omr/text/WordScanner.java @@ -37,11 +37,9 @@ */ public abstract class WordScanner { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(WordScanner.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The content string. */ private final String content; @@ -71,7 +69,6 @@ public abstract class WordScanner private int nextWordStop = -1; - //~ Constructors ------------------------------------------------------------------------------- //-------------// // WordScanner // //-------------// @@ -94,8 +91,6 @@ protected WordScanner (String content, this.chars = chars; } - //~ Methods ------------------------------------------------------------------------------------ - // //--------------// // getWordChars // //--------------// @@ -232,7 +227,6 @@ protected void lookAhead () nextWord = getNextWord(); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------------// // ManualScanner // //---------------// @@ -243,12 +237,10 @@ protected void lookAhead () public static class ManualScanner extends WordScanner { - //~ Instance fields ------------------------------------------------------------------------ /** Ratio of number of TextChar instances / content length. */ private final double ratio; - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new ManualScanner object. * @@ -270,7 +262,6 @@ public ManualScanner (String content, logger.debug("ManualScanner on ''{}''", content); } - //~ Methods -------------------------------------------------------------------------------- @Override protected int stringToDesc (int strIndex) { @@ -291,7 +282,6 @@ protected int stringToDesc (int strIndex) public static class OcrScanner extends WordScanner { - //~ Constructors --------------------------------------------------------------------------- /** * Creates a new OcrScanner object. @@ -312,7 +302,6 @@ public OcrScanner (String content, logger.debug("OcrScanner on ''{}''", content); } - //~ Methods -------------------------------------------------------------------------------- @Override protected int stringToDesc (int strIndex) { diff --git a/src/main/org/audiveris/omr/text/tesseract/TesseractOCR.java b/src/main/org/audiveris/omr/text/tesseract/TesseractOCR.java index e90bf894d..5e1af952a 100644 --- a/src/main/org/audiveris/omr/text/tesseract/TesseractOCR.java +++ b/src/main/org/audiveris/omr/text/tesseract/TesseractOCR.java @@ -42,6 +42,9 @@ import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import java.util.List; import java.util.Set; @@ -50,33 +53,35 @@ /** * Class {@code TesseractOCR} is an OCR service built on the Google Tesseract engine. - * *

                                                                                                                                            - * It relies on tesseract3 C++ program, accessed through a JavaCPP-based bridge.

                                                                                                                                            + * It relies on tesseract3 C++ program, accessed through a JavaCPP-based bridge. * * @author Hervé Bitteur */ public class TesseractOCR implements OCR { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(TesseractOCR.class); - /** Singleton. */ - private static final OCR INSTANCE = new TesseractOCR(); - /** Latin encoder, to check character validity. (not used yet) */ private static final CharsetEncoder encoder = Charset.forName("iso-8859-1").newEncoder(); - //~ Instance fields ---------------------------------------------------------------------------- - // + /** Warning message when OCR folder cannot be found. */ + private static final String ocrNotFoundMsg = "Tesseract data could not be found. " + + "Try setting the TESSDATA_PREFIX environment variable to the parent folder of \"tessdata\"."; + + /** The folder where Tesseract OCR material is stored. */ + private Path OCR_FOLDER; + + /** Boolean to avoid any new search for OCR folder. */ + private boolean OCR_FOLDER_SEARCHED; + /** To assign a serial number to each image processing order. */ private final AtomicInteger serial = new AtomicInteger(0); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates the TesseractOCR singleton. */ @@ -84,21 +89,6 @@ private TesseractOCR () { } - //~ Methods ------------------------------------------------------------------------------------ - // - //-------------// - // getInstance // - //-------------// - /** - * Report the service singleton. - * - * @return the TesseractOCR service instance - */ - public static OCR getInstance () - { - return INSTANCE; - } - //--------------// // getLanguages // //--------------// @@ -106,12 +96,13 @@ public static OCR getInstance () public Set getLanguages () { if (isAvailable()) { - TreeSet set = new TreeSet(); + final Path ocrFolder = getOcrFolder(); + TreeSet set = new TreeSet<>(); try { TessBaseAPI api = new TessBaseAPI(); - if (api.Init(WellKnowns.OCR_FOLDER.toString(), "eng") == 0) { + if (api.Init(ocrFolder.toString(), "eng") == 0) { StringGenericVector languages = new StringGenericVector(); api.GetAvailableLanguagesAsVector(languages); @@ -124,36 +115,55 @@ public Set getLanguages () return set; } catch (Throwable ex) { - logger.warn("Error in loading Tesseract languages", ex); - throw new UnavailableOcrException(); + final String msg = "Error in loading Tesseract languages"; + logger.warn(msg); + throw new UnavailableOcrException(msg, ex); } } return Collections.emptySet(); } - //-------------// - // isAvailable // - //-------------// - @Override - public boolean isAvailable () + //--------------// + // getOcrFolder // + //--------------// + /** + * Report the folder where Tesseract OCR data is available. + * + * @return the OCR folder + */ + public Path getOcrFolder () { - return constants.useOCR.isSet(); + if (!OCR_FOLDER_SEARCHED) { + OCR_FOLDER_SEARCHED = true; + OCR_FOLDER = findOcrFolder(); + } + + return OCR_FOLDER; } //----------// // identify // //----------// @Override - public String identify() + public String identify () { if (isAvailable()) { return "Tesseract OCR, version " + TessBaseAPI.Version().getString(); } else { - return "OCR engine not available"; + return OCR.NO_OCR; } } + //-------------// + // isAvailable // + //-------------// + @Override + public boolean isAvailable () + { + return constants.useOCR.isSet() && (getOcrFolder() != null); + } + //-----------// // recognize // //-----------// @@ -220,11 +230,60 @@ public List recognize (int interline, return null; } catch (UnsatisfiedLinkError ex) { - logger.warn("OCR link error", ex); - throw new UnavailableOcrException(); + final String msg = "OCR link error"; + logger.warn(msg); + throw new UnavailableOcrException(msg, ex); } } + //---------------// + // findOcrFolder // + //---------------// + private Path findOcrFolder () + { + // First, try to use TESSDATA_PREFIX environment variable + // which might denote a Tesseract installation + final String TESSDATA_PREFIX = "TESSDATA_PREFIX"; + final String tessPrefix = System.getenv(TESSDATA_PREFIX); + + if (tessPrefix != null) { + Path dir = Paths.get(tessPrefix); + + if (Files.isDirectory(dir)) { + return dir; + } + } + + if (WellKnowns.WINDOWS) { + // Fallback to default directory on Windows + final String pf32 = WellKnowns.OS_ARCH.equals("x86") ? "ProgramFiles" + : "ProgramFiles(x86)"; + + return Paths.get(System.getenv(pf32)).resolve("tesseract-ocr"); + } else if (WellKnowns.LINUX) { + // Scan common Linux TESSDATA locations + final String[] linuxOcrLocations = { + "/usr/share/tesseract-ocr", // Debian, Ubuntu and derivatives + "/usr/share", // OpenSUSE + "/usr/share/tesseract" // Fedora + }; + + return scanOcrLocations(linuxOcrLocations); + } else if (WellKnowns.MAC_OS_X) { + // Scan common Macintosh TESSDATA locations + final String[] macOcrLocations = { + "/opt/local/share", // Macports + "/usr/local/opt/tesseract/share" // Homebrew + }; + + return scanOcrLocations(macOcrLocations); + } + + logger.warn(ocrNotFoundMsg); + + return null; + } + //---------// // getMode // //---------// @@ -246,14 +305,52 @@ private int getMode (LayoutMode layoutMode) } } - //~ Inner Classes ------------------------------------------------------------------------------ + //------------------// + // scanOcrLocations // + //------------------// + private Path scanOcrLocations (String[] locations) + { + for (String loc : locations) { + final Path path = Paths.get(loc); + + if (Files.exists(path.resolve("tessdata"))) { + return path; + } + } + + logger.warn(ocrNotFoundMsg); + + return null; + } + + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of this class in application. + * + * @return the instance + */ + public static TesseractOCR getInstance () + { + return LazySingleton.INSTANCE; + } + + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { + + static final TesseractOCR INSTANCE = new TesseractOCR(); + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean useOCR = new Constant.Boolean( true, diff --git a/src/main/org/audiveris/omr/text/tesseract/TesseractOrder.java b/src/main/org/audiveris/omr/text/tesseract/TesseractOrder.java index 5cd8a1af7..2c6a58105 100644 --- a/src/main/org/audiveris/omr/text/tesseract/TesseractOrder.java +++ b/src/main/org/audiveris/omr/text/tesseract/TesseractOrder.java @@ -23,6 +23,7 @@ import org.audiveris.omr.WellKnowns; import org.audiveris.omr.text.FontInfo; +import org.audiveris.omr.text.OcrUtil; import org.audiveris.omr.text.TextChar; import org.audiveris.omr.text.TextLine; import org.audiveris.omr.text.TextWord; @@ -41,10 +42,12 @@ import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.imageio.ImageIO; @@ -60,7 +63,6 @@ */ public class TesseractOrder { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TesseractOrder.class); @@ -68,7 +70,7 @@ public class TesseractOrder private static final String UTF8 = "UTF-8"; /** To avoid repetitive warnings if OCR binding failed. */ - private static boolean userWarned; + private static volatile boolean userWarned; static { IIORegistry registry = IIORegistry.getDefaultInstance(); @@ -78,7 +80,6 @@ public class TesseractOrder new com.github.jaiimageio.impl.plugins.tiff.TIFFImageReaderSpi()); } - //~ Instance fields ---------------------------------------------------------------------------- /** Serial number for this order. */ private final int serial; @@ -100,8 +101,6 @@ public class TesseractOrder /** The image being processed. */ private final PIX image; - //~ Constructors ------------------------------------------------------------------------------- - // //----------------// // TesseractOrder // //----------------// @@ -114,7 +113,6 @@ public class TesseractOrder * @param lang The language specification * @param segMode The desired page segmentation mode * @param bufferedImage The image to process - * * @throws UnsatisfiedLinkError When bridge to C++ could not be loaded * @throws IOException When temporary Tiff buffer failed * @throws RuntimeException When PIX image failed @@ -125,7 +123,8 @@ public TesseractOrder (String label, String lang, int segMode, BufferedImage bufferedImage) - throws UnsatisfiedLinkError, IOException + throws UnsatisfiedLinkError, + IOException { this.label = label; this.serial = serial; @@ -144,8 +143,6 @@ public TesseractOrder (String label, } } - //~ Methods ------------------------------------------------------------------------------------ - // //---------// // process // //---------// @@ -156,12 +153,16 @@ public TesseractOrder (String label, */ public List process () { + if (!OcrUtil.getOcr().isAvailable()) { + return Collections.EMPTY_LIST; + } + try { - //api = new TessBaseAPI(WellKnowns.OCR_FOLDER.toString()); + final Path ocrFolder = TesseractOCR.getInstance().getOcrFolder(); api = new TessBaseAPI(); // Init API with proper language - if (api.Init(WellKnowns.OCR_FOLDER.toString(), lang) != 0) { + if (api.Init(ocrFolder.toString(), lang) != 0) { logger.warn("Could not initialize Tesseract with lang {}", lang); return finish(null); @@ -196,8 +197,30 @@ public List process () } } - private Line2D Baseline (ResultIterator rit, - int level) + //--------// + // finish // + //--------// + /** + * Convenient way to cleanup Tesseract resources while ending the current processing + * + * @param lines the lines found, if any + * @return the lines found, if nay + */ + private List finish (List lines) + { + if (image != null) { + pixDestroy(image); + } + + if (api != null) { + api.End(); + } + + return lines; + } + + private Line2D getBaseline (ResultIterator rit, + int level) { IntPointer x1 = new IntPointer(0); IntPointer y1 = new IntPointer(0); @@ -211,8 +234,8 @@ private Line2D Baseline (ResultIterator rit, } } - private Rectangle BoundingBox (PageIterator it, - int level) + private Rectangle getBoundingBox (PageIterator it, + int level) { IntPointer left = new IntPointer(0); IntPointer top = new IntPointer(0); @@ -230,28 +253,6 @@ private Rectangle BoundingBox (PageIterator it, } } - //--------// - // finish // - //--------// - /** - * Convenient way to cleanup Tesseract resources while ending the current processing - * - * @param lines the lines found, if any - * @return the lines found, if nay - */ - private List finish (List lines) - { - if (image != null) { - pixDestroy(image); - } - - if (api != null) { - api.End(); - } - - return lines; - } - //---------// // getFont // //---------// @@ -316,7 +317,7 @@ private FontInfo getFont (ResultIterator rit) private List getLines () { final ResultIterator it = api.GetIterator(); - final List lines = new ArrayList(); // All lines built so far + final List lines = new ArrayList<>(); // All lines built so far TextLine line = null; // The line being built TextWord word = null; // The word being built int nextLevel; @@ -349,9 +350,9 @@ private List getLines () } word = new TextWord( - BoundingBox(it, RIL_WORD), + getBoundingBox(it, RIL_WORD), it.GetUTF8Text(RIL_WORD).getString(UTF8), - Baseline(it, RIL_WORD), + getBaseline(it, RIL_WORD), it.Confidence(RIL_WORD) / 100.0, fontInfo, line); @@ -372,7 +373,7 @@ private List getLines () // Char/symbol to be processed wordAddChars( word, - BoundingBox(it, RIL_SYMBOL), + getBoundingBox(it, RIL_SYMBOL), it.GetUTF8Text(RIL_SYMBOL).getString(UTF8)); } while (it.Next(nextLevel)); @@ -382,7 +383,7 @@ private List getLines () } return lines; - } catch (Exception ex) { + } catch (UnsupportedEncodingException ex) { logger.warn("Error decoding tesseract output", ex); return null; @@ -395,8 +396,9 @@ private List getLines () // toTiffBuffer // //--------------// /** - * Convert the given image into a TIFF-formatted ByteBuffer for - * passing it directly to Tesseract. + * Convert the given image into a TIFF-formatted ByteBuffer for passing it directly + * to Tesseract. + *

                                                                                                                                            * A copy of the tiff buffer can be saved on disk, if so desired. * * @param image the input image @@ -407,21 +409,11 @@ private ByteBuffer toTiffBuffer (BufferedImage image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - ImageOutputStream ios = null; - - try { - ios = ImageIO.createImageOutputStream(baos); - - ImageWriter writer = ImageIO.getImageWritersByFormatName("tiff").next(); - writer.setOutput(ios); - writer.write(image); - } finally { - if (ios != null) { - ios.close(); - } - } - } catch (Exception ex) { + try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) { + ImageWriter writer = ImageIO.getImageWritersByFormatName("tiff").next(); + writer.setOutput(ios); + writer.write(image); + } catch (IOException ex) { logger.warn("Could not write image", ex); } @@ -439,18 +431,9 @@ private ByteBuffer toTiffBuffer (BufferedImage image) Files.createDirectories(WellKnowns.TEMP_FOLDER); } - try { - FileOutputStream fos = null; - - try { - fos = new FileOutputStream(path.toFile()); - fos.write(bytes); - } finally { - if (fos != null) { - fos.close(); - } - } - } catch (Exception ex) { + try (FileOutputStream fos = new FileOutputStream(path.toFile())) { + fos.write(bytes); + } catch (IOException ex) { logger.warn("Could not write to {}", path, ex); } } diff --git a/src/main/org/audiveris/omr/ui/Board.java b/src/main/org/audiveris/omr/ui/Board.java index d37995812..c2a9e8194 100644 --- a/src/main/org/audiveris/omr/ui/Board.java +++ b/src/main/org/audiveris/omr/ui/Board.java @@ -37,43 +37,49 @@ import org.slf4j.LoggerFactory; import java.awt.Component; +import java.util.Comparator; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; +import javax.swing.text.JTextComponent; /** * Class {@code Board} defines the common properties of any user board such as * PixelBoard, SectionBoard, and the like. *

                                                                                                                                            * Each board has a standard header composed of a title, a horizontal separator and optionally a - * dump button. The board body is handled by the subclass.

                                                                                                                                            + * dump button. The board body is handled by the subclass. + *

                                                                                                                                            *

                                                                                                                                            * Any board can be (de)selected in its containing {@link BoardsPane}. This can be done * programmatically using {@link #setSelected(boolean)} and manually (via a right-click in the - * BoardsPane).

                                                                                                                                            + * BoardsPane). + *

                                                                                                                                            *

                                                                                                                                            * Only selected boards can be seen in the BoardsPane display. A selected board can be made * currently (in)visible programmatically using {@link #setVisible(boolean)}. * Typically, {@link org.audiveris.omr.check.CheckBoard}'s are visible only when they carry - * glyph information.

                                                                                                                                            + * glyph information. + *

                                                                                                                                            *

                                                                                                                                            * By default, any board can have a related SelectionService, used for subscribe (input) and publish * (output). When {@link #connect} is called, the board instance is subscribed to its * SelectionService for a specific collection of event classes. Similarly, {@link #disconnect} - * un-subscribes the Board instance from the same event classes.

                                                                                                                                            + * un-subscribes the Board instance from the same event classes. + *

                                                                                                                                            *

                                                                                                                                            * This {@code Board} class is still an abstract class, since the onEvent() method must be - * provided by every subclass.

                                                                                                                                            + * provided by every subclass. + *

                                                                                                                                            * * @author Hervé Bitteur */ public abstract class Board - implements EventSubscriber, Comparable + implements EventSubscriber { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Board.class); @@ -104,7 +110,17 @@ public abstract class Board public static final Desc CHECK = new Desc("Check", 900); - //~ Instance fields ---------------------------------------------------------------------------- + /** To sort boards by their position. */ + public static final Comparator byPosition = new Comparator() + { + @Override + public int compare (Board b1, + Board b2) + { + return Integer.compare(b1.position, b2.position); + } + }; + /** The board instance name. */ private final String name; @@ -132,7 +148,6 @@ public abstract class Board /** Board is selected?. */ private boolean selected; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a board from a pre-defined descriptor (name + position). * @@ -195,39 +210,6 @@ public Board (String name, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // emptyFields // - //-------------// - /** - * Convenient method to empty all the text fields of a given JComponent. - * - * @param component the component to "blank". - */ - public static void emptyFields (JComponent component) - { - for (Component comp : component.getComponents()) { - if (comp instanceof JTextField) { - ((JTextField) comp).setText(""); - } - } - } - - //-----------// - // compareTo // - //-----------// - /** - * Allow to sort boards according to their preferred display position. - * - * @param that the other board to compare to - * @return comparison result - */ - @Override - public int compareTo (Board that) - { - return Integer.signum(this.position - that.position); - } - //---------// // connect // //---------// @@ -305,27 +287,6 @@ public boolean isSelected () return selected; } - //-------------// - // resizeBoard // - //-------------// - /** - * Resize board component, to adapt to its new composition. - */ - public void resizeBoard () - { - component.invalidate(); - component.validate(); - component.repaint(); - } - - //-----------// - // setParent // - //-----------// - public void setParent (BoardsPane parent) - { - this.parent = parent; - } - //-------------// // setSelected // //-------------// @@ -354,6 +315,27 @@ public void setSelected (boolean selected) } } + //-------------// + // resizeBoard // + //-------------// + /** + * Resize board component, to adapt to its new composition. + */ + public void resizeBoard () + { + component.invalidate(); + component.validate(); + component.repaint(); + } + + //-----------// + // setParent // + //-----------// + public void setParent (BoardsPane parent) + { + this.parent = parent; + } + //------------// // setVisible // //------------// @@ -445,16 +427,30 @@ private void defineLayout () body.setNoInsets(); CellConstraints cst = new CellConstraints(); - FormLayout layout = new FormLayout( - "pref", - "pref," + Panel.getFieldInterline() + ",pref"); + FormLayout layout = new FormLayout("pref", "pref," + Panel.getFieldInterline() + ",pref"); PanelBuilder builder = new PanelBuilder(layout, component); builder.add(header, cst.xy(1, 1)); builder.add(body, cst.xy(1, 3)); } - //~ Inner Classes ------------------------------------------------------------------------------ + //-------------// + // emptyFields // + //-------------// + /** + * Convenient method to empty all the text fields of a given JComponent. + * + * @param component the component to "blank". + */ + public static void emptyFields (JComponent component) + { + for (Component comp : component.getComponents()) { + if (comp instanceof JTextField) { + ((JTextComponent) comp).setText(""); + } + } + } + //------// // Desc // //------// @@ -463,7 +459,6 @@ private void defineLayout () */ public static class Desc { - //~ Instance fields ------------------------------------------------------------------------ /** Default name for this board. */ public final String name; @@ -471,7 +466,6 @@ public static class Desc /** Preferred position within its containing BoardsPane. */ public final int position; - //~ Constructors --------------------------------------------------------------------------- public Desc (String name, int position) { @@ -490,7 +484,6 @@ public Desc (String name, private static class Header extends Panel { - //~ Instance fields ------------------------------------------------------------------------ /** The board title. */ private final String title; @@ -504,11 +497,10 @@ private static class Header /** Dump button, if any. */ private final JButton dump; - //~ Constructors --------------------------------------------------------------------------- - public Header (String title, - boolean withCount, - boolean withVip, - boolean withDump) + Header (String title, + boolean withCount, + boolean withVip, + boolean withDump) { this.title = title; @@ -520,7 +512,6 @@ public Header (String title, defineLayout(); } - //~ Methods -------------------------------------------------------------------------------- private void defineLayout () { CellConstraints cst = new CellConstraints(); diff --git a/src/main/org/audiveris/omr/ui/BoardsPane.java b/src/main/org/audiveris/omr/ui/BoardsPane.java index 1dc4664e7..e5a2a66d0 100644 --- a/src/main/org/audiveris/omr/ui/BoardsPane.java +++ b/src/main/org/audiveris/omr/ui/BoardsPane.java @@ -53,7 +53,6 @@ * Class {@code BoardsPane} defines a view on a set of user {@link * Board} instances, where data related to current point, run, section, * glyph, etc can be displayed in dedicated boards. - * *

                                                                                                                                            * There is one BoardsPane instance for each view of the same sheet, * each with its own collection of board instances. @@ -62,16 +61,14 @@ */ public class BoardsPane { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(BoardsPane.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The concrete UI component */ private final Panel component; /** Sequence of current boards, kept ordered by board preferred position */ - private final List boards = new ArrayList(); + private final List boards = new ArrayList<>(); /** Unique (application-wide) name for this pane. */ private String name; @@ -79,7 +76,6 @@ public class BoardsPane /** Mouse listener */ private final MouseAdapter mouseAdapter = new MyMouseAdapter(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a BoardsPane, with initial boards. * @@ -94,7 +90,7 @@ public BoardsPane (List boards) board.setParent(this); } - Collections.sort(this.boards); + Collections.sort(this.boards, Board.byPosition); component = new Panel(); component.setNoInsets(); @@ -115,7 +111,6 @@ public BoardsPane (Board... boards) this(Arrays.asList(boards)); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // addBoard // //----------// @@ -136,7 +131,7 @@ public void addBoard (Board board) boards.add(board); board.setParent(this); - Collections.sort(this.boards); + Collections.sort(this.boards, Board.byPosition); update(); if (board.isSelected()) { @@ -203,6 +198,20 @@ public String getName () return name; } + //---------// + // setName // + //---------// + /** + * Assign the unique name for this boards pane. + * + * @param name the assigned name + */ + public void setName (String name) + { + this.name = name; + component.setName(name); + } + //-------------// // removeBoard // //-------------// @@ -221,20 +230,6 @@ public void removeBoard (Board board) update(); } - //---------// - // setName // - //---------// - /** - * Assign the unique name for this boards pane. - * - * @param name the assigned name - */ - public void setName (String name) - { - this.name = name; - component.setName(name); - } - //----------// // toString // //----------// @@ -267,22 +262,6 @@ public String toString () return sb.toString(); } - //--------// - // update // - //--------// - /** - * Modify the BoardsPane component composition. - */ - void update () - { - int count = component.getComponentCount(); - Component comp = component.getComponent(count - 1); - component.remove(comp); - component.add(defineLayout()); - component.revalidate(); - component.repaint(); - } - //--------------// // defineLayout // //--------------// @@ -345,7 +324,22 @@ private Board getBoard (String title) return null; } - //~ Inner Classes ------------------------------------------------------------------------------ + //--------// + // update // + //--------// + /** + * Modify the BoardsPane component composition. + */ + void update () + { + int count = component.getComponentCount(); + Component comp = component.getComponent(count - 1); + component.remove(comp); + component.add(defineLayout()); + component.revalidate(); + component.repaint(); + } + //----------------// // MyMouseAdapter // //----------------// @@ -356,7 +350,6 @@ private class MyMouseAdapter extends MouseAdapter implements ItemListener { - //~ Methods -------------------------------------------------------------------------------- //------------------// // itemStateChanged // diff --git a/src/main/org/audiveris/omr/ui/Colors.java b/src/main/org/audiveris/omr/ui/Colors.java index 28b7a36be..6ca0cc151 100644 --- a/src/main/org/audiveris/omr/ui/Colors.java +++ b/src/main/org/audiveris/omr/ui/Colors.java @@ -24,18 +24,19 @@ import java.awt.Color; /** - * Class {@code Colors} gathers alphabetically in one place the various colors used by + * Class {@code Colors} gathers in one place the various colors used by * Audiveris displays, in order to ensure consistency and compatibility. * * @author Hervé Bitteur */ -public class Colors +public abstract class Colors { - //~ Static fields/initializers ----------------------------------------------------------------- /** Global alpha transparency (0..255). */ private static final int alpha = 180; + // Color names below should better be listed alphabetically + // /** Annotations. */ public static final Color ANNOTATION = Color.LIGHT_GRAY; @@ -166,10 +167,9 @@ public class Colors /** Warping points. */ public static final Color WARP_POINT = Color.RED; - //~ Constructors ------------------------------------------------------------------------------- /** * Not meant to be instantiated. - * . */ + */ private Colors () { } diff --git a/src/main/org/audiveris/omr/ui/EntityAction.java b/src/main/org/audiveris/omr/ui/EntityAction.java index 3e387d91e..ef142b56e 100644 --- a/src/main/org/audiveris/omr/ui/EntityAction.java +++ b/src/main/org/audiveris/omr/ui/EntityAction.java @@ -47,12 +47,10 @@ public class EntityAction extends AbstractAction { - //~ Instance fields ---------------------------------------------------------------------------- /** Delegation to an existing action, if any */ private Action delegate = null; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates an action, and registers the action in the provided menu as * well as in the toolbar (if so desired) @@ -92,7 +90,7 @@ protected EntityAction (Collection entityActions, if (key != null) { item.setAccelerator( KeyStroke.getKeyStroke( - (int) key.charAt(0), + key.charAt(0), Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); } @@ -110,9 +108,9 @@ protected EntityAction (Collection entityActions, * Wraps an existing action, used as a delegate. * * @param entityActions collection of actions - * @param menu the menu where the related item is to be inserted - * @param toolBar the toolBar for icon insertion - * @param delegate the existing action + * @param menu the menu where the related item is to be inserted + * @param toolBar the toolBar for icon insertion + * @param delegate the existing action */ protected EntityAction (Collection entityActions, JMenu menu, @@ -153,7 +151,6 @@ protected EntityAction (Collection entityActions, this(entityActions, menu, toolBar, label, tip, null, icon); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // actionPerformed // //-----------------// @@ -164,4 +161,11 @@ public void actionPerformed (ActionEvent e) delegate.actionPerformed(e); } } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/ui/EntityBoard.java b/src/main/org/audiveris/omr/ui/EntityBoard.java index 86ba072d0..e2d9ea66a 100644 --- a/src/main/org/audiveris/omr/ui/EntityBoard.java +++ b/src/main/org/audiveris/omr/ui/EntityBoard.java @@ -56,32 +56,18 @@ * Class {@code EntityBoard} is a basic board related to an entity type. * * @param precise entity type - * * @author Hervé Bitteur */ public class EntityBoard extends Board implements ChangeListener, ActionListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(EntityBoard.class); - /** Events this board is interested in */ - protected static final Class[] eventsRead = new Class[]{EntityListEvent.class}; - - //~ Enumerations ------------------------------------------------------------------------------- - /** To select precise ID option. */ - public static enum IdOption - { - //~ Enumeration constant initializers ------------------------------------------------------ + /** Events this board is interested in. */ + private static final Class[] eventsRead = new Class[]{EntityListEvent.class}; - ID_NONE, - ID_LABEL, - ID_SPINNER; - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Counter of entities selection. */ protected JLabel count; @@ -106,7 +92,6 @@ public static enum IdOption /** To avoid loop, indicate that update() method id being processed. */ protected boolean selfUpdating = false; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code EntityBoard} object, with all entity fields by default. * @@ -186,7 +171,6 @@ public EntityBoard (Desc desc, defineLayout(); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // actionPerformed // //-----------------// @@ -293,6 +277,11 @@ protected FormLayout getFormLayout () //-------------------// // getSelectedEntity // //-------------------// + /** + * Report the selected entity (first in sequence). + * + * @return selected entity + */ @SuppressWarnings("unchecked") protected E getSelectedEntity () { @@ -425,7 +414,7 @@ private void defineLayout () */ private JSpinner makeIdSpinner (EntityIndex index) { - JSpinner spinner = new JSpinner(new SpinnerIdModel(index)); + JSpinner spinner = new JSpinner(new SpinnerIdModel<>(index)); spinner.setValue(0); // Initial value before listener is set! spinner.addChangeListener(this); spinner.setLocale(Locale.ENGLISH); @@ -434,4 +423,12 @@ private JSpinner makeIdSpinner (EntityIndex index) return spinner; } + + /** To select precise ID option. */ + public static enum IdOption + { + ID_NONE, + ID_LABEL, + ID_SPINNER; + } } diff --git a/src/main/org/audiveris/omr/ui/EntityView.java b/src/main/org/audiveris/omr/ui/EntityView.java index e36e4aa7f..dbb9f70e7 100644 --- a/src/main/org/audiveris/omr/ui/EntityView.java +++ b/src/main/org/audiveris/omr/ui/EntityView.java @@ -44,25 +44,21 @@ * Class {@code EntityView} is a basic graphical view for an entity type. * * @param precise entity type - * * @author Hervé Bitteur */ public class EntityView extends RubberPanel implements PropertyChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(EntityView.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying entity service. */ protected final EntityService entityService; /** Underlying entity index. */ protected final EntityIndex entityIndex; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code EntityView} object. * @@ -83,7 +79,6 @@ public EntityView (EntityService entityService) entityService.subscribeStrongly(EntityListEvent.class, this); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // onEvent // //---------// diff --git a/src/main/org/audiveris/omr/ui/ErrorsEditor.java b/src/main/org/audiveris/omr/ui/ErrorsEditor.java index 3a24ecc5d..79d2898bb 100644 --- a/src/main/org/audiveris/omr/ui/ErrorsEditor.java +++ b/src/main/org/audiveris/omr/ui/ErrorsEditor.java @@ -28,6 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Objects; import java.util.SortedSet; import java.util.TreeSet; @@ -48,11 +49,9 @@ */ public class ErrorsEditor { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ErrorsEditor.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Related sheet */ private final Sheet sheet; @@ -66,12 +65,11 @@ public class ErrorsEditor private final ListSelectionListener listener = new MyListener(); /** Set of error records */ - private final SortedSet recordSet = new TreeSet(); + private final SortedSet recordSet = new TreeSet<>(); /** Facade model for the JList */ - private final DefaultListModel model = new DefaultListModel(); + private final DefaultListModel model = new DefaultListModel<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create an instance of ErrorsEditor (one per sheet / score). * @@ -80,14 +78,13 @@ public class ErrorsEditor public ErrorsEditor (Sheet sheet) { this.sheet = sheet; - list = new JList(model); + list = new JList<>(model); scrollPane = new JScrollPane(list); scrollPane.setBorder(null); list.addListSelectionListener(listener); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } - //~ Methods ------------------------------------------------------------------------------------ // //----------// // // addError // // //----------// @@ -251,8 +248,6 @@ public JComponent getComponent () return scrollPane; } - //~ Inner Classes ------------------------------------------------------------------------------ - // // //----------------// // // getCurrentStep // // //----------------// @@ -277,7 +272,6 @@ public JComponent getComponent () private class MyListener implements ListSelectionListener { - //~ Methods -------------------------------------------------------------------------------- @Override public void valueChanged (ListSelectionEvent e) @@ -337,7 +331,6 @@ public void valueChanged (ListSelectionEvent e) private static class Record implements Comparable { - //~ Instance fields ------------------------------------------------------------------------ final Step step; @@ -348,11 +341,10 @@ private static class Record final String text; - //~ Constructors --------------------------------------------------------------------------- - public Record (Step step, - // OldSystemNode node, - Glyph glyph, - String text) + Record (Step step, + // OldSystemNode node, + Glyph glyph, + String text) { this.step = step; // this.node = node; @@ -360,7 +352,6 @@ public Record (Step step, this.text = text; } - //~ Methods -------------------------------------------------------------------------------- @Override public int compareTo (Record other) { @@ -368,6 +359,31 @@ public int compareTo (Record other) return toString().compareTo(other.toString()); } + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + + if (obj instanceof Record) { + return compareTo((Record) obj) == 0; + } + + return false; + } + + @Override + public int hashCode () + { + int hash = 7; + hash = (37 * hash) + Objects.hashCode(this.step); + hash = (37 * hash) + Objects.hashCode(this.glyph); + hash = (37 * hash) + Objects.hashCode(this.text); + + return hash; + } + @Override public String toString () { diff --git a/src/main/org/audiveris/omr/ui/FileDropHandler.java b/src/main/org/audiveris/omr/ui/FileDropHandler.java index a323c9b64..f08c38b15 100644 --- a/src/main/org/audiveris/omr/ui/FileDropHandler.java +++ b/src/main/org/audiveris/omr/ui/FileDropHandler.java @@ -51,11 +51,9 @@ public class FileDropHandler extends TransferHandler { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(FileDropHandler.class); - //~ Methods ------------------------------------------------------------------------------------ //-----------// // canImport // //-----------// @@ -129,26 +127,22 @@ public boolean importData (TransferSupport support) return true; } - //~ Inner Classes ------------------------------------------------------------------------------ //--------------// // DropBookTask // //--------------// private static class DropBookTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ private final File file; private Book book; - //~ Constructors --------------------------------------------------------------------------- - public DropBookTask (File file) + DropBookTask (File file) { this.file = file; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws Exception @@ -182,7 +176,6 @@ protected void finished () private static class DropInputTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ private final File file; @@ -190,15 +183,13 @@ private static class DropInputTask private Book book; - //~ Constructors --------------------------------------------------------------------------- - public DropInputTask (File file, - Step dropStep) + DropInputTask (File file, + Step dropStep) { this.file = file; this.dropStep = dropStep; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws Exception @@ -241,17 +232,14 @@ protected void finished () private static class DropSamplesTask extends VoidTask { - //~ Instance fields ------------------------------------------------------------------------ private final File file; - //~ Constructors --------------------------------------------------------------------------- - public DropSamplesTask (File file) + DropSamplesTask (File file) { this.file = file; } - //~ Methods -------------------------------------------------------------------------------- @Override protected Void doInBackground () throws Exception diff --git a/src/main/org/audiveris/omr/ui/GuiActions.java b/src/main/org/audiveris/omr/ui/GuiActions.java index 0905101cd..a02ba1bd2 100644 --- a/src/main/org/audiveris/omr/ui/GuiActions.java +++ b/src/main/org/audiveris/omr/ui/GuiActions.java @@ -85,25 +85,11 @@ public class GuiActions extends AbstractBean { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(GuiActions.class); - /** Options UI */ - private static Options options; - - // Resource injection - private static ResourceMap resource = Application.getInstance().getContext().getResourceMap( - GuiActions.class); - - /** Singleton */ - private static GuiActions INSTANCE; - - /** Create this action just once */ - private static volatile AboutAction aboutAction; - /** Should the errors window be displayed. */ public static final String ERRORS_WINDOW_DISPLAYED = "errorsWindowDisplayed"; @@ -113,23 +99,15 @@ public class GuiActions /** Should the boards window be displayed. */ public static final String BOARDS_WINDOW_DISPLAYED = "boardsWindowDisplayed"; - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // getInstance // - //-------------// - /** - * Report the singleton - * - * @return the unique instance of this class - */ - public static synchronized GuiActions getInstance () - { - if (INSTANCE == null) { - INSTANCE = new GuiActions(); - } + /** Options UI */ + private static Options options; - return INSTANCE; - } + // Resource injection + private static ResourceMap resource = Application.getInstance().getContext().getResourceMap( + GuiActions.class); + + /** Create this action just once */ + private static volatile AboutAction aboutAction; //---------------------// // browseGlobalSamples // @@ -143,7 +121,7 @@ public static synchronized GuiActions getInstance () public void browseGlobalSamples (ActionEvent e) { CursorController.launchWithDelayedMessage( - "Launching sample browser...", + "Launching global sample browser...", new Runnable() { @Override @@ -179,14 +157,15 @@ public void browseLocalSamples (ActionEvent e) if (repoPath != null) { CursorController.launchWithDelayedMessage( - "Launching sample browser...", + "Launching local sample browser...", new Runnable() { @Override public void run () { try { - new SampleBrowser(SampleRepository.getInstance(repoPath, true)).setVisible(); + new SampleBrowser(SampleRepository.getInstance(repoPath, true)) + .setVisible(); } catch (Throwable ex) { logger.warn("Could not launch samples browser. " + ex, ex); } @@ -275,6 +254,16 @@ public boolean isBoardsWindowDisplayed () return constants.boardsWindowDisplayed.getValue(); } + //--------------------------// + // setBoardsWindowDisplayed // + //--------------------------// + public void setBoardsWindowDisplayed (boolean value) + { + boolean oldValue = constants.boardsWindowDisplayed.getValue(); + constants.boardsWindowDisplayed.setValue(value); + firePropertyChange(BOARDS_WINDOW_DISPLAYED, oldValue, value); + } + //--------------------// // isBrowserSupported // //--------------------// @@ -296,6 +285,16 @@ public boolean isErrorsWindowDisplayed () return constants.errorsWindowDisplayed.getValue(); } + //--------------------------// + // setErrorsWindowDisplayed // + //--------------------------// + public void setErrorsWindowDisplayed (boolean value) + { + boolean oldValue = constants.errorsWindowDisplayed.getValue(); + constants.errorsWindowDisplayed.setValue(value); + firePropertyChange(ERRORS_WINDOW_DISPLAYED, oldValue, value); + } + //----------------------// // isLogWindowDisplayed // //----------------------// @@ -304,6 +303,16 @@ public boolean isLogWindowDisplayed () return constants.logWindowDisplayed.getValue(); } + //-----------------------// + // setLogWindowDisplayed // + //-----------------------// + public void setLogWindowDisplayed (boolean value) + { + boolean oldValue = constants.logWindowDisplayed.getValue(); + constants.logWindowDisplayed.setValue(value); + firePropertyChange(LOG_WINDOW_DISPLAYED, oldValue, value); + } + //--------------------// // launchSymbolRipper // //--------------------// @@ -327,16 +336,14 @@ public void launchSymbolRipper () @Action public void launchTrainer (ActionEvent e) { - CursorController.launchWithDelayedMessage( - "Launching trainer...", - new Runnable() - { - @Override - public void run () - { - Trainer.launch(); - } - }); + CursorController.launchWithDelayedMessage("Launching trainer...", new Runnable() + { + @Override + public void run () + { + Trainer.launch(); + } + }); } //-------------------// @@ -353,36 +360,6 @@ public void saveGlobalSamples (ActionEvent e) SampleRepository.getGlobalInstance().checkForSave(); } - //--------------------------// - // setBoardsWindowDisplayed // - //--------------------------// - public void setBoardsWindowDisplayed (boolean value) - { - boolean oldValue = constants.boardsWindowDisplayed.getValue(); - constants.boardsWindowDisplayed.setValue(value); - firePropertyChange(BOARDS_WINDOW_DISPLAYED, oldValue, value); - } - - //--------------------------// - // setErrorsWindowDisplayed // - //--------------------------// - public void setErrorsWindowDisplayed (boolean value) - { - boolean oldValue = constants.errorsWindowDisplayed.getValue(); - constants.errorsWindowDisplayed.setValue(value); - firePropertyChange(ERRORS_WINDOW_DISPLAYED, oldValue, value); - } - - //-----------------------// - // setLogWindowDisplayed // - //-----------------------// - public void setLogWindowDisplayed (boolean value) - { - boolean oldValue = constants.logWindowDisplayed.getValue(); - constants.logWindowDisplayed.setValue(value); - firePropertyChange(LOG_WINDOW_DISPLAYED, oldValue, value); - } - //-----------// // showAbout // //-----------// @@ -525,67 +502,36 @@ public void visitWiki (ActionEvent e) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-------------// - // AboutAction // + // getInstance // //-------------// /** - * Class {@code AboutAction} opens an 'About' dialog with some - * information about the application. + * Report the single instance of this class in application. * + * @return the instance */ - public static class AboutAction + public static GuiActions getInstance () { - //~ Enumerations --------------------------------------------------------------------------- - - private static enum Topic - { - //~ Enumeration constant initializers -------------------------------------------------- + return LazySingleton.INSTANCE; + } - /** Longer application description */ - description(new JTextField()), - /** Current version */ - version(new JTextField()), - /** Precise classes */ - classes(new JTextField()), - /** Link to web site */ - home(new JEditorPane("text/html", "")), - /** Link to book site */ - book(new JEditorPane("text/html", "")), - /** License */ - license(new JTextField()), - /** OCR version */ - ocr(new JTextField()), - /** Java vendor */ - javaVendor(new JTextField()), - /** Java version */ - javaVersion(new JTextField()), - /** Java runtime */ - javaRuntime(new JTextField()), - /** Java VM */ - javaVm(new JTextField()), - /** OS */ - os(new JTextField()), - /** Arch */ - osArch(new JTextField()); - //~ Instance fields -------------------------------------------------------------------- + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { - public final JTextComponent comp; + static final GuiActions INSTANCE = new GuiActions(); + } - //~ Constructors ----------------------------------------------------------------------- - Topic (JTextComponent comp) - { - this.comp = comp; - } - } + public static class AboutAction + { - //~ Instance fields ------------------------------------------------------------------------ // Dialog private JDialog aboutBox = null; private HyperlinkListener linkListener = new LinkListener(); - //~ Methods -------------------------------------------------------------------------------- public void actionPerformed (ActionEvent e) { if (aboutBox == null) { @@ -668,11 +614,14 @@ private JDialog createAboutBox () Topic.javaVersion.comp.setText(System.getProperty("java.version")); Topic.javaRuntime.comp.setText( System.getProperty("java.runtime.name") + " (build " - + System.getProperty("java.runtime.version") + ")"); + + System.getProperty("java.runtime.version") + + ")"); Topic.javaVm.comp.setText( System.getProperty("java.vm.name") + " (build " - + System.getProperty("java.vm.version") + ", " + System.getProperty("java.vm.info") - + ")"); + + System.getProperty("java.vm.version") + + ", " + + System.getProperty("java.vm.info") + + ")"); Topic.os.comp.setText( System.getProperty("os.name") + " " + System.getProperty("os.version")); Topic.osArch.comp.setText(System.getProperty("os.arch")); @@ -680,19 +629,53 @@ private JDialog createAboutBox () return dialog; } - //~ Inner Classes -------------------------------------------------------------------------- + private static enum Topic + { + /** Longer application description */ + description(new JTextField()), + /** Current version */ + version(new JTextField()), + /** Precise classes */ + classes(new JTextField()), + /** Link to web site */ + home(new JEditorPane("text/html", "")), + /** Link to book site */ + book(new JEditorPane("text/html", "")), + /** License */ + license(new JTextField()), + /** OCR version */ + ocr(new JTextField()), + /** Java vendor */ + javaVendor(new JTextField()), + /** Java version */ + javaVersion(new JTextField()), + /** Java runtime */ + javaRuntime(new JTextField()), + /** Java VM */ + javaVm(new JTextField()), + /** OS */ + os(new JTextField()), + /** Arch */ + osArch(new JTextField()); + + public final JTextComponent comp; + + Topic (JTextComponent comp) + { + this.comp = comp; + } + } + //------------// // ImagePanel // //------------// private static class ImagePanel extends JPanel { - //~ Instance fields -------------------------------------------------------------------- private Image img; - //~ Constructors ----------------------------------------------------------------------- - public ImagePanel (Image img) + ImagePanel (Image img) { this.img = img; @@ -704,13 +687,12 @@ public ImagePanel (Image img) setLayout(null); } - public ImagePanel (URI uri) + ImagePanel (URI uri) throws MalformedURLException { this(new ImageIcon(uri.toURL()).getImage()); } - //~ Methods ---------------------------------------------------------------------------- @Override public void paintComponent (Graphics g) { @@ -721,7 +703,6 @@ public void paintComponent (Graphics g) private static class LinkListener implements HyperlinkListener { - //~ Methods ---------------------------------------------------------------------------- @Override public void hyperlinkUpdate (HyperlinkEvent event) @@ -745,10 +726,9 @@ public void hyperlinkUpdate (HyperlinkEvent event) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String webSiteUrl = new Constant.String( "http://www.audiveris.org", @@ -758,10 +738,9 @@ private static final class Constants "https://github.com/Audiveris/audiveris/wiki", "URL of Audiveris wiki"); - private final Constant.String manualUrl = new Constant.String( - //"docs/manual/handbook.html", + private final Constant.String manualUrl = new Constant.String( //"docs/manual/handbook.html", "https://bacchushlg.gitbooks.io/audiveris-5-1/content/", - "URL of local Audiveris manual"); + "URL of Audiveris manual"); private final Constant.Boolean boardsWindowDisplayed = new Constant.Boolean( true, @@ -782,28 +761,23 @@ private static final class Constants private static class OptionsTask extends Task { - //~ Instance fields ------------------------------------------------------------------------ final Timer timer = new Timer(); - //~ Constructors --------------------------------------------------------------------------- - public OptionsTask () + OptionsTask () { super(OmrGui.getApplication()); - timer.schedule( - new TimerTask() + timer.schedule(new TimerTask() { @Override public void run () { logger.info("Building options window..."); } - }, - CursorController.delay); + }, CursorController.delay); } - //~ Methods -------------------------------------------------------------------------------- @Override protected Options doInBackground () throws Exception diff --git a/src/main/org/audiveris/omr/ui/MacApplication.java b/src/main/org/audiveris/omr/ui/MacApplication.java index 302e819a5..a6429541b 100644 --- a/src/main/org/audiveris/omr/ui/MacApplication.java +++ b/src/main/org/audiveris/omr/ui/MacApplication.java @@ -31,10 +31,13 @@ import java.awt.Image; import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.net.MalformedURLException; import java.net.URI; import java.nio.file.Paths; + import javax.swing.ImageIcon; /** @@ -47,7 +50,6 @@ public class MacApplication implements InvocationHandler { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(MacApplication.class); @@ -57,12 +59,11 @@ public class MacApplication static { try { eventClass = Class.forName("com.apple.eawt.ApplicationEvent"); - } catch (Exception e) { + } catch (ClassNotFoundException e) { eventClass = null; } } - //~ Methods ------------------------------------------------------------------------------------ /** * Invocation handler for * @@ -113,6 +114,10 @@ public Object invoke (Object proxy, Book book = OMR.engine.loadInput(Paths.get(filename)); book.createStubs(null); book.createStubsTabs(null); // Tabs are now accessible + + break; + + default: break; } @@ -138,7 +143,9 @@ public static boolean setupMacMenus () Object app = appClass.newInstance(); //Enable the about menu item and the preferences menu item - for (String methodName : new String[]{"setEnabledAboutMenu", "setEnabledPreferencesMenu"}) { + for (String methodName : new String[]{ + "setEnabledAboutMenu", + "setEnabledPreferencesMenu"}) { Method method = appClass.getMethod(methodName, boolean.class); method.invoke(app, true); } @@ -169,7 +176,14 @@ public static boolean setupMacMenus () setDockImage.invoke(application, icon); return true; - } catch (Exception ex) { + } catch (ClassNotFoundException | + IllegalAccessException | + IllegalArgumentException | + InstantiationException | + NoSuchMethodException | + SecurityException | + InvocationTargetException | + MalformedURLException ex) { logger.warn("Unable to setup Mac OS X GUI integration", ex); return false; @@ -205,7 +219,11 @@ private static String getFilename (Object event) } else { return (String) rval; } - } catch (Exception e) { + } catch (IllegalAccessException | + IllegalArgumentException | + NoSuchMethodException | + SecurityException | + InvocationTargetException e) { return null; } } @@ -215,7 +233,11 @@ private static void setHandled (Object event) try { Method handled = eventClass.getMethod("setHandled", boolean.class); handled.invoke(event, true); - } catch (Exception e) { + } catch (IllegalAccessException | + IllegalArgumentException | + NoSuchMethodException | + SecurityException | + InvocationTargetException e) { } } } diff --git a/src/main/org/audiveris/omr/ui/MainGui.java b/src/main/org/audiveris/omr/ui/MainGui.java index 6d2281f36..76a1a8477 100644 --- a/src/main/org/audiveris/omr/ui/MainGui.java +++ b/src/main/org/audiveris/omr/ui/MainGui.java @@ -40,6 +40,8 @@ import org.audiveris.omr.sig.ui.SigPainter; import org.audiveris.omr.step.ui.StepMenu; import org.audiveris.omr.step.ui.StepMonitoring; +import org.audiveris.omr.text.OCR; +import org.audiveris.omr.text.OcrUtil; import org.audiveris.omr.ui.action.ActionManager; import org.audiveris.omr.ui.action.Actions; import org.audiveris.omr.ui.selection.MouseMovement; @@ -94,20 +96,17 @@ public class MainGui extends OmrGui implements EventSubscriber, PropertyChangeListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(MainGui.class); - //~ Instance fields ---------------------------------------------------------------------------- - // - /** Official name of the application. */ - private String appName; - /** Sheet tabbed pane, which may contain several views. */ public StubsController stubsController; + /** Official name of the application. */ + private String appName; + /** The related concrete frame. */ private JFrame frame; @@ -135,7 +134,6 @@ public class MainGui /** Map of class resources. */ private ResourceMap resources; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code MainGui} instance, to handle any user display and interaction. */ @@ -143,8 +141,6 @@ public MainGui () { } - //~ Methods ------------------------------------------------------------------------------------ - // //----------// // clearLog // //----------// @@ -308,8 +304,7 @@ public void onEvent (StubEvent stubEvent) } final SheetStub stub = stubEvent.getData(); - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () @@ -504,6 +499,10 @@ protected void startup () logger.info("{} version {}", WellKnowns.TOOL_NAME, WellKnowns.TOOL_REF); logger.info("\n{}", LogUtil.allInitialMessages()); + if (!OcrUtil.getOcr().isAvailable()) { + logger.warn("{} Check log file for more details.", OCR.NO_OCR); + } + // Make the OmrGui instance available for the other classes OMR.gui = this; @@ -533,36 +532,35 @@ protected void startup () //--------------// private void defineLayout () { - /* - * +=============================================================+ - * |toolKeyPanel . . . . . . . . . . . . . . . . . . . . . . . . | - * |+=================+=============================+===========+| - * || toolBar . . . . . . . . . .| progressBar . . .| Memory . .|| - * |+=================+=============================+===========+| - * +=============================================================+ - * | horiSplitPane . . . . . . . . . . . . . . . . . . . . . . . | - * |+=========================================+=================+| - * | . . . . . . . . . . . . . . . . . . . . .|boardsScrollPane || - * | +========================================+ . . . . . . . . || - * | | stubsController . . . . . . . . . . . .| . . . . . . . . || - * | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * |m| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * |a| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * |i| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * |n| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || - * |P+=====================+==================+ . . . . . . . . || - * |a| logPane . . . . . . | errors . . . . . | . . . . . . . . || - * |n| . . . . . . . . . . |. . . . . . . . . | . . . . . . . . || - * |e| . . . . . . . . . . |. . . . . . . . . | . . . . . . . . || - * | +=====================+==================+=================+| - * +=============================================================+ - */ + // +=============================================================+ + // |toolKeyPanel . . . . . . . . . . . . . . . . . . . . . . . . | + // |+=================+=============================+===========+| + // || toolBar . . . . . . . . . .| progressBar . . .| Memory . .|| + // |+=================+=============================+===========+| + // +=============================================================+ + // | horiSplitPane . . . . . . . . . . . . . . . . . . . . . . . | + // |+=========================================+=================+| + // | . . . . . . . . . . . . . . . . . . . . .|boardsScrollPane || + // | +========================================+ . . . . . . . . || + // | | stubsController . . . . . . . . . . . .| . . . . . . . . || + // | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // |m| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // |a| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // |i| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // |n| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || + // |P+=====================+==================+ . . . . . . . . || + // |a| logPane . . . . . . | errors . . . . . | . . . . . . . . || + // |n| . . . . . . . . . . |. . . . . . . . . | . . . . . . . . || + // |e| . . . . . . . . . . |. . . . . . . . . | . . . . . . . . || + // | +=====================+==================+=================+| + // +=============================================================+ + // // Individual panes logPane = new LogPane(); @@ -717,12 +715,10 @@ private void defineMenus () */ private boolean needBottomPane () { - return GuiActions.getInstance().isLogWindowDisplayed() - || GuiActions.getInstance().isErrorsWindowDisplayed(); + return GuiActions.getInstance().isLogWindowDisplayed() || GuiActions.getInstance() + .isErrorsWindowDisplayed(); } - //~ Inner Classes ------------------------------------------------------------------------------ - // //------------------// // BoardsScrollPane // //------------------// @@ -733,7 +729,6 @@ private boolean needBottomPane () private static class BoardsScrollPane extends JScrollPane { - //~ Methods -------------------------------------------------------------------------------- public void setBoards (JComponent boards) { @@ -745,10 +740,9 @@ public void setBoards (JComponent boards) //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean preloadCostlyPackages = new Constant.Boolean( true, @@ -764,13 +758,11 @@ private static final class Constants private static class GuiExitListener implements ExitListener { - //~ Constructors --------------------------------------------------------------------------- - public GuiExitListener () + GuiExitListener () { } - //~ Methods -------------------------------------------------------------------------------- @Override public boolean canExit (EventObject eo) { @@ -799,7 +791,7 @@ public void willExit (EventObject eo) int count = 0; // NB: Use a COPY of instances, to avoid concurrent modification - for (Book book : new ArrayList(OMR.engine.getAllBooks())) { + for (Book book : new ArrayList<>(OMR.engine.getAllBooks())) { book.close(); count++; } diff --git a/src/main/org/audiveris/omr/ui/MemoryMeter.java b/src/main/org/audiveris/omr/ui/MemoryMeter.java index 2811bde07..ce6835b2e 100644 --- a/src/main/org/audiveris/omr/ui/MemoryMeter.java +++ b/src/main/org/audiveris/omr/ui/MemoryMeter.java @@ -21,7 +21,6 @@ // package org.audiveris.omr.ui; -import org.audiveris.omr.OMR; import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; import org.audiveris.omr.ui.util.UIUtil; @@ -44,7 +43,6 @@ /** * Class {@code MemoryMeter} encapsulates the display of a linear memory meter in MB * (both used and total), together with a garbage-collection button. - * *

                                                                                                                                            * There is a alarm threshold that triggers a color switch to red whenever the used memory exceeds * the threshold. @@ -53,14 +51,12 @@ */ public class MemoryMeter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); /** A mega as 2**20 */ - private static final double MEGA = 1024 * 1024; + private static final double MEGA = 1_024 * 1_024; - //~ Instance fields ---------------------------------------------------------------------------- /** Default foreground color, when under alarm threshold */ private Color defaultForeground; @@ -85,7 +81,6 @@ public class MemoryMeter /** Last value for threshold, in order to save on display */ private int lastThreshold = 0; - //~ Constructors ------------------------------------------------------------------------------- //-------------// // MemoryMeter // //-------------// @@ -104,10 +99,12 @@ public MemoryMeter () } } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // collectGarbage // //----------------// + /** + * Manually trigger garbage collection. + */ @Action public void collectGarbage () { @@ -237,19 +234,17 @@ public void run () monitorThread.start(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ /** Display period */ private final Constant.Integer samplingPeriod = new Constant.Integer( "MilliSeconds", - 2000, + 2_000, "Memory display period"); /** Alarm threshold ratio */ diff --git a/src/main/org/audiveris/omr/ui/OmrGlassPane.java b/src/main/org/audiveris/omr/ui/OmrGlassPane.java index e9413d623..219a0eb06 100644 --- a/src/main/org/audiveris/omr/ui/OmrGlassPane.java +++ b/src/main/org/audiveris/omr/ui/OmrGlassPane.java @@ -1,94 +1,92 @@ -//------------------------------------------------------------------------------------------------// -// // -// O m r G l a s s P a n e // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.ui; - -import org.audiveris.omr.ui.dnd.GhostGlassPane; -import org.audiveris.omr.ui.dnd.ScreenPoint; - -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Point; -import java.awt.Rectangle; - -/** - * Class {@code OmrGlassPane} is a GhostGlassPane to draw draggable shape plus - * related decoration (such as a link to some neighboring entity). - * - * @author Hervé Bitteur - */ -public class OmrGlassPane - extends GhostGlassPane -{ - //~ Instance fields ---------------------------------------------------------------------------- - - /** Reference point relative to this glassPane. */ - private Point reference; - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Set current reference point. - * - * @param reference the reference (screen-based) point to set - */ - public void setReference (ScreenPoint reference) - { - if (reference != null) { - this.reference = reference.getLocalPoint(this); - } else { - this.reference = null; - } - } - - /** - * On top of inter image, draw a link from inter center to reference point. - * - * @param g graphic environment - */ - @Override - public void paintComponent (Graphics g) - { - super.paintComponent(g); - - if (reference != null && overTarget) { - g.setColor(Color.RED); - g.drawLine(localPoint.x, localPoint.y, reference.x, reference.y); - } - } - - /** - * The scene is composed of inter image plus reference point if any. - * - * @param center inter center - * @return bounding box of inter + reference point if any - */ - @Override - protected Rectangle getSceneBounds (Point center) - { - Rectangle rect = getImageBounds(center); - - if (reference != null) { - rect.add(reference); - } - - return rect; - } -} +//------------------------------------------------------------------------------------------------// +// // +// O m r G l a s s P a n e // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.ui; + +import org.audiveris.omr.ui.dnd.GhostGlassPane; +import org.audiveris.omr.ui.dnd.ScreenPoint; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; + +/** + * Class {@code OmrGlassPane} is a GhostGlassPane to draw draggable shape plus + * related decoration (such as a link to some neighboring entity). + * + * @author Hervé Bitteur + */ +public class OmrGlassPane + extends GhostGlassPane +{ + + /** Reference point relative to this glassPane. */ + private Point reference; + + /** + * On top of inter image, draw a link from inter center to reference point. + * + * @param g graphic environment + */ + @Override + public void paintComponent (Graphics g) + { + super.paintComponent(g); + + if ((reference != null) && overTarget) { + g.setColor(Color.RED); + g.drawLine(localPoint.x, localPoint.y, reference.x, reference.y); + } + } + + /** + * Set current reference point. + * + * @param reference the reference (screen-based) point to set + */ + public void setReference (ScreenPoint reference) + { + if (reference != null) { + this.reference = reference.getLocalPoint(this); + } else { + this.reference = null; + } + } + + /** + * The scene is composed of inter image plus reference point if any. + * + * @param center inter center + * @return bounding box of inter + reference point if any + */ + @Override + protected Rectangle getSceneBounds (Point center) + { + Rectangle rect = getImageBounds(center); + + if (reference != null) { + rect.add(reference); + } + + return rect; + } +} diff --git a/src/main/org/audiveris/omr/ui/OmrGui.java b/src/main/org/audiveris/omr/ui/OmrGui.java index 9dfe78515..0ef349c7b 100644 --- a/src/main/org/audiveris/omr/ui/OmrGui.java +++ b/src/main/org/audiveris/omr/ui/OmrGui.java @@ -46,7 +46,6 @@ public abstract class OmrGui extends SingleFrameApplication { - //~ Methods ------------------------------------------------------------------------------------ /** * Erase the content of the log window (but not the log itself). diff --git a/src/main/org/audiveris/omr/ui/OmrUIDefaults.java b/src/main/org/audiveris/omr/ui/OmrUIDefaults.java index 76e08ded5..43a100e8a 100644 --- a/src/main/org/audiveris/omr/ui/OmrUIDefaults.java +++ b/src/main/org/audiveris/omr/ui/OmrUIDefaults.java @@ -43,22 +43,6 @@ public class OmrUIDefaults extends UIDefaults { - //~ Static fields/initializers ----------------------------------------------------------------- - - private static volatile OmrUIDefaults INSTANCE; - - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // getInstance // - //-------------// - public static OmrUIDefaults getInstance () - { - if (INSTANCE == null) { - INSTANCE = new OmrUIDefaults(); - } - - return INSTANCE; - } //------------// // getKeyCode // @@ -108,10 +92,11 @@ public void loadFrom (Properties properties) * @param file properties file path without locale or country information * or .properties extension * @throws FileNotFoundException if the specified file could not be found - * @throws IOException if the specified file could be read + * @throws IOException if the specified file could be read */ public void loadFrom (File file) - throws FileNotFoundException, IOException + throws FileNotFoundException, + IOException { String path = file.getPath(); StringBuilder b = new StringBuilder(path); @@ -130,19 +115,33 @@ public void loadFrom (File file) } Properties p = new Properties(); - InputStream in = null; - try { - in = new FileInputStream(file); + try (InputStream in = new FileInputStream(file)) { p.load(in); loadFrom(p); - } finally { - if (in != null) { - try { - in.close(); - } catch (Exception ignored) { - } - } } } + + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of this class in application. + * + * @return the instance + */ + public static OmrUIDefaults getInstance () + { + return LazySingleton.INSTANCE; + } + + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { + + static final OmrUIDefaults INSTANCE = new OmrUIDefaults(); + } + } diff --git a/src/main/org/audiveris/omr/ui/Options.java b/src/main/org/audiveris/omr/ui/Options.java index 0f404e7dd..da5846b34 100644 --- a/src/main/org/audiveris/omr/ui/Options.java +++ b/src/main/org/audiveris/omr/ui/Options.java @@ -64,11 +64,9 @@ */ public class Options { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Options.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The interface window. */ private final JFrame frame; @@ -91,7 +89,7 @@ public class Options private Integer rowIndex; /** Listener on searchField modif. */ - private DocumentListener docListener = new DocumentListener() + private final DocumentListener docListener = new DocumentListener() { @Override public void changedUpdate (DocumentEvent e) @@ -207,7 +205,6 @@ public void actionPerformed (ActionEvent e) } }; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Options object. */ @@ -290,8 +287,7 @@ public Options () resource.injectComponents(frame); // Make sure the search entry field gets the focus at creation time - frame.addWindowListener( - new WindowAdapter() + frame.addWindowListener(new WindowAdapter() { @Override public void windowOpened (WindowEvent e) @@ -301,7 +297,6 @@ public void windowOpened (WindowEvent e) }); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // getComponent // //--------------// diff --git a/src/main/org/audiveris/omr/ui/PixelCount.java b/src/main/org/audiveris/omr/ui/PixelCount.java index 2617511c6..b46c6a556 100644 --- a/src/main/org/audiveris/omr/ui/PixelCount.java +++ b/src/main/org/audiveris/omr/ui/PixelCount.java @@ -25,11 +25,12 @@ /** * A subclass of Constant.Integer, meant to store a number of pixels. + * + * @author Hervé Bitteur */ public class PixelCount extends Constant.Integer { - //~ Constructors ------------------------------------------------------------------------------- /** * Specific constructor, where 'unit' and 'name' are assigned later. diff --git a/src/main/org/audiveris/omr/ui/SpinnerIdModel.java b/src/main/org/audiveris/omr/ui/SpinnerIdModel.java index e23370641..8661892bd 100644 --- a/src/main/org/audiveris/omr/ui/SpinnerIdModel.java +++ b/src/main/org/audiveris/omr/ui/SpinnerIdModel.java @@ -35,24 +35,20 @@ * Class {@code SpinnerIdModel} is an ID spinner model backed by a {@link EntityIndex}. * * @author Hervé Bitteur - * * @param precise type for handled entity */ public class SpinnerIdModel extends AbstractSpinnerModel { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SpinnerIdModel.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying entity index. */ private final EntityIndex index; /** Current entity id value. */ private Integer currentId; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SpinnerIdModel} object. * @@ -63,7 +59,6 @@ public SpinnerIdModel (EntityIndex index) this.index = index; } - //~ Methods ------------------------------------------------------------------------------------ @Override public Integer getNextValue () { diff --git a/src/main/org/audiveris/omr/ui/ViewParameters.java b/src/main/org/audiveris/omr/ui/ViewParameters.java index 73ae638c5..662f28c17 100644 --- a/src/main/org/audiveris/omr/ui/ViewParameters.java +++ b/src/main/org/audiveris/omr/ui/ViewParameters.java @@ -50,12 +50,11 @@ public class ViewParameters extends AbstractBean { - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(ViewParameters.class); private static final Constants constants = new Constants(); + private static final Logger logger = LoggerFactory.getLogger(ViewParameters.class); + /** Should the annotations be painted. */ public static final String ANNOTATION_PAINTING = "annotationPainting"; @@ -104,82 +103,6 @@ public class ViewParameters /** Should the inters be painted with grade-based translucency in input view. */ public static final String TRANSLUCENT_PAINTING = "translucentPainting"; - //~ Enumerations ------------------------------------------------------------------------------- - /** - * Enum {@code PaintingLayer} defines layers to be painted. - */ - public static enum PaintingLayer - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Input: image or glyphs. */ - INPUT, - /** Union of input and output. */ - INPUT_OUTPUT, - /** Output: score entities. */ - OUTPUT; - //~ Instance fields ------------------------------------------------------------------------ - - /** Icon assigned to layer. */ - private Icon icon; - - //~ Methods -------------------------------------------------------------------------------- - /** - * Lazily building of layer icon. - * - * @return the layer icon - */ - public Icon getIcon () - { - if (icon == null) { - ResourceMap resource = Application.getInstance().getContext().getResourceMap( - ViewParameters.class); - - String key = getClass().getSimpleName() + "." + this + ".icon"; - String resourceName = resource.getString(key); - icon = new ImageIcon(ViewParameters.class.getResource(resourceName)); - } - - return icon; - } - } - - /** - * Enum {@code SelectionMode} defines type of entities to be selected. - */ - public static enum SelectionMode - { - //~ Enumeration constant initializers ------------------------------------------------------ - - MODE_GLYPH, - MODE_INTER, - MODE_SECTION; - - //~ Instance fields ------------------------------------------------------------------------ - /** Icon assigned to mode. */ - private Icon icon; - - //~ Methods -------------------------------------------------------------------------------- - /** - * Lazily building of mode icon. - * - * @return the mode icon - */ - public Icon getIcon () - { - if (icon == null) { - ResourceMap resource = Application.getInstance().getContext() - .getResourceMap(ViewParameters.class); - String key = getClass().getSimpleName() + "." + this + ".icon"; - String resourceName = resource.getString(key); - icon = new ImageIcon(ViewParameters.class.getResource(resourceName)); - } - - return icon; - } - } - - //~ Instance fields ---------------------------------------------------------------------------- /** Action for switching layers. (Must be lazily computed) */ private ApplicationAction layerAction; @@ -204,15 +127,6 @@ public Icon getIcon () /** Staff peak painting is chosen to be not persistent. */ private boolean staffPeakPainting = false; - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // getInstance // - //-------------// - public static ViewParameters getInstance () - { - return Holder.INSTANCE; - } - //------------------// // getPaintingLayer // //------------------// @@ -221,6 +135,16 @@ public PaintingLayer getPaintingLayer () return paintingLayer; } + //------------------// + // setPaintingLayer // + //------------------// + public void setPaintingLayer (PaintingLayer value) + { + PaintingLayer oldValue = getPaintingLayer(); + paintingLayer = value; + firePropertyChange(LAYER_PAINTING, oldValue, value); + } + //------------------// // getSelectionMode // //------------------// @@ -229,6 +153,16 @@ public SelectionMode getSelectionMode () return selectionMode; } + //------------------// + // setSelectionMode // + //------------------// + public void setSelectionMode (SelectionMode value) + { + SelectionMode oldValue = getSelectionMode(); + selectionMode = value; + firePropertyChange(SELECTION_MODE, oldValue, value); + } + //----------------------// // isAnnotationPainting // //----------------------// @@ -237,6 +171,16 @@ public boolean isAnnotationPainting () return constants.annotationPainting.getValue(); } + //-----------------------// + // setAnnotationPainting // + //-----------------------// + public void setAnnotationPainting (boolean value) + { + boolean oldValue = constants.annotationPainting.getValue(); + constants.annotationPainting.setValue(value); + firePropertyChange(ANNOTATION_PAINTING, oldValue, value); + } + //----------------------// // isAttachmentPainting // //----------------------// @@ -245,6 +189,16 @@ public boolean isAttachmentPainting () return constants.attachmentPainting.getValue(); } + //-----------------------// + // setAttachmentPainting // + //-----------------------// + public void setAttachmentPainting (boolean value) + { + boolean oldValue = constants.attachmentPainting.getValue(); + constants.attachmentPainting.setValue(value); + firePropertyChange(ATTACHMENT_PAINTING, oldValue, value); + } + //-----------------// // isErrorPainting // //-----------------// @@ -253,6 +207,16 @@ public boolean isErrorPainting () return errorPainting; } + //------------------// + // setErrorPainting // + //------------------// + public void setErrorPainting (boolean value) + { + boolean oldValue = errorPainting; + errorPainting = value; + firePropertyChange(ERROR_PAINTING, oldValue, value); + } + //-----------------// // isInputPainting // //-----------------// @@ -269,124 +233,6 @@ public boolean isInvalidSheetDisplay () return constants.invalidSheetDisplay.getValue(); } - //---------------------// - // isLetterBoxPainting // - //---------------------// - public boolean isLetterBoxPainting () - { - return constants.letterBoxPainting.getValue(); - } - - //----------------// - // isLinePainting // - //----------------// - public boolean isLinePainting () - { - return constants.linePainting.getValue(); - } - - //----------------// - // isMarkPainting // - //----------------// - public boolean isMarkPainting () - { - return constants.markPainting.getValue(); - } - - //------------------// - // isOutputPainting // - //------------------// - public boolean isOutputPainting () - { - return (paintingLayer == INPUT_OUTPUT) || (paintingLayer == OUTPUT); - } - - //--------------------// - // isSentencePainting // - //--------------------// - public boolean isSentencePainting () - { - return constants.sentencePainting.getValue(); - } - - //----------------// - // isSlotPainting // - //----------------// - public boolean isSlotPainting () - { - return constants.slotPainting.getValue(); - } - - //---------------------// - // isStaffLinePainting // - //---------------------// - public boolean isStaffLinePainting () - { - return staffLinePainting; - } - - //---------------------// - // isStaffPeakPainting // - //---------------------// - public boolean isStaffPeakPainting () - { - return staffPeakPainting; - } - - //-----------------------// - // isTranslationPainting // - //-----------------------// - public boolean isTranslationPainting () - { - return constants.translationPainting.getValue(); - } - - //-----------------------// - // isTranslucentPainting // - //-----------------------// - public boolean isTranslucentPainting () - { - return constants.translucentPainting.getValue(); - } - - //-----------------// - // isVoicePainting // - //-----------------// - public boolean isVoicePainting () - { - return voicePainting; - } - - //-----------------------// - // setAnnotationPainting // - //-----------------------// - public void setAnnotationPainting (boolean value) - { - boolean oldValue = constants.annotationPainting.getValue(); - constants.annotationPainting.setValue(value); - firePropertyChange(ANNOTATION_PAINTING, oldValue, value); - } - - //-----------------------// - // setAttachmentPainting // - //-----------------------// - public void setAttachmentPainting (boolean value) - { - boolean oldValue = constants.attachmentPainting.getValue(); - constants.attachmentPainting.setValue(value); - firePropertyChange(ATTACHMENT_PAINTING, oldValue, value); - } - - //------------------// - // setErrorPainting // - //------------------// - public void setErrorPainting (boolean value) - { - boolean oldValue = errorPainting; - errorPainting = value; - firePropertyChange(ERROR_PAINTING, oldValue, value); - } - //------------------------// // setInvalidSheetDisplay // //------------------------// @@ -397,6 +243,14 @@ public void setInvalidSheetDisplay (boolean value) firePropertyChange(INVALID_SHEET_DISPLAY, oldValue, value); } + //---------------------// + // isLetterBoxPainting // + //---------------------// + public boolean isLetterBoxPainting () + { + return constants.letterBoxPainting.getValue(); + } + //----------------------// // setLetterBoxPainting // //----------------------// @@ -407,6 +261,14 @@ public void setLetterBoxPainting (boolean value) firePropertyChange(LETTER_BOX_PAINTING, oldValue, value); } + //----------------// + // isLinePainting // + //----------------// + public boolean isLinePainting () + { + return constants.linePainting.getValue(); + } + //-----------------// // setLinePainting // //-----------------// @@ -417,6 +279,14 @@ public void setLinePainting (boolean value) firePropertyChange(LINE_PAINTING, oldValue, value); } + //----------------// + // isMarkPainting // + //----------------// + public boolean isMarkPainting () + { + return constants.markPainting.getValue(); + } + //-----------------// // setMarkPainting // //-----------------// @@ -428,23 +298,19 @@ public void setMarkPainting (boolean value) } //------------------// - // setPaintingLayer // + // isOutputPainting // //------------------// - public void setPaintingLayer (PaintingLayer value) + public boolean isOutputPainting () { - PaintingLayer oldValue = getPaintingLayer(); - paintingLayer = value; - firePropertyChange(LAYER_PAINTING, oldValue, value); + return (paintingLayer == INPUT_OUTPUT) || (paintingLayer == OUTPUT); } - //------------------// - // setSelectionMode // - //------------------// - public void setSelectionMode (SelectionMode value) + //--------------------// + // isSentencePainting // + //--------------------// + public boolean isSentencePainting () { - SelectionMode oldValue = getSelectionMode(); - selectionMode = value; - firePropertyChange(SELECTION_MODE, oldValue, value); + return constants.sentencePainting.getValue(); } //---------------------// @@ -457,6 +323,14 @@ public void setSentencePainting (boolean value) firePropertyChange(SENTENCE_PAINTING, oldValue, value); } + //----------------// + // isSlotPainting // + //----------------// + public boolean isSlotPainting () + { + return constants.slotPainting.getValue(); + } + //-----------------// // setSlotPainting // //-----------------// @@ -467,6 +341,14 @@ public void setSlotPainting (boolean value) firePropertyChange(SLOT_PAINTING, oldValue, value); } + //---------------------// + // isStaffLinePainting // + //---------------------// + public boolean isStaffLinePainting () + { + return staffLinePainting; + } + //----------------------// // setStaffLinePainting // //----------------------// @@ -477,6 +359,14 @@ public void setStaffLinePainting (boolean value) firePropertyChange(STAFF_LINE_PAINTING, oldValue, value); } + //---------------------// + // isStaffPeakPainting // + //---------------------// + public boolean isStaffPeakPainting () + { + return staffPeakPainting; + } + //----------------------// // setStaffPeakPainting // //----------------------// @@ -487,6 +377,14 @@ public void setStaffPeakPainting (boolean value) firePropertyChange(STAFF_PEAK_PAINTING, oldValue, value); } + //-----------------------// + // isTranslationPainting // + //-----------------------// + public boolean isTranslationPainting () + { + return constants.translationPainting.getValue(); + } + //------------------------// // setTranslationPainting // //------------------------// @@ -497,6 +395,14 @@ public void setTranslationPainting (boolean value) firePropertyChange(TRANSLATION_PAINTING, oldValue, value); } + //-----------------------// + // isTranslucentPainting // + //-----------------------// + public boolean isTranslucentPainting () + { + return constants.translucentPainting.getValue(); + } + //------------------------// // setTranslucentPainting // //------------------------// @@ -507,6 +413,14 @@ public void setTranslucentPainting (boolean value) firePropertyChange(TRANSLUCENT_PAINTING, oldValue, value); } + //-----------------// + // isVoicePainting // + //-----------------// + public boolean isVoicePainting () + { + return voicePainting; + } + //------------------// // setVoicePainting // //------------------// @@ -759,25 +673,100 @@ public void toggleVoices (ActionEvent e) { } - //~ Inner Interfaces --------------------------------------------------------------------------- - //--------// - // Holder // - //--------// - private static interface Holder + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of this class in application. + * + * @return the instance + */ + public static ViewParameters getInstance () + { + return LazySingleton.INSTANCE; + } + + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { + + static final ViewParameters INSTANCE = new ViewParameters(); + } + + /** + * Enum {@code PaintingLayer} defines layers to be painted. + */ + public static enum PaintingLayer { - //~ Static fields/initializers ------------------------------------------------------------- + /** Input: image or glyphs. */ + INPUT, + /** Union of input and output. */ + INPUT_OUTPUT, + /** Output: score entities. */ + OUTPUT; - public static final ViewParameters INSTANCE = new ViewParameters(); + /** Icon assigned to layer. */ + private Icon icon; + + /** + * Lazily building of layer icon. + * + * @return the layer icon + */ + public Icon getIcon () + { + if (icon == null) { + ResourceMap resource = Application.getInstance().getContext().getResourceMap( + ViewParameters.class); + + String key = getClass().getSimpleName() + "." + this + ".icon"; + String resourceName = resource.getString(key); + icon = new ImageIcon(ViewParameters.class.getResource(resourceName)); + } + + return icon; + } + } + + /** + * Enum {@code SelectionMode} defines type of entities to be selected. + */ + public static enum SelectionMode + { + MODE_GLYPH, + MODE_INTER, + MODE_SECTION; + + /** Icon assigned to mode. */ + private Icon icon; + + /** + * Lazily building of mode icon. + * + * @return the mode icon + */ + public Icon getIcon () + { + if (icon == null) { + ResourceMap resource = Application.getInstance().getContext().getResourceMap( + ViewParameters.class); + String key = getClass().getSimpleName() + "." + this + ".icon"; + String resourceName = resource.getString(key); + icon = new ImageIcon(ViewParameters.class.getResource(resourceName)); + } + + return icon; + } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean annotationPainting = new Constant.Boolean( true, diff --git a/src/main/org/audiveris/omr/ui/action/ActionDescriptor.java b/src/main/org/audiveris/omr/ui/action/ActionDescriptor.java index bf9b9b57c..06a3d9bc4 100644 --- a/src/main/org/audiveris/omr/ui/action/ActionDescriptor.java +++ b/src/main/org/audiveris/omr/ui/action/ActionDescriptor.java @@ -36,7 +36,6 @@ @XmlRootElement(name = "action") public class ActionDescriptor { - //~ Instance fields ---------------------------------------------------------------------------- /** Which UI domain (menu) should host this action. */ @XmlAttribute(name = "domain") @@ -62,6 +61,7 @@ public class ActionDescriptor @XmlAttribute(name = "method") public String methodName; + /** Containing topic for this action. */ @XmlAttribute(name = "topic") public AdvancedTopics.Topic topic; @@ -73,7 +73,6 @@ public class ActionDescriptor @XmlAttribute(name = "button") public String buttonClassName; - //~ Constructors ------------------------------------------------------------------------------- /** * To force instantiation through JAXB unmarshalling only. */ @@ -81,7 +80,6 @@ private ActionDescriptor () { } - //~ Methods ------------------------------------------------------------------------------------ //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/ui/action/ActionManager.java b/src/main/org/audiveris/omr/ui/action/ActionManager.java index ad6d78cbd..25605d23c 100644 --- a/src/main/org/audiveris/omr/ui/action/ActionManager.java +++ b/src/main/org/audiveris/omr/ui/action/ActionManager.java @@ -36,6 +36,7 @@ import java.awt.Component; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URI; @@ -59,20 +60,14 @@ */ public class ActionManager { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ActionManager.class); /** Class loader. */ private static final ClassLoader classLoader = ActionManager.class.getClassLoader(); - /** Singleton. */ - private static volatile ActionManager INSTANCE; - - //~ Instance fields ---------------------------------------------------------------------------- - // /** The map of all menus, so that we can directly provide some. */ - private final Map menuMap = new HashMap(); + private final Map menuMap = new HashMap<>(); /** The tool bar that hosts some actions. */ private final JToolBar toolBar = new JToolBar(); @@ -80,7 +75,6 @@ public class ActionManager /** The menu bar for all actions. */ private final JMenuBar menuBar = new JMenuBar(); - //~ Constructors ------------------------------------------------------------------------------- /** * Meant to be instantiated at most once. */ @@ -88,24 +82,6 @@ private ActionManager () { } - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // getInstance // - //-------------// - /** - * Report the single action manager instance. - * - * @return the unique instance of this class - */ - public static ActionManager getInstance () - { - if (INSTANCE == null) { - INSTANCE = new ActionManager(); - } - - return INSTANCE; - } - //-------------------// // getActionInstance // //-------------------// @@ -203,17 +179,16 @@ public void loadAllDescriptors () // Load classes first for system actions, then for user actions if any URI[] uris = new URI[]{ UriUtil.toURI(WellKnowns.RES_URI, "system-actions.xml"), - WellKnowns.CONFIG_FOLDER.resolve("user-actions.xml").toUri().normalize() - }; + WellKnowns.CONFIG_FOLDER.resolve("user-actions.xml").toUri().normalize()}; for (int i = 0; i < uris.length; i++) { URI uri = uris[i]; try { URL url = uri.toURL(); - InputStream input = url.openStream(); - Actions.loadActionDescriptors(input); - input.close(); + try (InputStream input = url.openStream()) { + Actions.loadActionDescriptors(input); + } } catch (IOException ex) { // Item does not exist if (i == 0) { @@ -333,7 +308,12 @@ private ApplicationAction registerAction (ActionDescriptor desc) } else { logger.error("Unknown action {} in class {}", desc.methodName, desc.className); } - } catch (Throwable ex) { + } catch (ClassNotFoundException | + IllegalAccessException | + IllegalArgumentException | + InstantiationException | + SecurityException | + InvocationTargetException ex) { logger.warn("Error while registering " + desc, ex); } @@ -396,11 +376,35 @@ private void registerDomainActions (String domain, item.setName(desc.menuName); menu.add(item); } - } catch (Throwable ex) { + } catch (ClassNotFoundException | + IllegalAccessException | + InstantiationException ex) { logger.warn("Error with " + desc.itemClassName, ex); } } } } } + + //-------------// + // getInstance // + //-------------// + /** + * Report the single instance of this class in application. + * + * @return the instance + */ + public static ActionManager getInstance () + { + return LazySingleton.INSTANCE; + } + + //---------------// + // LazySingleton // + //---------------// + private static class LazySingleton + { + + static final ActionManager INSTANCE = new ActionManager(); + } } diff --git a/src/main/org/audiveris/omr/ui/action/Actions.java b/src/main/org/audiveris/omr/ui/action/Actions.java index 015272da6..9c27c57dc 100644 --- a/src/main/org/audiveris/omr/ui/action/Actions.java +++ b/src/main/org/audiveris/omr/ui/action/Actions.java @@ -50,7 +50,6 @@ @XmlRootElement(name = "actions") public class Actions { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Actions.class); @@ -58,55 +57,17 @@ public class Actions private static volatile JAXBContext jaxbContext; /** The collection of all actions loaded so far. */ - private static final Set allDescriptors = new LinkedHashSet(); + private static final Set allDescriptors = new LinkedHashSet<>(); - //~ Enumerations ------------------------------------------------------------------------------- - /** - * Predefined list of domain names. - * Through the action list files, the user will be able to add new domain names. - * This classification is mainly used to define the related pull-down menus. - */ - public static enum Domain - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Domain of file actions */ - FILE, - /** Domain of book actions */ - BOOK, - /** Domain of sheet actions */ - SHEET, - /** Domain of individual steps */ - STEP, - /** Domain of various view features */ - VIEW, - /** Domain of utilities */ - TOOL, - /** Domain of plugins */ - PLUGIN, - /** Domain of help information */ - HELP; - //~ Constructors --------------------------------------------------------------------------- - - Domain () - { - } - } - - //~ Instance fields ---------------------------------------------------------------------------- - // /** Collection of descriptors loaded by unmarshalling one file. */ @XmlElement(name = "action") - private List descriptors = new ArrayList(); + private final List descriptors = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** No-arg constructor meant for JAXB. */ private Actions () { } - //~ Methods ------------------------------------------------------------------------------------ - // //-------------------// // getAllDescriptors // //-------------------// @@ -131,7 +92,7 @@ public static Set getAllDescriptors () */ public static Set getDomainNames () { - Set names = new LinkedHashSet(); + Set names = new LinkedHashSet<>(); // Predefined ones, except HELP for (Domain domain : Domain.values()) { @@ -163,7 +124,7 @@ public static Set getDomainNames () */ public static SortedSet getSections () { - SortedSet sections = new TreeSet(); + SortedSet sections = new TreeSet<>(); for (ActionDescriptor desc : allDescriptors) { sections.add(desc.section); @@ -212,10 +173,38 @@ public static void loadActionDescriptors (InputStream in) logger.warn("No section specified for {}", desc); it.remove(); - continue; } } allDescriptors.addAll(actions.descriptors); } + + /** + * Predefined list of domain names. + * Through the action list files, the user will be able to add new domain names. + * This classification is mainly used to define the related pull-down menus. + */ + public static enum Domain + { + /** Domain of file actions */ + FILE, + /** Domain of book actions */ + BOOK, + /** Domain of sheet actions */ + SHEET, + /** Domain of individual steps */ + STEP, + /** Domain of various view features */ + VIEW, + /** Domain of utilities */ + TOOL, + /** Domain of plugins */ + PLUGIN, + /** Domain of help information */ + HELP; + + Domain () + { + } + } } diff --git a/src/main/org/audiveris/omr/ui/action/AdvancedTopics.java b/src/main/org/audiveris/omr/ui/action/AdvancedTopics.java index 0ea42f581..ab97d21b5 100644 --- a/src/main/org/audiveris/omr/ui/action/AdvancedTopics.java +++ b/src/main/org/audiveris/omr/ui/action/AdvancedTopics.java @@ -1,336 +1,327 @@ -//------------------------------------------------------------------------------------------------// -// // -// A d v a n c e d T o p i c s // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.ui.action; - -import com.jgoodies.forms.builder.PanelBuilder; -import com.jgoodies.forms.layout.CellConstraints; -import com.jgoodies.forms.layout.FormLayout; - -import org.audiveris.omr.constant.Constant; -import org.audiveris.omr.constant.ConstantSet; -import org.audiveris.omr.plugin.PluginsManager; -import org.audiveris.omr.sheet.ui.StubsController; -import org.audiveris.omr.step.Step; -import org.audiveris.omr.ui.util.Panel; - -import org.jdesktop.application.Application; -import org.jdesktop.application.ResourceMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.WindowConstants; - -/** - * Class {@code AdvancedTopics} gathers all topics that are relevant for advanced users - * or developers only. - * - * @author Hervé Bitteur - */ -public abstract class AdvancedTopics -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Constants constants = new Constants(); - - private static final Logger logger = LoggerFactory.getLogger(AdvancedTopics.class); - - /** Layout for 2 items. */ - private static final FormLayout layout2 = new FormLayout("68dlu,15dlu,pref", "pref"); - - /** Layout for 3 items. */ - private static final FormLayout layout3 = new FormLayout("12dlu,1dlu,65dlu,2dlu,pref", "pref"); - - //~ Enumerations ------------------------------------------------------------------------------- - public static enum Topic - { - //~ Enumeration constant initializers ------------------------------------------------------ - - SAMPLES(constants.useSamples), - ANNOTATIONS(constants.useAnnotations), - PLOTS(constants.usePlots), - SPECIFIC_VIEWS(constants.useSpecificViews), - SPECIFIC_ITEMS(constants.useSpecificItems), - WINDOW_LAYOUT(constants.useWindowLayout), - DEBUG(constants.useDebug); - //~ Instance fields ------------------------------------------------------------------------ - - /** Underlying constant. */ - private final Constant.Boolean constant; - - //~ Constructors --------------------------------------------------------------------------- - Topic (Constant.Boolean constant) - { - this.constant = constant; - } - - //~ Methods -------------------------------------------------------------------------------- - public String getDescription () - { - return constant.getDescription(); - } - - public boolean isSet () - { - return constant.isSet(); - } - - public void set (boolean val) - { - constant.setValue(val); - } - } - - //~ Methods ------------------------------------------------------------------------------------ - //--------------// - // getComponent // - //--------------// - /** - * Report the selection dialog to be displayed to the user - * - * @return the dialog frame - */ - public static JFrame getComponent () - { - final JFrame frame = new JFrame(); - frame.setName("topicsFrame"); - frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - - JComponent framePane = (JComponent) frame.getContentPane(); - - Panel panel = new Panel(); - FormLayout layout = new FormLayout("pref", "pref, 1dlu, pref, 1dlu, pref"); - PanelBuilder builder = new PanelBuilder(layout, panel); - CellConstraints cst = new CellConstraints(); - int r = 1; - builder.add(new EarlyPane(), cst.xy(1, r)); - - r += 2; - builder.add(new PluginPane(), cst.xy(1, r)); - - r += 2; - builder.add(new AllTopicsPane(), cst.xy(1, r)); - - framePane.add(panel); - - // Resources injection - ResourceMap resource = Application.getInstance().getContext() - .getResourceMap(AdvancedTopics.class); - resource.injectComponents(frame); - - return frame; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //---------------// - // AllTopicsPane // - //---------------// - /** - * Pane for the advanced topic switches. - */ - private static final class AllTopicsPane - extends JPanel - { - //~ Constructors --------------------------------------------------------------------------- - - public AllTopicsPane () - { - setBorder(BorderFactory.createTitledBorder("These switches require a restart")); - - FormLayout layout = new FormLayout("pref", Panel.makeRows(Topic.values().length)); - PanelBuilder builder = new PanelBuilder(layout, this); - CellConstraints cst = new CellConstraints(); - int r = 1; - - for (Topic topic : Topic.values()) { - builder.add(new TopicPane(topic), cst.xy(1, r)); - r += 2; - } - } - } - - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Boolean useSamples = new Constant.Boolean( - false, - "Handling of samples repositories and classifier"); - - private final Constant.Boolean useAnnotations = new Constant.Boolean( - false, - "Production of image annotation with symbols"); - - private final Constant.Boolean usePlots = new Constant.Boolean( - false, - "Display of scale / stem / staves plots"); - - private final Constant.Boolean useSpecificViews = new Constant.Boolean( - false, - "Display of specific sheet views"); - - private final Constant.Boolean useSpecificItems = new Constant.Boolean( - false, - "Specific items shown in sheet view"); - - private final Constant.Boolean useWindowLayout = new Constant.Boolean( - false, - "Handling of main window layout"); - - private final Constant.Boolean useDebug = new Constant.Boolean( - false, - "Support for debug features"); - } - - //-----------// - // EarlyPane // - //-----------// - /** - * Which step should we trigger on any input image. - */ - private static class EarlyPane - extends Panel - implements ActionListener - { - //~ Instance fields ------------------------------------------------------------------------ - - private final JComboBox box; // ComboBox for desired step - - //~ Constructors --------------------------------------------------------------------------- - public EarlyPane () - { - box = new JComboBox(Step.values()); - box.setToolTipText("Which step to trigger on any image input"); - box.setSelectedItem(StubsController.getEarlyStep()); - box.addActionListener(this); - - PanelBuilder builder = new PanelBuilder(layout2, this); - CellConstraints cst = new CellConstraints(); - - final int r = 1; - builder.add(box, cst.xy(1, r)); - builder.add(new JLabel("Step triggered on image input"), cst.xy(3, r)); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void actionPerformed (ActionEvent e) - { - Step step = box.getItemAt(box.getSelectedIndex()); - StubsController.setEarlyStep(step); - } - } - - //------------// - // PluginPane // - //------------// - /** - * Which plugin should be the default one. - */ - private static class PluginPane - extends Panel - implements ActionListener - { - //~ Instance fields ------------------------------------------------------------------------ - - private final JComboBox box; // ComboBox for registered plugins - - //~ Constructors --------------------------------------------------------------------------- - public PluginPane () - { - // ComboBox for triggered step - box = new JComboBox( - PluginsManager.getInstance().getPluginIds().toArray(new String[0])); - box.setToolTipText("Default plugin to be launched"); - box.setSelectedItem(PluginsManager.defaultPluginId.getValue()); - - PanelBuilder builder = new PanelBuilder(layout2, this); - CellConstraints cst = new CellConstraints(); - - final int r = 1; - builder.add(box, cst.xy(1, r)); - builder.add(new JLabel("Plugin launched on MusicXML output"), cst.xy(3, r)); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void actionPerformed (ActionEvent e) - { - PluginsManager.defaultPluginId.setSpecific(box.getItemAt(box.getSelectedIndex())); - } - } - - //-----------// - // TopicPane // - //-----------// - /** - * Handling of one topic switch. - */ - private static final class TopicPane - extends Panel - implements ActionListener - { - //~ Instance fields ------------------------------------------------------------------------ - - final Topic topic; // Handled topic - - //~ Constructors --------------------------------------------------------------------------- - public TopicPane (Topic topic) - { - this.topic = topic; - - PanelBuilder builder = new PanelBuilder(layout3, this); - CellConstraints cst = new CellConstraints(); - - JCheckBox box = new JCheckBox(); - box.addActionListener(this); - box.setSelected(topic.isSet()); - - final int r = 1; - builder.add(box, cst.xy(1, r)); - builder.add(new JLabel(topic.name()), cst.xy(3, r)); - builder.add(new JLabel(topic.getDescription()), cst.xy(5, r)); - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public void actionPerformed (ActionEvent e) - { - JCheckBox box = (JCheckBox) e.getSource(); - topic.set(box.isSelected()); - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// A d v a n c e d T o p i c s // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.ui.action; + +import com.jgoodies.forms.builder.PanelBuilder; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; + +import org.audiveris.omr.constant.Constant; +import org.audiveris.omr.constant.ConstantSet; +import org.audiveris.omr.plugin.PluginsManager; +import org.audiveris.omr.sheet.ui.StubsController; +import org.audiveris.omr.step.Step; +import org.audiveris.omr.ui.util.Panel; + +import org.jdesktop.application.Application; +import org.jdesktop.application.ResourceMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collection; + +import javax.swing.BorderFactory; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.WindowConstants; + +/** + * Class {@code AdvancedTopics} gathers all topics that are relevant for advanced users + * or developers only. + * + * @author Hervé Bitteur + */ +public abstract class AdvancedTopics +{ + + private static final Constants constants = new Constants(); + + private static final Logger logger = LoggerFactory.getLogger(AdvancedTopics.class); + + /** Layout for 2 items. */ + private static final FormLayout layout2 = new FormLayout("68dlu,15dlu,pref", "pref"); + + /** Layout for 3 items. */ + private static final FormLayout layout3 = new FormLayout("12dlu,1dlu,65dlu,2dlu,pref", "pref"); + + /** Not meant to be instantiated. */ + private AdvancedTopics () + { + } + + //--------------// + // getComponent // + //--------------// + /** + * Report the selection dialog to be displayed to the user + * + * @return the dialog frame + */ + public static JFrame getComponent () + { + final JFrame frame = new JFrame(); + frame.setName("topicsFrame"); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + JComponent framePane = (JComponent) frame.getContentPane(); + + Panel panel = new Panel(); + FormLayout layout = new FormLayout("pref", "pref, 1dlu, pref, 1dlu, pref"); + PanelBuilder builder = new PanelBuilder(layout, panel); + CellConstraints cst = new CellConstraints(); + int r = 1; + builder.add(new EarlyPane(), cst.xy(1, r)); + + r += 2; + builder.add(new PluginPane(), cst.xy(1, r)); + + r += 2; + builder.add(new AllTopicsPane(), cst.xy(1, r)); + + framePane.add(panel); + + // Resources injection + ResourceMap resource = Application.getInstance().getContext().getResourceMap( + AdvancedTopics.class); + resource.injectComponents(frame); + + return frame; + } + + /** + * All advanced topics. + */ + public static enum Topic + { + SAMPLES(constants.useSamples), + ANNOTATIONS(constants.useAnnotations), + PLOTS(constants.usePlots), + SPECIFIC_VIEWS(constants.useSpecificViews), + SPECIFIC_ITEMS(constants.useSpecificItems), + WINDOW_LAYOUT(constants.useWindowLayout), + DEBUG(constants.useDebug); + + /** Underlying constant. */ + private final Constant.Boolean constant; + + Topic (Constant.Boolean constant) + { + this.constant = constant; + } + + public String getDescription () + { + return constant.getDescription(); + } + + public boolean isSet () + { + return constant.isSet(); + } + + public void set (boolean val) + { + constant.setValue(val); + } + } + + //---------------// + // AllTopicsPane // + //---------------// + /** + * Pane for the advanced topic switches. + */ + private static class AllTopicsPane + extends JPanel + { + + AllTopicsPane () + { + setBorder(BorderFactory.createTitledBorder("These switches require a restart")); + + FormLayout layout = new FormLayout("pref", Panel.makeRows(Topic.values().length)); + PanelBuilder builder = new PanelBuilder(layout, this); + CellConstraints cst = new CellConstraints(); + int r = 1; + + for (Topic topic : Topic.values()) { + builder.add(new TopicPane(topic), cst.xy(1, r)); + r += 2; + } + } + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Boolean useSamples = new Constant.Boolean( + false, + "Handling of samples repositories and classifier"); + + private final Constant.Boolean useAnnotations = new Constant.Boolean( + false, + "Production of image annotation with symbols"); + + private final Constant.Boolean usePlots = new Constant.Boolean( + false, + "Display of scale / stem / staves plots"); + + private final Constant.Boolean useSpecificViews = new Constant.Boolean( + false, + "Display of specific sheet views"); + + private final Constant.Boolean useSpecificItems = new Constant.Boolean( + false, + "Specific items shown in sheet view"); + + private final Constant.Boolean useWindowLayout = new Constant.Boolean( + false, + "Handling of main window layout"); + + private final Constant.Boolean useDebug = new Constant.Boolean( + false, + "Support for debug features"); + } + + //-----------// + // EarlyPane // + //-----------// + /** + * Which step should we trigger on any input image. + */ + private static class EarlyPane + extends Panel + implements ActionListener + { + + // ComboBox for desired step + private final JComboBox box; + + EarlyPane () + { + box = new JComboBox<>(Step.values()); + box.setToolTipText("Which step to trigger on any image input"); + box.setSelectedItem(StubsController.getEarlyStep()); + box.addActionListener(this); + + PanelBuilder builder = new PanelBuilder(layout2, this); + CellConstraints cst = new CellConstraints(); + + final int r = 1; + builder.add(box, cst.xy(1, r)); + builder.add(new JLabel("Step triggered on image input"), cst.xy(3, r)); + } + + @Override + public void actionPerformed (ActionEvent e) + { + Step step = box.getItemAt(box.getSelectedIndex()); + StubsController.setEarlyStep(step); + } + } + + //------------// + // PluginPane // + //------------// + /** + * Which plugin should be the default one. + */ + private static class PluginPane + extends Panel + implements ActionListener + { + + // ComboBox for registered plugins + private final JComboBox box; + + PluginPane () + { + final Collection ids = PluginsManager.getInstance().getPluginIds(); + box = new JComboBox<>(ids.toArray(new String[ids.size()])); + box.setToolTipText("Default plugin to be launched"); + box.setSelectedItem(PluginsManager.defaultPluginId.getValue()); + + PanelBuilder builder = new PanelBuilder(layout2, this); + CellConstraints cst = new CellConstraints(); + + final int r = 1; + builder.add(box, cst.xy(1, r)); + builder.add(new JLabel("Plugin launched on MusicXML output"), cst.xy(3, r)); + } + + @Override + public void actionPerformed (ActionEvent e) + { + PluginsManager.defaultPluginId.setSpecific(box.getItemAt(box.getSelectedIndex())); + } + } + + //-----------// + // TopicPane // + //-----------// + /** + * Handling of one topic switch. + */ + private static class TopicPane + extends Panel + implements ActionListener + { + + // Handled topic + final Topic topic; + + TopicPane (Topic topic) + { + this.topic = topic; + + PanelBuilder builder = new PanelBuilder(layout3, this); + CellConstraints cst = new CellConstraints(); + + JCheckBox box = new JCheckBox(); + box.addActionListener(this); + box.setSelected(topic.isSet()); + + final int r = 1; + builder.add(box, cst.xy(1, r)); + builder.add(new JLabel(topic.name()), cst.xy(3, r)); + builder.add(new JLabel(topic.getDescription()), cst.xy(5, r)); + } + + @Override + public void actionPerformed (ActionEvent e) + { + JCheckBox box = (JCheckBox) e.getSource(); + topic.set(box.isSelected()); + } + } +} diff --git a/src/main/org/audiveris/omr/ui/action/resources/Actions.properties b/src/main/org/audiveris/omr/ui/action/resources/Actions.properties index 46930ccda..0cc435225 100644 --- a/src/main/org/audiveris/omr/ui/action/resources/Actions.properties +++ b/src/main/org/audiveris/omr/ui/action/resources/Actions.properties @@ -9,7 +9,7 @@ # This is the generic version FILE.text = File -FILE.toolTipText = Management of image, script & book files +FILE.toolTipText = Management of image input files BOOK.text = Book BOOK.toolTipText = Management of book of sheets diff --git a/src/main/org/audiveris/omr/ui/action/resources/Actions_fr.properties b/src/main/org/audiveris/omr/ui/action/resources/Actions_fr.properties index 38c8b39ab..7044d34e1 100644 --- a/src/main/org/audiveris/omr/ui/action/resources/Actions_fr.properties +++ b/src/main/org/audiveris/omr/ui/action/resources/Actions_fr.properties @@ -9,7 +9,7 @@ # This is the FR version FILE.text = Fichier -FILE.toolTipText = Gestion des fichiers d'images, scripts et projets +FILE.toolTipText = Gestion des fichiers d'images BOOK.text = Document BOOK.toolTipText = Gestion de documents diff --git a/src/main/org/audiveris/omr/ui/dnd/AbstractGhostDropListener.java b/src/main/org/audiveris/omr/ui/dnd/AbstractGhostDropListener.java index c2680451c..004ec537c 100644 --- a/src/main/org/audiveris/omr/ui/dnd/AbstractGhostDropListener.java +++ b/src/main/org/audiveris/omr/ui/dnd/AbstractGhostDropListener.java @@ -39,15 +39,12 @@ public abstract class AbstractGhostDropListener implements GhostDropListener { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(AbstractGhostDropListener.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The related component */ protected JComponent component; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new AbstractGhostDropListener object * @@ -58,7 +55,6 @@ public AbstractGhostDropListener (JComponent component) this.component = component; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // dropped // //---------// diff --git a/src/main/org/audiveris/omr/ui/dnd/GhostComponentAdapter.java b/src/main/org/audiveris/omr/ui/dnd/GhostComponentAdapter.java index af9c822a9..08d3de0d1 100644 --- a/src/main/org/audiveris/omr/ui/dnd/GhostComponentAdapter.java +++ b/src/main/org/audiveris/omr/ui/dnd/GhostComponentAdapter.java @@ -31,13 +31,11 @@ * copied from the appearance of the component where the mouse is pressed. * * @param The precise type of action carried by the drop - * * @author Hervé Bitteur (from Romain Guy's demo) */ public class GhostComponentAdapter extends GhostDropAdapter { - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new GhostComponentAdapter object @@ -51,7 +49,6 @@ public GhostComponentAdapter (GhostGlassPane glassPane, super(glassPane, action); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // mousePressed // //--------------// diff --git a/src/main/org/audiveris/omr/ui/dnd/GhostDropAdapter.java b/src/main/org/audiveris/omr/ui/dnd/GhostDropAdapter.java index c32617696..e082211d2 100644 --- a/src/main/org/audiveris/omr/ui/dnd/GhostDropAdapter.java +++ b/src/main/org/audiveris/omr/ui/dnd/GhostDropAdapter.java @@ -37,21 +37,19 @@ public abstract class GhostDropAdapter extends MouseAdapter { - //~ Instance fields ---------------------------------------------------------------------------- - /** The related glasspane */ + /** The related glasspane. */ protected final GhostGlassPane glassPane; - /** The registered listeners */ - private final Set> listeners = new LinkedHashSet>(); - - /** The event-carried action */ + /** The event-carried action. */ protected A action; - /** The image to be displayed on the glasspane */ + /** The image to be displayed on the glasspane. */ protected BufferedImage image; - //~ Constructors ------------------------------------------------------------------------------- + /** The registered listeners. */ + private final Set> listeners = new LinkedHashSet<>(); + /** * Create a new GhostDropAdapter object * @@ -65,7 +63,6 @@ public GhostDropAdapter (GhostGlassPane glassPane, this.action = action; } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // addDropListener // //-----------------// @@ -84,6 +81,11 @@ public void addDropListener (GhostDropListener listener) //----------// // getImage // //----------// + /** + * Report the dragged image. + * + * @return dragged image + */ public BufferedImage getImage () { return image; @@ -114,7 +116,7 @@ public void mouseReleased (MouseEvent e) glassPane.setVisible(false); glassPane.setImage(null); - fireDropEvent(new GhostDropEvent(action, screenPoint)); + fireDropEvent(new GhostDropEvent<>(action, screenPoint)); } //--------------------// diff --git a/src/main/org/audiveris/omr/ui/dnd/GhostDropEvent.java b/src/main/org/audiveris/omr/ui/dnd/GhostDropEvent.java index 390c432e1..20c121031 100644 --- a/src/main/org/audiveris/omr/ui/dnd/GhostDropEvent.java +++ b/src/main/org/audiveris/omr/ui/dnd/GhostDropEvent.java @@ -30,7 +30,6 @@ */ public class GhostDropEvent { - //~ Instance fields ---------------------------------------------------------------------------- /** The drop location with respect to screen */ private final ScreenPoint screenPoint; @@ -38,7 +37,6 @@ public class GhostDropEvent /** The action carried by the drop event */ private final A action; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new GhostDropEvent object. * @@ -52,7 +50,6 @@ public GhostDropEvent (A action, this.screenPoint = screenPoint; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getAction // //-----------// diff --git a/src/main/org/audiveris/omr/ui/dnd/GhostDropListener.java b/src/main/org/audiveris/omr/ui/dnd/GhostDropListener.java index 130845fb3..369f15002 100644 --- a/src/main/org/audiveris/omr/ui/dnd/GhostDropListener.java +++ b/src/main/org/audiveris/omr/ui/dnd/GhostDropListener.java @@ -30,7 +30,6 @@ */ public interface GhostDropListener { - //~ Methods ------------------------------------------------------------------------------------ /** * Call-back function to receive the drop event diff --git a/src/main/org/audiveris/omr/ui/dnd/GhostGlassPane.java b/src/main/org/audiveris/omr/ui/dnd/GhostGlassPane.java index 3cd19b6b9..93ff9ecf1 100644 --- a/src/main/org/audiveris/omr/ui/dnd/GhostGlassPane.java +++ b/src/main/org/audiveris/omr/ui/dnd/GhostGlassPane.java @@ -44,7 +44,6 @@ public class GhostGlassPane extends JPanel { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GhostGlassPane.class); @@ -58,7 +57,6 @@ public class GhostGlassPane AlphaComposite.SRC_OVER, 0.2f); - //~ Instance fields ---------------------------------------------------------------------------- /** The image to be dragged. */ protected BufferedImage draggedImage = null; @@ -71,7 +69,6 @@ public class GhostGlassPane /** Are we over a droppable target?. */ protected boolean overTarget = false; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new GhostGlassPane object */ @@ -81,7 +78,6 @@ public GhostGlassPane () setName("GhostGlassPane"); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // paintComponent // //----------------// @@ -144,39 +140,6 @@ public void setPoint (ScreenPoint screenPoint) setLocalPoint(screenPoint.getLocalPoint(this)); } - //---------------// - // setLocalPoint // - //---------------// - /** - * Assign the current point, where the dragged image is to be displayed, - * and repaint as few as possible of the glass pane. - * - * @param localPoint the current location (glasspane-based) - */ - private void setLocalPoint (Point localPoint) - { - // Anything to repaint since last time the point was set? - if (draggedImage != null) { - Rectangle rect = getSceneBounds(localPoint); - Rectangle dirty = new Rectangle(rect); - - if (prevRectangle != null) { - dirty.add(prevRectangle); - } - - dirty.grow(1, 1); // To cope with rounding errors - - // Set new values now, to avoid race condition with repaint - this.localPoint = localPoint; - prevRectangle = rect; - - repaint(dirty.x, dirty.y, dirty.width, dirty.height); - } else { - this.localPoint = localPoint; - prevRectangle = null; - } - } - //----------------// // getImageBounds // //----------------// @@ -217,4 +180,37 @@ protected Rectangle getSceneBounds (Point center) { return getImageBounds(center); // By default } + + //---------------// + // setLocalPoint // + //---------------// + /** + * Assign the current point, where the dragged image is to be displayed, + * and repaint as few as possible of the glass pane. + * + * @param localPoint the current location (glasspane-based) + */ + private void setLocalPoint (Point localPoint) + { + // Anything to repaint since last time the point was set? + if (draggedImage != null) { + Rectangle rect = getSceneBounds(localPoint); + Rectangle dirty = new Rectangle(rect); + + if (prevRectangle != null) { + dirty.add(prevRectangle); + } + + dirty.grow(1, 1); // To cope with rounding errors + + // Set new values now, to avoid race condition with repaint + this.localPoint = localPoint; + prevRectangle = rect; + + repaint(dirty.x, dirty.y, dirty.width, dirty.height); + } else { + this.localPoint = localPoint; + prevRectangle = null; + } + } } diff --git a/src/main/org/audiveris/omr/ui/dnd/GhostImageAdapter.java b/src/main/org/audiveris/omr/ui/dnd/GhostImageAdapter.java index 01334b93f..14deea23b 100644 --- a/src/main/org/audiveris/omr/ui/dnd/GhostImageAdapter.java +++ b/src/main/org/audiveris/omr/ui/dnd/GhostImageAdapter.java @@ -27,13 +27,11 @@ * Class {@code GhostImageAdapter} is a {@link GhostDropAdapter} with a provided image. * * @param The precise type of action carried by the drop - * * @author Hervé Bitteur (from Romain Guy's demo) */ public class GhostImageAdapter extends GhostDropAdapter { - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new GhostImageAdapter object diff --git a/src/main/org/audiveris/omr/ui/dnd/GhostMotionAdapter.java b/src/main/org/audiveris/omr/ui/dnd/GhostMotionAdapter.java index 896b0ef9b..e67958650 100644 --- a/src/main/org/audiveris/omr/ui/dnd/GhostMotionAdapter.java +++ b/src/main/org/audiveris/omr/ui/dnd/GhostMotionAdapter.java @@ -37,15 +37,12 @@ public class GhostMotionAdapter extends MouseMotionAdapter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(GhostMotionAdapter.class); - //~ Instance fields ---------------------------------------------------------------------------- - /** The related glasspane */ + /** The related glasspane. */ protected GhostGlassPane glassPane; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new GhostMotionAdapter object * @@ -56,7 +53,6 @@ public GhostMotionAdapter (GhostGlassPane glassPane) this.glassPane = glassPane; } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // mouseDragged // //--------------// diff --git a/src/main/org/audiveris/omr/ui/dnd/GhostPictureAdapter.java b/src/main/org/audiveris/omr/ui/dnd/GhostPictureAdapter.java index 5bc09ef17..9b5b76dd9 100644 --- a/src/main/org/audiveris/omr/ui/dnd/GhostPictureAdapter.java +++ b/src/main/org/audiveris/omr/ui/dnd/GhostPictureAdapter.java @@ -32,13 +32,11 @@ * retrieved from the class resource path. * * @param The precise type of action carried by the drop - * * @author Hervé Bitteur (from Romain Guy's demo) */ public class GhostPictureAdapter extends GhostDropAdapter { - //~ Constructors ------------------------------------------------------------------------------- /** * Create a new GhostPictureAdapter object @@ -53,9 +51,9 @@ public GhostPictureAdapter (GhostGlassPane glassPane, { super(glassPane, action); - try { - image = ImageIO.read( - new BufferedInputStream(GhostPictureAdapter.class.getResourceAsStream(picture))); + try (BufferedInputStream bis = new BufferedInputStream( + GhostPictureAdapter.class.getResourceAsStream(picture));) { + image = ImageIO.read(bis); } catch (MalformedURLException mue) { throw new IllegalStateException("Invalid picture URL."); } catch (IOException ioe) { diff --git a/src/main/org/audiveris/omr/ui/dnd/ScreenPoint.java b/src/main/org/audiveris/omr/ui/dnd/ScreenPoint.java index db4cf1f3d..b36404994 100644 --- a/src/main/org/audiveris/omr/ui/dnd/ScreenPoint.java +++ b/src/main/org/audiveris/omr/ui/dnd/ScreenPoint.java @@ -35,7 +35,6 @@ public class ScreenPoint extends Point { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ScreenPoint object, from provided coordinates. @@ -62,7 +61,6 @@ public ScreenPoint (Component component, SwingUtilities.convertPointToScreen(this, component); } - //~ Methods ------------------------------------------------------------------------------------ //---------------// // getLocalPoint // //---------------// @@ -95,4 +93,10 @@ public boolean isInComponent (Component component) return bounds.contains(getLocalPoint(component)); } + + @Override + public Object clone () + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/main/org/audiveris/omr/ui/field/IntegerListSpinner.java b/src/main/org/audiveris/omr/ui/field/IntegerListSpinner.java index bcd58bfa4..19c479d98 100644 --- a/src/main/org/audiveris/omr/ui/field/IntegerListSpinner.java +++ b/src/main/org/audiveris/omr/ui/field/IntegerListSpinner.java @@ -33,7 +33,6 @@ public class IntegerListSpinner extends JSpinner { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new IntegerListSpinner object. diff --git a/src/main/org/audiveris/omr/ui/field/LCheckBox.java b/src/main/org/audiveris/omr/ui/field/LCheckBox.java index 7382eda2f..47ff9bf12 100644 --- a/src/main/org/audiveris/omr/ui/field/LCheckBox.java +++ b/src/main/org/audiveris/omr/ui/field/LCheckBox.java @@ -34,7 +34,6 @@ public class LCheckBox extends LField { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LCheckBox object. @@ -48,7 +47,6 @@ public LCheckBox (String label, super(label, tip, new JCheckBox()); } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // addActionListener // //-------------------// diff --git a/src/main/org/audiveris/omr/ui/field/LComboBox.java b/src/main/org/audiveris/omr/ui/field/LComboBox.java index 35f9f5b54..554c8127e 100644 --- a/src/main/org/audiveris/omr/ui/field/LComboBox.java +++ b/src/main/org/audiveris/omr/ui/field/LComboBox.java @@ -30,13 +30,11 @@ * a "Labeled Combo", where the label describes the dynamic content of the combo. * * @param type of combo entity - * * @author Hervé Bitteur */ public class LComboBox extends LField> { - //~ Constructors ------------------------------------------------------------------------------- /** * Create an editable labeled combo with provided @@ -50,10 +48,9 @@ public LComboBox (String label, String tip, E[] items) { - super(label, tip, new JComboBox(items)); + super(label, tip, new JComboBox<>(items)); } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // addActionListener // //-------------------// @@ -70,6 +67,11 @@ public void addActionListener (ActionListener listener) //-----------------// // getSelectedItem // //-----------------// + /** + * Report the selected item. + * + * @return selected item + */ @SuppressWarnings("unchecked") public E getSelectedItem () { @@ -79,6 +81,11 @@ public E getSelectedItem () //-----------------// // setSelectedItem // //-----------------// + /** + * Select the provided item. + * + * @param item provided item + */ public void setSelectedItem (E item) { getField().setSelectedItem(item); diff --git a/src/main/org/audiveris/omr/ui/field/LDoubleField.java b/src/main/org/audiveris/omr/ui/field/LDoubleField.java index 82b03972d..2a35fc06c 100644 --- a/src/main/org/audiveris/omr/ui/field/LDoubleField.java +++ b/src/main/org/audiveris/omr/ui/field/LDoubleField.java @@ -32,16 +32,13 @@ public class LDoubleField extends LTextField { - //~ Static fields/initializers ----------------------------------------------------------------- /** Default format for display in the field : {@value} */ public static final String DEFAULT_FORMAT = "%.5f"; - //~ Instance fields ---------------------------------------------------------------------------- /** Specific display format, if any */ private final String format; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an (initially) editable double labeled field with proper * characteristics @@ -110,7 +107,6 @@ public LDoubleField (boolean editable, this.format = format; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getValue // //----------// diff --git a/src/main/org/audiveris/omr/ui/field/LField.java b/src/main/org/audiveris/omr/ui/field/LField.java index 30a229a1a..958b548af 100644 --- a/src/main/org/audiveris/omr/ui/field/LField.java +++ b/src/main/org/audiveris/omr/ui/field/LField.java @@ -32,12 +32,10 @@ * @param precise subtype of JComponent field *
                                                                                                                                            * Labeled Field Component UML - * * @author Hervé Bitteur */ public class LField { - //~ Instance fields ---------------------------------------------------------------------------- /** The label. */ private final JLabel label; @@ -45,7 +43,6 @@ public class LField /** The field. */ private final C field; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LField object. * @@ -66,7 +63,6 @@ public LField (String label, } } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getField // //----------// diff --git a/src/main/org/audiveris/omr/ui/field/LHexaSpinner.java b/src/main/org/audiveris/omr/ui/field/LHexaSpinner.java index 4c86aff60..f59de2e49 100644 --- a/src/main/org/audiveris/omr/ui/field/LHexaSpinner.java +++ b/src/main/org/audiveris/omr/ui/field/LHexaSpinner.java @@ -37,7 +37,6 @@ public class LHexaSpinner extends LIntegerSpinner { - //~ Constructors ------------------------------------------------------------------------------- /** * Create an editable labeled hexa spinner with provided @@ -53,14 +52,12 @@ public LHexaSpinner (String label, spinner.setEditor(new HexaEditor(spinner)); } - //~ Inner Classes ------------------------------------------------------------------------------ //------------// // HexaEditor // //------------// private static class HexaEditor extends JSpinner.NumberEditor { - //~ Constructors --------------------------------------------------------------------------- HexaEditor (JSpinner spinner) { @@ -71,7 +68,6 @@ private static class HexaEditor ftf.setFormatterFactory(new HexaFormatterFactory()); } - //~ Methods -------------------------------------------------------------------------------- @Override public void stateChanged (ChangeEvent e) { @@ -86,7 +82,6 @@ public void stateChanged (ChangeEvent e) private static class HexaFormatter extends DefaultFormatter { - //~ Methods -------------------------------------------------------------------------------- @Override public Object stringToValue (String string) @@ -119,6 +114,13 @@ public String valueToString (Object value) return Long.toHexString(((Number) value).longValue()); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } //----------------------// @@ -127,9 +129,8 @@ public String valueToString (Object value) private static class HexaFormatterFactory extends DefaultFormatterFactory { - //~ Constructors --------------------------------------------------------------------------- - public HexaFormatterFactory () + HexaFormatterFactory () { super(new HexaFormatter()); } diff --git a/src/main/org/audiveris/omr/ui/field/LIntegerField.java b/src/main/org/audiveris/omr/ui/field/LIntegerField.java index 66ee22470..b431eec10 100644 --- a/src/main/org/audiveris/omr/ui/field/LIntegerField.java +++ b/src/main/org/audiveris/omr/ui/field/LIntegerField.java @@ -30,7 +30,6 @@ public class LIntegerField extends LTextField { - //~ Constructors ------------------------------------------------------------------------------- /** * Create a (constant) integer labeled field @@ -58,7 +57,6 @@ public LIntegerField (boolean editable, super(editable, label, tip); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getValue // //----------// diff --git a/src/main/org/audiveris/omr/ui/field/LIntegerSpinner.java b/src/main/org/audiveris/omr/ui/field/LIntegerSpinner.java index 3c6a57f6d..31cd97367 100644 --- a/src/main/org/audiveris/omr/ui/field/LIntegerSpinner.java +++ b/src/main/org/audiveris/omr/ui/field/LIntegerSpinner.java @@ -33,7 +33,6 @@ public class LIntegerSpinner extends LSpinner { - //~ Constructors ------------------------------------------------------------------------------- /** * Create an editable labeled spinner with provided @@ -48,7 +47,6 @@ public LIntegerSpinner (String label, super(label, tip); } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // addChangeListener // //-------------------// diff --git a/src/main/org/audiveris/omr/ui/field/LLabel.java b/src/main/org/audiveris/omr/ui/field/LLabel.java index 057d8bf44..c070e2294 100644 --- a/src/main/org/audiveris/omr/ui/field/LLabel.java +++ b/src/main/org/audiveris/omr/ui/field/LLabel.java @@ -31,7 +31,6 @@ public class LLabel extends LField { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code LLabel} object. @@ -46,7 +45,6 @@ public LLabel (String label, getField().setHorizontalAlignment(JLabel.CENTER); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getText // //---------// diff --git a/src/main/org/audiveris/omr/ui/field/LSpinner.java b/src/main/org/audiveris/omr/ui/field/LSpinner.java index 7de5b9da8..4705c96e0 100644 --- a/src/main/org/audiveris/omr/ui/field/LSpinner.java +++ b/src/main/org/audiveris/omr/ui/field/LSpinner.java @@ -35,7 +35,6 @@ */ public class LSpinner { - //~ Instance fields ---------------------------------------------------------------------------- /** * The related label @@ -47,7 +46,6 @@ public class LSpinner */ protected JSpinner spinner; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an editable labeled spinner with provided * characteristics. @@ -67,7 +65,6 @@ public LSpinner (String label, } } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // addChangeListener // //-------------------// diff --git a/src/main/org/audiveris/omr/ui/field/LTextField.java b/src/main/org/audiveris/omr/ui/field/LTextField.java index f01393520..cda1d4209 100644 --- a/src/main/org/audiveris/omr/ui/field/LTextField.java +++ b/src/main/org/audiveris/omr/ui/field/LTextField.java @@ -31,12 +31,10 @@ public class LTextField extends LField { - //~ Static fields/initializers ----------------------------------------------------------------- /** Default number of characters in the text field : {@value} */ private static final int FIELD_WIDTH = 6; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LTextField object. * @@ -73,7 +71,6 @@ public LTextField (String label, this(false, label, tip); } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getText // //---------// diff --git a/src/main/org/audiveris/omr/ui/field/SpinnerUtil.java b/src/main/org/audiveris/omr/ui/field/SpinnerUtil.java index 19d1b9024..04234dcc4 100644 --- a/src/main/org/audiveris/omr/ui/field/SpinnerUtil.java +++ b/src/main/org/audiveris/omr/ui/field/SpinnerUtil.java @@ -39,7 +39,11 @@ */ public abstract class SpinnerUtil { - //~ Methods ------------------------------------------------------------------------------------ + + /** Not meant to be instantiated. */ + private SpinnerUtil () + { + } //----------------// // fixIntegerList // @@ -57,21 +61,19 @@ public static void fixIntegerList (final JSpinner spinner) final JFormattedTextField ftf = editor.getTextField(); ftf.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "enterAction"); - ftf.getActionMap().put( - "enterAction", - new AbstractAction() - { - @Override - public void actionPerformed (ActionEvent e) - { - try { - spinner.setValue(Integer.parseInt(ftf.getText())); - } catch (Exception ex) { - // Reset to last value - ftf.setText(ftf.getValue().toString()); - } - } - }); + ftf.getActionMap().put("enterAction", new AbstractAction() + { + @Override + public void actionPerformed (ActionEvent e) + { + try { + spinner.setValue(Integer.parseInt(ftf.getText())); + } catch (NumberFormatException ex) { + // Reset to last value + ftf.setText(ftf.getValue().toString()); + } + } + }); } //-------------// diff --git a/src/main/org/audiveris/omr/ui/resources/GuiActions.properties b/src/main/org/audiveris/omr/ui/resources/GuiActions.properties index 2b4bbba70..afb518d31 100644 --- a/src/main/org/audiveris/omr/ui/resources/GuiActions.properties +++ b/src/main/org/audiveris/omr/ui/resources/GuiActions.properties @@ -103,7 +103,7 @@ defineTopics.Action.icon = ${icons.root}/apps/software-development.png # Help showManual.Action.text = Handbook -showManual.Action.shortDescription = Open Audiveris handbook +showManual.Action.shortDescription = Launch a browser on Audiveris handbook showManual.Action.icon = ${icons.root}/mimetypes/man.png visitWebSite.Action.text = Web Site diff --git a/src/main/org/audiveris/omr/ui/resources/GuiActions_fr.properties b/src/main/org/audiveris/omr/ui/resources/GuiActions_fr.properties index ebb029f6d..58369619e 100644 --- a/src/main/org/audiveris/omr/ui/resources/GuiActions_fr.properties +++ b/src/main/org/audiveris/omr/ui/resources/GuiActions_fr.properties @@ -49,8 +49,8 @@ clearLog.Action.shortDescription = Effacement de la fen\u00eatre de log # Help -#showManual.Action.text = Manuel -#showManual.Action.shortDescription = Affichage du manuel d'exploitation +showManual.Action.text = Manuel +showManual.Action.shortDescription = Navigation vers le manuel d'exploitation visitWebSite.Action.text = Site Web visitWebSite.Action.shortDescription = Navigation vers le site web d'Audiveris diff --git a/src/main/org/audiveris/omr/ui/selection/AnchoredTemplateEvent.java b/src/main/org/audiveris/omr/ui/selection/AnchoredTemplateEvent.java index 7832824d0..a7c8e204d 100644 --- a/src/main/org/audiveris/omr/ui/selection/AnchoredTemplateEvent.java +++ b/src/main/org/audiveris/omr/ui/selection/AnchoredTemplateEvent.java @@ -32,11 +32,9 @@ public class AnchoredTemplateEvent extends UserEvent { - //~ Instance fields ---------------------------------------------------------------------------- private final AnchoredTemplate anchoredTemplate; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code AnchoredTemplateEvent} object. * @@ -54,7 +52,6 @@ public AnchoredTemplateEvent (Object source, this.anchoredTemplate = anchoredTemplate; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// diff --git a/src/main/org/audiveris/omr/ui/selection/EntityListEvent.java b/src/main/org/audiveris/omr/ui/selection/EntityListEvent.java index bc7c0a53a..6a84fb5de 100644 --- a/src/main/org/audiveris/omr/ui/selection/EntityListEvent.java +++ b/src/main/org/audiveris/omr/ui/selection/EntityListEvent.java @@ -21,9 +21,9 @@ // package org.audiveris.omr.ui.selection; -import java.util.ArrayList; import org.audiveris.omr.util.Entity; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -36,18 +36,15 @@ * convenient to use a list than a true set. * * @param precise type for entities handled - * * @author Hervé Bitteur */ public class EntityListEvent extends UserEvent { - //~ Instance fields ---------------------------------------------------------------------------- /** The selected entity list, which may be empty but not null. */ private final List entities; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code EntityListEvent} object. * @@ -64,7 +61,7 @@ public EntityListEvent (Object source, super(source, hint, movement); if (entities != null) { - this.entities = Collections.unmodifiableList(new ArrayList(entities)); + this.entities = Collections.unmodifiableList(new ArrayList<>(entities)); } else { this.entities = Collections.emptyList(); } @@ -86,13 +83,12 @@ public EntityListEvent (Object source, super(source, hint, movement); if ((entities != null) && (entities.length > 0) && (entities[0] != null)) { - this.entities = new ArrayList(Arrays.asList(entities)); + this.entities = new ArrayList<>(Arrays.asList(entities)); } else { this.entities = Collections.emptyList(); } } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// diff --git a/src/main/org/audiveris/omr/ui/selection/EntityService.java b/src/main/org/audiveris/omr/ui/selection/EntityService.java index fc7fa871d..db0090b99 100644 --- a/src/main/org/audiveris/omr/ui/selection/EntityService.java +++ b/src/main/org/audiveris/omr/ui/selection/EntityService.java @@ -21,6 +21,7 @@ // package org.audiveris.omr.ui.selection; +import java.awt.Point; import static org.audiveris.omr.ui.selection.SelectionHint.ENTITY_INIT; import org.audiveris.omr.util.Entity; import org.audiveris.omr.util.EntityIndex; @@ -47,18 +48,15 @@ * {@link #disconnect} methods. * * @param precise entity type - * * @author Hervé Bitteur */ public class EntityService extends SelectionService implements EventSubscriber { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(EntityService.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The underlying entity index, if any. */ protected final EntityIndex index; @@ -66,9 +64,8 @@ public class EntityService protected final SelectionService locationService; /** Basket of entities selected via location (rectangle/point). */ - protected final List basket = new ArrayList(); + protected final List basket = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code EntityService} object with no underlying index. * @@ -103,7 +100,6 @@ public EntityService (EntityIndex index, this.locationService = locationService; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // connect // //---------// @@ -223,6 +219,12 @@ public void onEvent (UserEvent event) //-----------------// // getMostRelevant // //-----------------// + /** + * Among the list of selected entities, report the most "relevant" one. + * + * @param list the sequence of selected entities + * @return the chosen entity + */ protected E getMostRelevant (List list) { if (!list.isEmpty()) { @@ -271,7 +273,7 @@ protected void handleEntityListEvent (EntityListEvent listEvent) protected void handleEvent (IdEvent idEvent) { final E entity = index.getEntity(idEvent.getData()); - publish(new EntityListEvent(this, idEvent.hint, idEvent.movement, entity)); + publish(new EntityListEvent<>(this, idEvent.hint, idEvent.movement, entity)); } //---------------------// @@ -292,6 +294,9 @@ protected void handleLocationEvent (LocationEvent locationEvent) return; } + // Keep only relevant entities in basket + purgeBasket(); + final List selection = getSelectedEntityList(); if (hint.isLocation() || (hint.isContext() && selection.isEmpty())) { @@ -299,10 +304,11 @@ protected void handleLocationEvent (LocationEvent locationEvent) // Non-degenerated rectangle: look for contained entities basket.clear(); basket.addAll(index.getContainedEntities(rect)); - publish(new EntityListEvent(this, hint, movement, basket)); + publish(new EntityListEvent<>(this, hint, movement, basket)); } else { // Just a point: look for most relevant entity - E entity = getMostRelevant(index.getContainingEntities(rect.getLocation())); + final Point loc = rect.getLocation(); + final E entity = getMostRelevant(index.getContainingEntities(loc)); // Update basket switch (hint) { @@ -347,8 +353,19 @@ protected void handleLocationEvent (LocationEvent locationEvent) } // Publish basket - publish(new EntityListEvent(this, null, movement, basket)); + publish(new EntityListEvent<>(this, null, movement, basket)); } } } + + //-------------// + // purgeBasket // + //-------------// + /** + * Purge basket of no longer relevant entities. + */ + protected void purgeBasket () + { + // Void by default + } } diff --git a/src/main/org/audiveris/omr/ui/selection/IdEvent.java b/src/main/org/audiveris/omr/ui/selection/IdEvent.java index 288c643aa..c4ad7d5d5 100644 --- a/src/main/org/audiveris/omr/ui/selection/IdEvent.java +++ b/src/main/org/audiveris/omr/ui/selection/IdEvent.java @@ -29,12 +29,10 @@ public class IdEvent extends UserEvent { - //~ Instance fields ---------------------------------------------------------------------------- /** The selected entity id, which may be null. */ private final Integer id; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new IdEvent object. * @@ -52,7 +50,6 @@ public IdEvent (Object source, this.id = id; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// diff --git a/src/main/org/audiveris/omr/ui/selection/LocationEvent.java b/src/main/org/audiveris/omr/ui/selection/LocationEvent.java index b77af0739..7d6c45800 100644 --- a/src/main/org/audiveris/omr/ui/selection/LocationEvent.java +++ b/src/main/org/audiveris/omr/ui/selection/LocationEvent.java @@ -35,18 +35,15 @@ public class LocationEvent extends UserEvent { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(LocationEvent.class); - //~ Instance fields ---------------------------------------------------------------------------- /** * The location rectangle, which can be degenerated to a point when both - * width and height values equal zero + * width and height values equal zero. */ private final Rectangle rectangle; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LocationEvent object. * @@ -64,7 +61,6 @@ public LocationEvent (Object source, this.rectangle = rectangle; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// diff --git a/src/main/org/audiveris/omr/ui/selection/PixelEvent.java b/src/main/org/audiveris/omr/ui/selection/PixelEvent.java index 35421f137..f718ad2e5 100644 --- a/src/main/org/audiveris/omr/ui/selection/PixelEvent.java +++ b/src/main/org/audiveris/omr/ui/selection/PixelEvent.java @@ -29,12 +29,10 @@ public class PixelEvent extends UserEvent { - //~ Instance fields ---------------------------------------------------------------------------- /** The current pixel level, which may be null. */ private final Integer level; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PixelEvent} object. * @@ -52,7 +50,6 @@ public PixelEvent (Object source, this.level = level; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// diff --git a/src/main/org/audiveris/omr/ui/selection/RunEvent.java b/src/main/org/audiveris/omr/ui/selection/RunEvent.java index a2b731315..724ba3c8a 100644 --- a/src/main/org/audiveris/omr/ui/selection/RunEvent.java +++ b/src/main/org/audiveris/omr/ui/selection/RunEvent.java @@ -31,12 +31,10 @@ public class RunEvent extends UserEvent { - //~ Instance fields ---------------------------------------------------------------------------- /** The selected run, which may be null */ private final Run run; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new RunEvent object. * @@ -54,7 +52,6 @@ public RunEvent (Object source, this.run = run; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// diff --git a/src/main/org/audiveris/omr/ui/selection/SelectionHint.java b/src/main/org/audiveris/omr/ui/selection/SelectionHint.java index f8860a037..1ddb29ad0 100644 --- a/src/main/org/audiveris/omr/ui/selection/SelectionHint.java +++ b/src/main/org/audiveris/omr/ui/selection/SelectionHint.java @@ -22,8 +22,8 @@ package org.audiveris.omr.ui.selection; /** - * Enum {@code SelectionHint} gives a hint about what observers should - * do with the published selection. + * Enum {@code SelectionHint} gives a hint about what observers should do with the + * published selection. * * @author Hervé Bitteur */ @@ -31,33 +31,38 @@ public enum SelectionHint { /** * Designation is by location pointing, so we keep the original location - * information, and try to lookup for designated Run, Section and Glyph - *
                                                                                                                                            [MouseLeft] + * information, and try to lookup for designated Run, Section and Glyph. + *
                                                                                                                                            + * [MouseLeft] */ LOCATION_INIT, /** - * Designation is by location pointing while adding to the existing selection(s), so we keep the - * original location information, and try to lookup for designated Run, Section and Glyph - *
                                                                                                                                            [CTRL + MouseLeft] + * Designation is by location pointing while adding to the existing selection(s), + * so we keep the original location information, and try to lookup for designated + * Run, Section and Glyph. + *
                                                                                                                                            + * [CTRL + MouseLeft] */ LOCATION_ADD, /** - * Designation is by context pointing, discarding any previous selection - *
                                                                                                                                            [MouseRight] + * Designation is by context pointing, discarding any previous selection. + *
                                                                                                                                            + * [MouseRight] */ CONTEXT_INIT, /** - * Designation is by context pointing while keeping the existing selection(s) if any - *
                                                                                                                                            [CTRL + MouseRight] + * Designation is by context pointing while keeping the existing selection(s) if any. + *
                                                                                                                                            + * [CTRL + MouseRight] */ CONTEXT_ADD, /** - * Designation is at entity level + * Designation is at entity level. */ ENTITY_INIT, /** - * Entity information is for temporary display / evaluation only, with no impact on other - * structures such as entity basket + * Entity information is for temporary display / evaluation only, + * with no impact on other structures such as entity basket. */ ENTITY_TRANSIENT; diff --git a/src/main/org/audiveris/omr/ui/selection/SelectionService.java b/src/main/org/audiveris/omr/ui/selection/SelectionService.java index 703c22ab3..1e26b67d3 100644 --- a/src/main/org/audiveris/omr/ui/selection/SelectionService.java +++ b/src/main/org/audiveris/omr/ui/selection/SelectionService.java @@ -41,20 +41,17 @@ public class SelectionService extends ThreadSafeEventService { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(SelectionService.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Name of this service. */ private final String name; /** Allowed events. */ private final Class[] eventsAllowed; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SelectionService object. * @@ -71,10 +68,12 @@ public SelectionService (String name, setDefaultCacheSizePerClassOrTopic(1); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // dumpSubscribers // //-----------------// + /** + * Dump all current subscribers to this service. + */ public void dumpSubscribers () { logger.info("{} subscribers:", this); @@ -107,6 +106,11 @@ public void dumpSubscribers () //---------// // getName // //---------// + /** + * Report service name. + * + * @return service name + */ public String getName () { return name; @@ -232,6 +236,11 @@ public boolean unsubscribe (Class classe, //------------------// // getEventsAllowed // //------------------// + /** + * Report the event classes that can ne published on this service. + * + * @return the allowed classes of event + */ protected Class[] getEventsAllowed () { return eventsAllowed; @@ -251,14 +260,12 @@ private boolean isAllowed (Class classe) return false; } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean checkPublishedEvents = new Constant.Boolean( true, diff --git a/src/main/org/audiveris/omr/ui/selection/StubEvent.java b/src/main/org/audiveris/omr/ui/selection/StubEvent.java index 3cf5e1901..71fd85699 100644 --- a/src/main/org/audiveris/omr/ui/selection/StubEvent.java +++ b/src/main/org/audiveris/omr/ui/selection/StubEvent.java @@ -32,12 +32,10 @@ public class StubEvent extends UserEvent { - //~ Instance fields ---------------------------------------------------------------------------- /** The selected sheet stub, which may be null. */ private final SheetStub stub; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SheetEvent object. * @@ -55,7 +53,6 @@ public StubEvent (Object source, this.stub = stub; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// diff --git a/src/main/org/audiveris/omr/ui/selection/UserEvent.java b/src/main/org/audiveris/omr/ui/selection/UserEvent.java index 22211e069..99f016a35 100644 --- a/src/main/org/audiveris/omr/ui/selection/UserEvent.java +++ b/src/main/org/audiveris/omr/ui/selection/UserEvent.java @@ -32,11 +32,9 @@ */ public abstract class UserEvent { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(UserEvent.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The entity which created this event (cannot be null). */ public final Object source; @@ -46,7 +44,6 @@ public abstract class UserEvent /** Precise user mouse action (can be null). */ public MouseMovement movement; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new UserEvent object. * @@ -67,7 +64,6 @@ public UserEvent (Object source, this.movement = movement; } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getData // //---------// diff --git a/src/main/org/audiveris/omr/ui/symbol/Alignment.java b/src/main/org/audiveris/omr/ui/symbol/Alignment.java index 34d454184..be12bffb6 100644 --- a/src/main/org/audiveris/omr/ui/symbol/Alignment.java +++ b/src/main/org/audiveris/omr/ui/symbol/Alignment.java @@ -29,10 +29,11 @@ /** * Class {@code Alignment} defines how a location is to be understood (vertically and * horizontally) with respect to symbol rectangular bounds. + * + * @author Hervé Bitteur */ public class Alignment { - //~ Static fields/initializers ----------------------------------------------------------------- /** Pre-defined alignment on top left of symbol */ public static final Alignment TOP_LEFT = new Alignment(Vertical.TOP, Horizontal.LEFT); @@ -56,19 +57,16 @@ public class Alignment public static final Alignment BASELINE_LEFT = new Alignment(Vertical.BASELINE, Horizontal.LEFT); /** Pre-defined alignment on baseline center of symbol */ - public static final Alignment BASELINE_CENTER = new Alignment( - Vertical.BASELINE, - Horizontal.CENTER); + public static final Alignment BASELINE_CENTER = new Alignment(Vertical.BASELINE, + Horizontal.CENTER); /** Pre-defined alignment on baseline right of symbol */ - public static final Alignment BASELINE_RIGHT = new Alignment( - Vertical.BASELINE, - Horizontal.RIGHT); + public static final Alignment BASELINE_RIGHT + = new Alignment(Vertical.BASELINE, Horizontal.RIGHT); /** Pre-defined alignment on baseline origin of symbol (for text) */ - public static final Alignment BASELINE_XORIGIN = new Alignment( - Vertical.BASELINE, - Horizontal.XORIGIN); + public static final Alignment BASELINE_XORIGIN = new Alignment(Vertical.BASELINE, + Horizontal.XORIGIN); /** Pre-defined alignment on bottom left of symbol */ public static final Alignment BOTTOM_LEFT = new Alignment(Vertical.BOTTOM, Horizontal.LEFT); @@ -79,155 +77,12 @@ public class Alignment /** Pre-defined alignment on bottom right of symbol */ public static final Alignment BOTTOM_RIGHT = new Alignment(Vertical.BOTTOM, Horizontal.RIGHT); - //~ Enumerations ------------------------------------------------------------------------------- - //----------// - // Vertical // - //----------// - /** The reference y line for this symbol */ - public static enum Vertical - { - //~ Enumeration constant initializers ------------------------------------------------------ - - TOP, - MIDDLE, - BOTTOM, - BASELINE; - - //~ Methods -------------------------------------------------------------------------------- - //-----------// - // dyToPoint // - //-----------// - public int dyToPoint (Vertical that, - Rectangle rect) - { - if (this == BASELINE) { - if (that == BASELINE) { - return 0; - } else { - return rect.y + (((that.ordinal() - TOP.ordinal()) * rect.height) / 2); - } - } else if (that == BASELINE) { - return -rect.y + (((TOP.ordinal() - this.ordinal()) * rect.height) / 2); - } else { - return ((that.ordinal() - this.ordinal()) * rect.height) / 2; - } - } - - //-----------// - // dyToPoint // - //-----------// - public double dyToPoint (Vertical that, - Rectangle2D rect) - { - if (this == BASELINE) { - if (that == BASELINE) { - return 0; - } else { - return rect.getY() - + (((that.ordinal() - TOP.ordinal()) * rect.getHeight()) / 2); - } - } else if (that == BASELINE) { - return -rect.getY() + (((TOP.ordinal() - this.ordinal()) * rect.getHeight()) / 2); - } else { - return ((that.ordinal() - this.ordinal()) * rect.getHeight()) / 2; - } - } - - //----------------// - // dyToTextOrigin // - //----------------// - public int dyToTextOrigin (Rectangle rect) - { - return dyToPoint(BASELINE, rect); - } - - //----------------// - // dyToTextOrigin // - //----------------// - public double dyToTextOrigin (Rectangle2D rect) - { - return dyToPoint(BASELINE, rect); - } - } - - //------------// - // Horizontal // - //------------// - /** The reference x line for this symbol */ - public static enum Horizontal - { - //~ Enumeration constant initializers ------------------------------------------------------ - - LEFT, - CENTER, - RIGHT, - XORIGIN; - - //~ Methods -------------------------------------------------------------------------------- - //-----------// - // dxToPoint // - //-----------// - public int dxToPoint (Horizontal that, - Rectangle rect) - { - if (this == XORIGIN) { - if (that == XORIGIN) { - return 0; - } else { - return rect.x + (((that.ordinal() - LEFT.ordinal()) * rect.width) / 2); - } - } else if (that == XORIGIN) { - return -rect.x + (((LEFT.ordinal() - this.ordinal()) * rect.width) / 2); - } else { - return ((that.ordinal() - this.ordinal()) * rect.width) / 2; - } - } - - //-----------// - // dxToPoint // - //-----------// - public double dxToPoint (Horizontal that, - Rectangle2D rect) - { - if (this == XORIGIN) { - if (that == XORIGIN) { - return 0; - } else { - return rect.getX() - + (((that.ordinal() - LEFT.ordinal()) * rect.getWidth()) / 2); - } - } else if (that == XORIGIN) { - return -rect.getX() + (((LEFT.ordinal() - this.ordinal()) * rect.getWidth()) / 2); - } else { - return ((that.ordinal() - this.ordinal()) * rect.getWidth()) / 2; - } - } - - //----------------// - // dxToTextOrigin // - //----------------// - public int dxToTextOrigin (Rectangle rect) - { - return dxToPoint(XORIGIN, rect); - } - - //----------------// - // dxToTextOrigin // - //----------------// - public double dxToTextOrigin (Rectangle2D rect) - { - return dxToPoint(XORIGIN, rect); - } - } - - //~ Instance fields ---------------------------------------------------------------------------- /** The vertical alignment */ public final Vertical vertical; /** The horizontal alignment */ public final Horizontal horizontal; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an Alignment instance * @@ -245,7 +100,6 @@ public Alignment (Vertical vertical, this.horizontal = horizontal; } - //~ Methods ------------------------------------------------------------------------------------ //--------// // equals // //--------// @@ -288,9 +142,8 @@ public int hashCode () public Point toPoint (Alignment that, Rectangle rect) { - return new Point( - horizontal.dxToPoint(that.horizontal, rect), - vertical.dyToPoint(that.vertical, rect)); + return new Point(horizontal.dxToPoint(that.horizontal, rect), vertical.dyToPoint( + that.vertical, rect)); } //---------// @@ -307,9 +160,8 @@ public Point toPoint (Alignment that, public Point2D toPoint (Alignment that, Rectangle2D rect) { - return new Point2D.Double( - horizontal.dxToPoint(that.horizontal, rect), - vertical.dyToPoint(that.vertical, rect)); + return new Point2D.Double(horizontal.dxToPoint(that.horizontal, rect), vertical.dyToPoint( + that.vertical, rect)); } //----------// @@ -374,4 +226,140 @@ public Point translatedPoint (Alignment that, return new Point(location.x, location.y); } } + + //----------// + // Vertical // + //----------// + /** + * The reference y line for this symbol. + */ + public static enum Vertical + { + TOP, + MIDDLE, + BOTTOM, + BASELINE; + + //-----------// + // dyToPoint // + //-----------// + public int dyToPoint (Vertical that, + Rectangle rect) + { + if (this == BASELINE) { + if (that == BASELINE) { + return 0; + } else { + return rect.y + (((that.ordinal() - TOP.ordinal()) * rect.height) / 2); + } + } else if (that == BASELINE) { + return -rect.y + (((TOP.ordinal() - this.ordinal()) * rect.height) / 2); + } else { + return ((that.ordinal() - this.ordinal()) * rect.height) / 2; + } + } + + //-----------// + // dyToPoint // + //-----------// + public double dyToPoint (Vertical that, + Rectangle2D rect) + { + if (this == BASELINE) { + if (that == BASELINE) { + return 0; + } else { + return rect.getY() + (((that.ordinal() - TOP.ordinal()) * rect.getHeight()) / 2); + } + } else if (that == BASELINE) { + return -rect.getY() + (((TOP.ordinal() - this.ordinal()) * rect.getHeight()) / 2); + } else { + return ((that.ordinal() - this.ordinal()) * rect.getHeight()) / 2; + } + } + + //----------------// + // dyToTextOrigin // + //----------------// + public int dyToTextOrigin (Rectangle rect) + { + return dyToPoint(BASELINE, rect); + } + + //----------------// + // dyToTextOrigin // + //----------------// + public double dyToTextOrigin (Rectangle2D rect) + { + return dyToPoint(BASELINE, rect); + } + } + + //------------// + // Horizontal // + //------------// + /** + * The reference x line for this symbol. + */ + public static enum Horizontal + { + LEFT, + CENTER, + RIGHT, + XORIGIN; + + //-----------// + // dxToPoint // + //-----------// + public int dxToPoint (Horizontal that, + Rectangle rect) + { + if (this == XORIGIN) { + if (that == XORIGIN) { + return 0; + } else { + return rect.x + (((that.ordinal() - LEFT.ordinal()) * rect.width) / 2); + } + } else if (that == XORIGIN) { + return -rect.x + (((LEFT.ordinal() - this.ordinal()) * rect.width) / 2); + } else { + return ((that.ordinal() - this.ordinal()) * rect.width) / 2; + } + } + + //-----------// + // dxToPoint // + //-----------// + public double dxToPoint (Horizontal that, + Rectangle2D rect) + { + if (this == XORIGIN) { + if (that == XORIGIN) { + return 0; + } else { + return rect.getX() + (((that.ordinal() - LEFT.ordinal()) * rect.getWidth()) / 2); + } + } else if (that == XORIGIN) { + return -rect.getX() + (((LEFT.ordinal() - this.ordinal()) * rect.getWidth()) / 2); + } else { + return ((that.ordinal() - this.ordinal()) * rect.getWidth()) / 2; + } + } + + //----------------// + // dxToTextOrigin // + //----------------// + public int dxToTextOrigin (Rectangle rect) + { + return dxToPoint(XORIGIN, rect); + } + + //----------------// + // dxToTextOrigin // + //----------------// + public double dxToTextOrigin (Rectangle2D rect) + { + return dxToPoint(XORIGIN, rect); + } + } } diff --git a/src/main/org/audiveris/omr/ui/symbol/ArpeggiatosSymbol.java b/src/main/org/audiveris/omr/ui/symbol/ArpeggiatosSymbol.java index 0d13adb72..63309b722 100644 --- a/src/main/org/audiveris/omr/ui/symbol/ArpeggiatosSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/ArpeggiatosSymbol.java @@ -38,11 +38,9 @@ public class ArpeggiatosSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- private final int count; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new ArpeggiatosSymbol object. * @@ -56,7 +54,6 @@ public ArpeggiatosSymbol (int count, this.count = count; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/ArticulationSymbol.java b/src/main/org/audiveris/omr/ui/symbol/ArticulationSymbol.java index 739ed0bfe..31c97f914 100644 --- a/src/main/org/audiveris/omr/ui/symbol/ArticulationSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/ArticulationSymbol.java @@ -1,173 +1,168 @@ -//------------------------------------------------------------------------------------------------// -// // -// A r t i c u l a t i o n S y m b o l // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.ui.symbol; - -import org.audiveris.omr.glyph.Shape; -import static org.audiveris.omr.ui.symbol.Alignment.*; -import static org.audiveris.omr.ui.symbol.ShapeSymbol.decoComposite; - -import java.awt.Composite; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.font.TextLayout; -import java.awt.geom.Rectangle2D; - -/** - * Class {@code ArticulationSymbol} implements an articulation symbol. - * - * @author Hervé Bitteur - */ -public class ArticulationSymbol - extends ShapeSymbol -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - /** The head part. */ - private static final BasicSymbol head = Symbols.getSymbol(Shape.NOTEHEAD_BLACK); - - /** Ratio of head height for the decorated rectangle height. */ - private static final double yRatio = 2.5; - - /** Offset ratio of articulation center WRT decorated rectangle height. */ - private static final double dyRatio = -0.25; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Create a {@code ArticulationSymbol} (with decoration?) standard size - * - * @param shape the precise shape - * @param decorated true for a decorated image - * @param codes precise code for articulation part - */ - public ArticulationSymbol (Shape shape, - boolean decorated, - int... codes) - { - this(false, shape, decorated, codes); - } - - /** - * Create a {@code ArticulationSymbol} (with decoration?) - * - * @param isIcon true for an icon - * @param shape the precise shape - * @param decorated true for a decorated image - * @param codes precise code for articulation part - */ - protected ArticulationSymbol (boolean isIcon, - Shape shape, - boolean decorated, - int... codes) - { - super(isIcon, shape, decorated, codes); - } - - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // createIcon // - //------------// - @Override - protected ShapeSymbol createIcon () - { - return new ArticulationSymbol(true, shape, decorated, codes); - } - - //-----------// - // getParams // - //-----------// - @Override - protected Params getParams (MusicFont font) - { - MyParams p = new MyParams(); - - // Articulation symbol layout - p.layout = font.layout(getString()); - - Rectangle2D rs = p.layout.getBounds(); // Symbol bounds - - if (decorated) { - // Head layout - p.headLayout = font.layout(head.getString()); - - // Use a rectangle 'yRatio' times as high as note head - Rectangle2D rh = p.headLayout.getBounds(); // Head bounds - p.rect = new Rectangle( - (int) Math.ceil(Math.max(rh.getWidth(), rs.getWidth())), - (int) Math.ceil(yRatio * rh.getHeight())); - - // Define specific offset - p.offset = new Point(0, (int) Math.rint(dyRatio * p.rect.height)); - } else { - p.rect = rs.getBounds(); - } - - return p; - } - - //-------// - // paint // - //-------// - @Override - protected void paint (Graphics2D g, - Params params, - Point location, - Alignment alignment) - { - MyParams p = (MyParams) params; - - if (decorated) { - // Draw a note head (using composite) on the bottom - Point loc = alignment.translatedPoint(BOTTOM_CENTER, p.rect, location); - Composite oldComposite = g.getComposite(); - g.setComposite(decoComposite); - MusicFont.paint(g, p.headLayout, loc, BOTTOM_CENTER); - g.setComposite(oldComposite); - - // Articulation above head - loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); - loc.translate(p.offset.x, p.offset.y); - MusicFont.paint(g, p.layout, loc, AREA_CENTER); - } else { - // Articulation alone - Point loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); - MusicFont.paint(g, p.layout, loc, AREA_CENTER); - } - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //--------// - // Params // - //--------// - protected class MyParams - extends Params - { - //~ Instance fields ------------------------------------------------------------------------ - - // offset: if decorated, offset of symbol center vs decorated image center - // layout: articulation layout - // rect: global image (= head + artic if decorated, artic if not) - // - // Layout for head - TextLayout headLayout; - } -} +//------------------------------------------------------------------------------------------------// +// // +// A r t i c u l a t i o n S y m b o l // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.ui.symbol; + +import org.audiveris.omr.glyph.Shape; +import static org.audiveris.omr.ui.symbol.Alignment.*; +import static org.audiveris.omr.ui.symbol.ShapeSymbol.decoComposite; + +import java.awt.Composite; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; + +/** + * Class {@code ArticulationSymbol} implements an articulation symbol. + * + * @author Hervé Bitteur + */ +public class ArticulationSymbol + extends ShapeSymbol +{ + + /** The head part. */ + private static final BasicSymbol head = Symbols.getSymbol(Shape.NOTEHEAD_BLACK); + + /** Ratio of head height for the decorated rectangle height. */ + private static final double yRatio = 2.5; + + /** Offset ratio of articulation center WRT decorated rectangle height. */ + private static final double dyRatio = -0.25; + + /** + * Create a {@code ArticulationSymbol} (with decoration?) standard size + * + * @param shape the precise shape + * @param decorated true for a decorated image + * @param codes precise code for articulation part + */ + public ArticulationSymbol (Shape shape, + boolean decorated, + int... codes) + { + this(false, shape, decorated, codes); + } + + /** + * Create a {@code ArticulationSymbol} (with decoration?) + * + * @param isIcon true for an icon + * @param shape the precise shape + * @param decorated true for a decorated image + * @param codes precise code for articulation part + */ + protected ArticulationSymbol (boolean isIcon, + Shape shape, + boolean decorated, + int... codes) + { + super(isIcon, shape, decorated, codes); + } + + //------------// + // createIcon // + //------------// + @Override + protected ShapeSymbol createIcon () + { + return new ArticulationSymbol(true, shape, decorated, codes); + } + + //-----------// + // getParams // + //-----------// + @Override + protected Params getParams (MusicFont font) + { + MyParams p = new MyParams(); + + // Articulation symbol layout + p.layout = font.layout(getString()); + + Rectangle2D rs = p.layout.getBounds(); // Symbol bounds + + if (decorated) { + // Head layout + p.headLayout = font.layout(head.getString()); + + // Use a rectangle 'yRatio' times as high as note head + Rectangle2D rh = p.headLayout.getBounds(); // Head bounds + p.rect = new Rectangle( + (int) Math.ceil(Math.max(rh.getWidth(), rs.getWidth())), + (int) Math.ceil(yRatio * rh.getHeight())); + + // Define specific offset + p.offset = new Point(0, (int) Math.rint(dyRatio * p.rect.height)); + } else { + p.rect = rs.getBounds(); + } + + return p; + } + + //-------// + // paint // + //-------// + @Override + protected void paint (Graphics2D g, + Params params, + Point location, + Alignment alignment) + { + MyParams p = (MyParams) params; + + if (decorated) { + // Draw a note head (using composite) on the bottom + Point loc = alignment.translatedPoint(BOTTOM_CENTER, p.rect, location); + Composite oldComposite = g.getComposite(); + g.setComposite(decoComposite); + MusicFont.paint(g, p.headLayout, loc, BOTTOM_CENTER); + g.setComposite(oldComposite); + + // Articulation above head + loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); + loc.translate(p.offset.x, p.offset.y); + MusicFont.paint(g, p.layout, loc, AREA_CENTER); + } else { + // Articulation alone + Point loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); + MusicFont.paint(g, p.layout, loc, AREA_CENTER); + } + } + + //--------// + // Params // + //--------// + protected class MyParams + extends Params + { + + // offset: if decorated, offset of symbol center vs decorated image center + // layout: articulation layout + // rect: global image (= head + artic if decorated, artic if not) + // + // Layout for head + TextLayout headLayout; + } +} diff --git a/src/main/org/audiveris/omr/ui/symbol/AugmentationSymbol.java b/src/main/org/audiveris/omr/ui/symbol/AugmentationSymbol.java index 1d58f4cd0..9098af5f6 100644 --- a/src/main/org/audiveris/omr/ui/symbol/AugmentationSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/AugmentationSymbol.java @@ -1,161 +1,156 @@ -//------------------------------------------------------------------------------------------------// -// // -// A u g m e n t a t i o n S y m b o l // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.ui.symbol; - -import org.audiveris.omr.glyph.Shape; -import static org.audiveris.omr.ui.symbol.Alignment.*; -import static org.audiveris.omr.ui.symbol.ShapeSymbol.decoComposite; - -import java.awt.Composite; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.font.TextLayout; -import java.awt.geom.Rectangle2D; - -/** - * Class {@code AugmentationSymbol} implements a decorated augmentation symbol. - * - * @author Hervé Bitteur - */ -public class AugmentationSymbol - extends ShapeSymbol -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - /** The head part. */ - private static final BasicSymbol head = Symbols.getSymbol(Shape.NOTEHEAD_BLACK); - - /** Offset ratio of dot center WRT decorated rectangle width. */ - private static final double dxRatio = +0.25; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Create a {@code AugmentationSymbol} (with decoration?) standard size - * - * @param decorated true for a decorated image - */ - public AugmentationSymbol (boolean decorated) - { - this(false, decorated); - } - - /** - * Create a {@code AugmentationSymbol} (with decoration?) - * - * @param isIcon true for an icon - * @param decorated true for a decorated image - */ - protected AugmentationSymbol (boolean isIcon, - boolean decorated) - { - super(isIcon, Shape.AUGMENTATION_DOT, decorated, 46); - } - - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // createIcon // - //------------// - @Override - protected ShapeSymbol createIcon () - { - return new AugmentationSymbol(true, decorated); - } - - //-----------// - // getParams // - //-----------// - @Override - protected BasicSymbol.Params getParams (MusicFont font) - { - MyParams p = new MyParams(); - - // Augmentation symbol layout - p.layout = font.layout(getString()); - - Rectangle2D rs = p.layout.getBounds(); // Symbol bounds - - if (decorated) { - // Head layout - p.headLayout = font.layout(head.getString()); - - // Use a rectangle twice as wide as note head - Rectangle2D hRect = p.headLayout.getBounds(); - p.rect = new Rectangle( - (int) Math.ceil(2 * hRect.getWidth()), - (int) Math.ceil(hRect.getHeight())); - - // Define specific offset - p.offset = new Point((int) Math.rint(dxRatio * p.rect.width), 0); - } else { - p.rect = rs.getBounds(); - } - - return p; - } - - //-------// - // paint // - //-------// - @Override - protected void paint (Graphics2D g, - BasicSymbol.Params params, - Point location, - Alignment alignment) - { - MyParams p = (MyParams) params; - - if (decorated) { - // Draw a note head (using composite) on the left side - Point loc = alignment.translatedPoint(MIDDLE_LEFT, p.rect, location); - Composite oldComposite = g.getComposite(); - g.setComposite(decoComposite); - MusicFont.paint(g, p.headLayout, loc, MIDDLE_LEFT); - g.setComposite(oldComposite); - - // Augmentation on right side - loc.x += ((3 * p.rect.width) / 4); - MusicFont.paint(g, p.layout, loc, AREA_CENTER); - } else { - // Augmentation alone - Point loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); - MusicFont.paint(g, p.layout, loc, AREA_CENTER); - } - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //--------// - // Params // - //--------// - protected class MyParams - extends BasicSymbol.Params - { - //~ Instance fields ------------------------------------------------------------------------ - - // offset: if decorated, offset of symbol center vs decorated image center - // layout: dot layout - // rect: global image (= head + dot if decorated, dot if not) - // - // Layout for head - TextLayout headLayout; - } -} +//------------------------------------------------------------------------------------------------// +// // +// A u g m e n t a t i o n S y m b o l // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.ui.symbol; + +import org.audiveris.omr.glyph.Shape; +import static org.audiveris.omr.ui.symbol.Alignment.*; +import static org.audiveris.omr.ui.symbol.ShapeSymbol.decoComposite; + +import java.awt.Composite; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; + +/** + * Class {@code AugmentationSymbol} implements a decorated augmentation symbol. + * + * @author Hervé Bitteur + */ +public class AugmentationSymbol + extends ShapeSymbol +{ + + /** The head part. */ + private static final BasicSymbol head = Symbols.getSymbol(Shape.NOTEHEAD_BLACK); + + /** Offset ratio of dot center WRT decorated rectangle width. */ + private static final double dxRatio = +0.25; + + /** + * Create a {@code AugmentationSymbol} (with decoration?) standard size + * + * @param decorated true for a decorated image + */ + public AugmentationSymbol (boolean decorated) + { + this(false, decorated); + } + + /** + * Create a {@code AugmentationSymbol} (with decoration?) + * + * @param isIcon true for an icon + * @param decorated true for a decorated image + */ + protected AugmentationSymbol (boolean isIcon, + boolean decorated) + { + super(isIcon, Shape.AUGMENTATION_DOT, decorated, 46); + } + + //------------// + // createIcon // + //------------// + @Override + protected ShapeSymbol createIcon () + { + return new AugmentationSymbol(true, decorated); + } + + //-----------// + // getParams // + //-----------// + @Override + protected BasicSymbol.Params getParams (MusicFont font) + { + MyParams p = new MyParams(); + + // Augmentation symbol layout + p.layout = font.layout(getString()); + + Rectangle2D rs = p.layout.getBounds(); // Symbol bounds + + if (decorated) { + // Head layout + p.headLayout = font.layout(head.getString()); + + // Use a rectangle twice as wide as note head + Rectangle2D hRect = p.headLayout.getBounds(); + p.rect = new Rectangle( + (int) Math.ceil(2 * hRect.getWidth()), + (int) Math.ceil(hRect.getHeight())); + + // Define specific offset + p.offset = new Point((int) Math.rint(dxRatio * p.rect.width), 0); + } else { + p.rect = rs.getBounds(); + } + + return p; + } + + //-------// + // paint // + //-------// + @Override + protected void paint (Graphics2D g, + BasicSymbol.Params params, + Point location, + Alignment alignment) + { + MyParams p = (MyParams) params; + + if (decorated) { + // Draw a note head (using composite) on the left side + Point loc = alignment.translatedPoint(MIDDLE_LEFT, p.rect, location); + Composite oldComposite = g.getComposite(); + g.setComposite(decoComposite); + MusicFont.paint(g, p.headLayout, loc, MIDDLE_LEFT); + g.setComposite(oldComposite); + + // Augmentation on right side + loc.x += ((3 * p.rect.width) / 4); + MusicFont.paint(g, p.layout, loc, AREA_CENTER); + } else { + // Augmentation alone + Point loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); + MusicFont.paint(g, p.layout, loc, AREA_CENTER); + } + } + + //--------// + // Params // + //--------// + protected class MyParams + extends BasicSymbol.Params + { + + // offset: if decorated, offset of symbol center vs decorated image center + // layout: dot layout + // rect: global image (= head + dot if decorated, dot if not) + // + // Layout for head + TextLayout headLayout; + } +} diff --git a/src/main/org/audiveris/omr/ui/symbol/BackToBackSymbol.java b/src/main/org/audiveris/omr/ui/symbol/BackToBackSymbol.java index 9d9ff3b43..5d57ef53b 100644 --- a/src/main/org/audiveris/omr/ui/symbol/BackToBackSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/BackToBackSymbol.java @@ -39,7 +39,6 @@ public class BackToBackSymbol extends ShapeSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- // Total width, computed from width of RIGHT_REPEAT_SIGN symbol private static final double WIDTH_RATIO = 1.6; @@ -47,7 +46,6 @@ public class BackToBackSymbol // Abscissa of thin barline, computed from width of RIGHT_REPEAT_SIGN symbol private static final double DX_RATIO = 1.15; - //~ Instance fields ---------------------------------------------------------------------------- // The RIGHT_REPEAT_SIGN symbol private final ShapeSymbol rightSymbol = Symbols.getSymbol(RIGHT_REPEAT_SIGN); @@ -57,7 +55,6 @@ public class BackToBackSymbol // The REPEAT_DOT_PAIR symbol private final ShapeSymbol dotsSymbol = Symbols.getSymbol(REPEAT_DOT_PAIR); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a BackToBackSymbol * @@ -68,7 +65,6 @@ public BackToBackSymbol (boolean isIcon) super(isIcon, Shape.DOUBLE_BARLINE, false); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -120,14 +116,12 @@ protected void paint (Graphics2D g, MusicFont.paint(g, p.dotsLayout, loc, MIDDLE_RIGHT); } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // MyParams // //----------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ TextLayout thinLayout; diff --git a/src/main/org/audiveris/omr/ui/symbol/BasicSymbol.java b/src/main/org/audiveris/omr/ui/symbol/BasicSymbol.java index 8a9747c24..0c5731ad0 100644 --- a/src/main/org/audiveris/omr/ui/symbol/BasicSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/BasicSymbol.java @@ -55,7 +55,6 @@ public class BasicSymbol implements SymbolIcon { - //~ Static fields/initializers ----------------------------------------------------------------- protected static final Logger logger = LoggerFactory.getLogger(BasicSymbol.class); @@ -81,36 +80,34 @@ public class BasicSymbol 0); // m12 /** A transformation to turn 1 quadrant clockwise. */ - protected static final AffineTransform quadrantRotateOne = AffineTransform.getQuadrantRotateInstance( - 1); + protected static final AffineTransform quadrantRotateOne = AffineTransform + .getQuadrantRotateInstance(1); /** A transformation to turn 2 quadrants clockwise. */ - protected static final AffineTransform quadrantRotateTwo = AffineTransform.getQuadrantRotateInstance( - 2); + protected static final AffineTransform quadrantRotateTwo = AffineTransform + .getQuadrantRotateInstance(2); /** A transformation for really small icon display. */ - protected static AffineTransform tiny = AffineTransform.getScaleInstance(0.5, 0.5); - - //~ Instance fields ---------------------------------------------------------------------------- - /** To flag an icon symbol. */ - protected final boolean isIcon; + protected static final AffineTransform tiny = AffineTransform.getScaleInstance(0.5, 0.5); /** Sequence of point codes. */ public final int[] codes; - /** Related image, corresponding to standard interline. */ - private BufferedImage image; + /** To flag an icon symbol. */ + protected final boolean isIcon; /** Pre-scaled symbol for icon display. */ protected BasicSymbol icon; - /** Image dimension corresponding to standard interline. */ - private Dimension dimension; - /** Offset of centroid WRT area center, specified in ratio of width and height. */ protected Point2D centroidOffset; - //~ Constructors ------------------------------------------------------------------------------- + /** Related image, corresponding to standard interline. */ + private BufferedImage image; + + /** Image dimension corresponding to standard interline. */ + private Dimension dimension; + /** * Creates a new BasicSymbol object. * @@ -134,13 +131,15 @@ public BasicSymbol (int... codes) this(false, codes); } + /** + * Useful? + */ public BasicSymbol () { this.isIcon = false; this.codes = null; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // buildImage // //------------// @@ -237,6 +236,11 @@ public Point getRefPoint (Rectangle box) //-----------// // getString // //-----------// + /** + * Report the String defined by Unicode characters + * + * @return the resulting String + */ public final String getString () { return new String(codes, 0, codes.length); @@ -283,7 +287,7 @@ public String toString () StringBuilder sb = new StringBuilder(getClass().getSimpleName()); sb.append("{"); - sb.append(internalsString()); + sb.append(internals()); sb.append("}"); @@ -358,6 +362,12 @@ protected BufferedImage getImage () //-----------// // getParams // //-----------// + /** + * Report the specific Params object to draw this symbol. + * + * @param font scaled music font + * @return the specific Params object + */ protected Params getParams (MusicFont font) { Params p = new Params(); @@ -388,10 +398,15 @@ protected int getWidth () return dimension.width; } - //-----------------// - // internalsString // - //-----------------// - protected String internalsString () + //-----------// + // internals // + //-----------// + /** + * Report a string description of internals. + * + * @return string of internal informations + */ + protected String internals () { StringBuilder sb = new StringBuilder(); @@ -529,7 +544,6 @@ private int[] shiftedCodesOf (int[] codes) return values; } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// @@ -538,7 +552,6 @@ private int[] shiftedCodesOf (int[] codes) */ protected class Params { - //~ Instance fields ------------------------------------------------------------------------ /** Specific offset, if any, from area center. */ Point offset; diff --git a/src/main/org/audiveris/omr/ui/symbol/BeamHookSymbol.java b/src/main/org/audiveris/omr/ui/symbol/BeamHookSymbol.java index a6ade9130..9b04a58ee 100644 --- a/src/main/org/audiveris/omr/ui/symbol/BeamHookSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/BeamHookSymbol.java @@ -31,7 +31,6 @@ public class BeamHookSymbol extends BeamSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Create a BeamHookSymbol @@ -51,7 +50,6 @@ protected BeamHookSymbol (boolean isIcon) super(1, isIcon, Shape.BEAM_HOOK); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/BeamSymbol.java b/src/main/org/audiveris/omr/ui/symbol/BeamSymbol.java index 20321309a..23e1cc1be 100644 --- a/src/main/org/audiveris/omr/ui/symbol/BeamSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/BeamSymbol.java @@ -40,16 +40,13 @@ public class BeamSymbol extends ShapeSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- // The head+stem part private static final BasicSymbol quarter = Symbols.SYMBOL_QUARTER; - //~ Instance fields ---------------------------------------------------------------------------- - // Number of beams + /** Number of beams. */ protected final int beamCount; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a BeamSymbol. * @@ -80,7 +77,6 @@ protected BeamSymbol (int beamCount, this.beamCount = beamCount; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -151,14 +147,12 @@ protected void paint (Graphics2D g, g.setComposite(oldComposite); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ // layout for just quarter layout // rect for global image diff --git a/src/main/org/audiveris/omr/ui/symbol/BraceSymbol.java b/src/main/org/audiveris/omr/ui/symbol/BraceSymbol.java index 232cbae87..6023c1f4f 100644 --- a/src/main/org/audiveris/omr/ui/symbol/BraceSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/BraceSymbol.java @@ -39,7 +39,6 @@ public class BraceSymbol extends ShapeSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- // The upper part symbol private static final BasicSymbol upperSymbol = Symbols.SYMBOL_BRACE_UPPER_HALF; @@ -47,7 +46,6 @@ public class BraceSymbol // The lower part symbol private static final BasicSymbol lowerSymbol = Symbols.SYMBOL_BRACE_LOWER_HALF; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a BraceSymbol (which is made of upper and lower parts) * @@ -58,7 +56,6 @@ public BraceSymbol (boolean isIcon) super(isIcon, Shape.BRACE, false); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -101,14 +98,12 @@ protected void paint (Graphics2D g, MusicFont.paint(g, p.lowerLayout, loc, TOP_LEFT); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ TextLayout upperLayout; diff --git a/src/main/org/audiveris/omr/ui/symbol/BracketSymbol.java b/src/main/org/audiveris/omr/ui/symbol/BracketSymbol.java index 07acca930..4b81d3370 100644 --- a/src/main/org/audiveris/omr/ui/symbol/BracketSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/BracketSymbol.java @@ -39,7 +39,6 @@ public class BracketSymbol extends ShapeSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- // The upper serif private static final BasicSymbol upperSymbol = Symbols.SYMBOL_BRACKET_UPPER_SERIF; @@ -47,7 +46,6 @@ public class BracketSymbol // The lower serif private static final BasicSymbol lowerSymbol = Symbols.SYMBOL_BRACKET_LOWER_SERIF; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a BracketSymbol (which is made of upper and lower parts) * @@ -58,7 +56,6 @@ public BracketSymbol (boolean isIcon) super(isIcon, Shape.BRACKET, false); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -86,7 +83,8 @@ protected Params getParams (MusicFont font) Rectangle2D lowerRect = p.lowerLayout.getBounds(); p.rect = new Rectangle( (int) Math.ceil(upperRect.getWidth()), - (int) Math.floor(upperRect.getHeight() + trunkRect.getHeight() + lowerRect.getHeight())); + (int) Math.floor( + upperRect.getHeight() + trunkRect.getHeight() + lowerRect.getHeight())); return p; } @@ -109,14 +107,12 @@ protected void paint (Graphics2D g, MusicFont.paint(g, p.lowerLayout, loc, BOTTOM_LEFT); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ TextLayout upperLayout; diff --git a/src/main/org/audiveris/omr/ui/symbol/CrescendoSymbol.java b/src/main/org/audiveris/omr/ui/symbol/CrescendoSymbol.java index bbcfba570..a5b2c4d49 100644 --- a/src/main/org/audiveris/omr/ui/symbol/CrescendoSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/CrescendoSymbol.java @@ -32,11 +32,12 @@ /** * Class {@code CrescendoSymbol} displays a crescendo symbol: {@literal "<"}. + * + * @author Hervé Bitteur */ public class CrescendoSymbol extends ShapeSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new CrescendoSymbol object. @@ -50,7 +51,6 @@ public CrescendoSymbol (boolean isIcon, super(isIcon, shape, false); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -97,14 +97,12 @@ protected void paint (Graphics2D g, g.setStroke(oldStroke); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ Stroke stroke; } diff --git a/src/main/org/audiveris/omr/ui/symbol/CustomNumDenSymbol.java b/src/main/org/audiveris/omr/ui/symbol/CustomNumDenSymbol.java index bff2315e8..536685d8b 100644 --- a/src/main/org/audiveris/omr/ui/symbol/CustomNumDenSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/CustomNumDenSymbol.java @@ -32,11 +32,12 @@ /** * Class {@code CustomNumDenSymbol} displays a custom time signature, with just the N * and D letters. + * + * @author Hervé Bitteur */ public class CustomNumDenSymbol extends ShapeSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new CustomNumDenSymbol object, standard size @@ -56,7 +57,6 @@ protected CustomNumDenSymbol (boolean isIcon) super(isIcon, Shape.CUSTOM_TIME, true); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -102,14 +102,12 @@ protected void paint (Graphics2D g, OmrFont.paint(g, p.dLayout, loc, BOTTOM_CENTER); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ // layout not used // rect for global image diff --git a/src/main/org/audiveris/omr/ui/symbol/DecrescendoSymbol.java b/src/main/org/audiveris/omr/ui/symbol/DecrescendoSymbol.java index 84f4b8cb3..5f7c26b22 100644 --- a/src/main/org/audiveris/omr/ui/symbol/DecrescendoSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/DecrescendoSymbol.java @@ -30,11 +30,12 @@ /** * Class {@code DecrescendoSymbol} displays a decrescendo symbol: {@literal ">"}. + * + * @author Hervé Bitteur */ public class DecrescendoSymbol extends CrescendoSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new DecrescendoSymbol object. @@ -48,7 +49,6 @@ public DecrescendoSymbol (boolean isIcon, super(isIcon, shape); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/DoubleBarlineSymbol.java b/src/main/org/audiveris/omr/ui/symbol/DoubleBarlineSymbol.java index 6531dfa23..2795992c7 100644 --- a/src/main/org/audiveris/omr/ui/symbol/DoubleBarlineSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/DoubleBarlineSymbol.java @@ -37,16 +37,13 @@ public class DoubleBarlineSymbol extends ShapeSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- // Total width, computed from width of thin barline private static final double WIDTH_RATIO = 4.5; - //~ Instance fields ---------------------------------------------------------------------------- // The thin barline symbol private final ShapeSymbol thinSymbol = Symbols.getSymbol(Shape.THIN_BARLINE); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a DoubleBarlineSymbol * @@ -57,7 +54,6 @@ public DoubleBarlineSymbol (boolean isIcon) super(isIcon, Shape.DOUBLE_BARLINE, false); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/EndingSymbol.java b/src/main/org/audiveris/omr/ui/symbol/EndingSymbol.java index 43914797e..cd7526f3a 100644 --- a/src/main/org/audiveris/omr/ui/symbol/EndingSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/EndingSymbol.java @@ -36,7 +36,6 @@ public class EndingSymbol extends ShapeSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Create an EndingSymbol @@ -56,7 +55,6 @@ protected EndingSymbol (boolean isIcon) super(isIcon, Shape.ENDING, true); // Decorated } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/EvenStepSymbol.java b/src/main/org/audiveris/omr/ui/symbol/EvenStepSymbol.java index f2c56f886..994c247e6 100644 --- a/src/main/org/audiveris/omr/ui/symbol/EvenStepSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/EvenStepSymbol.java @@ -39,7 +39,6 @@ public class EvenStepSymbol extends ShapeSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new EvenStepSymbol object. @@ -53,7 +52,6 @@ public EvenStepSymbol (Shape shape, super(shape, codes); } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getParams // //-----------// @@ -93,14 +91,12 @@ protected void paint (Graphics2D g, g.fillRect(loc.x, loc.y - (p.line / 2), p.rect.width - 1, p.line); } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // MyParams // //----------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ int line; // Thickness of a ledger or staff line } diff --git a/src/main/org/audiveris/omr/ui/symbol/FermataArcSymbol.java b/src/main/org/audiveris/omr/ui/symbol/FermataArcSymbol.java index 90f71fcef..e84a68141 100644 --- a/src/main/org/audiveris/omr/ui/symbol/FermataArcSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/FermataArcSymbol.java @@ -1,152 +1,147 @@ -//------------------------------------------------------------------------------------------------// -// // -// F e r m a t a A r c S y m b o l // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.ui.symbol; - -import org.audiveris.omr.glyph.Shape; -import static org.audiveris.omr.glyph.Shape.DOT_set; -import static org.audiveris.omr.ui.symbol.Alignment.*; - -import java.awt.Color; -import java.awt.Composite; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.font.TextLayout; - -/** - * Class {@code FermataArcSymbol} implements Fermata Arc symbols with the related dot - * as decoration. - * - * @author Hervé Bitteur - */ -public class FermataArcSymbol - extends ShapeSymbol -{ - //~ Instance fields ---------------------------------------------------------------------------- - - // The DOT_set symbol - private final ShapeSymbol dotSymbol = Symbols.getSymbol(DOT_set); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Create a FermataArcSymbol (with decoration?) standard size. - * - * @param shape the precise shape - * @param decorated true for a decorated image - * @param codes precise code for rest part - */ - public FermataArcSymbol (Shape shape, - boolean decorated, - int... codes) - { - this(false, shape, decorated, codes); - } - - /** - * Create a FermataArcSymbol (with decoration?). - * - * @param isIcon true for an icon - * @param shape the precise shape - * @param decorated true for a decorated image - * @param codes precise code for rest part - */ - protected FermataArcSymbol (boolean isIcon, - Shape shape, - boolean decorated, - int... codes) - { - super(isIcon, shape, decorated, codes); - } - - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // createIcon // - //------------// - @Override - protected ShapeSymbol createIcon () - { - return new FermataArcSymbol(true, shape, decorated, codes); - } - - //-----------// - // getParams // - //-----------// - @Override - protected Params getParams (MusicFont font) - { - MyParams p = new MyParams(); - - // Full symbol (arc + dot) - p.layout = font.layout(codes); - p.rect = p.layout.getBounds().getBounds(); - - // Dot layout - p.dotLayout = font.layout(dotSymbol); - - return p; - } - - //-------// - // paint // - //-------// - @Override - protected void paint (Graphics2D g, - Params params, - Point location, - Alignment alignment) - { - // We paint the full fermata symbol first - // Then we paint a dot using white or decoComposite - MyParams p = (MyParams) params; - Alignment align = (shape == Shape.FERMATA_ARC) ? BOTTOM_CENTER : TOP_CENTER; - Point loc = alignment.translatedPoint(align, p.rect, location); - - MusicFont.paint(g, p.layout, loc, align); // Arc + Dot - - if (decorated) { - // Paint dot in gray - Composite oldComposite = g.getComposite(); - g.setComposite(decoComposite); - MusicFont.paint(g, p.dotLayout, loc, align); - g.setComposite(oldComposite); - } else { - // Erase dot using white color (?) - Color oldColor = g.getColor(); - g.setColor(Color.WHITE); - MusicFont.paint(g, p.dotLayout, loc, align); - g.setColor(oldColor); - } - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //--------// - // Params // - //--------// - protected class MyParams - extends Params - { - //~ Instance fields ------------------------------------------------------------------------ - - // layout for full fermata - // rect for full fermata - TextLayout dotLayout; // For the dot - } -} +//------------------------------------------------------------------------------------------------// +// // +// F e r m a t a A r c S y m b o l // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.ui.symbol; + +import org.audiveris.omr.glyph.Shape; +import static org.audiveris.omr.glyph.Shape.DOT_set; +import static org.audiveris.omr.ui.symbol.Alignment.*; + +import java.awt.Color; +import java.awt.Composite; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.font.TextLayout; + +/** + * Class {@code FermataArcSymbol} implements Fermata Arc symbols with the related dot + * as decoration. + * + * @author Hervé Bitteur + */ +public class FermataArcSymbol + extends ShapeSymbol +{ + + // The DOT_set symbol + private final ShapeSymbol dotSymbol = Symbols.getSymbol(DOT_set); + + /** + * Create a FermataArcSymbol (with decoration?) standard size. + * + * @param shape the precise shape + * @param decorated true for a decorated image + * @param codes precise code for rest part + */ + public FermataArcSymbol (Shape shape, + boolean decorated, + int... codes) + { + this(false, shape, decorated, codes); + } + + /** + * Create a FermataArcSymbol (with decoration?). + * + * @param isIcon true for an icon + * @param shape the precise shape + * @param decorated true for a decorated image + * @param codes precise code for rest part + */ + protected FermataArcSymbol (boolean isIcon, + Shape shape, + boolean decorated, + int... codes) + { + super(isIcon, shape, decorated, codes); + } + + //------------// + // createIcon // + //------------// + @Override + protected ShapeSymbol createIcon () + { + return new FermataArcSymbol(true, shape, decorated, codes); + } + + //-----------// + // getParams // + //-----------// + @Override + protected Params getParams (MusicFont font) + { + MyParams p = new MyParams(); + + // Full symbol (arc + dot) + p.layout = font.layout(codes); + p.rect = p.layout.getBounds().getBounds(); + + // Dot layout + p.dotLayout = font.layout(dotSymbol); + + return p; + } + + //-------// + // paint // + //-------// + @Override + protected void paint (Graphics2D g, + Params params, + Point location, + Alignment alignment) + { + // We paint the full fermata symbol first + // Then we paint a dot using white or decoComposite + MyParams p = (MyParams) params; + Alignment align = (shape == Shape.FERMATA_ARC) ? BOTTOM_CENTER : TOP_CENTER; + Point loc = alignment.translatedPoint(align, p.rect, location); + + MusicFont.paint(g, p.layout, loc, align); // Arc + Dot + + if (decorated) { + // Paint dot in gray + Composite oldComposite = g.getComposite(); + g.setComposite(decoComposite); + MusicFont.paint(g, p.dotLayout, loc, align); + g.setComposite(oldComposite); + } else { + // Erase dot using white color (?) + Color oldColor = g.getColor(); + g.setColor(Color.WHITE); + MusicFont.paint(g, p.dotLayout, loc, align); + g.setColor(oldColor); + } + } + + //--------// + // Params // + //--------// + protected class MyParams + extends Params + { + + // layout for full fermata + // rect for full fermata + TextLayout dotLayout; // For the dot + } +} diff --git a/src/main/org/audiveris/omr/ui/symbol/FlagsDownSymbol.java b/src/main/org/audiveris/omr/ui/symbol/FlagsDownSymbol.java index 8c520d7da..0180baf67 100644 --- a/src/main/org/audiveris/omr/ui/symbol/FlagsDownSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/FlagsDownSymbol.java @@ -38,12 +38,10 @@ public class FlagsDownSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- /** The number of flags. */ protected final int fn; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new FlagsDownSymbol object. * @@ -59,7 +57,6 @@ public FlagsDownSymbol (int flagCount, this.fn = flagCount; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -81,8 +78,8 @@ protected MyParams getParams (MusicFont font) 0, 0, (int) Math.ceil(p.rect2.getWidth()), - (((fn / 2) + ((fn + 1) % 2)) * Math.abs(p.dy)) - + ((fn % 2) * (int) Math.ceil(p.rect1.getHeight()))); + (((fn / 2) + ((fn + 1) % 2)) * Math.abs(p.dy)) + ((fn % 2) * (int) Math.ceil( + p.rect1.getHeight()))); return p; } @@ -90,6 +87,12 @@ protected MyParams getParams (MusicFont font) //------------// // initParams // //------------// + /** + * Specific initialization, based on provided font, depending on flag direction. + * + * @param font provided font + * @return initialized params + */ protected MyParams initParams (MusicFont font) { MyParams p = new MyParams(); @@ -127,14 +130,12 @@ protected void paint (Graphics2D g, } } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // MyParams // //----------// protected class MyParams extends BasicSymbol.Params { - //~ Instance fields ------------------------------------------------------------------------ TextLayout flag1; diff --git a/src/main/org/audiveris/omr/ui/symbol/FlagsUpSymbol.java b/src/main/org/audiveris/omr/ui/symbol/FlagsUpSymbol.java index ba76c8535..bbcf42f33 100644 --- a/src/main/org/audiveris/omr/ui/symbol/FlagsUpSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/FlagsUpSymbol.java @@ -33,7 +33,6 @@ public class FlagsUpSymbol extends FlagsDownSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new FlagsUpSymbol object. @@ -49,7 +48,6 @@ public FlagsUpSymbol (int flagCount, super(flagCount, isIcon, shape); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/FlatSymbol.java b/src/main/org/audiveris/omr/ui/symbol/FlatSymbol.java index c97166f35..33ef73851 100644 --- a/src/main/org/audiveris/omr/ui/symbol/FlatSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/FlatSymbol.java @@ -28,11 +28,12 @@ /** * Class {@code FlatSymbol} handles a flat or double-flat symbol with a refPoint. + * + * @author Hervé Bitteur */ public class FlatSymbol extends ShapeSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new FlatSymbol object. @@ -60,19 +61,18 @@ protected FlatSymbol (boolean isIcon, super(isIcon, shape, false, codes); } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // getRefPoint // //-------------// /** - * Report the symbol reference point which is lower than center for flats. + * {@inheritDoc} + *

                                                                                                                                            + * For a flat symbol, the reference point is lower than area center. */ @Override public Point getRefPoint (Rectangle box) { - return new Point( - box.x + (box.width / 2), - box.y + (int) Math.rint(box.height * 0.67)); + return new Point(box.x + (box.width / 2), box.y + (int) Math.rint(box.height * 0.67)); } //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/HeadsSymbol.java b/src/main/org/audiveris/omr/ui/symbol/HeadsSymbol.java index e88c972e4..5341e5ad8 100644 --- a/src/main/org/audiveris/omr/ui/symbol/HeadsSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/HeadsSymbol.java @@ -32,15 +32,15 @@ /** * Class {@code HeadsSymbol} displays a column of several identical heads. * (black, void or whole) + * + * @author Hervé Bitteur */ public class HeadsSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- private final int count; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new HeadsSymbol object. * @@ -58,7 +58,6 @@ public HeadsSymbol (int count, this.count = count; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -104,14 +103,12 @@ protected void paint (Graphics2D g, } } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // MyParams // //----------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ int dy; } diff --git a/src/main/org/audiveris/omr/ui/symbol/KeyFlatSymbol.java b/src/main/org/audiveris/omr/ui/symbol/KeyFlatSymbol.java index ea1325a7c..ec2bfc865 100644 --- a/src/main/org/audiveris/omr/ui/symbol/KeyFlatSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/KeyFlatSymbol.java @@ -25,11 +25,12 @@ /** * Class {@code KeyFlatSymbol} displays a Key Signature symbol. + * + * @author Hervé Bitteur */ public class KeyFlatSymbol extends KeySymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new KeyFlatSymbol object. @@ -45,7 +46,6 @@ public KeyFlatSymbol (int key, super(key, isIcon, shape, 98); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/KeySharpSymbol.java b/src/main/org/audiveris/omr/ui/symbol/KeySharpSymbol.java index 32afc75d0..a9807985b 100644 --- a/src/main/org/audiveris/omr/ui/symbol/KeySharpSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/KeySharpSymbol.java @@ -25,11 +25,12 @@ /** * Class {@code KeySharpSymbol} displays a Key Signature symbol. + * + * @author Hervé Bitteur */ public class KeySharpSymbol extends KeySymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new KeySharpSymbol object. @@ -45,7 +46,6 @@ public KeySharpSymbol (int key, super(key, isIcon, shape, 35); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/KeySymbol.java b/src/main/org/audiveris/omr/ui/symbol/KeySymbol.java index 548e5ced0..b35f6ddbe 100644 --- a/src/main/org/audiveris/omr/ui/symbol/KeySymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/KeySymbol.java @@ -32,16 +32,16 @@ /** * Class {@code KeySymbol} displays a Key Signature symbol. + * + * @author Hervé Bitteur */ public abstract class KeySymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- /** The key to represent, -7..-1 for flats, 1..7 for sharps */ protected final int key; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new KeySymbol object. * @@ -59,7 +59,6 @@ public KeySymbol (int key, this.key = key; } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getParams // //-----------// @@ -68,7 +67,7 @@ protected MyParams getParams (MusicFont font) { MyParams p = new MyParams(); - p.stepDy = font.getStaffInterline() / 2; + p.stepDy = font.getStaffInterline() / 2.0; // One item p.layout = layout(font); @@ -128,14 +127,12 @@ protected void paint (Graphics2D g, } } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // MyParams // //----------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ double stepDy; // Dy from one step to the other diff --git a/src/main/org/audiveris/omr/ui/symbol/LedgerSymbol.java b/src/main/org/audiveris/omr/ui/symbol/LedgerSymbol.java index 0d5fb8faa..f00e712f3 100644 --- a/src/main/org/audiveris/omr/ui/symbol/LedgerSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/LedgerSymbol.java @@ -38,12 +38,10 @@ public class LedgerSymbol extends ShapeSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- // The head part private static final BasicSymbol head = Symbols.getSymbol(Shape.NOTEHEAD_BLACK); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a LedgerSymbol (with decoration?) standard size * @@ -66,7 +64,6 @@ protected LedgerSymbol (boolean isIcon, super(isIcon, Shape.LEDGER, decorated); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/LongRestSymbol.java b/src/main/org/audiveris/omr/ui/symbol/LongRestSymbol.java index b7f8c2bee..076ed90c4 100644 --- a/src/main/org/audiveris/omr/ui/symbol/LongRestSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/LongRestSymbol.java @@ -34,7 +34,6 @@ public class LongRestSymbol extends RestSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Create a LongRestSymbol (with decoration?) @@ -59,7 +58,6 @@ private LongRestSymbol (boolean isIcon, super(isIcon, Shape.LONG_REST, decorated, 227); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/MusicFont.java b/src/main/org/audiveris/omr/ui/symbol/MusicFont.java index 86b415361..8a8fa6fec 100644 --- a/src/main/org/audiveris/omr/ui/symbol/MusicFont.java +++ b/src/main/org/audiveris/omr/ui/symbol/MusicFont.java @@ -45,7 +45,8 @@ * when rendering score views. *

                                                                                                                                            * The strategy is to use a properly scaled instance of this class to carry out the drawing of music - * symbols with the correct size. The scaling should address:

                                                                                                                                              + * symbols with the correct size. The scaling should address: + *
                                                                                                                                                *
                                                                                                                                              • An interline value adapted to current sheet (or even staff) scaling,
                                                                                                                                              • *
                                                                                                                                              • A specific font size meant for heads rendering, which may significantly differ from pure * interline-based scaling.
                                                                                                                                              • @@ -53,21 +54,21 @@ * To get properly scaled instances, use the convenient methods {@link #getBaseFont(int)} and * {@link #getHeadFont(Scale, int)}. *

                                                                                                                                                - * There are two well-known pre-scaled instances of this class:

                                                                                                                                                  + * There are two well-known pre-scaled instances of this class: + *
                                                                                                                                                    *
                                                                                                                                                  • {@link #baseMusicFont} for standard symbols (scale = 1)
                                                                                                                                                  • *
                                                                                                                                                  • {@link #iconMusicFont} for icon symbols (scale = 1/2)
                                                                                                                                                  • *
                                                                                                                                                  * The current underlying font is MusicalSymbols. * * @see - *
                                                                                                                                                  http://fonts.simplythebest.net/font/108/musical_symbols-font.font - * + * http://fonts.simplythebest.net/font/108/musical_symbols-font.font * @author Hervé Bitteur */ public class MusicFont extends OmrFont { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -87,7 +88,7 @@ public class MusicFont public static final int CODE_OFFSET = 0xf000; /** Cache of font according to scaling value (pointSize, staffInterline). */ - private static final Map scalingMap = new HashMap(); + private static final Map scalingMap = new HashMap<>(); /** Default interline value, consistent with base font: {@value}. */ public static final int DEFAULT_INTERLINE = 16; @@ -102,11 +103,9 @@ public class MusicFont getPointSize(DEFAULT_INTERLINE) / 2, DEFAULT_INTERLINE / 2); - //~ Instance fields ---------------------------------------------------------------------------- /** Interline value of the staves where this font is used. */ private final int staffInterline; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new MusicFont object. * @@ -120,7 +119,181 @@ private MusicFont (int sizePts, this.staffInterline = staffInterline; } - //~ Methods ------------------------------------------------------------------------------------ + //------------// + // buildImage // + //------------// + /** + * Build an image from the shape definition in MusicFont, using the + * intrinsic scaling of this font. + * + * @param shape the desired shape + * @param decorated true if shape display must use decorations + * @return the image built with proper scaling, or null + */ + public BufferedImage buildImage (Shape shape, + boolean decorated) + { + ShapeSymbol symbol = Symbols.getSymbol(shape, decorated); + + if (symbol == null) { + return null; + } else { + return symbol.buildImage(this); + } + } + + //--------// + // equals // + //--------// + @Override + public boolean equals (Object obj) + { + if (obj == this) { + return true; + } + + if (obj instanceof MusicFont) { + MusicFont that = (MusicFont) obj; + + if (this.staffInterline != that.staffInterline) { + return false; + } + + return super.equals(obj); + } + + return false; + } + + //----------// + // hashCode // + //----------// + @Override + public int hashCode () + { + int hash = 7; + hash = (71 * hash) + this.staffInterline; + + return hash; + } + + //--------// + // layout // + //--------// + /** + * Build a TextLayout from a Shape, using its related String of MusicFont characters, + * and potentially sized by an AffineTransform instance. + * + * @param shape the shape to be drawn with MusicFont chars + * @param fat potential affine transformation + * @return the (sized) TextLayout ready to be drawn + */ + public TextLayout layout (Shape shape, + AffineTransform fat) + { + ShapeSymbol symbol = Symbols.getSymbol(shape); + + if (symbol == null) { + logger.debug("No MusicFont symbol for {}", shape); + + return null; + } + + return layout(symbol.getString(), fat); + } + + //--------// + // layout // + //--------// + /** + * Build a TextLayout from a Shape, using its related String of + * MusicFont characters, and sized to fit the provided dimension. + * + * @param shape the shape to be drawn with MusicFont chars + * @param dimension the dim to fit as much as possible + * @return the adjusted TextLayout ready to be drawn + */ + public TextLayout layout (Shape shape, + Dimension dimension) + { + ShapeSymbol symbol = Symbols.getSymbol(shape); + + if (symbol != null) { + return layout(symbol, dimension); + } else { + return null; + } + } + + //--------// + // layout // + //--------// + /** + * Build a TextLayout from a symbol, using its related String of + * MusicFont characters, and sized to fit the provided dimension. + * + * @param symbol the symbol to be drawn with MusicFont chars + * @param dimension the dim to fit as much as possible + * @return the adjusted TextLayout ready to be drawn + */ + public TextLayout layout (BasicSymbol symbol, + Dimension dimension) + { + String str = symbol.getString(); + TextLayout layout = new TextLayout(str, this, frc); + + // Compute proper affine transformation + Rectangle2D rect = layout.getBounds(); + AffineTransform fat = AffineTransform.getScaleInstance( + dimension.width / rect.getWidth(), + dimension.height / rect.getHeight()); + + return layout(str, fat); + } + + //--------// + // layout // + //--------// + /** + * Build a TextLayout from a Shape, using its related String of + * MusicFont character codes. + * + * @param shape the shape to be drawn with MusicFont chars + * @return the TextLayout ready to be drawn + */ + public TextLayout layout (Shape shape) + { + return layout(shape, (AffineTransform) null); + } + + //--------// + // layout // + //--------// + /** + * Build a TextLayout from a String of MusicFont character codes. + * + * @param codes the MusicFont codes + * @return the TextLayout ready to be drawn + */ + public TextLayout layout (int... codes) + { + return layout(new String(codes, 0, codes.length)); + } + + //-------------------// + // getStaffInterline // + //-------------------// + /** + * Report the number of pixels of the interline that corresponds to the staves + * where this font is used. + * + * @return the scaled interline of the related staff size. + */ + protected int getStaffInterline () + { + return staffInterline; + } + //------------// // buildImage // //------------// @@ -265,8 +438,8 @@ public static int getHeadPointSize (Scale scale, { final Scale.MusicFontScale musicFontScale = scale.getMusicFontScale(); final Scale smallScale = scale.getSmallScale(); - final boolean isSmallStaff = (smallScale != null) - && (smallScale.getInterline() == staffInterline); + final boolean isSmallStaff = (smallScale != null) && (smallScale + .getInterline() == staffInterline); if (musicFontScale != null) { if (isSmallStaff) { @@ -294,6 +467,11 @@ public static int getHeadPointSize (Scale scale, //--------------// // getHeadRatio // //--------------// + /** + * Report an increase in head size, a trick for better head rendering. + * + * @return ratio slightly over 1.0 + */ public static double getHeadRatio () { return constants.headRatio.getValue(); @@ -337,154 +515,12 @@ public static int getPointSize (int staffInterline) return 4 * staffInterline; } - //------------// - // buildImage // - //------------// - /** - * Build an image from the shape definition in MusicFont, using the - * intrinsic scaling of this font. - * - * @param shape the desired shape - * @param decorated true if shape display must use decorations - * @return the image built with proper scaling, or null - */ - public BufferedImage buildImage (Shape shape, - boolean decorated) - { - ShapeSymbol symbol = Symbols.getSymbol(shape, decorated); - - if (symbol == null) { - return null; - } else { - return symbol.buildImage(this); - } - } - - //--------// - // layout // - //--------// - /** - * Build a TextLayout from a Shape, using its related String of MusicFont characters, - * and potentially sized by an AffineTransform instance. - * - * @param shape the shape to be drawn with MusicFont chars - * @param fat potential affine transformation - * @return the (sized) TextLayout ready to be drawn - */ - public TextLayout layout (Shape shape, - AffineTransform fat) - { - ShapeSymbol symbol = Symbols.getSymbol(shape); - - if (symbol == null) { - logger.debug("No MusicFont symbol for {}", shape); - - return null; - } - - return layout(symbol.getString(), fat); - } - - //--------// - // layout // - //--------// - /** - * Build a TextLayout from a Shape, using its related String of - * MusicFont characters, and sized to fit the provided dimension. - * - * @param shape the shape to be drawn with MusicFont chars - * @param dimension the dim to fit as much as possible - * @return the adjusted TextLayout ready to be drawn - */ - public TextLayout layout (Shape shape, - Dimension dimension) - { - ShapeSymbol symbol = Symbols.getSymbol(shape); - - if (symbol != null) { - return layout(symbol, dimension); - } else { - return null; - } - } - - //--------// - // layout // - //--------// - /** - * Build a TextLayout from a symbol, using its related String of - * MusicFont characters, and sized to fit the provided dimension. - * - * @param symbol the symbol to be drawn with MusicFont chars - * @param dimension the dim to fit as much as possible - * @return the adjusted TextLayout ready to be drawn - */ - public TextLayout layout (BasicSymbol symbol, - Dimension dimension) - { - String str = symbol.getString(); - TextLayout layout = new TextLayout(str, this, frc); - - // Compute proper affine transformation - Rectangle2D rect = layout.getBounds(); - AffineTransform fat = AffineTransform.getScaleInstance( - dimension.width / rect.getWidth(), - dimension.height / rect.getHeight()); - - return layout(str, fat); - } - - //--------// - // layout // - //--------// - /** - * Build a TextLayout from a Shape, using its related String of - * MusicFont character codes. - * - * @param shape the shape to be drawn with MusicFont chars - * @return the TextLayout ready to be drawn - */ - public TextLayout layout (Shape shape) - { - return layout(shape, (AffineTransform) null); - } - - //--------// - // layout // - //--------// - /** - * Build a TextLayout from a String of MusicFont character codes. - * - * @param codes the MusicFont codes - * @return the TextLayout ready to be drawn - */ - public TextLayout layout (int... codes) - { - return layout(new String(codes, 0, codes.length)); - } - - //-------------------// - // getStaffInterline // - //-------------------// - /** - * Report the number of pixels of the interline that corresponds to the staves - * where this font is used. - * - * @return the scaled interline of the related staff size. - */ - protected int getStaffInterline () - { - return staffInterline; - } - - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Ratio headRatio = new Constant.Ratio( 1.1, @@ -496,21 +532,18 @@ private static final class Constants //---------// private static class Scaling { - //~ Instance fields ------------------------------------------------------------------------ public int pointSize; public int staffInterline; - //~ Constructors --------------------------------------------------------------------------- - public Scaling (int pointSize, - int staffInterline) + Scaling (int pointSize, + int staffInterline) { this.pointSize = pointSize; this.staffInterline = staffInterline; } - //~ Methods -------------------------------------------------------------------------------- @Override public boolean equals (Object obj) { @@ -521,7 +554,7 @@ public boolean equals (Object obj) Scaling that = (Scaling) obj; return (this.pointSize == that.pointSize) - && (this.staffInterline == that.staffInterline); + && (this.staffInterline == that.staffInterline); } @Override diff --git a/src/main/org/audiveris/omr/ui/symbol/NonDraggableSymbol.java b/src/main/org/audiveris/omr/ui/symbol/NonDraggableSymbol.java index 2abaec956..b98448d8f 100644 --- a/src/main/org/audiveris/omr/ui/symbol/NonDraggableSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/NonDraggableSymbol.java @@ -38,11 +38,9 @@ public class NonDraggableSymbol extends ShapeSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- private static final AffineTransform at = AffineTransform.getScaleInstance(2, 2); - //~ Constructors ------------------------------------------------------------------------------- /** * Create an NonDraggableSymbol * @@ -57,7 +55,7 @@ public NonDraggableSymbol (int... codes) * Create an NonDraggableSymbol * * @param isIcon true for an icon - * @param codes the codes for MusicFont characters + * @param codes the codes for MusicFont characters */ protected NonDraggableSymbol (boolean isIcon, int... codes) @@ -65,7 +63,6 @@ protected NonDraggableSymbol (boolean isIcon, super(isIcon, Shape.NON_DRAGGABLE, true, codes); // Decorated } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/NumDenSymbol.java b/src/main/org/audiveris/omr/ui/symbol/NumDenSymbol.java index 28d6c78a9..f29d02943 100644 --- a/src/main/org/audiveris/omr/ui/symbol/NumDenSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/NumDenSymbol.java @@ -32,17 +32,17 @@ /** * Class {@code NumDenSymbol} displays a time sig, with numerator and denominator. + * + * @author Hervé Bitteur */ public class NumDenSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- private final int[] numCodes; private final int[] denCodes; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new NumDenSymbol object. * @@ -97,7 +97,6 @@ public NumDenSymbol (boolean isIcon, this.denCodes = denCodes; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -143,14 +142,12 @@ protected void paint (Graphics2D g, OmrFont.paint(g, p.denLayout, bot, AREA_CENTER); } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // MyParams // //----------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ final int dy; @@ -158,8 +155,7 @@ protected class MyParams final TextLayout denLayout; - //~ Constructors --------------------------------------------------------------------------- - public MyParams (MusicFont font) + MyParams (MusicFont font) { dy = (int) Math.rint(2 * font.getStaffInterline()); numLayout = font.layout(numCodes); diff --git a/src/main/org/audiveris/omr/ui/symbol/OmrFont.java b/src/main/org/audiveris/omr/ui/symbol/OmrFont.java index 2b6dd8370..bf8584824 100644 --- a/src/main/org/audiveris/omr/ui/symbol/OmrFont.java +++ b/src/main/org/audiveris/omr/ui/symbol/OmrFont.java @@ -29,6 +29,7 @@ import java.awt.Color; import java.awt.Font; +import java.awt.FontFormatException; import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; import java.awt.Point; @@ -37,6 +38,7 @@ import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ConcurrentModificationException; @@ -52,20 +54,18 @@ public abstract class OmrFont extends Font { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(OmrFont.class); - /** Needed for font size computation. */ - protected static final FontRenderContext frc = new FontRenderContext(null, true, true); - /** Default color for images. */ public static final Color defaultImageColor = Color.BLACK; + /** Needed for font size computation. */ + protected static final FontRenderContext frc = new FontRenderContext(null, true, true); + /** Cache for fonts. No style, no size. */ - private static final Map fontCache = new HashMap(); + private static final Map fontCache = new HashMap<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new OmrFont object. * @@ -80,7 +80,6 @@ protected OmrFont (String name, super(createFont(name, style, pointSize)); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // layout // //--------// @@ -200,7 +199,8 @@ private static Font createFont (String fontName, input.close(); } } - } catch (Exception ex) { + } catch (FontFormatException | + IOException ex) { logger.debug("Could not create custom font {} " + ex, fontName); } diff --git a/src/main/org/audiveris/omr/ui/symbol/OttavaClefSymbol.java b/src/main/org/audiveris/omr/ui/symbol/OttavaClefSymbol.java index 98fb83dd9..d44d501fc 100644 --- a/src/main/org/audiveris/omr/ui/symbol/OttavaClefSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/OttavaClefSymbol.java @@ -33,16 +33,16 @@ /** * Class {@code OttavaClefSymbol} displays a clef (bass or treble) with the addition of * an ottava (alta or bassa). + * + * @author Hervé Bitteur */ public class OttavaClefSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- // True for alta, false for bassa private final boolean isAlta; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new OttavaClefSymbol object. * @@ -60,7 +60,6 @@ public OttavaClefSymbol (boolean isAlta, this.isAlta = isAlta; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -102,14 +101,12 @@ protected void paint (Graphics2D g, } } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // MyParams // //----------// private class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ final TextLayout ottavaLayout; @@ -117,8 +114,7 @@ private class MyParams final Rectangle2D clefRect; - //~ Constructors --------------------------------------------------------------------------- - public MyParams (MusicFont font) + MyParams (MusicFont font) { ottavaLayout = Symbols.SYMBOL_OTTAVA.layout(font); ottavaRect = ottavaLayout.getBounds(); diff --git a/src/main/org/audiveris/omr/ui/symbol/RepeatDotSymbol.java b/src/main/org/audiveris/omr/ui/symbol/RepeatDotSymbol.java index 3ec43dde5..ab07c5ca3 100644 --- a/src/main/org/audiveris/omr/ui/symbol/RepeatDotSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/RepeatDotSymbol.java @@ -1,146 +1,143 @@ -//------------------------------------------------------------------------------------------------// -// // -// R e p e a t D o t S y m b o l // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.ui.symbol; - -import org.audiveris.omr.glyph.Shape; -import static org.audiveris.omr.ui.symbol.Alignment.*; -import static org.audiveris.omr.ui.symbol.ShapeSymbol.decoComposite; - -import java.awt.Composite; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.Rectangle2D; - -/** - * Class {@code RepeatDotSymbol} implements a decorated repeat dot symbol. - * - * @author Hervé Bitteur - */ -public class RepeatDotSymbol - extends ShapeSymbol -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - /** Global decorated height WRT dot height. */ - private static final double yRatio = 3.5; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Create a {@code RepeatDotSymbol} (with decoration?) standard size - * - * @param shape the precise shape - * @param decorated true for a decorated image - * @param codes precise code for articulation part - */ - public RepeatDotSymbol (Shape shape, - boolean decorated, - int... codes) - { - this(false, shape, decorated, codes); - } - - /** - * Create a {@code RepeatDotSymbol} (with decoration?) - * - * @param isIcon true for an icon - * @param shape the precise shape - * @param decorated true for a decorated image - * @param codes precise code for articulation part - */ - protected RepeatDotSymbol (boolean isIcon, - Shape shape, - boolean decorated, - int... codes) - { - super(isIcon, shape, decorated, codes); - } - - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // createIcon // - //------------// - @Override - protected ShapeSymbol createIcon () - { - return new RepeatDotSymbol(true, shape, decorated, codes); - } - - //-----------// - // getParams // - //-----------// - @Override - protected Params getParams (MusicFont font) - { - // offset: if decorated, offset of symbol center vs decorated image center - // layout: dot layout - // rect: global image (= other dot + dot if decorated, dot if not) - Params p = new Params(); - - // Dot layout - p.layout = font.layout(getString()); - - Rectangle2D rs = p.layout.getBounds(); // Symbol bounds - - if (decorated) { - // Use a rectangle yTimes times as high as dot - p.rect = new Rectangle( - (int) Math.ceil(rs.getWidth()), - (int) Math.ceil(yRatio * rs.getHeight())); - - // Define specific offset - p.offset = new Point(0, (int) Math.rint(rs.getHeight() * ((yRatio - 1) / 2))); - } else { - p.rect = rs.getBounds(); - } - - return p; - } - - //-------// - // paint // - //-------// - @Override - protected void paint (Graphics2D g, - Params p, - Point location, - Alignment alignment) - { - if (decorated) { - // Draw a dot (using composite) on the top - Point loc = alignment.translatedPoint(TOP_CENTER, p.rect, location); - Composite oldComposite = g.getComposite(); - g.setComposite(decoComposite); - MusicFont.paint(g, p.layout, loc, TOP_CENTER); - g.setComposite(oldComposite); - - // Dot below - loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); - loc.y += p.offset.y; - MusicFont.paint(g, p.layout, loc, AREA_CENTER); - } else { - // Dot alone - Point loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); - MusicFont.paint(g, p.layout, loc, AREA_CENTER); - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// R e p e a t D o t S y m b o l // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.ui.symbol; + +import org.audiveris.omr.glyph.Shape; +import static org.audiveris.omr.ui.symbol.Alignment.*; +import static org.audiveris.omr.ui.symbol.ShapeSymbol.decoComposite; + +import java.awt.Composite; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.geom.Rectangle2D; + +/** + * Class {@code RepeatDotSymbol} implements a decorated repeat dot symbol. + * + * @author Hervé Bitteur + */ +public class RepeatDotSymbol + extends ShapeSymbol +{ + + /** Global decorated height WRT dot height. */ + private static final double yRatio = 3.5; + + /** + * Create a {@code RepeatDotSymbol} (with decoration?) standard size + * + * @param shape the precise shape + * @param decorated true for a decorated image + * @param codes precise code for articulation part + */ + public RepeatDotSymbol (Shape shape, + boolean decorated, + int... codes) + { + this(false, shape, decorated, codes); + } + + /** + * Create a {@code RepeatDotSymbol} (with decoration?) + * + * @param isIcon true for an icon + * @param shape the precise shape + * @param decorated true for a decorated image + * @param codes precise code for articulation part + */ + protected RepeatDotSymbol (boolean isIcon, + Shape shape, + boolean decorated, + int... codes) + { + super(isIcon, shape, decorated, codes); + } + + //------------// + // createIcon // + //------------// + @Override + protected ShapeSymbol createIcon () + { + return new RepeatDotSymbol(true, shape, decorated, codes); + } + + //-----------// + // getParams // + //-----------// + @Override + protected Params getParams (MusicFont font) + { + // offset: if decorated, offset of symbol center vs decorated image center + // layout: dot layout + // rect: global image (= other dot + dot if decorated, dot if not) + Params p = new Params(); + + // Dot layout + p.layout = font.layout(getString()); + + Rectangle2D rs = p.layout.getBounds(); // Symbol bounds + + if (decorated) { + // Use a rectangle yTimes times as high as dot + p.rect = new Rectangle( + (int) Math.ceil(rs.getWidth()), + (int) Math.ceil(yRatio * rs.getHeight())); + + // Define specific offset + p.offset = new Point(0, (int) Math.rint(rs.getHeight() * ((yRatio - 1) / 2))); + } else { + p.rect = rs.getBounds(); + } + + return p; + } + + //-------// + // paint // + //-------// + @Override + protected void paint (Graphics2D g, + Params p, + Point location, + Alignment alignment) + { + if (decorated) { + // Draw a dot (using composite) on the top + Point loc = alignment.translatedPoint(TOP_CENTER, p.rect, location); + Composite oldComposite = g.getComposite(); + g.setComposite(decoComposite); + MusicFont.paint(g, p.layout, loc, TOP_CENTER); + g.setComposite(oldComposite); + + // Dot below + loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); + loc.y += p.offset.y; + MusicFont.paint(g, p.layout, loc, AREA_CENTER); + } else { + // Dot alone + Point loc = alignment.translatedPoint(AREA_CENTER, p.rect, location); + MusicFont.paint(g, p.layout, loc, AREA_CENTER); + } + } +} diff --git a/src/main/org/audiveris/omr/ui/symbol/ResizedSymbol.java b/src/main/org/audiveris/omr/ui/symbol/ResizedSymbol.java index 472fdfcbc..576a7cd37 100644 --- a/src/main/org/audiveris/omr/ui/symbol/ResizedSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/ResizedSymbol.java @@ -35,12 +35,10 @@ public class ResizedSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- /** The display ratio */ protected final double ratio; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a non decorated standard ResizedSymbol with the provided * shape and codes. @@ -75,7 +73,6 @@ public ResizedSymbol (boolean isIcon, this.ratio = ratio; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -85,13 +82,13 @@ protected ResizedSymbol createIcon () return new ResizedSymbol(true, shape, ratio, decorated, codes); } - //-----------------// - // internalsString // - //-----------------// + //-----------// + // internals // + //-----------// @Override - protected String internalsString () + protected String internals () { - StringBuilder sb = new StringBuilder(super.internalsString()); + StringBuilder sb = new StringBuilder(super.internals()); sb.append(" ratio:").append((float) ratio); diff --git a/src/main/org/audiveris/omr/ui/symbol/RestSymbol.java b/src/main/org/audiveris/omr/ui/symbol/RestSymbol.java index 8fe936498..6aa327bd5 100644 --- a/src/main/org/audiveris/omr/ui/symbol/RestSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/RestSymbol.java @@ -39,12 +39,10 @@ public class RestSymbol extends ShapeSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- - // The lines + /** Staff lines symbol. */ protected static final BasicSymbol linesSymbol = Symbols.SYMBOL_STAFF_LINES; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a RestSymbol (with decoration?) standard size. * @@ -75,7 +73,6 @@ protected RestSymbol (boolean isIcon, super(isIcon, shape, decorated, codes); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -150,14 +147,12 @@ protected void paint (Graphics2D g, } } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ // offset: if decorated, offset of symbol center vs decorated image center // layout: rest layout diff --git a/src/main/org/audiveris/omr/ui/symbol/ShapeSymbol.java b/src/main/org/audiveris/omr/ui/symbol/ShapeSymbol.java index 4f8a78ef0..442f60397 100644 --- a/src/main/org/audiveris/omr/ui/symbol/ShapeSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/ShapeSymbol.java @@ -33,11 +33,10 @@ /** * Class {@code ShapeSymbol} extends the {@link BasicSymbol} with the handling of a * related {@link Shape}. - * A ShapeSymbol thus adds several features:
                                                                                                                                                    - * + * A ShapeSymbol thus adds several features: + *
                                                                                                                                                      *
                                                                                                                                                    • It can be used for Drag & Drop operations, since it implements the {@link Transferable} * interface.
                                                                                                                                                    • - * *
                                                                                                                                                    • It can be used to train the glyph classifier when we don't have enough "real" glyphs * available.
                                                                                                                                                    • *
                                                                                                                                                    @@ -48,24 +47,21 @@ public class ShapeSymbol extends BasicSymbol implements Transferable { - //~ Static fields/initializers ----------------------------------------------------------------- /** The symbol meta data */ - public static DataFlavor DATA_FLAVOR = new DataFlavor(SymbolIcon.class, "shape-symbol"); + public static final DataFlavor DATA_FLAVOR = new DataFlavor(SymbolIcon.class, "shape-symbol"); /** Composite used for decoration */ - protected static AlphaComposite decoComposite = AlphaComposite.getInstance( + protected static final AlphaComposite decoComposite = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.15f); - //~ Instance fields ---------------------------------------------------------------------------- /** Related shape. */ protected final Shape shape; /** Is this a decorated symbol. (shape with additional stuff) */ protected final boolean decorated; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a ShapeSymbol with the provided shape and codes * @@ -97,26 +93,6 @@ public ShapeSymbol (Shape shape, this(false, shape, false, codes); } - //~ Methods ------------------------------------------------------------------------------------ - //-------------// - // numberCodes // - //-------------// - public static int[] numberCodes (int number) - { - ShapeSymbol symbol = Symbols.getSymbol(TIME_ZERO); - int base = symbol.codes[0]; - int[] numberCodes = (number > 9) ? new int[2] : new int[1]; - int index = 0; - - if (number > 9) { - numberCodes[index++] = base + (number / 10); - } - - numberCodes[index] = base + (number % 10); - - return numberCodes; - } - //----------// // getShape // //----------// @@ -135,7 +111,8 @@ public Shape getShape () //-----------------// @Override public Object getTransferData (DataFlavor flavor) - throws UnsupportedFlavorException, IOException + throws UnsupportedFlavorException, + IOException { if (isDataFlavorSupported(flavor)) { return this; @@ -185,16 +162,41 @@ protected ShapeSymbol createIcon () return new ShapeSymbol(true, shape, decorated, codes); } - //-----------------// - // internalsString // - //-----------------// + //-----------// + // internals // + //-----------// @Override - protected String internalsString () + protected String internals () { - StringBuilder sb = new StringBuilder(super.internalsString()); + StringBuilder sb = new StringBuilder(super.internals()); sb.append(" ").append(shape); return sb.toString(); } + + //-------------// + // numberCodes // + //-------------// + /** + * Report the codes for a time number symbol. + * + * @param number integer value of the symbol + * @return corresponding codes in music font + */ + public static int[] numberCodes (int number) + { + ShapeSymbol symbol = Symbols.getSymbol(TIME_ZERO); + int base = symbol.codes[0]; + int[] numberCodes = (number > 9) ? new int[2] : new int[1]; + int index = 0; + + if (number > 9) { + numberCodes[index++] = base + (number / 10); + } + + numberCodes[index] = base + (number % 10); + + return numberCodes; + } } diff --git a/src/main/org/audiveris/omr/ui/symbol/SlantedSymbol.java b/src/main/org/audiveris/omr/ui/symbol/SlantedSymbol.java index 75526a4f3..367a5c448 100644 --- a/src/main/org/audiveris/omr/ui/symbol/SlantedSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/SlantedSymbol.java @@ -34,11 +34,12 @@ * Class {@code SlantedSymbol} draws symbols made of several slanted parts. * (such as in DYNAMICS_FP, where both the F and the P are slanted and appear too far from each * other) + * + * @author Hervé Bitteur */ public class SlantedSymbol extends ShapeSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SlantedSymbol object, standard size @@ -66,7 +67,6 @@ protected SlantedSymbol (boolean isIcon, super(isIcon, shape, false, codes); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -98,18 +98,14 @@ protected MyParams getParams (MusicFont font) Rectangle2D r = layout.getBounds(); // Abscissa reduction because of slanted characters - // It's value depends on whether we have a 'f' or not + // Its value depends on whether we have a 'f' or not float dx; int c = code - MusicFont.CODE_OFFSET; - if ((c == 102) - || // F - (c == 196) - || // FF - (c == 236) - || // FFF - (c == 83) - || // SF + if ((c == 102) || // F + (c == 196) || // FF + (c == 236) || // FFF + (c == 83) || // SF (c == 90)) { // FZ dx = (float) r.getHeight() * 0.215f; // Measured } else { @@ -170,37 +166,37 @@ protected void paint (Graphics2D g, } } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ // layout not used // rect for global image - // Sequence of layouts + // + // Sequence of layouts: SmartLayout[] layouts; } //-------------// // SmartLayout // //-------------// + /** + * A trick to remove useless dx margin before and after the symbol is drawn. + */ protected static class SmartLayout { - //~ Instance fields ------------------------------------------------------------------------ // The standard character glyph final TextLayout layout; - // Translation before & after + // Translation before and after final float dx; - //~ Constructors --------------------------------------------------------------------------- - public SmartLayout (TextLayout layout, - float dx) + SmartLayout (TextLayout layout, + float dx) { this.layout = layout; this.dx = dx; diff --git a/src/main/org/audiveris/omr/ui/symbol/SlashedFlagSymbol.java b/src/main/org/audiveris/omr/ui/symbol/SlashedFlagSymbol.java index b82110ae8..7ea525213 100644 --- a/src/main/org/audiveris/omr/ui/symbol/SlashedFlagSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/SlashedFlagSymbol.java @@ -39,12 +39,10 @@ public class SlashedFlagSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- /** The small flag symbol. */ private final ShapeSymbol flagSymbol = Symbols.getSymbol(Shape.SMALL_FLAG); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code SmallFlagSymbol} object. * @@ -55,7 +53,6 @@ public SlashedFlagSymbol (boolean isIcon) super(isIcon, Shape.SMALL_FLAG_SLASH, false); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -105,14 +102,12 @@ protected void paint (Graphics2D g, g.setStroke(oldStroke); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// private class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ Stroke stroke; } diff --git a/src/main/org/audiveris/omr/ui/symbol/SlurSymbol.java b/src/main/org/audiveris/omr/ui/symbol/SlurSymbol.java index da83b6074..1446f71b6 100644 --- a/src/main/org/audiveris/omr/ui/symbol/SlurSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/SlurSymbol.java @@ -37,7 +37,6 @@ public class SlurSymbol extends ShapeSymbol { - //~ Constructors ------------------------------------------------------------------------------- /** * Create a SlurSymbol @@ -57,7 +56,6 @@ protected SlurSymbol (boolean isIcon) super(isIcon, Shape.SLUR, true); // Decorated } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/StemSymbol.java b/src/main/org/audiveris/omr/ui/symbol/StemSymbol.java index 772a04f9e..d995bd49f 100644 --- a/src/main/org/audiveris/omr/ui/symbol/StemSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/StemSymbol.java @@ -38,7 +38,6 @@ public class StemSymbol extends ShapeSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- // The head+stem part private static final BasicSymbol quarter = Symbols.SYMBOL_QUARTER; @@ -46,7 +45,6 @@ public class StemSymbol // The stem part private static final BasicSymbol stem = Symbols.SYMBOL_STEM; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a {@code StemSymbol} (with decoration?) standard size * @@ -69,7 +67,6 @@ protected StemSymbol (boolean isIcon, super(isIcon, Shape.STEM, decorated); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -136,14 +133,12 @@ protected void paint (Graphics2D g, } } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ // offset: if decorated, offset of symbol center vs decorated image center // layout: stem layout diff --git a/src/main/org/audiveris/omr/ui/symbol/SymbolIcon.java b/src/main/org/audiveris/omr/ui/symbol/SymbolIcon.java index 440a36c3a..33b705258 100644 --- a/src/main/org/audiveris/omr/ui/symbol/SymbolIcon.java +++ b/src/main/org/audiveris/omr/ui/symbol/SymbolIcon.java @@ -31,18 +31,15 @@ /** * Interface {@code SymbolIcon} handles the display of a symbol. - * A SymbolIcon can provide several features:
                                                                                                                                                      - * + * A SymbolIcon can provide several features: + *
                                                                                                                                                        *
                                                                                                                                                      • It can be used as an icon for buttons, menus, etc. For that purpose, * {@code SymbolIcon} * implements the {@link Icon} interface.
                                                                                                                                                      • - * *
                                                                                                                                                      • It can be used as an image for precise drawing on score views, whatever the desired * scale and display ratio. See {@link #buildImage} and {@link #paintSymbol} methods.
                                                                                                                                                      • - * *
                                                                                                                                                      • It may also be used to train the glyph classifier when we don't have enough "real" * glyphs available.
                                                                                                                                                      • - * *
                                                                                                                                                      • It may also be used to convey the reference point of that shape. * Most shapes have no reference point, and thus we use their area center, which is the center of * their bounding box. @@ -56,7 +53,6 @@ public interface SymbolIcon extends Icon { - //~ Methods ------------------------------------------------------------------------------------ /** * Paint the symbol that represents the related shape, using the scaled font and diff --git a/src/main/org/audiveris/omr/ui/symbol/SymbolImage.java b/src/main/org/audiveris/omr/ui/symbol/SymbolImage.java index ef31ecd24..e59de0827 100644 --- a/src/main/org/audiveris/omr/ui/symbol/SymbolImage.java +++ b/src/main/org/audiveris/omr/ui/symbol/SymbolImage.java @@ -33,12 +33,10 @@ public class SymbolImage extends BufferedImage { - //~ Instance fields ---------------------------------------------------------------------------- /** The reference point for this image. */ private final Point refPoint; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SymbolImage object. * @@ -55,7 +53,6 @@ public SymbolImage (int width, this.refPoint = refPoint; } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // getRefPoint // //-------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/SymbolRipper.java b/src/main/org/audiveris/omr/ui/symbol/SymbolRipper.java index 7db3dc1fb..0b06ed032 100644 --- a/src/main/org/audiveris/omr/ui/symbol/SymbolRipper.java +++ b/src/main/org/audiveris/omr/ui/symbol/SymbolRipper.java @@ -72,7 +72,6 @@ */ public class SymbolRipper { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(SymbolRipper.class); @@ -81,7 +80,6 @@ public class SymbolRipper UILookAndFeel.setUI(null); } - //~ Instance fields ---------------------------------------------------------------------------- /** Related frame */ private final JFrame frame; @@ -91,7 +89,7 @@ public class SymbolRipper //---------------// // paramListener // //---------------// - private ChangeListener paramListener = new ChangeListener() + private final ChangeListener paramListener = new ChangeListener() { @Override public void stateChanged (ChangeEvent e) @@ -127,7 +125,7 @@ public void stateChanged (ChangeEvent e) }; // Panel where the icon is drawn - private JPanel drawing; + private final JPanel drawing; // String used to draw the symbol private String string; @@ -165,18 +163,17 @@ public void stateChanged (ChangeEvent e) // x symbol private final String f = "%.3f"; - private LDoubleField xSym = new LDoubleField(false, "xSym", "x symbol", f); + private final LDoubleField xSym = new LDoubleField(false, "xSym", "x symbol", f); // w symbol - private LDoubleField wSym = new LDoubleField(false, "wSym", "w symbol", f); + private final LDoubleField wSym = new LDoubleField(false, "wSym", "w symbol", f); // y symbol - private LDoubleField ySym = new LDoubleField(false, "ySym", "y symbol", f); + private final LDoubleField ySym = new LDoubleField(false, "ySym", "y symbol", f); // y symbol - private LDoubleField hSym = new LDoubleField(false, "hSym", "h symbol", f); + private final LDoubleField hSym = new LDoubleField(false, "hSym", "h symbol", f); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SymbolRipper object. */ @@ -189,20 +186,21 @@ public SymbolRipper () // Actors drawing = new Drawing(); - fontBase.setModel(new SpinnerListModel(new Integer[]{0, 0xf000, 0x1d100})); + fontBase.setModel(new SpinnerListModel(new Integer[]{0, 0xf000, 0x1_d100})); SpinnerUtil.setRightAlignment(fontBase); SpinnerUtil.fixIntegerList(fontBase); fontName.setModel( new SpinnerListModel( - GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames())); + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getAvailableFontFamilyNames())); // Initial values ///fontName.getSpinner().setValue("MusicalSymbols"); fontName.getSpinner().setValue("Symbola"); fontBase.setValue(0); //0); fontSize.setValue(200); - pointCode.setModel(new SpinnerNumberModel(0x1d100, 0, 0x1d1ff, 1)); + pointCode.setModel(new SpinnerNumberModel(0x1_d100, 0, 0x1_d1ff, 1)); width.setValue(400); height.setValue(500); xOffset.setValue(200); @@ -234,7 +232,6 @@ public SymbolRipper () frame.repaint(); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // getFrame // //----------// @@ -248,19 +245,6 @@ public JFrame getFrame () return frame; } - //------// - // main // - //------// - /** - * Command line entry point, no arguments are used today. - * - * @param args unused - */ - public static void main (String... args) - { - new SymbolRipper(); - } - //------------// // buildImage // //------------// @@ -418,14 +402,25 @@ private void resizeDrawing () drawing.revalidate(); } - //~ Inner Classes ------------------------------------------------------------------------------ + //------// + // main // + //------// + /** + * Command line entry point, no arguments are used today. + * + * @param args unused + */ + public static void main (String... args) + { + new SymbolRipper(); + } + //---------// // Drawing // //---------// private class Drawing extends Panel { - //~ Methods -------------------------------------------------------------------------------- @Override public void paintComponent (Graphics g) diff --git a/src/main/org/audiveris/omr/ui/symbol/Symbols.java b/src/main/org/audiveris/omr/ui/symbol/Symbols.java index 176db2ff6..78e434169 100644 --- a/src/main/org/audiveris/omr/ui/symbol/Symbols.java +++ b/src/main/org/audiveris/omr/ui/symbol/Symbols.java @@ -37,18 +37,8 @@ */ public abstract class Symbols { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - Symbols.class); - - /** Map of (simple) symbols */ - private static final EnumMap sym = new EnumMap( - Shape.class); - - /** Map of decorated symbols */ - private static final EnumMap dec = new EnumMap( - Shape.class); + private static final Logger logger = LoggerFactory.getLogger(Symbols.class); /** Symbol of '8' char for ottava sign (alta or bassa) on F & G clefs */ public static final BasicSymbol SYMBOL_OTTAVA = new BasicSymbol(false, 165); @@ -92,18 +82,22 @@ public abstract class Symbols /** Symbol for FLAG_2_UP */ public static final ShapeSymbol SYMBOL_FLAG_2_UP = new ShapeSymbol(FLAG_2_UP, 75); + /** Map of (simple) symbols */ + private static final EnumMap sym = new EnumMap<>(Shape.class); + + /** Map of decorated symbols */ + private static final EnumMap dec = new EnumMap<>(Shape.class); + static { assignSymbols(); assignDecoratedSymbols(); } - //~ Constructors ------------------------------------------------------------------------------- /** This is just a functional class, no instance is needed. */ private Symbols () { } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // getSymbol // //-----------// diff --git a/src/main/org/audiveris/omr/ui/symbol/TemplateSymbol.java b/src/main/org/audiveris/omr/ui/symbol/TemplateSymbol.java index 6c94af2e0..be46266c8 100644 --- a/src/main/org/audiveris/omr/ui/symbol/TemplateSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/TemplateSymbol.java @@ -43,19 +43,18 @@ public class TemplateSymbol extends BasicSymbol { - //~ Static fields/initializers ----------------------------------------------------------------- /** Affine Transform for small symbol shapes. */ private static final AffineTransform smallAt = AffineTransform.getScaleInstance( Template.smallRatio, Template.smallRatio); - //~ Instance fields ---------------------------------------------------------------------------- + /** Template shape. */ protected final Shape shape; + /** Indicate a smaller symbol. */ protected final boolean isSmall; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TemplateSymbol object. * @@ -70,10 +69,15 @@ public TemplateSymbol (Shape shape, isSmall = shape.isSmall(); } - //~ Methods ------------------------------------------------------------------------------------ //-----------------// // getSymbolBounds // //-----------------// + /** + * Report the strict bounds of the symbol (within a perhaps larger template). + * + * @param font provided font + * @return relative symbol bounds within template + */ public Rectangle getSymbolBounds (MusicFont font) { return getParams(font).symbolRect; @@ -143,14 +147,12 @@ protected void paint (Graphics2D g, MusicFont.paint(g, p.layout, loc, AREA_CENTER); } - //~ Inner Classes ------------------------------------------------------------------------------ //----------// // MyParams // //----------// protected class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ Rectangle symbolRect; // Bounds for symbol inside template image } diff --git a/src/main/org/audiveris/omr/ui/symbol/TextFont.java b/src/main/org/audiveris/omr/ui/symbol/TextFont.java index d03eb3f70..795165827 100644 --- a/src/main/org/audiveris/omr/ui/symbol/TextFont.java +++ b/src/main/org/audiveris/omr/ui/symbol/TextFont.java @@ -42,23 +42,21 @@ public class TextFont extends OmrFont { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(TextFont.class); - /** Name of the chosen underlying text font */ - private static final String fontName = constants.defaultTextFontName.getValue(); + /** Ratio from a 300 DPI scan to font point-size (72 pt/inch) */ + public static final float TO_POINT = 72f / 300f; /** The base font used for text entities */ public static final TextFont baseTextFont = new TextFont( constants.defaultTextFontSize.getValue()); - /** Ratio from a 300 DPI scan to font point-size (72 pt/inch) */ - public static final float TO_POINT = 72f / 300f; + /** Name of the chosen underlying text font */ + private static final String fontName = constants.defaultTextFontName.getValue(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TextFont object. * @@ -98,8 +96,6 @@ public TextFont (int size) super(fontName, Font.PLAIN, size); } - //~ Methods ------------------------------------------------------------------------------------ - // //-----------------// // computeFontSize // //-----------------// @@ -131,14 +127,12 @@ public static Float computeFontSize (String content, } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String defaultTextFontName = new Constant.String( "Serif", diff --git a/src/main/org/audiveris/omr/ui/symbol/TextSymbol.java b/src/main/org/audiveris/omr/ui/symbol/TextSymbol.java index 6fd991ee7..4ecb7036c 100644 --- a/src/main/org/audiveris/omr/ui/symbol/TextSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/TextSymbol.java @@ -34,12 +34,10 @@ public class TextSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- /** The text string to use */ private final String str; - //~ Constructors ------------------------------------------------------------------------------- /** * Create an TextSymbol * @@ -67,7 +65,6 @@ protected TextSymbol (boolean isIcon, this.str = str; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/TransformedSymbol.java b/src/main/org/audiveris/omr/ui/symbol/TransformedSymbol.java index 843f67356..37ac524d0 100644 --- a/src/main/org/audiveris/omr/ui/symbol/TransformedSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/TransformedSymbol.java @@ -32,11 +32,12 @@ /** * Class {@code TransformedSymbol} displays a baseShape symbol with AffineTransform. + * + * @author Hervé Bitteur */ public class TransformedSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- /** The baseShape shape */ protected final Shape baseShape; @@ -47,7 +48,6 @@ public class TransformedSymbol /** Proper transformation */ private final AffineTransform at; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TransformedSymbol object. * @@ -67,7 +67,6 @@ public TransformedSymbol (boolean isIcon, this.at = at; } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// diff --git a/src/main/org/audiveris/omr/ui/symbol/TurnSlashSymbol.java b/src/main/org/audiveris/omr/ui/symbol/TurnSlashSymbol.java index b3c5c93cc..cca92fbea 100644 --- a/src/main/org/audiveris/omr/ui/symbol/TurnSlashSymbol.java +++ b/src/main/org/audiveris/omr/ui/symbol/TurnSlashSymbol.java @@ -33,16 +33,16 @@ /** * Class {@code TurnSlashSymbol} displays a TURN symbol with a vertical slash. + * + * @author Hervé Bitteur */ public class TurnSlashSymbol extends ShapeSymbol { - //~ Instance fields ---------------------------------------------------------------------------- /** The turn symbol */ private final ShapeSymbol turnSymbol = Symbols.getSymbol(Shape.TURN); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TurnSlashSymbol object. * @@ -53,7 +53,6 @@ public TurnSlashSymbol (boolean isIcon) super(isIcon, Shape.TURN_SLASH, false); } - //~ Methods ------------------------------------------------------------------------------------ //------------// // createIcon // //------------// @@ -97,19 +96,18 @@ protected void paint (Graphics2D g, Stroke oldStroke = g.getStroke(); g.setStroke(p.stroke); + Point top = alignment.translatedPoint(TOP_CENTER, p.rect, location); g.drawLine(loc.x, top.y, loc.x, top.y + p.rect.height); g.setStroke(oldStroke); } - //~ Inner Classes ------------------------------------------------------------------------------ //--------// // Params // //--------// private class MyParams extends Params { - //~ Instance fields ------------------------------------------------------------------------ Stroke stroke; } diff --git a/src/main/org/audiveris/omr/ui/treetable/AbstractCellEditor.java b/src/main/org/audiveris/omr/ui/treetable/AbstractCellEditor.java index b6492a412..26728b1b8 100644 --- a/src/main/org/audiveris/omr/ui/treetable/AbstractCellEditor.java +++ b/src/main/org/audiveris/omr/ui/treetable/AbstractCellEditor.java @@ -23,14 +23,12 @@ public class AbstractCellEditor implements CellEditor { - //~ Instance fields ---------------------------------------------------------------------------- /** * DOCUMENT ME! */ protected EventListenerList listenerList = new EventListenerList(); - //~ Methods ------------------------------------------------------------------------------------ //-----------------------// // addCellEditorListener // //-----------------------// @@ -77,7 +75,6 @@ public Object getCellEditorValue () * DOCUMENT ME! * * @param e DOCUMENT ME! - * * @return DOCUMENT ME! */ @Override @@ -107,7 +104,6 @@ public void removeCellEditorListener (CellEditorListener l) * DOCUMENT ME! * * @param anEvent DOCUMENT ME! - * * @return DOCUMENT ME! */ @Override @@ -130,9 +126,10 @@ public boolean stopCellEditing () return true; } - /* + /** * Notify all listeners that have registered interest for notification on * this event type. + * * @see EventListenerList */ protected void fireEditingCanceled () @@ -149,9 +146,10 @@ protected void fireEditingCanceled () } } - /* + /** * Notify all listeners that have registered interest for * notification on this event type. + * * @see EventListenerList */ protected void fireEditingStopped () diff --git a/src/main/org/audiveris/omr/ui/treetable/AbstractTreeTableModel.java b/src/main/org/audiveris/omr/ui/treetable/AbstractTreeTableModel.java index 000d789eb..acfa5d641 100644 --- a/src/main/org/audiveris/omr/ui/treetable/AbstractTreeTableModel.java +++ b/src/main/org/audiveris/omr/ui/treetable/AbstractTreeTableModel.java @@ -5,7 +5,6 @@ //----------------------------------------------------------------------------// package org.audiveris.omr.ui.treetable; - /* * @(#)AbstractTreeTableModel.java 1.2 98/10/27 * @@ -31,7 +30,6 @@ public abstract class AbstractTreeTableModel implements TreeTableModel { - //~ Instance fields ---------------------------------------------------------------------------- /** * DOCUMENT ME! @@ -43,7 +41,6 @@ public abstract class AbstractTreeTableModel */ protected Object root; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new AbstractTreeTableModel object. * @@ -54,7 +51,6 @@ public AbstractTreeTableModel (Object root) this.root = root; } - //~ Methods ------------------------------------------------------------------------------------ //----------------------// // addTreeModelListener // //----------------------// @@ -76,7 +72,6 @@ public void addTreeModelListener (TreeModelListener l) * DOCUMENT ME! * * @param column DOCUMENT ME! - * * @return DOCUMENT ME! */ @Override @@ -94,7 +89,6 @@ public Class getColumnClass (int column) * * @param parent DOCUMENT ME! * @param child DOCUMENT ME! - * * @return DOCUMENT ME! */ @Override @@ -143,7 +137,6 @@ public boolean isCellEditable (Object node, * DOCUMENT ME! * * @param node DOCUMENT ME! - * * @return DOCUMENT ME! */ @Override @@ -203,6 +196,7 @@ public void valueForPathChanged (TreePath path, * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. + * * @see EventListenerList */ protected void fireTreeNodesChanged (Object source, @@ -228,11 +222,16 @@ protected void fireTreeNodesChanged (Object source, } } - /* + /** * Notify all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. + * + * @param source DOCUMENT ME! + * @param path DOCUMENT ME! + * @param childIndices DOCUMENT ME! + * @param children DOCUMENT ME! * @see EventListenerList */ protected void fireTreeNodesInserted (Object source, @@ -258,11 +257,16 @@ protected void fireTreeNodesInserted (Object source, } } - /* + /** * Notify all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. + * + * @param source DOCUMENT ME! + * @param path DOCUMENT ME! + * @param childIndices DOCUMENT ME! + * @param children DOCUMENT ME! * @see EventListenerList */ protected void fireTreeNodesRemoved (Object source, @@ -288,11 +292,16 @@ protected void fireTreeNodesRemoved (Object source, } } - /* + /** * Notify all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. + * + * @param source DOCUMENT ME! + * @param path DOCUMENT ME! + * @param childIndices DOCUMENT ME! + * @param children DOCUMENT ME! * @see EventListenerList */ protected void fireTreeStructureChanged (Object source, diff --git a/src/main/org/audiveris/omr/ui/treetable/JTreeTable.java b/src/main/org/audiveris/omr/ui/treetable/JTreeTable.java index 76414b02f..92de1860e 100644 --- a/src/main/org/audiveris/omr/ui/treetable/JTreeTable.java +++ b/src/main/org/audiveris/omr/ui/treetable/JTreeTable.java @@ -51,14 +51,12 @@ public class JTreeTable extends JTable { - //~ Instance fields ---------------------------------------------------------------------------- /** * A subclass of JTree. */ protected TreeTableCellRenderer tree; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new JTreeTable object. * @@ -97,9 +95,8 @@ public JTreeTable (TreeTableModel treeTableModel) } } - //~ Methods ------------------------------------------------------------------------------------ - - /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to + /** + * Workaround for BasicTableUI anomaly. Make sure the UI never tries to * paint the editor. The UI currently uses different techniques to * paint the renderers and editors and overriding setBounds() below * is not the right thing to do for an editor. Returning -1 for the @@ -165,7 +162,6 @@ public void updateUI () LookAndFeel.installColorsAndFont(this, "Tree.background", "Tree.foreground", "Tree.font"); } - //~ Inner Classes ------------------------------------------------------------------------------ //---------------------// // TreeTableCellEditor // //---------------------// @@ -176,7 +172,6 @@ public class TreeTableCellEditor extends AbstractCellEditor implements TableCellEditor { - //~ Methods -------------------------------------------------------------------------------- @Override public Component getTableCellEditorComponent (JTable table, @@ -244,20 +239,17 @@ public class TreeTableCellRenderer extends JTree implements TableCellRenderer { - //~ Instance fields ------------------------------------------------------------------------ /** * Last table/tree row asked to renderer. */ protected int visibleRow; - //~ Constructors --------------------------------------------------------------------------- public TreeTableCellRenderer (TreeModel model) { super(model); } - //~ Methods -------------------------------------------------------------------------------- /** * TreeCellRenderer method. Overridden to update the visible row. */ @@ -354,24 +346,21 @@ public void updateUI () * change in the ListSelectionModel happens, the paths are updated in the * DefaultTreeSelectionModel. */ - protected class ListToTreeSelectionModelWrapper + protected final class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { - //~ Instance fields ------------------------------------------------------------------------ /** * Set to true when we are updating the ListSelectionModel. */ protected boolean updatingListSelectionModel; - //~ Constructors --------------------------------------------------------------------------- - public ListToTreeSelectionModelWrapper () + ListToTreeSelectionModelWrapper () { super(); getListSelectionModel().addListSelectionListener(createListSelectionListener()); } - //~ Methods -------------------------------------------------------------------------------- /** * Returns the list selection model. ListToTreeSelectionModelWrapper * listens for changes to this model and updates the selected paths @@ -409,6 +398,13 @@ public void resetRowSelection () // paths are the only thing that needs to be updated. } + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + /** * Creates and returns an instance of ListSelectionHandler. * @@ -454,7 +450,6 @@ protected void updateSelectedPathsFromSelectedRows () } } - //~ Inner Classes -------------------------------------------------------------------------- /** * Class responsible for calling updateSelectedPathsFromSelectedRows * when the selection of the list changse. @@ -462,7 +457,6 @@ protected void updateSelectedPathsFromSelectedRows () class ListSelectionHandler implements ListSelectionListener { - //~ Methods ---------------------------------------------------------------------------- @Override public void valueChanged (ListSelectionEvent e) diff --git a/src/main/org/audiveris/omr/ui/treetable/TreeTableModel.java b/src/main/org/audiveris/omr/ui/treetable/TreeTableModel.java index d7097c405..e9cdd3d2f 100644 --- a/src/main/org/audiveris/omr/ui/treetable/TreeTableModel.java +++ b/src/main/org/audiveris/omr/ui/treetable/TreeTableModel.java @@ -5,7 +5,6 @@ //----------------------------------------------------------------------------// package org.audiveris.omr.ui.treetable; - /* * TreeTableModel.java * @@ -41,7 +40,6 @@ public interface TreeTableModel extends TreeModel { - //~ Methods ------------------------------------------------------------------------------------ /** * Returns the type for column number. diff --git a/src/main/org/audiveris/omr/ui/treetable/TreeTableModelAdapter.java b/src/main/org/audiveris/omr/ui/treetable/TreeTableModelAdapter.java index 4e4e1ba17..1015d4b05 100644 --- a/src/main/org/audiveris/omr/ui/treetable/TreeTableModelAdapter.java +++ b/src/main/org/audiveris/omr/ui/treetable/TreeTableModelAdapter.java @@ -5,7 +5,6 @@ //----------------------------------------------------------------------------// package org.audiveris.omr.ui.treetable; - /* * @(#)TreeTableModelAdapter.java 1.2 98/10/27 * @@ -40,13 +39,11 @@ public class TreeTableModelAdapter extends AbstractTableModel { - //~ Instance fields ---------------------------------------------------------------------------- JTree tree; TreeTableModel treeTableModel; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new TreeTableModelAdapter object. * @@ -59,8 +56,7 @@ public TreeTableModelAdapter (TreeTableModel treeTableModel, this.tree = tree; this.treeTableModel = treeTableModel; - tree.addTreeExpansionListener( - new TreeExpansionListener() + tree.addTreeExpansionListener(new TreeExpansionListener() { // Don't use fireTableRowsInserted() here; the selection model // would get updated twice. @@ -81,8 +77,7 @@ public void treeCollapsed (TreeExpansionEvent event) // tree changes. We use delayedFireTableDataChanged as we can // not be guaranteed the tree will have finished processing // the event before us. - treeTableModel.addTreeModelListener( - new TreeModelListener() + treeTableModel.addTreeModelListener(new TreeModelListener() { @Override public void treeNodesChanged (TreeModelEvent e) @@ -110,7 +105,6 @@ public void treeStructureChanged (TreeModelEvent e) }); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // getColumnClass // //----------------// @@ -118,7 +112,6 @@ public void treeStructureChanged (TreeModelEvent e) * DOCUMENT ME! * * @param column DOCUMENT ME! - * * @return DOCUMENT ME! */ @Override @@ -149,7 +142,6 @@ public int getColumnCount () * DOCUMENT ME! * * @param column DOCUMENT ME! - * * @return DOCUMENT ME! */ @Override @@ -180,7 +172,6 @@ public int getRowCount () * * @param row DOCUMENT ME! * @param column DOCUMENT ME! - * * @return DOCUMENT ME! */ @Override @@ -198,7 +189,6 @@ public Object getValueAt (int row, * * @param row DOCUMENT ME! * @param column DOCUMENT ME! - * * @return DOCUMENT ME! */ @Override @@ -215,7 +205,6 @@ public boolean isCellEditable (int row, * DOCUMENT ME! * * @param row DOCUMENT ME! - * * @return DOCUMENT ME! */ public Object nodeForRow (int row) @@ -252,8 +241,7 @@ public void setValueAt (Object value, */ protected void delayedFireTableDataChanged () { - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () diff --git a/src/main/org/audiveris/omr/ui/util/AbstractMenuListener.java b/src/main/org/audiveris/omr/ui/util/AbstractMenuListener.java index 01b71b429..7170413b8 100644 --- a/src/main/org/audiveris/omr/ui/util/AbstractMenuListener.java +++ b/src/main/org/audiveris/omr/ui/util/AbstractMenuListener.java @@ -33,7 +33,6 @@ public class AbstractMenuListener implements MenuListener { - //~ Methods ------------------------------------------------------------------------------------ @Override public void menuCanceled (MenuEvent e) diff --git a/src/main/org/audiveris/omr/ui/util/AbstractMouseListener.java b/src/main/org/audiveris/omr/ui/util/AbstractMouseListener.java index 2ba83a8ad..71b1e932b 100644 --- a/src/main/org/audiveris/omr/ui/util/AbstractMouseListener.java +++ b/src/main/org/audiveris/omr/ui/util/AbstractMouseListener.java @@ -33,7 +33,6 @@ public abstract class AbstractMouseListener implements MouseListener { - //~ Methods ------------------------------------------------------------------------------------ @Override public void mouseClicked (MouseEvent e) diff --git a/src/main/org/audiveris/omr/ui/util/AttachmentHolder.java b/src/main/org/audiveris/omr/ui/util/AttachmentHolder.java index 46b7d2426..6ff41b0d0 100644 --- a/src/main/org/audiveris/omr/ui/util/AttachmentHolder.java +++ b/src/main/org/audiveris/omr/ui/util/AttachmentHolder.java @@ -33,7 +33,6 @@ */ public interface AttachmentHolder { - //~ Methods ------------------------------------------------------------------------------------ /** * Register an attachment with a key and a shape. diff --git a/src/main/org/audiveris/omr/ui/util/BasicAttachmentHolder.java b/src/main/org/audiveris/omr/ui/util/BasicAttachmentHolder.java index 5eb74950a..5b2053679 100644 --- a/src/main/org/audiveris/omr/ui/util/BasicAttachmentHolder.java +++ b/src/main/org/audiveris/omr/ui/util/BasicAttachmentHolder.java @@ -44,12 +44,10 @@ public class BasicAttachmentHolder implements AttachmentHolder { - //~ Instance fields ---------------------------------------------------------------------------- /** Map for attachments */ - protected Map attachments = new HashMap(); + protected Map attachments = new HashMap<>(); - //~ Methods ------------------------------------------------------------------------------------ //---------------// // addAttachment // //---------------// @@ -78,7 +76,7 @@ public Map getAttachments () public int removeAttachments (String prefix) { // To avoid concurrent modifications - List toRemove = new ArrayList(); + List toRemove = new ArrayList<>(); for (String key : attachments.keySet()) { if (key.startsWith(prefix)) { diff --git a/src/main/org/audiveris/omr/ui/util/ButtonTabComponent.java b/src/main/org/audiveris/omr/ui/util/ButtonTabComponent.java index cdf7b72f8..15ac4d620 100644 --- a/src/main/org/audiveris/omr/ui/util/ButtonTabComponent.java +++ b/src/main/org/audiveris/omr/ui/util/ButtonTabComponent.java @@ -58,11 +58,12 @@ /** * Component to be used as tabComponent. * Contains a JLabel to show the text and a JButton to close the tab it belongs to. + * + * @author Hervé Bitteur */ public class ButtonTabComponent extends JPanel { - //~ Static fields/initializers ----------------------------------------------------------------- /** The same listener for all TabButton instances in application!. */ private static final MouseListener buttonMouseListener = new MouseAdapter() @@ -90,11 +91,9 @@ public void mouseExited (MouseEvent e) } }; - //~ Instance fields ---------------------------------------------------------------------------- /** The containing JTabbedPane. */ private final ClosableTabbedPane pane; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ButtonTabComponent} object. * @@ -142,7 +141,6 @@ public String getText () ///setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0)); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // TabButton // //-----------// @@ -150,9 +148,8 @@ private class TabButton extends JButton implements ActionListener { - //~ Constructors --------------------------------------------------------------------------- - public TabButton () + TabButton () { final int size = 13; // Button side length setPreferredSize(new Dimension(size, size)); @@ -178,7 +175,6 @@ public TabButton () addActionListener(this); } - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { diff --git a/src/main/org/audiveris/omr/ui/util/ClosableTabbedPane.java b/src/main/org/audiveris/omr/ui/util/ClosableTabbedPane.java index 79e08b685..c90e0214f 100644 --- a/src/main/org/audiveris/omr/ui/util/ClosableTabbedPane.java +++ b/src/main/org/audiveris/omr/ui/util/ClosableTabbedPane.java @@ -22,8 +22,10 @@ package org.audiveris.omr.ui.util; import java.awt.Component; +import java.awt.Container; import javax.swing.Icon; +import javax.swing.JButton; import javax.swing.JTabbedPane; /** @@ -39,7 +41,6 @@ public class ClosableTabbedPane extends JTabbedPane { - //~ Methods ------------------------------------------------------------------------------------ //-----------// // insertTab // @@ -63,6 +64,31 @@ public void insertTab (String title, setTabComponentAt(i, new ButtonTabComponent(this)); } + //---------------------// + // removeClosingButton // + //---------------------// + /** + * Remove the closing button for the provided tab index. + * + * @param tabIndex index of tab in tabbed pane + */ + public void removeClosingButton (int tabIndex) + { + Component tab = getTabComponentAt(tabIndex); + + if (tab instanceof ButtonTabComponent) { + for (Component c : ((Container) tab).getComponents()) { + if (c instanceof JButton) { + ((Container) tab).remove(c); + tab.invalidate(); + tab.repaint(); + + return; + } + } + } + } + //-----------------// // tabAboutToClose // //-----------------// diff --git a/src/main/org/audiveris/omr/ui/util/CursorController.java b/src/main/org/audiveris/omr/ui/util/CursorController.java index 92cb1f345..c971c9f26 100644 --- a/src/main/org/audiveris/omr/ui/util/CursorController.java +++ b/src/main/org/audiveris/omr/ui/util/CursorController.java @@ -44,22 +44,29 @@ */ public class CursorController { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(CursorController.class); + /** Cursor when waiting. */ public static final Cursor busyCursor = new Cursor(Cursor.WAIT_CURSOR); + /** Cursor in standard status. */ public static final Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR); + /** Delay before busy cursor is displayed. */ public static final int delay = 500; // in milliseconds - //~ Constructors ------------------------------------------------------------------------------- private CursorController () { } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Wraps an action listener with a busy cursor if not performed within delay. + * + * @param component owner of cursor + * @param mainActionListener real listener to be wrapped + * @return the wrapped listener + */ public static ActionListener createListener (final Component component, final ActionListener mainActionListener) { diff --git a/src/main/org/audiveris/omr/ui/util/DynamicMenu.java b/src/main/org/audiveris/omr/ui/util/DynamicMenu.java index e7d2c8fee..7c23fb843 100644 --- a/src/main/org/audiveris/omr/ui/util/DynamicMenu.java +++ b/src/main/org/audiveris/omr/ui/util/DynamicMenu.java @@ -37,11 +37,9 @@ */ public abstract class DynamicMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(DynamicMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- /** The concrete UI menu. */ private JMenu menu; @@ -59,7 +57,6 @@ public void menuSelected (MenuEvent e) } }; - //~ Constructors ------------------------------------------------------------------------------- /** * Create the dynamic menu. * @@ -75,7 +72,8 @@ public DynamicMenu (String menuLabel, // Listener to menu selection, to modify content on-the-fly menu.addMenuListener(menuListener); - } catch (Exception ex) { + } catch (IllegalAccessException | + InstantiationException ex) { logger.error("Could not instantiate " + menuClass, ex); menu = null; } @@ -96,13 +94,13 @@ public DynamicMenu (Action action, // Listener to menu selection, to modify content on-the-fly menu.addMenuListener(menuListener); - } catch (Exception ex) { + } catch (IllegalAccessException | + InstantiationException ex) { logger.error("Could not instantiate " + menuClass, ex); menu = null; } } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getMenu // //---------// diff --git a/src/main/org/audiveris/omr/ui/util/FixedWidthIcon.java b/src/main/org/audiveris/omr/ui/util/FixedWidthIcon.java index b7dc4a89e..9c17073db 100644 --- a/src/main/org/audiveris/omr/ui/util/FixedWidthIcon.java +++ b/src/main/org/audiveris/omr/ui/util/FixedWidthIcon.java @@ -29,18 +29,17 @@ /** * Force an icon to be painted within a given width. + * + * @author Hervé Bitteur */ public class FixedWidthIcon implements Icon { - //~ Static fields/initializers ----------------------------------------------------------------- private static final int width = 35; // TODO: use an application constant - //~ Instance fields ---------------------------------------------------------------------------- private final Icon icon; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code FixedIcon} object. * @@ -52,7 +51,6 @@ public FixedWidthIcon (Icon icon) this.icon = icon; } - //~ Methods ------------------------------------------------------------------------------------ @Override public int getIconHeight () { diff --git a/src/main/org/audiveris/omr/ui/util/ItemRenderer.java b/src/main/org/audiveris/omr/ui/util/ItemRenderer.java index eec95d2be..432637fb7 100644 --- a/src/main/org/audiveris/omr/ui/util/ItemRenderer.java +++ b/src/main/org/audiveris/omr/ui/util/ItemRenderer.java @@ -31,7 +31,6 @@ */ public interface ItemRenderer { - //~ Methods ------------------------------------------------------------------------------------ /** * Render items on the provided graphics diff --git a/src/main/org/audiveris/omr/ui/util/ModelessOptionPane.java b/src/main/org/audiveris/omr/ui/util/ModelessOptionPane.java index ffe292771..5f13561aa 100644 --- a/src/main/org/audiveris/omr/ui/util/ModelessOptionPane.java +++ b/src/main/org/audiveris/omr/ui/util/ModelessOptionPane.java @@ -50,11 +50,9 @@ public class ModelessOptionPane extends JOptionPane { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ModelessOptionPane.class); - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // showConfirmDialog // //-------------------// @@ -80,13 +78,12 @@ public static int showModelessConfirmDialog (Component parentComponent, int optionType) throws HeadlessException { - final Exchanger exchanger = new Exchanger(); + final Exchanger exchanger = new Exchanger<>(); final JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE, optionType); Window window = getWindowForComponent(parentComponent); - final JDialog dialog = (window instanceof Frame) - ? new JDialog((Frame) window, title) + final JDialog dialog = (window instanceof Frame) ? new JDialog((Frame) window, title) : new JDialog((Dialog) window, title); WindowAdapter adapter = new WindowAdapter() @@ -118,8 +115,7 @@ public void windowClosing (WindowEvent e) dialog.addWindowListener(adapter); dialog.addWindowFocusListener(adapter); - dialog.addComponentListener( - new ComponentAdapter() + dialog.addComponentListener(new ComponentAdapter() { @Override public void componentShown (ComponentEvent ce) @@ -128,8 +124,7 @@ public void componentShown (ComponentEvent ce) pane.setValue(JOptionPane.UNINITIALIZED_VALUE); } }); - pane.addPropertyChangeListener( - new PropertyChangeListener() + pane.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange (PropertyChangeEvent event) @@ -137,11 +132,10 @@ public void propertyChange (PropertyChangeEvent event) // Let the defaultCloseOperation handle the closing // if the user closed the window without selecting a button // (newValue = null in that case). Otherwise, close the dialog. - if (dialog.isVisible() - && (event.getSource() == pane) - && (event.getPropertyName().equals(VALUE_PROPERTY)) - && (event.getNewValue() != null) - && (event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE)) { + if (dialog.isVisible() && (event.getSource() == pane) + && (event.getPropertyName().equals(VALUE_PROPERTY)) + && (event.getNewValue() != null) + && (event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE)) { JOptionPane pane = (JOptionPane) event.getSource(); dialog.setVisible(false); @@ -200,7 +194,7 @@ private static int optionOf (JOptionPane pane) if (selectedValue == null) { return JOptionPane.CLOSED_OPTION; } else if (selectedValue instanceof Integer) { - return ((Integer) selectedValue).intValue(); + return ((Integer) selectedValue); } else { return JOptionPane.CLOSED_OPTION; } diff --git a/src/main/org/audiveris/omr/ui/util/OmrFileFilter.java b/src/main/org/audiveris/omr/ui/util/OmrFileFilter.java index d5474dd25..ee366c346 100644 --- a/src/main/org/audiveris/omr/ui/util/OmrFileFilter.java +++ b/src/main/org/audiveris/omr/ui/util/OmrFileFilter.java @@ -36,7 +36,6 @@ public class OmrFileFilter extends FileFilter implements FilenameFilter { - //~ Instance fields ---------------------------------------------------------------------------- /** User readable description */ private final String description; @@ -44,7 +43,6 @@ public class OmrFileFilter /** Array of accepted file extensions */ private final String[] extensions; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a file filter, with only one file extension to consider * @@ -79,7 +77,8 @@ public OmrFileFilter (String description, String... extensions) { if (description == null) { - this.description = (extensions.length > 1) ? Arrays.toString(extensions) : extensions[0]; + this.description = (extensions.length > 1) ? Arrays.toString(extensions) + : extensions[0]; } else { this.description = description; } @@ -87,7 +86,6 @@ public OmrFileFilter (String description, this.extensions = extensions.clone(); } - //~ Methods ------------------------------------------------------------------------------------ //--------// // accept // //--------// diff --git a/src/main/org/audiveris/omr/ui/util/Panel.java b/src/main/org/audiveris/omr/ui/util/Panel.java index 6a7429c32..1be4ce97a 100644 --- a/src/main/org/audiveris/omr/ui/util/Panel.java +++ b/src/main/org/audiveris/omr/ui/util/Panel.java @@ -49,7 +49,6 @@ public class Panel extends JPanel ///FormDebugPanel { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -58,11 +57,9 @@ public class Panel /** Default Insets */ private static Insets DEFAULT_INSETS; - //~ Instance fields ---------------------------------------------------------------------------- /** Room for potential specific insets */ private Insets insets; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Panel object. */ @@ -71,7 +68,87 @@ public Panel () setBorder(null); } - //~ Methods ------------------------------------------------------------------------------------ + //-----------// + // getInsets // + //-----------// + /** + * By this way, Swing will paint the component with its specific inset values. + * + * @return the panel insets + */ + @Override + public Insets getInsets () + { + if (insets != null) { + return insets; + } else { + return getDefaultInsets(); + } + } + + //-----------// + // setInsets // + //-----------// + /** + * Set the panel insets (in number of pixels) on the four directions. + * + * @param top inset on top side + * @param left inset on the left side + * @param bottom inset on the bottom side + * @param right inset on the right side + */ + public void setInsets (int top, + int left, + int bottom, + int right) + { + insets = new Insets(top, left, bottom, right); + } + + //-------------// + // setNoInsets // + //-------------// + /** + * A convenient method to set all 4 insets values to zero. + */ + public void setNoInsets () + { + insets = new Insets(0, 0, 0, 0); + } + + //----------------// + // paintComponent // + //----------------// + /** + * This method is redefined to give a chance to draw the cell boundaries + * if so desired. + * + * @param g the graphic context + */ + @Override + protected void paintComponent (Graphics g) + { + // Note: Uncomment following line for FormDebugPanel + ///setPaintInBackground(true); + super.paintComponent(g); + } + + //------------------// + // getDefaultInsets // + //------------------// + private Insets getDefaultInsets () + { + if (DEFAULT_INSETS == null) { + DEFAULT_INSETS = new Insets( + constants.insetTop.getValue(), + constants.insetLeft.getValue(), + constants.insetBottom.getValue(), + constants.insetRight.getValue()); + } + + return DEFAULT_INSETS; + } + /** * Selector to the default button width. * @@ -145,6 +222,12 @@ public static String getPanelInterline () //-------------// // makeColumns // //-------------// + /** + * Build the columns specification, with default options. + * + * @param cols number of logical columns. + * @return proper col spec + */ public static String makeColumns (int cols) { return makeColumns(cols, "right:", Panel.getLabelWidth(), Panel.getFieldWidth()); @@ -153,6 +236,15 @@ public static String makeColumns (int cols) //-------------// // makeColumns // //-------------// + /** + * Build the columns specification. + * + * @param cols number of logical columns + * @param labelAlignment horizontal alignment to apply to label + * @param labelWidth width specification for labels + * @param fieldWidth width specification for fields + * @return proper columns specification + */ public static String makeColumns (int cols, String labelAlignment, String labelWidth, @@ -168,8 +260,8 @@ public static String makeColumns (int cols, sbc.append(",").append(fieldInterval).append(","); } - sbc.append(labelAlignment).append(labelWidth).append(",").append(labelInterval) - .append(",").append(fieldWidth); + sbc.append(labelAlignment).append(labelWidth).append(",").append(labelInterval).append( + ",").append(fieldWidth); } return sbc.toString(); @@ -221,6 +313,14 @@ public static FormLayout makeFormLayout (int rows, //-------------------// // makeLabelsColumns // //-------------------// + /** + * Build a column specification for just labels. + * + * @param cols number of columns. + * @param labelInterval horizontal gap specification between labels + * @param labelWidth width specification for labels + * @return proper columns specification + */ public static String makeLabelsColumns (int cols, String labelInterval, String labelWidth) @@ -298,95 +398,12 @@ public static String makeRows (int rows, return sbr.toString(); } - //-----------// - // getInsets // - //-----------// - /** - * By this way, Swing will paint the component with its specific inset values. - * - * @return the panel insets - */ - @Override - public Insets getInsets () - { - if (insets != null) { - return insets; - } else { - return getDefaultInsets(); - } - } - - //-----------// - // setInsets // - //-----------// - /** - * Set the panel insets (in number of pixels) on the four directions. - * - * @param top inset on top side - * @param left inset on the left side - * @param bottom inset on the bottom side - * @param right inset on the right side - */ - public void setInsets (int top, - int left, - int bottom, - int right) - { - insets = new Insets(top, left, bottom, right); - } - - //-------------// - // setNoInsets // - //-------------// - /** - * A convenient method to set all 4 insets values to zero. - */ - public void setNoInsets () - { - insets = new Insets(0, 0, 0, 0); - } - - //----------------// - // paintComponent // - //----------------// - /** - * This method is redefined to give a chance to draw the cell boundaries - * if so desired. - * - * @param g the graphic context - */ - @Override - protected void paintComponent (Graphics g) - { - // Note: Uncomment following line for FormDebugPanel - ///setPaintInBackground(true); - super.paintComponent(g); - } - - //------------------// - // getDefaultInsets // - //------------------// - private Insets getDefaultInsets () - { - if (DEFAULT_INSETS == null) { - DEFAULT_INSETS = new Insets( - constants.insetTop.getValue(), - constants.insetLeft.getValue(), - constants.insetBottom.getValue(), - constants.insetRight.getValue()); - } - - return DEFAULT_INSETS; - } - - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String buttonWidth = new Constant.String( "45dlu", diff --git a/src/main/org/audiveris/omr/ui/util/SeparableMenu.java b/src/main/org/audiveris/omr/ui/util/SeparableMenu.java index ff96bdcd3..d5555de5f 100644 --- a/src/main/org/audiveris/omr/ui/util/SeparableMenu.java +++ b/src/main/org/audiveris/omr/ui/util/SeparableMenu.java @@ -33,7 +33,6 @@ public class SeparableMenu extends JMenu { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SeparableMenu object. @@ -63,7 +62,6 @@ public SeparableMenu (String s) super(s); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // addSeparator // //--------------// @@ -85,16 +83,10 @@ public void addSeparator () //---------------// /** * Remove any potential orphan separator at the end of the menu. - * - * @param menu the menu to purge */ - public static void trimSeparator (JMenu menu) + public void trimSeparator () { - int count = menu.getMenuComponentCount(); - - if ((count > 0) && menu.getMenuComponent(count - 1) instanceof JSeparator) { - menu.remove(count - 1); - } + trimSeparator(this); } //---------------// @@ -102,13 +94,15 @@ public static void trimSeparator (JMenu menu) //---------------// /** * Remove any potential orphan separator at the end of the menu. + * + * @param menu the menu to purge */ - public void trimSeparator () + public static void trimSeparator (JMenu menu) { - int count = getMenuComponentCount(); + int count = menu.getMenuComponentCount(); - if ((count > 0) && getMenuComponent(count - 1) instanceof JSeparator) { - remove(count - 1); + if ((count > 0) && menu.getMenuComponent(count - 1) instanceof JSeparator) { + menu.remove(count - 1); } } } diff --git a/src/main/org/audiveris/omr/ui/util/SeparablePopupMenu.java b/src/main/org/audiveris/omr/ui/util/SeparablePopupMenu.java index addc4dc02..c3831e229 100644 --- a/src/main/org/audiveris/omr/ui/util/SeparablePopupMenu.java +++ b/src/main/org/audiveris/omr/ui/util/SeparablePopupMenu.java @@ -27,14 +27,14 @@ /** * Class {@code SeparablePopupMenu} is a popup menu which is able to collapse unneeded * separators. - * This is derived from {link SeparableMenu}. + *

                                                                                                                                                        + * This is derived from {@link SeparableMenu}. * * @author Hervé Bitteur */ public class SeparablePopupMenu extends JPopupMenu { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SeparablePopupMenu object. @@ -54,12 +54,11 @@ public SeparablePopupMenu (String s) super(s); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // addSeparator // //--------------// /** - * The separator will be inserted only if it is really necessary + * The separator will be inserted only if it is really necessary. */ @Override public void addSeparator () @@ -75,7 +74,7 @@ public void addSeparator () // trimSeparator // //---------------// /** - * Remove any potential orphan separator at the end of the menu + * Remove any potential orphan separator at the end of the menu. */ public void trimSeparator () { diff --git a/src/main/org/audiveris/omr/ui/util/SeparableToolBar.java b/src/main/org/audiveris/omr/ui/util/SeparableToolBar.java index 454159142..95b43b5db 100644 --- a/src/main/org/audiveris/omr/ui/util/SeparableToolBar.java +++ b/src/main/org/audiveris/omr/ui/util/SeparableToolBar.java @@ -37,7 +37,6 @@ public class SeparableToolBar extends JToolBar { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -48,7 +47,6 @@ public class SeparableToolBar constants.separatorWidth.getValue(), constants.separatorWidth.getValue()); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new SeparableToolBar object. */ @@ -89,7 +87,6 @@ public SeparableToolBar (String name, super(name, orientation); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // addSeparator // //--------------// @@ -123,14 +120,12 @@ public static void purgeSeparator (JToolBar toolBar) } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer separatorWidth = new Constant.Integer( "Pixels", diff --git a/src/main/org/audiveris/omr/ui/util/UILookAndFeel.java b/src/main/org/audiveris/omr/ui/util/UILookAndFeel.java index 5b467c1e1..d28f2a5fb 100644 --- a/src/main/org/audiveris/omr/ui/util/UILookAndFeel.java +++ b/src/main/org/audiveris/omr/ui/util/UILookAndFeel.java @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; /** * Class {@code UILookAndFeel} enables to select the UI Look and Feel to be used in this @@ -38,7 +39,6 @@ */ public class UILookAndFeel { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -53,12 +53,10 @@ public class UILookAndFeel } } - //~ Constructors ------------------------------------------------------------------------------- private UILookAndFeel () { } - //~ Methods ------------------------------------------------------------------------------------ // Available Themes: // // AbstractSkyTheme @@ -114,20 +112,21 @@ public static void setUI (String className) } else { UIManager.setLookAndFeel(constants.lookAndFeel.getValue()); } - } catch (Exception ex) { + } catch (ClassNotFoundException | + IllegalAccessException | + InstantiationException | + UnsupportedLookAndFeelException ex) { //ex.printStackTrace(); logger.warn(ex.toString()); } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.String lookAndFeel = new Constant.String( "com.jgoodies.looks.plastic.Plastic3DLookAndFeel", diff --git a/src/main/org/audiveris/omr/ui/util/UIPredicates.java b/src/main/org/audiveris/omr/ui/util/UIPredicates.java index b5bf7b37f..115d6466d 100644 --- a/src/main/org/audiveris/omr/ui/util/UIPredicates.java +++ b/src/main/org/audiveris/omr/ui/util/UIPredicates.java @@ -34,7 +34,6 @@ */ public abstract class UIPredicates { - //~ Constructors ------------------------------------------------------------------------------- /** * Not meant to be instantiated. @@ -43,7 +42,6 @@ private UIPredicates () { } - //~ Methods ------------------------------------------------------------------------------------ //------------------// // isAdditionWanted // //------------------// @@ -63,7 +61,7 @@ public static boolean isAdditionWanted (MouseEvent e) return left && command && !e.isPopupTrigger(); } else { return (SwingUtilities.isRightMouseButton(e) != SwingUtilities.isLeftMouseButton(e)) - && e.isControlDown(); + && e.isControlDown(); } } @@ -141,9 +139,9 @@ public static boolean isRubberWanted (MouseEvent e) { int onmask = BUTTON1_DOWN_MASK | SHIFT_DOWN_MASK; int offmask = BUTTON2_DOWN_MASK | BUTTON3_DOWN_MASK; -// int onmask = SHIFT_DOWN_MASK; -// int offmask = BUTTON2_DOWN_MASK; // middle button = wheel + // int onmask = SHIFT_DOWN_MASK; + // int offmask = BUTTON2_DOWN_MASK; // middle button = wheel return (e.getModifiersEx() & (onmask | offmask)) == onmask; } } diff --git a/src/main/org/audiveris/omr/ui/util/UIThread.java b/src/main/org/audiveris/omr/ui/util/UIThread.java index 180fe3bce..54ec06ee0 100644 --- a/src/main/org/audiveris/omr/ui/util/UIThread.java +++ b/src/main/org/audiveris/omr/ui/util/UIThread.java @@ -1,44 +1,44 @@ -//------------------------------------------------------------------------------------------------// -// // -// U I T h r e a d // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.ui.util; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation {@code UIThread} indicates a method which can be called only from the - * UI thread (EDT: Event Dispatching Thread), or a whole class dedicated to UI. - *

                                                                                                                                                        - * A non-UI thread must use the {@link javax.swing.SwingUtilities#invokeLater(Runnable doRun)} or - * {@link javax.swing.SwingUtilities#invokeAndWait(Runnable doRun)} to call such method safely. - * - * @author Hervé Bitteur - */ -@Documented -@Target({ElementType.TYPE, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UIThread -{ -} +//------------------------------------------------------------------------------------------------// +// // +// U I T h r e a d // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.ui.util; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation {@code UIThread} indicates a method which can be called only from the + * UI thread (EDT: Event Dispatching Thread), or a whole class dedicated to UI. + *

                                                                                                                                                        + * A non-UI thread must use the {@link javax.swing.SwingUtilities#invokeLater(Runnable doRun)} or + * {@link javax.swing.SwingUtilities#invokeAndWait(Runnable doRun)} to call such method safely. + * + * @author Hervé Bitteur + */ +@Documented +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface UIThread +{ +} diff --git a/src/main/org/audiveris/omr/ui/util/UIUtil.java b/src/main/org/audiveris/omr/ui/util/UIUtil.java index 2e116bd46..471288cf2 100644 --- a/src/main/org/audiveris/omr/ui/util/UIUtil.java +++ b/src/main/org/audiveris/omr/ui/util/UIUtil.java @@ -43,10 +43,10 @@ import java.io.File; import java.nio.file.Path; import java.util.Collection; -import java.util.Iterator; import javax.swing.AbstractAction; import javax.swing.AbstractButton; +import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; @@ -65,16 +65,17 @@ */ public abstract class UIUtil { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(UIUtil.class); /** - * Customized border for tool buttons, to use consistently in all UI - * components. + * Customized border for tool buttons, to use consistently in all UI components. */ private static Border toolBorder; + /** + * Listener which disposes a window being closed. + */ public static final WindowListener closeWindow = new WindowAdapter() { @Override @@ -84,7 +85,6 @@ public void windowClosing (WindowEvent e) } }; - //~ Methods ------------------------------------------------------------------------------------ //--------------------// // complementaryColor // //--------------------// @@ -167,13 +167,11 @@ public boolean accept (File f) public static void enableActions (Collection actions, boolean bool) { - for (Iterator it = actions.iterator(); it.hasNext();) { - Object next = it.next(); - + for (Object next : actions) { if (next instanceof AbstractAction) { - ((AbstractAction) next).setEnabled(bool); + ((Action) next).setEnabled(bool); } else if (next instanceof AbstractButton) { - ((AbstractButton) next).setEnabled(bool); + ((Component) next).setEnabled(bool); } else { logger.warn("Neither Button nor Action : {}", next); } @@ -230,8 +228,10 @@ public static File fileChooser (boolean save, Component parentFrame = parent; - while (parentFrame.getParent() != null) { - parentFrame = parentFrame.getParent(); + if (parentFrame != null) { + while (parentFrame.getParent() != null) { + parentFrame = parentFrame.getParent(); + } } try { @@ -266,16 +266,15 @@ public static File fileChooser (boolean save, } else { final JFileChooser fc = new JFileChooser(); // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6317789 -// final JFileChooser fc = new JFileChooser() -// { -// @Override -// public void updateUI () -// { -// putClientProperty("FileChooser.useShellFolder", false); -// super.updateUI(); -// } -// }; - + // final JFileChooser fc = new JFileChooser() + // { + // @Override + // public void updateUI () + // { + // putClientProperty("FileChooser.useShellFolder", false); + // super.updateUI(); + // } + // }; fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); // Pre-select the directory, and potentially the file to save to @@ -354,7 +353,7 @@ public static void insertTitle (JMenu menu, public static void minimize (JFrame frame) { int state = frame.getExtendedState(); - state = state & ICONIFIED; + state &= ICONIFIED; frame.setExtendedState(state); } @@ -512,7 +511,7 @@ public static void unMinimize (JFrame frame) int state = frame.getExtendedState(); if ((state & ICONIFIED) != 0) { - state = state & ~ICONIFIED; + state &= ~ICONIFIED; } frame.setExtendedState(state); @@ -520,4 +519,8 @@ public static void unMinimize (JFrame frame) frame.setVisible(true); frame.toFront(); } + + private UIUtil () + { + } } diff --git a/src/main/org/audiveris/omr/ui/util/WeakItemRenderer.java b/src/main/org/audiveris/omr/ui/util/WeakItemRenderer.java index b252566cc..524bab338 100644 --- a/src/main/org/audiveris/omr/ui/util/WeakItemRenderer.java +++ b/src/main/org/audiveris/omr/ui/util/WeakItemRenderer.java @@ -32,11 +32,12 @@ public class WeakItemRenderer implements ItemRenderer { - //~ Instance fields ---------------------------------------------------------------------------- + /** + * Weak reference to the underlying renderer. + */ protected final WeakReference weakRenderer; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new WeakItemRenderer object. * @@ -44,10 +45,9 @@ public class WeakItemRenderer */ public WeakItemRenderer (ItemRenderer renderer) { - weakRenderer = new WeakReference(renderer); + weakRenderer = new WeakReference<>(renderer); } - //~ Methods ------------------------------------------------------------------------------------ //-------------// // renderItems // //-------------// diff --git a/src/main/org/audiveris/omr/ui/util/WebBrowser.java b/src/main/org/audiveris/omr/ui/util/WebBrowser.java index 65bbc887e..e9bf9f200 100644 --- a/src/main/org/audiveris/omr/ui/util/WebBrowser.java +++ b/src/main/org/audiveris/omr/ui/util/WebBrowser.java @@ -28,6 +28,7 @@ import java.awt.Desktop; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; @@ -35,7 +36,6 @@ * Class {@code WebBrowser} gathers functionality to browse a webpage in an external * web browser. * It uses reflection for compatibility with Java 5 and Mac OS X. - * *

                                                                                                                                                        * Nota: Since using Desktop.browse() on a file under Windows crashes JVM 6, this feature is * currently delegated to an external and free utility named BareBonesBrowserLaunch, written by Dem @@ -44,47 +44,28 @@ * * @author Brenton Partridge * @author Hervé Bitteur (for delegation to BareBonesBrowserLaunch) - * */ public class WebBrowser { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(WebBrowser.class); /** Major browsers. */ private static final String[] browsers = { - "firefox", "opera", "konqueror", "epiphany", - "mozilla", "netscape" - }; + "firefox", + "opera", + "konqueror", + "epiphany", + "mozilla", + "netscape"}; /** Singleton instance, initially null. */ private static WebBrowser instance; - //~ Constructors ------------------------------------------------------------------------------- private WebBrowser () { } - //~ Methods ------------------------------------------------------------------------------------ - //------------// - // getBrowser // - //------------// - /** - * Get the singleton WebBrowser implementation. - * - * @return a WebBrowser implementation, not null - * in normal operation - */ - public static synchronized WebBrowser getBrowser () - { - if (instance == null) { - instance = setupBrowser(); - } - - return instance; - } - //-------------// // isSupported // //-------------// @@ -135,6 +116,23 @@ public String toString () return "WebBrowser(unimplemented fallback)"; } + //------------// + // getBrowser // + //------------// + /** + * Get the singleton WebBrowser implementation. + * + * @return a WebBrowser implementation, not null in normal operation + */ + public static synchronized WebBrowser getBrowser () + { + if (instance == null) { + instance = setupBrowser(); + } + + return instance; + } + //---------// // openURL // //---------// @@ -155,7 +153,8 @@ private static void openURL (String url) } else { //assume Unix or Linux for (String browser : browsers) { - if (Runtime.getRuntime().exec(new String[]{"which", browser}).waitFor() == 0) { + if (Runtime.getRuntime().exec(new String[]{"which", browser}) + .waitFor() == 0) { Runtime.getRuntime().exec(new String[]{browser, url}); return; @@ -164,7 +163,14 @@ private static void openURL (String url) logger.warn("Could not find any suitable web browser"); } - } catch (Exception ex) { + } catch (IOException | + ClassNotFoundException | + IllegalAccessException | + IllegalArgumentException | + InterruptedException | + NoSuchMethodException | + SecurityException | + InvocationTargetException ex) { logger.warn("Could not launch browser", ex); } } @@ -188,7 +194,11 @@ public boolean isSupported () Method supported = desktopClass.getMethod("isDesktopSupported"); return (Boolean) supported.invoke(null); - } catch (Exception e) { + } catch (IllegalAccessException | + IllegalArgumentException | + NoSuchMethodException | + SecurityException | + InvocationTargetException e) { return false; } } @@ -199,14 +209,14 @@ public String toString () return "WebBrowser(java.awt.Desktop)"; } }; - } catch (Exception e) { + } catch (ClassNotFoundException e) { logger.debug("java.awt.Desktop unsupported or error initializing"); } //If it's not supported, see if we have the Mac FileManager if (WellKnowns.MAC_OS_X) { try { - final Class fileMgr = Class.forName("com.apple.eio.FileManager"); + Class.forName("com.apple.eio.FileManager"); return new WebBrowser() { @@ -222,7 +232,7 @@ public String toString () return "WebBrowser(com.apple.eio.FileManager)"; } }; - } catch (Exception e) { + } catch (ClassNotFoundException e) { logger.debug("Apple EIO FileManager unsupported"); } } diff --git a/src/main/org/audiveris/omr/ui/view/HistoryMenu.java b/src/main/org/audiveris/omr/ui/view/HistoryMenu.java index e84a3e40d..0668e242c 100644 --- a/src/main/org/audiveris/omr/ui/view/HistoryMenu.java +++ b/src/main/org/audiveris/omr/ui/view/HistoryMenu.java @@ -43,21 +43,18 @@ */ public class HistoryMenu { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(HistoryMenu.class); - //~ Instance fields ---------------------------------------------------------------------------- - // Underlying path history + /** Underlying path history. */ protected final PathHistory history; - // Task class launched on selected path + /** Task class launched on selected path. */ protected final Class pathTaskClass; - // The concrete menu + /** The concrete menu. */ protected JMenu menu; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code HistoryMenu} object. * @@ -72,7 +69,6 @@ public HistoryMenu (PathHistory history, history.setMenu(this); } - //~ Methods ------------------------------------------------------------------------------------ //----------// // populate // //----------// @@ -85,26 +81,25 @@ public HistoryMenu (PathHistory history, public void populate (JMenu menu, Class resourceClass) { - history.feedMenu( - menu, - new ActionListener() - { - @Override - public void actionPerformed (ActionEvent e) - { - try { - final String name = e.getActionCommand().trim(); - - if (!name.isEmpty()) { - PathTask pathTask = pathTaskClass.newInstance(); - pathTask.setPath(Paths.get(name)); - pathTask.execute(); - } - } catch (Exception ex) { - logger.warn("Error in HistoryMenu " + ex, ex); - } - } - }); + history.feedMenu(menu, new ActionListener() + { + @Override + public void actionPerformed (ActionEvent e) + { + try { + final String name = e.getActionCommand().trim(); + + if (!name.isEmpty()) { + PathTask pathTask = pathTaskClass.newInstance(); + pathTask.setPath(Paths.get(name)); + pathTask.execute(); + } + } catch (IllegalAccessException | + InstantiationException ex) { + logger.warn("Error in HistoryMenu " + ex, ex); + } + } + }); // Resource injection ResourceMap resource = OmrGui.getApplication().getContext().getResourceMap(resourceClass); diff --git a/src/main/org/audiveris/omr/ui/view/LocationDependent.java b/src/main/org/audiveris/omr/ui/view/LocationDependent.java index 16d772eca..14f9d06f4 100644 --- a/src/main/org/audiveris/omr/ui/view/LocationDependent.java +++ b/src/main/org/audiveris/omr/ui/view/LocationDependent.java @@ -31,9 +31,9 @@ */ public interface LocationDependent { - //~ Methods ------------------------------------------------------------------------------------ - /** Update the entity with user current location. + /** + * Update the entity with user current location. * * @param rect the user selected location */ diff --git a/src/main/org/audiveris/omr/ui/view/LocationDependentMenu.java b/src/main/org/audiveris/omr/ui/view/LocationDependentMenu.java index 72612602c..2b8f82903 100644 --- a/src/main/org/audiveris/omr/ui/view/LocationDependentMenu.java +++ b/src/main/org/audiveris/omr/ui/view/LocationDependentMenu.java @@ -37,7 +37,6 @@ public class LocationDependentMenu extends JMenu implements LocationDependent { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new LocationDependentMenu object. @@ -68,7 +67,6 @@ public LocationDependentMenu (Action action) setAction(action); } - //~ Methods ------------------------------------------------------------------------------------ @Override public void updateUserLocation (Rectangle rect) { diff --git a/src/main/org/audiveris/omr/ui/view/LogSlider.java b/src/main/org/audiveris/omr/ui/view/LogSlider.java index b38752946..05b7c5543 100644 --- a/src/main/org/audiveris/omr/ui/view/LogSlider.java +++ b/src/main/org/audiveris/omr/ui/view/LogSlider.java @@ -24,7 +24,7 @@ import org.audiveris.omr.constant.Constant; import org.audiveris.omr.constant.ConstantSet; -import java.util.Hashtable; +import java.util.Hashtable; // Obsolete but mandatory import javax.swing.JLabel; import javax.swing.JSlider; @@ -32,7 +32,6 @@ /** * Class {@code LogSlider} is a specific {@link JSlider} which handles double values * with a logarithmic scale (while normal JSlider handles only integer values). - * *

                                                                                                                                                        * As with a basic JSlider, any external entity can be notified of new slider value, by first * registering to this LogSlider via the {@link #addChangeListener} method. @@ -42,7 +41,6 @@ public class LogSlider extends JSlider { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -51,11 +49,9 @@ public class LogSlider private static final double doubleUnit = unit; // To speed up - //~ Instance fields ---------------------------------------------------------------------------- // Base of log (generally 2 or 10) private final double base; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code LogSlider} instance. * @@ -80,7 +76,7 @@ public LogSlider (int base, super(orientation, min * unit, max * unit, initial * unit); // Cache data - this.base = (double) base; + this.base = base; // Ticks super.setMajorTickSpacing(unit); @@ -97,14 +93,19 @@ public LogSlider (int base, // break; // case VERTICAL : setBorder (BorderFactory.createEmptyBorder(0,0,0,5)); // } - // Create and populate the label table - Hashtable labelTable = new Hashtable(); + // + /** + * Create and populate the label table. + * NOTA: Obsolete class Hashtable is imposed by {@link JSlider#setLabelTable(Dictionary)} + */ + Hashtable labelTable = new Hashtable<>(); for (int i = min; i <= max; i++) { labelTable.put( - Integer.valueOf(i * unit), + i * unit, new JLabel( - (i < 0) ? ("1/" + (int) expOf(-i * unit)) : ("" + (int) expOf(i * unit)))); + (i < 0) ? ("1/" + (int) expOf(-i * unit)) + : ("" + (int) expOf(i * unit)))); } setLabelTable(labelTable); @@ -114,7 +115,6 @@ public LogSlider (int base, setSnapToTicks(true); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // getDoubleValue // //----------------// @@ -182,7 +182,7 @@ public void setMinorTickSpacing (int n) //-------// private double expOf (int i) { - return Math.pow(base, (double) i / doubleUnit); + return Math.pow(base, i / doubleUnit); } //-------// @@ -193,14 +193,12 @@ private int logOf (double d) return (int) Math.rint((doubleUnit * Math.log(d)) / Math.log(base)); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer resolution = new Constant.Integer( "Values", diff --git a/src/main/org/audiveris/omr/ui/view/MouseMonitor.java b/src/main/org/audiveris/omr/ui/view/MouseMonitor.java index 735f19e44..2e56d4db0 100644 --- a/src/main/org/audiveris/omr/ui/view/MouseMonitor.java +++ b/src/main/org/audiveris/omr/ui/view/MouseMonitor.java @@ -35,7 +35,6 @@ */ public interface MouseMonitor { - //~ Methods ------------------------------------------------------------------------------------ /** * Contextual action (by right button click + control) of an diff --git a/src/main/org/audiveris/omr/ui/view/PixelFocus.java b/src/main/org/audiveris/omr/ui/view/PixelFocus.java index 7a1ca3677..532aba0e6 100644 --- a/src/main/org/audiveris/omr/ui/view/PixelFocus.java +++ b/src/main/org/audiveris/omr/ui/view/PixelFocus.java @@ -35,7 +35,6 @@ */ public interface PixelFocus { - //~ Methods ------------------------------------------------------------------------------------ /** * Focus on a rectangle diff --git a/src/main/org/audiveris/omr/ui/view/Rubber.java b/src/main/org/audiveris/omr/ui/view/Rubber.java index a367d7acf..01cac4954 100644 --- a/src/main/org/audiveris/omr/ui/view/Rubber.java +++ b/src/main/org/audiveris/omr/ui/view/Rubber.java @@ -53,7 +53,7 @@ /** * Class {@code Rubber} keeps track of nothing more than a rectangle, * to define an area of interest. - * + *

                                                                                                                                                        * The rectangle can be degenerated to a simple point, when both its width and height are zero. * Moreover, the display can be moved or resized (see the precise triggers below). *

                                                                                                                                                        @@ -64,31 +64,30 @@ * can also be modified programmatically, thanks to the {@link #resetOrigin} and * {@link #resetRectangle} methods. *

                                                                                                                                                        - * Basic mouse handling is provided in the following way :

                                                                                                                                                          - *
                                                                                                                                                        • Define the point of interest. Default trigger is to click with the Left button.
                                                                                                                                                        • - *
                                                                                                                                                        • Define the rectangle of interest. Default trigger is to keep Shift pressed when mouse - * is moved.
                                                                                                                                                        • - *
                                                                                                                                                        • Zoom the display to the area delimited by the rubber. Default trigger is Shift + - * Control when mouse is released.
                                                                                                                                                        • - *
                                                                                                                                                        • Drag the component itself. Default trigger is when both Left + Right buttons are - * dragged.
                                                                                                                                                        - *

                                                                                                                                                        + * Basic mouse handling is provided in the following way : + *

                                                                                                                                                          + *
                                                                                                                                                        • Define the point of interest. Default trigger is to click with the Left button.
                                                                                                                                                        • + *
                                                                                                                                                        • Define the rectangle of interest. Default trigger is to keep Shift pressed when mouse + * is moved.
                                                                                                                                                        • + *
                                                                                                                                                        • Zoom the display to the area delimited by the rubber. Default trigger is Shift + + * Control when mouse is released.
                                                                                                                                                        • + *
                                                                                                                                                        • Drag the component itself. Default trigger is when both Left + Right buttons are + * dragged.
                                                                                                                                                        • + *
                                                                                                                                                        * Note: Actual triggers are defined by protected predicate methods that can be redefined in a * subclass. *

                                                                                                                                                        - * Mouse Events are handled in the following way:

                                                                                                                                                          - * - *
                                                                                                                                                        • Low-level events originate from a JComponent, where the Rubber is registered as a + * Mouse Events are handled in the following way: + *
                                                                                                                                                            + *
                                                                                                                                                          • Low-level events originate from a JComponent, where the Rubber is registered as a * MouseListener and a MouseMotionListener. The component can be linked by the Rubber constructor, * or later by using the {@link #connectComponent} method. Rubber is then called on its * mouseDragged, mousePressed, mouseReleased methods. - * - *
                                                                                                                                                          • High-level events, as computed by Rubber from low-level mouse events, are forwarded + *
                                                                                                                                                          • High-level events, as computed by Rubber from low-level mouse events, are forwarded * to a connected {@link MouseMonitor} if any, which is then called on its pointSelected, * pointAdded, contextSelected, rectangleSelected, rectangleZoomed methods. Generally, this * MouseMonitor is the originating JComponent, but this is not mandatory. *
                                                                                                                                                          - *

                                                                                                                                                          * The Rubber can be linked to a {@link Zoom} to cope with display factor of the related component, * but this is not mandatory: If no zoom is connected, a display factor of 1.0 is assumed. * @@ -97,7 +96,6 @@ public class Rubber extends MouseInputAdapter { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); @@ -112,7 +110,6 @@ public class Rubber private static final double factor = Math.pow(base, 1d / intervals); - //~ Instance fields ---------------------------------------------------------------------------- /** View from which the rubber will receive physical mouse events. */ protected JComponent component; @@ -137,7 +134,6 @@ public class Rubber // To ease debugging private final int id; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a rubber, with no predefined parameter (zoom, component) which are meant * to be provided later. @@ -174,7 +170,6 @@ public Rubber (JComponent component, setZoom(zoom); } - //~ Methods ------------------------------------------------------------------------------------ //------------------// // connectComponent // //------------------// @@ -183,7 +178,7 @@ public Rubber (JComponent component, * * @param component the related component */ - public void connectComponent (JComponent component) + public final void connectComponent (JComponent component) { // Clean up if needed disconnectComponent(this.component); @@ -262,8 +257,7 @@ public void mouseDragged (MouseEvent e) (vr.y + rawRect.y) - e.getY(), vr.width, vr.height); - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () @@ -489,7 +483,7 @@ public void render (Graphics unscaledGraphics) zoom.scale(v); } - final Stroke oldStroke = UIUtil.setAbsoluteStroke(g, 1f); + UIUtil.setAbsoluteStroke(g, 1f); g.setColor(Color.BLACK); g.draw(v); } @@ -561,11 +555,11 @@ public void setMouseMonitor (MouseMonitor mouseMonitor) /** * Allows to specify that a zoom is attached to the displayed * component, and thus the reported rectangle or center must be - * dezoomed on the fly. + * de-zoomed on the fly. * * @param zoom the component related zoom */ - public void setZoom (Zoom zoom) + public final void setZoom (Zoom zoom) { this.zoom = zoom; } @@ -737,14 +731,12 @@ private void updateSize (MouseEvent e) component.repaint(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean showCross = new Constant.Boolean( true, diff --git a/src/main/org/audiveris/omr/ui/view/RubberPanel.java b/src/main/org/audiveris/omr/ui/view/RubberPanel.java index a2d70c12c..02d210584 100644 --- a/src/main/org/audiveris/omr/ui/view/RubberPanel.java +++ b/src/main/org/audiveris/omr/ui/view/RubberPanel.java @@ -26,9 +26,7 @@ import org.audiveris.omr.ui.selection.LocationEvent; import org.audiveris.omr.ui.selection.MouseMovement; import org.audiveris.omr.ui.selection.SelectionHint; - import static org.audiveris.omr.ui.selection.SelectionHint.*; - import org.audiveris.omr.ui.selection.SelectionService; import org.audiveris.omr.ui.selection.UserEvent; import org.audiveris.omr.util.ClassUtil; @@ -75,13 +73,11 @@ public class RubberPanel extends JPanel implements ChangeListener, MouseMonitor, EventSubscriber { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(RubberPanel.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Current display zoom, if any. */ protected Zoom zoom; @@ -94,7 +90,6 @@ public class RubberPanel /** Location Service if any (for Location event). */ protected SelectionService locationService; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a bare RubberPanel, assuming zoom and rubber will be assigned later. */ @@ -120,7 +115,6 @@ public RubberPanel (Zoom zoom, logger.debug("new RubberPanel zoom={} rubber={}", zoom, rubber); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // contextAdded // //--------------// @@ -158,6 +152,19 @@ public Dimension getModelSize () } } + //--------------// + // setModelSize // + //--------------// + /** + * Assign the size of the model object, that is the un-scaled size. + * + * @param modelSize the model size to use + */ + public void setModelSize (Dimension modelSize) + { + this.modelSize = new Dimension(modelSize); + } + //----------------// // getPanelCenter // //----------------// @@ -245,6 +252,27 @@ public Zoom getZoom () return zoom; } + //---------// + // setZoom // + //---------// + /** + * Assign a zoom to this panel + * + * @param zoom the zoom assigned + */ + public final void setZoom (final Zoom zoom) + { + // Clean up if needed + unsetZoom(this.zoom); + + this.zoom = zoom; + + if (zoom != null) { + // Add a listener on this zoom + zoom.addChangeListener(this); + } + } + //---------// // onEvent // //---------// @@ -295,6 +323,11 @@ public void pointSelected (Point pt, //---------// // publish // //---------// + /** + * Publish the provided location event on the proper service. + * + * @param locationEvent the provided location event + */ public void publish (LocationEvent locationEvent) { locationService.publish(locationEvent); @@ -325,15 +358,14 @@ public void rectangleZoomed (final Rectangle rect, showFocusLocation(rect, true); // Then, adjust zoom ratio to fit the rectangle size - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () { Rectangle vr = getVisibleRect(); - double zoomX = (double) vr.width / (double) rect.width; - double zoomY = (double) vr.height / (double) rect.height; + double zoomX = vr.width / (double) rect.width; + double zoomY = vr.height / (double) rect.height; zoom.setRatio(Math.min(zoomX, zoomY)); } }); @@ -384,19 +416,6 @@ public void setLocationService (SelectionService locationService) this.locationService = locationService; } - //--------------// - // setModelSize // - //--------------// - /** - * Assign the size of the model object, that is the un-scaled size. - * - * @param modelSize the model size to use - */ - public void setModelSize (Dimension modelSize) - { - this.modelSize = new Dimension(modelSize); - } - //-----------// // setRubber // //-----------// @@ -415,33 +434,12 @@ public final void setRubber (Rubber rubber) rubber.setMouseMonitor(this); } - //---------// - // setZoom // - //---------// - /** - * Assign a zoom to this panel - * - * @param zoom the zoom assigned - */ - public final void setZoom (final Zoom zoom) - { - // Clean up if needed - unsetZoom(this.zoom); - - this.zoom = zoom; - - if (zoom != null) { - // Add a listener on this zoom - zoom.addChangeListener(this); - } - } - //-------------------// // showFocusLocation // //-------------------// /** * Update the display, so that the location rectangle gets visible. - * + *

                                                                                                                                                          * NOTA: Subclasses that override this method should call this * super implementation or the display will not be updated by default. * @@ -628,6 +626,11 @@ protected final void paintComponent (Graphics initialGraphics) //---------------------// // handleLocationEvent // //---------------------// + /** + * Handle the provided location event + * + * @param locationEvent the location event to process + */ protected void handleLocationEvent (LocationEvent locationEvent) { // Location => move view focus on this location w/ markers @@ -706,14 +709,12 @@ protected void setFocusLocation (Rectangle rect, } } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final PixelCount focusMargin = new PixelCount(20, "Margin visible around a focus"); } diff --git a/src/main/org/audiveris/omr/ui/view/ScrollView.java b/src/main/org/audiveris/omr/ui/view/ScrollView.java index 48d86aab6..e4ffb4f02 100644 --- a/src/main/org/audiveris/omr/ui/view/ScrollView.java +++ b/src/main/org/audiveris/omr/ui/view/ScrollView.java @@ -44,10 +44,12 @@ * Class {@code ScrollView} contains a JScrollPane, which provides a comprehensive * combination of the following entities. *

                                                                                                                                                          - *
                                                                                                                                                          view:
                                                                                                                                                          the display of a {@link RubberPanel}, a component potentially + *
                                                                                                                                                          view:
                                                                                                                                                          + *
                                                                                                                                                          the display of a {@link RubberPanel}, a component potentially * wired to a {@link Zoom} and a mouse adapter {@link Rubber}
                                                                                                                                                          - *
                                                                                                                                                          zoom:
                                                                                                                                                          the {@link Zoom} whose ratio is to be used when the component is - * rendered
                                                                                                                                                          + *
                                                                                                                                                          zoom:
                                                                                                                                                          + *
                                                                                                                                                          the {@link Zoom} whose ratio is to be used when the component is + * rendered
                                                                                                                                                          *
                                                                                                                                                          * * @author Hervé Bitteur @@ -55,20 +57,17 @@ */ public class ScrollView { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(ScrollView.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Current view inside the scrolled pane. */ protected RubberPanel view; /** The concrete UI component. */ private final JScrollPane component = new JScrollPane(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a bare view pane. * Other related entities, such as view, pixel monitor or zoom, can be added later @@ -99,7 +98,6 @@ public ScrollView (RubberPanel view) setView(view); } - //~ Methods ------------------------------------------------------------------------------------ //-----------// // fitHeight // //-----------// @@ -247,7 +245,7 @@ public RubberPanel getView () * * @param view the pre-built panel */ - public void setView (RubberPanel view) + public final void setView (RubberPanel view) { // Display the view in the scrollpane component.setViewportView(view); @@ -313,25 +311,9 @@ private void bindKeys (JComponent component) actionMap.put("ShiftRightAction", new ShiftRightAction()); } - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // Constants // - //-----------// - private static final class Constants - extends ConstantSet - { - //~ Instance fields ------------------------------------------------------------------------ - - private final Constant.Integer unitIncrement = new Constant.Integer( - "Pixels", - 20, - "Size of mouse wheel increment for ScrollView"); - } - private class DownAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -339,12 +321,18 @@ public void actionPerformed (ActionEvent e) final JScrollBar vertical = component.getVerticalScrollBar(); vertical.setValue(vertical.getValue() + vertical.getUnitIncrement()); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } private class LeftAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -352,12 +340,18 @@ public void actionPerformed (ActionEvent e) final JScrollBar horizontal = component.getHorizontalScrollBar(); horizontal.setValue(horizontal.getValue() - horizontal.getUnitIncrement()); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } private class RightAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -365,12 +359,18 @@ public void actionPerformed (ActionEvent e) final JScrollBar horizontal = component.getHorizontalScrollBar(); horizontal.setValue(horizontal.getValue() + horizontal.getUnitIncrement()); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } private class ShiftDownAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -378,12 +378,18 @@ public void actionPerformed (ActionEvent e) final JScrollBar vertical = component.getVerticalScrollBar(); vertical.setValue(vertical.getValue() + 1); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } private class ShiftLeftAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -391,12 +397,18 @@ public void actionPerformed (ActionEvent e) final JScrollBar horizontal = component.getHorizontalScrollBar(); horizontal.setValue(horizontal.getValue() - 1); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } private class ShiftRightAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -404,12 +416,18 @@ public void actionPerformed (ActionEvent e) final JScrollBar horizontal = component.getHorizontalScrollBar(); horizontal.setValue(horizontal.getValue() + 1); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } private class ShiftUpAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -417,12 +435,18 @@ public void actionPerformed (ActionEvent e) final JScrollBar vertical = component.getVerticalScrollBar(); vertical.setValue(vertical.getValue() - 1); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } } private class UpAction extends AbstractAction { - //~ Methods -------------------------------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) @@ -430,5 +454,25 @@ public void actionPerformed (ActionEvent e) final JScrollBar vertical = component.getVerticalScrollBar(); vertical.setValue(vertical.getValue() - vertical.getUnitIncrement()); } + + @Override + public Object clone () + throws CloneNotSupportedException + { + return super.clone(); //To change body of generated methods, choose Tools | Templates. + } + } + + //-----------// + // Constants // + //-----------// + private static class Constants + extends ConstantSet + { + + private final Constant.Integer unitIncrement = new Constant.Integer( + "Pixels", + 20, + "Size of mouse wheel increment for ScrollView"); } } diff --git a/src/main/org/audiveris/omr/ui/view/Zoom.java b/src/main/org/audiveris/omr/ui/view/Zoom.java index b8ad89f64..55fb25d20 100644 --- a/src/main/org/audiveris/omr/ui/view/Zoom.java +++ b/src/main/org/audiveris/omr/ui/view/Zoom.java @@ -33,6 +33,7 @@ import java.awt.geom.Line2D; import java.util.LinkedHashSet; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -41,7 +42,7 @@ * Class {@code Zoom} encapsulates a zoom ratio, which is typically the ratio between * display values (such as the size of the display of an entity) and model values * (such as the size of the entity itself). - * + *

                                                                                                                                                          * For example, a Zoom with ratio set to a 2.0 value would double the display of a given entity. *

                                                                                                                                                          * Since this class is meant to be used when handling display tasks, it also provides utility @@ -61,32 +62,29 @@ */ public class Zoom { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); private static final Logger logger = LoggerFactory.getLogger(Zoom.class); - // To assign a unique Id - private static int globalId; + /** To assign a unique Id. */ + private static AtomicInteger globalId = new AtomicInteger(0); - //~ Instance fields ---------------------------------------------------------------------------- - /** Unique event, created lazily */ + /** Unique event, created lazily. */ protected ChangeEvent changeEvent = null; - /** Potential logarithmic slider to drive this zoom */ + /** Potential logarithmic slider to drive this zoom. */ protected LogSlider slider; - /** Collection of event listeners */ - protected Set listeners = new LinkedHashSet(); + /** Collection of event listeners. */ + protected Set listeners = new LinkedHashSet<>(); - /** Current ratio value */ + /** Current ratio value. */ protected double ratio; - // Unique Id (to ease debugging) - private int id = ++globalId; + /** Unique Id (to ease debugging). */ + private final int id = globalId.incrementAndGet(); - //~ Constructors ------------------------------------------------------------------------------- /** * Create a zoom entity, with a default ratio value of 1. */ @@ -121,7 +119,6 @@ public Zoom (LogSlider slider, setRatio(ratio); } - //~ Methods ------------------------------------------------------------------------------------ //-------------------// // addChangeListener // //-------------------// @@ -169,6 +166,29 @@ public double getRatio () return ratio; } + //----------// + // setRatio // + //----------// + /** + * Change the display zoom ratio. Nota, if the zoom is coupled with a + * slider, this slider has the final word concerning the precise zoom + * value, since the slider uses integer (or fractional) values. + * + * @param ratio the new ratio + */ + public final void setRatio (double ratio) + { + logger.debug("setRatio ratio={}", ratio); + + // Propagate to slider (useful to keep slider in sync when ratio is + // set programmatically) + if (slider != null) { + slider.setDoubleValue(ratio); + } else { + forceRatio(ratio); + } + } + //----------------------// // removeChangeListener // //----------------------// @@ -251,7 +271,6 @@ public void scale (Line2D line) * Coordinate computation, Source → Display * * @param val a source value - * * @return the (scaled) display value */ public int scaled (double val) @@ -266,7 +285,6 @@ public int scaled (double val) * Coordinate computation, Source → Display * * @param pt source point - * * @return the corresponding (scaled) point */ public Point scaled (Point pt) @@ -284,7 +302,6 @@ public Point scaled (Point pt) * Coordinate computation, Source → Display * * @param dim source dimension - * * @return the corresponding (scaled) dimension */ public Dimension scaled (Dimension dim) @@ -302,7 +319,6 @@ public Dimension scaled (Dimension dim) * Coordinate computation, Source → Display * * @param rect source rectangle - * * @return the corresponding (scaled) rectangle */ public Rectangle scaled (Rectangle rect) @@ -313,29 +329,6 @@ public Rectangle scaled (Rectangle rect) return r; } - //----------// - // setRatio // - //----------// - /** - * Change the display zoom ratio. Nota, if the zoom is coupled with a - * slider, this slider has the final word concerning the precise zoom - * value, since the slider uses integer (or fractional) values. - * - * @param ratio the new ratio - */ - public void setRatio (double ratio) - { - logger.debug("setRatio ratio={}", ratio); - - // Propagate to slider (useful to keep slider in sync when ratio is - // set programmatically) - if (slider != null) { - slider.setDoubleValue(ratio); - } else { - forceRatio(ratio); - } - } - //-----------// // setSlider // //-----------// @@ -345,7 +338,7 @@ public void setRatio (double ratio) * * @param slider the related slider UI */ - public void setSlider (final LogSlider slider) + public final void setSlider (final LogSlider slider) { this.slider = slider; logger.debug("setSlider"); @@ -354,15 +347,14 @@ public void setSlider (final LogSlider slider) slider.setFocusable(false); slider.setDoubleValue(ratio); - slider.addChangeListener( - new ChangeListener() + slider.addChangeListener(new ChangeListener() { @Override public void stateChanged (ChangeEvent e) { // Forward the new zoom ratio - if (constants.continuousSliderReading.getValue() - || !slider.getValueIsAdjusting()) { + if (constants.continuousSliderReading.getValue() || !slider + .getValueIsAdjusting()) { double newRatio = slider.getDoubleValue(); logger.debug("Slider firing zoom newRatio={}", newRatio); @@ -480,14 +472,12 @@ private void forceRatio (double ratio) fireStateChanged(); } - //~ Inner Classes ------------------------------------------------------------------------------ //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean continuousSliderReading = new Constant.Boolean( true, diff --git a/src/main/org/audiveris/omr/ui/view/ZoomAssembly.java b/src/main/org/audiveris/omr/ui/view/ZoomAssembly.java index 870157933..fc5e906aa 100644 --- a/src/main/org/audiveris/omr/ui/view/ZoomAssembly.java +++ b/src/main/org/audiveris/omr/ui/view/ZoomAssembly.java @@ -36,7 +36,6 @@ */ public class ZoomAssembly { - //~ Instance fields ---------------------------------------------------------------------------- /** The concrete UI component. */ protected final Panel component = new Panel(); @@ -50,7 +49,6 @@ public class ZoomAssembly /** Mouse adapter. */ protected final Rubber rubber = new Rubber(zoom); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ZoomAssembly} object. */ @@ -68,7 +66,6 @@ public ZoomAssembly () inputMap.put(KeyStroke.getKeyStroke("DOWN"), "none"); } - //~ Methods ------------------------------------------------------------------------------------ //--------------// // getComponent // //--------------// diff --git a/src/main/org/audiveris/omr/util/AbstractEntity.java b/src/main/org/audiveris/omr/util/AbstractEntity.java index 3378fbc4f..d48e62d58 100644 --- a/src/main/org/audiveris/omr/util/AbstractEntity.java +++ b/src/main/org/audiveris/omr/util/AbstractEntity.java @@ -41,13 +41,9 @@ public abstract class AbstractEntity implements Entity { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - AbstractEntity.class); + private static final Logger logger = LoggerFactory.getLogger(AbstractEntity.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // @@ -63,7 +59,6 @@ public abstract class AbstractEntity /** (Debug) flag this as VIP. */ protected boolean vip; - //~ Methods ------------------------------------------------------------------------------------ //--------// // dumpOf // //--------// @@ -83,21 +78,21 @@ public int getId () } //-------// - // isVip // + // setId // //-------// @Override - public boolean isVip () + public void setId (int id) { - return vip; + this.id = id; } //-------// - // setId // + // isVip // //-------// @Override - public void setId (int id) + public boolean isVip () { - this.id = id; + return vip; } //--------// @@ -136,6 +131,11 @@ public String toString () //-----------// // internals // //-----------// + /** + * Report description of object internals. + * + * @return internals description + */ protected String internals () { return ""; diff --git a/src/main/org/audiveris/omr/util/BasicIndex.java b/src/main/org/audiveris/omr/util/BasicIndex.java index 247dcc892..c0ed7d651 100644 --- a/src/main/org/audiveris/omr/util/BasicIndex.java +++ b/src/main/org/audiveris/omr/util/BasicIndex.java @@ -21,7 +21,7 @@ // package org.audiveris.omr.util; -import org.audiveris.omr.glyph.BasicGlyph; +import org.audiveris.omr.glyph.Glyph; import org.audiveris.omr.ui.selection.EntityService; import org.audiveris.omr.ui.symbol.BasicSymbol; @@ -54,31 +54,24 @@ * Class {@code BasicIndex} * * @param precise type for indexed entities - * * @author Hervé Bitteur */ @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement -@XmlType(propOrder = { - "lastIdValue", "entities"} -) +@XmlType(propOrder = {"lastIdValue", "entities"}) public class BasicIndex implements EntityIndex { - //~ Static fields/initializers ----------------------------------------------------------------- - private static final Logger logger = LoggerFactory.getLogger( - BasicIndex.class); + private static final Logger logger = LoggerFactory.getLogger(BasicIndex.class); - //~ Instance fields ---------------------------------------------------------------------------- - // // Persistent data //---------------- // /** Collection of all entities registered in this index, sorted on ID. */ @XmlElement(name = "entities") @XmlJavaTypeAdapter(Adapter.class) - protected final ConcurrentSkipListMap entities = new ConcurrentSkipListMap(); + protected final ConcurrentSkipListMap entities = new ConcurrentSkipListMap<>(); // Transient data //--------------- @@ -95,7 +88,6 @@ public class BasicIndex /** (debug) for easy inspection via browser. */ private Collection values; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code BasicIndex} object. * @@ -115,7 +107,6 @@ protected BasicIndex () values = entities.values(); // Useful for debugging only } - //~ Methods ------------------------------------------------------------------------------------ //----------------------// // getContainedEntities // //----------------------// @@ -161,6 +152,16 @@ public EntityService getEntityService () return entityService; } + //------------------// + // setEntityService // + //------------------// + @Override + public void setEntityService (EntityService entityService) + { + this.entityService = entityService; + entityService.connect(); + } + //------------// // getIdAfter // //------------// @@ -222,6 +223,15 @@ public int getLastId () return lastId.get(); } + //-----------// + // setLastId // + //-----------// + @Override + public void setLastId (int lastId) + { + this.lastId.set(lastId); + } + //---------// // getName // //---------// @@ -317,28 +327,14 @@ public void reset () entities.clear(); } - //------------------// - // setEntityService // - //------------------// - @Override - public void setEntityService (EntityService entityService) - { - this.entityService = entityService; - entityService.connect(); - } - - //-----------// - // setLastId // - //-----------// - @Override - public void setLastId (int lastId) - { - this.lastId.set(lastId); - } - //-----------// // setVipIds // //-----------// + /** + * Record the IDs of VIP entities. + * + * @param vipIds collection of VIP IDs + */ public void setVipIds (List vipIds) { this.vipIds = vipIds; @@ -361,6 +357,11 @@ public String toString () //------------// // generateId // //------------// + /** + * Report the next available ID value. + * + * @return next available ID + */ protected int generateId () { return lastId.incrementAndGet(); @@ -369,6 +370,11 @@ protected int generateId () //-----------// // internals // //-----------// + /** + * Report a description string of class internals. + * + * @return description string of internals + */ protected String internals () { return getName(); @@ -402,14 +408,15 @@ private void afterUnmarshal (Unmarshaller um, values = entities.values(); } - //~ Inner Classes ------------------------------------------------------------------------------ //------------------// // InterfaceAdapter // //------------------// + /** + * @param precise entity type + */ public static class InterfaceAdapter extends XmlAdapter, EntityIndex> { - //~ Methods -------------------------------------------------------------------------------- @Override public BasicIndex marshal (EntityIndex itf) @@ -438,14 +445,13 @@ public EntityIndex unmarshal (BasicIndex basic) private static class Adapter extends XmlAdapter, ConcurrentSkipListMap> { - //~ Methods -------------------------------------------------------------------------------- @Override public IndexValue marshal (ConcurrentSkipListMap map) throws Exception { - IndexValue value = new IndexValue(); - value.list = new ArrayList(map.values()); + IndexValue value = new IndexValue<>(); + value.list = new ArrayList<>(map.values()); return value; } @@ -455,19 +461,17 @@ public ConcurrentSkipListMap unmarshal (IndexValue value) throws Exception { // TODO: is sorting needed? - Collections.sort( - value.list, - new Comparator() - { - @Override - public int compare (E e1, - E e2) - { - return Integer.compare(e1.getId(), e2.getId()); - } - }); - - ConcurrentSkipListMap map = new ConcurrentSkipListMap(); + Collections.sort(value.list, new Comparator() + { + @Override + public int compare (E e1, + E e2) + { + return Integer.compare(e1.getId(), e2.getId()); + } + }); + + ConcurrentSkipListMap map = new ConcurrentSkipListMap<>(); for (E entity : value.list) { map.put(entity.getId(), entity); @@ -488,12 +492,10 @@ public int compare (E e1, */ private static class IndexValue { - //~ Instance fields ------------------------------------------------------------------------ @XmlElementRefs({ - @XmlElementRef(type = BasicGlyph.class) - , @XmlElementRef(type = BasicSymbol.class) - }) + @XmlElementRef(type = Glyph.class), + @XmlElementRef(type = BasicSymbol.class)}) ArrayList list; // Flat list of entities (each with its embedded id) } } diff --git a/src/main/org/audiveris/omr/util/BasicTask.java b/src/main/org/audiveris/omr/util/BasicTask.java index cc2c7b756..2d73eb5d3 100644 --- a/src/main/org/audiveris/omr/util/BasicTask.java +++ b/src/main/org/audiveris/omr/util/BasicTask.java @@ -28,17 +28,15 @@ /** * Class {@code BasicTask} is an Application Framework Task for Audiveris application. * - * @author Hervé Bitteur - * * @param the result type returned by this {@code SwingWorker's} * {@code doInBackground} and {@code get} methods * @param the type used for carrying out intermediate results by this * {@code SwingWorker's} {@code publish} and {@code process} methods + * @author Hervé Bitteur */ public abstract class BasicTask extends Task { - //~ Constructors ------------------------------------------------------------------------------- /** * Audiveris application is injected into this task. diff --git a/src/main/org/audiveris/omr/util/BrokenLine.java b/src/main/org/audiveris/omr/util/BrokenLine.java index 0acaa7261..1a798391b 100644 --- a/src/main/org/audiveris/omr/util/BrokenLine.java +++ b/src/main/org/audiveris/omr/util/BrokenLine.java @@ -43,11 +43,11 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** * Class {@code BrokenLine} handles the broken line defined by a sequence of points * which can be modified at any time. - * *

                                                                                                                                                          * This class make use of several distance parameters, presented here from smaller to larger: *

                                                                                                                                                          @@ -66,7 +66,8 @@ *

                                                                                                                                                          * Nota: Internal reference points data can still be modified at any time, since the * BrokenLine, just like a List, merely handles points pointers. - * For example, to move a point, just call point.setLocation() method.

                                                                                                                                                          + * For example, to move a point, just call point.setLocation() method. + *

                                                                                                                                                          *

                                                                                                                                                          * This ability of dynamic modification is the main reason why this class is not simply implemented * as a Path2D. If a Path2D instance is needed, use the {@link #toGeoPath()} conversion method. @@ -78,20 +79,16 @@ @XmlRootElement(name = "broken-line") public class BrokenLine { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Constants constants = new Constants(); - private static final Logger logger = LoggerFactory.getLogger( - BrokenLine.class); + private static final Logger logger = LoggerFactory.getLogger(BrokenLine.class); - //~ Instance fields ---------------------------------------------------------------------------- - // - /** The ordered sequence of points */ + /** The ordered sequence of points. */ @XmlElement(name = "point") - private final List points = new ArrayList(); + @XmlJavaTypeAdapter(Jaxb.PointAdapter.class) + private final List points = new ArrayList<>(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new BrokenLine object with an initially empty sequence of points. */ @@ -119,33 +116,6 @@ public BrokenLine (Collection points) resetPoints(points); } - //~ Methods ------------------------------------------------------------------------------------ - //---------------------// - // getDraggingDistance // - //---------------------// - /** - * Report the dragging distance. - * - * @return the dragging distance, specified in pixels - */ - public static int getDraggingDistance () - { - return constants.draggingDistance.getValue(); - } - - //-------------------// - // getStickyDistance // - //-------------------// - /** - * Report the maximum distance (from a point, from a segment). - * - * @return the maximum distance, specified in pixels - */ - public static int getStickyDistance () - { - return constants.stickyDistance.getValue(); - } - //----------// // addPoint // //----------// @@ -370,7 +340,6 @@ public void removePoint (Point point) points.remove(point); } - // //-------------// // resetPoints // //-------------// @@ -437,14 +406,38 @@ public String toString () return "{BrokenLine " + getSequenceString() + "}"; } - //~ Inner Classes ------------------------------------------------------------------------------ + //---------------------// + // getDraggingDistance // + //---------------------// + /** + * Report the dragging distance. + * + * @return the dragging distance, specified in pixels + */ + public static int getDraggingDistance () + { + return constants.draggingDistance.getValue(); + } + + //-------------------// + // getStickyDistance // + //-------------------// + /** + * Report the maximum distance (from a point, from a segment). + * + * @return the maximum distance, specified in pixels + */ + public static int getStickyDistance () + { + return constants.stickyDistance.getValue(); + } + //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Integer colinearDistance = new Constant.Integer( "Pixels", diff --git a/src/main/org/audiveris/omr/util/ByteUtil.java b/src/main/org/audiveris/omr/util/ByteUtil.java index 68cd7b75c..2b6ac6bab 100644 --- a/src/main/org/audiveris/omr/util/ByteUtil.java +++ b/src/main/org/audiveris/omr/util/ByteUtil.java @@ -32,7 +32,10 @@ */ public abstract class ByteUtil { - //~ Methods ------------------------------------------------------------------------------------ + + private ByteUtil () + { + } /** * Fill the ByteProcessor with provided value. diff --git a/src/main/org/audiveris/omr/util/ChartPlotter.java b/src/main/org/audiveris/omr/util/ChartPlotter.java index a6048a9a3..a091b12d0 100644 --- a/src/main/org/audiveris/omr/util/ChartPlotter.java +++ b/src/main/org/audiveris/omr/util/ChartPlotter.java @@ -42,12 +42,10 @@ */ public class ChartPlotter { - //~ Static fields/initializers ----------------------------------------------------------------- /** Height of marks below zero line. */ - public static double MARK = 2.5; + public static final double MARK = 2.5; - //~ Instance fields ---------------------------------------------------------------------------- /** Collection of series. */ protected final XYSeriesCollection dataset = new XYSeriesCollection(); @@ -57,7 +55,6 @@ public class ChartPlotter /** Line and item renderer. */ protected final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code ChartPlotter} object. * @@ -84,7 +81,6 @@ public ChartPlotter (String title, plot.setRenderer(renderer); } - //~ Methods ------------------------------------------------------------------------------------ //-----// // add // //-----// diff --git a/src/main/org/audiveris/omr/util/ClassUtil.java b/src/main/org/audiveris/omr/util/ClassUtil.java index da39c877b..05fa2eb07 100644 --- a/src/main/org/audiveris/omr/util/ClassUtil.java +++ b/src/main/org/audiveris/omr/util/ClassUtil.java @@ -33,11 +33,13 @@ */ public abstract class ClassUtil { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(ClassUtil.class); - //~ Methods ------------------------------------------------------------------------------------ + private ClassUtil () + { + } + //-----------------// // getCallingFrame // //-----------------// diff --git a/src/main/org/audiveris/omr/util/Clock.java b/src/main/org/audiveris/omr/util/Clock.java index beb884d7d..cc65f3796 100644 --- a/src/main/org/audiveris/omr/util/Clock.java +++ b/src/main/org/audiveris/omr/util/Clock.java @@ -35,7 +35,6 @@ */ public abstract class Clock { - //~ Static fields/initializers ----------------------------------------------------------------- /** To have a reference time */ private static long startTime = System.currentTimeMillis(); @@ -46,8 +45,11 @@ public abstract class Clock DateFormat.FULL, Locale.US); - /** General time formatting. Locale to be used, could be: //Locale.US; - * //Locale.FRANCE; */ + /** + * General time formatting. + *

                                                                                                                                                          + * Locale to be used, could be: Locale.US; Locale.FRANCE; + */ private static final Locale locale = Locale.getDefault(); /** Corresponding number format */ @@ -60,23 +62,21 @@ public abstract class Clock timeFormatter.applyPattern("000,000.00"); } - //~ Constructors ------------------------------------------------------------------------------- /** To prevent instantiation. */ private Clock () { } - //~ Methods ------------------------------------------------------------------------------------ //---------// // getDate // //---------// /** - * Retrieves the values of current date and time of day, and formats a + * Retrieve the values of current date and time of day, and formats a * standard string, * * @return A standardized date + time string */ - public static String getDate () + public static synchronized String getDate () { return dateFormatter.format(new Date()); } @@ -85,10 +85,11 @@ public static String getDate () // getElapsed // //------------// /** - * Retrieves the number of milliseconds since the reference start time, and - * formats a standardized string using seconds and milliseconds. NB: The - * start time is usually the time when this class was elaborated. It can - * also be later reset, via the 'reset' method. + * Retrieve the number of milliseconds since the reference start time, and + * formats a standardized string using seconds and milliseconds. + *

                                                                                                                                                          + * NB: The start time is usually the time when this class was elaborated. + * It can also be later reset, via the 'reset' method. * * @return A standardized duration string */ @@ -96,14 +97,14 @@ public static String getElapsed () { long delta = System.currentTimeMillis() - startTime; - return timeFormatter.format((double) delta / 1000); + return timeFormatter.format((double) delta / 1_000); } //-----------// // resetTime // //-----------// /** - * Resets the reference start value at the time this method is called. + * Reset the reference start value at the time this method is called. */ public static void resetTime () { diff --git a/src/main/org/audiveris/omr/util/Concurrency.java b/src/main/org/audiveris/omr/util/Concurrency.java index c6d55fe42..9c221340d 100644 --- a/src/main/org/audiveris/omr/util/Concurrency.java +++ b/src/main/org/audiveris/omr/util/Concurrency.java @@ -30,12 +30,11 @@ */ public interface Concurrency { - //~ Methods ------------------------------------------------------------------------------------ /** - * Report whether the entity can be used concurrently + * Report whether the entity can be used concurrently. * - * @return true if thread safe, false otherwise + * @return true if so */ boolean isThreadSafe (); } diff --git a/src/main/org/audiveris/omr/util/Corner.java b/src/main/org/audiveris/omr/util/Corner.java index c58dce29f..917fab4b6 100644 --- a/src/main/org/audiveris/omr/util/Corner.java +++ b/src/main/org/audiveris/omr/util/Corner.java @@ -35,7 +35,6 @@ */ public class Corner { - //~ Static fields/initializers ----------------------------------------------------------------- public static final Corner TOP_LEFT = new Corner(TOP, LEFT); @@ -52,14 +51,12 @@ public class Corner TOP_LEFT, BOTTOM_RIGHT); - //~ Instance fields ---------------------------------------------------------------------------- /** The vertical side. */ public final VerticalSide vSide; /** The horizontal side. */ public final HorizontalSide hSide; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Corner object. * @@ -73,12 +70,21 @@ private Corner (VerticalSide vSide, this.hSide = hSide; } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Report the corner integer ID. + * + * @return id + */ public int getId () { return values.indexOf(this); } + /** + * Report the corresponding anchor for this corner. + * + * @return stem-based anchor + */ public Anchor stemAnchor () { if (this == TOP_LEFT) { diff --git a/src/main/org/audiveris/omr/util/Dumper.java b/src/main/org/audiveris/omr/util/Dumper.java index e0da722e5..3998a563f 100644 --- a/src/main/org/audiveris/omr/util/Dumper.java +++ b/src/main/org/audiveris/omr/util/Dumper.java @@ -35,28 +35,26 @@ * When used on a class instance, all class internal fields which are considered as "relevant" are * printed using their toString() method, then we walk up the inheritance tree and repeat the same * actions, until there is no more superclass or until the superclass we have reached is considered - * as non-relevant.

                                                                                                                                                          + * as non-relevant. *

                                                                                                                                                          * A (super)class is considered "relevant" if the static method {@code isClassRelevant(class)} - * returns true. This method can be overridden in a subclass of Dumper to adapt to local needs.

                                                                                                                                                          + * returns true. This method can be overridden in a subclass of Dumper to adapt to local needs. *

                                                                                                                                                          * A field is considered "relevant" if the following condition if the method * {@code isFieldRelevant(field)} returns true. Similarly, the behavior of this predicate can be - * customized by subclassing the Dumper class.

                                                                                                                                                          + * customized by subclassing the Dumper class. *

                                                                                                                                                          * There are several kinds of print outs available through subclassing. Each of them export two * public methods: {@code dump()} which prints the result on default output stream, and * {@code dumpOf()} which simply returns the generated dump string. - * - *

                                                                                                                                                          • Column a dump with one line per field
                                                                                                                                                          • - * - *
                                                                                                                                                          • Row a dump with all information on one row
                                                                                                                                                          • - * - *
                                                                                                                                                          • Html an Html stream with fields arranged in tables
                                                                                                                                                          • - * + *
                                                                                                                                                              + *
                                                                                                                                                            • Column: a dump with one line per field
                                                                                                                                                            • + *
                                                                                                                                                            • Row: a dump with all information on one row
                                                                                                                                                            • + *
                                                                                                                                                            • Html: an Html stream with fields arranged in tables
                                                                                                                                                            • *
                                                                                                                                                            - * + *

                                                                                                                                                            * Here are some examples of use: + * *

                                                                                                                                                              * // Using the predefined static helper methods
                                                                                                                                                              * Dumper.dump(myinstance);
                                                                                                                                                            @@ -65,7 +63,7 @@
                                                                                                                                                              * System.out.println(Dumper.dumpOf(myinstance));
                                                                                                                                                              * System.out.println(Dumper.htmlDumpOf(myinstance));
                                                                                                                                                              *
                                                                                                                                                            - *  // Using directly the Dumper subclasses
                                                                                                                                                            + * // Using directly the Dumper subclasses
                                                                                                                                                              * new Dumper.Column(myinstance).print();
                                                                                                                                                              * System.out.println(new Dumper.Row(myinstance).toString());
                                                                                                                                                              * display(new Dumper.Html(myinstance).toString());
                                                                                                                                                            @@ -75,12 +73,10 @@
                                                                                                                                                              */
                                                                                                                                                             public class Dumper
                                                                                                                                                             {
                                                                                                                                                            -    //~ Static fields/initializers -----------------------------------------------------------------
                                                                                                                                                             
                                                                                                                                                                 /** Maximum number of collection items printed */
                                                                                                                                                                 private static final int MAX_COLLECTION_INDEX = 9;
                                                                                                                                                             
                                                                                                                                                            -    //~ Instance fields ----------------------------------------------------------------------------
                                                                                                                                                                 /** To filter classes and fields */
                                                                                                                                                                 protected final Relevance relevance;
                                                                                                                                                             
                                                                                                                                                            @@ -104,7 +100,6 @@ public class Dumper
                                                                                                                                                                  */
                                                                                                                                                                 protected Class classe;
                                                                                                                                                             
                                                                                                                                                            -    //~ Constructors -------------------------------------------------------------------------------
                                                                                                                                                                 /**
                                                                                                                                                                  * Creates a new Dumper.
                                                                                                                                                                  *
                                                                                                                                                            @@ -119,7 +114,7 @@ public Dumper (Relevance relevance,
                                                                                                                                                                     this.relevance = relevance;
                                                                                                                                                             
                                                                                                                                                                     // (re)Allocate the string buffer
                                                                                                                                                            -        sb = new StringBuilder(1024);
                                                                                                                                                            +        sb = new StringBuilder(1_024);
                                                                                                                                                             
                                                                                                                                                                     // Cache the object & the related class
                                                                                                                                                                     this.object = object;
                                                                                                                                                            @@ -127,7 +122,6 @@ public Dumper (Relevance relevance,
                                                                                                                                                                     classe = object.getClass();
                                                                                                                                                                 }
                                                                                                                                                             
                                                                                                                                                            -    //~ Methods ------------------------------------------------------------------------------------
                                                                                                                                                                 //-------//
                                                                                                                                                                 // print //
                                                                                                                                                                 //-------//
                                                                                                                                                            @@ -136,7 +130,6 @@ public Dumper (Relevance relevance,
                                                                                                                                                                  */
                                                                                                                                                                 public void print ()
                                                                                                                                                                 {
                                                                                                                                                            -        System.out.println(this);
                                                                                                                                                                 }
                                                                                                                                                             
                                                                                                                                                                 //----------//
                                                                                                                                                            @@ -180,6 +173,11 @@ protected void printClassProlog ()
                                                                                                                                                                 //----------------------//
                                                                                                                                                                 // printCollectionValue //
                                                                                                                                                                 //----------------------//
                                                                                                                                                            +    /**
                                                                                                                                                            +     * Print the provided collection to the dump.
                                                                                                                                                            +     *
                                                                                                                                                            +     * @param collection the collection object to print
                                                                                                                                                            +     */
                                                                                                                                                                 protected void printCollectionValue (Collection collection)
                                                                                                                                                                 {
                                                                                                                                                                     sb.append("[");
                                                                                                                                                            @@ -323,30 +321,35 @@ private void processObject ()
                                                                                                                                                                     } while (relevance.isClassRelevant(classe));
                                                                                                                                                                 }
                                                                                                                                                             
                                                                                                                                                            -    //~ Inner Classes ------------------------------------------------------------------------------
                                                                                                                                                                 //--------//
                                                                                                                                                                 // Column //
                                                                                                                                                                 //--------//
                                                                                                                                                                 /**
                                                                                                                                                                  * Class {@code Column} implements a Dumper where all fields are
                                                                                                                                                            -     * presented in one column, each field on a separate line. The column can be
                                                                                                                                                            -     * left indented, according to the specified indentation level.
                                                                                                                                                            +     * presented in one column, each field on a separate line.
                                                                                                                                                            +     * 

                                                                                                                                                            + * The column can be left indented, according to the specified indentation level. */ public static class Column extends Dumper { - //~ Static fields/initializers ------------------------------------------------------------- private static final String MEMBER_GAP = " "; private static final String INDENT_GAP = ". "; - //~ Instance fields ------------------------------------------------------------------------ private final String title; private final StringBuilder prefix; - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a Column. + * + * @param relevance selection policy + * @param object the object to dump + * @param title title of the dump + * @param level initial indentation level + */ public Column (Relevance relevance, Object object, String title, @@ -369,12 +372,10 @@ public Column (Relevance relevance, } } - //~ Methods -------------------------------------------------------------------------------- @Override protected void printClassProlog () { - // We print the class name only for the lowest class in - // heritance hierarchy + // We print the class name only for the lowest class in heritance hierarchy if (object.getClass() == classe) { sb.append("\n"); sb.append(prefix).append(classe.getName()); @@ -403,21 +404,25 @@ protected void printField (String name, public static class Html extends Dumper { - //~ Constructors --------------------------------------------------------------------------- + /** + * Create an Html object. + * + * @param relevance selection policy + * @param object the object to dump + */ public Html (Relevance relevance, Object object) { super(relevance, object, true); } - //~ Methods -------------------------------------------------------------------------------- @Override public String toString () { // Style - sb.append(""); + sb.append(""); // Table begin sb.append(""); @@ -468,15 +473,19 @@ protected void printField (String name, public static class Row extends Dumper { - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a Row object. + * + * @param relevance selection policy + * @param object the object to dump + */ public Row (Relevance relevance, Object object) { super(relevance, object, false); } - //~ Methods -------------------------------------------------------------------------------- @Override protected void printClassEpilog () { diff --git a/src/main/org/audiveris/omr/util/Dumping.java b/src/main/org/audiveris/omr/util/Dumping.java index 55b8f3163..b65b7c0d2 100644 --- a/src/main/org/audiveris/omr/util/Dumping.java +++ b/src/main/org/audiveris/omr/util/Dumping.java @@ -27,6 +27,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Set; @@ -39,12 +40,10 @@ */ public class Dumping { - //~ Instance fields ---------------------------------------------------------------------------- /** The relevance filter to be used */ protected final Relevance relevance; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new Dumping service. * @@ -65,7 +64,6 @@ public Dumping (Package... rootPackages) relevance = new PackageRelevance(rootPackages); } - //~ Methods ------------------------------------------------------------------------------------ //------// // dump // //------// @@ -136,7 +134,6 @@ public void dump (Object obj, * Return a line which contains the whole set of internal data * * @param obj the object whose data is to be printed - * * @return the string of data values */ public String dumpOf (Object obj) @@ -152,7 +149,6 @@ public String dumpOf (Object obj) * html editor can easily render this. * * @param obj the object to dump - * * @return the HTML string */ public String htmlDumpOf (Object obj) @@ -160,13 +156,14 @@ public String htmlDumpOf (Object obj) return new Html(relevance, obj).toString(); } - //~ Inner Interfaces --------------------------------------------------------------------------- //-----------// // Relevance // //-----------// + /** + * Handles if a field or class is relevant for dumping. + */ public static interface Relevance { - //~ Methods -------------------------------------------------------------------------------- /** * Predicate to determine if a given class is worth being printed. @@ -185,35 +182,39 @@ public static interface Relevance boolean isFieldRelevant (Field field); } - //~ Inner Classes ------------------------------------------------------------------------------ //------------------// // PackageRelevance // //------------------// /** - * A relevance filter, based on root packages + * A relevance filter, based on root packages. */ public static class PackageRelevance implements Relevance { - //~ Instance fields ------------------------------------------------------------------------ - /** Collection of root packages, to filter non-relevant classes */ - protected final Set rootPackages = new LinkedHashSet(); + /** Collection of root packages, to filter non-relevant classes. */ + protected final Set rootPackages = new LinkedHashSet<>(); - //~ Constructors --------------------------------------------------------------------------- + /** + * Create a PackageRelevance from a collection of packages. + * + * @param rootPackages the roots of relevant packages + */ public PackageRelevance (Collection rootPackages) { this.rootPackages.addAll(rootPackages); } + /** + * Create a PackageRelevance from an array of packages. + * + * @param rootPackages the roots of relevant packages + */ public PackageRelevance (Package... rootPackages) { - for (Package pkg : rootPackages) { - this.rootPackages.add(pkg); - } + this.rootPackages.addAll(Arrays.asList(rootPackages)); } - //~ Methods -------------------------------------------------------------------------------- //-----------------// // isClassRelevant // //-----------------// @@ -245,11 +246,7 @@ public boolean isFieldRelevant (Field field) } // We don't print non-user visible entities - if (field.getName().indexOf('$') != -1) { - return false; - } - - return true; + return field.getName().indexOf('$') == -1; } } } diff --git a/src/main/org/audiveris/omr/util/Entities.java b/src/main/org/audiveris/omr/util/Entities.java index 7f79dd688..b11d0a64d 100644 --- a/src/main/org/audiveris/omr/util/Entities.java +++ b/src/main/org/audiveris/omr/util/Entities.java @@ -31,13 +31,12 @@ import java.util.List; /** - * Class {@code Entities} + * Class {@code Entities} provides utility methods for entities. * * @author Hervé Bitteur */ public class Entities { - //~ Static fields/initializers ----------------------------------------------------------------- /** To compare Entity instances according to their id. */ public static final Comparator byId = new Comparator() @@ -50,7 +49,10 @@ public int compare (Entity o1, } }; - //~ Methods ------------------------------------------------------------------------------------ + private Entities () + { + } + //-------------------// // containedEntities // //-------------------// @@ -74,7 +76,7 @@ public static List containedEntities (Iterator(); + list = new ArrayList<>(); } list.add(entity); @@ -110,7 +112,7 @@ public static List containingEntities (Iterator(); + list = new ArrayList<>(); } list.add(entity); @@ -156,7 +158,7 @@ public static Rectangle getBounds (Collection entities) // ids // //-----// /** - * Build a string with just the ids of the entity collection. + * Build a string with just the IDs of the entity collection. * * @param entities the collection of Entity instances * @return the string built diff --git a/src/main/org/audiveris/omr/util/Entity.java b/src/main/org/audiveris/omr/util/Entity.java index 169bbd23d..00c180961 100644 --- a/src/main/org/audiveris/omr/util/Entity.java +++ b/src/main/org/audiveris/omr/util/Entity.java @@ -33,7 +33,6 @@ public interface Entity extends Vip { - //~ Methods ------------------------------------------------------------------------------------ /** * Tell whether the entity contains the provided point diff --git a/src/main/org/audiveris/omr/util/EntityIndex.java b/src/main/org/audiveris/omr/util/EntityIndex.java index 5844e142e..1d1faf13f 100644 --- a/src/main/org/audiveris/omr/util/EntityIndex.java +++ b/src/main/org/audiveris/omr/util/EntityIndex.java @@ -36,13 +36,11 @@ * retrieve a entity within the collection based on its (strictly positive integer) ID. * * @param specific type for entity - * * @author Hervé Bitteur */ @XmlJavaTypeAdapter(BasicIndex.InterfaceAdapter.class) public interface EntityIndex { - //~ Methods ------------------------------------------------------------------------------------ /** * Look up the index for all entities contained in the provided rectangle. diff --git a/src/main/org/audiveris/omr/util/FileUtil.java b/src/main/org/audiveris/omr/util/FileUtil.java index 1bd2027b5..c19e6c739 100644 --- a/src/main/org/audiveris/omr/util/FileUtil.java +++ b/src/main/org/audiveris/omr/util/FileUtil.java @@ -51,7 +51,6 @@ */ public abstract class FileUtil { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(FileUtil.class); @@ -59,7 +58,10 @@ public abstract class FileUtil private static final int BACKUP_MAX = 99; - //~ Methods ------------------------------------------------------------------------------------ + private FileUtil () + { + } + //-----------------// // avoidExtensions // //-----------------// @@ -163,42 +165,47 @@ public static void copy (File source, //----------// // copyTree // //----------// + /** + * Recursively copy a hierarchy of files and directories to another. + * + * @param sourceDir source directory + * @param targetDir target directory + * @throws IOException if an IO problem occurs + */ public static void copyTree (final Path sourceDir, final Path targetDir) throws IOException { - Files.walkFileTree( - sourceDir, - new SimpleFileVisitor() - { - @Override - public FileVisitResult preVisitDirectory (Path dir, - BasicFileAttributes attrs) - throws IOException - { - Path target = targetDir.resolve(sourceDir.relativize(dir)); - - try { - Files.copy(dir, target); - } catch (FileAlreadyExistsException e) { - if (!Files.isDirectory(target)) { - throw e; - } - } - - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFile (Path file, - BasicFileAttributes attrs) - throws IOException - { - Files.copy(file, targetDir.resolve(sourceDir.relativize(file))); - - return FileVisitResult.CONTINUE; - } - }); + Files.walkFileTree(sourceDir, new SimpleFileVisitor() + { + @Override + public FileVisitResult preVisitDirectory (Path dir, + BasicFileAttributes attrs) + throws IOException + { + Path target = targetDir.resolve(sourceDir.relativize(dir)); + + try { + Files.copy(dir, target); + } catch (FileAlreadyExistsException e) { + if (!Files.isDirectory(target)) { + throw e; + } + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile (Path file, + BasicFileAttributes attrs) + throws IOException + { + Files.copy(file, targetDir.resolve(sourceDir.relativize(file))); + + return FileVisitResult.CONTINUE; + } + }); } //-----------// @@ -250,30 +257,28 @@ public static void deleteDirectory (Path directory) throw new IllegalArgumentException(directory + " is not a directory"); } - Files.walkFileTree( - directory, - new SimpleFileVisitor() - { - @Override - public FileVisitResult visitFile (Path file, - BasicFileAttributes attrs) - throws IOException - { - Files.delete(file); - - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory (Path dir, - IOException exc) - throws IOException - { - Files.delete(dir); - - return FileVisitResult.CONTINUE; - } - }); + Files.walkFileTree(directory, new SimpleFileVisitor() + { + @Override + public FileVisitResult visitFile (Path file, + BasicFileAttributes attrs) + throws IOException + { + Files.delete(file); + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory (Path dir, + IOException exc) + throws IOException + { + Files.delete(dir); + + return FileVisitResult.CONTINUE; + } + }); } //--------------// @@ -283,14 +288,15 @@ public FileVisitResult postVisitDirectory (Path dir, * From a file "path/name.ext", return the final ".ext" portion. *

                                                                                                                                                            * Nota: the dot character is part of the extension, since we - * could have the following cases:

                                                                                                                                                              - *
                                                                                                                                                            • "path/name.ext" → ".ext" - *
                                                                                                                                                            • "path/name." → "." (just the dot) - *
                                                                                                                                                            • "path/name" → "" (the empty string)
                                                                                                                                                            + * could have the following cases: + *
                                                                                                                                                              + *
                                                                                                                                                            • "path/name.ext" → ".ext" + *
                                                                                                                                                            • "path/name." → "." (just the dot) + *
                                                                                                                                                            • "path/name" → "" (the empty string) + *
                                                                                                                                                            * * @param file the File to process - * - * @return the extension, which may be "" + * @return the extension, which can be the empty string ("") */ public static String getExtension (File file) { @@ -304,14 +310,15 @@ public static String getExtension (File file) * From a path "path/name.ext", return the final ".ext" portion. *

                                                                                                                                                            * Nota: the dot character is part of the extension, since we - * could have the following cases:

                                                                                                                                                              - *
                                                                                                                                                            • "path/name.ext" → ".ext" - *
                                                                                                                                                            • "path/name." → "." (just the dot) - *
                                                                                                                                                            • "path/name" → "" (the empty string)
                                                                                                                                                            + * could have the following cases: + *
                                                                                                                                                              + *
                                                                                                                                                            • "path/name.ext" → ".ext" + *
                                                                                                                                                            • "path/name." → "." (just the dot) + *
                                                                                                                                                            • "path/name" → "" (the empty string) + *
                                                                                                                                                            * * @param path the File to process - * - * @return the extension, which may be "" + * @return the extension, which can be the empty string ("") */ public static String getExtension (Path path) { @@ -321,6 +328,12 @@ public static String getExtension (Path path) //--------------// // getExtension // //--------------// + /** + * Report the (last) extension in provided string. + * + * @param name the input file name + * @return its last extension, or "" if none + */ public static String getExtension (String name) { int i = name.lastIndexOf('.'); @@ -340,7 +353,6 @@ public static String getExtension (String name) * From a file "path/name.ext1.ext2", return the "name.ext1" portion. * * @param file provided abstract path name - * * @return just the name, w/o path and final extension */ public static String getNameSansExtension (File file) @@ -356,7 +368,6 @@ public static String getNameSansExtension (File file) * From a file "path/name.ext1.ext2", return the "name.ext1" portion. * * @param path provided abstract path name - * * @return just the name, w/o path and final extension */ public static String getNameSansExtension (Path path) @@ -400,6 +411,12 @@ public boolean accept (Path entry) //---------------// // sansExtension // //---------------// + /** + * Report the provided name without its last extension. + * + * @param name input file name + * @return name with its last extension removed + */ public static String sansExtension (String name) { int i = name.lastIndexOf('.'); @@ -432,53 +449,51 @@ public static List walkDown (final Path folder, final FileSystem fs = FileSystems.getDefault(); final PathMatcher dirMatcher = fs.getPathMatcher(dirGlob); final PathMatcher fileMatcher = fs.getPathMatcher(fileGlob); - final List pathsFound = new ArrayList(); + final List pathsFound = new ArrayList<>(); if (!Files.exists(folder)) { return pathsFound; } try { - Files.walkFileTree( - folder, - new SimpleFileVisitor() - { - @Override - public FileVisitResult preVisitDirectory (Path dir, - BasicFileAttributes attrs) - throws IOException - { - if (dir.equals(folder) || dirMatcher.matches(dir)) { - return FileVisitResult.CONTINUE; - } else { - return FileVisitResult.SKIP_SUBTREE; - } - } - - @Override - public FileVisitResult visitFile (Path file, - BasicFileAttributes attrs) - throws IOException - { - if (fileMatcher.matches(file)) { - pathsFound.add(file); - } - - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory (Path dir, - IOException exc) - throws IOException - { - if (dirMatcher.matches(dir)) { - pathsFound.add(dir); - } - - return FileVisitResult.CONTINUE; - } - }); + Files.walkFileTree(folder, new SimpleFileVisitor() + { + @Override + public FileVisitResult preVisitDirectory (Path dir, + BasicFileAttributes attrs) + throws IOException + { + if (dir.equals(folder) || dirMatcher.matches(dir)) { + return FileVisitResult.CONTINUE; + } else { + return FileVisitResult.SKIP_SUBTREE; + } + } + + @Override + public FileVisitResult visitFile (Path file, + BasicFileAttributes attrs) + throws IOException + { + if (fileMatcher.matches(file)) { + pathsFound.add(file); + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory (Path dir, + IOException exc) + throws IOException + { + if (dirMatcher.matches(dir)) { + pathsFound.add(dir); + } + + return FileVisitResult.CONTINUE; + } + }); } catch (IOException ex) { logger.warn("Error in browsing " + folder + " " + ex, ex); } diff --git a/src/main/org/audiveris/omr/util/IdUtil.java b/src/main/org/audiveris/omr/util/IdUtil.java index 7866f5fff..416afefcf 100644 --- a/src/main/org/audiveris/omr/util/IdUtil.java +++ b/src/main/org/audiveris/omr/util/IdUtil.java @@ -35,7 +35,6 @@ */ public abstract class IdUtil { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(IdUtil.class); @@ -50,7 +49,10 @@ public int compare (String id1, } }; - //~ Methods ------------------------------------------------------------------------------------ + private IdUtil () + { + } + /** * Compare the integer values of two provided IDs, checking that they share the * same prefix. diff --git a/src/main/org/audiveris/omr/util/IndentingXMLStreamWriter.java b/src/main/org/audiveris/omr/util/IndentingXMLStreamWriter.java index ef712bc8f..2906a188d 100644 --- a/src/main/org/audiveris/omr/util/IndentingXMLStreamWriter.java +++ b/src/main/org/audiveris/omr/util/IndentingXMLStreamWriter.java @@ -1,441 +1,436 @@ -//------------------------------------------------------------------------------------------------// -// // -// I n d e n t i n g X M L S t r e a m W r i t e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.util; - -import java.util.ArrayDeque; -import java.util.Deque; - -import javax.xml.namespace.NamespaceContext; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; - -/** - * Class {@code IndentingXMLStreamWriter} handles indentation for XML output. - *

                                                                                                                                                            - * It is a workaround to avoid JAXB default limit to 8 steps. - * - * @author Kohsuke Kawaguchi (the author of the internal Sun implementation of class - * IndentingXMLStreamWriter in com.sun.xml.internal.txw2.output package, this class is derived from) - * @author Hervé Bitteur (using an underlying writer instance) - */ -public class IndentingXMLStreamWriter - implements XMLStreamWriter -{ - //~ Enumerations ------------------------------------------------------------------------------- - - /** What we have written in current element. */ - protected static enum Seen - { - //~ Enumeration constant initializers ------------------------------------------------------ - - /** Neither data/text nor sub-element. */ - NOTHING, - /** A (sub-) element. */ - ELEMENT, - /** CData or characters. */ - DATA; - } - - //~ Instance fields ---------------------------------------------------------------------------- - /** The actual writer, to which any real work is delegated. */ - protected final XMLStreamWriter writer; - - /** The indentation for one step. If null, no indentation is performed at all. */ - protected final String indentStep; - - /** Stack of states, parallel to elements being written. */ - protected final Deque stateStack = new ArrayDeque(); - - /** Current state (in current element). */ - protected Seen state = Seen.NOTHING; - - /** Current stack depth. */ - protected int depth = 0; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code IndentingXmlStreamWriter} object with default indent step of - * 2 spaces. - * - * @param writer the underlying writer - */ - public IndentingXMLStreamWriter (XMLStreamWriter writer) - { - this(writer, " "); - } - - /** - * Creates a new {@code IndentingXmlStreamWriter} object. - * - * @param writer the underlying writer - * @param indentStep the indentation string for one step. If null, no indentation is performed. - */ - public IndentingXMLStreamWriter (XMLStreamWriter writer, - String indentStep) - { - this.writer = writer; - this.indentStep = indentStep; - } - - //~ Methods ------------------------------------------------------------------------------------ - @Override - public void close () - throws XMLStreamException - { - writer.close(); - } - - @Override - public void flush () - throws XMLStreamException - { - writer.flush(); - } - - @Override - public NamespaceContext getNamespaceContext () - { - return writer.getNamespaceContext(); - } - - @Override - public String getPrefix (String uri) - throws XMLStreamException - { - return writer.getPrefix(uri); - } - - @Override - public Object getProperty (String name) - throws IllegalArgumentException - { - return writer.getProperty(name); - } - - @Override - public void setDefaultNamespace (String uri) - throws XMLStreamException - { - writer.setDefaultNamespace(uri); - } - - @Override - public void setNamespaceContext (NamespaceContext context) - throws XMLStreamException - { - writer.setNamespaceContext(context); - } - - @Override - public void setPrefix (String prefix, - String uri) - throws XMLStreamException - { - writer.setPrefix(prefix, uri); - } - - @Override - public void writeAttribute (String localName, - String value) - throws XMLStreamException - { - writer.writeAttribute(localName, value); - } - - @Override - public void writeAttribute (String prefix, - String namespaceURI, - String localName, - String value) - throws XMLStreamException - { - writer.writeAttribute(prefix, namespaceURI, localName, value); - } - - @Override - public void writeAttribute (String namespaceURI, - String localName, - String value) - throws XMLStreamException - { - writer.writeAttribute(namespaceURI, localName, value); - } - - @Override - public void writeCData (String data) - throws XMLStreamException - { - state = Seen.DATA; - writer.writeCData(data); - } - - @Override - public void writeCharacters (String text) - throws XMLStreamException - { - state = Seen.DATA; - writer.writeCharacters(text); - } - - @Override - public void writeCharacters (char[] text, - int start, - int len) - throws XMLStreamException - { - state = Seen.DATA; - writer.writeCharacters(text, start, len); - } - - @Override - public void writeComment (String data) - throws XMLStreamException - { - writer.writeComment(data); - } - - @Override - public void writeDTD (String dtd) - throws XMLStreamException - { - writer.writeDTD(dtd); - } - - @Override - public void writeDefaultNamespace (String namespaceURI) - throws XMLStreamException - { - writer.writeDefaultNamespace(namespaceURI); - } - - @Override - public void writeEmptyElement (String namespaceURI, - String localName) - throws XMLStreamException - { - onEmptyElement(); - writer.writeEmptyElement(namespaceURI, localName); - } - - @Override - public void writeEmptyElement (String prefix, - String localName, - String namespaceURI) - throws XMLStreamException - { - onEmptyElement(); - writer.writeEmptyElement(prefix, localName, namespaceURI); - } - - @Override - public void writeEmptyElement (String localName) - throws XMLStreamException - { - onEmptyElement(); - writer.writeEmptyElement(localName); - } - - @Override - public void writeEndDocument () - throws XMLStreamException - { - writer.writeEndDocument(); - } - - @Override - public void writeEndElement () - throws XMLStreamException - { - onEndElement(); - writer.writeEndElement(); - } - - @Override - public void writeEntityRef (String name) - throws XMLStreamException - { - writer.writeEntityRef(name); - } - - @Override - public void writeNamespace (String prefix, - String namespaceURI) - throws XMLStreamException - { - writer.writeNamespace(prefix, namespaceURI); - } - - @Override - public void writeProcessingInstruction (String target) - throws XMLStreamException - { - writer.writeProcessingInstruction(target); - } - - @Override - public void writeProcessingInstruction (String target, - String data) - throws XMLStreamException - { - writer.writeProcessingInstruction(target, data); - } - - @Override - public void writeStartDocument () - throws XMLStreamException - { - writer.writeStartDocument(); - - if (indentStep != null) { - doNewline(); - } - } - - @Override - public void writeStartDocument (String version) - throws XMLStreamException - { - writer.writeStartDocument(version); - - if (indentStep != null) { - doNewline(); - } - } - - @Override - public void writeStartDocument (String encoding, - String version) - throws XMLStreamException - { - writer.writeStartDocument(encoding, version); - - if (indentStep != null) { - doNewline(); - } - } - - @Override - public void writeStartElement (String localName) - throws XMLStreamException - { - onStartElement(); - writer.writeStartElement(localName); - } - - @Override - public void writeStartElement (String namespaceURI, - String localName) - throws XMLStreamException - { - onStartElement(); - writer.writeStartElement(namespaceURI, localName); - } - - @Override - public void writeStartElement (String prefix, - String localName, - String namespaceURI) - throws XMLStreamException - { - onStartElement(); - writer.writeStartElement(prefix, localName, namespaceURI); - } - - /** - * Print indentation for the current level. - * - * @throws XMLStreamException if there was an error processing the XML - */ - protected void doIndent () - throws XMLStreamException - { - if (indentStep != null) { - for (int i = 0; i < depth; i++) { - writer.writeCharacters(indentStep); - } - } - } - - /** - * Insert a newline. - * - * @throws XMLStreamException if there was an error processing the XML - */ - protected void doNewline () - throws XMLStreamException - { - writer.writeCharacters("\n"); - } - - /** - * Call-back on writeEmptyElement() - * - * @throws XMLStreamException if there was an error processing the XML - */ - protected void onEmptyElement () - throws XMLStreamException - { - state = Seen.ELEMENT; - - if ((indentStep != null) && (depth > 0)) { - doNewline(); - doIndent(); - } - } - - /** - * Call-back on writeEndElement() - * - * @throws XMLStreamException if there was an error processing the XML - */ - protected void onEndElement () - throws XMLStreamException - { - depth--; - - if ((indentStep != null) && (state == Seen.ELEMENT)) { - doNewline(); - doIndent(); - } - - state = stateStack.removeFirst(); - } - - /** - * Call-back on any writeStartElement() - * - * @throws XMLStreamException if there was an error processing the XML - */ - protected void onStartElement () - throws XMLStreamException - { - stateStack.addFirst(Seen.ELEMENT); - state = Seen.NOTHING; - - if ((indentStep != null) && (depth > 0)) { - doNewline(); - doIndent(); - } - - depth++; - } -} +//------------------------------------------------------------------------------------------------// +// // +// I n d e n t i n g X M L S t r e a m W r i t e r // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.util; + +import java.util.ArrayDeque; +import java.util.Deque; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +/** + * Class {@code IndentingXMLStreamWriter} handles indentation for XML output. + *

                                                                                                                                                            + * It is a workaround to avoid JAXB default limit to 8 steps. + * + * @author Kohsuke Kawaguchi (the author of the internal Sun implementation of class + * IndentingXMLStreamWriter in com.sun.xml.internal.txw2.output package, this class is + * derived from) + * @author Hervé Bitteur (using an underlying writer instance) + */ +public class IndentingXMLStreamWriter + implements XMLStreamWriter +{ + + /** The actual writer, to which any real work is delegated. */ + protected final XMLStreamWriter writer; + + /** The indentation for one step. If null, no indentation is performed at all. */ + protected final String indentStep; + + /** Stack of states, parallel to elements being written. */ + protected final Deque stateStack = new ArrayDeque<>(); + + /** Current state (in current element). */ + protected Seen state = Seen.NOTHING; + + /** Current stack depth. */ + protected int depth = 0; + + /** + * Creates a new {@code IndentingXmlStreamWriter} object with default indent step of + * 2 spaces. + * + * @param writer the underlying writer + */ + public IndentingXMLStreamWriter (XMLStreamWriter writer) + { + this(writer, " "); + } + + /** + * Creates a new {@code IndentingXmlStreamWriter} object. + * + * @param writer the underlying writer + * @param indentStep the indentation string for one step. If null, no indentation is performed. + */ + public IndentingXMLStreamWriter (XMLStreamWriter writer, + String indentStep) + { + this.writer = writer; + this.indentStep = indentStep; + } + + @Override + public void close () + throws XMLStreamException + { + writer.close(); + } + + @Override + public void flush () + throws XMLStreamException + { + writer.flush(); + } + + @Override + public NamespaceContext getNamespaceContext () + { + return writer.getNamespaceContext(); + } + + @Override + public void setNamespaceContext (NamespaceContext context) + throws XMLStreamException + { + writer.setNamespaceContext(context); + } + + @Override + public String getPrefix (String uri) + throws XMLStreamException + { + return writer.getPrefix(uri); + } + + @Override + public Object getProperty (String name) + throws IllegalArgumentException + { + return writer.getProperty(name); + } + + @Override + public void setDefaultNamespace (String uri) + throws XMLStreamException + { + writer.setDefaultNamespace(uri); + } + + @Override + public void setPrefix (String prefix, + String uri) + throws XMLStreamException + { + writer.setPrefix(prefix, uri); + } + + @Override + public void writeAttribute (String localName, + String value) + throws XMLStreamException + { + writer.writeAttribute(localName, value); + } + + @Override + public void writeAttribute (String prefix, + String namespaceURI, + String localName, + String value) + throws XMLStreamException + { + writer.writeAttribute(prefix, namespaceURI, localName, value); + } + + @Override + public void writeAttribute (String namespaceURI, + String localName, + String value) + throws XMLStreamException + { + writer.writeAttribute(namespaceURI, localName, value); + } + + @Override + public void writeCData (String data) + throws XMLStreamException + { + state = Seen.DATA; + writer.writeCData(data); + } + + @Override + public void writeCharacters (String text) + throws XMLStreamException + { + state = Seen.DATA; + writer.writeCharacters(text); + } + + @Override + public void writeCharacters (char[] text, + int start, + int len) + throws XMLStreamException + { + state = Seen.DATA; + writer.writeCharacters(text, start, len); + } + + @Override + public void writeComment (String data) + throws XMLStreamException + { + writer.writeComment(data); + } + + @Override + public void writeDTD (String dtd) + throws XMLStreamException + { + writer.writeDTD(dtd); + } + + @Override + public void writeDefaultNamespace (String namespaceURI) + throws XMLStreamException + { + writer.writeDefaultNamespace(namespaceURI); + } + + @Override + public void writeEmptyElement (String namespaceURI, + String localName) + throws XMLStreamException + { + onEmptyElement(); + writer.writeEmptyElement(namespaceURI, localName); + } + + @Override + public void writeEmptyElement (String prefix, + String localName, + String namespaceURI) + throws XMLStreamException + { + onEmptyElement(); + writer.writeEmptyElement(prefix, localName, namespaceURI); + } + + @Override + public void writeEmptyElement (String localName) + throws XMLStreamException + { + onEmptyElement(); + writer.writeEmptyElement(localName); + } + + @Override + public void writeEndDocument () + throws XMLStreamException + { + writer.writeEndDocument(); + } + + @Override + public void writeEndElement () + throws XMLStreamException + { + onEndElement(); + writer.writeEndElement(); + } + + @Override + public void writeEntityRef (String name) + throws XMLStreamException + { + writer.writeEntityRef(name); + } + + @Override + public void writeNamespace (String prefix, + String namespaceURI) + throws XMLStreamException + { + writer.writeNamespace(prefix, namespaceURI); + } + + @Override + public void writeProcessingInstruction (String target) + throws XMLStreamException + { + writer.writeProcessingInstruction(target); + } + + @Override + public void writeProcessingInstruction (String target, + String data) + throws XMLStreamException + { + writer.writeProcessingInstruction(target, data); + } + + @Override + public void writeStartDocument () + throws XMLStreamException + { + writer.writeStartDocument(); + + if (indentStep != null) { + doNewline(); + } + } + + @Override + public void writeStartDocument (String version) + throws XMLStreamException + { + writer.writeStartDocument(version); + + if (indentStep != null) { + doNewline(); + } + } + + @Override + public void writeStartDocument (String encoding, + String version) + throws XMLStreamException + { + writer.writeStartDocument(encoding, version); + + if (indentStep != null) { + doNewline(); + } + } + + @Override + public void writeStartElement (String localName) + throws XMLStreamException + { + onStartElement(); + writer.writeStartElement(localName); + } + + @Override + public void writeStartElement (String namespaceURI, + String localName) + throws XMLStreamException + { + onStartElement(); + writer.writeStartElement(namespaceURI, localName); + } + + @Override + public void writeStartElement (String prefix, + String localName, + String namespaceURI) + throws XMLStreamException + { + onStartElement(); + writer.writeStartElement(prefix, localName, namespaceURI); + } + + /** + * Print indentation for the current level. + * + * @throws XMLStreamException if there was an error processing the XML + */ + protected void doIndent () + throws XMLStreamException + { + if (indentStep != null) { + for (int i = 0; i < depth; i++) { + writer.writeCharacters(indentStep); + } + } + } + + /** + * Insert a newline. + * + * @throws XMLStreamException if there was an error processing the XML + */ + protected void doNewline () + throws XMLStreamException + { + writer.writeCharacters("\n"); + } + + /** + * Call-back on writeEmptyElement() + * + * @throws XMLStreamException if there was an error processing the XML + */ + protected void onEmptyElement () + throws XMLStreamException + { + state = Seen.ELEMENT; + + if ((indentStep != null) && (depth > 0)) { + doNewline(); + doIndent(); + } + } + + /** + * Call-back on writeEndElement() + * + * @throws XMLStreamException if there was an error processing the XML + */ + protected void onEndElement () + throws XMLStreamException + { + depth--; + + if ((indentStep != null) && (state == Seen.ELEMENT)) { + doNewline(); + doIndent(); + } + + state = stateStack.removeFirst(); + } + + /** + * Call-back on any writeStartElement() + * + * @throws XMLStreamException if there was an error processing the XML + */ + protected void onStartElement () + throws XMLStreamException + { + stateStack.addFirst(Seen.ELEMENT); + state = Seen.NOTHING; + + if ((indentStep != null) && (depth > 0)) { + doNewline(); + doIndent(); + } + + depth++; + } + + /** What we have written in current element. */ + protected static enum Seen + { + /** Neither data/text nor sub-element. */ + NOTHING, + /** A (sub-) element. */ + ELEMENT, + /** CData or characters. */ + DATA; + } +} diff --git a/src/main/org/audiveris/omr/util/InstancesWatcher.java b/src/main/org/audiveris/omr/util/InstancesWatcher.java index d5070a6ce..b149c3c90 100644 --- a/src/main/org/audiveris/omr/util/InstancesWatcher.java +++ b/src/main/org/audiveris/omr/util/InstancesWatcher.java @@ -37,15 +37,12 @@ */ public class InstancesWatcher { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(InstancesWatcher.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Weak references to instances. */ - private final Set> actives = new LinkedHashSet>(); + private final Set> actives = new LinkedHashSet<>(); - //~ Methods ------------------------------------------------------------------------------------ /** * Register an instance. * (to be called in class constructor) @@ -54,7 +51,7 @@ public class InstancesWatcher */ public synchronized void addRef (E ref) { - actives.add(new WeakReference(ref)); + actives.add(new WeakReference<>(ref)); } /** diff --git a/src/main/org/audiveris/omr/util/IntUtil.java b/src/main/org/audiveris/omr/util/IntUtil.java index 780c074cb..6f24b8d85 100644 --- a/src/main/org/audiveris/omr/util/IntUtil.java +++ b/src/main/org/audiveris/omr/util/IntUtil.java @@ -34,11 +34,14 @@ */ public abstract class IntUtil { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(IntUtil.class); - //~ Methods ------------------------------------------------------------------------------------ + /** Not meant to be instantiated. */ + private IntUtil () + { + } + //-----------// // parseInts // //-----------// @@ -50,7 +53,7 @@ public abstract class IntUtil */ public static List parseInts (String str) { - final List intList = new ArrayList(); + final List intList = new ArrayList<>(); final String[] tokens = str.split("\\s*,\\s*"); for (String token : tokens) { diff --git a/src/main/org/audiveris/omr/util/Jaxb.java b/src/main/org/audiveris/omr/util/Jaxb.java index 0739609a4..548de49ea 100644 --- a/src/main/org/audiveris/omr/util/Jaxb.java +++ b/src/main/org/audiveris/omr/util/Jaxb.java @@ -63,43 +63,61 @@ */ public abstract class Jaxb { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Jaxb.class); - //~ Methods ------------------------------------------------------------------------------------ + /** Not meant to be instantiated. */ + private Jaxb () + { + } + //---------// // marshal // //---------// + /** + * Marshal an object to a file, using provided JAXB context. + * + * @param object instance to marshal + * @param path target file + * @param jaxbContext proper context + * @throws IOException on IO error + * @throws JAXBException on JAXB error + * @throws XMLStreamException on XML error + */ public static void marshal (Object object, Path path, JAXBContext jaxbContext) - throws IOException, JAXBException, XMLStreamException + throws IOException, + JAXBException, + XMLStreamException { - OutputStream os = null; - try { + try (OutputStream os = Files.newOutputStream(path, CREATE);) { Marshaller m = jaxbContext.createMarshaller(); - os = Files.newOutputStream(path, CREATE); - XMLStreamWriter writer = new IndentingXMLStreamWriter( XMLOutputFactory.newInstance().createXMLStreamWriter(os, "UTF-8")); m.marshal(object, writer); - } finally { - if (os != null) { - os.flush(); - os.close(); - } + os.flush(); } } //---------// // marshal // //---------// + /** + * Marshal an object to a stream, using provided JAXB context. + * + * @param object instance to marshal + * @param os output stream, not closed by this method + * @param jaxbContext proper context + * @throws JAXBException on JAXB error + * @throws XMLStreamException on XML error + */ public static void marshal (Object object, OutputStream os, JAXBContext jaxbContext) - throws JAXBException, XMLStreamException + throws JAXBException, + XMLStreamException { Marshaller m = jaxbContext.createMarshaller(); XMLStreamWriter writer = new IndentingXMLStreamWriter( @@ -110,9 +128,19 @@ public static void marshal (Object object, //-----------// // unmarshal // //-----------// + /** + * Unmarshal an object from a file, using provided JAXB context. + * + * @param path input file + * @param jaxbContext proper context + * @return the unmarshalled object + * @throws IOException on IO error + * @throws JAXBException on JAXB error + */ public static Object unmarshal (Path path, JAXBContext jaxbContext) - throws IOException, JAXBException + throws IOException, + JAXBException { InputStream is = null; @@ -128,14 +156,15 @@ public static Object unmarshal (Path path, } } - //~ Inner Classes ------------------------------------------------------------------------------ //----------------------// // AtomicIntegerAdapter // //----------------------// + /** + * Adapter for AtomicInteger. + */ public static class AtomicIntegerAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public Integer marshal (AtomicInteger atomic) @@ -156,16 +185,15 @@ public AtomicInteger unmarshal (Integer i) // BooleanPositiveAdapter // //------------------------// /** - * Only true value is marshalled into the output, false value is not marshalled. + * Adapter for Boolean, by which only true value is marshalled into the output, + * false value is not marshalled. */ public static class BooleanPositiveAdapter extends XmlAdapter { - //~ Static fields/initializers ------------------------------------------------------------- private static final String TRUE = Boolean.toString(true); - //~ Methods -------------------------------------------------------------------------------- @Override public String marshal (Boolean b) throws Exception @@ -192,10 +220,12 @@ public Boolean unmarshal (String s) //--------------// // CubicAdapter // //--------------// + /** + * Adapter for Cubic. + */ public static class CubicAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public CubicFacade marshal (CubicCurve2D curve) @@ -219,11 +249,9 @@ public CubicCurve2D unmarshal (CubicFacade facade) return facade.getCurve(); } - //~ Inner Classes -------------------------------------------------------------------------- @XmlRootElement private static class CubicFacade { - //~ Instance fields -------------------------------------------------------------------- @XmlAttribute @XmlJavaTypeAdapter(type = double.class, value = Double1Adapter.class) @@ -257,12 +285,12 @@ private static class CubicFacade @XmlJavaTypeAdapter(type = double.class, value = Double1Adapter.class) public double y2; - //~ Constructors ----------------------------------------------------------------------- - public CubicFacade () + // Needed for JAXB + CubicFacade () { } - public CubicFacade (CubicCurve2D curve) + CubicFacade (CubicCurve2D curve) { this.x1 = curve.getX1(); this.y1 = curve.getY1(); @@ -274,7 +302,6 @@ public CubicFacade (CubicCurve2D curve) this.y2 = curve.getY2(); } - //~ Methods ---------------------------------------------------------------------------- public CubicCurve2D getCurve () { return new CubicCurve2D.Double(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); @@ -285,10 +312,12 @@ public CubicCurve2D getCurve () //------------------// // DimensionAdapter // //------------------// + /** + * Adapter for Dimension. + */ public static class DimensionAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public DimensionFacade marshal (Dimension dim) @@ -312,10 +341,8 @@ public Dimension unmarshal (DimensionFacade facade) return facade.getDimension(); } - //~ Inner Classes -------------------------------------------------------------------------- private static class DimensionFacade { - //~ Instance fields -------------------------------------------------------------------- @XmlAttribute(name = "w") public int width; @@ -323,11 +350,10 @@ private static class DimensionFacade @XmlAttribute(name = "h") public int height; - //~ Constructors ----------------------------------------------------------------------- /** - * Creates a new instance of DimensionFacade. + * Needed for JAXB. */ - public DimensionFacade () + DimensionFacade () { } @@ -336,13 +362,12 @@ public DimensionFacade () * * @param dimension the interfaced dimension */ - public DimensionFacade (Dimension dimension) + DimensionFacade (Dimension dimension) { width = dimension.width; height = dimension.height; } - //~ Methods ---------------------------------------------------------------------------- public Dimension getDimension () { return new Dimension(width, height); @@ -353,10 +378,12 @@ public Dimension getDimension () //----------------// // Double1Adapter // //----------------// + /** + * Adapter for Double, with maximum 1 decimal. + */ public static class Double1Adapter extends XmlAdapter { - //~ Static fields/initializers ------------------------------------------------------------- private static final DecimalFormat decimal = new DecimalFormat(); @@ -365,7 +392,6 @@ public static class Double1Adapter decimal.setMaximumFractionDigits(1); // For a maximum of 1 decimal } - //~ Methods -------------------------------------------------------------------------------- @Override public String marshal (Double d) throws Exception @@ -392,10 +418,12 @@ public Double unmarshal (String s) //----------------// // Double3Adapter // //----------------// + /** + * Adapter for Double, with maximum 3 decimals. + */ public static class Double3Adapter extends XmlAdapter { - //~ Static fields/initializers ------------------------------------------------------------- private static final DecimalFormat decimal = new DecimalFormat(); @@ -404,7 +432,6 @@ public static class Double3Adapter decimal.setMaximumFractionDigits(3); // For a maximum of 3 decimals } - //~ Methods -------------------------------------------------------------------------------- @Override public String marshal (Double d) throws Exception @@ -431,10 +458,12 @@ public Double unmarshal (String s) //----------------// // Double5Adapter // //----------------// + /** + * Adapter for Double, with maximum 5 decimals. + */ public static class Double5Adapter extends XmlAdapter { - //~ Static fields/initializers ------------------------------------------------------------- private static final DecimalFormat decimal = new DecimalFormat(); @@ -443,7 +472,6 @@ public static class Double5Adapter decimal.setMaximumFractionDigits(5); // For a maximum of 5 decimals } - //~ Methods -------------------------------------------------------------------------------- @Override public String marshal (Double d) throws Exception @@ -470,10 +498,12 @@ public Double unmarshal (String s) //---------------// // Line2DAdapter // //---------------// + /** + * Adapter for Line2D. + */ public static class Line2DAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public Line2DFacade marshal (Line2D line) @@ -497,11 +527,9 @@ public Line2D unmarshal (Line2DFacade facade) return facade.getLine(); } - //~ Inner Classes -------------------------------------------------------------------------- @XmlRootElement private static class Line2DFacade { - //~ Instance fields -------------------------------------------------------------------- @XmlElement public Point2DFacade p1; @@ -509,54 +537,74 @@ private static class Line2DFacade @XmlElement public Point2DFacade p2; - //~ Constructors ----------------------------------------------------------------------- - public Line2DFacade () + Line2DFacade () { } - public Line2DFacade (Line2D line) + Line2DFacade (Line2D line) { Objects.requireNonNull(line, "Cannot create Line2DFacade with a null line"); p1 = new Point2DFacade(line.getP1()); p2 = new Point2DFacade(line.getP2()); } - //~ Methods ---------------------------------------------------------------------------- public Line2D getLine () { return new Line2D.Double(p1.x, p1.y, p2.x, p2.y); } + + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("Line2DF{"); + + if (p1 != null) { + sb.append("p1:").append(p1); + } + + if (p2 != null) { + sb.append(",p2:").append(p2); + } + + sb.append('}'); + + return sb.toString(); + } } } //---------------// // MarshalLogger // //---------------// + /** + * A logger specific for marshalling. + */ public static class MarshalLogger extends Marshaller.Listener { - //~ Methods -------------------------------------------------------------------------------- @Override public void afterMarshal (Object source) { - logger.info("GL afterMarshal source: {}", source); + logger.info("GL afterMarshal {}", source); } @Override public void beforeMarshal (Object source) { - logger.info("GL beforeMarshal source: {}", source); + logger.info("GL beforeMarshal {}", source); } } //-------------// // PathAdapter // //-------------// + /** + * Adapter for Path interface. + */ public static class PathAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public String marshal (Path path) @@ -583,10 +631,12 @@ public Path unmarshal (String str) //----------------// // Point2DAdapter // //----------------// + /** + * Adapter for Point2D. + */ public static class Point2DAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public Point2DFacade marshal (Point2D point) @@ -614,10 +664,12 @@ public Point2D unmarshal (Point2DFacade facade) //--------------// // PointAdapter // //--------------// + /** + * Adapter for Point. + */ public static class PointAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public PointFacade marshal (Point point) @@ -641,10 +693,8 @@ public Point unmarshal (PointFacade facade) return facade.getPoint(); } - //~ Inner Classes -------------------------------------------------------------------------- private static class PointFacade { - //~ Instance fields -------------------------------------------------------------------- @XmlAttribute public int x; @@ -652,32 +702,44 @@ private static class PointFacade @XmlAttribute public int y; - //~ Constructors ----------------------------------------------------------------------- - public PointFacade () + // Needed for JAXB + PointFacade () { } - public PointFacade (Point point) + PointFacade (Point point) { this.x = point.x; this.y = point.y; } - //~ Methods ---------------------------------------------------------------------------- public Point getPoint () { return new Point(x, y); } + + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("PointF{"); + sb.append("x:").append(x); + sb.append(",y:").append(y); + sb.append('}'); + + return sb.toString(); + } } } //--------------------// // Rectangle2DAdapter // //--------------------// + /** + * Adapter for Rectangle2D. + */ public static class Rectangle2DAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public Rectangle2DFacade marshal (Rectangle2D rect) @@ -701,10 +763,8 @@ public Rectangle2D unmarshal (Rectangle2DFacade facade) return facade.getRectangle2D(); } - //~ Inner Classes -------------------------------------------------------------------------- private static class Rectangle2DFacade { - //~ Instance fields -------------------------------------------------------------------- @XmlAttribute(name = "x") @XmlJavaTypeAdapter(type = double.class, value = Double3Adapter.class) @@ -722,8 +782,7 @@ private static class Rectangle2DFacade @XmlJavaTypeAdapter(type = double.class, value = Double3Adapter.class) public double height; - //~ Constructors ----------------------------------------------------------------------- - public Rectangle2DFacade (Rectangle2D rect) + Rectangle2DFacade (Rectangle2D rect) { x = rect.getX(); y = rect.getY(); @@ -735,7 +794,6 @@ private Rectangle2DFacade () { } - //~ Methods ---------------------------------------------------------------------------- public Rectangle2D getRectangle2D () { return new Rectangle2D.Double(x, y, width, height); @@ -746,10 +804,12 @@ public Rectangle2D getRectangle2D () //------------------// // RectangleAdapter // //------------------// + /** + * Adapter for Rectangle. + */ public static class RectangleAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public RectangleFacade marshal (Rectangle rect) @@ -773,10 +833,8 @@ public Rectangle unmarshal (RectangleFacade facade) return facade.getRectangle(); } - //~ Inner Classes -------------------------------------------------------------------------- private static class RectangleFacade { - //~ Instance fields -------------------------------------------------------------------- @XmlAttribute public int x; @@ -790,12 +848,11 @@ private static class RectangleFacade @XmlAttribute(name = "h") public int height; - //~ Constructors ----------------------------------------------------------------------- - public RectangleFacade () + RectangleFacade () { } - public RectangleFacade (Rectangle rect) + RectangleFacade (Rectangle rect) { x = rect.x; y = rect.y; @@ -803,21 +860,35 @@ public RectangleFacade (Rectangle rect) height = rect.height; } - //~ Methods ---------------------------------------------------------------------------- public Rectangle getRectangle () { return new Rectangle(x, y, width, height); } + + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("RectangleF{"); + sb.append("x:").append(x); + sb.append(",y:").append(y); + sb.append(",w:").append(width); + sb.append(",h:").append(height); + sb.append('}'); + + return sb.toString(); + } } } //----------------------// // StringIntegerAdapter // //----------------------// + /** + * Adapter for Integer, which gives String value mandatory for @XmlID support. + */ public static class StringIntegerAdapter extends XmlAdapter { - //~ Methods -------------------------------------------------------------------------------- @Override public String marshal (Integer i) @@ -845,33 +916,37 @@ public Integer unmarshal (String s) //-----------------// // UnmarshalLogger // //-----------------// + /** + * Specific logger for unmarshalling. + */ public static class UnmarshalLogger extends Unmarshaller.Listener { - //~ Methods -------------------------------------------------------------------------------- @Override public void afterUnmarshal (Object target, Object parent) { - logger.info("GL afterUnmarshal parent: {} for {}", parent, target); + logger.info("GL afterUnmarshal parent:{} of {}", parent, target); } @Override public void beforeUnmarshal (Object target, Object parent) { - logger.info("GL beforeUnmarshal parent: {} for class {}", parent, target.getClass()); + logger.info("GL beforeUnmarshal parent:{} for target {}", parent, target.getClass()); } } //---------------// // Point2DFacade // //---------------// + /** + * A facade to Point2D. + */ @XmlRootElement private static class Point2DFacade { - //~ Instance fields ------------------------------------------------------------------------ @XmlAttribute @XmlJavaTypeAdapter(type = double.class, value = Double1Adapter.class) @@ -881,21 +956,30 @@ private static class Point2DFacade @XmlJavaTypeAdapter(type = double.class, value = Double1Adapter.class) public double y; - //~ Constructors --------------------------------------------------------------------------- - public Point2DFacade () + Point2DFacade () { } - public Point2DFacade (Point2D point) + Point2DFacade (Point2D point) { this.x = point.getX(); this.y = point.getY(); } - //~ Methods -------------------------------------------------------------------------------- public Point2D getPoint () { return new Point2D.Double(x, y); } + + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder("Point2DF{"); + sb.append("x:").append(x); + sb.append(",y:").append(y); + sb.append('}'); + + return sb.toString(); + } } } diff --git a/src/main/org/audiveris/omr/util/Memory.java b/src/main/org/audiveris/omr/util/Memory.java index 29821a518..b78bb346f 100644 --- a/src/main/org/audiveris/omr/util/Memory.java +++ b/src/main/org/audiveris/omr/util/Memory.java @@ -29,17 +29,14 @@ */ public abstract class Memory { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Runtime rt = Runtime.getRuntime(); - //~ Constructors ------------------------------------------------------------------------------- /** Not meant to be instantiated. */ private Memory () { } - //~ Methods ------------------------------------------------------------------------------------ //------// // free // //------// diff --git a/src/main/org/audiveris/omr/util/Merger.java b/src/main/org/audiveris/omr/util/Merger.java deleted file mode 100644 index 2df548dfc..000000000 --- a/src/main/org/audiveris/omr/util/Merger.java +++ /dev/null @@ -1,84 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// M e r g e r // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.util; - -import java.util.ArrayList; -import java.util.List; - -/** - * Class {@code Merger} - * - * @author Hervé Bitteur - */ -public abstract class Merger -{ - //~ Methods ------------------------------------------------------------------------------------ - - public void mergeAll (List list) - { - List merged = new ArrayList(); - - // Browse list - for (E current : list) { - E candidate = current; - - // Keep on working while we do have a candidate to check for merge - CandidateLoop: - while (true) { - // Set up candidate environment - setupCandidate(candidate); - - // Check candidate vs all preceding ones until current excluded - HeadsLoop: - for (E head : list) { - if (head == current) { - break CandidateLoop; // Actual end of sub-list - } - - // Check for a possible merge - if ((head != candidate) && (!isMerged(head)) && canMerge(head, candidate)) { - merge(head, candidate); // Head swallows candidate - merged.add(candidate); - candidate = head; // This is a new candidate - - break HeadsLoop; - } - } - } - } - - // Discard the merged filaments - list.removeAll(merged); - } - - protected abstract boolean canMerge (E head, - E candidate); - - protected abstract boolean isMerged (E e); - - protected abstract boolean merge (E head, - E candidate); - - protected void setupCandidate (E candidate) - { - } -} diff --git a/src/main/org/audiveris/omr/util/NameSet.java b/src/main/org/audiveris/omr/util/NameSet.java index 9dac91f33..83b804b22 100644 --- a/src/main/org/audiveris/omr/util/NameSet.java +++ b/src/main/org/audiveris/omr/util/NameSet.java @@ -43,21 +43,21 @@ *

                                                                                                                                                            * Actually, rather than a set, it is a list where the most recently used * are kept at the head of the list. There is no duplicate in the set (tests are - * case-insensitive).

                                                                                                                                                            + * case-insensitive). + *

                                                                                                                                                            *

                                                                                                                                                            - * The NameSet can additionally be used to dynamically generate and handle a menu.

                                                                                                                                                            + * The NameSet can additionally be used to dynamically generate and handle a menu. + *

                                                                                                                                                            * * @author Hervé Bitteur */ @ThreadSafe public class NameSet { - //~ Static fields/initializers ----------------------------------------------------------------- /** Separator */ private static final String SEPARATOR = ";"; - //~ Instance fields ---------------------------------------------------------------------------- /** Global name for this set. */ private final String setName; @@ -65,12 +65,11 @@ public class NameSet private final Constant.String constant; /** List of names in this set. */ - private final List names = new ArrayList(); + private final List names = new ArrayList<>(); /** Max number of names in this set. */ private final int maxNameCount; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new set of names, with some customizing parameters. * @@ -94,7 +93,6 @@ public NameSet (String setName, } } - //~ Methods ------------------------------------------------------------------------------------ //-----// // add // //-----// @@ -168,6 +166,11 @@ public void menuSelected (MenuEvent e) //---------// // isEmpty // //---------// + /** + * Tell whether the set is empty + * + * @return true if so + */ public synchronized boolean isEmpty () { return names.isEmpty(); @@ -204,7 +207,7 @@ public synchronized boolean remove (String name) //----------------// private void updateConstant () { - StringBuilder buf = new StringBuilder(1024); + StringBuilder buf = new StringBuilder(1_024); for (String n : names) { if (buf.length() > 0) { diff --git a/src/main/org/audiveris/omr/util/NamedDouble.java b/src/main/org/audiveris/omr/util/NamedDouble.java index ef8e81c6f..f25bad5bc 100644 --- a/src/main/org/audiveris/omr/util/NamedDouble.java +++ b/src/main/org/audiveris/omr/util/NamedDouble.java @@ -1,123 +1,120 @@ -//------------------------------------------------------------------------------------------------// -// // -// N a m e d D o u b l e // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.util; - -import org.audiveris.omr.constant.Constant; - -/** - * Class {@code NamedDouble} is a documented Double. - * - * @author Hervé Bitteur - */ -public class NamedDouble -{ - //~ Instance fields ---------------------------------------------------------------------------- - - private final String name; - - private final String quantityUnit; - - private double value; - - private final String description; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code BasicNamedDouble} object. - * - * @param name a name for this entity - * @param quantityUnit unit used by value - * @param value initial value - * @param description semantic - */ - public NamedDouble (String name, - String quantityUnit, - double value, - String description) - { - this.name = name; - this.quantityUnit = quantityUnit; - this.value = value; - this.description = description; - } - - /** - * Creates a new {@code BasicNamedDouble} object based on a {@link Constant.Double}. - * - * @param cst the provided Constant.DOuble instance - */ - public NamedDouble (Constant.Double cst) - { - this(cst.getName(), cst.getQuantityUnit(), cst.getValue(), cst.getDescription()); - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Get the description sentence recorded with the NamedDouble - * - * @return the description sentence as a string - */ - public String getDescription () - { - return description; - } - - /** - * Report the name of the NamedDouble - * - * @return the NamedDouble name - */ - public String getName () - { - return name; - } - - /** - * Report the unit, if any, used as base of quantity measure - * - * @return the quantity unit, if any - */ - public String getQuantityUnit () - { - return quantityUnit; - } - - /** - * Report the current value - * - * @return current value - */ - public Double getValue () - { - return value; - } - - /** - * Assign a new value - * - * @param value value to be assigned - */ - public void setValue (double value) - { - this.value = value; - } -} +//------------------------------------------------------------------------------------------------// +// // +// N a m e d D o u b l e // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.util; + +import org.audiveris.omr.constant.Constant; + +/** + * Class {@code NamedDouble} is a documented Double. + * + * @author Hervé Bitteur + */ +public class NamedDouble +{ + + private final String name; + + private final String quantityUnit; + + private double value; + + private final String description; + + /** + * Creates a new {@code BasicNamedDouble} object. + * + * @param name a name for this entity + * @param quantityUnit unit used by value + * @param value initial value + * @param description semantic + */ + public NamedDouble (String name, + String quantityUnit, + double value, + String description) + { + this.name = name; + this.quantityUnit = quantityUnit; + this.value = value; + this.description = description; + } + + /** + * Creates a new {@code BasicNamedDouble} object based on a {@link Constant.Double}. + * + * @param cst the provided Constant.DOuble instance + */ + public NamedDouble (Constant.Double cst) + { + this(cst.getName(), cst.getQuantityUnit(), cst.getValue(), cst.getDescription()); + } + + /** + * Get the description sentence recorded with the NamedDouble + * + * @return the description sentence as a string + */ + public String getDescription () + { + return description; + } + + /** + * Report the name of the NamedDouble + * + * @return the NamedDouble name + */ + public String getName () + { + return name; + } + + /** + * Report the unit, if any, used as base of quantity measure + * + * @return the quantity unit, if any + */ + public String getQuantityUnit () + { + return quantityUnit; + } + + /** + * Report the current value + * + * @return current value + */ + public Double getValue () + { + return value; + } + + /** + * Assign a new value + * + * @param value value to be assigned + */ + public void setValue (double value) + { + this.value = value; + } +} diff --git a/src/main/org/audiveris/omr/util/Navigable.java b/src/main/org/audiveris/omr/util/Navigable.java index a9e045d20..ff53ff648 100644 --- a/src/main/org/audiveris/omr/util/Navigable.java +++ b/src/main/org/audiveris/omr/util/Navigable.java @@ -31,6 +31,11 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Flag a field member ar user navigable or not + * + * @author Hervé Bitteur + */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) diff --git a/src/main/org/audiveris/omr/util/Nest.java b/src/main/org/audiveris/omr/util/Nest.java deleted file mode 100644 index b7f663fc7..000000000 --- a/src/main/org/audiveris/omr/util/Nest.java +++ /dev/null @@ -1,132 +0,0 @@ -//------------------------------------------------------------------------------------------------// -// // -// N e s t // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.util; - -import org.audiveris.omr.ui.selection.SelectionService; - -import java.awt.Rectangle; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -/** - * Interface {@code Nest} describes a collection manager, with the ability to retrieve - * a member within the collection based on its ID or its location. - * It also handles a SelectionService that deals with member selection. - * - * @param specific type for collection member - * - * @author Hervé Bitteur - */ -public interface Nest -{ - //~ Methods ------------------------------------------------------------------------------------ - - /** - * Look up for all members that are contained in the provided rectangle - * - * @param rect the provided containing rectangle - * @return the member found, which may be empty - */ - Set containedMembers (Rectangle rect); - - /** - * Remove link and subscription to locationService - * - * @param locationService the location service - */ - void cutServices (SelectionService locationService); - - /** - * Export the whole unmodifiable collection of members in the nest. - * - * @return the collection of members - */ - Collection getAllMembers (); - - /** - * Retrieve a member via its Id. - * - * @param id the member id to search for - * @return the member found, or null otherwise - */ - T getMember (int id); - - /** - * Report the nest selection service. - * - * @return the nest selection service - */ - SelectionService getMemberService (); - - /** - * Report a name for this nest instance. - * - * @return a (distinguished) name - */ - String getName (); - - /** - * Report the currently selected list of members if any - * - * @return the current list of members, or null - */ - List getSelectedMembers (); - - /** - * Look up for all members that intersect the provided rectangle. - * - * @param rect the coordinates rectangle - * @return the members found, which may be an empty list - */ - Set intersectedMembers (Rectangle rect); - - /** - * Check whether the provided member is among the VIP ones - * - * @param member the member (ID) to check - * @return true if this is a VIP member - */ - boolean isVip (T member); - - /** - * Assign a unique id (within this Nest instance) to the provided member. - * - * @param member the provided member - * @return the assigned unique id - */ - int register (T member); - - /** - * Remove the provided member - * - * @param member the member to remove - */ - void remove (T member); - - /** - * Inject dependency on location service, and trigger subscriptions - * - * @param locationService the location service - */ - void setServices (SelectionService locationService); -} diff --git a/src/main/org/audiveris/omr/util/OmrExecutors.java b/src/main/org/audiveris/omr/util/OmrExecutors.java index 7af3c1a0d..11282c47a 100644 --- a/src/main/org/audiveris/omr/util/OmrExecutors.java +++ b/src/main/org/audiveris/omr/util/OmrExecutors.java @@ -39,7 +39,8 @@ /** * Class {@code OmrExecutors} handles several pools of threads provided to Audiveris - * application:
                                                                                                                                                              + * application: + *
                                                                                                                                                                *
                                                                                                                                                              • lowExecutor: a fixed nb (#cpu+1) of threads with low priority
                                                                                                                                                              • *
                                                                                                                                                              • highExecutor: a fixed nb (#cpu+1) of threads with high priority
                                                                                                                                                              • *
                                                                                                                                                              • cachedLowExecutor: a varying nb of threads with low priority
                                                                                                                                                              • @@ -49,7 +50,6 @@ */ public class OmrExecutors { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(OmrExecutors.class); @@ -61,15 +61,6 @@ public class OmrExecutors /** Number of processors available. */ private static final int cpuCount = Runtime.getRuntime().availableProcessors(); - static { - if (constants.printEnvironment.isSet()) { - logger.info( - "Environment. CPU count: {}, Use of parallelism: {}", - cpuCount, - defaultParallelism.getValue()); - } - } - // Specific pools private static final Pool highs = new Highs(); @@ -83,7 +74,15 @@ public class OmrExecutors /** To prevent parallel creation of pools when closing. */ private static volatile boolean creationAllowed = true; - //~ Constructors ------------------------------------------------------------------------------- + static { + if (constants.printEnvironment.isSet()) { + logger.info( + "Environment. CPU count: {}, Use of parallelism: {}", + cpuCount, + defaultParallelism.getValue()); + } + } + /** * Not meant to be instantiated. */ @@ -91,7 +90,6 @@ private OmrExecutors () { } - //~ Methods ------------------------------------------------------------------------------------ //----------------------// // getCachedLowExecutor // //----------------------// @@ -187,18 +185,15 @@ public static boolean shutdown () return result; } - //~ Inner Classes ------------------------------------------------------------------------------ //------// // Pool // //------// private abstract static class Pool { - //~ Instance fields ------------------------------------------------------------------------ /** The underlying pool of threads. */ protected ExecutorService pool; - //~ Methods -------------------------------------------------------------------------------- /** * Name the pool. */ @@ -286,10 +281,9 @@ public synchronized boolean isActive () //-----------// // Constants // //-----------// - private static final class Constants + private static class Constants extends ConstantSet { - //~ Instance fields ------------------------------------------------------------------------ private final Constant.Boolean printEnvironment = new Constant.Boolean( false, @@ -312,7 +306,6 @@ private static final class Constants private static class CachedLows extends Pool { - //~ Methods -------------------------------------------------------------------------------- @Override public String getName () @@ -333,7 +326,6 @@ protected ExecutorService createPool () private static class Default extends Param { - //~ Methods -------------------------------------------------------------------------------- @Override public Boolean getSpecific () @@ -377,7 +369,6 @@ public boolean setSpecific (Boolean specific) private static class Factory implements ThreadFactory { - //~ Instance fields ------------------------------------------------------------------------ private final ThreadGroup group; @@ -389,7 +380,6 @@ private static class Factory private final AtomicInteger threadNumber = new AtomicInteger(0); - //~ Constructors --------------------------------------------------------------------------- Factory (String threadPrefix, int threadPriority, long stackSize) @@ -401,7 +391,6 @@ private static class Factory this.stackSize = stackSize; } - //~ Methods -------------------------------------------------------------------------------- @Override public Thread newThread (Runnable r) { @@ -431,7 +420,6 @@ private String getOneThreadName () private static class Highs extends Pool { - //~ Methods -------------------------------------------------------------------------------- @Override public String getName () @@ -455,7 +443,6 @@ protected ExecutorService createPool () private static class Lows extends Pool { - //~ Methods -------------------------------------------------------------------------------- @Override public String getName () diff --git a/src/main/org/audiveris/omr/util/PathHistory.java b/src/main/org/audiveris/omr/util/PathHistory.java index 82246782e..fef9ee428 100644 --- a/src/main/org/audiveris/omr/util/PathHistory.java +++ b/src/main/org/audiveris/omr/util/PathHistory.java @@ -42,11 +42,9 @@ */ public class PathHistory { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(PathHistory.class); - //~ Instance fields ---------------------------------------------------------------------------- /** Underlying list of names. */ private final NameSet nameSet; @@ -56,7 +54,6 @@ public class PathHistory /** Related UI menu, if any. Null when no UI is used */ private HistoryMenu menu; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PathHistory} object. * @@ -74,10 +71,14 @@ public PathHistory (String name, this.folderConstant = folderConstant; } - //~ Methods ------------------------------------------------------------------------------------ //-----// // add // //-----// + /** + * Add a path to history + * + * @param path the path to include + */ public void add (Path path) { nameSet.add(path.toAbsolutePath().toString()); @@ -90,8 +91,7 @@ public void add (Path path) if (OMR.gui != null) { // Enable input history menu - SwingUtilities.invokeLater( - new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run () @@ -105,6 +105,13 @@ public void run () //----------// // feedMenu // //----------// + /** + * Populate a menu with path history. + * + * @param menu menu to populate, if null it is allocated + * @param itemListener listener for each menu item + * @return the populated menu + */ public JMenu feedMenu (JMenu menu, final ActionListener itemListener) { @@ -114,6 +121,11 @@ public JMenu feedMenu (JMenu menu, //---------// // isEmpty // //---------// + /** + * Tell whether history is empty. + * + * @return true if so + */ public boolean isEmpty () { return nameSet.isEmpty(); @@ -122,6 +134,12 @@ public boolean isEmpty () //--------// // remove // //--------// + /** + * Remove a path from history + * + * @param path to be removed + * @return true if actually removed + */ public boolean remove (Path path) { return nameSet.remove(path.toAbsolutePath().toString()); @@ -130,6 +148,11 @@ public boolean remove (Path path) //---------// // setMenu // //---------// + /** + * Set the related UI menu + * + * @param menu the related menu + */ public void setMenu (HistoryMenu menu) { this.menu = menu; diff --git a/src/main/org/audiveris/omr/util/PathTask.java b/src/main/org/audiveris/omr/util/PathTask.java index e548210bb..ddda6c471 100644 --- a/src/main/org/audiveris/omr/util/PathTask.java +++ b/src/main/org/audiveris/omr/util/PathTask.java @@ -31,11 +31,10 @@ public abstract class PathTask extends VoidTask { - //~ Instance fields ---------------------------------------------------------------------------- + /** Underlying path. */ protected Path path; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new {@code PathTask} object. */ @@ -53,7 +52,11 @@ protected PathTask (Path path) this.path = path; } - //~ Methods ------------------------------------------------------------------------------------ + /** + * Set the path value. + * + * @param path the path used by the task + */ public void setPath (Path path) { this.path = path; diff --git a/src/main/org/audiveris/omr/util/Predicate.java b/src/main/org/audiveris/omr/util/Predicate.java index bbf2254b7..91f1d7c30 100644 --- a/src/main/org/audiveris/omr/util/Predicate.java +++ b/src/main/org/audiveris/omr/util/Predicate.java @@ -25,10 +25,10 @@ * Interface {@code Predicate} is used to specify a filter on a provided entity. * * @param Specific type of the entity on which the predicate is checked + * @author Hervé Bitteur */ public interface Predicate { - //~ Methods ------------------------------------------------------------------------------------ /** * Run a check on the provided entity, and return the result diff --git a/src/main/org/audiveris/omr/util/RegexUtil.java b/src/main/org/audiveris/omr/util/RegexUtil.java index aa794a90b..fdfca7b32 100644 --- a/src/main/org/audiveris/omr/util/RegexUtil.java +++ b/src/main/org/audiveris/omr/util/RegexUtil.java @@ -30,14 +30,12 @@ */ public abstract class RegexUtil { - //~ Constructors ------------------------------------------------------------------------------- - // Not meant to be instantiated + /** Not meant to be instantiated. */ private RegexUtil () { } - //~ Methods ------------------------------------------------------------------------------------ //--------// // escape // //--------// diff --git a/src/main/org/audiveris/omr/util/StopWatch.java b/src/main/org/audiveris/omr/util/StopWatch.java index ccffcf974..089d7b60d 100644 --- a/src/main/org/audiveris/omr/util/StopWatch.java +++ b/src/main/org/audiveris/omr/util/StopWatch.java @@ -32,21 +32,19 @@ */ public class StopWatch { - //~ Instance fields ---------------------------------------------------------------------------- - /** Name for this watch instance */ + /** Name for this watch instance. */ private final String name; - /** Sequence of all tasks so far */ - private final List tasks = new ArrayList(128); + /** Sequence of all tasks so far. */ + private final List tasks = new ArrayList<>(128); - /** Current task (null if not running) */ + /** Current task (null if not running). */ private Task task; - /** Current sum of tasks times */ + /** Current sum of tasks durations. */ private long total; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new StopWatch object. * @@ -57,10 +55,12 @@ public StopWatch (String name) this.name = name; } - //~ Methods ------------------------------------------------------------------------------------ //-------// // print // //-------// + /** + * Print watch on default stream. + */ public void print () { print(System.out); @@ -69,6 +69,11 @@ public void print () //-------// // print // //-------// + /** + * Print watch on provided stream. + * + * @param out provided stream + */ public void print (PrintStream out) { stop(); @@ -99,6 +104,13 @@ public void print (PrintStream out) //-------// // start // //-------// + /** + * Start measurement of a new task. + *

                                                                                                                                                                + * Beforehand, this stops duration measurement of current task if any. + * + * @param label task name + */ public void start (String label) { if (task != null) { @@ -111,6 +123,9 @@ public void start (String label) //------// // stop // //------// + /** + * Stop duration measurement for current task. + */ public void stop () { if (task != null) { @@ -142,25 +157,22 @@ public String toString () return sb.toString(); } - //~ Inner Classes ------------------------------------------------------------------------------ //------// // Task // //------// private static class Task { - //~ Instance fields ------------------------------------------------------------------------ - /** Label for this task */ + /** Label for this task. */ private final String label; - /** Start time */ + /** Start time. */ private final long start; - /** Elapsed time */ + /** Elapsed time. */ private long elapsed; - //~ Constructors --------------------------------------------------------------------------- - public Task (String label) + Task (String label) { this.label = label; start = System.currentTimeMillis(); diff --git a/src/main/org/audiveris/omr/util/StringUtil.java b/src/main/org/audiveris/omr/util/StringUtil.java index 6d499ba5b..f4bee64ce 100644 --- a/src/main/org/audiveris/omr/util/StringUtil.java +++ b/src/main/org/audiveris/omr/util/StringUtil.java @@ -25,17 +25,28 @@ import java.util.List; /** - * Class {@code StringUtil} + * Class {@code StringUtil} provides String utilities. * * @author Hervé Bitteur */ public abstract class StringUtil { - //~ Methods ------------------------------------------------------------------------------------ + + /** Not meant to be instantiated. */ + private StringUtil () + { + } //---------// // compare // //---------// + /** + * Compare two Strings, handling null cases. + * + * @param s1 a string + * @param s2 another string + * @return comparison result (-1, 0, +1) + */ public static int compare (String s1, String s2) { @@ -58,14 +69,14 @@ public static int compare (String s1, // parseInts // //-----------// /** - * Parse a string of strings, separated by comma. + * Parse a string of integer tokens, separated by comma. * * @param str the string to parse * @return the sequence of strings */ public static List parseStrings (String str) { - final List strList = new ArrayList(); + final List strList = new ArrayList<>(); final String[] tokens = str.split("\\s*,\\s*"); for (String token : tokens) { diff --git a/src/main/org/audiveris/omr/util/TreeNode.java b/src/main/org/audiveris/omr/util/TreeNode.java index 5fcf5255c..0ee59eac1 100644 --- a/src/main/org/audiveris/omr/util/TreeNode.java +++ b/src/main/org/audiveris/omr/util/TreeNode.java @@ -31,31 +31,32 @@ * Class {@code TreeNode} handles a node in a tree hierarchy, * which is typically the tree organization of a score entity. *

                                                                                                                                                                - * A TreeNode has :

                                                                                                                                                                • A parent (which may be null) to which the TreeNode - * belongs
                                                                                                                                                                • A list (which may be empty) of contained chidren, for which the - * TreeNode is the parent.
                                                                                                                                                                + * A TreeNode has : + *
                                                                                                                                                                  + *
                                                                                                                                                                • A parent (which may be null) to which the TreeNode + * belongs + *
                                                                                                                                                                • A list (which may be empty) of contained chidren, for which the + * TreeNode is the parent. + *
                                                                                                                                                                * * @author Hervé Bitteur */ public abstract class TreeNode { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(TreeNode.class); - //~ Instance fields ---------------------------------------------------------------------------- /** - * Children : the list of nodes just below in the tree + * Children : the list of nodes just below in the tree. */ - protected final List children = new ArrayList(); + protected final List children = new ArrayList<>(); /** - * Container : the node just above in the tree + * Container : the node just above in the tree. */ @Navigable(false) protected TreeNode parent; - //~ Constructors ------------------------------------------------------------------------------- /** * Create a node in the tree, given its parent. * @@ -70,7 +71,6 @@ public TreeNode (TreeNode parent) } } - //~ Methods ------------------------------------------------------------------------------------ //----------// // addChild // //----------// @@ -193,7 +193,7 @@ public synchronized List getChildrenCopy () { logger.debug("getChildrenCopy of {}", this); - return new ArrayList(children); + return new ArrayList<>(children); } //----------------// @@ -230,6 +230,20 @@ public TreeNode getParent () return parent; } + //-----------// + // setParent // + //-----------// + /** + * Modify the link to the parent of this node. + * + * @param parent the (new) parent + */ + public void setParent (TreeNode parent) + { + logger.debug("setParent parent={} for {}", parent, this); + this.parent = parent; + } + //--------------------// // getPreviousSibling // //--------------------// @@ -268,17 +282,4 @@ public void setChildrenParent () } } - //-----------// - // setParent // - //-----------// - /** - * Modify the link to the parent of this node. - * - * @param parent the (new) parent - */ - public void setParent (TreeNode parent) - { - logger.debug("setParent parent={} for {}", parent, this); - this.parent = parent; - } } diff --git a/src/main/org/audiveris/omr/util/UriUtil.java b/src/main/org/audiveris/omr/util/UriUtil.java index 297b01bc0..9af66095f 100644 --- a/src/main/org/audiveris/omr/util/UriUtil.java +++ b/src/main/org/audiveris/omr/util/UriUtil.java @@ -28,12 +28,17 @@ import java.net.URL; /** + * URI utilities. * * @author Hervé Bitteur */ public abstract class UriUtil { - //~ Methods ------------------------------------------------------------------------------------ + + /** Not meant to be instantiated. */ + private UriUtil () + { + } //--------// // toFile // diff --git a/src/main/org/audiveris/omr/util/Vip.java b/src/main/org/audiveris/omr/util/Vip.java index 1a7b96f90..2a1260c1d 100644 --- a/src/main/org/audiveris/omr/util/Vip.java +++ b/src/main/org/audiveris/omr/util/Vip.java @@ -29,7 +29,6 @@ */ public interface Vip { - //~ Methods ------------------------------------------------------------------------------------ /** * (Debug) Report whether this object is flagged as VIP diff --git a/src/main/org/audiveris/omr/util/WeakPropertyChangeListener.java b/src/main/org/audiveris/omr/util/WeakPropertyChangeListener.java index ec3a08974..27f19e6b4 100644 --- a/src/main/org/audiveris/omr/util/WeakPropertyChangeListener.java +++ b/src/main/org/audiveris/omr/util/WeakPropertyChangeListener.java @@ -34,12 +34,10 @@ public class WeakPropertyChangeListener implements PropertyChangeListener { - //~ Instance fields ---------------------------------------------------------------------------- /** The concrete listener */ protected final WeakReference weakListener; - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new WeakPropertyChangeListener object from a concrete listener * @@ -47,10 +45,9 @@ public class WeakPropertyChangeListener */ public WeakPropertyChangeListener (PropertyChangeListener listener) { - weakListener = new WeakReference(listener); + weakListener = new WeakReference<>(listener); } - //~ Methods ------------------------------------------------------------------------------------ //----------------// // propertyChange // //----------------// diff --git a/src/main/org/audiveris/omr/util/WindowsRegistry.java b/src/main/org/audiveris/omr/util/WindowsRegistry.java index e4045317c..41a3db4b6 100644 --- a/src/main/org/audiveris/omr/util/WindowsRegistry.java +++ b/src/main/org/audiveris/omr/util/WindowsRegistry.java @@ -21,10 +21,13 @@ // package org.audiveris.omr.util; +import org.audiveris.omr.WellKnowns; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; @@ -39,11 +42,14 @@ */ public class WindowsRegistry { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(WindowsRegistry.class); - //~ Methods ------------------------------------------------------------------------------------ + /** Not meant to be instantiated. */ + private WindowsRegistry () + { + } + //-------// // query // //-------// @@ -56,14 +62,16 @@ public class WindowsRegistry public static List query (String... args) { // Output lines - List output = new ArrayList(); + List output = new ArrayList<>(); // Command arguments - List cmdArgs = new ArrayList(); + List cmdArgs = new ArrayList<>(); cmdArgs.addAll(Arrays.asList("cmd.exe", "/c", "reg", "query")); cmdArgs.addAll(Arrays.asList(args)); logger.debug("cmdArgs: {}", cmdArgs); + BufferedReader br = null; + try { // Spawn cmd process ProcessBuilder pb = new ProcessBuilder(cmdArgs); @@ -73,8 +81,9 @@ public static List query (String... args) // Read output InputStream is = process.getInputStream(); - InputStreamReader isr = new InputStreamReader(is); - BufferedReader br = new BufferedReader(isr); + InputStreamReader isr = new InputStreamReader(is, WellKnowns.FILE_ENCODING); + br = new BufferedReader(isr); + String line; while ((line = br.readLine()) != null) { @@ -84,8 +93,19 @@ public static List query (String... args) // Wait for process completion int exitValue = process.waitFor(); logger.debug("Exit value is: {}", exitValue); - } catch (Exception ex) { + } catch (RuntimeException rex) { + throw rex; + } catch (IOException | + InterruptedException ex) { logger.warn("Error running " + cmdArgs, ex); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException ex) { + logger.warn("Error closing cmd reader {}", ex.toString(), ex); + } + } } return output; diff --git a/src/main/org/audiveris/omr/util/WrappedBoolean.java b/src/main/org/audiveris/omr/util/WrappedBoolean.java index 99f436df2..756bea0c3 100644 --- a/src/main/org/audiveris/omr/util/WrappedBoolean.java +++ b/src/main/org/audiveris/omr/util/WrappedBoolean.java @@ -30,7 +30,6 @@ public class WrappedBoolean extends Wrapper { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new WrappedBoolean object. @@ -42,7 +41,6 @@ public WrappedBoolean (boolean value) super(value); } - //~ Methods ------------------------------------------------------------------------------------ //-----// // set // //-----// diff --git a/src/main/org/audiveris/omr/util/WrappedDouble.java b/src/main/org/audiveris/omr/util/WrappedDouble.java index 812324fb7..232f87f7b 100644 --- a/src/main/org/audiveris/omr/util/WrappedDouble.java +++ b/src/main/org/audiveris/omr/util/WrappedDouble.java @@ -30,7 +30,6 @@ public class WrappedDouble extends Wrapper { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new WrappedDouble object. diff --git a/src/main/org/audiveris/omr/util/WrappedInteger.java b/src/main/org/audiveris/omr/util/WrappedInteger.java index 031b3c0f6..9d23d16f1 100644 --- a/src/main/org/audiveris/omr/util/WrappedInteger.java +++ b/src/main/org/audiveris/omr/util/WrappedInteger.java @@ -30,7 +30,6 @@ public class WrappedInteger extends Wrapper { - //~ Constructors ------------------------------------------------------------------------------- /** * Creates a new WrappedInteger object. diff --git a/src/main/org/audiveris/omr/util/Wrapper.java b/src/main/org/audiveris/omr/util/Wrapper.java index 6670b8e7a..c1a979a38 100644 --- a/src/main/org/audiveris/omr/util/Wrapper.java +++ b/src/main/org/audiveris/omr/util/Wrapper.java @@ -22,25 +22,27 @@ package org.audiveris.omr.util; /** - * Class {@code Wrapper} is used to wrap a mutable output value + * Class {@code Wrapper} is used to wrap a mutable input value * * @param The specific type for carried value - * * @author Hervé Bitteur */ public class Wrapper { - //~ Instance fields ---------------------------------------------------------------------------- - /** The wrapped value */ + /** The wrapped value. */ public T value; + /** + * Creates a new {@code Wrapper} object. + * + * @param value underlying value + */ public Wrapper (T value) { this.value = value; } - //~ Methods ------------------------------------------------------------------------------------ //----------// // toString // //----------// diff --git a/src/main/org/audiveris/omr/util/XmlUtil.java b/src/main/org/audiveris/omr/util/XmlUtil.java index 10236d63e..eaef2d25b 100644 --- a/src/main/org/audiveris/omr/util/XmlUtil.java +++ b/src/main/org/audiveris/omr/util/XmlUtil.java @@ -28,7 +28,11 @@ */ public abstract class XmlUtil { - //~ Methods ------------------------------------------------------------------------------------ + + /** Not meant to be instantiated. */ + private XmlUtil () + { + } //----------------------------// // stripNonValidXMLCharacters // @@ -36,14 +40,14 @@ public abstract class XmlUtil /** * Copied from Mark Mclaren blog: * http://cse-mjmcl.cse.bris.ac.uk/blog/2007/02/14/1171465494443.html - * + *

                                                                                                                                                                * This method ensures that the output String has only valid XML * unicode characters as specified by the XML 1.0 standard. - * + *

                                                                                                                                                                * For reference, please * see * the standard. - * + *

                                                                                                                                                                * Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | * [#x10000-#x10FFFF] * (any Unicode character, excluding the surrogate blocks, FFFE, and FFFF) @@ -63,12 +67,11 @@ public static String stripNonValidXMLCharacters (String input, StringBuilder sb = new StringBuilder(); for (char c : input.toCharArray()) { - if ((c == 0x9) - || (c == 0xA) - || (c == 0xD) - || ((c >= 0x20) && (c <= 0xD7FF)) - || ((c >= 0xE000) && (c <= 0xFFFD)) - || ((c >= 0x10000) && (c <= 0x10FFFF))) { + if ((c == 0x9) || (c == 0xA) + || (c == 0xD) + || ((c >= 0x20) && (c <= 0xD7FF)) + || ((c >= 0xE000) && (c <= 0xFFFD)) + || ((c >= 0x10000) && (c <= 0x10FFFF))) { sb.append(c); } else if (stripped != null) { stripped.set(true); diff --git a/src/main/org/audiveris/omr/util/Zip.java b/src/main/org/audiveris/omr/util/Zip.java index ecaa620f5..54a696ec6 100644 --- a/src/main/org/audiveris/omr/util/Zip.java +++ b/src/main/org/audiveris/omr/util/Zip.java @@ -21,6 +21,8 @@ // package org.audiveris.omr.util; +import org.audiveris.omr.WellKnowns; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,21 +44,21 @@ /** * Class {@code Zip} is a convenient utility that provides both writing and reading * from a file which is transparently compressed in Zip. + *

                                                                                                                                                                + * No longer used. * * @author Hervé Bitteur */ public abstract class Zip { - //~ Static fields/initializers ----------------------------------------------------------------- private static final Logger logger = LoggerFactory.getLogger(Zip.class); - //~ Constructors ------------------------------------------------------------------------------- + /** Not meant to be instantiated. */ private Zip () { } - //~ Methods ------------------------------------------------------------------------------------ //--------------------// // createInputStream // //-------------------// @@ -65,7 +67,6 @@ private Zip () * and by reading the first entry in this zip file. * * @param file the zip file - * * @return a InputStream on the zip entry */ public static InputStream createInputStream (File file) @@ -98,7 +99,6 @@ public static InputStream createInputStream (File file) * extension added. * * @param file the file (with no zip extension) - * * @return a OutputStream on the zip entry */ public static OutputStream createOutputStream (File file) @@ -114,7 +114,7 @@ public static OutputStream createOutputStream (File file) } catch (FileNotFoundException ex) { System.err.println(ex.toString()); System.err.println(file + " not found"); - } catch (Exception ex) { + } catch (IOException ex) { System.err.println(ex.toString()); } @@ -130,7 +130,6 @@ public static OutputStream createOutputStream (File file) * this zip file. * * @param file the file (with no zip extension) - * * @return a reader on the zip entry */ public static Reader createReader (File file) @@ -144,7 +143,7 @@ public static Reader createReader (File file) ZipEntry entry = (ZipEntry) entries.nextElement(); InputStream is = zf.getInputStream(entry); - return new InputStreamReader(is); + return new InputStreamReader(is, WellKnowns.FILE_ENCODING); } } catch (FileNotFoundException ex) { System.err.println(ex.toString()); @@ -165,7 +164,6 @@ public static Reader createReader (File file) * added. * * @param file the file (with no zip extension) - * * @return a writer on the zip entry */ public static Writer createWriter (File file) @@ -177,11 +175,11 @@ public static Writer createWriter (File file) ZipEntry ze = new ZipEntry(file.getName()); zos.putNextEntry(ze); - return new OutputStreamWriter(zos); + return new OutputStreamWriter(zos, WellKnowns.FILE_ENCODING); } catch (FileNotFoundException ex) { System.err.println(ex.toString()); System.err.println(file + " not found"); - } catch (Exception ex) { + } catch (IOException ex) { System.err.println(ex.toString()); } @@ -191,8 +189,17 @@ public static Writer createWriter (File file) //---------// // isEmpty // //---------// + /** + * Tell whether the archive file is empty. + * + * @param file provided file + * @return true if so + * @throws FileNotFoundException if file is not found + * @throws IOException if any IO goes wrong + */ public static boolean isEmpty (File file) - throws FileNotFoundException, IOException + throws FileNotFoundException, + IOException { String path = file.getCanonicalPath(); ZipFile zf = new ZipFile(path); diff --git a/src/main/org/audiveris/omr/util/ZipFileSystem.java b/src/main/org/audiveris/omr/util/ZipFileSystem.java index eff5a4dcd..92a51a8f5 100644 --- a/src/main/org/audiveris/omr/util/ZipFileSystem.java +++ b/src/main/org/audiveris/omr/util/ZipFileSystem.java @@ -1,104 +1,108 @@ -//------------------------------------------------------------------------------------------------// -// // -// Z i p F i l e S y s t e m // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.util; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Objects; -import java.util.zip.ZipOutputStream; - -/** - * Class {@code ZipFileSystem} gathers utility methods to read from and write to a zip - * file (considered as a file system). - *

                                                                                                                                                                - * The {@link #create(java.nio.file.Path)} and {@link #open(java.nio.file.Path)} methods expect an - * abstract path to a zip file and return the root path of the zip file system. - * This root path can subsequently be used to create/delete/read/write folders and files - * transparently under this root umbrella. - *

                                                                                                                                                                - * When IO operations are finished, the file system must be closed via a {@link FileSystem#close()} - * on the root path like {@code root.getFileSystem().close();} - * - * @author Hervé Bitteur - */ -public abstract class ZipFileSystem -{ - //~ Methods ------------------------------------------------------------------------------------ - - //--------// - // create // - //--------// - /** - * Create a new zip file system at the location provided by '{@code path}' parameter. - * If such file already exists, it is deleted beforehand. - *

                                                                                                                                                                - * When IO operations are finished, the file system must be closed via {@link FileSystem#close} - * - * @param path path to zip file system - * @return the root path of the (zipped) file system - * @throws IOException if anything goes wrong - */ - public static Path create (Path path) - throws IOException - { - Objects.requireNonNull(path, "ZipFileSystem.create: path is null"); - - Files.deleteIfExists(path); - - // Make sure the containing folder exists - Files.createDirectories(path.getParent()); - - // Make it a zip file - ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(path.toFile())); - zos.close(); - - // Finally open the file system just created - return open(path); - } - - //------// - // open // - //------// - /** - * Open a zip file system (supposed to already exist at location provided by - * '{@code path}' parameter) for reading or writing. - *

                                                                                                                                                                - * When IO operations are finished, the file system must be closed via {@link FileSystem#close} - * - * @param path (zip) file path - * @return the root path of the (zipped) file system - * @throws IOException if anything goes wrong - */ - public static Path open (Path path) - throws IOException - { - Objects.requireNonNull(path, "ZipFileSystem.open: path is null"); - - FileSystem fileSystem = FileSystems.newFileSystem(path, null); - - return fileSystem.getPath(fileSystem.getSeparator()); - } -} +//------------------------------------------------------------------------------------------------// +// // +// Z i p F i l e S y s t e m // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.util; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.zip.ZipOutputStream; + +/** + * Class {@code ZipFileSystem} gathers utility methods to read from and write to a zip + * file (considered as a file system). + *

                                                                                                                                                                + * The {@link #create(java.nio.file.Path)} and {@link #open(java.nio.file.Path)} methods expect an + * abstract path to a zip file and return the root path of the zip file system. + * This root path can subsequently be used to create/delete/read/write folders and files + * transparently under this root umbrella. + *

                                                                                                                                                                + * When IO operations are finished, the file system must be closed via a {@link FileSystem#close()} + * on the root path like {@code root.getFileSystem().close();} + * + * @author Hervé Bitteur + */ +public abstract class ZipFileSystem +{ + + /** Not meant to be instantiated. */ + private ZipFileSystem () + { + } + + //--------// + // create // + //--------// + /** + * Create a new zip file system at the location provided by '{@code path}' parameter. + * If such file already exists, it is deleted beforehand. + *

                                                                                                                                                                + * When IO operations are finished, the file system must be closed via {@link FileSystem#close} + * + * @param path path to zip file system + * @return the root path of the (zipped) file system + * @throws IOException if anything goes wrong + */ + public static Path create (Path path) + throws IOException + { + Objects.requireNonNull(path, "ZipFileSystem.create: path is null"); + + Files.deleteIfExists(path); + + // Make sure the containing folder exists + Files.createDirectories(path.getParent()); + + // Make it a zip file + ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(path.toFile())); + zos.close(); + + // Finally open the file system just created + return open(path); + } + + //------// + // open // + //------// + /** + * Open a zip file system (supposed to already exist at location provided by + * '{@code path}' parameter) for reading or writing. + *

                                                                                                                                                                + * When IO operations are finished, the file system must be closed via {@link FileSystem#close} + * + * @param path (zip) file path + * @return the root path of the (zipped) file system + * @throws IOException if anything goes wrong + */ + public static Path open (Path path) + throws IOException + { + Objects.requireNonNull(path, "ZipFileSystem.open: path is null"); + + FileSystem fileSystem = FileSystems.newFileSystem(path, null); + + return fileSystem.getPath(fileSystem.getSeparator()); + } +} diff --git a/src/main/org/audiveris/omr/util/param/BooleanParam.java b/src/main/org/audiveris/omr/util/param/BooleanParam.java index b53db1737..ef3fa9c14 100644 --- a/src/main/org/audiveris/omr/util/param/BooleanParam.java +++ b/src/main/org/audiveris/omr/util/param/BooleanParam.java @@ -1,72 +1,73 @@ -//------------------------------------------------------------------------------------------------// -// // -// B o o l e a n P a r a m // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.util.param; - -import javax.xml.bind.annotation.adapters.XmlAdapter; - -/** - * Class {@code BooleanParam} is a Param for Boolean. - * - * @author Hervé Bitteur - */ -public class BooleanParam - extends Param -{ - //~ Inner Classes ------------------------------------------------------------------------------ - - public static class Adapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public String marshal (BooleanParam val) - throws Exception - { - if (val == null) { - return null; - } - - Boolean spc = val.getSpecific(); - - if (spc == null) { - return null; - } - - return spc.toString(); - } - - @Override - public BooleanParam unmarshal (String str) - throws Exception - { - if ((str == null) || str.trim().isEmpty()) { - return null; - } - - BooleanParam b = new BooleanParam(); - b.setSpecific(Boolean.parseBoolean(str)); - - return b; - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// B o o l e a n P a r a m // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.util.param; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * Class {@code BooleanParam} is a Param for Boolean. + * + * @author Hervé Bitteur + */ +public class BooleanParam + extends Param +{ + + /** + * JAXB adapter for BooleanParam type. + */ + public static class Adapter + extends XmlAdapter + { + + @Override + public String marshal (BooleanParam val) + throws Exception + { + if (val == null) { + return null; + } + + Boolean spc = val.getSpecific(); + + if (spc == null) { + return null; + } + + return spc.toString(); + } + + @Override + public BooleanParam unmarshal (String str) + throws Exception + { + if ((str == null) || str.trim().isEmpty()) { + return null; + } + + BooleanParam b = new BooleanParam(); + b.setSpecific(Boolean.parseBoolean(str)); + + return b; + } + } +} diff --git a/src/main/org/audiveris/omr/util/param/ConstantBasedParam.java b/src/main/org/audiveris/omr/util/param/ConstantBasedParam.java index 28b1f2e72..dfe6804df 100644 --- a/src/main/org/audiveris/omr/util/param/ConstantBasedParam.java +++ b/src/main/org/audiveris/omr/util/param/ConstantBasedParam.java @@ -1,109 +1,104 @@ -//------------------------------------------------------------------------------------------------// -// // -// C o n s t a n t B a s e d P a r a m // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.util.param; - -import org.audiveris.omr.constant.Constant; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Class {@code ConstantBasedParam} is a {@link Param} backed by an application constant - * as is the case for many default Param. - * - * @author Hervé Bitteur - * - * @param type for value - * @param type for value constant - */ -public class ConstantBasedParam - extends Param -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(ConstantBasedParam.class); - - //~ Instance fields ---------------------------------------------------------------------------- - private final C cst; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code ConstantBasedParam} object. - * - * @param constant the underlying constant - */ - public ConstantBasedParam (C constant) - { - this.cst = constant; - } - - //~ Methods ------------------------------------------------------------------------------------ - @Override - public E getSourceValue () - { - return (E) cst.getSourceValue(); - } - - @Override - public E getSpecific () - { - if (cst.isSourceValue()) { - return null; - } else { - return (E) cst.getValue(); - } - } - - @Override - public E getValue () - { - return (E) cst.getValue(); - } - - @Override - public boolean isSpecific () - { - return !cst.isSourceValue(); - } - - @Override - public boolean setSpecific (E specific) - { - if (!getValue().equals(specific)) { - if (specific == null) { - if (!cst.isSourceValue()) { - cst.resetToSource(); - logger.info( - "Default " + cst.getDescription() + " reset to {}", - cst.getSourceValue()); - } - } else { - cst.setValue(specific); - logger.info("Default " + cst.getDescription() + " set to {}", cst.getValue()); - } - - return true; - } - - return false; - } -} +//------------------------------------------------------------------------------------------------// +// // +// C o n s t a n t B a s e d P a r a m // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.util.param; + +import org.audiveris.omr.constant.Constant; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class {@code ConstantBasedParam} is a {@link Param} backed by an application constant + * as is the case for many default Param. + * + * @author Hervé Bitteur + * @param type for value + * @param type for value constant + */ +public class ConstantBasedParam + extends Param +{ + + private static final Logger logger = LoggerFactory.getLogger(ConstantBasedParam.class); + + private final C cst; + + /** + * Creates a new {@code ConstantBasedParam} object. + * + * @param constant the underlying constant + */ + public ConstantBasedParam (C constant) + { + this.cst = constant; + } + + @Override + public E getSourceValue () + { + return (E) cst.getSourceValue(); + } + + @Override + public E getSpecific () + { + if (cst.isSourceValue()) { + return null; + } else { + return (E) cst.getValue(); + } + } + + @Override + public E getValue () + { + return (E) cst.getValue(); + } + + @Override + public boolean isSpecific () + { + return !cst.isSourceValue(); + } + + @Override + public boolean setSpecific (E specific) + { + if (!getValue().equals(specific)) { + if (specific == null) { + if (!cst.isSourceValue()) { + cst.resetToSource(); + logger.info( + "Default " + cst.getDescription() + " reset to {}", + cst.getSourceValue()); + } + } else { + cst.setValue(specific); + logger.info("Default " + cst.getDescription() + " set to {}", cst.getValue()); + } + + return true; + } + + return false; + } +} diff --git a/src/main/org/audiveris/omr/util/param/Param.java b/src/main/org/audiveris/omr/util/param/Param.java index b23323611..32251fd53 100644 --- a/src/main/org/audiveris/omr/util/param/Param.java +++ b/src/main/org/audiveris/omr/util/param/Param.java @@ -27,7 +27,8 @@ /** * Class {@code Param} defines data value at default level, book level, sheet level. *

                                                                                                                                                                - * The {@link #getValue()} reports the current data value:

                                                                                                                                                                  + * The {@link #getValue()} reports the current data value: + *
                                                                                                                                                                    *
                                                                                                                                                                  1. If the param instance has a non-null specific value, this specific value is returned. *
                                                                                                                                                                  2. Otherwise, if this instance has a registered parent param, parent.getValue() is returned. *
                                                                                                                                                                  3. Otherwise, null is returned. @@ -36,14 +37,12 @@ * Param UML * * @param type of parameter handled - * * @author Hervé Bitteur */ @XmlAccessorType(XmlAccessType.NONE) public class Param { - //~ Instance fields ---------------------------------------------------------------------------- // Persistent data //---------------- // @@ -56,7 +55,19 @@ public class Param /** Parent param, if any, to inherit from. */ protected Param parent; - //~ Methods ------------------------------------------------------------------------------------ + //----------------// + // getSourceValue // + //----------------// + /** + * Report the source value if any, by default this return null. + * + * @return the source value + */ + public E getSourceValue () + { + return null; + } + //-------------// // getSpecific // //-------------// @@ -94,19 +105,6 @@ public E getValue () return null; } - //----------------// - // getSourceValue // - //----------------// - /** - * Report the source value if any, by default this return null. - * - * @return the source value - */ - public E getSourceValue () - { - return null; - } - //------------// // isSpecific // //------------// diff --git a/src/main/org/audiveris/omr/util/param/StringParam.java b/src/main/org/audiveris/omr/util/param/StringParam.java index 21d65dbeb..b41953247 100644 --- a/src/main/org/audiveris/omr/util/param/StringParam.java +++ b/src/main/org/audiveris/omr/util/param/StringParam.java @@ -1,62 +1,63 @@ -//------------------------------------------------------------------------------------------------// -// // -// S t r i n g P a r a m // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omr.util.param; - -import javax.xml.bind.annotation.adapters.XmlAdapter; - -/** - * Class {@code StringParam} is a param for string. - * - * @author Hervé Bitteur - */ -public class StringParam - extends Param -{ - //~ Inner Classes ------------------------------------------------------------------------------ - - public static class Adapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public String marshal (StringParam val) - throws Exception - { - if (val == null) { - return null; - } - - return val.getSpecific(); - } - - @Override - public StringParam unmarshal (String str) - throws Exception - { - StringParam sp = new StringParam(); - sp.setSpecific(str); - - return sp; - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// S t r i n g P a r a m // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omr.util.param; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * Class {@code StringParam} is a param for string. + * + * @author Hervé Bitteur + */ +public class StringParam + extends Param +{ + + /** + * JAXB adapter for StringParam type. + */ + public static class Adapter + extends XmlAdapter + { + + @Override + public String marshal (StringParam val) + throws Exception + { + if (val == null) { + return null; + } + + return val.getSpecific(); + } + + @Override + public StringParam unmarshal (String str) + throws Exception + { + StringParam sp = new StringParam(); + sp.setSpecific(str); + + return sp; + } + } +} diff --git a/src/main/org/audiveris/omrdataset/api/OmrShape.java b/src/main/org/audiveris/omrdataset/api/OmrShape.java index 90ca9a20b..b820b97c3 100644 --- a/src/main/org/audiveris/omrdataset/api/OmrShape.java +++ b/src/main/org/audiveris/omrdataset/api/OmrShape.java @@ -1,350 +1,363 @@ -//------------------------------------------------------------------------------------------------// -// // -// O m r S h a p e // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omrdataset.api; - -/** - * Class {@code OmrShape} is the OMR-Dataset definition of symbol shapes. - *

                                                                                                                                                                    - * This is a small subset of the list of symbol names described by SMuFL specification available at - * http://www.smufl.org/. - * Symbols are gathered using the same group numbers and names as found in SMuFL specification. - *

                                                                                                                                                                    - * This subset is meant to focus on fixed-shape symbols. Accordingly, all symbols with varying size - * (hair-pins, slurs, beams, etc) have been left out, their recognition is not based on a symbol - * classifier. - * The only exception is the brace symbol, which can vary in size, but we hope that a - * classifier could recognize the brace center part. - *

                                                                                                                                                                    - * We added a few names:

                                                                                                                                                                      - *
                                                                                                                                                                    • none. - * This is a special name to indicate the absence of any valid symbol. - *
                                                                                                                                                                    • arpeggiato. - * We could find only arpeggiato's ending with an arrow head, either {@code arpeggiatoUp} or - * {@code arpeggiatoDown}. - *
                                                                                                                                                                    • keyFlat, keyNatural, keySharp. - * They represent flat, natural and sharp signs within a key signature. - * We hope that full-context training will allow to recognize them as such. - *
                                                                                                                                                                    - * All the barline and repeat symbols are meant to be defined for the staff height only. - * - * @see http://www.smufl.org/ - * - * @author Hervé Bitteur - */ -public enum OmrShape -{ - none("No valid shape"), - - // - // 4.1 Staff brackets and dividers - // - brace("Brace"), - bracketTop("Bracket top"), - bracketBottom("Bracket bottom"), - - // - // 4.2 Staves - // - legerLine("Leger line"), - // - // 4.3 Barlines - // - barlineSingle("Single barline"), - barlineDouble("Double barline"), - barlineFinal("Final barline"), - barlineReverseFinal("Reverse final barline"), - barlineHeavy("Heavy barline"), - barlineHeavyHeavy("Heavy double barline"), - barlineDashed("Dashed barline"), - barlineDotted("Dotted barline"), - - // - // 4.4 Repeats - // - repeatLeft("Left (start) repeat sign"), - repeatRight("Right (end) repeat sign"), - repeatRightLeft("Right and left repeat sign"), - repeatDots("Repeat dots"), - repeatDot("Repeat dot"), - dalSegno("Dal segno (D.S.)"), - daCapo("Da capo (D.C.)"), - segno("Segno"), - coda("Coda"), - codaSquare("Square coda"), - - // - // 4.5 Clefs - // - gClef("G clef"), - gClef8vb("G clef ottava bassa"), - gClef8va("G clef ottava alta"), - gClef15mb("G clef quindicesima bassa"), - gClef15ma("G clef quindicesima alta"), - cClef("C clef"), - fClef("F clef"), - fClef8vb("F clef ottava bassa"), - fClef8va("F clef ottava alta"), - fClef15mb("F clef quindicesima bassa"), - fClef15ma("F clef quindicesima alta"), - unpitchedPercussionClef1("Unpitched percussion clef 1"), - gClefChange("G clef change"), - cClefChange("C clef change"), - fClefChange("F clef change"), - clef8("8 for clefs"), - clef15("15 for clefs"), - - // - // 4.6 Time signatures - // - timeSig0("Time signature 0"), - timeSig1("Time signature 1"), - timeSig2("Time signature 2"), - timeSig3("Time signature 3"), - timeSig4("Time signature 4"), - timeSig5("Time signature 5"), - timeSig6("Time signature 6"), - timeSig7("Time signature 7"), - timeSig8("Time signature 8"), - timeSig9("Time signature 9"), - timeSig12("Time signature 12"), - timeSig16("Time signature 16"), - timeSigCommon("Common time"), - timeSigCutCommon("Cut time"), - timeSig2over4("2/4 time signature"), - timeSig2over2("2/2 time signature"), - timeSig3over2("3/2 time signature"), - timeSig3over4("3/4 time signature"), - timeSig3over8("3/8 time signature"), - timeSig4over4("4/4 time signature"), - timeSig5over4("5/4 time signature"), - timeSig5over8("5/8 time signature"), - timeSig6over4("6/4 time signature"), - timeSig6over8("6/8 time signature"), - timeSig7over8("7/8 time signature"), - timeSig9over8("9/8 time signature"), - timeSig12over8("12/8 time signature"), - - // - // 4.7 Noteheads - // - noteheadBlack("Black notehead"), - noteheadBlackSmall("Black notehead (small staff)"), - noteheadHalf("Half (minim) notehead"), - noteheadHalfSmall("Half (minim) notehead (small staff)"), - noteheadWhole("Whole (semibreve) notehead"), - noteheadWholeSmall("Whole notehead (small staff)"), - noteheadDoubleWhole("Double whole (breve) notehead"), - noteheadDoubleWholeSmall("Double whole note (breve) (small staff)"), - noteheadXBlack("X notehead black"), - noteheadXHalf("X notehead half"), - noteheadXWhole("X notehead whole"), - augmentationDot("Augmentation dot"), - // - // 4.15 Stems - // - stem("Combining stem"), - // - // 4.16 Tremolos - // - tremolo1("Combining tremolo 1"), - tremolo2("Combining tremolo 2"), - tremolo3("Combining tremolo 3"), - tremolo4("Combining tremolo 4"), - tremolo5("Combining tremolo 5"), - // - // 4.17 Flags - // - flag8thUp("Combining flag 1 (8th) above"), - flag8thUpSmall("Combining flag 1 (8th) above (small staff)"), - flag16thUp("Combining flag 2 (16th) above"), - flag32ndUp("Combining flag 3 (32nd) above"), - flag64thUp("Combining flag 4 (64th) above"), - flag128thUp("Combining flag 5 (128th) above"), - flag256thUp("Combining flag 6 (256th) above"), - flag512thUp("Combining flag 7 (512th) above"), - flag1024thUp("Combining flag 8 (1024th) above"), - flag8thDown("Combining flag 1 (8th) below"), - flag8thDownSmall("Combining flag 1 (8th) below (small staff)"), - flag16thDown("Combining flag 2 (16th) below"), - flag32ndDown("Combining flag 3 (32nd) below"), - flag64thDown("Combining flag 4 (64th) below"), - flag128thDown("Combining flag 5 (128th) below"), - flag256thDown("Combining flag 6 (256th) below"), - flag512thDown("Combining flag 7 (512th) below"), - flag1024thDown("Combining flag 8 (1024th) below"), - // - // 4.18 Standard accidentals - // - accidentalFlat("Flat"), - accidentalFlatSmall("Flat (for small staves)"), - accidentalNatural("Natural"), - accidentalNaturalSmall("Natural (for small staves)"), - accidentalSharp("Sharp"), - accidentalSharpSmall("Sharp (for small staves)"), - accidentalDoubleSharp("Double sharp"), - accidentalDoubleFlat("Double flat"), - - // - // 4.18bis Alterations for key signatures (NOTA: this is an addition to SMuFL) - // - keyFlat("Flat in key signature"), - keyNatural("Natural in key signature"), - keySharp("Sharp in key signature"), - // - // 4.39 Articulations - // - articAccentAbove("Accent above"), - articAccentBelow("Accent below"), - articStaccatoAbove("Staccato above"), - articStaccatoBelow("Staccato below"), - articTenutoAbove("Tenuto above"), - articTenutoBelow("Tenuto below"), - articStaccatissimoAbove("Staccatissimo above"), - articStaccatissimoBelow("Staccatissimo below"), - articMarcatoAbove("Marcato above"), - articMarcatoBelow("Marcato below"), - articTenutoStaccatoAbove("Louré (tenuto-staccato) above"), - articTenutoStaccatoBelow("Louré (tenuto-staccato) below"), - // - // 4.40 Holds and pauses - // - fermataAbove("Fermata above staff"), - fermataBelow("Fermata below staff"), - breathMarkComma("Breath mark (comma)"), - caesura("Caesura"), - // - // 4.41 Rests - // - restMaxima("Maxima rest"), - restLonga("Longa rest"), - restDoubleWhole("Double whole (breve) rest"), - restWhole("Whole (semibreve) rest"), - restHalf("Half (minim) rest"), - restQuarter("Quarter (crotchet) rest"), - rest8th("Eighth (quaver) rest"), - rest16th("16th (semiquaver) rest"), - rest32nd("32nd (demisemiquaver) rest"), - rest64th("64th (hemidemisemiquaver) rest"), - rest128th("128th (semihemidemisemiquaver) rest"), - rest256th("256th rest"), - rest512th("512th rest"), - rest1024th("1024th rest"), - restHBar("Multiple measure rest"), - - // - // 4.43 Octaves - // - ottavaAlta("Ottava alta (8va)"), - ottavaBassaVb("Ottava bassa (8vb)"), - // - // 4.44 Dynamics - // - dynamicPiano("Piano (p)"), - dynamicMezzo("Mezzo (m)"), - dynamicForte("Forte (f)"), - dynamicRinforzando("Rinforzando (r)"), - dynamicSforzando("Sforzando (s)"), - dynamicZ("Z"), - dynamicNiente("Niente (n)"), - dynamicPPPPPP("pppppp"), - dynamicPPPPP("ppppp"), - dynamicPPPP("pppp"), - dynamicPPP("ppp"), - dynamicPP("pp"), - dynamicMP("mp"), - dynamicMF("mf"), - dynamicPF("pf"), - dynamicFF("ff"), - dynamicFFF("fff"), - dynamicFFFF("ffff"), - dynamicFFFFF("fffff"), - dynamicFFFFFF("ffffff"), - dynamicFortePiano("Forte-piano (fp)"), - dynamicForzando("Forzando (fz)"), - dynamicSforzando1("Sforzando 1 (sf)"), - dynamicSforzandoPiano("Sforzando-piano (sfp)"), - dynamicSforzandoPianissimo("Sforzando-pianissimo (sfpp)"), - dynamicSforzato("Sforzato (sfz)"), - dynamicSforzatoPiano("Sforzato-piano (sfzp)"), - dynamicSforzatoFF("Sforzatissimo (sffz)"), - dynamicRinforzando1("Rinforzando 1 (rf)"), - dynamicRinforzando2("Rinforzando 2 (rfz)"), - - // - // 4.46 Common ornaments - // - graceNoteAcciaccaturaStemUp("Slashed grace note stem up"), - graceNoteAppoggiaturaStemUp("Grace note stem up"), - ornamentTrill("Trill"), - ornamentTurn("Turn"), - ornamentTurnInverted("Inverted turn"), - ornamentTurnSlash("Turn with slash"), - ornamentTurnUp("Turn up"), - ornamentMordent("Mordent"), - ornamentMordentInverted("Inverted mordent"), - - // - // 4.52 String techniques - // - stringsDownBow("Down bow"), - stringsUpBow("Up bow"), - // - // 4.53 Plucked techniques (NOTA: I found only arpeggiato down and up, with an arrow head) - // - arpeggiato("Arpeggiato"), - // - // 4.55 Keyboard techniques - // - keyboardPedalPed("Pedal mark"), - keyboardPedalUp("Pedal up mark"), - // - // 4.75 Tuplets - // - tuplet3("Tuplet 3"), - tuplet6("Tuplet 6"), - // - // 4.115 Fingering - // - fingering0("Fingering 0 (open string)"), - fingering1("Fingering 1 (thumb)"), - fingering2("Fingering 2 (index finger)"), - fingering3("Fingering 3 (middle finger)"), - fingering4("Fingering 4 (ring finger)"), - fingering5("Fingering 5 (little finger)"), - fingeringPLower("Fingering p (pulgar; right-hand thumb "), - fingeringILower("Fingering i (indicio; right-hand index finger for guitar)"), - fingeringMLower("Fingering m (medio; right-hand middle finger for guitar)"), - fingeringALower("Fingering a (anular; right-hand ring finger for guitar)"); - - /** Short explanation of the symbol shape. */ - public final String description; - - /** - * Define a symbol shape - * - * @param description textual symbol description - */ - OmrShape (String description) - { - this.description = description; - } -} +//------------------------------------------------------------------------------------------------// +// // +// O m r S h a p e // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omrdataset.api; + +/** + * Class {@code OmrShape} is the OMR-Dataset definition of symbol shapes. + *

                                                                                                                                                                    + * This is a small subset of the list of symbol names described by SMuFL specification available at + * http://www.smufl.org/. + * Symbols are gathered using the same group numbers and names as found in SMuFL specification. + *

                                                                                                                                                                    + * This subset is meant to focus on fixed-shape symbols. Accordingly, all symbols with varying size + * (hair-pins, slurs, beams, etc) have been left out, their recognition is not based on a symbol + * classifier. + * The only exception is the brace symbol, which can vary in size, but we hope that a + * classifier could recognize the brace center part. + *

                                                                                                                                                                    + * We added a few names: + *

                                                                                                                                                                      + *
                                                                                                                                                                    • none. + * This is a special name to indicate the absence of any valid symbol. + *
                                                                                                                                                                    • arpeggiato. + * We could find only arpeggiato's ending with an arrow head, either {@code arpeggiatoUp} or + * {@code arpeggiatoDown}. + *
                                                                                                                                                                    • keyFlat, keyNatural, keySharp. + * They represent flat, natural and sharp signs within a key signature. + * We hope that full-context training will allow to recognize them as such. + *
                                                                                                                                                                    + * All the barline and repeat symbols are meant to be defined for the staff height only. + * + * @see http://www.smufl.org/ + * @author Hervé Bitteur + */ +public enum OmrShape +{ + none("No valid shape"), + + // + // 4.1 Staff brackets and dividers + // + brace("Brace"), + bracketTop("Bracket top"), + bracketBottom("Bracket bottom"), + + // + // 4.2 Staves + // + legerLine("Leger line"), + + // + // 4.3 Barlines + // + barlineSingle("Single barline"), + barlineDouble("Double barline"), + barlineFinal("Final barline"), + barlineReverseFinal("Reverse final barline"), + barlineHeavy("Heavy barline"), + barlineHeavyHeavy("Heavy double barline"), + barlineDashed("Dashed barline"), + barlineDotted("Dotted barline"), + + // + // 4.4 Repeats + // + repeatLeft("Left (start) repeat sign"), + repeatRight("Right (end) repeat sign"), + repeatRightLeft("Right and left repeat sign"), + repeatDots("Repeat dots"), + repeatDot("Repeat dot"), + dalSegno("Dal segno (D.S.)"), + daCapo("Da capo (D.C.)"), + segno("Segno"), + coda("Coda"), + codaSquare("Square coda"), + + // + // 4.5 Clefs + // + gClef("G clef"), + gClef8vb("G clef ottava bassa"), + gClef8va("G clef ottava alta"), + gClef15mb("G clef quindicesima bassa"), + gClef15ma("G clef quindicesima alta"), + cClef("C clef"), + fClef("F clef"), + fClef8vb("F clef ottava bassa"), + fClef8va("F clef ottava alta"), + fClef15mb("F clef quindicesima bassa"), + fClef15ma("F clef quindicesima alta"), + unpitchedPercussionClef1("Unpitched percussion clef 1"), + gClefChange("G clef change"), + cClefChange("C clef change"), + fClefChange("F clef change"), + clef8("8 for clefs"), + clef15("15 for clefs"), + + // + // 4.6 Time signatures + // + timeSig0("Time signature 0"), + timeSig1("Time signature 1"), + timeSig2("Time signature 2"), + timeSig3("Time signature 3"), + timeSig4("Time signature 4"), + timeSig5("Time signature 5"), + timeSig6("Time signature 6"), + timeSig7("Time signature 7"), + timeSig8("Time signature 8"), + timeSig9("Time signature 9"), + timeSig12("Time signature 12"), + timeSig16("Time signature 16"), + timeSigCommon("Common time"), + timeSigCutCommon("Cut time"), + timeSig2over4("2/4 time signature"), + timeSig2over2("2/2 time signature"), + timeSig3over2("3/2 time signature"), + timeSig3over4("3/4 time signature"), + timeSig3over8("3/8 time signature"), + timeSig4over4("4/4 time signature"), + timeSig5over4("5/4 time signature"), + timeSig5over8("5/8 time signature"), + timeSig6over4("6/4 time signature"), + timeSig6over8("6/8 time signature"), + timeSig7over8("7/8 time signature"), + timeSig9over8("9/8 time signature"), + timeSig12over8("12/8 time signature"), + + // + // 4.7 Noteheads + // + noteheadBlack("Black notehead"), + noteheadBlackSmall("Black notehead (small staff)"), + noteheadHalf("Half (minim) notehead"), + noteheadHalfSmall("Half (minim) notehead (small staff)"), + noteheadWhole("Whole (semibreve) notehead"), + noteheadWholeSmall("Whole notehead (small staff)"), + noteheadDoubleWhole("Double whole (breve) notehead"), + noteheadDoubleWholeSmall("Double whole note (breve) (small staff)"), + noteheadXBlack("X notehead black"), + noteheadXHalf("X notehead half"), + noteheadXWhole("X notehead whole"), + augmentationDot("Augmentation dot"), + + // + // 4.15 Stems + // + stem("Combining stem"), + + // + // 4.16 Tremolos + // + tremolo1("Combining tremolo 1"), + tremolo2("Combining tremolo 2"), + tremolo3("Combining tremolo 3"), + tremolo4("Combining tremolo 4"), + tremolo5("Combining tremolo 5"), + + // + // 4.17 Flags + // + flag8thUp("Combining flag 1 (8th) above"), + flag8thUpSmall("Combining flag 1 (8th) above (small staff)"), + flag16thUp("Combining flag 2 (16th) above"), + flag32ndUp("Combining flag 3 (32nd) above"), + flag64thUp("Combining flag 4 (64th) above"), + flag128thUp("Combining flag 5 (128th) above"), + flag256thUp("Combining flag 6 (256th) above"), + flag512thUp("Combining flag 7 (512th) above"), + flag1024thUp("Combining flag 8 (1024th) above"), + flag8thDown("Combining flag 1 (8th) below"), + flag8thDownSmall("Combining flag 1 (8th) below (small staff)"), + flag16thDown("Combining flag 2 (16th) below"), + flag32ndDown("Combining flag 3 (32nd) below"), + flag64thDown("Combining flag 4 (64th) below"), + flag128thDown("Combining flag 5 (128th) below"), + flag256thDown("Combining flag 6 (256th) below"), + flag512thDown("Combining flag 7 (512th) below"), + flag1024thDown("Combining flag 8 (1024th) below"), + + // + // 4.18 Standard accidentals + // + accidentalFlat("Flat"), + accidentalFlatSmall("Flat (for small staves)"), + accidentalNatural("Natural"), + accidentalNaturalSmall("Natural (for small staves)"), + accidentalSharp("Sharp"), + accidentalSharpSmall("Sharp (for small staves)"), + accidentalDoubleSharp("Double sharp"), + accidentalDoubleFlat("Double flat"), + + // + // 4.18bis Alterations for key signatures (NOTA: this is an addition to SMuFL) + // + keyFlat("Flat in key signature"), + keyNatural("Natural in key signature"), + keySharp("Sharp in key signature"), + + // + // 4.39 Articulations + // + articAccentAbove("Accent above"), + articAccentBelow("Accent below"), + articStaccatoAbove("Staccato above"), + articStaccatoBelow("Staccato below"), + articTenutoAbove("Tenuto above"), + articTenutoBelow("Tenuto below"), + articStaccatissimoAbove("Staccatissimo above"), + articStaccatissimoBelow("Staccatissimo below"), + articMarcatoAbove("Marcato above"), + articMarcatoBelow("Marcato below"), + articTenutoStaccatoAbove("Louré (tenuto-staccato) above"), + articTenutoStaccatoBelow("Louré (tenuto-staccato) below"), + + // + // 4.40 Holds and pauses + // + fermataAbove("Fermata above staff"), + fermataBelow("Fermata below staff"), + breathMarkComma("Breath mark (comma)"), + caesura("Caesura"), + + // + // 4.41 Rests + // + restMaxima("Maxima rest"), + restLonga("Longa rest"), + restDoubleWhole("Double whole (breve) rest"), + restWhole("Whole (semibreve) rest"), + restHalf("Half (minim) rest"), + restQuarter("Quarter (crotchet) rest"), + rest8th("Eighth (quaver) rest"), + rest16th("16th (semiquaver) rest"), + rest32nd("32nd (demisemiquaver) rest"), + rest64th("64th (hemidemisemiquaver) rest"), + rest128th("128th (semihemidemisemiquaver) rest"), + rest256th("256th rest"), + rest512th("512th rest"), + rest1024th("1024th rest"), + restHBar("Multiple measure rest"), + + // + // 4.43 Octaves + // + ottavaAlta("Ottava alta (8va)"), + ottavaBassaVb("Ottava bassa (8vb)"), + + // + // 4.44 Dynamics + // + dynamicPiano("Piano (p)"), + dynamicMezzo("Mezzo (m)"), + dynamicForte("Forte (f)"), + dynamicRinforzando("Rinforzando (r)"), + dynamicSforzando("Sforzando (s)"), + dynamicZ("Z"), + dynamicNiente("Niente (n)"), + dynamicPPPPPP("pppppp"), + dynamicPPPPP("ppppp"), + dynamicPPPP("pppp"), + dynamicPPP("ppp"), + dynamicPP("pp"), + dynamicMP("mp"), + dynamicMF("mf"), + dynamicPF("pf"), + dynamicFF("ff"), + dynamicFFF("fff"), + dynamicFFFF("ffff"), + dynamicFFFFF("fffff"), + dynamicFFFFFF("ffffff"), + dynamicFortePiano("Forte-piano (fp)"), + dynamicForzando("Forzando (fz)"), + dynamicSforzando1("Sforzando 1 (sf)"), + dynamicSforzandoPiano("Sforzando-piano (sfp)"), + dynamicSforzandoPianissimo("Sforzando-pianissimo (sfpp)"), + dynamicSforzato("Sforzato (sfz)"), + dynamicSforzatoPiano("Sforzato-piano (sfzp)"), + dynamicSforzatoFF("Sforzatissimo (sffz)"), + dynamicRinforzando1("Rinforzando 1 (rf)"), + dynamicRinforzando2("Rinforzando 2 (rfz)"), + + // + // 4.46 Common ornaments + // + graceNoteAcciaccaturaStemUp("Slashed grace note stem up"), + graceNoteAppoggiaturaStemUp("Grace note stem up"), + ornamentTrill("Trill"), + ornamentTurn("Turn"), + ornamentTurnInverted("Inverted turn"), + ornamentTurnSlash("Turn with slash"), + ornamentTurnUp("Turn up"), + ornamentMordent("Mordent"), + ornamentMordentInverted("Inverted mordent"), + + // + // 4.52 String techniques + // + stringsDownBow("Down bow"), + stringsUpBow("Up bow"), + + // + // 4.53 Plucked techniques (NOTA: I found only arpeggiato down and up, with an arrow head) + // + arpeggiato("Arpeggiato"), + + // + // 4.55 Keyboard techniques + // + keyboardPedalPed("Pedal mark"), + keyboardPedalUp("Pedal up mark"), + + // + // 4.75 Tuplets + // + tuplet3("Tuplet 3"), + tuplet6("Tuplet 6"), + + // + // 4.115 Fingering + // + fingering0("Fingering 0 (open string)"), + fingering1("Fingering 1 (thumb)"), + fingering2("Fingering 2 (index finger)"), + fingering3("Fingering 3 (middle finger)"), + fingering4("Fingering 4 (ring finger)"), + fingering5("Fingering 5 (little finger)"), + fingeringPLower("Fingering p (pulgar; right-hand thumb "), + fingeringILower("Fingering i (indicio; right-hand index finger for guitar)"), + fingeringMLower("Fingering m (medio; right-hand middle finger for guitar)"), + fingeringALower("Fingering a (anular; right-hand ring finger for guitar)"); + + /** Short explanation of the symbol shape. */ + public final String description; + + /** + * Define a symbol shape + * + * @param description textual symbol description + */ + OmrShape (String description) + { + this.description = description; + } +} diff --git a/src/main/org/audiveris/omrdataset/api/OmrShapes.java b/src/main/org/audiveris/omrdataset/api/OmrShapes.java index 857d56788..85f9867bf 100644 --- a/src/main/org/audiveris/omrdataset/api/OmrShapes.java +++ b/src/main/org/audiveris/omrdataset/api/OmrShapes.java @@ -1,182 +1,177 @@ -//------------------------------------------------------------------------------------------------// -// // -// O m r S h a p e s // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omrdataset.api; - -import static org.audiveris.omrdataset.api.OmrShape.*; - -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; - -/** - * Class {@code OmrShapes} complements enum {@link OmrShape} with related features. - * - * @author Hervé Bitteur - */ -public abstract class OmrShapes -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - /** OmrShapes as a list a strings. */ - public static final List NAMES = getNames(); - - /** Predefined combos for time signatures. */ - public static final EnumSet TIME_COMBOS = EnumSet.of( - timeSig2over4, - timeSig2over2, - timeSig3over2, - timeSig3over4, - timeSig3over8, - timeSig4over4, - timeSig5over4, - timeSig5over8, - timeSig6over4, - timeSig6over8, - timeSig7over8, - timeSig9over8, - timeSig12over8); - - /** Map of predefined combos to num/den integer pairs. */ - public static final Map COMBO_MAP = buildComboMap(); - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Report the list of OmrShape values, to be used by DL4J. - * - * @return OmrShape values, as a List - */ - public static final List getNames () - { - List list = new ArrayList(); - - for (OmrShape shape : OmrShape.values()) { - list.add(shape.toString()); - } - - return list; - } - - /** - * Report the integer value, if any, conveyed by the provided shape - * - * @param shape provided shape - * @return related value or null - */ - public static Integer integerValueOf (OmrShape shape) - { - switch (shape) { - case timeSig0: - return 0; - - case timeSig1: - return 1; - - case timeSig2: - return 2; - - case timeSig3: - return 3; - - case timeSig4: - return 4; - - case timeSig5: - return 5; - - case timeSig6: - return 6; - - case timeSig7: - return 7; - - case timeSig8: - return 8; - - case timeSig9: - return 9; - - case timeSig12: - return 12; - - case timeSig16: - return 16; - } - - return null; - } - - /** - * Print out the omrShape ordinal and name. - */ - public static void printOmrShapes () - { - for (OmrShape shape : OmrShape.values()) { - System.out.printf("%3d %s%n", shape.ordinal(), shape.toString()); - } - } - - private static Map buildComboMap () - { - final Map map = new EnumMap(OmrShape.class); - map.put(OmrShape.timeSig2over4, new NumDen(2, 4)); - map.put(OmrShape.timeSig2over2, new NumDen(2, 2)); - map.put(OmrShape.timeSig3over2, new NumDen(3, 2)); - map.put(OmrShape.timeSig3over4, new NumDen(3, 4)); - map.put(OmrShape.timeSig3over8, new NumDen(3, 8)); - map.put(OmrShape.timeSig4over4, new NumDen(4, 4)); - map.put(OmrShape.timeSig5over4, new NumDen(5, 4)); - map.put(OmrShape.timeSig5over8, new NumDen(5, 8)); - map.put(OmrShape.timeSig6over4, new NumDen(6, 4)); - map.put(OmrShape.timeSig6over8, new NumDen(6, 8)); - map.put(OmrShape.timeSig7over8, new NumDen(7, 8)); - map.put(OmrShape.timeSig9over8, new NumDen(9, 8)); - map.put(OmrShape.timeSig12over8, new NumDen(12, 8)); - - return map; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //--------// - // NumDen // - //--------// - /** - * Handles the numerator and denominator structure as a spair of integer values. - */ - public static class NumDen - { - //~ Instance fields ------------------------------------------------------------------------ - - public final int num; - - public final int den; - - //~ Constructors --------------------------------------------------------------------------- - public NumDen (int num, - int den) - { - this.num = num; - this.den = den; - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// O m r S h a p e s // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omrdataset.api; + +import static org.audiveris.omrdataset.api.OmrShape.*; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; + +/** + * Class {@code OmrShapes} complements enum {@link OmrShape} with related features. + * + * @author Hervé Bitteur + */ +public abstract class OmrShapes +{ + + /** OmrShapes as a list a strings. */ + public static final List NAMES = getNames(); + + /** Predefined combos for time signatures. */ + public static final EnumSet TIME_COMBOS = EnumSet.of( + timeSig2over4, + timeSig2over2, + timeSig3over2, + timeSig3over4, + timeSig3over8, + timeSig4over4, + timeSig5over4, + timeSig5over8, + timeSig6over4, + timeSig6over8, + timeSig7over8, + timeSig9over8, + timeSig12over8); + + /** Map of predefined combos to num/den integer pairs. */ + public static final Map COMBO_MAP = buildComboMap(); + + /** + * Report the list of OmrShape values, to be used by DL4J. + * + * @return OmrShape values, as a List + */ + public static final List getNames () + { + List list = new ArrayList<>(); + + for (OmrShape shape : OmrShape.values()) { + list.add(shape.toString()); + } + + return list; + } + + /** + * Report the integer value, if any, conveyed by the provided shape + * + * @param shape provided shape + * @return related value or null + */ + public static Integer integerValueOf (OmrShape shape) + { + switch (shape) { + case timeSig0: + return 0; + + case timeSig1: + return 1; + + case timeSig2: + return 2; + + case timeSig3: + return 3; + + case timeSig4: + return 4; + + case timeSig5: + return 5; + + case timeSig6: + return 6; + + case timeSig7: + return 7; + + case timeSig8: + return 8; + + case timeSig9: + return 9; + + case timeSig12: + return 12; + + case timeSig16: + return 16; + } + + return null; + } + + /** + * Print out the omrShape ordinal and name. + */ + public static void printOmrShapes () + { + for (OmrShape shape : OmrShape.values()) { + System.out.printf("%3d %s%n", shape.ordinal(), shape.toString()); + } + } + + private static Map buildComboMap () + { + final Map map = new EnumMap<>(OmrShape.class); + map.put(OmrShape.timeSig2over4, new NumDen(2, 4)); + map.put(OmrShape.timeSig2over2, new NumDen(2, 2)); + map.put(OmrShape.timeSig3over2, new NumDen(3, 2)); + map.put(OmrShape.timeSig3over4, new NumDen(3, 4)); + map.put(OmrShape.timeSig3over8, new NumDen(3, 8)); + map.put(OmrShape.timeSig4over4, new NumDen(4, 4)); + map.put(OmrShape.timeSig5over4, new NumDen(5, 4)); + map.put(OmrShape.timeSig5over8, new NumDen(5, 8)); + map.put(OmrShape.timeSig6over4, new NumDen(6, 4)); + map.put(OmrShape.timeSig6over8, new NumDen(6, 8)); + map.put(OmrShape.timeSig7over8, new NumDen(7, 8)); + map.put(OmrShape.timeSig9over8, new NumDen(9, 8)); + map.put(OmrShape.timeSig12over8, new NumDen(12, 8)); + + return map; + } + + //--------// + // NumDen // + //--------// + /** + * Handles the numerator and denominator structure as a spair of integer values. + */ + public static class NumDen + { + + public final int num; + + public final int den; + + public NumDen (int num, + int den) + { + this.num = num; + this.den = den; + } + } +} diff --git a/src/main/org/audiveris/omrdataset/api/SheetAnnotations.java b/src/main/org/audiveris/omrdataset/api/SheetAnnotations.java index eaf485aa4..1c1f75eb0 100644 --- a/src/main/org/audiveris/omrdataset/api/SheetAnnotations.java +++ b/src/main/org/audiveris/omrdataset/api/SheetAnnotations.java @@ -1,361 +1,299 @@ -//------------------------------------------------------------------------------------------------// -// // -// S h e e t A n n o t a t i o n s // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omrdataset.api; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Dimension; -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.adapters.XmlAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - -/** - * Class {@code SheetAnnotations} represents the symbols information for a sheet. - * It's essentially a sequence of: {symbol name + symbol bounding box} - * - * @author Hervé Bitteur - */ -@XmlAccessorType(XmlAccessType.NONE) -@XmlRootElement(name = "Annotations") -public class SheetAnnotations -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger( - SheetAnnotations.class); - - /** Un/marshalling context for use with JAXB. */ - private static volatile JAXBContext jaxbContext; - - //~ Instance fields ---------------------------------------------------------------------------- - @XmlAttribute(name = "version") - private String version; - - @XmlAttribute(name = "complete") - private Boolean complete; - - @XmlElement(name = "Source") - private String source; - - @XmlElement(name = "Page") - private SheetInfo sheetInfo; - - @XmlElement(name = "Symbol") - private ArrayList symbols = new ArrayList(); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SheetAnnotations} object. - */ - public SheetAnnotations () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Add a symbol to the annotations - * - * @param symbol symbol to add - */ - public void addSymbol (SymbolInfo symbol) - { - symbols.add(symbol); - } - - /** - * Report information about sheet. - * - * @return sheet information - */ - public SheetInfo getSheetInfo () - { - return sheetInfo; - } - - /** - * @return the source - */ - public String getSource () - { - return source; - } - - /** - * Report the (live) list of symbols in sheet. - * - * @return symbols list - */ - public List getSymbols () - { - return symbols; - } - - /** - * @return the version - */ - public String getVersion () - { - return version; - } - - /** - * Report whether these annotations are complete. - * - * @return the complete - */ - public boolean isComplete () - { - return (complete != null) && complete; - } - - //----------// - // marshall // - //----------// - /** - * Marshall this instance to the provided XML file. - * - * @param path to the XML output file - * @throws IOException in case of IO problem - * @throws JAXBException in case of marshalling problem - */ - public void marshall (Path path) - throws IOException, JAXBException - { - if (!Files.exists(path.getParent())) { - Files.createDirectories(path.getParent()); - } - - OutputStream os = new BufferedOutputStream( - Files.newOutputStream(path, StandardOpenOption.CREATE)); - Marshaller m = getJaxbContext().createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - m.marshal(this, os); - os.flush(); - os.close(); - } - - /** - * Set completeness information. - * - * @param complete the complete to set - */ - public void setComplete (boolean complete) - { - this.complete = complete ? true : null; - } - - /** - * @param sheetInfo the sheetInfo to set - */ - public void setSheetInfo (SheetInfo sheetInfo) - { - this.sheetInfo = sheetInfo; - } - - /** - * @param source the source to set - */ - public void setSource (String source) - { - this.source = source; - } - - /** - * @param version the version to set - */ - public void setVersion (String version) - { - this.version = version; - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("Annotations{"); - sb.append("version:").append(version); - - if (source != null) { - sb.append(" source:").append(source); - } - - if (sheetInfo != null) { - sb.append(" sheet:").append(sheetInfo); - } - - sb.append(" symbols:").append(symbols.size()); - - sb.append("}"); - - return sb.toString(); - } - - //-----------// - // unmarshal // - //-----------// - /** - * Load SheetAnnotations from the annotations XML file. - * - * @param path to the XML input file. - * @return the unmarshalled SheetAnnotations object - * @throws IOException in case of IO problem - */ - public static SheetAnnotations unmarshal (Path path) - throws IOException - { - logger.debug("SheetAnnotations unmarshalling {}", path); - - try { - InputStream is = Files.newInputStream(path, StandardOpenOption.READ); - Unmarshaller um = getJaxbContext().createUnmarshaller(); - SheetAnnotations sheetInfo = (SheetAnnotations) um.unmarshal(is); - logger.debug("Unmarshalled {}", sheetInfo); - is.close(); - - return sheetInfo; - } catch (JAXBException ex) { - logger.warn("Error unmarshalling " + path + " " + ex, ex); - - return null; - } - } - - //----------------// - // getJaxbContext // - //----------------// - private static JAXBContext getJaxbContext () - throws JAXBException - { - // Lazy creation - if (jaxbContext == null) { - jaxbContext = JAXBContext.newInstance(SheetAnnotations.class); - } - - return jaxbContext; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------// - // SheetInfo // - //-----------// - public static class SheetInfo - { - //~ Instance fields ------------------------------------------------------------------------ - - @XmlElement(name = "Image") - public final String imageFileName; - - @XmlElement(name = "Size") - @XmlJavaTypeAdapter(DimensionAdapter.class) - public final Dimension dim; - - //~ Constructors --------------------------------------------------------------------------- - public SheetInfo (String imageFileName, - Dimension dim) - { - this.imageFileName = imageFileName; - this.dim = dim; - } - - // No-arg constructor needed by JAXB - private SheetInfo () - { - this.imageFileName = null; - this.dim = null; - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String toString () - { - return "{" + imageFileName + " [width=" + dim.width + ",height=" + dim.height + "]}"; - } - - //~ Inner Classes -------------------------------------------------------------------------- - public static class DimensionAdapter - extends XmlAdapter - { - //~ Methods ---------------------------------------------------------------------------- - - @Override - public DimensionFacade marshal (Dimension dim) - throws Exception - { - return new DimensionFacade(dim); - } - - @Override - public Dimension unmarshal (DimensionFacade facade) - throws Exception - { - return facade.getDimension(); - } - - //~ Inner Classes ---------------------------------------------------------------------- - private static class DimensionFacade - { - //~ Instance fields ---------------------------------------------------------------- - - @XmlAttribute(name = "w") - public int width; - - @XmlAttribute(name = "h") - public int height; - - //~ Constructors ------------------------------------------------------------------- - public DimensionFacade (Dimension dimension) - { - width = dimension.width; - height = dimension.height; - } - - private DimensionFacade () - { - } - - //~ Methods ------------------------------------------------------------------------ - public Dimension getDimension () - { - return new Dimension(width, height); - } - } - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// S h e e t A n n o t a t i o n s // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omrdataset.api; + +import org.audiveris.omr.util.Jaxb; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Dimension; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +/** + * Class {@code SheetAnnotations} represents the symbols information for a sheet. + * It's essentially a sequence of: {symbol name + symbol bounding box} + * + * @author Hervé Bitteur + */ +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = "Annotations") +public class SheetAnnotations +{ + + private static final Logger logger = LoggerFactory.getLogger(SheetAnnotations.class); + + /** Un/marshalling context for use with JAXB. */ + private static volatile JAXBContext jaxbContext; + + @XmlAttribute(name = "version") + private String version; + + @XmlAttribute(name = "complete") + private Boolean complete; + + @XmlElement(name = "Source") + private String source; + + @XmlElement(name = "Page") + private SheetInfo sheetInfo; + + @XmlElement(name = "Symbol") + private ArrayList symbols = new ArrayList<>(); + + /** + * Creates a new {@code SheetAnnotations} object. + */ + public SheetAnnotations () + { + } + + /** + * Add a symbol to the annotations + * + * @param symbol symbol to add + */ + public void addSymbol (SymbolInfo symbol) + { + symbols.add(symbol); + } + + /** + * Report information about sheet. + * + * @return sheet information + */ + public SheetInfo getSheetInfo () + { + return sheetInfo; + } + + /** + * @return the source + */ + public String getSource () + { + return source; + } + + /** + * Report the (live) list of symbols in sheet. + * + * @return symbols list + */ + public List getSymbols () + { + return symbols; + } + + /** + * @return the version + */ + public String getVersion () + { + return version; + } + + /** + * Report whether these annotations are complete. + * + * @return the complete + */ + public boolean isComplete () + { + return (complete != null) && complete; + } + + //----------// + // marshall // + //----------// + /** + * Marshall this instance to the provided XML file. + * + * @param path to the XML output file + * @throws IOException in case of IO problem + * @throws JAXBException in case of marshalling problem + */ + public void marshall (Path path) + throws IOException, + JAXBException + { + if (!Files.exists(path.getParent())) { + Files.createDirectories(path.getParent()); + } + + OutputStream os = new BufferedOutputStream(Files.newOutputStream(path, + StandardOpenOption.CREATE)); + Marshaller m = getJaxbContext().createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + m.marshal(this, os); + os.flush(); + os.close(); + } + + /** + * Set completeness information. + * + * @param complete the complete to set + */ + public void setComplete (boolean complete) + { + this.complete = complete ? true : null; + } + + /** + * @param sheetInfo the sheetInfo to set + */ + public void setSheetInfo (SheetInfo sheetInfo) + { + this.sheetInfo = sheetInfo; + } + + /** + * @param source the source to set + */ + public void setSource (String source) + { + this.source = source; + } + + /** + * @param version the version to set + */ + public void setVersion (String version) + { + this.version = version; + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("Annotations{"); + sb.append("version:").append(version); + + if (source != null) { + sb.append(" source:").append(source); + } + + if (sheetInfo != null) { + sb.append(" sheet:").append(sheetInfo); + } + + sb.append(" symbols:").append(symbols.size()); + + sb.append("}"); + + return sb.toString(); + } + + //-----------// + // unmarshal // + //-----------// + /** + * Load SheetAnnotations from the annotations XML file. + * + * @param path to the XML input file. + * @return the unmarshalled SheetAnnotations object + * @throws IOException in case of IO problem + */ + public static SheetAnnotations unmarshal (Path path) + throws IOException + { + logger.debug("SheetAnnotations unmarshalling {}", path); + + try { + SheetAnnotations sheetInfo = (SheetAnnotations) Jaxb.unmarshal(path, getJaxbContext()); + logger.debug("Unmarshalled {}", sheetInfo); + + return sheetInfo; + } catch (JAXBException ex) { + logger.warn("Error unmarshalling " + path + " " + ex, ex); + + return null; + } + } + + //----------------// + // getJaxbContext // + //----------------// + private static JAXBContext getJaxbContext () + throws JAXBException + { + // Lazy creation + if (jaxbContext == null) { + jaxbContext = JAXBContext.newInstance(SheetAnnotations.class); + } + + return jaxbContext; + } + + //-----------// + // SheetInfo // + //-----------// + public static class SheetInfo + { + + @XmlElement(name = "Image") + public final String imageFileName; + + @XmlElement(name = "Size") + @XmlJavaTypeAdapter(Jaxb.DimensionAdapter.class) + public final Dimension dim; + + public SheetInfo (String imageFileName, + Dimension dim) + { + this.imageFileName = imageFileName; + this.dim = dim; + } + + // No-arg constructor needed by JAXB + private SheetInfo () + { + this.imageFileName = null; + this.dim = null; + } + + @Override + public String toString () + { + return "{" + imageFileName + " [width=" + dim.width + ",height=" + dim.height + "]}"; + } + } +} diff --git a/src/main/org/audiveris/omrdataset/api/SymbolInfo.java b/src/main/org/audiveris/omrdataset/api/SymbolInfo.java index 01842e8ac..9b98eaeeb 100644 --- a/src/main/org/audiveris/omrdataset/api/SymbolInfo.java +++ b/src/main/org/audiveris/omrdataset/api/SymbolInfo.java @@ -1,436 +1,422 @@ -//------------------------------------------------------------------------------------------------// -// // -// S y m b o l I n f o // -// // -//------------------------------------------------------------------------------------------------// -// -// -// Copyright © Audiveris 2018. All rights reserved. -// -// This program is free software: you can redistribute it and/or modify it under the terms of the -// GNU Affero General Public License as published by the Free Software Foundation, either version -// 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License along with this -// program. If not, see . -//------------------------------------------------------------------------------------------------// -// -package org.audiveris.omrdataset.api; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.geom.Rectangle2D; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.xml.bind.Unmarshaller; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.adapters.XmlAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - -/** - * Class {@code SymbolInfo} handles info about one OMR symbol (name, bounding box). - * - * @author Hervé Bitteur - */ -public class SymbolInfo -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger( - SymbolInfo.class); - - //~ Instance fields ---------------------------------------------------------------------------- - @XmlAttribute(name = "interline") - @XmlJavaTypeAdapter(value = Double3Adapter.class, type = double.class) - private final double interline; - - @XmlAttribute(name = "id") - private final Integer id; - - @XmlAttribute(name = "scale") - @XmlJavaTypeAdapter(Double3Adapter.class) - private final Double scale; - - @XmlAttribute(name = "shape") - @XmlJavaTypeAdapter(OmrShapeAdapter.class) - private OmrShape omrShape; - - @XmlElement(name = "Bounds") - @XmlJavaTypeAdapter(Rectangle2DAdapter.class) - private final Rectangle2D bounds; - - /** Inner symbols, if any. */ - @XmlElement(name = "Symbol") - private List innerSymbols; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code SymbolInfo} object. - * - * @param omrShape symbol OMR shape - * @param interline related interline - * @param id symbol id, if any - * @param scale ratio WRT standard symbol size, optional - * @param bounds symbol bounding box within containing image - */ - public SymbolInfo (OmrShape omrShape, - int interline, - Integer id, - Double scale, - Rectangle2D bounds) - { - this.omrShape = omrShape; - this.interline = interline; - this.id = id; - this.scale = scale; - this.bounds = bounds; - } - - /** - * No-arg constructor needed for JAXB. - */ - private SymbolInfo () - { - omrShape = null; - interline = 0; - id = null; - scale = null; - bounds = null; - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Add an inner symbol within this one. - * - * @param symbol the inner symbol to add - */ - public void addInnerSymbol (SymbolInfo symbol) - { - if (innerSymbols == null) { - innerSymbols = new ArrayList(); - } - - innerSymbols.add(symbol); - } - - /** - * @return a COPY of the bounds - */ - public Rectangle2D getBounds () - { - Rectangle2D copy = new Rectangle2D.Double(); - copy.setRect(bounds); - - return copy; - } - - /** - * Report symbol id (a positive integer) - * - * @return symbol id or 0 - */ - public int getId () - { - if (id == null) { - return 0; - } - - return id; - } - - /** - * Report the inner symbols, if any - * - * @return un-mutable list of inner symbols, perhaps empty but never null - */ - public List getInnerSymbols () - { - if (innerSymbols == null) { - return Collections.emptyList(); - } - - return Collections.unmodifiableList(innerSymbols); - } - - /** - * @return the interline - */ - public double getInterline () - { - return interline; - } - - /** - * @return the omrShape, perhaps null - */ - public OmrShape getOmrShape () - { - return omrShape; - } - - /** - * @return the scale, perhaps null - */ - public Double getScale () - { - return scale; - } - - @Override - public String toString () - { - StringBuilder sb = new StringBuilder("Symbol{"); - sb.append(omrShape); - - if ((innerSymbols != null) && !innerSymbols.isEmpty()) { - sb.append(" OUTER"); - } - - sb.append(" interline:").append(interline); - - if (id != null) { - sb.append(" id:").append(id); - } - - if (scale != null) { - sb.append(" scale:").append(scale); - } - - sb.append(" ").append(bounds); - - sb.append("}"); - - return sb.toString(); - } - - /** - * If there is a special name for a smaller version of this symbol, use it. - */ - public void useSmallName () - { - OmrShape smallShape = getSmallShape(omrShape); - - if (smallShape != null) { - setOmrShape(smallShape); - } - } - - /** - * Called after all the properties (except IDREF) are unmarshalled - * for this object, but before this object is set to the parent object. - */ - @PostConstruct - private void afterUnmarshal (Unmarshaller um, - Object parent) - { - if (omrShape == null) { - logger.warn("*** Null shape {}", this); - } - } - - /** - * Report the name, if any, for a small version of the provided shape - * - * @param omrShape the provided shape - * @return the shape for small version, or null - */ - private OmrShape getSmallShape (OmrShape omrShape) - { - switch (omrShape) { - // Clefs (change) - case cClef: - return OmrShape.cClefChange; - - case fClef: - return OmrShape.fClefChange; - - case gClef: - return OmrShape.gClefChange; - - // Accidentals - case accidentalFlat: - return OmrShape.accidentalFlatSmall; - - case accidentalNatural: - return OmrShape.accidentalNaturalSmall; - - case accidentalSharp: - return OmrShape.accidentalSharpSmall; - - // Flags - case flag8thUp: - return OmrShape.flag8thUpSmall; - - case flag8thDown: - return OmrShape.flag8thDownSmall; - - // Note heads - case noteheadBlack: - return OmrShape.noteheadBlackSmall; - - case noteheadHalf: - return OmrShape.noteheadHalfSmall; - - case noteheadWhole: - return OmrShape.noteheadWholeSmall; - - case noteheadDoubleWhole: - return OmrShape.noteheadDoubleWholeSmall; - - default: - return null; - } - } - - /** - * @param omrShape the omrShape to set - */ - private void setOmrShape (OmrShape omrShape) - { - logger.info("Renamed scaled {} as {}", this, omrShape); - this.omrShape = omrShape; - } - - //~ Inner Classes ------------------------------------------------------------------------------ - //-----------------// - // OmrShapeAdapter // - //-----------------// - /** - * We need a specific adapter to warn about unknown shape names. - */ - public static class OmrShapeAdapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public String marshal (OmrShape shape) - throws Exception - { - if (shape == null) { - return null; - } - - return shape.toString(); - } - - @Override - public OmrShape unmarshal (String string) - throws Exception - { - try { - return OmrShape.valueOf(string); - } catch (IllegalArgumentException ex) { - logger.warn("*** Unknown shape name: {}", string); - - return null; - } - } - } - - //----------------// - // Double3Adapter // - //----------------// - private static class Double3Adapter - extends XmlAdapter - { - //~ Static fields/initializers ------------------------------------------------------------- - - private static final DecimalFormat decimal3 = new DecimalFormat(); - - static { - decimal3.setGroupingUsed(false); - decimal3.setMaximumFractionDigits(3); // For a maximum of 3 decimals - } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public String marshal (Double d) - throws Exception - { - if (d == null) { - return null; - } - - return decimal3.format(d); - } - - @Override - public Double unmarshal (String s) - throws Exception - { - return Double.valueOf(s); - } - } - - //--------------------// - // Rectangle2DAdapter // - //--------------------// - private static class Rectangle2DAdapter - extends XmlAdapter - { - //~ Methods -------------------------------------------------------------------------------- - - @Override - public Rectangle2DFacade marshal (Rectangle2D rect) - throws Exception - { - return new Rectangle2DFacade(rect); - } - - @Override - public Rectangle2D unmarshal (Rectangle2DFacade facade) - throws Exception - { - return facade.getRectangle2D(); - } - - //~ Inner Classes -------------------------------------------------------------------------- - @XmlJavaTypeAdapter(value = Double3Adapter.class, type = double.class) - private static class Rectangle2DFacade - { - //~ Instance fields -------------------------------------------------------------------- - - @XmlAttribute(name = "x") - public double x; - - @XmlAttribute(name = "y") - public double y; - - @XmlAttribute(name = "w") - public double width; - - @XmlAttribute(name = "h") - public double height; - - //~ Constructors ----------------------------------------------------------------------- - public Rectangle2DFacade (Rectangle2D rect) - { - x = rect.getX(); - y = rect.getY(); - width = rect.getWidth(); - height = rect.getHeight(); - } - - private Rectangle2DFacade () - { - } - - //~ Methods ---------------------------------------------------------------------------- - public Rectangle2D getRectangle2D () - { - return new Rectangle2D.Double(x, y, width, height); - } - } - } -} +//------------------------------------------------------------------------------------------------// +// // +// S y m b o l I n f o // +// // +//------------------------------------------------------------------------------------------------// +// +// +// Copyright © Audiveris 2018. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify it under the terms of the +// GNU Affero General Public License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License along with this +// program. If not, see . +//------------------------------------------------------------------------------------------------// +// +package org.audiveris.omrdataset.api; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.geom.Rectangle2D; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.adapters.XmlAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +/** + * Class {@code SymbolInfo} handles info about one OMR symbol (name, bounding box). + * + * @author Hervé Bitteur + */ +public class SymbolInfo +{ + + private static final Logger logger = LoggerFactory.getLogger(SymbolInfo.class); + + @XmlAttribute(name = "interline") + @XmlJavaTypeAdapter(value = Double3Adapter.class, type = double.class) + private final double interline; + + @XmlAttribute(name = "id") + private final Integer id; + + @XmlAttribute(name = "scale") + @XmlJavaTypeAdapter(Double3Adapter.class) + private final Double scale; + + @XmlAttribute(name = "shape") + @XmlJavaTypeAdapter(OmrShapeAdapter.class) + private OmrShape omrShape; + + @XmlElement(name = "Bounds") + @XmlJavaTypeAdapter(Rectangle2DAdapter.class) + private final Rectangle2D bounds; + + /** Inner symbols, if any. */ + @XmlElement(name = "Symbol") + private List innerSymbols; + + /** + * Creates a new {@code SymbolInfo} object. + * + * @param omrShape symbol OMR shape + * @param interline related interline + * @param id symbol id, if any + * @param scale ratio WRT standard symbol size, optional + * @param bounds symbol bounding box within containing image + */ + public SymbolInfo (OmrShape omrShape, + int interline, + Integer id, + Double scale, + Rectangle2D bounds) + { + this.omrShape = omrShape; + this.interline = interline; + this.id = id; + this.scale = scale; + this.bounds = bounds; + } + + /** + * No-arg constructor needed for JAXB. + */ + private SymbolInfo () + { + omrShape = null; + interline = 0; + id = null; + scale = null; + bounds = null; + } + + /** + * Add an inner symbol within this one. + * + * @param symbol the inner symbol to add + */ + public void addInnerSymbol (SymbolInfo symbol) + { + if (innerSymbols == null) { + innerSymbols = new ArrayList<>(); + } + + innerSymbols.add(symbol); + } + + /** + * @return a COPY of the bounds + */ + public Rectangle2D getBounds () + { + Rectangle2D copy = new Rectangle2D.Double(); + copy.setRect(bounds); + + return copy; + } + + /** + * Report symbol id (a positive integer) + * + * @return symbol id or 0 + */ + public int getId () + { + if (id == null) { + return 0; + } + + return id; + } + + /** + * Report the inner symbols, if any + * + * @return un-mutable list of inner symbols, perhaps empty but never null + */ + public List getInnerSymbols () + { + if (innerSymbols == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(innerSymbols); + } + + /** + * @return the interline + */ + public double getInterline () + { + return interline; + } + + /** + * @return the omrShape, perhaps null + */ + public OmrShape getOmrShape () + { + return omrShape; + } + + /** + * @return the scale, perhaps null + */ + public Double getScale () + { + return scale; + } + + @Override + public String toString () + { + StringBuilder sb = new StringBuilder("Symbol{"); + sb.append(omrShape); + + if ((innerSymbols != null) && !innerSymbols.isEmpty()) { + sb.append(" OUTER"); + } + + sb.append(" interline:").append(interline); + + if (id != null) { + sb.append(" id:").append(id); + } + + if (scale != null) { + sb.append(" scale:").append(scale); + } + + sb.append(" ").append(bounds); + + sb.append("}"); + + return sb.toString(); + } + + /** + * If there is a special name for a smaller version of this symbol, use it. + */ + public void useSmallName () + { + OmrShape smallShape = getSmallShape(omrShape); + + if (smallShape != null) { + setOmrShape(smallShape); + } + } + + /** + * Called after all the properties (except IDREF) are unmarshalled + * for this object, but before this object is set to the parent object. + */ + @PostConstruct + private void afterUnmarshal (Unmarshaller um, + Object parent) + { + if (omrShape == null) { + logger.warn("*** Null shape {}", this); + } + } + + /** + * Report the name, if any, for a small version of the provided shape + * + * @param omrShape the provided shape + * @return the shape for small version, or null + */ + private OmrShape getSmallShape (OmrShape omrShape) + { + switch (omrShape) { + // Clefs (change) + case cClef: + return OmrShape.cClefChange; + + case fClef: + return OmrShape.fClefChange; + + case gClef: + return OmrShape.gClefChange; + + // Accidentals + case accidentalFlat: + return OmrShape.accidentalFlatSmall; + + case accidentalNatural: + return OmrShape.accidentalNaturalSmall; + + case accidentalSharp: + return OmrShape.accidentalSharpSmall; + + // Flags + case flag8thUp: + return OmrShape.flag8thUpSmall; + + case flag8thDown: + return OmrShape.flag8thDownSmall; + + // Note heads + case noteheadBlack: + return OmrShape.noteheadBlackSmall; + + case noteheadHalf: + return OmrShape.noteheadHalfSmall; + + case noteheadWhole: + return OmrShape.noteheadWholeSmall; + + case noteheadDoubleWhole: + return OmrShape.noteheadDoubleWholeSmall; + + default: + return null; + } + } + + /** + * @param omrShape the omrShape to set + */ + private void setOmrShape (OmrShape omrShape) + { + logger.info("Renamed scaled {} as {}", this, omrShape); + this.omrShape = omrShape; + } + + //-----------------// + // OmrShapeAdapter // + //-----------------// + /** + * We need a specific adapter to warn about unknown shape names. + */ + public static class OmrShapeAdapter + extends XmlAdapter + { + + @Override + public String marshal (OmrShape shape) + throws Exception + { + if (shape == null) { + return null; + } + + return shape.toString(); + } + + @Override + public OmrShape unmarshal (String string) + throws Exception + { + try { + return OmrShape.valueOf(string); + } catch (IllegalArgumentException ex) { + logger.warn("*** Unknown shape name: {}", string); + + return null; + } + } + } + + //----------------// + // Double3Adapter // + //----------------// + private static class Double3Adapter + extends XmlAdapter + { + + private static final DecimalFormat decimal3 = new DecimalFormat(); + + static { + decimal3.setGroupingUsed(false); + decimal3.setMaximumFractionDigits(3); // For a maximum of 3 decimals + } + + @Override + public String marshal (Double d) + throws Exception + { + if (d == null) { + return null; + } + + return decimal3.format(d); + } + + @Override + public Double unmarshal (String s) + throws Exception + { + return Double.valueOf(s); + } + } + + //--------------------// + // Rectangle2DAdapter // + //--------------------// + private static class Rectangle2DAdapter + extends XmlAdapter + { + + @Override + public Rectangle2DFacade marshal (Rectangle2D rect) + throws Exception + { + return new Rectangle2DFacade(rect); + } + + @Override + public Rectangle2D unmarshal (Rectangle2DFacade facade) + throws Exception + { + return facade.getRectangle2D(); + } + + @XmlJavaTypeAdapter(value = Double3Adapter.class, type = double.class) + private static class Rectangle2DFacade + { + + @XmlAttribute(name = "x") + public double x; + + @XmlAttribute(name = "y") + public double y; + + @XmlAttribute(name = "w") + public double width; + + @XmlAttribute(name = "h") + public double height; + + public Rectangle2DFacade (Rectangle2D rect) + { + x = rect.getX(); + y = rect.getY(); + width = rect.getWidth(); + height = rect.getHeight(); + } + + private Rectangle2DFacade () + { + } + + public Rectangle2D getRectangle2D () + { + return new Rectangle2D.Double(x, y, width, height); + } + } + } +} diff --git a/src/test/org/audiveris/omr/CLITest.java b/src/test/org/audiveris/omr/CLITest.java index f23402451..0dec424da 100644 --- a/src/test/org/audiveris/omr/CLITest.java +++ b/src/test/org/audiveris/omr/CLITest.java @@ -102,7 +102,7 @@ public void testRunError () String[] args = new String[]{"-run", "fooBar"}; try { - CLI.Parameters params = instance.parseParameters(args); + instance.parseParameters(args); fail(); } catch (CmdLineException ex) { @@ -190,7 +190,7 @@ public void testStepEmpty () String[] args = new String[]{"-step"}; try { - CLI.Parameters params = instance.parseParameters(args); + instance.parseParameters(args); fail(); } catch (CmdLineException ex) { diff --git a/src/test/org/audiveris/omr/glyph/GlyphFactoryTest.java b/src/test/org/audiveris/omr/glyph/GlyphFactoryTest.java index 777e436d5..9f6f31766 100644 --- a/src/test/org/audiveris/omr/glyph/GlyphFactoryTest.java +++ b/src/test/org/audiveris/omr/glyph/GlyphFactoryTest.java @@ -6,13 +6,13 @@ package org.audiveris.omr.glyph; import static org.audiveris.omr.run.Orientation.HORIZONTAL; + import org.audiveris.omr.run.Run; import org.audiveris.omr.run.RunTable; import org.junit.Test; import java.awt.Dimension; -import java.awt.Point; /** * @@ -42,8 +42,7 @@ public void testCreateGlyphs () System.out.println("createGlyphs"); RunTable runTable = createHorizontalInstance(); - Point offset = null; - GlyphFactory.buildGlyphs(runTable, offset); + GlyphFactory.buildGlyphs(runTable, null); } //--------------------------// diff --git a/src/test/org/audiveris/omr/jai/ImageInfo.java b/src/test/org/audiveris/omr/jai/ImageInfo.java index d18b578b0..3f712f125 100644 --- a/src/test/org/audiveris/omr/jai/ImageInfo.java +++ b/src/test/org/audiveris/omr/jai/ImageInfo.java @@ -121,6 +121,11 @@ public static void print (PlanarImage pi) case DataBuffer.TYPE_UNDEFINED: System.out.println("undefined"); + break; + + default: + System.out.println("unknown type: " + sm.getDataType()); + break; } @@ -146,6 +151,11 @@ public static void print (PlanarImage pi) case Transparency.TRANSLUCENT: System.out.println("translucent"); + break; + + default: + System.out.println("unknown transparency: " + cm.getTransparency()); + break; } } else { diff --git a/src/test/org/audiveris/omr/jai/RGBToBilevel.java b/src/test/org/audiveris/omr/jai/RGBToBilevel.java deleted file mode 100644 index dd8164bf7..000000000 --- a/src/test/org/audiveris/omr/jai/RGBToBilevel.java +++ /dev/null @@ -1,139 +0,0 @@ -//-----------------------------------------------------------------------// -// // -// R G B T o B i l e v e l // -// // -//-----------------------------------------------------------------------// -/* - * Copyright (c) 2002 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * -Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * -Redistribution in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY - * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR - * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING - * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS - * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, - * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER - * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF - * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that Software is not designed,licensed or intended for use in - * the design, construction, operation or maintenance of any nuclear facility. - */ -package org.audiveris.omr.jai; - -import java.awt.*; -import java.awt.event.*; -import java.awt.image.*; -import java.awt.image.renderable.*; - -import javax.media.jai.*; - -/** - * Demo code for dithering a 24-bit RGB image to a monochrome (1-bit - * or bilevel) image. The source image must be an 24-bit RGB image. - * The result is displayed. - * - * Usage: java RGBToBilevel filename [true] - * - * If the second argument is present and equal to "true" then error diffusion - * will be used; otherwise ordered dithering will be used. - */ -public class RGBToBilevel - extends Frame -{ - //~ Constructors ------------------------------------------------------------------------------- - - RGBToBilevel (final String fileName, - boolean isErrorDiffusion) - { - // Load the file. - PlanarImage src = JAI.create("fileload", fileName); - - // Load the ParameterBlock for the dithering operation - // and set the operation name. - ParameterBlock pb = new ParameterBlock(); - pb.addSource(src); - - String opName = null; - - if (isErrorDiffusion) { - opName = "errordiffusion"; - - LookupTableJAI lut = new LookupTableJAI( - new byte[][]{ - {(byte) 0x00, (byte) 0xff}, - {(byte) 0x00, (byte) 0xff}, - {(byte) 0x00, (byte) 0xff} - }); - pb.add(lut); - pb.add(KernelJAI.ERROR_FILTER_FLOYD_STEINBERG); - } else { - opName = "ordereddither"; - - ColorCube cube = ColorCube.createColorCube( - DataBuffer.TYPE_BYTE, - 0, - new int[]{2, 2, 2}); - pb.add(cube); - pb.add(KernelJAI.DITHER_MASK_443); - } - - // Create a layout containing an IndexColorModel which maps - // zero to zero and unity to 255; force SampleModel to be bilevel. - ImageLayout layout = new ImageLayout(); - byte[] map = new byte[]{(byte) 0x00, (byte) 0xff}; - ColorModel cm = new IndexColorModel(1, 2, map, map, map); - layout.setColorModel(cm); - - SampleModel sm = new MultiPixelPackedSampleModel( - DataBuffer.TYPE_BYTE, - src.getWidth(), - src.getHeight(), - 1); - layout.setSampleModel(sm); - - // Create a hint containing the layout. - RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); - - // Dither the image. - final PlanarImage dst = JAI.create(opName, pb, hints); - - // Exit on window closing. - addWindowListener( - new WindowAdapter() - { - public void windowClosing (WindowEvent we) - { - JAI.create("filestore", dst, fileName + ".out", "PNG", null); - System.exit(0); - } - }); - - // Display the result. - //// ATTENTION A REMPLACER : add(new ScrollingImagePanel(dst, dst.getWidth(), dst.getHeight())); - pack(); - setVisible(true); - } - - //~ Methods ------------------------------------------------------------------------------------ - public static void main (String[] args) - { - new RGBToBilevel(args[0], (args.length > 1) ? args[1].equals("true") : false); - } -} diff --git a/src/test/org/audiveris/omr/jaxb/facade/TestFacade.java b/src/test/org/audiveris/omr/jaxb/facade/TestFacade.java index c3d07ea6c..1efcfc774 100644 --- a/src/test/org/audiveris/omr/jaxb/facade/TestFacade.java +++ b/src/test/org/audiveris/omr/jaxb/facade/TestFacade.java @@ -97,7 +97,7 @@ private void marshall () } private void unmarshall () - throws JAXBException, FileNotFoundException + throws JAXBException, FileNotFoundException, IOException { System.out.println("================================================================"); System.out.println("Unmarshalling ..."); @@ -106,6 +106,7 @@ private void unmarshall () Unmarshaller um = jaxbContext.createUnmarshaller(); InputStream is = new FileInputStream(source); MyClass mc = (MyClass) um.unmarshal(is); + is.close(); System.out.println("Unmarshalled from " + source); new Dumping().dump(mc); System.out.println("Map class: " + mc.allEntities.getClass()); diff --git a/src/test/org/audiveris/omr/jaxb/itf/TestCompound.java b/src/test/org/audiveris/omr/jaxb/itf/TestCompound.java index c39a90f19..61e7ab68a 100644 --- a/src/test/org/audiveris/omr/jaxb/itf/TestCompound.java +++ b/src/test/org/audiveris/omr/jaxb/itf/TestCompound.java @@ -96,7 +96,7 @@ private void marshall () } private void unmarshall () - throws JAXBException, FileNotFoundException + throws JAXBException, FileNotFoundException, IOException { System.out.println("========================================================="); System.out.println("Unmarshalling ..."); @@ -106,6 +106,7 @@ private void unmarshall () InputStream is = new FileInputStream(source); MyCompound compound = (MyCompound) um.unmarshal(is); + is.close(); System.out.println("Unmarshalled from " + source); new Dumping().dump(compound); System.out.println("compound.index: " + compound.index); diff --git a/src/test/org/audiveris/omr/jaxb/itf/TestEntity.java b/src/test/org/audiveris/omr/jaxb/itf/TestEntity.java index 4a87e4be8..e8442e46c 100644 --- a/src/test/org/audiveris/omr/jaxb/itf/TestEntity.java +++ b/src/test/org/audiveris/omr/jaxb/itf/TestEntity.java @@ -94,7 +94,7 @@ private void marshall () } private void unmarshall () - throws JAXBException, FileNotFoundException + throws JAXBException, FileNotFoundException, IOException { System.out.println("========================================================="); System.out.println("Unmarshalling ..."); @@ -104,6 +104,7 @@ private void unmarshall () InputStream is = new FileInputStream(source); MyBasicIndex index = (MyBasicIndex) um.unmarshal(is); + is.close(); System.out.println("Unmarshalled from " + source); new Dumping().dump(index); System.out.println("index: " + index); diff --git a/src/test/org/audiveris/omr/jaxb/refs/TestRefs.java b/src/test/org/audiveris/omr/jaxb/refs/TestRefs.java index af17c9def..4c4f2954e 100644 --- a/src/test/org/audiveris/omr/jaxb/refs/TestRefs.java +++ b/src/test/org/audiveris/omr/jaxb/refs/TestRefs.java @@ -123,7 +123,7 @@ private void marshall () } private void unmarshall () - throws JAXBException, FileNotFoundException + throws JAXBException, FileNotFoundException, IOException { System.out.println("========================================================="); System.out.println("Unmarshalling ..."); @@ -136,6 +136,7 @@ private void unmarshall () // um.setProperty(IDResolver.class.getName(), resolver); // um.setListener(resolver.createListener()); Universe universe = (Universe) um.unmarshal(is); + is.close(); System.out.println("Unmarshalled from " + source); new Dumping().dump(universe); diff --git a/src/test/org/audiveris/omr/jaxb/table/TableTest.java b/src/test/org/audiveris/omr/jaxb/table/TableTest.java index 199186037..79f675644 100644 --- a/src/test/org/audiveris/omr/jaxb/table/TableTest.java +++ b/src/test/org/audiveris/omr/jaxb/table/TableTest.java @@ -98,7 +98,7 @@ private void marshall () } private void unmarshall () - throws JAXBException, FileNotFoundException + throws JAXBException, FileNotFoundException, IOException { System.out.println("========================================================="); System.out.println("Unmarshalling ..."); @@ -108,6 +108,8 @@ private void unmarshall () Unmarshaller um = jaxbContext.createUnmarshaller(); Table table = (Table) um.unmarshal(is); + is.close(); + System.out.println("Unmarshalled from " + source); new Dumping().dump(table); diff --git a/src/test/org/audiveris/omr/math/BasicLineCheck.java b/src/test/org/audiveris/omr/math/BasicLineCheck.java index 3f4868df9..ffe893e9d 100644 --- a/src/test/org/audiveris/omr/math/BasicLineCheck.java +++ b/src/test/org/audiveris/omr/math/BasicLineCheck.java @@ -84,7 +84,7 @@ public static void checkGetMeanDistance (BasicLine l) public static void checkGetNoMeanDistance (BasicLine l) { try { - double md = l.getMeanDistance(); + l.getMeanDistance(); print(l); fail("Exception should be raised" + " when less than 2 points are known"); } catch (Exception expected) { @@ -196,7 +196,7 @@ public static void checkNoArgLine (BasicLine l) public static void checkNoDistanceOf (BasicLine l) { try { - double d = l.distanceOf(0, 0); + l.distanceOf(0, 0); fail("Exception should be raised" + " when line parameters are not set"); } catch (Exception expected) { checkException(expected); @@ -252,7 +252,7 @@ public static void checkSingleInclude (BasicLine l) try { print(l); - double d = l.distanceOf(0, 0); + l.distanceOf(0, 0); fail("Exception should be raised" + " when line parameters are not set"); } catch (Exception expected) { checkException(expected); @@ -483,21 +483,21 @@ public void testPlaceHolder () protected static void assertParamsUndefined (BasicLine l) { try { - double a = l.getA(); + l.getA(); fail("Exception should be raised" + " when parameter A is undefined"); } catch (Exception expected) { checkException(expected); } try { - double a = l.getB(); + l.getB(); fail("Exception should be raised" + " when parameter B is undefined"); } catch (Exception expected) { checkException(expected); } try { - double a = l.getC(); + l.getC(); fail("Exception should be raised" + " when parameter C is undefined"); } catch (Exception expected) { checkException(expected); diff --git a/src/test/org/audiveris/omr/math/BasicLineTest.java b/src/test/org/audiveris/omr/math/BasicLineTest.java index 6b1374eb6..0f2ce1a56 100644 --- a/src/test/org/audiveris/omr/math/BasicLineTest.java +++ b/src/test/org/audiveris/omr/math/BasicLineTest.java @@ -182,8 +182,8 @@ public void testSingularMeanDistance () BasicLine l = new BasicLine(xx, yy); print(l); - double md = l.getMeanDistance(); - fail("Exception should be raised" + " when using a line not properly defined"); + l.getMeanDistance(); + fail("Exception should be raised when using a line not properly defined"); } catch (Exception expected) { checkException(expected); } @@ -197,7 +197,7 @@ public void testSingularPoints () try { BasicLine l = new BasicLine(xx, yy); print(l); - fail("Exception should be raised" + " when line is singularly defined"); + fail("Exception should be raised when line is singularly defined"); } catch (Exception expected) { checkException(expected); } @@ -241,7 +241,7 @@ public void testVerticalYAt () l.includePoint(0, 0); l.includePoint(0, 1); - double y = l.yAtX(0d); + l.yAtX(0d); fail("Exception should be raised" + " when Yat is called on a vertical line"); } catch (Exception expected) { checkException(expected); diff --git a/src/test/org/audiveris/omr/math/ClusteringTest.java b/src/test/org/audiveris/omr/math/ClusteringTest.java index a21c1a1b5..2579fd03e 100644 --- a/src/test/org/audiveris/omr/math/ClusteringTest.java +++ b/src/test/org/audiveris/omr/math/ClusteringTest.java @@ -36,7 +36,7 @@ public static double[] generateSample (int N) // generate random values according to some laws for (int i = 0; i < N; i++) { - double r = (100 * i) / N; + double r = (100 * i) / (double) N; if (r < 20) { // 20% of law #1 x[i] = gaussianSample(2.0, 0.5); @@ -81,7 +81,7 @@ public void testAlgorithmEM () // display mixture coefficients and law parameters for (int k = 0; k < G; k++) { - System.out.printf("%f * %s\n", pi[k], laws[k]); + System.out.printf("%f * %s%n", pi[k], laws[k]); } } } diff --git a/src/test/org/audiveris/omr/math/CombinationsTest.java b/src/test/org/audiveris/omr/math/CombinationsTest.java deleted file mode 100644 index adcc9ceab..000000000 --- a/src/test/org/audiveris/omr/math/CombinationsTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © Audiveris 2018. All rights reserved. - * This software is released under the GNU General Public License. - * Goto http://kenai.com/projects/audiveris to report bugs or suggestions. - */ -package org.audiveris.omr.math; - -import org.junit.Test; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * - * @author Hervé Bitteur - */ -public class CombinationsTest -{ - //~ Static fields/initializers ----------------------------------------------------------------- - - private static final Logger logger = LoggerFactory.getLogger(CombinationsTest.class); - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new {@code CombinationsTest} object. - */ - public CombinationsTest () - { - } - - //~ Methods ------------------------------------------------------------------------------------ - /** - * Test of getVectors method, of class Combinations. - */ - @Test - public void testGetVectors2 () - { - System.out.println("getVectors2"); - - int n = 2; - boolean[][] result = Combinations.getVectors(n); - logger.info("{}", Combinations.dumpOf(result)); - } - - /** - * Test of getVectors method, of class Combinations. - */ - @Test - public void testGetVectors4 () - { - System.out.println("getVectors4"); - - int n = 4; - boolean[][] result = Combinations.getVectors(n); - logger.info("{}", Combinations.dumpOf(result)); - } - - /** - * Test of getVectors method, of class Combinations. - */ - @Test - public void testGetVectors8 () - { - System.out.println("getVectors8"); - - int n = 8; - boolean[][] result = Combinations.getVectors(n); - logger.info("{}", Combinations.dumpOf(result)); - } -} diff --git a/src/test/org/audiveris/omr/math/HistogramTest.java b/src/test/org/audiveris/omr/math/HistogramTest.java index 15c5b2e87..37fa803a7 100644 --- a/src/test/org/audiveris/omr/math/HistogramTest.java +++ b/src/test/org/audiveris/omr/math/HistogramTest.java @@ -333,7 +333,8 @@ private int assertAreSameLength (List expecteds, private void assertElementsEqual (Entry expected, Entry actual) { - if ((expected.getKey() != actual.getKey()) || (expected.getValue() != actual.getValue())) { + if (!expected.getKey().equals(actual.getKey()) + || !expected.getValue().equals(actual.getValue())) { throw new AssertionError("Expected: " + expected + " Actual: " + actual); } } diff --git a/src/test/org/audiveris/omr/math/NaturalSplineTest.java b/src/test/org/audiveris/omr/math/NaturalSplineTest.java index bad6a0a79..58388c4ff 100644 --- a/src/test/org/audiveris/omr/math/NaturalSplineTest.java +++ b/src/test/org/audiveris/omr/math/NaturalSplineTest.java @@ -35,9 +35,8 @@ public void testInterpolate0 () double[] yy = new double[]{}; try { - NaturalSpline spline = NaturalSpline.interpolate(xx, yy); + NaturalSpline.interpolate(xx, yy); fail("Exception should be raised when no points are defined"); - System.out.println(spline.toString()); } catch (IllegalArgumentException expected) { checkException(expected); } @@ -53,9 +52,8 @@ public void testInterpolate1 () double[] yy = new double[]{1}; try { - NaturalSpline spline = NaturalSpline.interpolate(xx, yy); + NaturalSpline.interpolate(xx, yy); fail("Exception should be raised when only one point is defined"); - System.out.println(spline.toString()); } catch (IllegalArgumentException expected) { checkException(expected); } @@ -105,9 +103,8 @@ public void testInterpolateDiff () double[] yy = new double[]{1}; try { - NaturalSpline spline = NaturalSpline.interpolate(xx, yy); + NaturalSpline.interpolate(xx, yy); fail("Exception should be raised when no points are defined"); - System.out.println(spline.toString()); } catch (IllegalArgumentException expected) { checkException(expected); } @@ -157,9 +154,8 @@ public void testInterpolateNull () double[] yy = null; try { - NaturalSpline spline = NaturalSpline.interpolate(xx, yy); + NaturalSpline.interpolate(xx, yy); fail("Exception should be raised when no points are defined"); - System.out.println(spline.toString()); } catch (NullPointerException expected) { checkException(expected); } diff --git a/src/test/org/audiveris/omr/moment/MomentsExtractorTest.java b/src/test/org/audiveris/omr/moment/MomentsExtractorTest.java index 2f0c1a7f6..1b6c6ca48 100644 --- a/src/test/org/audiveris/omr/moment/MomentsExtractorTest.java +++ b/src/test/org/audiveris/omr/moment/MomentsExtractorTest.java @@ -135,11 +135,36 @@ private void printRelations () relations.add(new Relation(shape, s)); } - Collections.sort(relations); + // Sort by increasing distance + Collections.sort( + relations, + new Comparator() + { + @Override + public int compare (Relation r1, + Relation r2) + { + return Double.compare(r1.distance, r2.distance); + } + }); + allRelations.add(new ShapeRelations(shape, relations)); } - Collections.sort(allRelations); + // Sort by increasing distance + Collections.sort( + allRelations, + new Comparator() + { + @Override + public int compare (ShapeRelations o1, + ShapeRelations o2) + { + return Double.compare( + o1.relations.get(0).distance, + o2.relations.get(0).distance); + } + }); for (ShapeRelations shapeRelations : allRelations) { StringBuilder sb = new StringBuilder(); @@ -177,7 +202,6 @@ private void reconstruct (Shape shape, // Relation // //----------// private class Relation - implements Comparable { //~ Instance fields ------------------------------------------------------------------------ @@ -197,12 +221,6 @@ private class Relation } //~ Methods -------------------------------------------------------------------------------- - @Override - public int compareTo (Relation other) - { - return Double.compare(distance, other.distance); - } - @Override public String toString () { @@ -214,7 +232,6 @@ public String toString () // ShapeRelations // //----------------// private class ShapeRelations - implements Comparable { //~ Instance fields ------------------------------------------------------------------------ @@ -229,12 +246,5 @@ private class ShapeRelations this.shape = shape; this.relations = relations; } - - //~ Methods -------------------------------------------------------------------------------- - @Override - public int compareTo (ShapeRelations that) - { - return Double.compare(this.relations.get(0).distance, that.relations.get(0).distance); - } } }