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 Stem::invisible_b (Grob*me)
237 return ! (heads_i (me) && Rhythmic_head::balltype_i (support_head (me)) >= 1);
241 Stem::get_center_distance (Grob*me, Direction d)
243 int staff_center = 0;
244 int distance = (int) (d* (head_positions (me)[d] - staff_center));
245 return distance >? 0;
249 Stem::get_default_dir (Grob*me)
251 int du = get_center_distance (me,UP);
252 int dd = get_center_distance (me,DOWN);
255 return Direction (sign (dd -du));
257 return to_dir (me->get_grob_property ("neutral-direction"));
261 Stem::get_default_stem_end_position (Grob*me)
263 bool grace_b = to_boolean (me->get_grob_property ("grace"));
268 SCM scm_len = me->get_grob_property ("length");
269 if (gh_number_p (scm_len))
271 length_f = gh_scm2double (scm_len);
275 s = me->get_grob_property ("lengths");
276 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
277 a.push (gh_scm2double (ly_car (q)));
279 // stem uses half-spaces
280 length_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
285 s = me->get_grob_property ("stem-shorten");
286 for (SCM q = s; gh_pair_p (q); q = ly_cdr (q))
287 a.push (gh_scm2double (ly_car (q)));
290 // stem uses half-spaces
292 // fixme: use scm_list_n_ref () iso. array[]
293 Real shorten_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
295 /* On boundary: shorten only half */
296 if (abs (chord_start_f (me)) == 0.5)
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);
309 /* stems in unnatural (forced) direction should be shortened,
310 according to [Roush & Gourlay] */
311 if (chord_start_f (me)
312 && (get_direction (me) != get_default_dir (me)))
313 length_f -= shorten_f;
315 Interval hp = head_positions (me);
316 Real st = hp[dir] + dir * length_f;
322 Make a little room if we have a flag and there is a dot.
326 maybe we should consider moving the dot to the right?
331 Grob * closest_to_flag = extremal_heads (me)[dir];
332 Grob * dots = closest_to_flag
333 ? Rhythmic_head::dots_l (closest_to_flag ) : 0;
337 Real dp = Staff_symbol_referencer::position_f (dots);
338 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2; // should divide by staffspace
341 Very gory: add myself to the X-support of the parent,
342 which should be a dot-column.
344 if (dir * (st + flagy - dp) < 0.5)
345 Side_position_interface::add_support (dots->get_parent (X_AXIS), me);
348 previous approach was to lengthen the stem. This is not
349 good typesetting practice. */
354 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
355 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
364 Number of hooks on the flag, ie. the log of the duration.
367 Stem::flag_i (Grob*me)
369 SCM s = me->get_grob_property ("duration-log");
370 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
374 Stem::position_noteheads (Grob*me)
379 Link_array<Grob> heads =
380 Pointer_group_interface__extract_grobs (me, (Grob*)0, "heads");
382 heads.sort (compare_position);
383 Direction dir =get_direction (me);
389 Grob *hed = support_head (me);
390 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
391 for (int i=0; i < heads.size (); i++)
393 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
397 bool parity= true; // todo: make me settable.
398 int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
399 for (int i=1; i < heads.size (); i ++)
401 Real p = Staff_symbol_referencer::position_f (heads[i]);
402 int dy =abs (lastpos- (int)p);
408 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
410 heads[i]->translate_axis (l * get_direction (me), X_AXIS);
421 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
423 Stem::before_line_breaking (SCM smob)
425 Grob*me = unsmob_grob (smob);
426 stem_end_position (me); // ugh. Trigger direction calc.
427 position_noteheads (me);
429 if (invisible_b (me))
431 me->remove_grob_property ("molecule-callback");
435 return SCM_UNSPECIFIED;
440 When in a beam with tuplet brackets, brew_mol is called early,
441 caching a wrong value.
443 MAKE_SCHEME_CALLBACK (Stem, height, 2);
445 Stem::height (SCM smob, SCM ax)
447 Axis a = (Axis)gh_scm2int (ax);
448 Grob * me = unsmob_grob (smob);
449 assert (a == Y_AXIS);
451 SCM mol = me->get_uncached_molecule ();
454 iv = unsmob_molecule (mol)->extent (a);
455 return ly_interval2scm (iv);
462 /* TODO: rename flag-style into something more appropriate,
463 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
464 "single" and "double". Needs more discussion.
466 String style, fstyle, staffline_offs;
467 SCM fst = me->get_grob_property ("flag-style");
468 if (gh_string_p (fst))
470 fstyle = ly_scm2string (fst);
473 SCM st = me->get_grob_property ("style");
474 if (gh_symbol_p (st))
476 style = (ly_scm2string (scm_symbol_to_string (st)));
482 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
484 if (String::compare_i (style, "mensural") == 0)
485 /* Mensural notation: For notes on staff lines, use different
486 flags than for notes between staff lines. The idea is that
487 flags are always vertically aligned with the staff lines,
488 regardless if the note head is on a staff line or between two
489 staff lines. In other words, the inner end of a flag always
490 touches a staff line.
495 /* Urrgh! We have to detect wether this stem ends on a staff
496 line or between two staff lines. But we can not call
497 stem_end_position(me) or get_default_stem_end_position(me),
498 since this encounters the flag and hence results in an
499 infinite recursion. However, in pure mensural notation,
500 there are no multiple note heads attached to a single stem,
501 neither is there usually need for using the stem_shorten
502 property (except for 32th and 64th notes, but that is not a
503 problem since the stem length in this case is augmented by
504 an integral multiple of staff_space). Hence, it should be
505 sufficient to just take the first note head, assume it's
506 the only one, look if it's on a staff line, and select the
507 flag's shape accordingly. In the worst case, the shape
508 looks slightly misplaced, but that will usually be the
509 programmer's fault (e.g. when trying to attach multiple
510 note heads to a single stem in mensural notation). */
511 Grob *first = first_head(me);
512 int sz = Staff_symbol_referencer::line_count (me)-1;
513 int p = (int)rint (Staff_symbol_referencer::position_f (first));
514 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
518 staffline_offs = "2";
525 char c = (get_direction (me) == UP) ? 'u' : 'd';
527 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
529 = Font_interface::get_default_font (me)->find_by_name (index_str);
530 if (!fstyle.empty_b ())
531 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
535 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
537 Stem::dim_callback (SCM e, SCM ax)
539 Axis a = (Axis) gh_scm2int (ax);
540 assert (a == X_AXIS);
541 Grob *se = unsmob_grob (e);
543 if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
547 r = flag (se).extent (X_AXIS);
549 return ly_interval2scm (r);
554 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
557 Stem::brew_molecule (SCM smob)
559 Grob*me = unsmob_grob (smob);
561 Direction d = get_direction (me);
564 Real y1 = Staff_symbol_referencer::position_f (first_head (me));
565 Real y2 = stem_end_position (me);
567 Interval stem_y (y1 <? y2,y2 >? y1);
571 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
573 if (Grob *hed = support_head (me))
576 must not take ledgers into account.
578 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
579 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
581 y_attach = head_height.linear_combination (y_attach);
582 stem_y[Direction (-d)] += d * y_attach/dy;
585 if (!invisible_b (me))
587 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
589 * me->paper_l ()->get_var ("linethickness");
591 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
592 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
593 mol.add_molecule (ss);
596 if (!beam_l (me) && abs (flag_i (me)) > 2)
598 Molecule fl = flag (me);
599 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
600 mol.add_molecule (fl);
603 return mol.smobbed_copy ();
607 move the stem to right of the notehead if it is up.
609 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
611 Stem::off_callback (SCM element_smob, SCM)
613 Grob *me = unsmob_grob (element_smob);
616 if (Grob * f = first_head (me))
618 Interval head_wid = Note_head::head_extent(f, X_AXIS);
621 Note_head::stem_attachment_coordinate(f, X_AXIS);
623 Direction d = get_direction (me);
625 Real real_attach = head_wid.linear_combination (d * attach);
630 If not centered: correct for stem thickness.
635 = gh_scm2double (me->get_grob_property ("thickness"))
636 * me->paper_l ()->get_var ("linethickness");
639 r += - d * rule_thick * 0.5;
642 return gh_double2scm (r);
648 Stem::beam_l (Grob*me)
650 SCM b= me->get_grob_property ("beam");
651 return unsmob_grob (b);
655 // ugh still very long.
657 Stem::calc_stem_info (Grob*me)
659 SCM scm_info = me->get_grob_property ("stem-info");
661 if (gh_pair_p (scm_info ))
665 si.ideal_y = gh_scm2double (gh_car (scm_info));
666 si.max_y = gh_scm2double (gh_cadr (scm_info));
667 si.min_y = gh_scm2double (gh_caddr (scm_info));
672 Grob * beam = beam_l (me);
674 Direction beam_dir = Directional_element_interface::get (beam);
677 programming_error ("Beam dir not set.");
682 Real staff_space = Staff_symbol_referencer::staff_space (me);
683 Real half_space = staff_space / 2;
685 int multiplicity = Beam::get_multiplicity (beam);
686 Real interbeam_f = Beam::get_interbeam (beam);
688 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
690 info.ideal_y = chord_start_f (me);
692 // for simplicity, we calculate as if dir == UP
693 info.ideal_y *= beam_dir;
694 SCM grace_prop = me->get_grob_property ("grace");
696 bool grace_b = to_boolean (grace_prop);
701 s = me->get_grob_property ("beamed-minimum-lengths");
703 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
704 a.push (gh_scm2double (ly_car (q)));
707 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
708 s = me->get_grob_property ("beamed-lengths");
711 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
712 a.push (gh_scm2double (ly_car (q)));
714 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
716 if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
717 /* normal beamed stem */
721 info.ideal_y += thick + (multiplicity - 1) * interbeam_f;
723 info.min_y = info.ideal_y;
724 info.max_y = 1000; // INT_MAX;
726 info.ideal_y += stem_length;
727 info.min_y += minimum_length;
730 lowest beam of (UP) beam must never be lower than second staffline
732 Hmm, reference (Wanske?)
734 Although this (additional) rule is probably correct,
735 I expect that highest beam (UP) should also never be lower
736 than middle staffline, just as normal stems.
739 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
740 if (!grace_b && !no_extend_b)
742 /* highest beam of (UP) beam must never be lower than middle
744 lowest beam of (UP) beam must never be lower than second staffline
748 >? (- 2 * half_space - thick
749 + (multiplicity > 0) * thick
750 + interbeam_f * (multiplicity - 1));
756 info.ideal_y -= thick;
757 info.max_y = info.ideal_y;
758 info.min_y = - 1000 ; // INT_MAX;
760 info.ideal_y -= stem_length;
761 info.max_y -= minimum_length;
764 info.ideal_y = (info.max_y <? info.ideal_y) >? info.min_y;
766 s = beam->get_grob_property ("shorten");
768 info.ideal_y -= gh_scm2double (s);
770 Grob *common = me->common_refpoint (beam, Y_AXIS);
771 Real interstaff_f = beam_dir *
772 (me->relative_coordinate (common, Y_AXIS)
773 - beam->relative_coordinate (common, Y_AXIS));
775 info.ideal_y += interstaff_f;
776 info.min_y += interstaff_f;
777 info.max_y += interstaff_f ;
779 me->set_grob_property ("stem-info",
780 scm_list_n (gh_double2scm (info.ideal_y),
781 gh_double2scm (info.max_y),
782 gh_double2scm (info.min_y),
789 Stem::has_interface (Grob*m)
791 return m && m->has_interface (ly_symbol2scm ("stem-interface"));
794 ADD_INTERFACE (Stem,"stem-interface",
796 "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");