2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2000 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
13 #include "directional-element-interface.hh"
14 #include "note-head.hh"
17 #include "paper-def.hh"
18 #include "rhythmic-head.hh"
20 #include "molecule.hh"
21 #include "paper-column.hh"
25 #include "group-interface.hh"
26 #include "cross-staff.hh"
27 #include "staff-symbol-referencer.hh"
32 Stem::set_beaming (Score_element*me ,int i, Direction d )
34 SCM pair = me->get_elt_property ("beaming");
36 if (!gh_pair_p (pair))
38 pair = gh_cons (gh_int2scm (0),gh_int2scm (0));
39 me-> set_elt_property ("beaming", pair);
41 index_set_cell (pair, d, gh_int2scm (i));
45 Stem::beam_count (Score_element*me,Direction d)
47 SCM p=me->get_elt_property ("beaming");
49 return gh_scm2int (index_cell (p,d));
55 Stem::head_positions (Score_element*me)
63 Drul_array<Score_element*> 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 (Score_element*me)
73 return head_positions(me)[get_direction (me)]
74 * Staff_symbol_referencer::staff_space (me)/2.0;
78 Stem::stem_end_position (Score_element*me)
80 SCM p =me->get_elt_property ("stem-end-position");
85 pos = get_default_stem_end_position (me);
86 me->set_elt_property ("stem-end-position", gh_double2scm (pos));
89 pos = gh_scm2double (p);
95 Stem::get_direction (Score_element*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 (Score_element*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_elt_property ("stem-end-position", gh_double2scm (se));
122 Stem::type_i (Score_element*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 (Score_element*me)
133 SCM h = me->get_elt_property ("support-head");
134 Score_element * nh = unsmob_element (h);
137 else if (heads_i (me) == 1)
143 return unsmob_element (gh_car (me->get_elt_property ("heads")));
146 return first_head (me);
151 Stem::heads_i (Score_element*me)
153 Pointer_group_interface gi (me, "heads");
158 The note head which forms one end of the stem.
161 Stem::first_head (Score_element*me)
163 return extremal_heads (me)[-get_direction (me)];
167 START is part where stem reaches `last' head.
169 Drul_array<Score_element*>
170 Stem::extremal_heads (Score_element*me)
172 const int inf = 1000000;
173 Drul_array<int> extpos;
177 Drul_array<Score_element *> exthead;
178 exthead[LEFT] = exthead[RIGHT] =0;
180 for (SCM s = me->get_elt_property ("heads"); gh_pair_p (s); s = gh_cdr (s))
182 Score_element * n = unsmob_element (gh_car (s));
185 int p = int(Staff_symbol_referencer::position_f (n));
189 if (d* p > d* extpos[d])
194 } while (flip (&d) != DOWN);
201 Stem::add_head (Score_element*me, Score_element *n)
203 n->set_elt_property ("stem", me->self_scm ());
204 n->add_dependency (me);
206 if (Note_head::has_interface (n))
208 Pointer_group_interface (me, "heads").add_element (n);
212 n->set_elt_property ("rest", n->self_scm ());
217 Stem::invisible_b (Score_element*me)
219 return !(heads_i (me) && Rhythmic_head::balltype_i (support_head (me)) >= 1);
223 Stem::get_center_distance (Score_element*me, Direction d)
225 int staff_center = 0;
226 int distance = (int) (d*(head_positions(me)[d] - staff_center));
227 return distance >? 0;
231 Stem::get_default_dir (Score_element*me)
233 int du = get_center_distance (me,UP);
234 int dd = get_center_distance (me,DOWN);
237 return Direction (sign (dd -du));
239 return to_dir (me->get_elt_property ("default-neutral-direction"));
243 ugh. A is used for different purposes. This functionality should be
244 moved into scheme at some point to get rid of the silly
245 conversions. (but lets wait till we have namespaces in SCM)
248 Stem::get_default_stem_end_position (Score_element*me)
250 bool grace_b = to_boolean (me->get_elt_property ("grace"));
251 String type_str = grace_b ? "grace-" : "";
256 SCM scm_len = me->get_elt_property("length");
257 if (gh_number_p (scm_len))
259 length_f = gh_scm2double (scm_len);
263 s = scm_eval2 (ly_symbol2scm ((type_str + "stem-length").ch_C()),
265 for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
266 a.push (gh_scm2double (gh_car (q)));
268 // stem uses half-spaces
269 length_f = a[((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
274 s = scm_eval2 (ly_symbol2scm ((type_str + "stem-shorten").ch_C()),
276 for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
277 a.push (gh_scm2double (gh_car (q)));
280 // stem uses half-spaces
282 // fixme: use gh_list_ref () iso. array[]
283 Real shorten_f = a[((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
286 'set-default-stemlen' sets direction too
288 Direction dir = get_direction (me);
291 dir = get_default_dir (me);
292 Directional_element_interface::set (me, dir);
296 stems in unnatural (forced) direction should be shortened,
297 according to [Roush & Gourlay]
299 if (((int)chord_start_f (me))
300 && (get_direction (me) != get_default_dir (me)))
301 length_f -= shorten_f;
304 Real st = head_positions(me)[dir] + dir * length_f;
306 bool no_extend_b = to_boolean (me->get_elt_property ("no-stem-extend"));
307 if (!grace_b && !no_extend_b && dir * st < 0)
317 Stem::flag_i (Score_element*me)
319 SCM s = me->get_elt_property ("duration-log");
320 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
324 Stem::position_noteheads (Score_element*me)
329 Link_array<Score_element> heads =
330 Pointer_group_interface__extract_elements (me, (Score_element*)0, "heads");
332 heads.sort (compare_position);
333 Direction dir =get_direction (me);
339 Real w = support_head (me)->extent (X_AXIS)[dir];
340 for (int i=0; i < heads.size (); i++)
342 heads[i]->translate_axis (w - heads[i]->extent (X_AXIS)[dir], X_AXIS);
345 bool parity= true; // todo: make me settable.
346 int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
347 for (int i=1; i < heads.size (); i ++)
349 Real p = Staff_symbol_referencer::position_f (heads[i]);
350 int dy =abs (lastpos- (int)p);
356 Real l = heads[i]->extent (X_AXIS).length ();
357 heads[i]->translate_axis (l * get_direction (me), X_AXIS);
368 MAKE_SCHEME_CALLBACK(Stem,before_line_breaking);
370 Stem::before_line_breaking (SCM smob)
372 Score_element*me = unsmob_element (smob);
373 stem_end_position (me); // ugh. Trigger direction calc.
374 position_noteheads (me);
376 if (invisible_b (me))
378 me->remove_elt_property ("molecule-callback");
382 set_spacing_hints (me);
383 return SCM_UNSPECIFIED;
389 set stem directions for hinting the optical spacing correction.
391 Modifies DIR_LIST property of the Stem's Paper_column
393 TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
396 Stem::set_spacing_hints (Score_element*me)
398 if (!invisible_b (me))
400 SCM scmdir = gh_int2scm (get_direction (me));
402 Item* item = dynamic_cast<Item*> (me);
403 Item * col = item->column_l ();
404 SCM dirlist =col->get_elt_property ("dir-list");
405 if (dirlist == SCM_UNDEFINED)
408 if (scm_sloppy_memq (scmdir, dirlist) == SCM_EOL)
410 dirlist = gh_cons (scmdir, dirlist);
411 col->set_elt_property ("dir-list", dirlist);
417 Stem::flag (Score_element*me)
420 SCM st = me->get_elt_property ("flag-style");
421 if ( gh_string_p (st))
423 style = ly_scm2string (st);
426 char c = (get_direction (me) == UP) ? 'u' : 'd';
427 Molecule m = me->lookup_l ()->afm_find (String ("flags-") + to_str (c) +
428 to_str (flag_i (me)));
429 if (!style.empty_b ())
430 m.add_molecule(me->lookup_l ()->afm_find (String ("flags-") + to_str (c) + style));
435 Stem::dim_callback (Score_element *se, Axis )
438 if (unsmob_element (se->get_elt_property ("beam")) || abs (flag_i (se)) <= 2)
442 r = flag (se).extent (X_AXIS);
448 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh! Should be settable.
451 MAKE_SCHEME_CALLBACK(Stem,brew_molecule);
454 Stem::brew_molecule (SCM smob)
456 Score_element*me = unsmob_element (smob);
458 Direction d = get_direction (me);
461 Real y1 = Staff_symbol_referencer::position_f (first_head (me));
462 Real y2 = stem_end_position (me);
464 Interval stem_y(y1,y2);
465 stem_y.unite (Interval (y2,y1));
467 Real dy = Staff_symbol_referencer::staff_space (me)/2.0;
469 if (support_head (me))
470 head_wid = support_head (me)->extent (X_AXIS).length ();
471 stem_y[Direction(-d)] += d * head_wid * tan(ANGLE)/(2*dy);
473 if (!invisible_b (me))
475 Real stem_width = gh_scm2double (me->get_elt_property ("thickness")) * me->paper_l ()->get_var ("stafflinethickness");
476 Molecule ss =me->lookup_l ()->filledbox (Box (Interval (-stem_width/2, stem_width/2),
477 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
478 mol.add_molecule (ss);
481 if (!beam_l (me) && abs (flag_i (me)) > 2)
483 Molecule fl = flag (me);
484 fl.translate_axis(stem_y[d]*dy, Y_AXIS);
485 mol.add_molecule (fl);
488 return mol.create_scheme();
492 Stem::off_callback (Score_element * me, Axis)
495 if (Score_element * f = first_head (me))
497 Interval head_wid(0, f->extent (X_AXIS).length ());
499 if (to_boolean (me->get_elt_property ("stem-centered")))
500 return head_wid.center ();
502 Real rule_thick = gh_scm2double (me->get_elt_property ("thickness")) * me->paper_l ()->get_var ("stafflinethickness");
503 Direction d = get_direction (me);
504 r = head_wid[d] - d * rule_thick ;
512 Stem::beam_l (Score_element*me)
514 SCM b= me->get_elt_property ("beam");
515 return unsmob_element (b);
519 // ugh still very long.
521 Stem::calc_stem_info (Score_element*me)
523 Score_element * beam = beam_l (me);
525 Direction beam_dir = Directional_element_interface::get (beam);
528 programming_error ("Beam dir not set.");
533 Real staff_space = Staff_symbol_referencer::staff_space (me);
534 Real half_space = staff_space / 2;
535 int multiplicity = Beam::get_multiplicity (beam);
538 SCM space_proc = beam->get_elt_property ("beam-space-function");
539 SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
540 Real interbeam_f = gh_scm2double (space) * staff_space;
542 Real thick = gh_scm2double (beam->get_elt_property ("beam-thickness"));
544 info.idealy_f_ = chord_start_f (me);
546 // for simplicity, we calculate as if dir == UP
547 info.idealy_f_ *= beam_dir;
548 SCM grace_prop = me->get_elt_property ("grace");
550 bool grace_b = to_boolean (grace_prop);
554 String type_str = grace_b ? "grace-" : "";
556 s = scm_eval2 (ly_symbol2scm ((type_str + "beamed-stem-minimum-length").ch_C()), SCM_EOL);
558 for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
559 a.push (gh_scm2double (gh_car (q)));
562 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
563 s = scm_eval2 (ly_symbol2scm ((type_str + "beamed-stem-length").ch_C()),
567 for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
568 a.push (gh_scm2double (gh_car (q)));
570 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
572 if (!beam_dir || (beam_dir == Directional_element_interface::get (me)))
573 /* normal beamed stem */
577 info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
579 info.miny_f_ = info.idealy_f_;
580 info.maxy_f_ = INT_MAX;
582 info.idealy_f_ += stem_length;
583 info.miny_f_ += minimum_length;
586 lowest beam of (UP) beam must never be lower than second staffline
588 Hmm, reference (Wanske?)
590 Although this (additional) rule is probably correct,
591 I expect that highest beam (UP) should also never be lower
592 than middle staffline, just as normal stems.
595 bool no_extend_b = to_boolean (me->get_elt_property ("no-stem-extend"));
596 if (!grace_b && !no_extend_b)
598 /* highest beam of (UP) beam must never be lower than middle
600 lowest beam of (UP) beam must never be lower than second staffline
604 >? (- 2 * half_space - thick
605 + (multiplicity > 0) * thick
606 + interbeam_f * (multiplicity - 1));
612 info.idealy_f_ -= thick;
613 info.maxy_f_ = info.idealy_f_;
614 info.miny_f_ = -INT_MAX;
616 info.idealy_f_ -= stem_length;
617 info.maxy_f_ -= minimum_length;
620 info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
622 s = beam->get_elt_property ("shorten");
624 info.idealy_f_ -= gh_scm2double (s);
626 Real interstaff_f = -beam_dir* calc_interstaff_dist (dynamic_cast<Item*> (me), dynamic_cast<Spanner*> (beam));
628 info.idealy_f_ += interstaff_f;
629 info.miny_f_ += interstaff_f;
630 info.maxy_f_ += interstaff_f ;
636 Stem::has_interface (Score_element*m)
638 return m && m->has_interface (ly_symbol2scm ("stem-interface"));
642 Stem::set_interface (Score_element*me)
644 me->set_elt_property ("heads", SCM_EOL);
645 me->add_offset_callback ( &Stem::off_callback, X_AXIS);
646 me->set_interface (ly_symbol2scm ("stem-interface"));