2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
12 #include <math.h> // m_pi
15 #include "directional-element-interface.hh"
16 #include "note-head.hh"
19 #include "paper-def.hh"
20 #include "rhythmic-head.hh"
21 #include "font-interface.hh"
22 #include "molecule.hh"
23 #include "paper-column.hh"
27 #include "group-interface.hh"
28 #include "staff-symbol-referencer.hh"
30 #include "side-position-interface.hh"
33 Stem::set_beaming (Grob*me ,int i, Direction d)
35 SCM pair = me->get_grob_property ("beaming");
37 if (!gh_pair_p (pair))
39 pair = gh_cons (gh_int2scm (0),gh_int2scm (0));
40 me-> set_grob_property ("beaming", pair);
42 index_set_cell (pair, d, gh_int2scm (i));
46 Stem::beam_count (Grob*me,Direction d)
48 SCM p=me->get_grob_property ("beaming");
50 return gh_scm2int (index_cell (p,d));
56 Stem::head_positions (Grob*me)
64 Drul_array<Grob*> e (extremal_heads (me));
66 return Interval (Staff_symbol_referencer::position_f (e[DOWN]),
67 Staff_symbol_referencer::position_f (e[UP]));
72 Stem::chord_start_f (Grob*me)
74 return head_positions (me)[get_direction (me)]
75 * Staff_symbol_referencer::staff_space (me)/2.0;
79 Stem::stem_end_position (Grob*me)
81 SCM p =me->get_grob_property ("stem-end-position");
85 pos = get_default_stem_end_position (me);
86 me->set_grob_property ("stem-end-position", gh_double2scm (pos));
89 pos = gh_scm2double (p);
95 Stem::get_direction (Grob*me)
97 Direction d = Directional_element_interface::get (me);
101 d = get_default_dir (me);
103 Directional_element_interface::set (me, d);
110 Stem::set_stemend (Grob*me, Real se)
113 Direction d= get_direction (me);
115 if (d && d * head_positions (me)[get_direction (me)] >= se*d)
116 warning (_ ("Weird stem size; check for narrow beams"));
118 me->set_grob_property ("stem-end-position", gh_double2scm (se));
122 Stem::type_i (Grob*me)
124 return first_head (me) ? Rhythmic_head::balltype_i (first_head (me)) : 2;
128 Note head that determines hshift for upstems
131 Stem::support_head (Grob*me)
133 SCM h = me->get_grob_property ("support-head");
134 Grob * nh = unsmob_grob (h);
137 else if (heads_i (me) == 1)
143 return unsmob_grob (ly_car (me->get_grob_property ("heads")));
146 return first_head (me);
151 Stem::heads_i (Grob*me)
153 return Pointer_group_interface::count (me, "heads");
157 The note head which forms one end of the stem.
160 Stem::first_head (Grob*me)
162 return extremal_heads (me)[-get_direction (me)];
166 START is part where stem reaches `last' head.
169 Stem::extremal_heads (Grob*me)
171 const int inf = 1000000;
172 Drul_array<int> extpos;
176 Drul_array<Grob *> exthead;
177 exthead[LEFT] = exthead[RIGHT] =0;
179 for (SCM s = me->get_grob_property ("heads"); gh_pair_p (s); s = ly_cdr (s))
181 Grob * n = unsmob_grob (ly_car (s));
184 int p = int (Staff_symbol_referencer::position_f (n));
188 if (d* p > d* extpos[d])
193 } while (flip (&d) != DOWN);
200 icmp (int const &a, int const &b)
206 Stem::note_head_positions (Grob *me)
209 for (SCM s = me->get_grob_property ("heads"); gh_pair_p (s); s = ly_cdr (s))
211 Grob * n = unsmob_grob (ly_car (s));
212 int p = int (Staff_symbol_referencer::position_f (n));
223 Stem::add_head (Grob*me, Grob *n)
225 n->set_grob_property ("stem", me->self_scm ());
226 n->add_dependency (me);
228 if (Note_head::has_interface (n))
230 Pointer_group_interface::add_grob (me, ly_symbol2scm ("heads"), n);
234 n->set_grob_property ("rest", n->self_scm ());
239 Stem::invisible_b (Grob*me)
241 return ! (heads_i (me) && Rhythmic_head::balltype_i (support_head (me)) >= 1);
245 Stem::get_center_distance (Grob*me, Direction d)
247 int staff_center = 0;
248 int distance = (int) (d* (head_positions (me)[d] - staff_center));
249 return distance >? 0;
253 Stem::get_default_dir (Grob*me)
255 int du = get_center_distance (me,UP);
256 int dd = get_center_distance (me,DOWN);
259 return Direction (sign (dd -du));
261 return to_dir (me->get_grob_property ("neutral-direction"));
265 Stem::get_default_stem_end_position (Grob*me)
267 bool grace_b = to_boolean (me->get_grob_property ("grace"));
272 SCM scm_len = me->get_grob_property ("length");
273 if (gh_number_p (scm_len))
275 length_f = gh_scm2double (scm_len);
279 s = me->get_grob_property ("lengths");
280 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
281 a.push (gh_scm2double (ly_car (q)));
283 // stem uses half-spaces
284 length_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
289 s = me->get_grob_property ("stem-shorten");
290 for (SCM q = s; gh_pair_p (q); q = ly_cdr (q))
291 a.push (gh_scm2double (ly_car (q)));
294 // stem uses half-spaces
296 // fixme: use scm_list_n_ref () iso. array[]
297 Real shorten_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
300 'set-default-stemlen' sets direction too
302 Direction dir = get_direction (me);
305 dir = get_default_dir (me);
306 Directional_element_interface::set (me, dir);
310 stems in unnatural (forced) direction should be shortened,
311 according to [Roush & Gourlay]
313 if (( (int)chord_start_f (me))
314 && (get_direction (me) != get_default_dir (me)))
315 length_f -= shorten_f;
317 Interval hp = head_positions (me);
318 Real st = hp[dir] + dir * length_f;
324 Make a little room if we have a flag and there is a dot.
328 maybe we should consider moving the dot to the right?
333 Grob * closest_to_flag = extremal_heads (me)[dir];
334 Grob * dots = closest_to_flag
335 ? Rhythmic_head::dots_l (closest_to_flag ) : 0;
339 Real dp = Staff_symbol_referencer::position_f (dots);
340 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2; // should divide by staffspace
343 Very gory: add myself to the X-support of the parent,
344 which should be a dot-column.
346 if (dir * (st + flagy - dp) < 0.5)
347 Side_position_interface::add_support (dots->get_parent (X_AXIS), me);
350 previous approach was to lengthen the stem. This is not
351 good typesetting practice. */
356 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
357 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
366 Number of hooks on the flag, ie. the log of the duration.
369 Stem::flag_i (Grob*me)
371 SCM s = me->get_grob_property ("duration-log");
372 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
376 Stem::position_noteheads (Grob*me)
381 Link_array<Grob> heads =
382 Pointer_group_interface__extract_grobs (me, (Grob*)0, "heads");
384 heads.sort (compare_position);
385 Direction dir =get_direction (me);
391 Grob *hed = support_head (me);
392 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
393 for (int i=0; i < heads.size (); i++)
395 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
399 bool parity= true; // todo: make me settable.
400 int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
401 for (int i=1; i < heads.size (); i ++)
403 Real p = Staff_symbol_referencer::position_f (heads[i]);
404 int dy =abs (lastpos- (int)p);
410 Real l = heads[i]->extent (heads[i], X_AXIS).length ();
411 heads[i]->translate_axis (l * get_direction (me), X_AXIS);
422 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
424 Stem::before_line_breaking (SCM smob)
426 Grob*me = unsmob_grob (smob);
427 stem_end_position (me); // ugh. Trigger direction calc.
428 position_noteheads (me);
430 if (invisible_b (me))
432 me->remove_grob_property ("molecule-callback");
436 return SCM_UNSPECIFIED;
441 When in a beam with tuplet brackets, brew_mol is called early,
442 caching a wrong value.
444 MAKE_SCHEME_CALLBACK (Stem, height, 2);
446 Stem::height (SCM smob, SCM ax)
448 Axis a = (Axis)gh_scm2int (ax);
449 Grob * me = unsmob_grob (smob);
450 assert (a == Y_AXIS);
452 SCM mol = me->get_uncached_molecule ();
455 iv = unsmob_molecule (mol)->extent (a);
456 return ly_interval2scm (iv);
463 /* TODO: rename flag-style into something more appropriate,
464 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
465 "single" and "double". Needs more discussion.
467 String style, fstyle, staffline_offs;
468 SCM fst = me->get_grob_property ("flag-style");
469 if (gh_string_p (fst))
471 fstyle = ly_scm2string (fst);
474 SCM st = me->get_grob_property ("style");
475 if (gh_symbol_p (st))
477 style = (ly_scm2string (scm_symbol_to_string (st)));
483 if (String::compare_i (style, "mensural") == 0)
484 /* Mensural notation: For notes on staff lines, use different
485 flags than for notes between staff lines. The idea is that
486 flags are always vertically aligned with the staff lines,
487 regardless if the note head is on a staff line or between two
488 staff lines. In other words, the inner end of a flag always
489 touches a staff line.
492 /* Urrgh! We have to detect wether this stem ends on a staff
493 line or between two staff lines. But we can not call
494 stem_end_position(me) or get_default_stem_end_position(me),
495 since this encounters the flag and hence results in an
496 infinite recursion. However, in pure mensural notation,
497 there are no multiple note heads attached to a single stem,
498 neither is there usually need for using the stem_shorten
499 property (except for 32th and 64th notes, but that is not a
500 problem since the stem length in this case is augmented by
501 an integral multiple of staff_space). Hence, it should be
502 sufficient to just take the first note head, assume it's
503 the only one, look if it's on a staff line, and select the
504 flag's shape accordingly. In the worst case, the shape
505 looks slightly misplaced, but that will usually be the
506 programmer's fault (e.g. when trying to attach multiple
507 note heads to a single stem in mensural notation).
510 Grob *first = first_head(me);
511 int sz = Staff_symbol_referencer::line_count (me)-1;
512 int p = (int)rint (Staff_symbol_referencer::position_f (first));
513 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
519 char c = (get_direction (me) == UP) ? 'u' : 'd';
521 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
523 = Font_interface::get_default_font (me)->find_by_name (index_str);
524 if (!fstyle.empty_b ())
525 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
529 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
531 Stem::dim_callback (SCM e, SCM ax)
533 Axis a = (Axis) gh_scm2int (ax);
534 assert (a == X_AXIS);
535 Grob *se = unsmob_grob (e);
537 if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
541 r = flag (se).extent (X_AXIS);
543 return ly_interval2scm (r);
548 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
551 Stem::brew_molecule (SCM smob)
553 Grob*me = unsmob_grob (smob);
555 Direction d = get_direction (me);
558 Real y1 = Staff_symbol_referencer::position_f (first_head (me));
559 Real y2 = stem_end_position (me);
561 Interval stem_y (y1 <? y2,y2 >? y1);
565 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
567 if (Grob *hed = support_head (me))
570 must not take ledgers into account.
572 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
573 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
575 y_attach = head_height.linear_combination (y_attach);
576 stem_y[Direction (-d)] += d * y_attach/dy;
579 if (!invisible_b (me))
581 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
583 * me->paper_l ()->get_var ("stafflinethickness");
585 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
586 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
587 mol.add_molecule (ss);
590 if (!beam_l (me) && abs (flag_i (me)) > 2)
592 Molecule fl = flag (me);
593 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
594 mol.add_molecule (fl);
597 return mol.smobbed_copy ();
601 move the stem to right of the notehead if it is up.
603 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
605 Stem::off_callback (SCM element_smob, SCM)
607 Grob *me = unsmob_grob (element_smob);
610 if (Grob * f = first_head (me))
612 Interval head_wid = Note_head::head_extent(f, X_AXIS);
615 Note_head::stem_attachment_coordinate(f, X_AXIS);
617 Direction d = get_direction (me);
619 Real real_attach = head_wid.linear_combination (d * attach);
624 If not centered: correct for stem thickness.
629 = gh_scm2double (me->get_grob_property ("thickness"))
630 * me->paper_l ()->get_var ("stafflinethickness");
633 r += - d * rule_thick * 0.5;
636 return gh_double2scm (r);
642 Stem::beam_l (Grob*me)
644 SCM b= me->get_grob_property ("beam");
645 return unsmob_grob (b);
649 // ugh still very long.
651 Stem::calc_stem_info (Grob*me)
653 Grob * beam = beam_l (me);
655 Direction beam_dir = Directional_element_interface::get (beam);
658 programming_error ("Beam dir not set.");
663 Real staff_space = Staff_symbol_referencer::staff_space (me);
664 Real half_space = staff_space / 2;
665 int multiplicity = Beam::get_multiplicity (beam);
668 SCM space_proc = beam->get_grob_property ("space-function");
669 SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
670 Real interbeam_f = gh_scm2double (space) * staff_space;
672 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
674 info.idealy_f_ = chord_start_f (me);
676 // for simplicity, we calculate as if dir == UP
677 info.idealy_f_ *= beam_dir;
678 SCM grace_prop = me->get_grob_property ("grace");
680 bool grace_b = to_boolean (grace_prop);
685 s = me->get_grob_property ("beamed-minimum-lengths");
687 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
688 a.push (gh_scm2double (ly_car (q)));
691 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
692 s = me->get_grob_property ("beamed-lengths");
695 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
696 a.push (gh_scm2double (ly_car (q)));
698 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
700 if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
701 /* normal beamed stem */
705 info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
707 info.miny_f_ = info.idealy_f_;
708 info.maxy_f_ = INT_MAX;
710 info.idealy_f_ += stem_length;
711 info.miny_f_ += minimum_length;
714 lowest beam of (UP) beam must never be lower than second staffline
716 Hmm, reference (Wanske?)
718 Although this (additional) rule is probably correct,
719 I expect that highest beam (UP) should also never be lower
720 than middle staffline, just as normal stems.
723 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
724 if (!grace_b && !no_extend_b)
726 /* highest beam of (UP) beam must never be lower than middle
728 lowest beam of (UP) beam must never be lower than second staffline
732 >? (- 2 * half_space - thick
733 + (multiplicity > 0) * thick
734 + interbeam_f * (multiplicity - 1));
740 info.idealy_f_ -= thick;
741 info.maxy_f_ = info.idealy_f_;
742 info.miny_f_ = -INT_MAX;
744 info.idealy_f_ -= stem_length;
745 info.maxy_f_ -= minimum_length;
748 info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
750 s = beam->get_grob_property ("shorten");
752 info.idealy_f_ -= gh_scm2double (s);
754 Grob *common = me->common_refpoint (beam, Y_AXIS);
755 Real interstaff_f = beam_dir *
756 (me->relative_coordinate (common, Y_AXIS)
757 - beam->relative_coordinate (common, Y_AXIS));
759 info.idealy_f_ += interstaff_f;
760 info.miny_f_ += interstaff_f;
761 info.maxy_f_ += interstaff_f ;
767 Stem::has_interface (Grob*m)
769 return m && m->has_interface (ly_symbol2scm ("stem-interface"));
773 Stem::set_interface (Grob*me)
775 me->set_interface (ly_symbol2scm ("stem-interface"));