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 = 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 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.
493 /* Urrgh! We have to detect wether this stem ends on a staff
494 line or between two staff lines. But we can not call
495 stem_end_position(me) or get_default_stem_end_position(me),
496 since this encounters the flag and hence results in an
497 infinite recursion. However, in pure mensural notation,
498 there are no multiple note heads attached to a single stem,
499 neither is there usually need for using the stem_shorten
500 property (except for 32th and 64th notes, but that is not a
501 problem since the stem length in this case is augmented by
502 an integral multiple of staff_space). Hence, it should be
503 sufficient to just take the first note head, assume it's
504 the only one, look if it's on a staff line, and select the
505 flag's shape accordingly. In the worst case, the shape
506 looks slightly misplaced, but that will usually be the
507 programmer's fault (e.g. when trying to attach multiple
508 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";
520 char c = (get_direction (me) == UP) ? 'u' : 'd';
522 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
524 = Font_interface::get_default_font (me)->find_by_name (index_str);
525 if (!fstyle.empty_b ())
526 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
530 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
532 Stem::dim_callback (SCM e, SCM ax)
534 Axis a = (Axis) gh_scm2int (ax);
535 assert (a == X_AXIS);
536 Grob *se = unsmob_grob (e);
538 if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
542 r = flag (se).extent (X_AXIS);
544 return ly_interval2scm (r);
549 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
552 Stem::brew_molecule (SCM smob)
554 Grob*me = unsmob_grob (smob);
556 Direction d = get_direction (me);
559 Real y1 = Staff_symbol_referencer::position_f (first_head (me));
560 Real y2 = stem_end_position (me);
562 Interval stem_y (y1 <? y2,y2 >? y1);
566 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
568 if (Grob *hed = support_head (me))
571 must not take ledgers into account.
573 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
574 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
576 y_attach = head_height.linear_combination (y_attach);
577 stem_y[Direction (-d)] += d * y_attach/dy;
580 if (!invisible_b (me))
582 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
584 * me->paper_l ()->get_var ("stafflinethickness");
586 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
587 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
588 mol.add_molecule (ss);
591 if (!beam_l (me) && abs (flag_i (me)) > 2)
593 Molecule fl = flag (me);
594 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
595 mol.add_molecule (fl);
598 return mol.smobbed_copy ();
602 move the stem to right of the notehead if it is up.
604 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
606 Stem::off_callback (SCM element_smob, SCM)
608 Grob *me = unsmob_grob (element_smob);
611 if (Grob * f = first_head (me))
613 Interval head_wid = Note_head::head_extent(f, X_AXIS);
616 Note_head::stem_attachment_coordinate(f, X_AXIS);
618 Direction d = get_direction (me);
620 Real real_attach = head_wid.linear_combination (d * attach);
625 If not centered: correct for stem thickness.
630 = gh_scm2double (me->get_grob_property ("thickness"))
631 * me->paper_l ()->get_var ("stafflinethickness");
634 r += - d * rule_thick * 0.5;
637 return gh_double2scm (r);
643 Stem::beam_l (Grob*me)
645 SCM b= me->get_grob_property ("beam");
646 return unsmob_grob (b);
650 // ugh still very long.
652 Stem::calc_stem_info (Grob*me)
654 Grob * beam = beam_l (me);
656 Direction beam_dir = Directional_element_interface::get (beam);
659 programming_error ("Beam dir not set.");
664 Real staff_space = Staff_symbol_referencer::staff_space (me);
665 Real half_space = staff_space / 2;
666 int multiplicity = Beam::get_multiplicity (beam);
669 SCM space_proc = beam->get_grob_property ("space-function");
670 SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
671 Real interbeam_f = gh_scm2double (space) * staff_space;
673 Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
675 info.idealy_f_ = chord_start_f (me);
677 // for simplicity, we calculate as if dir == UP
678 info.idealy_f_ *= beam_dir;
679 SCM grace_prop = me->get_grob_property ("grace");
681 bool grace_b = to_boolean (grace_prop);
686 s = me->get_grob_property ("beamed-minimum-lengths");
688 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
689 a.push (gh_scm2double (ly_car (q)));
692 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
693 s = me->get_grob_property ("beamed-lengths");
696 for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
697 a.push (gh_scm2double (ly_car (q)));
699 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
701 if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
702 /* normal beamed stem */
706 info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
708 info.miny_f_ = info.idealy_f_;
709 info.maxy_f_ = INT_MAX;
711 info.idealy_f_ += stem_length;
712 info.miny_f_ += minimum_length;
715 lowest beam of (UP) beam must never be lower than second staffline
717 Hmm, reference (Wanske?)
719 Although this (additional) rule is probably correct,
720 I expect that highest beam (UP) should also never be lower
721 than middle staffline, just as normal stems.
724 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
725 if (!grace_b && !no_extend_b)
727 /* highest beam of (UP) beam must never be lower than middle
729 lowest beam of (UP) beam must never be lower than second staffline
733 >? (- 2 * half_space - thick
734 + (multiplicity > 0) * thick
735 + interbeam_f * (multiplicity - 1));
741 info.idealy_f_ -= thick;
742 info.maxy_f_ = info.idealy_f_;
743 info.miny_f_ = -INT_MAX;
745 info.idealy_f_ -= stem_length;
746 info.maxy_f_ -= minimum_length;
749 info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
751 s = beam->get_grob_property ("shorten");
753 info.idealy_f_ -= gh_scm2double (s);
755 Grob *common = me->common_refpoint (beam, Y_AXIS);
756 Real interstaff_f = beam_dir *
757 (me->relative_coordinate (common, Y_AXIS)
758 - beam->relative_coordinate (common, Y_AXIS));
760 info.idealy_f_ += interstaff_f;
761 info.miny_f_ += interstaff_f;
762 info.maxy_f_ += interstaff_f ;
768 Stem::has_interface (Grob*m)
770 return m && m->has_interface (ly_symbol2scm ("stem-interface"));
774 Stem::set_interface (Grob*me)
776 me->set_interface (ly_symbol2scm ("stem-interface"));