lilypond-1.1.61
[lilypond.git] / lily / beam.cc
blob43975b65dc62e8c2ff64296ac93b6e2ca71232b4
1 /*
2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1999 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 */
13 [TODO]
14 * center beam symbol
15 * less hairy code
16 * redo grouping
18 TODO:
20 The relationship Stem <-> Beam is way too hairy. Let's figure who
21 needs what, and what information should be available when.
25 #include <math.h>
27 #include "new-beaming.hh"
28 #include "proto.hh"
29 #include "dimensions.hh"
30 #include "beam.hh"
31 #include "misc.hh"
32 #include "debug.hh"
33 #include "molecule.hh"
34 #include "leastsquares.hh"
35 #include "stem.hh"
36 #include "paper-def.hh"
37 #include "lookup.hh"
39 Beam::Beam ()
41 slope_f_ = 0;
42 left_y_ = 0;
43 quantisation_ = NORMAL;
44 multiple_i_ = 0;
47 void
48 Beam::add_stem (Stem*s)
50 #if 0
51 if (!stems_.size ())
53 dim_cache_[Y_AXIS]->parent_l_ = s->dim_cache_[Y_AXIS];
55 #endif
56 stems_.push (s);
57 s->add_dependency (this);
59 assert (!s->beam_l_);
60 s->beam_l_ = this;
62 if (!spanned_drul_[LEFT])
63 set_bounds (LEFT,s);
64 else
65 set_bounds (RIGHT,s);
68 Stem_info
69 Beam::get_stem_info (Stem *s)
71 Stem_info i;
72 for (int i=0; i < sinfo_.size (); i++)
74 if (sinfo_[i].stem_l_ == s)
75 return sinfo_[i];
77 assert (false);
78 return i;
81 Molecule*
82 Beam::do_brew_molecule_p () const
84 Molecule *mol_p = new Molecule;
85 if (!sinfo_.size ())
86 return mol_p;
88 Real x0 = stems_[0]->hpos_f ();
89 for (int j=0; j <stems_.size (); j++)
91 Stem *i = stems_[j];
92 Stem * prev = (j > 0)? stems_[j-1] : 0;
93 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
95 Molecule sb = stem_beams (i, next, prev);
96 Real x = i->hpos_f ()-x0;
97 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
98 i->staff_line_leading_f ()/2 ));
99 mol_p->add_molecule (sb);
101 mol_p->translate_axis (x0
102 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
104 return mol_p;
107 Offset
108 Beam::center () const
110 Stem_info si = sinfo_[0];
112 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
113 return Offset (w, ( w* slope_f_) *
114 si.stem_l_->staff_line_leading_f ()/2);
117 void
118 Beam::do_pre_processing ()
120 if (!dir_)
121 dir_ = get_default_dir ();
124 set_direction (dir_);
127 void
128 Beam::do_print () const
130 #ifndef NPRINT
131 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
132 Spanner::do_print ();
133 #endif
136 void
137 Beam::do_post_processing ()
139 if (stems_.size () < 2)
141 warning (_ ("beam with less than two stems"));
142 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
143 return ;
145 calculate_slope ();
146 set_stemlens ();
149 void
150 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
152 if (Stem * os = dynamic_cast<Stem*> (o))
153 stems_.substitute (os,
154 dynamic_cast<Stem *> (n));
157 Interval
158 Beam::do_width () const
160 return Interval (stems_[0]->hpos_f (),
161 stems_.top ()->hpos_f ());
164 Direction
165 Beam::get_default_dir () const
167 Drul_array<int> total;
168 total[UP] = total[DOWN] = 0;
169 Drul_array<int> count;
170 count[UP] = count[DOWN] = 0;
171 Direction d = DOWN;
173 Direction beamdir;
174 for (int i=0; i <stems_.size (); i++)
175 do {
176 Stem *s = stems_[i];
177 int current = s->dir_
178 ? (1 + d * s->dir_)/2
179 : s->get_center_distance ((Direction)-d);
181 if (current)
183 total[d] += current;
184 count[d] ++;
187 } while (flip(&d) != DOWN);
190 [Ross] states that the majority of the notes dictates the
191 direction (and not the mean of "center distance")
193 But is that because it really looks better, or because he
194 wants to provide some real simple hands-on rules.
196 We have our doubts, so we simply provide all sensible alternatives.
198 If dir is not determined: up (see stem::get_default_dir ())
201 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
202 switch (a)
204 case MAJORITY:
205 beamdir = (count[UP] >= count[DOWN]) ? UP : DOWN;
206 break;
207 case MEAN:
208 // mean center distance
209 beamdir = (total[UP] >= total[DOWN]) ? UP : DOWN;
210 break;
211 default:
212 case MEDIAN:
213 // median center distance
214 if (!count[DOWN])
215 beamdir = UP;
216 if (!count[UP])
217 beamdir = DOWN;
218 else
219 beamdir = (total[UP] / count[UP] >= total[DOWN] / count[DOWN]) ? UP : DOWN;
220 break;
222 return beamdir;
225 void
226 Beam::set_direction (Direction d)
228 dir_ = d;
229 for (int i=0; i <stems_.size (); i++)
231 Stem *s = stems_[i];
232 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
234 SCM force = s->remove_elt_property (dir_forced_scm_sym);
235 if (force == SCM_BOOL_F)
236 s->dir_ = d;
241 See Documentation/tex/fonts.doc
244 void
245 Beam::solve_slope ()
247 assert (sinfo_.size () > 1);
248 DOUT << "Beam::solve_slope: \n";
250 Least_squares l;
251 for (int i=0; i < sinfo_.size (); i++)
253 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
255 l.minimise (slope_f_, left_y_);
259 ugh. Naming: this doesn't check, but sets as well.
262 Real
263 Beam::check_stemlengths_f (bool set_b)
265 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
267 Real beam_f = paper_l ()->beam_thickness_f ();
268 Real staffline_f = paper_l ()->rule_thickness ();
269 Real epsilon_f = staffline_f / 8;
270 Real dy_f = 0.0;
271 for (int i=0; i < sinfo_.size (); i++)
273 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
275 // correct for knee
276 if (dir_ != sinfo_[i].dir_)
278 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
279 y -= dir_ * (beam_f / 2
280 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
281 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
282 sinfo_.top ().stem_l_->staff_symbol_l ())
283 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
284 * interbeam_f / internote_f;
287 if (set_b)
288 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
290 y *= dir_;
291 if (y > sinfo_[i].maxy_f_)
292 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
293 if (y < sinfo_[i].miny_f_)
295 // when all too short, normal stems win..
296 if (dy_f < -epsilon_f)
297 warning (_ ("weird beam shift, check your knees"));
298 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
301 return dy_f;
304 void
305 Beam::set_steminfo ()
307 if(!stems_.size ())
308 return;
310 assert (multiple_i_);
311 int total_count_i = 0;
312 int forced_count_i = 0;
313 for (int i=0; i < stems_.size (); i++)
315 Stem *s = stems_[i];
317 s->set_default_extents ();
318 if (s->invisible_b ())
319 continue;
320 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
321 forced_count_i++;
322 total_count_i++;
325 bool grace_b = get_elt_property (grace_scm_sym) != SCM_BOOL_F;
326 String type_str = grace_b ? "grace_" : "";
327 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
328 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
329 + to_str (multiple_i_ <? stem_max));
331 Real leftx = 0;
332 for (int i=0; i < stems_.size (); i++)
334 Stem *s = stems_[i];
335 if (s->invisible_b ())
336 continue;
338 Stem_info info (s, multiple_i_);
339 if (leftx == 0)
340 leftx = info.x_;
341 info.x_ -= leftx;
342 if (info.dir_ == dir_)
344 if (forced_count_i == total_count_i)
345 info.idealy_f_ -= shorten_f;
346 else if (forced_count_i > total_count_i / 2)
347 info.idealy_f_ -= shorten_f / 2;
349 sinfo_.push (info);
353 void
354 Beam::calculate_slope ()
356 set_steminfo ();
357 if (!sinfo_.size ())
358 slope_f_ = left_y_ = 0;
359 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
361 slope_f_ = 0;
362 left_y_ = sinfo_[0].idealy_f_;
363 left_y_ *= dir_;
365 else
367 solve_slope ();
368 Real solved_slope_f = slope_f_;
371 steep slope running against lengthened stem is suspect
373 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
375 // urg, these y internote-y-dimensions
376 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
378 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
379 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
380 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
381 && (slope_f_ > steep))
382 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
383 && (slope_f_ < -steep)))
385 slope_f_ = 0;
389 This neat trick is by Werner Lemberg,
390 damped = tanh (slope_f_)
391 corresponds with some tables in [Wanske]
393 SCM damp = remove_elt_property (damping_scm_sym);
394 int damping = 1; // ugh.
395 if (damp!= SCM_BOOL_F)
396 damping = gh_int2scm (SCM_CDR(damp));
398 if (damping)
399 slope_f_ = 0.6 * tanh (slope_f_) / damping;
401 quantise_dy ();
403 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
404 left_y_ += damped_slope_dy_f;
406 left_y_ *= dir_;
407 slope_f_ *= dir_;
411 void
412 Beam::quantise_dy ()
415 [Ross] (simplification of)
416 Try to set slope_f_ complying with y-span of:
417 - zero
418 - beam_f / 2 + staffline_f / 2
419 - beam_f + staffline_f
420 + n * interline
423 if (quantisation_ <= NONE)
424 return;
426 Real interline_f = stems_[0]->staff_line_leading_f ();
427 Real internote_f = interline_f / 2;
428 Real staffline_f = paper_l ()->rule_thickness ();
429 Real beam_f = paper_l ()->beam_thickness_f ();
431 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
433 // dim(y) = internote; so slope = (y/internote)/x
434 Real dy_f = dx_f * abs (slope_f_ * internote_f);
436 Real quanty_f = 0.0;
438 /* UGR. ICE in 2.8.1; bugreport filed. */
439 Array<Real> allowed_fraction (3);
440 allowed_fraction[0] = 0;
441 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
442 allowed_fraction[2] = (beam_f + staffline_f);
445 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
446 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
447 ? iv[SMALLER]
448 : iv[BIGGER];
451 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
454 static int test_pos = 0;
459 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
462 void
463 Beam::quantise_left_y (bool extend_b)
466 we only need to quantise the start of the beam as dy is quantised too
467 if extend_b then stems must *not* get shorter
470 if (quantisation_ <= NONE)
471 return;
474 ----------------------------------------------------------
475 ########
476 ########
477 ########
478 --------------########------------------------------------
479 ########
481 hang straddle sit inter hang
484 Real space = stems_[0]->staff_line_leading_f ();
485 Real internote_f = space /2;
486 Real staffline_f = paper_l ()->rule_thickness ();
487 Real beam_f = paper_l ()->beam_thickness_f ();
490 [TODO]
491 it would be nice to have all allowed positions in a runtime matrix:
492 (multiplicity, minimum_beam_dy, maximum_beam_dy)
495 Real straddle = 0;
496 Real sit = beam_f / 2 - staffline_f / 2;
497 Real inter = space / 2;
498 Real hang = space - beam_f / 2 + staffline_f / 2;
501 Put all allowed positions into an array.
502 Whether a position is allowed or not depends on
503 strictness of quantisation, multiplicity and direction.
505 For simplicity, we'll assume dir = UP and correct if
506 dir = DOWN afterwards.
509 // dim(left_y_) = internote
510 Real dy_f = dir_ * left_y_ * internote_f;
512 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
513 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
515 Array<Real> allowed_position;
516 if (quantisation_ != TEST)
518 if (quantisation_ <= NORMAL)
520 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
521 allowed_position.push (straddle);
522 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
523 allowed_position.push (sit);
524 allowed_position.push (hang);
526 else
527 // TODO: check and fix TRADITIONAL
529 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
530 allowed_position.push (straddle);
531 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
532 allowed_position.push (sit);
533 if (beamdy_f >= -staffline_f / 2)
534 allowed_position.push (hang);
537 else
539 if (test_pos == 0)
541 allowed_position.push (hang);
542 cout << "hang" << hang << "\n";
544 else if (test_pos==1)
546 allowed_position.push (straddle);
547 cout << "straddle" << straddle << endl;
549 else if (test_pos==2)
551 allowed_position.push (sit);
552 cout << "sit" << sit << endl;
554 else if (test_pos==3)
556 allowed_position.push (inter);
557 cout << "inter" << inter << endl;
561 Interval iv = quantise_iv (allowed_position, space, dy_f);
563 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
564 if (extend_b)
565 quanty_f = iv[BIGGER];
567 // dim(left_y_) = internote
568 left_y_ = dir_ * quanty_f / internote_f;
571 void
572 Beam::set_stemlens ()
574 Real staffline_f = paper_l ()->rule_thickness ();
575 // enge floots
576 Real epsilon_f = staffline_f / 8;
579 // je bent zelf eng --hwn.
580 Real dy_f = check_stemlengths_f (false);
581 for (int i = 0; i < 2; i++)
583 left_y_ += dy_f * dir_;
584 quantise_left_y (dy_f);
585 dy_f = check_stemlengths_f (true);
586 if (abs (dy_f) <= epsilon_f)
588 break;
592 test_pos++;
593 test_pos %= 4;
596 void
597 Beam::set_beaming (Beaming_info_list *beaming)
599 Direction d = LEFT;
600 for (int i=0; i < stems_.size (); i++)
604 if (stems_[i]->beams_i_drul_[d] < 0)
605 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
607 while (flip (&d) != LEFT);
612 void
613 Beam::do_add_processing ()
615 for (int i=0; i < stems_.size () ; i++)
617 Direction d = LEFT;
618 do {
619 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
620 } while ((flip (&d)) != LEFT);
623 if (stems_.size ())
625 stems_[0]->beams_i_drul_[LEFT] =0;
626 stems_.top()->beams_i_drul_[RIGHT] =0;
633 beams to go with one stem.
635 Molecule
636 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
638 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
639 (prev && !(prev->hpos_f () < here->hpos_f ())))
640 programming_error ("Beams are not left-to-right");
642 Real staffline_f = paper_l ()->rule_thickness ();
643 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
645 Real internote_f = here->staff_line_leading_f ()/2;
646 Real beam_f = paper_l ()->beam_thickness_f ();
648 Real dy = interbeam_f;
649 Real stemdx = staffline_f;
650 Real sl = slope_f_* internote_f;
651 lookup_l ()->beam (sl, 20 PT, 1 PT);
653 Molecule leftbeams;
654 Molecule rightbeams;
656 // UGH
657 Real nw_f = paper_l ()->note_width () * 0.8;
659 /* half beams extending to the left. */
660 if (prev)
662 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
663 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
665 Half beam should be one note-width,
666 but let's make sure two half-beams never touch
668 Real w = here->hpos_f () - prev->hpos_f ();
669 w = w/2 <? nw_f;
670 Molecule a;
671 if (lhalfs) // generates warnings if not
672 a = lookup_l ()->beam (sl, w, beam_f);
673 a.translate (Offset (-w, -w * sl));
674 for (int j = 0; j < lhalfs; j++)
676 Molecule b (a);
677 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
678 leftbeams.add_molecule (b);
682 if (next)
684 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
685 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
687 Real w = next->hpos_f () - here->hpos_f ();
688 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
689 a.translate_axis( - stemdx/2, X_AXIS);
690 int j = 0;
691 Real gap_f = 0;
693 SCM gap = get_elt_property (beam_gap_scm_sym);
694 if (gap != SCM_BOOL_F)
696 int gap_i = gh_scm2int (gap);
697 int nogap = rwholebeams - gap_i;
699 for (; j < nogap; j++)
701 Molecule b (a);
702 b.translate_axis (-dir_ * dy * j, Y_AXIS);
703 rightbeams.add_molecule (b);
705 // TODO: notehead widths differ for different types
706 gap_f = nw_f / 2;
707 w -= 2 * gap_f;
708 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
711 for (; j < rwholebeams; j++)
713 Molecule b (a);
714 b.translate (Offset (gap_f, -dir_ * dy * j));
715 rightbeams.add_molecule (b);
718 w = w/2 <? nw_f;
719 if (rhalfs)
720 a = lookup_l ()->beam (sl, w, beam_f);
722 for (; j < rwholebeams + rhalfs; j++)
724 Molecule b (a);
725 b.translate_axis (-dir_ * dy * j, Y_AXIS);
726 rightbeams.add_molecule (b);
730 leftbeams.add_molecule (rightbeams);
733 Does beam quanting think of the asymetry of beams?
734 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
736 return leftbeams;