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 (me).get ();
101 d = get_default_dir (me);
103 Directional_element_interface (me).set (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_);
219 Score_element * me = this;
221 me->set_elt_property ("heads", SCM_EOL);
222 Stem::set_interface (me);
223 me->add_offset_callback ( &Stem::off_callback, X_AXIS);
227 Stem::invisible_b (Score_element*me)
229 return !(heads_i (me) && Rhythmic_head::balltype_i (support_head (me)) >= 1);
233 Stem::get_center_distance (Score_element*me, Direction d)
235 int staff_center = 0;
236 int distance = (int) (d*(head_positions(me)[d] - staff_center));
237 return distance >? 0;
241 Stem::get_default_dir (Score_element*me)
243 int du = get_center_distance (me,UP);
244 int dd = get_center_distance (me,DOWN);
247 return Direction (sign (dd -du));
249 return to_dir (me->get_elt_property ("default-neutral-direction"));
253 ugh. A is used for different purposes. This functionality should be
254 moved into scheme at some point to get rid of the silly
255 conversions. (but lets wait till we have namespaces in SCM)
258 Stem::get_default_stem_end_position (Score_element*me)
260 bool grace_b = to_boolean (me->get_elt_property ("grace"));
261 String type_str = grace_b ? "grace-" : "";
266 SCM scm_len = me->get_elt_property("length");
267 if (gh_number_p (scm_len))
269 length_f = gh_scm2double (scm_len);
273 s = scm_eval (ly_symbol2scm ((type_str + "stem-length").ch_C()));
274 for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
275 a.push (gh_scm2double (gh_car (q)));
277 // stem uses half-spaces
278 length_f = a[((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
283 s = scm_eval (ly_symbol2scm ((type_str + "stem-shorten").ch_C()));
284 for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
285 a.push (gh_scm2double (gh_car (q)));
288 // stem uses half-spaces
290 // fixme: use gh_list_ref () iso. array[]
291 Real shorten_f = a[((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
294 'set-default-stemlen' sets direction too
296 Direction dir = get_direction (me);
299 dir = get_default_dir (me);
300 Directional_element_interface (me).set (dir);
304 stems in unnatural (forced) direction should be shortened,
305 according to [Roush & Gourlay]
307 if (((int)chord_start_f (me))
308 && (get_direction (me) != get_default_dir (me)))
309 length_f -= shorten_f;
312 Real st = head_positions(me)[dir] + dir * length_f;
314 bool no_extend_b = to_boolean (me->get_elt_property ("no-stem-extend"));
315 if (!grace_b && !no_extend_b && dir * st < 0)
325 Stem::flag_i (Score_element*me)
327 SCM s = me->get_elt_property ("duration-log");
328 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
332 Stem::position_noteheads (Score_element*me)
337 Link_array<Score_element> heads =
338 Pointer_group_interface__extract_elements (me, (Score_element*)0, "heads");
340 heads.sort (compare_position);
341 Direction dir =get_direction (me);
347 Real w = support_head (me)->extent (X_AXIS)[dir];
348 for (int i=0; i < heads.size (); i++)
350 heads[i]->translate_axis (w - heads[i]->extent (X_AXIS)[dir], X_AXIS);
353 bool parity= true; // todo: make me settable.
354 int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
355 for (int i=1; i < heads.size (); i ++)
357 Real p = Staff_symbol_referencer::position_f (heads[i]);
358 int dy =abs (lastpos- (int)p);
364 Real l = heads[i]->extent (X_AXIS).length ();
365 heads[i]->translate_axis (l * get_direction (me), X_AXIS);
376 MAKE_SCHEME_CALLBACK(Stem,before_line_breaking);
378 Stem::before_line_breaking (SCM smob)
380 Score_element*me = unsmob_element (smob);
381 stem_end_position (me); // ugh. Trigger direction calc.
382 position_noteheads (me);
384 if (invisible_b (me))
386 me->remove_elt_property ("molecule-callback");
390 set_spacing_hints (me);
391 return SCM_UNDEFINED;
397 set stem directions for hinting the optical spacing correction.
399 Modifies DIR_LIST property of the Stem's Paper_column
401 TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
404 Stem::set_spacing_hints (Score_element*me)
406 if (!invisible_b (me))
408 SCM scmdir = gh_int2scm (get_direction (me));
410 Item* item = dynamic_cast<Item*> (me);
411 Item * col = item->column_l ();
412 SCM dirlist =col->get_elt_property ("dir-list");
413 if (dirlist == SCM_UNDEFINED)
416 if (scm_sloppy_memq (scmdir, dirlist) == SCM_EOL)
418 dirlist = gh_cons (scmdir, dirlist);
419 col->set_elt_property ("dir-list", dirlist);
425 Stem::flag (Score_element*me)
428 SCM st = me->get_elt_property ("flag-style");
429 if ( gh_string_p (st))
431 style = ly_scm2string (st);
434 char c = (get_direction (me) == UP) ? 'u' : 'd';
435 Molecule m = me->lookup_l ()->afm_find (String ("flags-") + to_str (c) +
436 to_str (flag_i (me)));
437 if (!style.empty_b ())
438 m.add_molecule(me->lookup_l ()->afm_find (String ("flags-") + to_str (c) + style));
443 Stem::dim_callback (Score_element *se, Axis )
446 if (unsmob_element (se->get_elt_property ("beam")) || abs (flag_i (se)) <= 2)
450 r = flag (se).extent (X_AXIS);
456 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh! Should be settable.
459 MAKE_SCHEME_CALLBACK(Stem,brew_molecule);
462 Stem::brew_molecule (SCM smob)
464 Score_element*me = unsmob_element (smob);
466 Direction d = get_direction (me);
469 Real y1 = Staff_symbol_referencer::position_f (first_head (me));
470 Real y2 = stem_end_position (me);
472 Interval stem_y(y1,y2);
473 stem_y.unite (Interval (y2,y1));
475 Real dy = Staff_symbol_referencer::staff_space (me)/2.0;
477 if (support_head (me))
478 head_wid = support_head (me)->extent (X_AXIS).length ();
479 stem_y[Direction(-d)] += d * head_wid * tan(ANGLE)/(2*dy);
481 if (!invisible_b (me))
483 Real stem_width = me->paper_l ()->get_var ("stemthickness");
484 Molecule ss =me->lookup_l ()->filledbox (Box (Interval (-stem_width/2, stem_width/2),
485 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
486 mol.add_molecule (ss);
489 if (!beam_l (me) && abs (flag_i (me)) > 2)
491 Molecule fl = flag (me);
492 fl.translate_axis(stem_y[d]*dy, Y_AXIS);
493 mol.add_molecule (fl);
496 return mol.create_scheme();
500 Stem::off_callback (Score_element * me, Axis)
503 if (Score_element * f = first_head (me))
505 Interval head_wid(0, f->extent (X_AXIS).length ());
507 if (to_boolean (me->get_elt_property ("stem-centered")))
508 return head_wid.center ();
510 Real rule_thick = me->paper_l ()->get_var ("stemthickness");
511 Direction d = get_direction (me);
512 r = head_wid[d] - d * rule_thick ;
520 Stem::beam_l (Score_element*me)
522 SCM b= me->get_elt_property ("beam");
523 return dynamic_cast<Beam*> (unsmob_element (b));
527 // ugh still very long.
529 Stem::calc_stem_info (Score_element*me)
531 Beam * beam = beam_l (me);
533 Direction beam_dir = Directional_element_interface (beam).get ();
536 programming_error ("Beam dir not set.");
541 Real staff_space = Staff_symbol_referencer::staff_space (me);
542 Real half_space = staff_space / 2;
543 Real interbeam_f = me->paper_l ()->interbeam_f (beam->get_multiplicity ());
544 Real thick = gh_scm2double (beam->get_elt_property ("beam-thickness"));
545 int multiplicity = beam->get_multiplicity ();
548 info.idealy_f_ = chord_start_f (me);
550 // for simplicity, we calculate as if dir == UP
551 info.idealy_f_ *= beam_dir;
552 SCM grace_prop = me->get_elt_property ("grace");
554 bool grace_b = to_boolean (grace_prop);
558 String type_str = grace_b ? "grace-" : "";
560 s = scm_eval (ly_symbol2scm ((type_str + "beamed-stem-minimum-length").ch_C()));
562 for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
563 a.push (gh_scm2double (gh_car (q)));
566 Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
567 s = scm_eval (ly_symbol2scm ((type_str + "beamed-stem-length").ch_C()));
570 for (SCM q = s; q != SCM_EOL; q = gh_cdr (q))
571 a.push (gh_scm2double (gh_car (q)));
573 Real stem_length = a[multiplicity <? (a.size () - 1)] * staff_space;
575 if (!beam_dir || (beam_dir == Directional_element_interface (me).get ()))
576 /* normal beamed stem */
580 info.idealy_f_ += thick + (multiplicity - 1) * interbeam_f;
582 info.miny_f_ = info.idealy_f_;
583 info.maxy_f_ = INT_MAX;
585 info.idealy_f_ += stem_length;
586 info.miny_f_ += minimum_length;
589 lowest beam of (UP) beam must never be lower than second staffline
591 Hmm, reference (Wanske?)
593 Although this (additional) rule is probably correct,
594 I expect that highest beam (UP) should also never be lower
595 than middle staffline, just as normal stems.
598 bool no_extend_b = to_boolean (me->get_elt_property ("no-stem-extend"));
599 if (!grace_b && !no_extend_b)
601 /* highest beam of (UP) beam must never be lower than middle
603 lowest beam of (UP) beam must never be lower than second staffline
607 >? (- 2 * half_space - thick
608 + (multiplicity > 0) * thick
609 + interbeam_f * (multiplicity - 1));
615 info.idealy_f_ -= thick;
616 info.maxy_f_ = info.idealy_f_;
617 info.miny_f_ = -INT_MAX;
619 info.idealy_f_ -= stem_length;
620 info.maxy_f_ -= minimum_length;
623 info.idealy_f_ = (info.maxy_f_ <? info.idealy_f_) >? info.miny_f_;
625 s = beam->get_elt_property ("shorten");
627 info.idealy_f_ -= gh_scm2double (s);
629 Real interstaff_f = -beam_dir* calc_interstaff_dist (dynamic_cast<Item*> (me), beam);
631 info.idealy_f_ += interstaff_f;
632 info.miny_f_ += interstaff_f;
633 info.maxy_f_ += interstaff_f ;
639 Stem::has_interface (Score_element*m)
641 return m && m->has_interface (ly_symbol2scm ("stem-interface"));
645 Stem::set_interface (Score_element*m)
647 return m->set_interface (ly_symbol2scm ("stem-interface"));