GraphicalMusicSheet.ts 38 KB

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