lilypond-1.3.12
[lilypond.git] / lily / stem.cc
blob232caf5aed7ac5caddd77103835aa5f983d725f4
1 /*
2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996, 1997--1999 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
12 #include "dimension-cache.hh"
13 #include "stem.hh"
14 #include "debug.hh"
15 #include "paper-def.hh"
16 #include "note-head.hh"
17 #include "lookup.hh"
18 #include "molecule.hh"
19 #include "paper-column.hh"
20 #include "misc.hh"
21 #include "beam.hh"
22 #include "rest.hh"
23 #include "group-interface.hh"
24 #include "cross-staff.hh"
25 #include "staff-symbol-referencer.hh"
28 void
29 Stem::set_beaming (int i, Direction d )
31 SCM pair = get_elt_property ("beaming");
33 if (!gh_pair_p (pair))
35 pair = gh_cons (gh_int2scm (0),gh_int2scm (0));
36 set_elt_property ("beaming", pair);
38 index_set_cell (pair, d, gh_int2scm (i));
41 int
42 Stem::beam_count (Direction d) const
44 SCM p=get_elt_property ("beaming");
45 if (gh_pair_p (p))
46 return gh_scm2int (index_cell (p,d));
47 else
48 return 0;
51 Interval_t<int>
52 Stem::head_positions () const
54 /*
55 Mysterious FreeBSD fix by John Galbraith. Somehow, the empty intervals
56 trigger FP exceptions on FreeBSD. Fix: do not return infinity
59 if (!first_head ())
61 return Interval_t<int> (100,-100);
64 Link_array<Note_head> head_l_arr =
65 Group_interface__extract_elements (this, (Note_head*)0, "heads");
67 Interval_t<int> r;
68 for (int i =0; i < head_l_arr.size (); i++)
70 Staff_symbol_referencer_interface si (head_l_arr[i]);
71 int p = (int)si.position_f ();
72 r[BIGGER] = r[BIGGER] >? p;
73 r[SMALLER] = r[SMALLER] <? p;
75 return r;
78 Real
79 Stem::stem_begin_f () const
81 return yextent_[Direction(-get_direction ())];
84 Real
85 Stem::chord_start_f () const
87 return head_positions()[get_direction ()]
88 * Staff_symbol_referencer_interface (this).staff_line_leading_f ()/2.0;
91 Real
92 Stem::stem_end_f () const
94 return yextent_[get_direction ()];
97 void
98 Stem::set_stemend (Real se)
100 // todo: margins
101 if (get_direction () && get_direction () * head_positions()[get_direction ()] >= se*get_direction ())
102 warning (_ ("Weird stem size; check for narrow beams"));
105 yextent_[get_direction ()] = se;
106 yextent_[Direction(-get_direction ())] = head_positions()[-get_direction ()];
110 Stem::type_i () const
112 return first_head () ? first_head ()->balltype_i () : 2;
118 Note head that determines hshift for upstems
120 Score_element*
121 Stem::support_head ()const
123 SCM h = get_elt_property ("support-head");
124 Score_element * nh = unsmob_element (h);
125 if (nh)
126 return nh;
127 else
128 return first_head ();
133 The note head which forms one end of the stem.
135 Note_head*
136 Stem::first_head () const
138 const int inf = 1000000;
139 int pos = -inf;
140 Direction dir = get_direction ();
143 Note_head *nh =0;
144 for (SCM s = get_elt_property ("heads"); gh_pair_p (s); s = gh_cdr (s))
146 Note_head * n = dynamic_cast<Note_head*> (unsmob_element (gh_car (s)));
147 Staff_symbol_referencer_interface si (n);
148 int p = dir * int(si.position_f ());
149 if (p > pos)
151 nh = n;
155 return nh;
158 void
159 Stem::add_head (Rhythmic_head *n)
161 n->set_elt_property ("stem", this->self_scm_);
162 n->add_dependency (this); // ?
165 Group_interface gi (this);
166 if (Note_head *nh = dynamic_cast<Note_head *> (n))
167 gi.name_ = "heads";
168 else
169 gi.name_ = "rests";
171 gi.add_element (n);
174 Stem::Stem ()
176 set_elt_property ("heads", SCM_EOL);
177 set_elt_property ("rests", SCM_EOL);
180 bool
181 Stem::invisible_b () const
183 return !(first_head () && first_head()->balltype_i () >= 1);
187 Stem::get_center_distance (Direction d) const
189 int staff_center = 0;
190 int distance = d*(head_positions()[d] - staff_center);
191 return distance >? 0;
194 Direction
195 Stem::get_default_dir () const
197 int du = get_center_distance (UP);
198 int dd = get_center_distance (DOWN);
200 if (sign (dd - du))
201 return Direction (sign (dd -du));
203 return Direction (int(paper_l ()->get_var ("stem_default_neutral_direction")));
206 void
207 Stem::set_default_stemlen ()
209 Real length_f = 0.;
210 SCM scm_len = get_elt_property("length");
211 if (scm_len != SCM_UNDEFINED)
213 length_f = gh_scm2double (scm_len);
215 else
216 length_f = paper_l ()->get_var ("stem_length0");
218 bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
219 String type_str = grace_b ? "grace_" : "";
221 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten0");
223 /* URGURGURG
224 'set-default-stemlen' sets direction too
226 if (!get_direction ())
227 set_direction (get_default_dir ());
230 stems in unnatural (forced) direction should be shortened,
231 according to [Roush & Gourlay]
233 if (((int)chord_start_f ())
234 && (get_direction () != get_default_dir ()))
235 length_f -= shorten_f;
237 if (flag_i () >= 5)
238 length_f += 2.0;
239 if (flag_i () >= 6)
240 length_f += 1.0;
242 set_stemend ((get_direction () > 0) ? head_positions()[BIGGER] + length_f:
243 head_positions()[SMALLER] - length_f);
245 bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
246 if (!grace_b && !no_extend_b && (get_direction () * stem_end_f () < 0))
247 set_stemend (0);
251 Stem::flag_i () const
253 SCM s = get_elt_property ("duration-log");
254 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
257 //xxx
258 void
259 Stem::set_default_extents ()
261 if (yextent_.empty_b ())
262 set_default_stemlen ();
265 void
266 Stem::set_noteheads ()
268 if (!first_head ())
269 return;
271 Link_array<Score_element> heads =
272 Group_interface__extract_elements (this, (Score_element*)0, "heads");
274 heads.sort (compare_position);
275 Direction dir =get_direction ();
277 if (dir < 0)
278 heads.reverse ();
281 Real w = support_head ()->extent (X_AXIS)[dir];
282 for (int i=0; i < heads.size (); i++)
284 heads[i]->translate_axis (w - heads[i]->extent (X_AXIS)[dir], X_AXIS);
287 bool parity= true;
288 int lastpos = int (Staff_symbol_referencer_interface (heads[0]).position_f ());
289 for (int i=1; i < heads.size (); i ++)
291 Real p = Staff_symbol_referencer_interface (heads[i]).position_f ();
292 int dy =abs (lastpos- (int)p);
294 if (dy <= 1)
296 if (parity)
298 Real l = heads[i]->extent (X_AXIS).length ();
299 heads[i]->translate_axis (l * get_direction (), X_AXIS);
301 parity = !parity;
303 else
304 parity = true;
306 lastpos = int (p);
310 void
311 Stem::do_pre_processing ()
313 if (yextent_.empty_b ())
314 set_default_extents ();
315 set_noteheads ();
317 if (invisible_b ())
319 set_elt_property ("transparent", SCM_BOOL_T);
320 set_empty (Y_AXIS);
321 set_empty (X_AXIS);
324 set_spacing_hints ();
330 set stem directions for hinting the optical spacing correction.
332 Modifies DIR_LIST property of the Stem's Score_column
334 TODO: more advanced: supply height of noteheads as well, for more advanced spacing possibilities
337 void
338 Stem::set_spacing_hints ()
340 if (!invisible_b ())
342 SCM scmdir = gh_int2scm (get_direction ());
343 SCM dirlist = column_l ()->get_elt_property ("dir-list");
344 if (dirlist == SCM_UNDEFINED)
345 dirlist = SCM_EOL;
347 if (scm_sloppy_memq (scmdir, dirlist) == SCM_EOL)
349 dirlist = gh_cons (scmdir, dirlist);
350 column_l ()->set_elt_property ("dir-list", dirlist);
355 Molecule
356 Stem::flag () const
358 String style;
359 SCM st = get_elt_property ("style");
360 if ( st != SCM_UNDEFINED)
362 style = ly_scm2string (st);
365 char c = (get_direction () == UP) ? 'u' : 'd';
366 Molecule m = lookup_l ()->afm_find (String ("flags-") + to_str (c) +
367 to_str (flag_i ()));
368 if (!style.empty_b ())
369 m.add_molecule(lookup_l ()->afm_find (String ("flags-") + to_str (c) + style));
370 return m;
373 Interval
374 Stem::dim_callback (Dimension_cache const* c)
376 Stem * s = dynamic_cast<Stem*> (c->element_l ());
378 Interval r (0, 0);
379 if (s->get_elt_property ("beam") != SCM_UNDEFINED || abs (s->flag_i ()) <= 2)
380 ; // TODO!
381 else
383 r = s->flag ().dim_.x ();
384 r += s->note_delta_f ();
386 return r;
390 const Real ANGLE = 20* (2.0*M_PI/360.0); // ugh!
392 Molecule*
393 Stem::do_brew_molecule_p () const
395 Molecule *mol_p =new Molecule;
396 Interval stem_y = yextent_;
397 Real dy = staff_symbol_referencer_interface (this)
398 .staff_line_leading_f ()/2.0;
400 Real head_wid = 0;
401 if (support_head ())
402 head_wid = support_head ()->extent (X_AXIS).length ();
403 stem_y[Direction(-get_direction ())] += get_direction () * head_wid * tan(ANGLE)/(2*dy);
405 if (!invisible_b ())
407 Real stem_width = paper_l ()->get_var ("stemthickness");
408 Molecule ss =lookup_l ()->filledbox (Box (Interval (-stem_width/2, stem_width/2),
409 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
410 mol_p->add_molecule (ss);
413 if (!beam_l () && abs (flag_i ()) > 2)
415 Molecule fl = flag ();
416 fl.translate_axis(stem_y[get_direction ()]*dy, Y_AXIS);
417 mol_p->add_molecule (fl);
420 if (first_head ())
422 mol_p->translate_axis (note_delta_f (), X_AXIS);
424 return mol_p;
427 Real
428 Stem::note_delta_f () const
430 Real r=0;
431 if (first_head ())
433 Interval head_wid(0, first_head()->extent (X_AXIS).length ());
434 Real rule_thick = paper_l ()->get_var ("stemthickness");
436 Interval stem_wid(-rule_thick/2, rule_thick/2);
437 if (get_direction () == CENTER)
438 r = head_wid.center ();
439 else
440 r = head_wid[get_direction ()] - stem_wid[get_direction ()];
442 return r;
445 Real
446 Stem::hpos_f () const
448 return note_delta_f () + Item::hpos_f ();
452 Beam*
453 Stem::beam_l ()const
455 SCM b= get_elt_property ("beam");
456 return dynamic_cast<Beam*> (unsmob_element (b));
460 // ugh still very long.
461 Stem_info
462 Stem::calc_stem_info () const
464 assert (beam_l ());
466 Direction beam_dir = beam_l ()->get_direction ();
467 if (!beam_dir)
469 programming_error ("Beam dir not set.");
470 beam_dir = UP;
473 Stem_info info;
474 Real internote_f
475 = staff_symbol_referencer_interface (this).staff_line_leading_f ()/2;
476 Real interbeam_f = paper_l ()->interbeam_f (beam_l ()->get_multiplicity ());
477 Real beam_f = gh_scm2double (beam_l ()->get_elt_property ("beam-thickness"));
479 info.idealy_f_ = chord_start_f ();
481 // for simplicity, we calculate as if dir == UP
482 info.idealy_f_ *= beam_dir;
484 bool grace_b = get_elt_property ("grace") != SCM_UNDEFINED;
485 bool no_extend_b = get_elt_property ("no-stem-extend") != SCM_UNDEFINED;
487 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
488 String type_str = grace_b ? "grace_" : "";
489 Real min_stem_f = paper_l ()->get_var (type_str + "minimum_stem_length"
490 + to_str (beam_l ()->get_multiplicity () <? stem_max)) * internote_f;
491 Real stem_f = paper_l ()->get_var (type_str + "stem_length"
492 + to_str (beam_l ()->get_multiplicity () <? stem_max)) * internote_f;
494 if (!beam_dir || (beam_dir == get_direction ()))
495 /* normal beamed stem */
497 if (beam_l ()->get_multiplicity ())
499 info.idealy_f_ += beam_f;
500 info.idealy_f_ += (beam_l ()->get_multiplicity () - 1) * interbeam_f;
502 info.miny_f_ = info.idealy_f_;
503 info.maxy_f_ = INT_MAX;
505 info.idealy_f_ += stem_f;
506 info.miny_f_ += min_stem_f;
509 lowest beam of (UP) beam must never be lower than second staffline
511 Hmm, reference (Wanske?)
513 Although this (additional) rule is probably correct,
514 I expect that highest beam (UP) should also never be lower
515 than middle staffline, just as normal stems.
518 if (!grace_b && !no_extend_b)
520 //highest beam of (UP) beam must never be lower than middle staffline
521 info.miny_f_ = info.miny_f_ >? 0;
522 //lowest beam of (UP) beam must never be lower than second staffline
523 info.miny_f_ = info.miny_f_ >? (- 2 * internote_f - beam_f
524 + (beam_l ()->get_multiplicity () > 0) * beam_f + interbeam_f * (beam_l ()->get_multiplicity () - 1));
527 else
528 /* knee */
530 info.idealy_f_ -= beam_f;
531 info.maxy_f_ = info.idealy_f_;
532 info.miny_f_ = -INT_MAX;
534 info.idealy_f_ -= stem_f;
535 info.maxy_f_ -= min_stem_f;
538 info.idealy_f_ = info.maxy_f_ <? info.idealy_f_;
539 info.idealy_f_ = info.miny_f_ >? info.idealy_f_;
541 Real interstaff_f = calc_interstaff_dist (this, beam_l ());
543 SCM s = beam_l ()->get_elt_property ("shorten");
544 if (s != SCM_UNDEFINED)
545 info.idealy_f_ -= gh_double2scm (s);
547 info.idealy_f_ += interstaff_f * beam_dir;
548 info.miny_f_ += interstaff_f * beam_dir;
549 info.maxy_f_ += interstaff_f * beam_dir;
551 return info;