GraphicalMusicSheet.ts 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. import {MusicSheet} from "../MusicSheet";
  2. import {SourceMeasure} from "../VoiceData/SourceMeasure";
  3. import {StaffMeasure} from "./StaffMeasure";
  4. import {GraphicalMusicPage} from "./GraphicalMusicPage";
  5. import {VerticalGraphicalStaffEntryContainer} from "./VerticalGraphicalStaffEntryContainer";
  6. import {GraphicalLabel} from "./GraphicalLabel";
  7. import {GraphicalLine} from "./GraphicalLine";
  8. import {MusicSystem} from "./MusicSystem";
  9. import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
  10. import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
  11. import {PointF2D} from "../../Common/DataObjects/PointF2D";
  12. import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
  13. import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
  14. import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
  15. import {Fraction} from "../../Common/DataObjects/Fraction";
  16. import {GraphicalNote} from "./GraphicalNote";
  17. import {Instrument} from "../Instrument";
  18. import {BoundingBox} from "./BoundingBox";
  19. import {Note} from "../VoiceData/Note";
  20. import {MusicSheetCalculator} from "./MusicSheetCalculator";
  21. import {Logging} from "../../Common/Logging";
  22. import Dictionary from "typescript-collections/dist/lib/Dictionary";
  23. import {CollectionUtil} from "../../Util/CollectionUtil";
  24. import {SelectionStartSymbol} from "./SelectionStartSymbol";
  25. import {SelectionEndSymbol} from "./SelectionEndSymbol";
  26. import {OutlineAndFillStyleEnum} from "./DrawingEnums";
  27. /**
  28. * The graphical counterpart of a [[MusicSheet]]
  29. */
  30. export class GraphicalMusicSheet {
  31. constructor(musicSheet: MusicSheet, calculator: MusicSheetCalculator) {
  32. this.musicSheet = musicSheet;
  33. this.numberOfStaves = this.musicSheet.Staves.length;
  34. this.calculator = calculator;
  35. this.sourceToGraphicalMeasureLinks = new Dictionary<SourceMeasure, StaffMeasure[]>();
  36. this.calculator.initialize(this);
  37. }
  38. public sourceToGraphicalMeasureLinks: Dictionary<SourceMeasure, StaffMeasure[]>;
  39. private musicSheet: MusicSheet;
  40. //private fontInfo: FontInfo = FontInfo.Info;
  41. private calculator: MusicSheetCalculator;
  42. private musicPages: GraphicalMusicPage[] = [];
  43. private measureList: StaffMeasure[][] = [];
  44. private verticalGraphicalStaffEntryContainers: VerticalGraphicalStaffEntryContainer[] = [];
  45. private title: GraphicalLabel;
  46. private subtitle: GraphicalLabel;
  47. private composer: GraphicalLabel;
  48. private lyricist: GraphicalLabel;
  49. private cursors: GraphicalLine[] = [];
  50. private selectionStartSymbol: SelectionStartSymbol;
  51. private selectionEndSymbol: SelectionEndSymbol;
  52. private minAllowedSystemWidth: number;
  53. //private systemImages: Dictionary<MusicSystem, SystemImageProperties> = new Dictionary<MusicSystem, SystemImageProperties>();
  54. private numberOfStaves: number;
  55. private leadSheet: boolean = false;
  56. public get ParentMusicSheet(): MusicSheet {
  57. return this.musicSheet;
  58. }
  59. public get GetCalculator(): MusicSheetCalculator {
  60. return this.calculator;
  61. }
  62. public get MusicPages(): GraphicalMusicPage[] {
  63. return this.musicPages;
  64. }
  65. public set MusicPages(value: GraphicalMusicPage[]) {
  66. this.musicPages = value;
  67. }
  68. //public get FontInfo(): FontInfo {
  69. // return this.fontInfo;
  70. //}
  71. public get MeasureList(): StaffMeasure[][] {
  72. return this.measureList;
  73. }
  74. public set MeasureList(value: StaffMeasure[][]) {
  75. this.measureList = value;
  76. }
  77. public get VerticalGraphicalStaffEntryContainers(): VerticalGraphicalStaffEntryContainer[] {
  78. return this.verticalGraphicalStaffEntryContainers;
  79. }
  80. public set VerticalGraphicalStaffEntryContainers(value: VerticalGraphicalStaffEntryContainer[]) {
  81. this.verticalGraphicalStaffEntryContainers = value;
  82. }
  83. public get Title(): GraphicalLabel {
  84. return this.title;
  85. }
  86. public set Title(value: GraphicalLabel) {
  87. this.title = value;
  88. }
  89. public get Subtitle(): GraphicalLabel {
  90. return this.subtitle;
  91. }
  92. public set Subtitle(value: GraphicalLabel) {
  93. this.subtitle = value;
  94. }
  95. public get Composer(): GraphicalLabel {
  96. return this.composer;
  97. }
  98. public set Composer(value: GraphicalLabel) {
  99. this.composer = value;
  100. }
  101. public get Lyricist(): GraphicalLabel {
  102. return this.lyricist;
  103. }
  104. public set Lyricist(value: GraphicalLabel) {
  105. this.lyricist = value;
  106. }
  107. public get Cursors(): GraphicalLine[] {
  108. return this.cursors;
  109. }
  110. public get SelectionStartSymbol(): SelectionStartSymbol {
  111. return this.selectionStartSymbol;
  112. }
  113. public get SelectionEndSymbol(): SelectionEndSymbol {
  114. return this.selectionEndSymbol;
  115. }
  116. public get MinAllowedSystemWidth(): number {
  117. return this.minAllowedSystemWidth;
  118. }
  119. public set MinAllowedSystemWidth(value: number) {
  120. this.minAllowedSystemWidth = value;
  121. }
  122. // public get SystemImages(): Dictionary<MusicSystem, SystemImageProperties> {
  123. // return this.systemImages;
  124. // }
  125. public get NumberOfStaves(): number {
  126. return this.numberOfStaves;
  127. }
  128. public get LeadSheet(): boolean {
  129. return this.leadSheet;
  130. }
  131. public set LeadSheet(value: boolean) {
  132. this.leadSheet = value;
  133. }
  134. public static transformRelativeToAbsolutePosition(graphicalMusicSheet: GraphicalMusicSheet): void {
  135. for (let i: number = 0; i < graphicalMusicSheet.MusicPages.length; i++) {
  136. let pageAbsolute: PointF2D = graphicalMusicSheet.MusicPages[i].setMusicPageAbsolutePosition(i, graphicalMusicSheet.ParentMusicSheet.rules);
  137. let page: GraphicalMusicPage = graphicalMusicSheet.MusicPages[i];
  138. page.PositionAndShape.calculateAbsolutePositionsRecursive(pageAbsolute.x, pageAbsolute.y);
  139. }
  140. }
  141. public Initialize(): void {
  142. this.verticalGraphicalStaffEntryContainers = [];
  143. this.musicPages = [];
  144. this.measureList = [];
  145. }
  146. public reCalculate(): void {
  147. this.calculator.calculate();
  148. }
  149. public prepare(): void {
  150. this.calculator.prepareGraphicalMusicSheet();
  151. }
  152. public EnforceRedrawOfMusicSystems(): void {
  153. for (let idx: number = 0, len: number = this.musicPages.length; idx < len; ++idx) {
  154. let graphicalMusicPage: GraphicalMusicPage = this.musicPages[idx];
  155. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  156. let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  157. musicSystem.needsToBeRedrawn = true;
  158. }
  159. }
  160. }
  161. public getClickedObject<T>(positionOnMusicSheet: PointF2D): T {
  162. for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
  163. let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
  164. return graphicalMusicPage.PositionAndShape.getClickedObjectOfType<T>(positionOnMusicSheet);
  165. }
  166. return undefined;
  167. }
  168. public findGraphicalStaffEntryFromMeasureList(staffIndex: number, measureIndex: number, sourceStaffEntry: SourceStaffEntry): GraphicalStaffEntry {
  169. for (let i: number = measureIndex; i < this.measureList.length; i++) {
  170. let graphicalMeasure: StaffMeasure = this.measureList[i][staffIndex];
  171. for (let idx: number = 0, len: number = graphicalMeasure.staffEntries.length; idx < len; ++idx) {
  172. let graphicalStaffEntry: GraphicalStaffEntry = graphicalMeasure.staffEntries[idx];
  173. if (graphicalStaffEntry.sourceStaffEntry === sourceStaffEntry) {
  174. return graphicalStaffEntry;
  175. }
  176. }
  177. }
  178. return undefined;
  179. }
  180. public findNextGraphicalStaffEntry(staffIndex: number, measureIndex: number, graphicalStaffEntry: GraphicalStaffEntry): GraphicalStaffEntry {
  181. let graphicalMeasure: StaffMeasure = graphicalStaffEntry.parentMeasure;
  182. let graphicalStaffEntryIndex: number = graphicalMeasure.staffEntries.indexOf(graphicalStaffEntry);
  183. if (graphicalStaffEntryIndex < graphicalMeasure.staffEntries.length - 1) {
  184. return graphicalMeasure.staffEntries[graphicalStaffEntryIndex + 1];
  185. } else if (measureIndex < this.measureList.length - 1) {
  186. let nextMeasure: StaffMeasure = this.measureList[measureIndex + 1][staffIndex];
  187. if (nextMeasure.staffEntries.length > 0) {
  188. return nextMeasure.staffEntries[0];
  189. }
  190. }
  191. return undefined;
  192. }
  193. public getFirstVisibleMeasuresListFromIndeces(start: number, end: number): StaffMeasure[] {
  194. let graphicalMeasures: StaffMeasure[] = [];
  195. let numberOfStaves: number = this.measureList[0].length;
  196. for (let i: number = start; i <= end; i++) {
  197. for (let j: number = 0; j < numberOfStaves; j++) {
  198. if (this.measureList[i][j].isVisible()) {
  199. graphicalMeasures.push(this.measureList[i][j]);
  200. break;
  201. }
  202. }
  203. }
  204. return graphicalMeasures;
  205. }
  206. public orderMeasuresByStaffLine(measures: StaffMeasure[]): StaffMeasure[][] {
  207. let orderedMeasures: StaffMeasure[][] = [];
  208. let mList: StaffMeasure[] = [];
  209. orderedMeasures.push(mList);
  210. for (let i: number = 0; i < measures.length; i++) {
  211. if (i === 0) {
  212. mList.push(measures[0]);
  213. } else {
  214. if (measures[i].ParentStaffLine === measures[i - 1].ParentStaffLine) {
  215. mList.push(measures[i]);
  216. } else {
  217. if (orderedMeasures.indexOf(mList) === -1) {
  218. orderedMeasures.push(mList);
  219. }
  220. mList = [];
  221. orderedMeasures.push(mList);
  222. mList.push(measures[i]);
  223. }
  224. }
  225. }
  226. return orderedMeasures;
  227. }
  228. public initializeActiveClefs(): ClefInstruction[] {
  229. let activeClefs: ClefInstruction[] = [];
  230. let firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
  231. if (firstSourceMeasure !== undefined) {
  232. for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
  233. let clef: ClefInstruction = new ClefInstruction();
  234. if (firstSourceMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
  235. for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
  236. let abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
  237. if (abstractNotationInstruction instanceof ClefInstruction) {
  238. clef = <ClefInstruction>abstractNotationInstruction;
  239. }
  240. }
  241. }
  242. activeClefs.push(clef);
  243. }
  244. }
  245. return activeClefs;
  246. }
  247. public GetMainKey(): KeyInstruction {
  248. let firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
  249. if (firstSourceMeasure !== undefined) {
  250. for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
  251. for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
  252. let abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
  253. if (abstractNotationInstruction instanceof KeyInstruction) {
  254. return <KeyInstruction>abstractNotationInstruction;
  255. }
  256. }
  257. }
  258. }
  259. return undefined;
  260. }
  261. public getOrCreateVerticalContainer(timestamp: Fraction): VerticalGraphicalStaffEntryContainer {
  262. if (this.verticalGraphicalStaffEntryContainers.length === 0 ||
  263. (CollectionUtil.getLastElement(this.verticalGraphicalStaffEntryContainers).AbsoluteTimestamp).lt(timestamp)) {
  264. let verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
  265. new VerticalGraphicalStaffEntryContainer(this.numberOfStaves, timestamp);
  266. this.verticalGraphicalStaffEntryContainers.push(verticalGraphicalStaffEntryContainer);
  267. return verticalGraphicalStaffEntryContainer;
  268. }
  269. for (let i: number = this.verticalGraphicalStaffEntryContainers.length - 1; i >= 0; i--) {
  270. if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp.lt(timestamp)) {
  271. let verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
  272. new VerticalGraphicalStaffEntryContainer(this.numberOfStaves, timestamp);
  273. this.verticalGraphicalStaffEntryContainers.splice(i + 1, 0, verticalGraphicalStaffEntryContainer);
  274. return verticalGraphicalStaffEntryContainer;
  275. }
  276. if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp.Equals(timestamp)) {
  277. return this.verticalGraphicalStaffEntryContainers[i];
  278. }
  279. }
  280. return undefined;
  281. }
  282. public GetVerticalContainerFromTimestamp(timestamp: Fraction, startIndex: number = 0): VerticalGraphicalStaffEntryContainer {
  283. let index: number = CollectionUtil.binarySearch(this.verticalGraphicalStaffEntryContainers,
  284. new VerticalGraphicalStaffEntryContainer(0, timestamp),
  285. VerticalGraphicalStaffEntryContainer.compareByTimestamp,
  286. startIndex,
  287. this.verticalGraphicalStaffEntryContainers.length - startIndex);
  288. if (index >= 0) {
  289. return this.verticalGraphicalStaffEntryContainers[index];
  290. }
  291. return undefined;
  292. }
  293. public GetInterpolatedIndexInVerticalContainers(musicTimestamp: Fraction): number {
  294. let containers: VerticalGraphicalStaffEntryContainer[] = this.verticalGraphicalStaffEntryContainers;
  295. let leftIndex: number = 0;
  296. let rightIndex: number = containers.length - 1;
  297. let foundIndex: number;
  298. let leftTS: Fraction = undefined;
  299. let rightTS: Fraction = undefined;
  300. if (musicTimestamp.lte(containers[containers.length - 1].AbsoluteTimestamp)) {
  301. while (rightIndex - leftIndex > 1) {
  302. let middleIndex: number = Math.floor((rightIndex + leftIndex) / 2);
  303. if (containers[leftIndex].AbsoluteTimestamp.Equals(musicTimestamp)) {
  304. rightIndex = leftIndex;
  305. break;
  306. } else if (containers[rightIndex].AbsoluteTimestamp.Equals(musicTimestamp)) {
  307. leftIndex = rightIndex;
  308. break;
  309. } else if (containers[middleIndex].AbsoluteTimestamp.Equals(musicTimestamp)) {
  310. return this.verticalGraphicalStaffEntryContainers.indexOf(containers[middleIndex]);
  311. } else if (musicTimestamp.lt(containers[middleIndex].AbsoluteTimestamp)) {
  312. rightIndex = middleIndex;
  313. } else {
  314. leftIndex = middleIndex;
  315. }
  316. }
  317. if (leftIndex === rightIndex) {
  318. return this.verticalGraphicalStaffEntryContainers.indexOf(containers[leftIndex]);
  319. }
  320. leftTS = containers[leftIndex].AbsoluteTimestamp;
  321. rightTS = containers[rightIndex].AbsoluteTimestamp;
  322. } else {
  323. leftTS = containers[containers.length - 1].AbsoluteTimestamp;
  324. rightTS = Fraction.plus(this.getLongestStaffEntryDuration(containers.length - 1), leftTS);
  325. rightIndex = containers.length;
  326. }
  327. let diff: number = rightTS.RealValue - leftTS.RealValue;
  328. let diffTS: number = rightTS.RealValue - musicTimestamp.RealValue;
  329. foundIndex = rightIndex - (diffTS / diff);
  330. return Math.min(foundIndex, this.verticalGraphicalStaffEntryContainers.length);
  331. }
  332. public getVisibleStavesIndecesFromSourceMeasure(visibleMeasures: StaffMeasure[]): number[] {
  333. let visibleInstruments: Instrument[] = [];
  334. let visibleStavesIndeces: number[] = [];
  335. for (let idx: number = 0, len: number = visibleMeasures.length; idx < len; ++idx) {
  336. let graphicalMeasure: StaffMeasure = visibleMeasures[idx];
  337. let instrument: Instrument = graphicalMeasure.ParentStaff.ParentInstrument;
  338. if (visibleInstruments.indexOf(instrument) === -1) {
  339. visibleInstruments.push(instrument);
  340. }
  341. }
  342. for (let idx: number = 0, len: number = visibleInstruments.length; idx < len; ++idx) {
  343. let instrument: Instrument = visibleInstruments[idx];
  344. let index: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(instrument);
  345. for (let j: number = 0; j < instrument.Staves.length; j++) {
  346. visibleStavesIndeces.push(index + j);
  347. }
  348. }
  349. return visibleStavesIndeces;
  350. }
  351. public getGraphicalMeasureFromSourceMeasureAndIndex(sourceMeasure: SourceMeasure, index: number): StaffMeasure {
  352. for (let i: number = 0; i < this.measureList.length; i++) {
  353. if (this.measureList[i][0].parentSourceMeasure === sourceMeasure) {
  354. return this.measureList[i][index];
  355. }
  356. }
  357. return undefined;
  358. }
  359. public getMeasureIndex(graphicalMeasure: StaffMeasure, measureIndex: number, inListIndex: number): boolean {
  360. measureIndex = 0;
  361. inListIndex = 0;
  362. for (; measureIndex < this.measureList.length; measureIndex++) {
  363. for (let idx: number = 0, len: number = this.measureList[measureIndex].length; idx < len; ++idx) {
  364. let measure: StaffMeasure = this.measureList[measureIndex][idx];
  365. if (measure === graphicalMeasure) {
  366. return true;
  367. }
  368. }
  369. }
  370. return false;
  371. }
  372. public GetNearesNote(clickPosition: PointF2D, maxClickDist: PointF2D): GraphicalNote {
  373. let initialSearchArea: number = 10;
  374. let foundNotes: GraphicalNote[] = [];
  375. let region: BoundingBox = new BoundingBox();
  376. region.BorderLeft = clickPosition.x - initialSearchArea;
  377. region.BorderTop = clickPosition.y - initialSearchArea;
  378. region.BorderRight = clickPosition.x + initialSearchArea;
  379. region.BorderBottom = clickPosition.y + initialSearchArea;
  380. region.AbsolutePosition = new PointF2D(0, 0);
  381. for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
  382. let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
  383. let entries: GraphicalNote[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalNote>(region);
  384. //let entriesArr: GraphicalNote[] = __as__<GraphicalNote[]>(entries, GraphicalNote[]) ? ? entries;
  385. if (entries === undefined) {
  386. continue;
  387. } else {
  388. for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
  389. let note: GraphicalNote = entries[idx2];
  390. if (Math.abs(note.PositionAndShape.AbsolutePosition.x - clickPosition.x) < maxClickDist.x
  391. && Math.abs(note.PositionAndShape.AbsolutePosition.y - clickPosition.y) < maxClickDist.y) {
  392. foundNotes.push(note);
  393. }
  394. }
  395. }
  396. }
  397. let closest: GraphicalNote = undefined;
  398. for (let idx: number = 0, len: number = foundNotes.length; idx < len; ++idx) {
  399. let note: GraphicalNote = foundNotes[idx];
  400. if (closest === undefined) {
  401. closest = note;
  402. } else {
  403. if (note.parentStaffEntry.relInMeasureTimestamp === undefined) {
  404. continue;
  405. }
  406. let deltaNew: number = this.CalculateDistance(note.PositionAndShape.AbsolutePosition, clickPosition);
  407. let deltaOld: number = this.CalculateDistance(closest.PositionAndShape.AbsolutePosition, clickPosition);
  408. if (deltaNew < deltaOld) {
  409. closest = note;
  410. }
  411. }
  412. }
  413. if (closest !== undefined) {
  414. return closest;
  415. }
  416. return undefined;
  417. }
  418. public GetClickableLabel(clickPosition: PointF2D): GraphicalLabel {
  419. let initialSearchAreaX: number = 4;
  420. let initialSearchAreaY: number = 4;
  421. let region: BoundingBox = new BoundingBox();
  422. region.BorderLeft = clickPosition.x - initialSearchAreaX;
  423. region.BorderTop = clickPosition.y - initialSearchAreaY;
  424. region.BorderRight = clickPosition.x + initialSearchAreaX;
  425. region.BorderBottom = clickPosition.y + initialSearchAreaY;
  426. region.AbsolutePosition = new PointF2D(0, 0);
  427. for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
  428. let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
  429. let entries: GraphicalLabel[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalLabel>(region);
  430. if (entries.length !== 1) {
  431. continue;
  432. } else {
  433. for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
  434. let clickedLabel: GraphicalLabel = entries[idx2];
  435. return clickedLabel;
  436. }
  437. }
  438. }
  439. return undefined;
  440. }
  441. public GetNearestStaffEntry(clickPosition: PointF2D): GraphicalStaffEntry {
  442. let initialSearchArea: number = 10;
  443. let foundEntries: GraphicalStaffEntry[] = [];
  444. let region: BoundingBox = new BoundingBox(undefined);
  445. region.BorderLeft = clickPosition.x - initialSearchArea;
  446. region.BorderTop = clickPosition.y - initialSearchArea;
  447. region.BorderRight = clickPosition.x + initialSearchArea;
  448. region.BorderBottom = clickPosition.y + initialSearchArea;
  449. region.AbsolutePosition = new PointF2D(0, 0);
  450. for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
  451. let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
  452. let entries: GraphicalStaffEntry[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalStaffEntry>(region, false);
  453. if (entries === undefined || entries.length === 0) {
  454. continue;
  455. } else {
  456. for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
  457. let gse: GraphicalStaffEntry = entries[idx2];
  458. foundEntries.push(gse);
  459. }
  460. }
  461. }
  462. let closest: GraphicalStaffEntry = undefined;
  463. for (let idx: number = 0, len: number = foundEntries.length; idx < len; ++idx) {
  464. let gse: GraphicalStaffEntry = foundEntries[idx];
  465. if (closest === undefined) {
  466. closest = gse;
  467. } else {
  468. if (gse.relInMeasureTimestamp === undefined) {
  469. continue;
  470. }
  471. let deltaNew: number = this.CalculateDistance(gse.PositionAndShape.AbsolutePosition, clickPosition);
  472. let deltaOld: number = this.CalculateDistance(closest.PositionAndShape.AbsolutePosition, clickPosition);
  473. if (deltaNew < deltaOld) {
  474. closest = gse;
  475. }
  476. }
  477. }
  478. if (closest !== undefined) {
  479. return closest;
  480. }
  481. return undefined;
  482. }
  483. public GetPossibleCommentAnchor(clickPosition: PointF2D): SourceStaffEntry {
  484. let entry: GraphicalStaffEntry = this.GetNearestStaffEntry(clickPosition);
  485. if (entry === undefined) {
  486. return undefined;
  487. }
  488. return entry.sourceStaffEntry;
  489. }
  490. public getClickedObjectOfType<T>(positionOnMusicSheet: PointF2D): T {
  491. for (let idx: number = 0, len: number = this.musicPages.length; idx < len; ++idx) {
  492. let page: GraphicalMusicPage = this.musicPages[idx];
  493. let o: Object = page.PositionAndShape.getClickedObjectOfType<T>(positionOnMusicSheet);
  494. if (o !== undefined) {
  495. return (o as T);
  496. }
  497. }
  498. return undefined;
  499. }
  500. public tryGetTimestampFromPosition(positionOnMusicSheet: PointF2D): Fraction {
  501. let entry: GraphicalStaffEntry = this.getClickedObjectOfType<GraphicalStaffEntry>(positionOnMusicSheet);
  502. if (entry === undefined) {
  503. return undefined;
  504. }
  505. return entry.getAbsoluteTimestamp();
  506. }
  507. public tryGetClickableLabel(positionOnMusicSheet: PointF2D): GraphicalLabel {
  508. try {
  509. return this.GetClickableLabel(positionOnMusicSheet);
  510. } catch (ex) {
  511. Logging.log("GraphicalMusicSheet.tryGetClickableObject", "positionOnMusicSheet: " + positionOnMusicSheet, ex);
  512. }
  513. return undefined;
  514. }
  515. public tryGetTimeStampFromPosition(positionOnMusicSheet: PointF2D): Fraction {
  516. try {
  517. let entry: GraphicalStaffEntry = this.GetNearestStaffEntry(positionOnMusicSheet);
  518. if (entry === undefined) {
  519. return undefined;
  520. }
  521. return entry.getAbsoluteTimestamp();
  522. } catch (ex) {
  523. Logging.log(
  524. "GraphicalMusicSheet.tryGetTimeStampFromPosition",
  525. "positionOnMusicSheet: " + positionOnMusicSheet, ex
  526. );
  527. }
  528. return undefined;
  529. }
  530. public getStaffEntry(index: number): GraphicalStaffEntry {
  531. let container: VerticalGraphicalStaffEntryContainer = this.VerticalGraphicalStaffEntryContainers[index];
  532. let staffEntry: GraphicalStaffEntry = undefined;
  533. try {
  534. for (let idx: number = 0, len: number = container.StaffEntries.length; idx < len; ++idx) {
  535. let entry: GraphicalStaffEntry = container.StaffEntries[idx];
  536. if (entry === undefined || !entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
  537. continue;
  538. }
  539. if (staffEntry === undefined) {
  540. staffEntry = entry;
  541. } else if (entry.PositionAndShape !== undefined && staffEntry.PositionAndShape !== undefined) {
  542. if (staffEntry.PositionAndShape.RelativePosition.x > entry.PositionAndShape.RelativePosition.x) {
  543. staffEntry = entry;
  544. }
  545. }
  546. }
  547. } catch (ex) {
  548. Logging.log("GraphicalMusicSheet.getStaffEntry", ex);
  549. }
  550. return staffEntry;
  551. }
  552. public GetPreviousVisibleContainerIndex(index: number): number {
  553. for (let i: number = index - 1; i >= 0; i--) {
  554. let entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
  555. for (let idx: number = 0, len: number = entries.length; idx < len; ++idx) {
  556. let entry: GraphicalStaffEntry = entries[idx];
  557. if (entry !== undefined && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
  558. return i;
  559. }
  560. }
  561. }
  562. return -1;
  563. }
  564. public GetNextVisibleContainerIndex(index: number): number {
  565. for (let i: number = index + 1; i < this.verticalGraphicalStaffEntryContainers.length; ++i) {
  566. let entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
  567. for (let idx: number = 0, len: number = entries.length; idx < len; ++idx) {
  568. let entry: GraphicalStaffEntry = entries[idx];
  569. if (entry !== undefined && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
  570. return i;
  571. }
  572. }
  573. }
  574. return -1;
  575. }
  576. public findClosestLeftStaffEntry(fractionalIndex: number, searchOnlyVisibleEntries: boolean): GraphicalStaffEntry {
  577. let foundEntry: GraphicalStaffEntry = undefined;
  578. let leftIndex: number = <number>Math.floor(fractionalIndex);
  579. leftIndex = Math.min(this.VerticalGraphicalStaffEntryContainers.length - 1, leftIndex);
  580. for (let i: number = leftIndex; i >= 0; i--) {
  581. foundEntry = this.getStaffEntry(i);
  582. if (foundEntry !== undefined) {
  583. if (searchOnlyVisibleEntries) {
  584. if (foundEntry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
  585. return foundEntry;
  586. }
  587. } else {
  588. return foundEntry;
  589. }
  590. }
  591. }
  592. return undefined;
  593. }
  594. public findClosestRightStaffEntry(fractionalIndex: number, returnOnlyVisibleEntries: boolean): GraphicalStaffEntry {
  595. let foundEntry: GraphicalStaffEntry = undefined;
  596. let rightIndex: number = <number>Math.max(0, Math.ceil(fractionalIndex));
  597. for (let i: number = rightIndex; i < this.VerticalGraphicalStaffEntryContainers.length; i++) {
  598. foundEntry = this.getStaffEntry(i);
  599. if (foundEntry !== undefined) {
  600. if (returnOnlyVisibleEntries) {
  601. if (foundEntry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
  602. return foundEntry;
  603. }
  604. } else {
  605. return foundEntry;
  606. }
  607. }
  608. }
  609. return undefined;
  610. }
  611. public calculateCursorLineAtTimestamp(musicTimestamp: Fraction, styleEnum: OutlineAndFillStyleEnum): GraphicalLine {
  612. let result: [number, MusicSystem] = this.calculateXPositionFromTimestamp(musicTimestamp);
  613. let xPos: number = result[0];
  614. let correspondingMusicSystem: MusicSystem = result[1];
  615. if (correspondingMusicSystem === undefined || correspondingMusicSystem.StaffLines.length === 0) {
  616. return undefined;
  617. }
  618. let yCoordinate: number = correspondingMusicSystem.PositionAndShape.AbsolutePosition.y;
  619. let height: number = CollectionUtil.last(correspondingMusicSystem.StaffLines).PositionAndShape.RelativePosition.y + 4;
  620. return new GraphicalLine(new PointF2D(xPos, yCoordinate), new PointF2D(xPos, yCoordinate + height), 3, styleEnum);
  621. }
  622. public calculateXPositionFromTimestamp(timeStamp: Fraction): [number, MusicSystem] {
  623. let currentMusicSystem: MusicSystem = undefined;
  624. let fractionalIndex: number = this.GetInterpolatedIndexInVerticalContainers(timeStamp);
  625. let previousStaffEntry: GraphicalStaffEntry = this.findClosestLeftStaffEntry(fractionalIndex, true);
  626. let nextStaffEntry: GraphicalStaffEntry = this.findClosestRightStaffEntry(fractionalIndex, true);
  627. let currentTimeStamp: number = timeStamp.RealValue;
  628. if (previousStaffEntry === undefined && nextStaffEntry === undefined) {
  629. return [0, undefined];
  630. }
  631. let previousStaffEntryMusicSystem: MusicSystem = undefined;
  632. if (previousStaffEntry !== undefined) {
  633. previousStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
  634. } else {
  635. previousStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
  636. }
  637. let nextStaffEntryMusicSystem: MusicSystem = undefined;
  638. if (nextStaffEntry !== undefined) {
  639. nextStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
  640. } else {
  641. nextStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
  642. }
  643. if (previousStaffEntryMusicSystem === nextStaffEntryMusicSystem) {
  644. currentMusicSystem = previousStaffEntryMusicSystem;
  645. let fraction: number;
  646. let previousStaffEntryPositionX: number;
  647. let nextStaffEntryPositionX: number;
  648. if (previousStaffEntry === undefined) {
  649. previousStaffEntryPositionX = nextStaffEntryPositionX = nextStaffEntry.PositionAndShape.AbsolutePosition.x;
  650. fraction = 0;
  651. } else if (nextStaffEntry === undefined) {
  652. previousStaffEntryPositionX = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
  653. nextStaffEntryPositionX = currentMusicSystem.GetRightBorderAbsoluteXPosition();
  654. let sm: SourceMeasure = previousStaffEntry.parentMeasure.parentSourceMeasure;
  655. fraction = (currentTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue) / (
  656. Fraction.plus(sm.AbsoluteTimestamp, sm.Duration).RealValue - previousStaffEntry.getAbsoluteTimestamp().RealValue);
  657. } else {
  658. previousStaffEntryPositionX = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
  659. nextStaffEntryPositionX = nextStaffEntry.PositionAndShape.AbsolutePosition.x;
  660. if (previousStaffEntry === nextStaffEntry) {
  661. fraction = 0;
  662. } else {
  663. fraction = (currentTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue) /
  664. (nextStaffEntry.getAbsoluteTimestamp().RealValue - previousStaffEntry.getAbsoluteTimestamp().RealValue);
  665. }
  666. }
  667. fraction = Math.min(1, Math.max(0, fraction));
  668. let interpolatedXPosition: number = previousStaffEntryPositionX + fraction * (nextStaffEntryPositionX - previousStaffEntryPositionX);
  669. return [interpolatedXPosition, currentMusicSystem];
  670. } else {
  671. let nextSystemLeftBorderTimeStamp: number = nextStaffEntry.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.RealValue;
  672. let fraction: number;
  673. let interpolatedXPosition: number;
  674. if (currentTimeStamp < nextSystemLeftBorderTimeStamp) {
  675. currentMusicSystem = previousStaffEntryMusicSystem;
  676. let previousStaffEntryPositionX: number = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
  677. let previousSystemRightBorderX: number = currentMusicSystem.GetRightBorderAbsoluteXPosition();
  678. fraction = (currentTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue) /
  679. (nextSystemLeftBorderTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue);
  680. fraction = Math.min(1, Math.max(0, fraction));
  681. interpolatedXPosition = previousStaffEntryPositionX + fraction * (previousSystemRightBorderX - previousStaffEntryPositionX);
  682. } else {
  683. currentMusicSystem = nextStaffEntryMusicSystem;
  684. let nextStaffEntryPositionX: number = nextStaffEntry.PositionAndShape.AbsolutePosition.x;
  685. let nextSystemLeftBorderX: number = currentMusicSystem.GetLeftBorderAbsoluteXPosition();
  686. fraction = (currentTimeStamp - nextSystemLeftBorderTimeStamp) /
  687. (nextStaffEntry.getAbsoluteTimestamp().RealValue - nextSystemLeftBorderTimeStamp);
  688. fraction = Math.min(1, Math.max(0, fraction));
  689. interpolatedXPosition = nextSystemLeftBorderX + fraction * (nextStaffEntryPositionX - nextSystemLeftBorderX);
  690. }
  691. return [interpolatedXPosition, currentMusicSystem];
  692. }
  693. }
  694. public GetNumberOfVisibleInstruments(): number {
  695. let visibleInstrumentCount: number = 0;
  696. for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
  697. let instrument: Instrument = this.musicSheet.Instruments[idx];
  698. if (instrument.Visible === true) {
  699. visibleInstrumentCount++;
  700. }
  701. }
  702. return visibleInstrumentCount;
  703. }
  704. public GetNumberOfFollowedInstruments(): number {
  705. let followedInstrumentCount: number = 0;
  706. for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
  707. let instrument: Instrument = this.musicSheet.Instruments[idx];
  708. if (instrument.Following === true) {
  709. followedInstrumentCount++;
  710. }
  711. }
  712. return followedInstrumentCount;
  713. }
  714. public GetGraphicalFromSourceMeasure(sourceMeasure: SourceMeasure): StaffMeasure[] {
  715. return this.sourceToGraphicalMeasureLinks.getValue(sourceMeasure);
  716. }
  717. public GetGraphicalFromSourceStaffEntry(sourceStaffEntry: SourceStaffEntry): GraphicalStaffEntry {
  718. let graphicalMeasure: StaffMeasure = this.GetGraphicalFromSourceMeasure(sourceStaffEntry.VerticalContainerParent.ParentMeasure)
  719. [sourceStaffEntry.ParentStaff.idInMusicSheet];
  720. return graphicalMeasure.findGraphicalStaffEntryFromTimestamp(sourceStaffEntry.Timestamp);
  721. }
  722. public GetGraphicalNoteFromSourceNote(note: Note, containingGse: GraphicalStaffEntry): GraphicalNote {
  723. for (let idx: number = 0, len: number = containingGse.notes.length; idx < len; ++idx) {
  724. let graphicalNotes: GraphicalNote[] = containingGse.notes[idx];
  725. for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
  726. let graphicalNote: GraphicalNote = graphicalNotes[idx2];
  727. if (graphicalNote.sourceNote === note) {
  728. return graphicalNote;
  729. }
  730. }
  731. }
  732. return undefined;
  733. }
  734. private CalculateDistance(pt1: PointF2D, pt2: PointF2D): number {
  735. let deltaX: number = pt1.x - pt2.x;
  736. let deltaY: number = pt1.y - pt2.y;
  737. return (deltaX * deltaX) + (deltaY * deltaY);
  738. }
  739. private getLongestStaffEntryDuration(index: number): Fraction {
  740. let maxLength: Fraction = new Fraction(0, 1);
  741. for (let idx: number = 0, len: number = this.verticalGraphicalStaffEntryContainers[index].StaffEntries.length; idx < len; ++idx) {
  742. let graphicalStaffEntry: GraphicalStaffEntry = this.verticalGraphicalStaffEntryContainers[index].StaffEntries[idx];
  743. if (graphicalStaffEntry === undefined) {
  744. continue;
  745. }
  746. for (let idx2: number = 0, len2: number = graphicalStaffEntry.notes.length; idx2 < len2; ++idx2) {
  747. let graphicalNotes: GraphicalNote[] = graphicalStaffEntry.notes[idx2];
  748. for (let idx3: number = 0, len3: number = graphicalNotes.length; idx3 < len3; ++idx3) {
  749. let note: GraphicalNote = graphicalNotes[idx3];
  750. if (maxLength.lt(note.graphicalNoteLength)) {
  751. maxLength = note.graphicalNoteLength;
  752. }
  753. }
  754. }
  755. }
  756. return maxLength;
  757. }
  758. }
  759. export class SystemImageProperties {
  760. public positionInPixels: PointF2D;
  761. public systemImageId: number;
  762. public system: MusicSystem;
  763. }