+MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
+SCM
+Beam::brew_molecule (SCM smob)
+{
+ Grob *me =unsmob_grob (smob);
+
+ Molecule mol;
+ if (!gh_pair_p (me->get_grob_property ("stems")))
+ return SCM_EOL;
+ Real x0, dx;
+ Link_array<Item>stems =
+ Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
+ if (visible_stem_count (me))
+ {
+ // ugh -> use commonx
+ x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
+ dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
+ }
+ else
+ {
+ x0 = stems[0]->relative_coordinate (0, X_AXIS);
+ dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
+ }
+
+ Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
+ Real dy = pos.delta ();
+ Real dydx = dy && dx ? dy/dx : 0;
+
+ for (int i=0; i < stems.size (); i++)
+ {
+ Item *item = stems[i];
+ Item *prev = (i > 0)? stems[i-1] : 0;
+ Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
+
+ Molecule sb = stem_beams (me, item, next, prev, dydx);
+ Real x = item->relative_coordinate (0, X_AXIS) - x0;
+ sb.translate (Offset (x, x * dydx + pos[LEFT]));
+ mol.add_molecule (sb);
+ }
+
+ mol.translate_axis (x0
+ - dynamic_cast<Spanner*> (me)
+ ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
+ X_AXIS);
+
+ if (DEBUG_QUANTING)
+ {
+ /*
+ This code prints the demerits for each beam. Perhaps this
+ should be switchable for those who want to twiddle with the
+ parameters.
+ */
+ String str;
+ if (1)
+ {
+ str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
+ str += ":";
+ }
+ str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
+ "%.2f");
+
+ SCM properties = Font_interface::font_alist_chain (me);
+
+ Molecule tm = Text_item::text2molecule (me, gh_str02scm (str.ch_C ()), properties);
+ mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
+ }
+
+ return mol.smobbed_copy ();
+}
+
+int
+Beam::forced_stem_count (Grob *me)
+{
+ Link_array<Item>stems =
+ Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
+ int f = 0;
+ for (int i=0; i < stems.size (); i++)
+ {
+ Item *s = stems[i];
+
+ if (Stem::invisible_b (s))
+ continue;
+
+ if (((int)Stem::chord_start_f (s))
+ && (Stem::get_direction (s) != Stem::get_default_dir (s)))
+ f++;
+ }
+ return f;
+}
+
+
+
+
+int
+Beam::visible_stem_count (Grob *me)
+{
+ Link_array<Item>stems =
+ Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
+ int c = 0;
+ for (int i = stems.size (); i--;)
+ {
+ if (!Stem::invisible_b (stems[i]))
+ c++;
+ }
+ return c;
+}
+
+Item*
+Beam::first_visible_stem (Grob *me)
+{
+ Link_array<Item>stems =
+ Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
+
+ for (int i = 0; i < stems.size (); i++)
+ {
+ if (!Stem::invisible_b (stems[i]))
+ return stems[i];
+ }
+ return 0;
+}
+
+Item*
+Beam::last_visible_stem (Grob *me)
+{
+ Link_array<Item>stems =
+ Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
+ for (int i = stems.size (); i--;)
+ {
+ if (!Stem::invisible_b (stems[i]))
+ return stems[i];
+ }
+ return 0;
+}
+
+
+/*
+ [TODO]
+
+ handle rest under beam (do_post: beams are calculated now)
+ what about combination of collisions and rest under beam.
+
+ Should lookup
+
+ rest -> stem -> beam -> interpolate_y_position ()
+*/
+MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
+SCM
+Beam::rest_collision_callback (SCM element_smob, SCM axis)
+{
+ Grob *rest = unsmob_grob (element_smob);
+ Axis a = (Axis) gh_scm2int (axis);
+
+ assert (a == Y_AXIS);
+
+ Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
+ Grob *stem = st;
+ if (!stem)
+ return gh_double2scm (0.0);
+ Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
+ if (!beam
+ || !Beam::has_interface (beam)
+ || !Beam::visible_stem_count (beam))
+ return gh_double2scm (0.0);
+
+ // make callback for rest from this.
+ // todo: make sure this calced already.
+
+ // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
+ Interval pos (0, 0);
+ SCM s = beam->get_grob_property ("positions");
+ if (gh_pair_p (s) && gh_number_p (ly_car (s)))
+ pos = ly_scm2interval (s);
+
+ Real dy = pos.delta ();
+ // ugh -> use commonx
+ Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
+ Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
+ Real dydx = dy && dx ? dy/dx : 0;
+
+ Direction d = Stem::get_direction (stem);
+ Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
+
+ Real staff_space = Staff_symbol_referencer::staff_space (rest);
+
+
+ Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
+
+ Real minimum_dist
+ = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
+ Real dist =
+ minimum_dist + -d * (beamy - rest_dim) >? 0;
+
+ int stafflines = Staff_symbol_referencer::line_count (rest);
+
+ // move discretely by half spaces.
+ int discrete_dist = int (ceil (dist));
+
+ // move by whole spaces inside the staff.
+ if (discrete_dist < stafflines+1)
+ discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
+
+ return gh_double2scm (-d * discrete_dist);
+}
+
+
+bool
+Beam::has_interface (Grob *me)
+{
+ return me->has_interface (ly_symbol2scm ("beam-interface"));
+}
+
+
+ADD_INTERFACE (Beam, "beam-interface",
+ "A beam.
+
+#'thickness= weight of beams, in staffspace
+
+
+We take the least squares line through the ideal-length stems, and
+then damp that using
+
+ damped = tanh (slope)
+
+this gives an unquantized left and right position for the beam end.
+Then we take all combinations of quantings near these left and right
+positions, and give them a score (according to how close they are to
+the ideal slope, how close the result is to the ideal stems, etc.). We
+take the best scoring combination.
+
+",
+ "concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy direction damping flag-width-function neutral-direction positions thickness");
+