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 me->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 = Note_head::head_extent (heads[i], X_AXIS).length ();
412 heads[i]->translate_axis (l * get_direction (me), X_AXIS);
423 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
425 Stem::before_line_breaking (SCM smob)
427 Grob*me = unsmob_grob (smob);
428 stem_end_position (me); // ugh. Trigger direction calc.
429 position_noteheads (me);
431 if (invisible_b (me))
433 me->remove_grob_property ("molecule-callback");
437 return SCM_UNSPECIFIED;
442 When in a beam with tuplet brackets, brew_mol is called early,
443 caching a wrong value.
445 MAKE_SCHEME_CALLBACK (Stem, height, 2);
447 Stem::height (SCM smob, SCM ax)
449 Axis a = (Axis)gh_scm2int (ax);
450 Grob * me = unsmob_grob (smob);
451 assert (a == Y_AXIS);
453 SCM mol = me->get_uncached_molecule ();
456 iv = unsmob_molecule (mol)->extent (a);
457 return ly_interval2scm (iv);
464 /* TODO: rename flag-style into something more appropriate,
465 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
466 "single" and "double". Needs more discussion.
468 String style, fstyle, staffline_offs;
469 SCM fst = me->get_grob_property ("flag-style");
470 if (gh_string_p (fst))
472 fstyle = ly_scm2string (fst);
475 SCM st = me->get_grob_property ("style");
476 if (gh_symbol_p (st))
478 style = (ly_scm2string (scm_symbol_to_string (st)));
484 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
486 if (String::compare_i (style, "mensural") == 0)
487 /* Mensural notation: For notes on staff lines, use different
488 flags than for notes between staff lines. The idea is that
489 flags are always vertically aligned with the staff lines,
490 regardless if the note head is on a staff line or between two
491 staff lines. In other words, the inner end of a flag always
492 touches a staff line.
497 /* Urrgh! We have to detect wether this stem ends on a staff
498 line or between two staff lines. But we can not call
499 stem_end_position(me) or get_default_stem_end_position(me),
500 since this encounters the flag and hence results in an
501 infinite recursion. However, in pure mensural notation,
502 there are no multiple note heads attached to a single stem,
503 neither is there usually need for using the stem_shorten
504 property (except for 32th and 64th notes, but that is not a
505 problem since the stem length in this case is augmented by
506 an integral multiple of staff_space). Hence, it should be
507 sufficient to just take the first note head, assume it's
508 the only one, look if it's on a staff line, and select the
509 flag's shape accordingly. In the worst case, the shape
510 looks slightly misplaced, but that will usually be the
511 programmer's fault (e.g. when trying to attach multiple
512 note heads to a single stem in mensural notation). */
513 Grob *first = first_head(me);
514 int sz = Staff_symbol_referencer::line_count (me)-1;
515 int p = (int)rint (Staff_symbol_referencer::position_f (first));
516 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
520 staffline_offs = "2";
527 char c = (get_direction (me) == UP) ? 'u' : 'd';
529 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
531 = Font_interface::get_default_font (me)->find_by_name (index_str);
532 if (!fstyle.empty_b ())
533 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
537 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
539 Stem::dim_callback (SCM e, SCM ax)
541 Axis a = (Axis) gh_scm2int (ax);
542 assert (a == X_AXIS);
543 Grob *se = unsmob_grob (e);
545 if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
549 r = flag (se).extent (X_AXIS);
551 return ly_interval2scm (r);
556 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
559 Stem::brew_molecule (SCM smob)
561 Grob*me = unsmob_grob (smob);
563 Direction d = get_direction (me);
566 Real y1 = Staff_symbol_referencer::position_f (first_head (me));
567 Real y2 = stem_end_position (me);
569 Interval stem_y (y1 <? y2,y2 >? y1);
573 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
575 if (Grob *hed = support_head (me))
578 must not take ledgers into account.
580 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
581 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
583 y_attach = head_height.linear_combination (y_attach);
584 stem_y[Direction (-d)] += d * y_attach/dy;
587 if (!invisible_b (me))
589 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
591 * me->paper_l ()->get_var ("stafflinethickness");
593 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
594 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
595 mol.add_molecule (ss);
598 if (!beam_l (me) && abs (flag_i (me)) > 2)
600 Molecule fl = flag (me);
601 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
602 mol.add_molecule (fl);
605 return mol.smobbed_copy ();
609 move the stem to right of the notehead if it is up.
611 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
613 Stem::off_callback (SCM element_smob, SCM)
615 Grob *me = unsmob_grob (element_smob);
618 if (Grob * f = first_head (me))
620 Interval head_wid = Note_head::head_extent(f, X_AXIS);
623 Note_head::stem_attachment_coordinate(f, X_AXIS);
625 Direction d = get_direction (me);
627 Real real_attach = head_wid.linear_combination (d * attach);
632 If not centered: correct for stem thickness.
637 = gh_scm2double (me->get_grob_property ("thickness"))
638 * me->paper_l ()->get_var ("stafflinethickness");
641 r += - d * rule_thick * 0.5;
644 return gh_double2scm (r);
650 Stem::beam_l (Grob*me)
652 SCM b= me->get_grob_property ("beam");
653 return unsmob_grob (b);
657 // ugh still very long.
659 Stem::calc_stem_info (Grob*me)
661 Grob * beam = beam_l (me);
663 Direction beam_dir = Directional_element_interface::get (beam);
666 programming_error ("Beam dir not set.");
671 Real staff_space = Staff_symbol_referencer::staff_space (me);
672 Real half_space = staff_space / 2;
673 int multiplicity = Beam::get_multiplicity (beam);
676 SCM space_proc = beam->get_grob_property ("space-function");
677 SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
678 Real interbeam_f = gh_scm2double (space) * staff_space;
680 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
682 info.idealy_f_ = chord_start_f (me);
684 // for simplicity, we calculate as if dir == UP
685 info.idealy_f_ *= beam_dir;
686 SCM grace_prop = me->get_grob_property ("grace");
688 bool grace_b = to_boolean (grace_prop);
693 s = me->get_grob_property ("beamed-minimum-lengths");
695 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
696 a.push (gh_scm2double (ly_car (q)));
699 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
700 s = me->get_grob_property ("beamed-lengths");
703 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
704 a.push (gh_scm2double (ly_car (q)));
706 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
708 if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
709 /* normal beamed stem */
713 info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
715 info.miny_f_ = info.idealy_f_;
716 info.maxy_f_ = INT_MAX;
718 info.idealy_f_ += stem_length;
719 info.miny_f_ += minimum_length;
722 lowest beam of (UP) beam must never be lower than second staffline
724 Hmm, reference (Wanske?)
726 Although this (additional) rule is probably correct,
727 I expect that highest beam (UP) should also never be lower
728 than middle staffline, just as normal stems.
731 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
732 if (!grace_b && !no_extend_b)
734 /* highest beam of (UP) beam must never be lower than middle
736 lowest beam of (UP) beam must never be lower than second staffline
740 >? (- 2 * half_space - thick
741 + (multiplicity > 0) * thick
742 + interbeam_f * (multiplicity - 1));
748 info.idealy_f_ -= thick;
749 info.maxy_f_ = info.idealy_f_;
750 info.miny_f_ = -INT_MAX;
752 info.idealy_f_ -= stem_length;
753 info.maxy_f_ -= minimum_length;
756 info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
758 s = beam->get_grob_property ("shorten");
760 info.idealy_f_ -= gh_scm2double (s);
762 Grob *common = me->common_refpoint (beam, Y_AXIS);
763 Real interstaff_f = beam_dir *
764 (me->relative_coordinate (common, Y_AXIS)
765 - beam->relative_coordinate (common, Y_AXIS));
767 info.idealy_f_ += interstaff_f;
768 info.miny_f_ += interstaff_f;
769 info.maxy_f_ += interstaff_f ;
775 Stem::has_interface (Grob*m)
777 return m && m->has_interface (ly_symbol2scm ("stem-interface"));
781 Stem::set_interface (Grob*me)
783 me->set_interface (ly_symbol2scm ("stem-interface"));