GraphicalMusicSheet.ts 39 KB

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