Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / lily / beam.cc
blobe3135dadfa92609362dccfd467e5f164dde1bde7
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 Jan Nieuwenhuizen <janneke@gnu.org>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
22 TODO:
24 - Determine auto knees based on positions if it's set by the user.
26 - the code is littered with * and / staff_space calls for
27 #'positions. Consider moving to real-world coordinates?
29 Problematic issue is user tweaks (user tweaks are in staff-coordinates.)
31 Notes:
33 - Stems run to the Y-center of the beam.
35 - beam_translation is the offset between Y centers of the beam.
38 #include "beam.hh"
40 #include "beam-scoring-problem.hh"
41 #include "beaming-pattern.hh"
42 #include "directional-element-interface.hh"
43 #include "grob-array.hh"
44 #include "international.hh"
45 #include "interval-set.hh"
46 #include "item.hh"
47 #include "least-squares.hh"
48 #include "lookup.hh"
49 #include "main.hh"
50 #include "misc.hh"
51 #include "note-head.hh"
52 #include "output-def.hh"
53 #include "pointer-group-interface.hh"
54 #include "rhythmic-head.hh"
55 #include "spanner.hh"
56 #include "staff-symbol-referencer.hh"
57 #include "stem.hh"
58 #include "warn.hh"
60 #if DEBUG_BEAM_SCORING
61 #include "text-interface.hh" // debug output.
62 #include "font-interface.hh" // debug output.
63 #endif
65 #include <map>
68 Beam_stem_segment::Beam_stem_segment ()
70 max_connect_ = 1000; // infinity
71 stem_ = 0;
72 width_ = 0.0;
73 stem_x_ = 0.0;
74 rank_ = 0;
75 stem_index_ = 0;
76 dir_ = CENTER;
79 bool
80 beam_segment_less (Beam_segment const& a, Beam_segment const& b)
82 return a.horizontal_[LEFT] < b.horizontal_[LEFT];
85 Beam_segment::Beam_segment ()
87 vertical_count_ = 0;
90 void
91 Beam::add_stem (Grob *me, Grob *s)
93 if (Stem::get_beam (s))
95 programming_error ("Stem already has beam");
96 return ;
99 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
100 s->set_object ("beam", me->self_scm ());
101 add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
104 Real
105 Beam::get_beam_thickness (Grob *me)
107 return robust_scm2double (me->get_property ("beam-thickness"), 0)
108 * Staff_symbol_referencer::staff_space (me);
111 /* Return the translation between 2 adjoining beams. */
112 Real
113 Beam::get_beam_translation (Grob *me)
115 int beam_count = get_beam_count (me);
116 Real staff_space = Staff_symbol_referencer::staff_space (me);
117 Real line = Staff_symbol_referencer::line_thickness (me);
118 Real beam_thickness = get_beam_thickness (me);
119 Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
121 Real beam_translation = beam_count < 4
122 ? (2 * staff_space + line - beam_thickness) / 2.0
123 : (3 * staff_space + line - beam_thickness) / 3.0;
125 return fract * beam_translation;
128 /* Maximum beam_count. */
130 Beam::get_beam_count (Grob *me)
132 int m = 0;
134 extract_grob_set (me, "stems", stems);
135 for (vsize i = 0; i < stems.size (); i++)
137 Grob *stem = stems[i];
138 m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
140 return m;
143 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
145 Beam::calc_normal_stems (SCM smob)
147 Grob *me = unsmob_grob (smob);
149 extract_grob_set (me, "stems", stems);
150 SCM val = Grob_array::make_array ();
151 Grob_array *ga = unsmob_grob_array (val);
152 for (vsize i = 0; i < stems.size (); i++)
153 if (Stem::is_normal_stem (stems[i]))
154 ga->add (stems[i]);
156 return val;
159 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
161 Beam::calc_direction (SCM smob)
163 Grob *me = unsmob_grob (smob);
165 /* Beams with less than 2 two stems don't make much sense, but could happen
166 when you do
168 r8[ c8 r8]
172 Direction dir = CENTER;
174 int count = normal_stem_count (me);
175 if (count < 2)
177 extract_grob_set (me, "stems", stems);
178 if (stems.size () == 0)
180 me->warning (_ ("removing beam with no stems"));
181 me->suicide ();
183 return SCM_UNSPECIFIED;
185 else
187 Grob *stem = first_normal_stem (me);
190 This happens for chord tremolos.
192 if (!stem)
193 stem = stems[0];
195 if (is_direction (stem->get_property_data ("direction")))
196 dir = to_dir (stem->get_property_data ("direction"));
197 else
198 dir = to_dir (stem->get_property ("default-direction"));
202 if (count >= 1)
204 if (!dir)
205 dir = get_default_dir (me);
207 consider_auto_knees (me);
210 if (dir)
212 set_stem_directions (me, dir);
215 return scm_from_int (dir);
220 /* We want a maximal number of shared beams, but if there is choice, we
221 * take the one that is closest to the end of the stem. This is for
222 * situations like
227 * |===|
228 * |=
233 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
234 Direction left_dir,
235 Direction right_dir)
237 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
239 int best_count = 0;
240 int best_start = 0;
241 for (int i = lslice[-left_dir];
242 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
244 int count = 0;
245 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
247 int k = -right_dir * scm_to_int (scm_car (s)) + i;
248 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
249 count++;
252 if (count >= best_count)
254 best_count = count;
255 best_start = i;
259 return best_start;
262 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
264 Beam::calc_beaming (SCM smob)
266 Grob *me = unsmob_grob (smob);
268 extract_grob_set (me, "stems", stems);
270 Slice last_int;
271 last_int.set_empty ();
273 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
274 Direction last_dir = CENTER;
275 for (vsize i = 0; i < stems.size (); i++)
277 Grob *this_stem = stems[i];
278 SCM this_beaming = this_stem->get_property ("beaming");
280 Direction this_dir = get_grob_direction (this_stem);
281 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
283 int start_point = position_with_maximal_common_beams
284 (last_beaming, this_beaming,
285 last_dir ? last_dir : this_dir,
286 this_dir);
288 Direction d = LEFT;
289 Slice new_slice;
292 new_slice.set_empty ();
293 SCM s = index_get_cell (this_beaming, d);
294 for (; scm_is_pair (s); s = scm_cdr (s))
296 int new_beam_pos
297 = start_point - this_dir * scm_to_int (scm_car (s));
299 new_slice.add_point (new_beam_pos);
300 scm_set_car_x (s, scm_from_int (new_beam_pos));
303 while (flip (&d) != LEFT);
305 if (!new_slice.is_empty ())
306 last_int = new_slice;
308 else
311 FIXME: what's this for?
313 SCM s = scm_cdr (this_beaming);
314 for (; scm_is_pair (s); s = scm_cdr (s))
316 int np = -this_dir * scm_to_int (scm_car (s));
317 scm_set_car_x (s, scm_from_int (np));
318 last_int.add_point (np);
322 if (scm_ilength (scm_cdr (this_beaming)) > 0)
324 last_beaming = this_beaming;
325 last_dir = this_dir;
329 return SCM_EOL;
332 bool
333 operator <(Beam_stem_segment const &a,
334 Beam_stem_segment const &b)
336 return a.rank_ < b.rank_;
339 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
341 // TODO - should store result in a property?
342 vector<Beam_segment>
343 Beam::get_beam_segments (Grob *me_grob, Grob **common)
345 /* ugh, this has a side-effect that we need to ensure that
346 Stem #'beaming is correct */
347 (void) me_grob->get_property ("beaming");
349 Spanner *me = dynamic_cast<Spanner*> (me_grob);
351 extract_grob_set (me, "stems", stems);
352 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
354 commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS);
355 commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS);
357 *common = commonx;
359 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
360 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
362 Position_stem_segments_map stem_segments;
363 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
365 /* There are two concepts of "rank" that are used in the following code.
366 The beam_rank is the vertical position of the beam (larger numbers are
367 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
368 is the horizontal position of the segment (this is incremented by two
369 for each stem; the beam segment on the right side of the stem has
370 a higher rank (by one) than its neighbour to the left). */
371 Slice ranks;
372 for (vsize i = 0; i < stems.size (); i++)
374 Grob *stem = stems[i];
375 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
376 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
377 SCM beaming = stem->get_property ("beaming");
378 Direction d = LEFT;
381 // Find the maximum and minimum beam ranks.
382 // Given that RANKS is never reset to empty, the interval will always be
383 // smallest for the left beamlet of the first stem, and then it might grow.
384 // Do we really want this? (It only affects the tremolo gaps) --jneem
385 for (SCM s = index_get_cell (beaming, d);
386 scm_is_pair (s); s = scm_cdr (s))
388 if (!scm_is_integer (scm_car (s)))
389 continue;
391 int beam_rank = scm_to_int (scm_car (s));
392 ranks.add_point (beam_rank);
395 for (SCM s = index_get_cell (beaming, d);
396 scm_is_pair (s); s = scm_cdr (s))
398 if (!scm_is_integer (scm_car (s)))
399 continue;
401 int beam_rank = scm_to_int (scm_car (s));
402 Beam_stem_segment seg;
403 seg.stem_ = stem;
404 seg.stem_x_ = stem_x;
405 seg.rank_ = 2 * i + (d+1)/2;
406 seg.width_ = stem_width;
407 seg.stem_index_ = i;
408 seg.dir_ = d;
409 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
411 Direction stem_dir = get_grob_direction (stem);
413 seg.gapped_
414 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
415 stem_segments[beam_rank].push_back (seg);
418 while (flip (&d) != LEFT);
421 Drul_array<Real> break_overshoot
422 = robust_scm2drul (me->get_property ("break-overshoot"),
423 Drul_array<Real> (-0.5, 0.0));
425 vector<Beam_segment> segments;
426 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
427 i != stem_segments.end (); i++)
429 vector<Beam_stem_segment> segs = (*i).second;
430 vector_sort (segs, less<Beam_stem_segment> ());
432 Beam_segment current;
434 // Iterate over all of the segments of the current beam rank,
435 // merging the adjacent Beam_stem_segments into one Beam_segment
436 // when appropriate.
437 int vertical_count = (*i).first;
438 for (vsize j = 0; j < segs.size (); j++)
440 // Keeping track of the different directions here is a little tricky.
441 // segs[j].dir_ is the direction of the beam segment relative to the stem
442 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
443 // its stem) whereas event_dir refers to the edge of the beam segment that
444 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
445 // are looking at that edge of the beam segment that is furthest from its
446 // stem).
447 Direction event_dir = LEFT;
448 Beam_stem_segment const& seg = segs[j];
451 Beam_stem_segment const& neighbor_seg = segs[j + event_dir];
452 // TODO: make names clearer? --jneem
453 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
454 // on_beam_bound: whether the current segment is on the boundary of just that part
455 // of the beam with the current beam_rank
456 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
457 : seg.stem_index_ == stems.size() - 1;
458 bool on_beam_bound = (event_dir == LEFT) ? j == 0 :
459 j == segs.size () - 1;
460 bool inside_stem = (event_dir == LEFT)
461 ? seg.stem_index_ > 0
462 : seg.stem_index_ + 1 < stems.size () ;
464 bool event = on_beam_bound
465 || abs (seg.rank_ - neighbor_seg.rank_) > 1
466 || (abs (vertical_count) >= seg.max_connect_
467 || abs (vertical_count) >= neighbor_seg.max_connect_);
469 if (!event)
470 // Then this edge of the current segment is irrelevent because it will
471 // be connected with the next segment in the event_dir direction.
472 continue;
474 current.vertical_count_ = vertical_count;
475 current.horizontal_[event_dir] = seg.stem_x_;
476 if (seg.dir_ == event_dir)
477 // then we are examining the edge of a beam segment that is furthest
478 // from its stem.
480 if (on_line_bound
481 && me->get_bound (event_dir)->break_status_dir ())
483 current.horizontal_[event_dir]
484 = (robust_relative_extent (me->get_bound (event_dir),
485 commonx, X_AXIS)[RIGHT]
486 + event_dir * break_overshoot[event_dir]);
488 else
490 Grob *stem = stems[seg.stem_index_];
491 Drul_array<Real> beamlet_length =
492 robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
493 Drul_array<Real> max_proportion =
494 robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
495 Real length = beamlet_length[seg.dir_];
497 if (inside_stem)
499 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
500 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
502 length = min (length,
503 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
505 current.horizontal_[event_dir] += event_dir * length;
508 else
509 // we are examining the edge of a beam segment that is closest
510 // (ie. touching, unless there is a gap) its stem.
512 current.horizontal_[event_dir] += event_dir * seg.width_/2;
513 if (seg.gapped_)
515 current.horizontal_[event_dir] -= event_dir * gap_length;
517 if (Stem::is_invisible (seg.stem_))
520 Need to do this in case of whole notes. We don't want the
521 heads to collide with the beams.
523 extract_grob_set (seg.stem_, "note-heads", heads);
525 for (vsize k = 0; k < heads.size (); k ++)
526 current.horizontal_[event_dir]
527 = event_dir * min (event_dir * current.horizontal_[event_dir],
528 - gap_length/2
529 + event_dir
530 * heads[k]->extent (commonx,
531 X_AXIS)[-event_dir]);
536 if (event_dir == RIGHT)
538 segments.push_back (current);
539 current = Beam_segment ();
542 while (flip (&event_dir) != LEFT);
547 return segments;
550 MAKE_SCHEME_CALLBACK (Beam, print, 1);
552 Beam::print (SCM grob)
554 Spanner *me = unsmob_spanner (grob);
555 Grob *commonx = 0;
556 vector<Beam_segment> segments = get_beam_segments (me, &commonx);
558 Interval span;
559 if (normal_stem_count (me))
561 span[LEFT] = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
562 span[RIGHT] = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
564 else
566 extract_grob_set (me, "stems", stems);
567 span[LEFT] = stems[0]->relative_coordinate (commonx, X_AXIS);
568 span[RIGHT] = stems.back ()->relative_coordinate (commonx, X_AXIS);
571 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
573 SCM posns = me->get_property ("quantized-positions");
574 Interval pos;
575 if (!is_number_pair (posns))
577 programming_error ("no beam positions?");
578 pos = Interval (0, 0);
580 else
581 pos = ly_scm2realdrul (posns);
583 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
585 Real dy = pos[RIGHT] - pos[LEFT];
586 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
588 Real beam_thickness = get_beam_thickness (me);
589 Real beam_dy = get_beam_translation (me);
591 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
593 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
595 Stencil the_beam;
597 int extreme = (segments[0].vertical_count_ == 0
598 ? segments[0].vertical_count_
599 : segments.back ().vertical_count_);
601 for (vsize i = 0; i < segments.size (); i ++)
603 Real local_slope = slope;
605 Makes local slope proportional to the ratio of the length of this beam
606 to the total length.
608 if (feather_dir)
609 local_slope += (feather_dir * segments[i].vertical_count_
610 * beam_dy
611 * placements.length ()
612 / span.length ());
614 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
616 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
617 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
619 Interval weights (1 - multiplier, multiplier);
621 if (feather_dir != LEFT)
622 weights.swap ();
624 // we need two translations: the normal one and
625 // the one of the lowest segment
626 int idx[] = {i, extreme};
627 Real translations[2];
629 for (int j = 0; j < 2; j++)
630 translations[j] = slope
631 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
632 + pos.linear_combination (CENTER)
633 + beam_dy * segments[idx[j]].vertical_count_;
635 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
638 Tricky. The manipulation of the variable `weighted_average' below ensures
639 that beams with a RIGHT grow direction will start from the position of the
640 lowest segment at 0, and this error will decrease and decrease over the
641 course of the beam. Something with a LEFT grow direction, on the other
642 hand, will always start in the correct place but progressively accrue
643 error at broken places. This code shifts beams up given where they are
644 in the total span length (controlled by the variable `multiplier'). To
645 better understand what it does, try commenting it out: you'll see that
646 all of the RIGHT growing beams immediately start too low and get better
647 over line breaks, whereas all of the LEFT growing beams start just right
648 and get worse over line breaks.
650 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
652 if (segments[0].vertical_count_ < 0 && feather_dir)
653 weighted_average += beam_dy * (segments.size () - 1) * factor;
655 b.translate_axis (weighted_average, Y_AXIS);
657 the_beam.add_stencil (b);
661 #if (DEBUG_BEAM_SCORING)
662 SCM annotation = me->get_property ("annotation");
663 if (scm_is_string (annotation))
665 extract_grob_set (me, "stems", stems);
668 This code prints the demerits for each beam. Perhaps this
669 should be switchable for those who want to twiddle with the
670 parameters.
672 string str;
673 SCM properties = Font_interface::text_font_alist_chain (me);
675 properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
676 properties);
678 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
680 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
681 (me->layout ()->self_scm (), properties, annotation));
683 if (!score.is_empty ())
685 score.translate_axis (me->relative_coordinate(commonx, X_AXIS), X_AXIS);
686 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
689 #endif
691 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
692 return the_beam.smobbed_copy ();
695 Direction
696 Beam::get_default_dir (Grob *me)
698 extract_grob_set (me, "stems", stems);
700 Drul_array<Real> extremes (0.0, 0.0);
701 for (iterof (s, stems); s != stems.end (); s++)
703 Interval positions = Stem::head_positions (*s);
704 Direction d = DOWN;
707 if (sign (positions[d]) == d)
708 extremes[d] = d * max (d * positions[d], d * extremes[d]);
710 while (flip (&d) != DOWN);
713 Drul_array<int> total (0, 0);
714 Drul_array<int> count (0, 0);
716 bool force_dir = false;
717 for (vsize i = 0; i < stems.size (); i++)
719 Grob *s = stems[i];
720 Direction stem_dir = CENTER;
721 SCM stem_dir_scm = s->get_property_data ("direction");
722 if (is_direction (stem_dir_scm))
724 stem_dir = to_dir (stem_dir_scm);
725 force_dir = true;
727 else
728 stem_dir = to_dir (s->get_property ("default-direction"));
730 if (!stem_dir)
731 stem_dir = to_dir (s->get_property ("neutral-direction"));
733 if (stem_dir)
735 count[stem_dir] ++;
736 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
741 if (!force_dir)
743 if (abs (extremes[UP]) > -extremes[DOWN])
744 return DOWN;
745 else if (extremes[UP] < -extremes[DOWN])
746 return UP;
749 Direction dir = CENTER;
750 Direction d = CENTER;
751 if ((d = (Direction) sign (count[UP] - count[DOWN])))
752 dir = d;
753 else if (count[UP]
754 && count[DOWN]
755 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN]/count[DOWN])))
756 dir = d;
757 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
758 dir = d;
759 else
760 dir = to_dir (me->get_property ("neutral-direction"));
762 return dir;
765 /* Set all stems with non-forced direction to beam direction.
766 Urg: non-forced should become `without/with unforced' direction,
767 once stem gets cleaned-up. */
768 void
769 Beam::set_stem_directions (Grob *me, Direction d)
771 extract_grob_set (me, "stems", stems);
773 for (vsize i = 0; i < stems.size (); i++)
775 Grob *s = stems[i];
777 SCM forcedir = s->get_property_data ("direction");
778 if (!to_dir (forcedir))
779 set_grob_direction (s, d);
784 Only try horizontal beams for knees. No reliable detection of
785 anything else is possible here, since we don't know funky-beaming
786 settings, or X-distances (slopes!) People that want sloped
787 knee-beams, should set the directions manually.
790 TODO:
792 this routine should take into account the stemlength scoring
793 of a possible knee/nonknee beam.
795 void
796 Beam::consider_auto_knees (Grob *me)
798 SCM scm = me->get_property ("auto-knee-gap");
799 if (!scm_is_number (scm))
800 return;
802 Interval_set gaps;
804 gaps.set_full ();
806 extract_grob_set (me, "normal-stems", stems);
808 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
809 Real staff_space = Staff_symbol_referencer::staff_space (me);
811 vector<Interval> head_extents_array;
812 for (vsize i = 0; i < stems.size (); i++)
814 Grob *stem = stems[i];
816 Interval head_extents = Stem::head_positions (stem);
817 if (!head_extents.is_empty ())
819 head_extents[LEFT] += -1;
820 head_extents[RIGHT] += 1;
821 head_extents *= staff_space * 0.5;
824 We could subtract beam Y position, but this routine only
825 sets stem directions, a constant shift does not have an
826 influence.
828 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
830 if (to_dir (stem->get_property_data ("direction")))
832 Direction stemdir = to_dir (stem->get_property ("direction"));
833 head_extents[-stemdir] = -stemdir * infinity_f;
836 head_extents_array.push_back (head_extents);
838 gaps.remove_interval (head_extents);
841 Interval max_gap;
842 Real max_gap_len = 0.0;
844 for (vsize i = gaps.allowed_regions_.size () -1; i != VPOS ;i--)
846 Interval gap = gaps.allowed_regions_[i];
849 the outer gaps are not knees.
851 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
852 continue;
854 if (gap.length () >= max_gap_len)
856 max_gap_len = gap.length ();
857 max_gap = gap;
861 Real beam_translation = get_beam_translation (me);
862 Real beam_thickness = Beam::get_beam_thickness (me);
863 int beam_count = Beam::get_beam_count (me);
864 Real height_of_beams = beam_thickness / 2
865 + (beam_count - 1) * beam_translation;
866 Real threshold = scm_to_double (scm) + height_of_beams;
868 if (max_gap_len > threshold)
870 int j = 0;
871 for (vsize i = 0; i < stems.size (); i++)
873 Grob *stem = stems[i];
874 Interval head_extents = head_extents_array[j++];
876 Direction d = (head_extents.center () < max_gap.center ())
877 ? UP : DOWN;
879 stem->set_property ("direction", scm_from_int (d));
881 head_extents.intersect (max_gap);
882 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
887 /* Set stem's shorten property if unset.
889 TODO:
890 take some y-position (chord/beam/nearest?) into account
891 scmify forced-fraction
893 This is done in beam because the shorten has to be uniform over the
894 entire beam.
899 void
900 set_minimum_dy (Grob *me, Real *dy)
902 if (*dy)
905 If dy is smaller than the smallest quant, we
906 get absurd direction-sign penalties.
909 Real ss = Staff_symbol_referencer::staff_space (me);
910 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
911 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
912 Real sit = (beam_thickness - slt) / 2;
913 Real inter = 0.5;
914 Real hang = 1.0 - (beam_thickness - slt) / 2;
916 *dy = sign (*dy) * max (fabs (*dy),
917 min (min (sit, inter), hang));
923 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
925 Beam::calc_stem_shorten (SCM smob)
927 Grob *me = unsmob_grob (smob);
930 shortening looks silly for x staff beams
932 if (is_knee (me))
933 return scm_from_int (0);
935 Real forced_fraction = 1.0 * forced_stem_count (me)
936 / normal_stem_count (me);
938 int beam_count = get_beam_count (me);
940 SCM shorten_list = me->get_property ("beamed-stem-shorten");
941 if (shorten_list == SCM_EOL)
942 return scm_from_int (0);
944 Real staff_space = Staff_symbol_referencer::staff_space (me);
946 SCM shorten_elt
947 = robust_list_ref (beam_count -1, shorten_list);
948 Real shorten = scm_to_double (shorten_elt) * staff_space;
950 shorten *= forced_fraction;
953 if (shorten)
954 return scm_from_double (shorten);
956 return scm_from_double (0.0);
960 Interval
961 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
963 extract_grob_set (me, "stems", stems);
964 if (stems.empty ())
965 return default_value;
967 Interval head_positions;
968 Slice multiplicity;
969 for (vsize i = 0; i < stems.size(); i++)
971 head_positions.unite (Stem::head_positions (stems[i]));
972 multiplicity.unite (Stem::beam_multiplicity (stems[i]));
975 Direction dir = get_grob_direction (me);
976 Real y = head_positions[dir]
977 * 0.5 * Staff_symbol_referencer::staff_space (me)
978 + dir * get_beam_translation (me) * (multiplicity.length () + 1);
980 y /= Staff_symbol_referencer::staff_space (me);
981 return Interval (y,y);
986 Compute a first approximation to the beam slope.
988 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
990 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
992 Grob *me = unsmob_grob (smob);
994 int count = normal_stem_count (me);
995 Interval pos (0,0);
996 if (count < 1)
997 return ly_interval2scm (no_visible_stem_positions (me, pos));
999 vector<Real> x_posns;
1000 extract_grob_set (me, "normal-stems", stems);
1001 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
1002 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1004 Real my_y = me->relative_coordinate (commony, Y_AXIS);
1006 Grob *fvs = first_normal_stem (me);
1007 Grob *lvs = last_normal_stem (me);
1009 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
1010 + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
1011 Stem::get_stem_info (lvs).ideal_y_
1012 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
1014 Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1015 for (vsize i = 0; i < stems.size (); i++)
1017 Grob *s = stems[i];
1019 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1020 x_posns.push_back (x);
1022 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
1024 Real y = 0;
1025 Real slope = 0;
1026 Real dy = 0;
1027 Real ldy = 0.0;
1028 if (!ideal.delta ())
1030 Interval chord (Stem::chord_start_y (stems[0]),
1031 Stem::chord_start_y (stems.back ()));
1033 /* Simple beams (2 stems) on middle line should be allowed to be
1034 slightly sloped.
1036 However, if both stems reach middle line,
1037 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
1039 For that case, we apply artificial slope */
1040 if (!ideal[LEFT] && chord.delta () && count == 2)
1042 /* FIXME. -> UP */
1043 Direction d = (Direction) (sign (chord.delta ()) * UP);
1044 pos[d] = get_beam_thickness (me) / 2;
1045 pos[-d] = -pos[d];
1047 else
1048 pos = ideal;
1051 For broken beams this doesn't work well. In this case, the
1052 slope esp. of the first part of a broken beam should predict
1053 where the second part goes.
1055 ldy = pos[RIGHT] - pos[LEFT];
1057 else
1059 vector<Offset> ideals;
1060 for (vsize i = 0; i < stems.size (); i++)
1062 Grob *s = stems[i];
1063 ideals.push_back (Offset (x_posns[i],
1064 Stem::get_stem_info (s).ideal_y_
1065 + s->relative_coordinate (commony, Y_AXIS)
1066 - my_y));
1069 minimise_least_squares (&slope, &y, ideals);
1071 dy = slope * dx;
1073 set_minimum_dy (me, &dy);
1075 ldy = dy;
1076 pos = Interval (y, (y + dy));
1080 "position" is relative to the staff.
1082 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1084 me->set_property ("least-squares-dy", scm_from_double (ldy));
1085 return ly_interval2scm (pos);
1089 // Assuming V is not empty, pick a 'reasonable' point inside V.
1090 static Real
1091 point_in_interval (Interval v, Real dist)
1093 if (isinf (v[DOWN]))
1094 return v[UP] - dist;
1095 else if (isinf (v[UP]))
1096 return v[DOWN] + dist;
1097 else
1098 return v.center ();
1102 We can't combine with previous function, since check concave and
1103 slope damping comes first.
1105 TODO: we should use the concaveness to control the amount of damping
1106 applied.
1108 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
1110 Beam::shift_region_to_valid (SCM grob, SCM posns)
1112 Grob *me = unsmob_grob (grob);
1115 Code dup.
1117 vector<Real> x_posns;
1118 extract_grob_set (me, "stems", stems);
1119 extract_grob_set (me, "covered-grobs", covered);
1121 Grob *common[NO_AXES] = { me, me };
1122 for (Axis a = X_AXIS; a < NO_AXES; incr (a)) {
1123 common[a] = common_refpoint_of_array (stems, me, a);
1124 common[a] = common_refpoint_of_array (covered, common[a], a);
1126 Grob *fvs = first_normal_stem (me);
1128 if (!fvs)
1129 return posns;
1130 Interval x_span;
1131 x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS);
1132 for (vsize i = 0; i < stems.size (); i++)
1134 Grob *s = stems[i];
1136 Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
1137 x_posns.push_back (x);
1140 Grob *lvs = last_normal_stem (me);
1141 x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
1143 Drul_array<Real> pos = ly_scm2interval (posns);
1145 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1147 Real beam_dy = pos[RIGHT] - pos[LEFT];
1148 Real beam_left_y = pos[LEFT];
1149 Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0;
1152 Shift the positions so that we have a chance of finding good
1153 quants (i.e. no short stem failures.)
1155 Interval feasible_left_point;
1156 feasible_left_point.set_full ();
1158 for (vsize i = 0; i < stems.size (); i++)
1160 Grob *s = stems[i];
1161 if (Stem::is_invisible (s))
1162 continue;
1164 Direction d = get_grob_direction (s);
1165 Real left_y
1166 = Stem::get_stem_info (s).shortest_y_
1167 - slope * x_posns [i];
1170 left_y is now relative to the stem S. We want relative to
1171 ourselves, so translate:
1173 left_y
1174 += + s->relative_coordinate (common[Y_AXIS], Y_AXIS)
1175 - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1177 Interval flp;
1178 flp.set_full ();
1179 flp[-d] = left_y;
1181 feasible_left_point.intersect (flp);
1185 We have two intervals here, one for the up variant (beams goes
1186 over the collision) one for the down.
1188 Drul_array<Interval> collision_free (feasible_left_point,
1189 feasible_left_point);
1191 vector<Grob*> filtered;
1193 We only update these for objects that are too large for quanting
1194 to find a workaround. Typically, these are notes with
1195 stems, and timesig/keysig/clef, which take out the entire area
1196 inside the staff as feasible.
1198 The code below disregards the thickness and multiplicity of the
1199 beam. This should not be a problem, as the beam quanting will
1200 take care of computing the impact those exactly.
1202 Real min_y_size = 2.0;
1203 for (vsize i = 0; i < covered.size(); i++)
1205 if (!covered[i]->is_live())
1206 continue;
1208 Box b;
1209 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1210 b[a] = covered[i]->extent (common[a], a);
1212 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1213 continue;
1215 if (intersection (b[X_AXIS], x_span).is_empty ())
1216 continue;
1218 filtered.push_back (covered[i]);
1219 Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1220 if (head_stem && Stem::is_normal_stem (head_stem)
1221 && Note_head::has_interface (covered[i]))
1223 if (Stem::get_beam (head_stem))
1226 We must assume that stems are infinitely long in this
1227 case, as asking for the length of the stem typically
1228 leads to circular dependencies.
1230 This strategy assumes that we don't want to handle the
1231 collision of beams in opposite non-forced directions
1232 with this code, where shortening the stems of both
1233 would resolve the problem, eg.
1236 | |
1237 =====
1239 =====
1240 | |
1243 Such beams would need a coordinating grob to resolve
1244 the collision, since both will likely want to occupy
1245 the centerline.
1247 Direction stemdir = get_grob_direction (head_stem);
1248 b[Y_AXIS][stemdir] = stemdir * infinity_f;
1250 else
1252 // TODO - should we include the extent of the stem here?
1256 if (b[Y_AXIS].length () < min_y_size)
1257 continue;
1259 Direction d = LEFT;
1262 Real x = b[X_AXIS][d] - x_span[LEFT];
1263 Real dy = slope * x;
1265 Direction yd = DOWN;
1268 Real left_y = b[Y_AXIS][yd];
1270 if (left_y == yd * infinity_f)
1272 collision_free[yd].set_empty ();
1273 continue;
1276 left_y -= dy;
1278 // Translate back to beam as ref point.
1279 left_y -= -me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1281 Interval allowed;
1282 allowed.set_full ();
1284 allowed[-yd] = left_y;
1285 collision_free[yd].intersect (allowed);
1287 while (flip (&yd) != DOWN);
1289 while (flip (&d) != LEFT);
1292 Grob_array *arr =
1293 Pointer_group_interface::get_grob_array (me,
1294 ly_symbol2scm ("covered-grobs"));
1295 arr->set_array (filtered);
1297 if (collision_free[DOWN].contains (beam_left_y)
1298 || collision_free[UP].contains (beam_left_y))
1300 // We're good to go. Do nothing.
1302 else if (!collision_free[DOWN].is_empty ()
1303 || !collision_free[UP].is_empty ())
1305 // We have space above or below collisions (or, no collisions at
1306 // all).
1307 Interval best =
1308 (collision_free[DOWN].length () > collision_free[UP].length ()) ?
1309 collision_free[DOWN] : collision_free[UP];
1311 beam_left_y = point_in_interval (best, 2.0);
1313 else if (!feasible_left_point.is_empty ())
1315 // We are somewhat screwed: we have a collision, but at least
1316 // there is a way to satisfy stem length constraints.
1317 beam_left_y = point_in_interval (feasible_left_point, 2.0);
1319 else
1321 // We are completely screwed.
1322 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1325 pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1326 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1328 return ly_interval2scm (pos);
1331 /* This neat trick is by Werner Lemberg,
1332 damped = tanh (slope)
1333 corresponds with some tables in [Wanske] CHECKME */
1334 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1336 Beam::slope_damping (SCM smob, SCM posns)
1338 Grob *me = unsmob_grob (smob);
1339 Drul_array<Real> pos = ly_scm2interval (posns);
1341 if (normal_stem_count (me) <= 1)
1342 return posns;
1344 SCM s = me->get_property ("damping");
1345 Real damping = scm_to_double (s);
1346 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1347 if (concaveness >= 10000)
1349 pos[LEFT] = pos[RIGHT];
1350 me->set_property ("least-squares-dy", scm_from_double (0));
1351 damping = 0;
1354 if (damping)
1356 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1358 Real dy = pos[RIGHT] - pos[LEFT];
1360 Grob *fvs = first_normal_stem (me);
1361 Grob *lvs = last_normal_stem (me);
1363 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1365 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1366 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1368 Real slope = dy && dx ? dy / dx : 0;
1370 slope = 0.6 * tanh (slope) / (damping + concaveness);
1372 Real damped_dy = slope * dx;
1374 set_minimum_dy (me, &damped_dy);
1376 pos[LEFT] += (dy - damped_dy) / 2;
1377 pos[RIGHT] -= (dy - damped_dy) / 2;
1379 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1382 return ly_interval2scm (pos);
1386 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1388 Beam::quanting (SCM smob, SCM posns)
1390 Grob *me = unsmob_grob (smob);
1391 Drul_array<Real> ys(0, 0);
1392 ys = robust_scm2drul (posns, ys);
1393 Beam_scoring_problem problem (me, ys);
1395 ys = problem.solve ();
1396 return ly_interval2scm (ys);
1401 Report slice containing the numbers that are both in (car BEAMING)
1402 and (cdr BEAMING)
1404 Slice
1405 where_are_the_whole_beams (SCM beaming)
1407 Slice l;
1409 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1411 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1413 l.add_point (scm_to_int (scm_car (s)));
1416 return l;
1419 /* Return the Y position of the stem-end, given the Y-left, Y-right
1420 in POS for stem S. This Y position is relative to S. */
1421 Real
1422 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1423 Real xl, Real xr, Direction feather_dir,
1424 Drul_array<Real> pos, bool french)
1426 Real beam_translation = get_beam_translation (me);
1427 Direction stem_dir = get_grob_direction (stem);
1429 Real dx = xr - xl;
1430 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl)/dx : 0;
1431 Real xdir = 2*relx-1;
1433 Real stem_y = linear_combination(pos, xdir);
1435 SCM beaming = stem->get_property ("beaming");
1437 Slice beam_slice (french
1438 ? where_are_the_whole_beams (beaming)
1439 : Stem::beam_multiplicity (stem));
1440 if (beam_slice.is_empty ())
1441 beam_slice = Slice (0,0);
1442 Interval beam_multiplicity(beam_slice[LEFT],
1443 beam_slice[RIGHT]);
1446 feather dir = 1 , relx 0->1 : factor 0 -> 1
1447 feather dir = 0 , relx 0->1 : factor 1 -> 1
1448 feather dir = -1, relx 0->1 : factor 1 -> 0
1450 Real feather_factor = 1;
1451 if (feather_dir > 0)
1452 feather_factor = relx;
1453 else if (feather_dir < 0)
1454 feather_factor = 1 - relx;
1456 stem_y += feather_factor * beam_translation
1457 * beam_multiplicity[Direction(((french) ? DOWN : UP)*stem_dir)];
1458 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1459 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1461 return stem_y + id;
1465 Hmm. At this time, beam position and slope are determined. Maybe,
1466 stem directions and length should set to relative to the chord's
1467 position of the beam. */
1468 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1470 Beam::set_stem_lengths (SCM smob)
1472 Grob *me = unsmob_grob (smob);
1474 /* trigger callbacks. */
1475 (void) me->get_property ("direction");
1476 (void) me->get_property ("beaming");
1478 SCM posns = me->get_property ("positions");
1480 extract_grob_set (me, "stems", stems);
1481 if (!stems.size ())
1482 return posns;
1484 Grob *common[2];
1485 for (int a = 2; a--;)
1486 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1488 Drul_array<Real> pos = ly_scm2realdrul (posns);
1489 Real staff_space = Staff_symbol_referencer::staff_space (me);
1490 scale_drul (&pos, staff_space);
1492 bool gap = false;
1493 Real thick = 0.0;
1494 if (robust_scm2int (me->get_property ("gap-count"), 0))
1496 gap = true;
1497 thick = get_beam_thickness (me);
1500 Grob *fvs = first_normal_stem (me);
1501 Grob *lvs = last_normal_stem (me);
1503 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1504 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1505 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1507 for (vsize i = 0; i < stems.size (); i++)
1509 Grob *s = stems[i];
1511 bool french = to_boolean (s->get_property ("french-beaming"));
1512 Real stem_y = calc_stem_y (me, s, common,
1513 xl, xr, feather_dir,
1514 pos, french && s != lvs && s!= fvs);
1517 Make the stems go up to the end of the beam. This doesn't matter
1518 for normal beams, but for tremolo beams it looks silly otherwise.
1520 if (gap
1521 && !Stem::is_invisible (s))
1522 stem_y += thick * 0.5 * get_grob_direction (s);
1525 Do set_stemend for invisible stems too, so tuplet brackets
1526 have a reference point for sloping
1528 Stem::set_stemend (s, 2 * stem_y / staff_space);
1531 return posns;
1534 void
1535 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1537 extract_grob_set (me, "stems", stems);
1539 Direction d = LEFT;
1540 for (vsize i = 0; i < stems.size (); i++)
1543 Don't overwrite user settings.
1547 Grob *stem = stems[i];
1548 SCM beaming_prop = stem->get_property ("beaming");
1549 if (beaming_prop == SCM_EOL
1550 || index_get_cell (beaming_prop, d) == SCM_EOL)
1552 int count = beaming->beamlet_count (i, d);
1553 if (i > 0
1554 && i + 1 < stems.size ()
1555 && Stem::is_invisible (stem))
1556 count = min (count, beaming->beamlet_count (i,-d));
1558 if ( ((i == 0 && d == LEFT)
1559 || (i == stems.size ()-1 && d == RIGHT))
1560 && stems.size () > 1
1561 && to_boolean (me->get_property ("clip-edges")))
1562 count = 0;
1564 Stem::set_beaming (stem, count, d);
1567 while (flip (&d) != LEFT);
1572 Beam::forced_stem_count (Grob *me)
1574 extract_grob_set (me, "normal-stems", stems);
1576 int f = 0;
1577 for (vsize i = 0; i < stems.size (); i++)
1579 Grob *s = stems[i];
1581 /* I can imagine counting those boundaries as a half forced stem,
1582 but let's count them full for now. */
1583 Direction defdir = to_dir (s->get_property ("default-direction"));
1585 if (abs (Stem::chord_start_y (s)) > 0.1
1586 && defdir
1587 && get_grob_direction (s) != defdir)
1588 f++;
1590 return f;
1594 Beam::normal_stem_count (Grob *me)
1596 extract_grob_set (me, "normal-stems", stems);
1597 return stems.size ();
1600 Grob *
1601 Beam::first_normal_stem (Grob *me)
1603 extract_grob_set (me, "normal-stems", stems);
1604 return stems.size () ? stems[0] : 0;
1607 Grob *
1608 Beam::last_normal_stem (Grob *me)
1610 extract_grob_set (me, "normal-stems", stems);
1611 return stems.size () ? stems.back () : 0;
1615 [TODO]
1617 handle rest under beam (do_post: beams are calculated now)
1618 what about combination of collisions and rest under beam.
1620 Should lookup
1622 rest -> stem -> beam -> interpolate_y_position ()
1624 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1626 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1628 Grob *rest = unsmob_grob (smob);
1629 if (scm_is_number (rest->get_property ("staff-position")))
1630 return scm_from_int (0);
1632 Real offset = robust_scm2double (prev_offset, 0.0);
1634 Grob *st = unsmob_grob (rest->get_object ("stem"));
1635 Grob *stem = st;
1636 if (!stem)
1637 return scm_from_double (0.0);
1638 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1639 if (!beam
1640 || !Beam::has_interface (beam)
1641 || !Beam::normal_stem_count (beam))
1642 return scm_from_double (0.0);
1644 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1645 Drul_array<Real> (0,0)));
1647 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1649 scale_drul (&pos, staff_space);
1651 Real dy = pos[RIGHT] - pos[LEFT];
1653 Drul_array<Grob*> visible_stems (first_normal_stem (beam),
1654 last_normal_stem (beam));
1655 extract_grob_set (beam, "stems", stems);
1657 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1659 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1660 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1661 Real slope = dy && dx ? dy / dx : 0;
1663 Direction d = get_grob_direction (stem);
1664 Real stem_y = pos[LEFT]
1665 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1667 Real beam_translation = get_beam_translation (beam);
1668 Real beam_thickness = Beam::get_beam_thickness (beam);
1671 TODO: this is not strictly correct for 16th knee beams.
1673 int beam_count
1674 = Stem::beam_multiplicity (stem).length () + 1;
1676 Real height_of_my_beams = beam_thickness / 2
1677 + (beam_count - 1) * beam_translation;
1678 Real beam_y = stem_y - d * height_of_my_beams;
1680 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1682 Interval rest_extent = rest->extent (rest, Y_AXIS);
1683 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1685 Real rest_dim = rest_extent[d];
1686 Real minimum_distance
1687 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1688 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1690 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1692 shift /= staff_space;
1693 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1695 /* Always move discretely by half spaces */
1696 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1698 /* Inside staff, move by whole spaces*/
1699 if ((rest_extent[d] + staff_space * shift) * d
1700 < rad
1701 || (rest_extent[-d] + staff_space * shift) * -d
1702 < rad)
1703 shift = ceil (fabs (shift)) * sign (shift);
1705 return scm_from_double (offset + staff_space * shift);
1708 bool
1709 Beam::is_knee (Grob *me)
1711 SCM k = me->get_property ("knee");
1712 if (scm_is_bool (k))
1713 return ly_scm2bool (k);
1715 bool knee = false;
1716 int d = 0;
1717 extract_grob_set (me, "stems", stems);
1718 for (vsize i = stems.size (); i--;)
1720 Direction dir = get_grob_direction (stems[i]);
1721 if (d && d != dir)
1723 knee = true;
1724 break;
1726 d = dir;
1729 me->set_property ("knee", ly_bool2scm (knee));
1731 return knee;
1734 bool
1735 Beam::is_cross_staff (Grob *me)
1737 extract_grob_set (me, "stems", stems);
1738 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1739 for (vsize i = 0; i < stems.size (); i++)
1740 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1741 return true;
1742 return false;
1745 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1747 Beam::calc_cross_staff (SCM smob)
1749 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1753 Beam::get_direction_beam_count (Grob *me, Direction d)
1755 extract_grob_set (me, "stems", stems);
1756 int bc = 0;
1758 for (vsize i = stems.size (); i--;)
1761 Should we take invisible stems into account?
1763 if (get_grob_direction (stems[i]) == d)
1764 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1767 return bc;
1770 ADD_INTERFACE (Beam,
1771 "A beam.\n"
1772 "\n"
1773 "The @code{beam-thickness} property is the weight of beams,"
1774 " measured in staffspace. The @code{direction} property is"
1775 " not user-serviceable. Use the @code{direction} property"
1776 " of @code{Stem} instead.\n"
1777 "\n"
1778 "The following properties may be set in the @code{details}"
1779 " list.\n"
1780 "\n"
1781 "@table @code\n"
1782 "@item stem-length-demerit-factor\n"
1783 "Demerit factor used for inappropriate stem lengths.\n"
1784 "@item secondary-beam-demerit\n"
1785 "Demerit used in quanting calculations for multiple"
1786 " beams.\n"
1787 "@item region-size\n"
1788 "Size of region for checking quant scores.\n"
1789 "@item beam-eps\n"
1790 "Epsilon for beam quant code to check for presence"
1791 " in gap.\n"
1792 "@item stem-length-limit-penalty\n"
1793 "Penalty for differences in stem lengths on a beam.\n"
1794 "@item damping-direction-penalty\n"
1795 "Demerit penalty applied when beam direction is different"
1796 " from damping direction.\n"
1797 "@item hint-direction-penalty\n"
1798 "Demerit penalty applied when beam direction is different"
1799 " from damping direction, but damping slope is"
1800 " <= @code{round-to-zero-slope}.\n"
1801 "@item musical-direction-factor\n"
1802 "Demerit scaling factor for difference between"
1803 " beam slope and music slope.\n"
1804 "@item ideal-slope-factor\n"
1805 "Demerit scaling factor for difference between"
1806 " beam slope and damping slope.\n"
1807 "@item round-to-zero-slope\n"
1808 "Damping slope which is considered zero for purposes of"
1809 " calculating direction penalties.\n"
1810 "@end table\n",
1812 /* properties */
1813 "annotation "
1814 "auto-knee-gap "
1815 "beamed-stem-shorten "
1816 "beaming "
1817 "beam-thickness "
1818 "break-overshoot "
1819 "clip-edges "
1820 "concaveness "
1821 "covered-grobs "
1822 "damping "
1823 "details "
1824 "direction "
1825 "gap "
1826 "gap-count "
1827 "grow-direction "
1828 "inspect-quants "
1829 "knee "
1830 "length-fraction "
1831 "least-squares-dy "
1832 "neutral-direction "
1833 "normal-stems "
1834 "positions "
1835 "quantized-positions "
1836 "shorten "
1837 "stems "