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);
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;
299 /* On boundary: shorten only half */
300 if (abs (chord_start_f (me)) == 0.5)
304 'set-default-stemlen' sets direction too
306 Direction dir = get_direction (me);
309 dir = get_default_dir (me);
310 Directional_element_interface::set (me, dir);
313 /* stems in unnatural (forced) direction should be shortened,
314 according to [Roush & Gourlay] */
315 if (chord_start_f (me)
316 && (get_direction (me) != get_default_dir (me)))
317 length_f -= shorten_f;
319 Interval hp = head_positions (me);
320 Real st = hp[dir] + dir * length_f;
326 Make a little room if we have a flag and there is a dot.
330 maybe we should consider moving the dot to the right?
335 Grob * closest_to_flag = extremal_heads (me)[dir];
336 Grob * dots = closest_to_flag
337 ? Rhythmic_head::dots_l (closest_to_flag ) : 0;
341 Real dp = Staff_symbol_referencer::position_f (dots);
342 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2; // should divide by staffspace
345 Very gory: add myself to the X-support of the parent,
346 which should be a dot-column.
348 if (dir * (st + flagy - dp) < 0.5)
349 Side_position_interface::add_support (dots->get_parent (X_AXIS), me);
352 previous approach was to lengthen the stem. This is not
353 good typesetting practice. */
358 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
359 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
368 Number of hooks on the flag, ie. the log of the duration.
371 Stem::flag_i (Grob*me)
373 SCM s = me->get_grob_property ("duration-log");
374 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
378 Stem::position_noteheads (Grob*me)
383 Link_array<Grob> heads =
384 Pointer_group_interface__extract_grobs (me, (Grob*)0, "heads");
386 heads.sort (compare_position);
387 Direction dir =get_direction (me);
393 Grob *hed = support_head (me);
394 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
395 for (int i=0; i < heads.size (); i++)
397 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
401 bool parity= true; // todo: make me settable.
402 int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
403 for (int i=1; i < heads.size (); i ++)
405 Real p = Staff_symbol_referencer::position_f (heads[i]);
406 int dy =abs (lastpos- (int)p);
412 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
414 heads[i]->translate_axis (l * get_direction (me), X_AXIS);
425 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
427 Stem::before_line_breaking (SCM smob)
429 Grob*me = unsmob_grob (smob);
430 stem_end_position (me); // ugh. Trigger direction calc.
431 position_noteheads (me);
433 if (invisible_b (me))
435 me->remove_grob_property ("molecule-callback");
439 return SCM_UNSPECIFIED;
444 When in a beam with tuplet brackets, brew_mol is called early,
445 caching a wrong value.
447 MAKE_SCHEME_CALLBACK (Stem, height, 2);
449 Stem::height (SCM smob, SCM ax)
451 Axis a = (Axis)gh_scm2int (ax);
452 Grob * me = unsmob_grob (smob);
453 assert (a == Y_AXIS);
455 SCM mol = me->get_uncached_molecule ();
458 iv = unsmob_molecule (mol)->extent (a);
459 return ly_interval2scm (iv);
466 /* TODO: rename flag-style into something more appropriate,
467 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
468 "single" and "double". Needs more discussion.
470 String style, fstyle, staffline_offs;
471 SCM fst = me->get_grob_property ("flag-style");
472 if (gh_string_p (fst))
474 fstyle = ly_scm2string (fst);
477 SCM st = me->get_grob_property ("style");
478 if (gh_symbol_p (st))
480 style = (ly_scm2string (scm_symbol_to_string (st)));
486 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
488 if (String::compare_i (style, "mensural") == 0)
489 /* Mensural notation: For notes on staff lines, use different
490 flags than for notes between staff lines. The idea is that
491 flags are always vertically aligned with the staff lines,
492 regardless if the note head is on a staff line or between two
493 staff lines. In other words, the inner end of a flag always
494 touches a staff line.
499 /* Urrgh! We have to detect wether this stem ends on a staff
500 line or between two staff lines. But we can not call
501 stem_end_position(me) or get_default_stem_end_position(me),
502 since this encounters the flag and hence results in an
503 infinite recursion. However, in pure mensural notation,
504 there are no multiple note heads attached to a single stem,
505 neither is there usually need for using the stem_shorten
506 property (except for 32th and 64th notes, but that is not a
507 problem since the stem length in this case is augmented by
508 an integral multiple of staff_space). Hence, it should be
509 sufficient to just take the first note head, assume it's
510 the only one, look if it's on a staff line, and select the
511 flag's shape accordingly. In the worst case, the shape
512 looks slightly misplaced, but that will usually be the
513 programmer's fault (e.g. when trying to attach multiple
514 note heads to a single stem in mensural notation). */
515 Grob *first = first_head(me);
516 int sz = Staff_symbol_referencer::line_count (me)-1;
517 int p = (int)rint (Staff_symbol_referencer::position_f (first));
518 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
522 staffline_offs = "2";
529 char c = (get_direction (me) == UP) ? 'u' : 'd';
531 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
533 = Font_interface::get_default_font (me)->find_by_name (index_str);
534 if (!fstyle.empty_b ())
535 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
539 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
541 Stem::dim_callback (SCM e, SCM ax)
543 Axis a = (Axis) gh_scm2int (ax);
544 assert (a == X_AXIS);
545 Grob *se = unsmob_grob (e);
547 if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
551 r = flag (se).extent (X_AXIS);
553 return ly_interval2scm (r);
558 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
561 Stem::brew_molecule (SCM smob)
563 Grob*me = unsmob_grob (smob);
565 Direction d = get_direction (me);
568 Real y1 = Staff_symbol_referencer::position_f (first_head (me));
569 Real y2 = stem_end_position (me);
571 Interval stem_y (y1 <? y2,y2 >? y1);
575 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
577 if (Grob *hed = support_head (me))
580 must not take ledgers into account.
582 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
583 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
585 y_attach = head_height.linear_combination (y_attach);
586 stem_y[Direction (-d)] += d * y_attach/dy;
589 if (!invisible_b (me))
591 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
593 * me->paper_l ()->get_var ("stafflinethickness");
595 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
596 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
597 mol.add_molecule (ss);
600 if (!beam_l (me) && abs (flag_i (me)) > 2)
602 Molecule fl = flag (me);
603 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
604 mol.add_molecule (fl);
607 return mol.smobbed_copy ();
611 move the stem to right of the notehead if it is up.
613 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
615 Stem::off_callback (SCM element_smob, SCM)
617 Grob *me = unsmob_grob (element_smob);
620 if (Grob * f = first_head (me))
622 Interval head_wid = Note_head::head_extent(f, X_AXIS);
625 Note_head::stem_attachment_coordinate(f, X_AXIS);
627 Direction d = get_direction (me);
629 Real real_attach = head_wid.linear_combination (d * attach);
634 If not centered: correct for stem thickness.
639 = gh_scm2double (me->get_grob_property ("thickness"))
640 * me->paper_l ()->get_var ("stafflinethickness");
643 r += - d * rule_thick * 0.5;
646 return gh_double2scm (r);
652 Stem::beam_l (Grob*me)
654 SCM b= me->get_grob_property ("beam");
655 return unsmob_grob (b);
659 // ugh still very long.
661 Stem::calc_stem_info (Grob*me)
663 SCM scm_info = me->get_grob_property ("stem-info");
665 if (gh_pair_p (scm_info ))
669 si.idealy_f_ = gh_scm2double (gh_car (scm_info));
670 si.maxy_f_ = gh_scm2double (gh_cadr (scm_info));
671 si.miny_f_ = gh_scm2double (gh_caddr (scm_info));
676 Grob * beam = beam_l (me);
678 Direction beam_dir = Directional_element_interface::get (beam);
681 programming_error ("Beam dir not set.");
686 Real staff_space = Staff_symbol_referencer::staff_space (me);
687 Real half_space = staff_space / 2;
688 int multiplicity = Beam::get_multiplicity (beam);
691 SCM space_proc = beam->get_grob_property ("space-function");
692 SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
693 Real interbeam_f = gh_scm2double (space) * staff_space;
695 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
697 info.idealy_f_ = chord_start_f (me);
699 // for simplicity, we calculate as if dir == UP
700 info.idealy_f_ *= beam_dir;
701 SCM grace_prop = me->get_grob_property ("grace");
703 bool grace_b = to_boolean (grace_prop);
708 s = me->get_grob_property ("beamed-minimum-lengths");
710 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
711 a.push (gh_scm2double (ly_car (q)));
714 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
715 s = me->get_grob_property ("beamed-lengths");
718 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
719 a.push (gh_scm2double (ly_car (q)));
721 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
723 if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
724 /* normal beamed stem */
728 info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
730 info.miny_f_ = info.idealy_f_;
731 info.maxy_f_ = 1000; // INT_MAX;
733 info.idealy_f_ += stem_length;
734 info.miny_f_ += minimum_length;
737 lowest beam of (UP) beam must never be lower than second staffline
739 Hmm, reference (Wanske?)
741 Although this (additional) rule is probably correct,
742 I expect that highest beam (UP) should also never be lower
743 than middle staffline, just as normal stems.
746 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
747 if (!grace_b && !no_extend_b)
749 /* highest beam of (UP) beam must never be lower than middle
751 lowest beam of (UP) beam must never be lower than second staffline
755 >? (- 2 * half_space - thick
756 + (multiplicity > 0) * thick
757 + interbeam_f * (multiplicity - 1));
763 info.idealy_f_ -= thick;
764 info.maxy_f_ = info.idealy_f_;
765 info.miny_f_ = - 1000 ; // INT_MAX;
767 info.idealy_f_ -= stem_length;
768 info.maxy_f_ -= minimum_length;
771 info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
773 s = beam->get_grob_property ("shorten");
775 info.idealy_f_ -= gh_scm2double (s);
777 Grob *common = me->common_refpoint (beam, Y_AXIS);
778 Real interstaff_f = beam_dir *
779 (me->relative_coordinate (common, Y_AXIS)
780 - beam->relative_coordinate (common, Y_AXIS));
782 info.idealy_f_ += interstaff_f;
783 info.miny_f_ += interstaff_f;
784 info.maxy_f_ += interstaff_f ;
786 me->set_grob_property ("stem-info",
787 scm_list_n (gh_double2scm (info.idealy_f_),
788 gh_double2scm (info.maxy_f_ ),
789 gh_double2scm (info.miny_f_),
796 Stem::has_interface (Grob*m)
798 return m && m->has_interface (ly_symbol2scm ("stem-interface"));
802 Stem::set_interface (Grob*me)
804 me->set_interface (ly_symbol2scm ("stem-interface"));