1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | Vex.Flow.StaveNote = (function() {
|
13 | var StaveNote = function(note_struct) {
|
14 | if (arguments.length > 0) this.init(note_struct);
|
15 | };
|
16 | StaveNote.CATEGORY = "stavenotes";
|
17 |
|
18 |
|
19 | function L() { if (StaveNote.DEBUG) Vex.L("Vex.Flow.StaveNote", arguments); }
|
20 |
|
21 | var Stem = Vex.Flow.Stem;
|
22 | var NoteHead = Vex.Flow.NoteHead;
|
23 |
|
24 |
|
25 | StaveNote.STEM_UP = Stem.UP;
|
26 | StaveNote.STEM_DOWN = Stem.DOWN;
|
27 |
|
28 |
|
29 | var shiftRestVertical = function(rest, note, dir) {
|
30 | var delta = (note.isrest ? 0.0 : 1.0) * dir;
|
31 |
|
32 | rest.line += delta;
|
33 | rest.max_line += delta;
|
34 | rest.min_line += delta;
|
35 | rest.note.setKeyLine(0, rest.note.getKeyLine(0) + (delta));
|
36 | };
|
37 |
|
38 |
|
39 | var centerRest = function(rest, noteU, noteL) {
|
40 | var delta = rest.line - Vex.MidLine(noteU.min_line, noteL.max_line);
|
41 | rest.note.setKeyLine(0, rest.note.getKeyLine(0) - delta);
|
42 | rest.line -= delta;
|
43 | rest.max_line -= delta;
|
44 | rest.min_line -= delta;
|
45 | };
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | StaveNote.format = function(notes, state) {
|
51 | if (!notes || notes.length < 2) return false;
|
52 |
|
53 | if (notes[0].getStave() != null) return StaveNote.formatByY(notes, state);
|
54 |
|
55 | var notes_list= [];
|
56 |
|
57 | for (var i = 0; i < notes.length; i++) {
|
58 | var props = notes[i].getKeyProps();
|
59 | var line = props[0].line;
|
60 | var minL = props[props.length -1].line;
|
61 | var stem_dir = notes[i].getStemDirection();
|
62 | var stem_max = notes[i].getStemLength() / 10;
|
63 | var stem_min = notes[i].getStemMinumumLength() / 10;
|
64 |
|
65 | var maxL;
|
66 | if (notes[i].isRest()) {
|
67 | maxL = line + notes[i].glyph.line_above;
|
68 | minL = line - notes[i].glyph.line_below;
|
69 | } else {
|
70 | maxL = stem_dir == 1 ? props[props.length -1].line + stem_max
|
71 | : props[props.length -1].line;
|
72 | minL = stem_dir == 1 ? props[0].line
|
73 | : props[0].line - stem_max;
|
74 | }
|
75 | notes_list.push(
|
76 | {line: props[0].line,
|
77 | max_line: maxL,
|
78 | min_line: minL,
|
79 | isrest: notes[i].isRest(),
|
80 | stem_dir: stem_dir,
|
81 | stem_max: stem_max,
|
82 | stem_min: stem_min,
|
83 | voice_shift: notes[i].getVoiceShiftWidth(),
|
84 | is_displaced: notes[i].isDisplaced(),
|
85 | note: notes[i]});
|
86 | }
|
87 |
|
88 | var voices = notes_list.length;
|
89 |
|
90 | var noteU = notes_list[0];
|
91 | var noteM = voices > 2 ? notes_list[1] : null;
|
92 | var noteL = voices > 2 ? notes_list[2] : notes_list[1];
|
93 |
|
94 |
|
95 |
|
96 | if (voices == 2 && noteU.stem_dir == -1 && noteL.stem_dir == 1) {
|
97 | noteU = notes_list[1];
|
98 | noteL = notes_list[0];
|
99 | }
|
100 |
|
101 | var voice_x_shift = Math.max(noteU.voice_shift, noteL.voice_shift);
|
102 | var x_shift = 0;
|
103 | var stem_delta;
|
104 |
|
105 |
|
106 | if (voices == 2) {
|
107 | var line_spacing = noteU.stem_dir == noteL.stem_dir ? 0.0 : 0.5;
|
108 |
|
109 | if (noteU.stem_dir == noteL.stem_dir &&
|
110 | noteU.min_line <= noteL.max_line) {
|
111 | if (!noteU.isrest) {
|
112 | stem_delta = Math.abs(noteU.line - (noteL.max_line + 0.5));
|
113 | stem_delta = Math.max(stem_delta, noteU.stem_min);
|
114 | noteU.min_line = noteU.line - stem_delta;
|
115 | noteU.note.setStemLength(stem_delta * 10);
|
116 | }
|
117 | }
|
118 | if (noteU.min_line <= noteL.max_line + line_spacing) {
|
119 | if (noteU.isrest) {
|
120 |
|
121 | shiftRestVertical(noteU, noteL, 1);
|
122 | } else if (noteL.isrest) {
|
123 |
|
124 | shiftRestVertical(noteL, noteU, -1);
|
125 | } else {
|
126 | x_shift = voice_x_shift;
|
127 | if (noteU.stem_dir == noteL.stem_dir)
|
128 |
|
129 | noteU.note.setXShift(x_shift + 3);
|
130 | else
|
131 |
|
132 | noteL.note.setXShift(x_shift);
|
133 | }
|
134 | }
|
135 |
|
136 |
|
137 | return true;
|
138 | }
|
139 |
|
140 |
|
141 | if (noteM != null && noteM.min_line < noteL.max_line + 0.5) {
|
142 | if (!noteM.isrest) {
|
143 | stem_delta = Math.abs(noteM.line - (noteL.max_line + 0.5));
|
144 | stem_delta = Math.max(stem_delta, noteM.stem_min);
|
145 | noteM.min_line = noteM.line - stem_delta;
|
146 | noteM.note.setStemLength(stem_delta * 10);
|
147 | }
|
148 | }
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 | if (noteM.isrest && !noteU.isrest && !noteL.isrest) {
|
155 | if (noteU.min_line <= noteM.max_line ||
|
156 | noteM.min_line <= noteL.max_line) {
|
157 | var rest_height = noteM.max_line - noteM.min_line;
|
158 | var space = noteU.min_line - noteL.max_line;
|
159 | if (rest_height < space)
|
160 |
|
161 | centerRest(noteM, noteU, noteL);
|
162 | else {
|
163 | x_shift = voice_x_shift + 3;
|
164 | noteM.note.setXShift(x_shift);
|
165 | }
|
166 |
|
167 | return true;
|
168 | }
|
169 | }
|
170 |
|
171 |
|
172 | if (noteU.isrest && noteM.isrest && noteL.isrest) {
|
173 |
|
174 | shiftRestVertical(noteU, noteM, 1);
|
175 |
|
176 | shiftRestVertical(noteL, noteM, -1);
|
177 |
|
178 | return true;
|
179 | }
|
180 |
|
181 |
|
182 | if (noteM.isrest && noteU.isrest && noteM.min_line <= noteL.max_line)
|
183 |
|
184 | shiftRestVertical(noteM, noteL, 1);
|
185 | if (noteM.isrest && noteL.isrest && noteU.min_line <= noteM.max_line)
|
186 |
|
187 | shiftRestVertical(noteM, noteU, -1);
|
188 | if (noteU.isrest && noteU.min_line <= noteM.max_line)
|
189 |
|
190 | shiftRestVertical(noteU, noteM, 1);
|
191 | if (noteL.isrest && noteM.min_line <= noteL.max_line)
|
192 |
|
193 | shiftRestVertical(noteL, noteM, -1);
|
194 |
|
195 |
|
196 | if ((!noteU.isrest && !noteM.isrest && noteU.min_line <= noteM.max_line + 0.5) ||
|
197 | (!noteM.isrest && !noteL.isrest && noteM.min_line <= noteL.max_line)) {
|
198 | x_shift = voice_x_shift + 3;
|
199 | noteM.note.setXShift(x_shift);
|
200 | }
|
201 |
|
202 | return true;
|
203 | };
|
204 |
|
205 | StaveNote.formatByY = function(notes, state) {
|
206 |
|
207 |
|
208 | var hasStave = true;
|
209 | var i;
|
210 |
|
211 | for (i = 0; i < notes.length; i++) {
|
212 | hasStave = hasStave && notes[i].getStave() != null;
|
213 | }
|
214 |
|
215 | if (!hasStave) throw new Vex.RERR("Stave Missing",
|
216 | "All notes must have a stave - Vex.Flow.ModifierContext.formatMultiVoice!");
|
217 |
|
218 | var x_shift = 0;
|
219 |
|
220 | for (i = 0; i < notes.length - 1; i++) {
|
221 | var top_note = notes[i];
|
222 | var bottom_note = notes[i + 1];
|
223 |
|
224 | if (top_note.getStemDirection() == Vex.Flow.StaveNote.STEM_DOWN) {
|
225 | top_note = notes[i + 1];
|
226 | bottom_note = notes[i];
|
227 | }
|
228 |
|
229 | var top_keys = top_note.getKeyProps();
|
230 | var bottom_keys = bottom_note.getKeyProps();
|
231 |
|
232 | var topY = top_note.getStave().getYForLine(top_keys[0].line);
|
233 | var bottomY = bottom_note.getStave().getYForLine(bottom_keys[bottom_keys.length - 1].line);
|
234 |
|
235 | var line_space = top_note.getStave().options.spacing_between_lines_px;
|
236 | if (Math.abs(topY - bottomY) == line_space / 2) {
|
237 | x_shift = top_note.getVoiceShiftWidth();
|
238 | bottom_note.setXShift(x_shift);
|
239 | }
|
240 | }
|
241 |
|
242 | state.right_shift += x_shift;
|
243 | };
|
244 |
|
245 | StaveNote.postFormat = function(notes) {
|
246 | if (!notes) return false;
|
247 |
|
248 | notes.forEach(function(note) {
|
249 | note.postFormat();
|
250 | });
|
251 |
|
252 | return true;
|
253 | };
|
254 |
|
255 |
|
256 |
|
257 | Vex.Inherit(StaveNote, Vex.Flow.StemmableNote, {
|
258 | init: function(note_struct) {
|
259 | StaveNote.superclass.init.call(this, note_struct);
|
260 |
|
261 | this.keys = note_struct.keys;
|
262 | this.clef = note_struct.clef;
|
263 | this.octave_shift = note_struct.octave_shift;
|
264 | this.beam = null;
|
265 |
|
266 |
|
267 | this.glyph = Vex.Flow.durationToGlyph(this.duration, this.noteType);
|
268 | if (!this.glyph) {
|
269 | throw new Vex.RuntimeError("BadArguments",
|
270 | "Invalid note initialization data (No glyph found): " +
|
271 | JSON.stringify(note_struct));
|
272 | }
|
273 |
|
274 |
|
275 | this.displaced = false;
|
276 | this.dot_shiftY = 0;
|
277 |
|
278 | this.keyProps = [];
|
279 |
|
280 | this.use_default_head_x = false;
|
281 |
|
282 |
|
283 | this.note_heads = [];
|
284 | this.modifiers = [];
|
285 |
|
286 | Vex.Merge(this.render_options, {
|
287 |
|
288 | glyph_font_scale: 35,
|
289 |
|
290 | stroke_px: 3
|
291 | });
|
292 |
|
293 | this.calculateKeyProps();
|
294 |
|
295 | this.buildStem();
|
296 |
|
297 |
|
298 | if (note_struct.auto_stem) {
|
299 | this.autoStem();
|
300 | } else {
|
301 | this.setStemDirection(note_struct.stem_direction);
|
302 | }
|
303 |
|
304 | this.buildNoteHeads();
|
305 |
|
306 |
|
307 | this.calcExtraPx();
|
308 | },
|
309 |
|
310 |
|
311 | buildStem: function() {
|
312 | var glyph = this.getGlyph();
|
313 |
|
314 | var y_extend = 0;
|
315 | if (glyph.code_head == "v95" || glyph.code_head == "v3e") {
|
316 | y_extend = -4;
|
317 | }
|
318 |
|
319 | var stem = new Stem({
|
320 | y_extend: y_extend
|
321 | });
|
322 |
|
323 | if (this.isRest()) {
|
324 | stem.hide = true;
|
325 | }
|
326 |
|
327 | this.setStem(stem);
|
328 | },
|
329 |
|
330 |
|
331 | buildNoteHeads: function() {
|
332 | var stem_direction = this.getStemDirection();
|
333 |
|
334 | var keys = this.getKeys();
|
335 |
|
336 | var last_line = null;
|
337 | var line_diff = null;
|
338 | var displaced = false;
|
339 |
|
340 |
|
341 | var start_i = 0;
|
342 | var end_i = keys.length;
|
343 | var step_i = 1;
|
344 |
|
345 |
|
346 | if (stem_direction === Stem.DOWN) {
|
347 | start_i = keys.length - 1;
|
348 | end_i = -1;
|
349 | step_i = -1;
|
350 | }
|
351 |
|
352 | for (var i = start_i; i != end_i; i += step_i) {
|
353 | var note_props = this.keyProps[i];
|
354 |
|
355 | var line = note_props.line;
|
356 |
|
357 |
|
358 |
|
359 | if (last_line === null) {
|
360 | last_line = line;
|
361 | } else {
|
362 | line_diff = Math.abs(last_line - line);
|
363 | if (line_diff === 0 || line_diff === 0.5) {
|
364 | displaced = !displaced;
|
365 | } else {
|
366 | displaced = false;
|
367 | this.use_default_head_x = true;
|
368 | }
|
369 | }
|
370 | last_line = line;
|
371 |
|
372 | var note_head = new NoteHead({
|
373 | duration: this.duration,
|
374 | note_type: this.noteType,
|
375 | displaced: displaced,
|
376 | stem_direction: stem_direction,
|
377 | custom_glyph_code: note_props.code,
|
378 | glyph_font_scale: this.render_options.glyph_font_scale,
|
379 | x_shift: note_props.shift_right,
|
380 | line: note_props.line
|
381 | });
|
382 |
|
383 | this.note_heads[i] = note_head;
|
384 | }
|
385 | },
|
386 |
|
387 |
|
388 | autoStem: function() {
|
389 | var auto_stem_direction;
|
390 |
|
391 |
|
392 | this.min_line = this.keyProps[0].line;
|
393 | this.max_line = this.keyProps[this.keyProps.length - 1].line;
|
394 | var decider = (this.min_line + this.max_line) / 2;
|
395 |
|
396 | if (decider < 3) {
|
397 | auto_stem_direction = 1;
|
398 | } else {
|
399 | auto_stem_direction = -1;
|
400 | }
|
401 |
|
402 | this.setStemDirection(auto_stem_direction);
|
403 | },
|
404 |
|
405 |
|
406 | calculateKeyProps: function() {
|
407 | var last_line = null;
|
408 | for (var i = 0; i < this.keys.length; ++i) {
|
409 | var key = this.keys[i];
|
410 |
|
411 |
|
412 |
|
413 | if (this.glyph.rest) this.glyph.position = key;
|
414 | var options = { octave_shift: this.octave_shift || 0 };
|
415 | var props = Vex.Flow.keyProperties(key, this.clef, options);
|
416 | if (!props) {
|
417 | throw new Vex.RuntimeError("BadArguments",
|
418 | "Invalid key for note properties: " + key);
|
419 | }
|
420 |
|
421 |
|
422 | if (props.key === "R") {
|
423 | if (this.duration === "1" || this.duration === "w") {
|
424 | props.line = 4;
|
425 | } else {
|
426 | props.line = 3;
|
427 | }
|
428 | }
|
429 |
|
430 |
|
431 | var line = props.line;
|
432 | if (last_line === null) {
|
433 | last_line = line;
|
434 | } else {
|
435 | if (Math.abs(last_line - line) == 0.5) {
|
436 | this.displaced = true;
|
437 | props.displaced = true;
|
438 |
|
439 |
|
440 |
|
441 | if (this.keyProps.length > 0) {
|
442 | this.keyProps[i-1].displaced = true;
|
443 | }
|
444 | }
|
445 | }
|
446 |
|
447 | last_line = line;
|
448 | this.keyProps.push(props);
|
449 | }
|
450 |
|
451 |
|
452 | this.keyProps.sort(function(a, b) { return a.line - b.line; });
|
453 | },
|
454 |
|
455 |
|
456 | getBoundingBox: function() {
|
457 | if (!this.preFormatted) throw new Vex.RERR("UnformattedNote",
|
458 | "Can't call getBoundingBox on an unformatted note.");
|
459 |
|
460 | var metrics = this.getMetrics();
|
461 |
|
462 | var w = metrics.width;
|
463 | var x = this.getAbsoluteX() - metrics.modLeftPx - metrics.extraLeftPx;
|
464 |
|
465 | var min_y = 0;
|
466 | var max_y = 0;
|
467 | var half_line_spacing = this.getStave().getSpacingBetweenLines() / 2;
|
468 | var line_spacing = half_line_spacing * 2;
|
469 |
|
470 | if (this.isRest()) {
|
471 | var y = this.ys[0];
|
472 | var frac = Vex.Flow.durationToFraction(this.duration);
|
473 | if (frac.equals(1) || frac.equals(2)) {
|
474 | min_y = y - half_line_spacing;
|
475 | max_y = y + half_line_spacing;
|
476 | } else {
|
477 | min_y = y - (this.glyph.line_above * line_spacing);
|
478 | max_y = y + (this.glyph.line_below * line_spacing);
|
479 | }
|
480 | } else if (this.glyph.stem) {
|
481 | var ys = this.getStemExtents();
|
482 | ys.baseY += half_line_spacing * this.stem_direction;
|
483 | min_y = Vex.Min(ys.topY, ys.baseY);
|
484 | max_y = Vex.Max(ys.topY, ys.baseY);
|
485 | } else {
|
486 | min_y = null;
|
487 | max_y = null;
|
488 |
|
489 | for (var i=0; i < this.ys.length; ++i) {
|
490 | var yy = this.ys[i];
|
491 | if (i === 0) {
|
492 | min_y = yy;
|
493 | max_y = yy;
|
494 | } else {
|
495 | min_y = Vex.Min(yy, min_y);
|
496 | max_y = Vex.Max(yy, max_y);
|
497 | }
|
498 | min_y -= half_line_spacing;
|
499 | max_y += half_line_spacing;
|
500 | }
|
501 | }
|
502 |
|
503 | return new Vex.Flow.BoundingBox(x, min_y, w, max_y - min_y);
|
504 | },
|
505 |
|
506 |
|
507 |
|
508 | getLineNumber: function(is_top_note) {
|
509 | if(!this.keyProps.length) throw new Vex.RERR("NoKeyProps",
|
510 | "Can't get bottom note line, because note is not initialized properly.");
|
511 | var result_line = this.keyProps[0].line;
|
512 |
|
513 |
|
514 | for(var i=0; i<this.keyProps.length; i++){
|
515 | var this_line = this.keyProps[i].line;
|
516 | if(is_top_note)
|
517 | if(this_line > result_line)
|
518 | result_line = this_line;
|
519 | else
|
520 | if(this_line < result_line)
|
521 | result_line = this_line;
|
522 | }
|
523 |
|
524 | return result_line;
|
525 | },
|
526 |
|
527 |
|
528 | isRest: function() { return this.glyph.rest; },
|
529 |
|
530 |
|
531 | isChord: function() { return !this.isRest() && this.keys.length > 1; },
|
532 |
|
533 |
|
534 | hasStem: function() { return this.glyph.stem; },
|
535 |
|
536 |
|
537 |
|
538 | getYForTopText: function(text_line) {
|
539 | var extents = this.getStemExtents();
|
540 | return Vex.Min(this.stave.getYForTopText(text_line),
|
541 | extents.topY - (this.render_options.annotation_spacing * (text_line + 1)));
|
542 | },
|
543 | getYForBottomText: function(text_line) {
|
544 | var extents = this.getStemExtents();
|
545 | return Vex.Max(this.stave.getYForTopText(text_line),
|
546 | extents.baseY + (this.render_options.annotation_spacing * (text_line)));
|
547 | },
|
548 |
|
549 |
|
550 |
|
551 | setStave: function(stave) {
|
552 | var superclass = Vex.Flow.StaveNote.superclass;
|
553 | superclass.setStave.call(this, stave);
|
554 |
|
555 | var ys = this.note_heads.map(function(note_head) {
|
556 | note_head.setStave(stave);
|
557 | return note_head.getY();
|
558 | });
|
559 |
|
560 | this.setYs(ys);
|
561 |
|
562 | var bounds = this.getNoteHeadBounds();
|
563 | if(!this.beam){
|
564 | this.stem.setYBounds(bounds.y_top, bounds.y_bottom);
|
565 | }
|
566 |
|
567 | return this;
|
568 | },
|
569 |
|
570 |
|
571 | getKeys: function() { return this.keys; },
|
572 |
|
573 |
|
574 | getKeyProps: function() {
|
575 | return this.keyProps;
|
576 | },
|
577 |
|
578 |
|
579 | isDisplaced: function() {
|
580 | return this.displaced;
|
581 | },
|
582 |
|
583 |
|
584 | setNoteDisplaced: function(displaced) {
|
585 | this.displaced = displaced;
|
586 | return this;
|
587 | },
|
588 |
|
589 |
|
590 | getTieRightX: function() {
|
591 | var tieStartX = this.getAbsoluteX();
|
592 | tieStartX += this.glyph.head_width + this.x_shift + this.extraRightPx;
|
593 | if (this.modifierContext) tieStartX += this.modifierContext.getExtraRightPx();
|
594 | return tieStartX;
|
595 | },
|
596 |
|
597 |
|
598 | getTieLeftX: function() {
|
599 | var tieEndX = this.getAbsoluteX();
|
600 | tieEndX += this.x_shift - this.extraLeftPx;
|
601 | return tieEndX;
|
602 | },
|
603 |
|
604 |
|
605 | getLineForRest: function() {
|
606 | var rest_line = this.keyProps[0].line;
|
607 | if (this.keyProps.length > 1) {
|
608 | var last_line = this.keyProps[this.keyProps.length - 1].line;
|
609 | var top = Vex.Max(rest_line, last_line);
|
610 | var bot = Vex.Min(rest_line, last_line);
|
611 | rest_line = Vex.MidLine(top, bot);
|
612 | }
|
613 |
|
614 | return rest_line;
|
615 | },
|
616 |
|
617 |
|
618 |
|
619 | getModifierStartXY: function(position, index) {
|
620 | if (!this.preFormatted) throw new Vex.RERR("UnformattedNote",
|
621 | "Can't call GetModifierStartXY on an unformatted note");
|
622 |
|
623 | if (this.ys.length === 0) throw new Vex.RERR("NoYValues",
|
624 | "No Y-Values calculated for this note.");
|
625 |
|
626 | var x = 0;
|
627 | if (position == Vex.Flow.Modifier.Position.LEFT) {
|
628 |
|
629 | x = -1 * 2;
|
630 | } else if (position == Vex.Flow.Modifier.Position.RIGHT) {
|
631 |
|
632 | x = this.glyph.head_width + this.x_shift + 2;
|
633 | } else if (position == Vex.Flow.Modifier.Position.BELOW ||
|
634 | position == Vex.Flow.Modifier.Position.ABOVE) {
|
635 | x = this.glyph.head_width / 2;
|
636 | }
|
637 |
|
638 | return { x: this.getAbsoluteX() + x, y: this.ys[index] };
|
639 | },
|
640 |
|
641 |
|
642 |
|
643 |
|
644 |
|
645 | setKeyStyle: function(index, style) {
|
646 | this.note_heads[index].setStyle(style);
|
647 | return this;
|
648 | },
|
649 |
|
650 | setKeyLine: function(index, line) {
|
651 | this.keyProps[index].line = line;
|
652 | this.note_heads[index].setLine(line);
|
653 | return this;
|
654 | },
|
655 |
|
656 | getKeyLine: function(index) {
|
657 | return this.keyProps[index].line;
|
658 | },
|
659 |
|
660 |
|
661 |
|
662 | addToModifierContext: function(mContext) {
|
663 | this.setModifierContext(mContext);
|
664 | for (var i = 0; i < this.modifiers.length; ++i) {
|
665 | this.modifierContext.addModifier(this.modifiers[i]);
|
666 | }
|
667 | this.modifierContext.addModifier(this);
|
668 | this.setPreFormatted(false);
|
669 | return this;
|
670 | },
|
671 |
|
672 |
|
673 |
|
674 |
|
675 |
|
676 |
|
677 | addModifier: function(index, modifier) {
|
678 | modifier.setNote(this);
|
679 | modifier.setIndex(index);
|
680 | this.modifiers.push(modifier);
|
681 | this.setPreFormatted(false);
|
682 | return this;
|
683 | },
|
684 |
|
685 |
|
686 | addAccidental: function(index, accidental) {
|
687 | return this.addModifier(index, accidental);
|
688 | },
|
689 |
|
690 |
|
691 | addArticulation: function(index, articulation) {
|
692 | return this.addModifier(index, articulation);
|
693 | },
|
694 |
|
695 |
|
696 | addAnnotation: function(index, annotation) {
|
697 | return this.addModifier(index, annotation);
|
698 | },
|
699 |
|
700 |
|
701 | addDot: function(index) {
|
702 | var dot = new Vex.Flow.Dot();
|
703 | dot.setDotShiftY(this.glyph.dot_shiftY);
|
704 | this.dots++;
|
705 | return this.addModifier(index, dot);
|
706 | },
|
707 |
|
708 |
|
709 | addDotToAll: function() {
|
710 | for (var i = 0; i < this.keys.length; ++i)
|
711 | this.addDot(i);
|
712 | return this;
|
713 | },
|
714 |
|
715 |
|
716 | getAccidentals: function() {
|
717 | return this.modifierContext.getModifiers("accidentals");
|
718 | },
|
719 |
|
720 |
|
721 | getDots: function() {
|
722 | return this.modifierContext.getModifiers("dots");
|
723 | },
|
724 |
|
725 |
|
726 |
|
727 | getVoiceShiftWidth: function() {
|
728 |
|
729 | return this.glyph.head_width * (this.displaced ? 2 : 1);
|
730 | },
|
731 |
|
732 |
|
733 |
|
734 | calcExtraPx: function() {
|
735 | this.setExtraLeftPx((this.displaced && this.stem_direction == -1) ?
|
736 | this.glyph.head_width : 0);
|
737 | this.setExtraRightPx((this.displaced && this.stem_direction == 1) ?
|
738 | this.glyph.head_width : 0);
|
739 | },
|
740 |
|
741 |
|
742 | preFormat: function() {
|
743 | if (this.preFormatted) return;
|
744 | if (this.modifierContext) this.modifierContext.preFormat();
|
745 |
|
746 | var width = this.glyph.head_width + this.extraLeftPx + this.extraRightPx;
|
747 |
|
748 |
|
749 | if (this.glyph.flag && this.beam === null && this.stem_direction == 1) {
|
750 | width += this.glyph.head_width;
|
751 | }
|
752 |
|
753 | this.setWidth(width);
|
754 | this.setPreFormatted(true);
|
755 | },
|
756 |
|
757 |
|
758 | getNoteHeadBounds: function() {
|
759 |
|
760 | var y_top = null;
|
761 | var y_bottom = null;
|
762 |
|
763 | var highest_line = this.stave.getNumLines();
|
764 | var lowest_line = 1;
|
765 |
|
766 | this.note_heads.forEach(function(note_head) {
|
767 | var line = note_head.getLine();
|
768 | var y = note_head.getY();
|
769 |
|
770 | if (y_top === null || y < y_top) {
|
771 | y_top = y;
|
772 | }
|
773 |
|
774 | if (y_bottom === null || y > y_bottom) {
|
775 | y_bottom = y;
|
776 | }
|
777 |
|
778 | highest_line = line > highest_line ? line : highest_line;
|
779 | lowest_line = line < lowest_line ? line : lowest_line;
|
780 |
|
781 | }, this);
|
782 |
|
783 | return {
|
784 | y_top: y_top,
|
785 | y_bottom: y_bottom,
|
786 | highest_line: highest_line,
|
787 | lowest_line: lowest_line
|
788 | };
|
789 | },
|
790 |
|
791 |
|
792 | getNoteHeadBeginX: function(){
|
793 | return this.getAbsoluteX() + this.x_shift;
|
794 | },
|
795 |
|
796 |
|
797 | getNoteHeadEndX: function(){
|
798 | var x_begin = this.getNoteHeadBeginX();
|
799 | return x_begin + this.glyph.head_width - (Vex.Flow.STEM_WIDTH / 2);
|
800 | },
|
801 |
|
802 |
|
803 | drawLedgerLines: function(){
|
804 | if (this.isRest()) { return; }
|
805 | if (!this.context) throw new Vex.RERR("NoCanvasContext",
|
806 | "Can't draw without a canvas context.");
|
807 | var ctx = this.context;
|
808 |
|
809 | var bounds = this.getNoteHeadBounds();
|
810 | var highest_line = bounds.highest_line;
|
811 | var lowest_line = bounds.lowest_line;
|
812 | var head_x = this.note_heads[0].getAbsoluteX();
|
813 |
|
814 | var that = this;
|
815 | function stroke(y) {
|
816 | if (that.use_default_head_x === true) {
|
817 | head_x = that.getAbsoluteX() + that.x_shift;
|
818 | }
|
819 | var x = head_x - that.render_options.stroke_px;
|
820 | var length = ((head_x + that.glyph.head_width) - head_x) +
|
821 | (that.render_options.stroke_px * 2);
|
822 |
|
823 | ctx.fillRect(x, y, length, 1);
|
824 | }
|
825 |
|
826 | var line;
|
827 | for (line = 6; line <= highest_line; ++line) {
|
828 | stroke(this.stave.getYForNote(line));
|
829 | }
|
830 |
|
831 | for (line = 0; line >= lowest_line; --line) {
|
832 | stroke(this.stave.getYForNote(line));
|
833 | }
|
834 | },
|
835 |
|
836 |
|
837 | drawModifiers: function(){
|
838 | if (!this.context) throw new Vex.RERR("NoCanvasContext",
|
839 | "Can't draw without a canvas context.");
|
840 | var ctx = this.context;
|
841 | for (var i = 0; i < this.modifiers.length; i++) {
|
842 | var mod = this.modifiers[i];
|
843 | var note_head = this.note_heads[mod.getIndex()];
|
844 | var key_style = note_head.getStyle();
|
845 | if(key_style) {
|
846 | ctx.save();
|
847 | note_head.applyStyle(ctx);
|
848 | }
|
849 | mod.setContext(ctx);
|
850 | mod.draw();
|
851 | if(key_style) {
|
852 | ctx.restore();
|
853 | }
|
854 | }
|
855 | },
|
856 |
|
857 |
|
858 | drawFlag: function(){
|
859 | if (!this.context) throw new Vex.RERR("NoCanvasContext",
|
860 | "Can't draw without a canvas context.");
|
861 | var ctx = this.context;
|
862 | var glyph = this.getGlyph();
|
863 | var render_flag = this.beam === null;
|
864 | var bounds = this.getNoteHeadBounds();
|
865 |
|
866 | var x_begin = this.getNoteHeadBeginX();
|
867 | var x_end = this.getNoteHeadEndX();
|
868 |
|
869 | if (glyph.flag && render_flag) {
|
870 | var note_stem_height = this.stem.getHeight();
|
871 | var flag_x, flag_y, flag_code;
|
872 |
|
873 | if (this.getStemDirection() === Stem.DOWN) {
|
874 |
|
875 | flag_x = x_begin + 1;
|
876 | flag_y = bounds.y_top - note_stem_height + 2;
|
877 | flag_code = glyph.code_flag_downstem;
|
878 |
|
879 | } else {
|
880 |
|
881 | flag_x = x_end + 1;
|
882 | flag_y = bounds.y_bottom - note_stem_height - 2;
|
883 | flag_code = glyph.code_flag_upstem;
|
884 | }
|
885 |
|
886 |
|
887 | Vex.Flow.renderGlyph(ctx, flag_x, flag_y,
|
888 | this.render_options.glyph_font_scale, flag_code);
|
889 | }
|
890 | },
|
891 |
|
892 |
|
893 | drawNoteHeads: function(){
|
894 | this.note_heads.forEach(function(note_head) {
|
895 | note_head.setContext(this.context).draw();
|
896 | }, this);
|
897 | },
|
898 |
|
899 |
|
900 | drawStem: function(stem_struct){
|
901 | if (!this.context) throw new Vex.RERR("NoCanvasContext",
|
902 | "Can't draw without a canvas context.");
|
903 |
|
904 | if (stem_struct) {
|
905 | this.setStem(new Stem(stem_struct));
|
906 | }
|
907 |
|
908 | this.stem.setContext(this.context).draw();
|
909 | },
|
910 |
|
911 |
|
912 | draw: function() {
|
913 | if (!this.context) throw new Vex.RERR("NoCanvasContext",
|
914 | "Can't draw without a canvas context.");
|
915 | if (!this.stave) throw new Vex.RERR("NoStave",
|
916 | "Can't draw without a stave.");
|
917 | if (this.ys.length === 0) throw new Vex.RERR("NoYValues",
|
918 | "Can't draw note without Y values.");
|
919 |
|
920 | var x_begin = this.getNoteHeadBeginX();
|
921 | var x_end = this.getNoteHeadEndX();
|
922 |
|
923 | var render_stem = this.hasStem() && !this.beam;
|
924 |
|
925 |
|
926 | this.note_heads.forEach(function(note_head) {
|
927 | note_head.setX(x_begin);
|
928 | }, this);
|
929 |
|
930 |
|
931 | this.stem.setNoteHeadXBounds(x_begin, x_end);
|
932 |
|
933 | L("Rendering ", this.isChord() ? "chord :" : "note :", this.keys);
|
934 |
|
935 |
|
936 | this.drawLedgerLines();
|
937 | if (render_stem) this.drawStem();
|
938 | this.drawNoteHeads();
|
939 | this.drawFlag();
|
940 | this.drawModifiers();
|
941 | }
|
942 | });
|
943 |
|
944 | return StaveNote;
|
945 | }());
|