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 (-1),gh_int2scm (-1));
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);
235 Apparently, this is never used.
238 me->set_grob_property ("rest", n->self_scm ());
244 Stem::invisible_b (Grob*me)
246 return ! (heads_i (me) && Rhythmic_head::balltype_i (support_head (me)) >= 1);
250 Stem::get_center_distance (Grob*me, Direction d)
252 int staff_center = 0;
253 int distance = (int) (d* (head_positions (me)[d] - staff_center));
254 return distance >? 0;
258 Stem::get_default_dir (Grob*me)
260 int du = get_center_distance (me,UP);
261 int dd = get_center_distance (me,DOWN);
264 return Direction (sign (dd -du));
266 return to_dir (me->get_grob_property ("neutral-direction"));
270 Stem::get_default_stem_end_position (Grob*me)
272 bool grace_b = to_boolean (me->get_grob_property ("grace"));
277 SCM scm_len = me->get_grob_property ("length");
278 if (gh_number_p (scm_len))
280 length_f = gh_scm2double (scm_len);
284 s = me->get_grob_property ("lengths");
285 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
286 a.push (gh_scm2double (ly_car (q)));
288 // stem uses half-spaces
289 length_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
294 s = me->get_grob_property ("stem-shorten");
295 for (SCM q = s; gh_pair_p (q); q = ly_cdr (q))
296 a.push (gh_scm2double (ly_car (q)));
299 // stem uses half-spaces
301 // fixme: use scm_list_n_ref () iso. array[]
302 Real shorten_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
304 /* On boundary: shorten only half */
305 if (abs (chord_start_f (me)) == 0.5)
309 'set-default-stemlen' sets direction too
311 Direction dir = get_direction (me);
314 dir = get_default_dir (me);
315 Directional_element_interface::set (me, dir);
318 /* stems in unnatural (forced) direction should be shortened,
319 according to [Roush & Gourlay] */
320 if (chord_start_f (me)
321 && (get_direction (me) != get_default_dir (me)))
322 length_f -= shorten_f;
324 Interval hp = head_positions (me);
325 Real st = hp[dir] + dir * length_f;
331 Make a little room if we have a flag and there is a dot.
335 maybe we should consider moving the dot to the right?
340 Grob * closest_to_flag = extremal_heads (me)[dir];
341 Grob * dots = closest_to_flag
342 ? Rhythmic_head::dots_l (closest_to_flag ) : 0;
346 Real dp = Staff_symbol_referencer::position_f (dots);
347 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2; // should divide by staffspace
350 Very gory: add myself to the X-support of the parent,
351 which should be a dot-column.
353 if (dir * (st + flagy - dp) < 0.5)
354 Side_position_interface::add_support (dots->get_parent (X_AXIS), me);
357 previous approach was to lengthen the stem. This is not
358 good typesetting practice. */
363 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
364 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
373 Number of hooks on the flag, ie. the log of the duration.
376 Stem::flag_i (Grob*me)
378 SCM s = me->get_grob_property ("duration-log");
379 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
383 Stem::position_noteheads (Grob*me)
388 Link_array<Grob> heads =
389 Pointer_group_interface__extract_grobs (me, (Grob*)0, "heads");
391 heads.sort (compare_position);
392 Direction dir =get_direction (me);
398 Grob *hed = support_head (me);
399 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
400 for (int i=0; i < heads.size (); i++)
402 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
406 bool parity= true; // todo: make me settable.
407 int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
408 for (int i=1; i < heads.size (); i ++)
410 Real p = Staff_symbol_referencer::position_f (heads[i]);
411 int dy =abs (lastpos- (int)p);
417 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
419 heads[i]->translate_axis (l * get_direction (me), X_AXIS);
430 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
432 Stem::before_line_breaking (SCM smob)
434 Grob*me = unsmob_grob (smob);
435 stem_end_position (me); // ugh. Trigger direction calc.
436 position_noteheads (me);
438 if (invisible_b (me))
440 me->remove_grob_property ("molecule-callback");
444 return SCM_UNSPECIFIED;
449 When in a beam with tuplet brackets, brew_mol is called early,
450 caching a wrong value.
452 MAKE_SCHEME_CALLBACK (Stem, height, 2);
454 Stem::height (SCM smob, SCM ax)
456 Axis a = (Axis)gh_scm2int (ax);
457 Grob * me = unsmob_grob (smob);
458 assert (a == Y_AXIS);
460 SCM mol = me->get_uncached_molecule ();
463 iv = unsmob_molecule (mol)->extent (a);
464 return ly_interval2scm (iv);
471 /* TODO: rename flag-style into something more appropriate,
472 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
473 "single" and "double". Needs more discussion.
475 String style, fstyle, staffline_offs;
476 SCM fst = me->get_grob_property ("flag-style");
477 if (gh_string_p (fst))
479 fstyle = ly_scm2string (fst);
482 SCM st = me->get_grob_property ("style");
483 if (gh_symbol_p (st))
485 style = (ly_scm2string (scm_symbol_to_string (st)));
491 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
493 if (String::compare_i (style, "mensural") == 0)
494 /* Mensural notation: For notes on staff lines, use different
495 flags than for notes between staff lines. The idea is that
496 flags are always vertically aligned with the staff lines,
497 regardless if the note head is on a staff line or between two
498 staff lines. In other words, the inner end of a flag always
499 touches a staff line.
504 /* Urrgh! We have to detect wether this stem ends on a staff
505 line or between two staff lines. But we can not call
506 stem_end_position(me) or get_default_stem_end_position(me),
507 since this encounters the flag and hence results in an
508 infinite recursion. However, in pure mensural notation,
509 there are no multiple note heads attached to a single stem,
510 neither is there usually need for using the stem_shorten
511 property (except for 32th and 64th notes, but that is not a
512 problem since the stem length in this case is augmented by
513 an integral multiple of staff_space). Hence, it should be
514 sufficient to just take the first note head, assume it's
515 the only one, look if it's on a staff line, and select the
516 flag's shape accordingly. In the worst case, the shape
517 looks slightly misplaced, but that will usually be the
518 programmer's fault (e.g. when trying to attach multiple
519 note heads to a single stem in mensural notation). */
520 Grob *first = first_head(me);
521 int sz = Staff_symbol_referencer::line_count (me)-1;
522 int p = (int)rint (Staff_symbol_referencer::position_f (first));
523 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
527 staffline_offs = "2";
534 char c = (get_direction (me) == UP) ? 'u' : 'd';
536 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
538 = Font_interface::get_default_font (me)->find_by_name (index_str);
539 if (!fstyle.empty_b ())
540 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
544 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
546 Stem::dim_callback (SCM e, SCM ax)
548 Axis a = (Axis) gh_scm2int (ax);
549 assert (a == X_AXIS);
550 Grob *se = unsmob_grob (e);
552 if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
556 r = flag (se).extent (X_AXIS);
558 return ly_interval2scm (r);
563 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
566 Stem::brew_molecule (SCM smob)
568 Grob*me = unsmob_grob (smob);
570 Direction d = get_direction (me);
573 Real y1 = Staff_symbol_referencer::position_f (first_head (me));
574 Real y2 = stem_end_position (me);
576 Interval stem_y (y1 <? y2,y2 >? y1);
580 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
582 if (Grob *hed = support_head (me))
585 must not take ledgers into account.
587 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
588 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
590 y_attach = head_height.linear_combination (y_attach);
591 stem_y[Direction (-d)] += d * y_attach/dy;
594 if (!invisible_b (me))
596 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
598 * me->paper_l ()->get_var ("stafflinethickness");
600 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
601 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
602 mol.add_molecule (ss);
605 if (!beam_l (me) && abs (flag_i (me)) > 2)
607 Molecule fl = flag (me);
608 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
609 mol.add_molecule (fl);
612 return mol.smobbed_copy ();
616 move the stem to right of the notehead if it is up.
618 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
620 Stem::off_callback (SCM element_smob, SCM)
622 Grob *me = unsmob_grob (element_smob);
625 if (Grob * f = first_head (me))
627 Interval head_wid = Note_head::head_extent(f, X_AXIS);
630 Note_head::stem_attachment_coordinate(f, X_AXIS);
632 Direction d = get_direction (me);
634 Real real_attach = head_wid.linear_combination (d * attach);
639 If not centered: correct for stem thickness.
644 = gh_scm2double (me->get_grob_property ("thickness"))
645 * me->paper_l ()->get_var ("stafflinethickness");
648 r += - d * rule_thick * 0.5;
651 return gh_double2scm (r);
657 Stem::beam_l (Grob*me)
659 SCM b= me->get_grob_property ("beam");
660 return unsmob_grob (b);
664 // ugh still very long.
666 Stem::calc_stem_info (Grob*me)
668 SCM scm_info = me->get_grob_property ("stem-info");
670 if (gh_pair_p (scm_info ))
674 si.ideal_y = gh_scm2double (gh_car (scm_info));
675 si.max_y = gh_scm2double (gh_cadr (scm_info));
676 si.min_y = gh_scm2double (gh_caddr (scm_info));
681 Grob * beam = beam_l (me);
683 Direction beam_dir = Directional_element_interface::get (beam);
686 programming_error ("Beam dir not set.");
691 Real staff_space = Staff_symbol_referencer::staff_space (me);
692 Real half_space = staff_space / 2;
694 int multiplicity = Beam::get_multiplicity (beam);
695 Real interbeam_f = Beam::get_interbeam (beam);
697 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
699 info.ideal_y = chord_start_f (me);
701 // for simplicity, we calculate as if dir == UP
702 info.ideal_y *= beam_dir;
703 SCM grace_prop = me->get_grob_property ("grace");
705 bool grace_b = to_boolean (grace_prop);
710 s = me->get_grob_property ("beamed-minimum-lengths");
712 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
713 a.push (gh_scm2double (ly_car (q)));
716 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
717 s = me->get_grob_property ("beamed-lengths");
720 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
721 a.push (gh_scm2double (ly_car (q)));
723 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
725 if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
726 /* normal beamed stem */
730 info.ideal_y += thick + (multiplicity - 1) * interbeam_f;
732 info.min_y = info.ideal_y;
733 info.max_y = 1000; // INT_MAX;
735 info.ideal_y += stem_length;
736 info.min_y += minimum_length;
739 lowest beam of (UP) beam must never be lower than second staffline
741 Hmm, reference (Wanske?)
743 Although this (additional) rule is probably correct,
744 I expect that highest beam (UP) should also never be lower
745 than middle staffline, just as normal stems.
748 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
749 if (!grace_b && !no_extend_b)
751 /* highest beam of (UP) beam must never be lower than middle
753 lowest beam of (UP) beam must never be lower than second staffline
757 >? (- 2 * half_space - thick
758 + (multiplicity > 0) * thick
759 + interbeam_f * (multiplicity - 1));
765 info.ideal_y -= thick;
766 info.max_y = info.ideal_y;
767 info.min_y = - 1000 ; // INT_MAX;
769 info.ideal_y -= stem_length;
770 info.max_y -= minimum_length;
773 info.ideal_y = (info.max_y <? info.ideal_y) >? info.min_y;
775 s = beam->get_grob_property ("shorten");
777 info.ideal_y -= gh_scm2double (s);
779 Grob *common = me->common_refpoint (beam, Y_AXIS);
780 Real interstaff_f = beam_dir *
781 (me->relative_coordinate (common, Y_AXIS)
782 - beam->relative_coordinate (common, Y_AXIS));
784 info.ideal_y += interstaff_f;
785 info.min_y += interstaff_f;
786 info.max_y += interstaff_f ;
788 me->set_grob_property ("stem-info",
789 scm_list_n (gh_double2scm (info.ideal_y),
790 gh_double2scm (info.max_y),
791 gh_double2scm (info.min_y),
798 Stem::has_interface (Grob*m)
800 return m && m->has_interface (ly_symbol2scm ("stem-interface"));
803 ADD_INTERFACE (Stem,"stem-interface",
805 "thickness stem-info beamed-lengths beamed-minimum-lengths lengths beam stem-shorten duration-log beaming neutral-direction stem-end-position support-head heads direction length style no-stem-extend flag-style dir-forced");