2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
11 #include <math.h> // m_pi
14 #include "directional-element-interface.hh"
15 #include "note-head.hh"
18 #include "paper-def.hh"
19 #include "rhythmic-head.hh"
20 #include "font-interface.hh"
21 #include "molecule.hh"
22 #include "paper-column.hh"
26 #include "group-interface.hh"
27 #include "staff-symbol-referencer.hh"
29 #include "side-position-interface.hh"
32 Stem::set_beaming (Grob*me ,int i, Direction d)
34 SCM pair = me->get_grob_property ("beaming");
36 if (!gh_pair_p (pair))
38 pair = gh_cons (gh_int2scm (0),gh_int2scm (0));
39 me-> set_grob_property ("beaming", pair);
41 index_set_cell (pair, d, gh_int2scm (i));
45 Stem::beam_count (Grob*me,Direction d)
47 SCM p=me->get_grob_property ("beaming");
49 return gh_scm2int (index_cell (p,d));
55 Stem::head_positions (Grob*me)
63 Drul_array<Grob*> e (extremal_heads (me));
65 return Interval (Staff_symbol_referencer::position_f (e[DOWN]),
66 Staff_symbol_referencer::position_f (e[UP]));
71 Stem::chord_start_f (Grob*me)
73 return head_positions (me)[get_direction (me)]
74 * Staff_symbol_referencer::staff_space (me)/2.0;
78 Stem::stem_end_position (Grob*me)
80 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_element (me, "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->parent_l (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_elements (me, (Grob*)0, "heads");
384 heads.sort (compare_position);
385 Direction dir =get_direction (me);
391 Grob *hed = support_head (me);
392 Real w = hed->extent (hed, X_AXIS)[dir];
393 for (int i=0; i < heads.size (); i++)
395 heads[i]->translate_axis (w - heads[i]->extent (heads[i], X_AXIS)[dir], X_AXIS);
398 bool parity= true; // todo: make me settable.
399 int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
400 for (int i=1; i < heads.size (); i ++)
402 Real p = Staff_symbol_referencer::position_f (heads[i]);
403 int dy =abs (lastpos- (int)p);
409 Real l = heads[i]->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 set_spacing_hints (me);
436 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);
462 set stem directions for hinting the optical spacing correction.
464 Modifies DIR_LIST property of the Stem's Paper_column
466 TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
469 Stem::set_spacing_hints (Grob*me)
471 if (!invisible_b (me))
473 SCM scmdir = gh_int2scm (get_direction (me));
475 Item* item = dynamic_cast<Item*> (me);
476 Item * col = item->column_l ();
477 SCM dirlist =col->get_grob_property ("dir-list");
478 if (scm_c_memq (scmdir, dirlist) == SCM_BOOL_F)
480 dirlist = gh_cons (scmdir, dirlist);
481 col->set_grob_property ("dir-list", dirlist);
489 /* TODO: rename flag-style into something more appropriate,
490 e.g. "stroke-style", maybe with values "" (i.e. no stroke),
491 "single" and "double". Needs more discussion.
493 String style, fstyle, staffline_offs;
494 SCM fst = me->get_grob_property ("flag-style");
495 if (gh_string_p (fst))
497 fstyle = ly_scm2string (fst);
500 SCM st = me->get_grob_property ("style");
501 if (gh_symbol_p (st))
503 style = (ly_scm2string (scm_symbol_to_string (st)));
509 if (String::compare_i (style, "mensural") == 0)
510 /* Mensural notation: For notes on staff lines, use different
511 flags than for notes between staff lines. The idea is that
512 flags are always vertically aligned with the staff lines,
513 regardless if the note head is on a staff line or between two
514 staff lines. In other words, the inner end of a flag always
515 touches a staff line.
518 /* Urrgh! We have to detect wether this stem ends on a staff
519 line or between two staff lines. But we can not call
520 stem_end_position(me) or get_default_stem_end_position(me),
521 since this encounters the flag and hence results in an
522 infinite recursion. However, in pure mensural notation,
523 there are no multiple note heads attached to a single stem,
524 neither is there usually need for using the stem_shorten
525 property (except for 32th and 64th notes, but that is not a
526 problem since the stem length in this case is augmented by
527 an integral multiple of staff_space). Hence, it should be
528 sufficient to just take the first note head, assume it's
529 the only one, look if it's on a staff line, and select the
530 flag's shape accordingly. In the worst case, the shape
531 looks slightly misplaced, but that will usually be the
532 programmer's fault (e.g. when trying to attach multiple
533 note heads to a single stem in mensural notation).
536 Grob *first = first_head(me);
537 int sz = Staff_symbol_referencer::line_count (me)-1;
538 int p = (int)rint (Staff_symbol_referencer::position_f (first));
539 staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
545 char c = (get_direction (me) == UP) ? 'u' : 'd';
547 = String ("flags-") + style + to_str (c) + staffline_offs + to_str (flag_i (me));
549 = Font_interface::get_default_font (me)->find_by_name (index_str);
550 if (!fstyle.empty_b ())
551 m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
555 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
557 Stem::dim_callback (SCM e, SCM ax)
559 Axis a = (Axis) gh_scm2int (ax);
560 assert (a == X_AXIS);
561 Grob *se = unsmob_grob (e);
563 if (unsmob_grob (se->get_grob_property ("beam")) || abs (flag_i (se)) <= 2)
567 r = flag (se).extent (X_AXIS);
569 return ly_interval2scm (r);
574 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
577 Stem::brew_molecule (SCM smob)
579 Grob*me = unsmob_grob (smob);
581 Direction d = get_direction (me);
584 Real y1 = Staff_symbol_referencer::position_f (first_head (me));
585 Real y2 = stem_end_position (me);
587 Interval stem_y (y1,y2);
588 stem_y.unite (Interval (y2,y1));
591 Real dy = Staff_symbol_referencer::staff_space (me)/2.0;
593 if (Grob *hed = support_head (me))
595 Interval head_height = hed->extent (hed,Y_AXIS);
596 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
598 y_attach = head_height.linear_combination (y_attach);
599 stem_y[Direction (-d)] += d * 2*y_attach;
602 if (!invisible_b (me))
604 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
606 * me->paper_l ()->get_var ("stafflinethickness");
608 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
609 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
610 mol.add_molecule (ss);
613 if (!beam_l (me) && abs (flag_i (me)) > 2)
615 Molecule fl = flag (me);
616 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
617 mol.add_molecule (fl);
620 return mol.smobbed_copy ();
624 move the stem to right of the notehead if it is up.
626 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
628 Stem::off_callback (SCM element_smob, SCM)
630 Grob *me = unsmob_grob (element_smob);
633 if (Grob * f = first_head (me))
635 Interval head_wid = f->extent (f,X_AXIS);
638 Note_head::stem_attachment_coordinate(f, X_AXIS);
640 Direction d = get_direction (me);
642 Real real_attach = head_wid.linear_combination (d * attach);
647 If not centered: correct for stem thickness.
652 = gh_scm2double (me->get_grob_property ("thickness"))
653 * me->paper_l ()->get_var ("stafflinethickness");
656 r += - d * rule_thick * 0.5;
659 return gh_double2scm (r);
665 Stem::beam_l (Grob*me)
667 SCM b= me->get_grob_property ("beam");
668 return unsmob_grob (b);
672 // ugh still very long.
674 Stem::calc_stem_info (Grob*me)
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_ = 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_ = -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 ;
790 Stem::has_interface (Grob*m)
792 return m && m->has_interface (ly_symbol2scm ("stem-interface"));
796 Stem::set_interface (Grob*me)
798 me->set_interface (ly_symbol2scm ("stem-interface"));