2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1998, 1998 Han-Wen Nienhuys <hanwen@stack.nl>
7 Jan Nieuwenhuizen <jan@digicash.com>
26 #include "abbreviation-beam.hh"
30 #include "molecule.hh"
31 #include "leastsquares.hh"
33 #include "paper-def.hh"
35 #include "grouping.hh"
36 #include "stem-info.hh"
37 #include "main.hh" // experimental features
40 IMPLEMENT_IS_TYPE_B1 (Beam, Spanner);
48 quantisation_ = NORMAL;
56 s->add_dependency (this);
59 if (!spanned_drul_[LEFT])
66 Beam::brew_molecule_p () const
68 Molecule *mol_p = new Molecule;
70 Real internote_f = paper ()->internote_f ();
72 Real x0 = stems_[0]->hpos_f ();
73 for (int j=0; j <stems_.size (); j++)
76 Stem * prev = (j > 0)? stems_[j-1] : 0;
77 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
79 Molecule sb = stem_beams (i, next, prev);
80 Real x = i->hpos_f ()-x0;
81 sb.translate (Offset (x, (x * slope_f_ + left_y_) * internote_f));
84 mol_p->translate_axis (x0
85 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
93 Real w= (paper ()->note_width () + width ().length ())/2.0;
94 return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
98 Beam::do_pre_processing ()
105 Beam::do_print () const
108 DOUT << "slope_f_ " <<slope_f_ << "left ypos " << left_y_;
109 Spanner::do_print ();
114 Beam::do_post_processing ()
116 if (stems_.size () < 2)
118 warning (_ ("Beam with less than 2 stems"));
119 transparent_b_ = true;
127 Beam::do_substitute_dependent (Score_elem*o,Score_elem*n)
129 if (o->is_type_b (Stem::static_name ()))
130 stems_.substitute ((Stem*)o->item (), n? (Stem*) n->item ():0);
134 Beam::do_width () const
136 return Interval (stems_[0]->hpos_f (),
137 stems_.top ()->hpos_f ());
141 Beam::set_default_dir ()
143 Drul_array<int> total;
144 total[UP] = total[DOWN] = 0;
145 Drul_array<int> count;
146 count[UP] = count[DOWN] = 0;
149 for (int i=0; i <stems_.size (); i++)
152 int current = s->dir_
153 ? (1 + d * s->dir_)/2
154 : s->get_center_distance ((Direction)-d);
162 } while (flip(&d) != DOWN);
167 } while (flip(&d) != DOWN);
170 [Ross] states that the majority of the notes dictates the
171 direction (and not the mean of "center distance")
173 But is that because it really looks better, or because he
174 wants to provide some real simple hands-on rules.
179 // fixme. make runtime.
181 // dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
183 // mean centre distance
184 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
186 for (int i=0; i <stems_.size (); i++)
190 if (!s->dir_forced_b_)
196 See Documentation/tex/fonts.doc
202 should use minimum energy formulation (cf linespacing)
205 assert (multiple_i_);
206 Array<Stem_info> sinfo;
207 for (int j=0; j <stems_.size (); j++)
211 i->mult_i_ = multiple_i_;
212 i->set_default_extents ();
213 if (i->invisible_b ())
220 slope_f_ = left_y_ = 0;
221 else if (sinfo.size () == 1)
224 left_y_ = sinfo[0].idealy_f_;
228 Real leftx = sinfo[0].x_;
230 for (int i=0; i < sinfo.size (); i++)
232 sinfo[i].x_ -= leftx;
233 l.input.push (Offset (sinfo[i].x_, sinfo[i].idealy_f_));
236 l.minimise (slope_f_, left_y_);
240 for (int i=0; i < sinfo.size (); i++)
242 Real y = sinfo[i].x_ * slope_f_ + left_y_;
243 Real my = sinfo[i].miny_f_;
252 solved_slope_f_ = slope_f_;
254 This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
255 with some tables in [Wanske]
258 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
267 [Ross] (simplification of)
268 Try to set slope_f_ complying with y-span of:
270 - beam_f / 2 + staffline_f / 2
271 - beam_f + staffline_f
275 if (quantisation_ <= NONE)
278 Real interline_f = paper ()->interline_f ();
279 Real internote_f = interline_f / 2;
280 Real staffline_f = paper ()->rule_thickness ();
281 Real beam_f = paper ()->beam_thickness_f ();
283 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
285 // dim(y) = internote; so slope = (y/internote)/x
286 Real dy_f = dx_f * abs (slope_f_ * internote_f);
290 /* UGR. ICE in 2.8.1; bugreport filed. */
291 Array<Real> allowed_fraction (3);
292 allowed_fraction[0] = 0;
293 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
294 allowed_fraction[2] = (beam_f + staffline_f);
297 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
298 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
303 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
306 static int test_pos = 0;
311 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
315 Beam::quantise_left_y (bool extend_b)
318 we only need to quantise the start of the beam as dy is quantised too
319 if extend_b then stems must *not* get shorter
322 if (quantisation_ <= NONE)
326 ----------------------------------------------------------
330 --------------########------------------------------------
333 hang straddle sit inter hang
336 Real interline_f = paper ()->interline_f ();
337 Real internote_f = paper ()->internote_f ();
338 Real staffline_f = paper ()->rule_thickness ();
339 Real beam_f = paper ()->beam_thickness_f ();
343 it would be nice to have all allowed positions in a runtime matrix:
344 (multiplicity, minimum_beam_dy, maximum_beam_dy)
348 Real sit = beam_f / 2 - staffline_f / 2;
349 Real inter = interline_f / 2;
350 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
353 Put all allowed positions into an array.
354 Whether a position is allowed or not depends on
355 strictness of quantisation, multiplicity and direction.
357 For simplicity, we'll assume dir = UP and correct if
358 dir = DOWN afterwards.
361 // dim(left_y_) = internote
362 Real dy_f = dir_ * left_y_ * internote_f;
364 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
365 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
367 Array<Real> allowed_position;
368 if (quantisation_ != TEST)
370 if (quantisation_ <= NORMAL)
372 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
373 allowed_position.push (straddle);
374 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
375 allowed_position.push (sit);
376 allowed_position.push (hang);
379 // TODO: check and fix TRADITIONAL
381 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
382 allowed_position.push (straddle);
383 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
384 allowed_position.push (sit);
385 if (beamdy_f >= -staffline_f / 2)
386 allowed_position.push (hang);
393 allowed_position.push (hang);
394 cout << "hang" << hang << endl;
396 else if (test_pos==1)
398 allowed_position.push (straddle);
399 cout << "straddle" << straddle << endl;
401 else if (test_pos==2)
403 allowed_position.push (sit);
404 cout << "sit" << sit << endl;
406 else if (test_pos==3)
408 allowed_position.push (inter);
409 cout << "inter" << inter << endl;
414 // this currently never happens
415 Real q = (dy_f / interline_f - dy_i) * interline_f;
416 if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
417 allowed_position.push (inter);
420 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
422 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
424 quanty_f = iv.max ();
426 // dim(left_y_) = internote
427 left_y_ = dir_ * quanty_f / internote_f;
431 Beam::set_stemlens ()
433 Real staffline_f = paper ()->rule_thickness ();
434 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
435 Real internote_f = paper ()->internote_f ();
436 Real beam_f = paper ()->beam_thickness_f ();
439 Real epsilon_f = staffline_f / 8;
443 Damped and quantised slopes, esp. in monotone scales such as
447 will soon produce the minimal stem-length for one of the extreme
448 stems, which is wrong (and ugly). The minimum stemlength should
449 be kept rather small, in order to handle extreme beaming, such as
451 [c c' 'c] %assuming no knee
454 To avoid these short stems for normal cases, we'll correct for
455 the loss in slope, if necessary.
458 ugh, another hack. who's next?
459 Writing this all down, i realise (at last) that the Right Thing to
460 do is to assign uglyness to slope and stem-lengths and then minimise
461 the total uglyness of a beam.
462 Steep slopes are ugly, shortened stems are ugly, lengthened stems
468 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
469 Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f / 2;
470 if (abs (damped_slope_dy_f) <= epsilon_f)
471 damped_slope_dy_f = 0;
473 Real x0 = stems_[0]->hpos_f ();
476 for (int jj = 0; jj < 10; jj++)
478 left_y_ += dy_f * dir_;
479 quantise_left_y (dy_f);
481 for (int i=0; i < stems_.size (); i++)
484 if (s->transparent_b_)
487 Real x = s->hpos_f () - x0;
488 // urg move this to stem-info
489 Real sy = left_y_ + slope_f_ * x;
491 sy -= dir_ * (beam_f / 2
492 + (s->mult_i_ - 1) * interbeam_f) / internote_f;
494 Real y = s->stem_end_f () * dir_;
496 if (y > info.maxy_f_)
497 dy_f = dy_f <? info.maxy_f_ - y;
498 if (y < info.miny_f_)
500 // when all too short, normal stems win..
501 if (dy_f < -epsilon_f)
502 warning ( _("Weird beam shift, check your knees."));
503 dy_f = dy_f >? info.miny_f_ - y;
506 if (dy_f && damped_slope_dy_f
507 && (sign (dy_f) == sign (damped_slope_dy_f)))
508 dy_f += damped_slope_dy_f;
509 damped_slope_dy_f = 0;
510 if (abs (dy_f) <= epsilon_f)
512 DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
523 ugh. this is broken and should be rewritten.
527 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
531 assert (cur.children.size () == stems_.size ());
538 for (int j=0; j <stems_.size (); j++)
542 int f = s->flag_i_ - 2;
547 b= cur.generate_beams (flags, fi);
550 assert (stems_.size () == b.size ()/2);
553 for (int j=0, i=0; i < b.size () && j <stems_.size (); i+= 2, j++)
556 s->beams_left_i_ = b[i];
557 s->beams_right_i_ = b[i+1];
558 multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]);
563 beams to go with one stem.
566 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
568 assert (!next || next->hpos_f () > here->hpos_f ());
569 assert (!prev || prev->hpos_f () < here->hpos_f ());
571 Real staffline_f = paper ()->rule_thickness ();
572 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
573 Real internote_f = paper ()->internote_f ();
574 Real beam_f = paper ()->beam_thickness_f ();
576 Real dy = interbeam_f;
577 Real stemdx = staffline_f;
578 Real sl = slope_f_* internote_f;
579 paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
584 /* half beams extending to the left. */
587 int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
588 int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
589 Real w = (here->hpos_f () - prev->hpos_f ())/4 <? paper ()->note_width ();;
591 if (lhalfs) // generates warnings if not
592 a = paper ()->lookup_l ()->beam (sl, w, beam_f);
593 a.translate (Offset (-w, -w * sl));
594 for (int j = 0; j < lhalfs; j++)
597 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
604 int rhalfs = here->beams_right_i_ - next->beams_left_i_;
605 int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
607 Real w = next->hpos_f () - here->hpos_f ();
608 Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
609 a.translate_axis( - stemdx/2, X_AXIS);
612 if (here->beam_gap_i_)
614 int nogap = rwholebeams - here->beam_gap_i_;
615 for (; j < nogap; j++)
618 b.translate_axis (-dir_ * dy * j, Y_AXIS);
621 // TODO: notehead widths differ for different types
622 gap_f = paper ()->note_width () / 2;
624 a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
627 for (; j < rwholebeams; j++)
630 b.translate (Offset (gap_f, -dir_ * dy * j));
634 w = w/4 <? paper ()->note_width ();
636 a = paper ()->lookup_l ()->beam (sl, w, beam_f);
638 for (; j < rwholebeams + rhalfs; j++)
641 b.translate_axis (-dir_ * dy * j, Y_AXIS);
646 leftbeams.add (rightbeams);
649 Does beam quanting think of the asymetry of beams?
650 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.