--- /dev/null
+/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 by Arkkra Enterprises */
+/* All rights reserved */
+/*
+ * Name: beamstem.c
+ *
+ * Description: This file contains functions for setting lengths of note
+ * stems, which also involves beaming considerations.
+ */
+
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+/*
+ * Several functions need to know the value of the "stemlen" parameter, so
+ * instead of them all calling vvpath, define a holding place here.
+ */
+static float Defstemsteps;
+
+static void proclist P((struct MAINLL *mainll_p, int vno));
+static void proctablist P((struct MAINLL *mainll_p, int vno));
+static int stemforced P((struct GRPSYL *gs_p, struct GRPSYL *ogs_p));
+static void setbeam P((struct GRPSYL *start_p, struct GRPSYL *end_p,
+ struct GRPSYL *ogs_p));
+static void restore_ry P((struct GRPSYL *start_p, struct GRPSYL *end_p));
+static double embedgrace P((struct GRPSYL *start_p, double b1, double b0));
+static double embedclef P((struct GRPSYL *start_p, double b1, double b0));
+static double beamoff P((struct GRPSYL *gs_p, int side, double boundary,
+ struct GRPSYL *start_p));
+static void embedrest P((struct GRPSYL *start_p, struct GRPSYL *last_p,
+ double b1, double b0));
+static double avoidothervoice P((struct GRPSYL *start_p, struct GRPSYL *last_p,
+ double b1, double b0, struct GRPSYL *ogs_p));
+static void setgroupvert P((int, struct GRPSYL *, struct GRPSYL *));
+static void settuplet P((struct GRPSYL *start_p, struct STAFF *staff_p));
+static void expgroup P((struct GRPSYL *gs_p, struct GRPSYL *ogs_p));
+static void applywith P((struct GRPSYL *gs_p, int side));
+\f
+/*
+ * Name: beamstem()
+ *
+ * Abstract: Set stem lengths for all notes that have stems or slash/alt.
+ *
+ * Returns: void
+ *
+ * Description: This function loops through the main linked list. For each
+ * linked list of groups on each visible staff, it calls proclist
+ * to set stem lengths.
+ */
+
+void
+beamstem()
+
+{
+ register struct MAINLL *mainll_p; /* point along main linked list */
+ int n; /* loop variable */
+
+
+ debug(16, "beamstem CSSpass=%d", CSSpass);
+ initstructs(); /* clean out old SSV info */
+
+ /*
+ * Loop once for each item in the main linked list. Apply any SSVs
+ * that are found.
+ */
+ for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+ if (mainll_p->str == S_SSV) {
+
+ asgnssv(mainll_p->u.ssv_p);
+
+ } else if (mainll_p->str == S_STAFF &&
+ mainll_p->u.staff_p->visible == YES &&
+ ! is_mrpt(mainll_p->u.staff_p->groups_p[0])) {
+ /*
+ * For this visible staff, call a subroutine to process
+ * each list of groups on it.
+ */
+ for (n = 0; n < MAXVOICES; n++) {
+ if (mainll_p->u.staff_p->groups_p[n] != 0) {
+ /* set global default stem steps */
+ Defstemsteps = vvpath(mainll_p->
+ u.staff_p->staffno,
+ n + 1, STEMLEN)->stemlen;
+ if (is_tab_staff(mainll_p->u.staff_p->
+ staffno)) {
+ proctablist(mainll_p, n);
+ } else {
+ proclist(mainll_p, n);
+ }
+ }
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: proclist()
+ *
+ * Abstract: Process linked list of groups.
+ *
+ * Returns: void
+ *
+ * Description: This function loops through the linked list of groups for one
+ * voice for one measure, first handling the grace groups, then
+ * doing a second loop for the nongrace groups. For each non-
+ * beamed note that needs it, it sets the stem length. For each
+ * beamed group, it calls setbeam to figure out the equation
+ * of the beam, and set the stem lengths accordingly. It also
+ * sets the relative vertical coords of the groups. These coords
+ * then get altered to include "with" lists and tuplet marks.
+ */
+
+static void
+proclist(mainll_p, vno)
+
+struct MAINLL *mainll_p; /* MLL struct for staff we're dealing with */
+int vno; /* voice we're to deal with, 0 to MAXVOICES-1 */
+
+{
+ struct GRPSYL *gs_p; /* point to first group in a linked list */
+ struct GRPSYL *ogs_p; /* point to first group in other linked list */
+ struct STAFF *staff_p; /* point to the staff it's connected to */
+ struct GRPSYL *savegs_p;/* save incoming gs_p */
+ struct GRPSYL *beamst_p;/* point at first group of a beamed set */
+ float notedist; /* distance between outer notes of a group */
+ float defsteps; /* additional default steps long to make stem*/
+ int bf; /* number of beams/flags */
+
+
+ debug(32, "proclist file=%s line=%d vno=%d", mainll_p->inputfile,
+ mainll_p->inputlineno, vno);
+ /*
+ * Set pointers to 1st group in our list and in the "other" list, as
+ * appropriate. Voices 1 and 2 (vno=0,1) refer to each other as the
+ * "other" voice. (If there is only one voice, ogs_p is set to voice 2
+ * (vno=1) which is a null pointer.) Voice 3 (vno=2) always ignores
+ * the other voices, so for it, ogs_p is a null pointer.
+ */
+ gs_p = mainll_p->u.staff_p->groups_p[ vno ];
+ ogs_p = vno == 2 ? (struct GRPSYL *)0 :
+ mainll_p->u.staff_p->groups_p[ ! vno ];
+
+ staff_p = mainll_p->u.staff_p; /* also point at staff */
+
+ /* set globals like Staffscale for use by the rest of the file */
+ set_staffscale(staff_p->staffno);
+
+ beamst_p = 0; /* prevent useless 'used before set' warnings */
+
+ /*
+ * Loop through every group, skipping rests, spaces, and nongrace
+ * groups, setting the stem length of grace groups.
+ */
+ for (savegs_p = gs_p; gs_p != 0; gs_p = gs_p->next) {
+ if (gs_p->grpcont != GC_NOTES)
+ continue;
+ if (gs_p->grpvalue == GV_NORMAL)
+ continue;
+
+ /*
+ * If we are at the start of a beamed set of groups, remember
+ * this place. Then, when we find the end of the set, call
+ * setbeam to figure out the equation of the beam and set the
+ * stem lengths.
+ */
+ if (gs_p->beamloc != NOITEM) {
+ if (gs_p->beamloc == STARTITEM)
+ beamst_p = gs_p;
+ if (gs_p->beamloc == ENDITEM)
+ setbeam(beamst_p, nextsimilar(gs_p), ogs_p);
+
+ continue;
+ }
+
+ /* if we get here, this group is not in a beamed set */
+
+ /* if not affected by CSS, do on normal pass, and only then */
+ /* if affected by CSS, do on CSS pass, and only then */
+ if (css_affects_stemtip(gs_p) != CSSpass) {
+ continue;
+ }
+
+ /*
+ * If the user specified a nonzero stem length, that's only the
+ * part of it that's not between the notes. So add the distance
+ * between the outer notes of the group. However, if they
+ * specified 0, they should get no stem.
+ */
+ if (IS_STEMLEN_KNOWN(gs_p->stemlen)) {
+ if (gs_p->stemlen != 0.0) {
+ gs_p->stemlen *= Staffscale;
+ notedist = gs_p->notelist[0].c[RY] - gs_p->
+ notelist[ gs_p->nnotes - 1 ].c[RY];
+ gs_p->stemlen += notedist;
+ }
+ continue;
+ }
+
+ /*
+ * Grace quarter notes default to just a note head and no stem.
+ * So set their stem length to 0.
+ */
+ if (gs_p->basictime == 4) {
+ gs_p->stemlen = 0;
+ continue;
+ }
+
+ /*
+ * If stemlen parm is zero, force length to zero. This will
+ * look bad for non-quarter notes, but that's what they
+ * asked for.
+ */
+ if (Defstemsteps == 0.0) {
+ gs_p->stemlen = 0.0;
+ continue;
+ }
+
+ /*
+ * Set the stems to the requested length, plus the distance
+ * between the highest and lowest note of the group, except
+ * longer for notes with more than 2 flags or beams. Unlike
+ * nongrace groups, stems need not reach the center line of
+ * the staff.
+ */
+ /* find distance between outer notes of the group */
+ notedist = gs_p->notelist[0].c[RY] -
+ gs_p->notelist[ gs_p->nnotes - 1 ].c[RY];
+
+ /* set len to default length + distance between outer notes */
+ gs_p->stemlen = (Defstemsteps * SM_STEMFACTOR) * Stepsize +
+ notedist;
+
+ bf = drmo(gs_p->basictime) - 2; /* no. of beams/flags */
+ if (bf > 2)
+ gs_p->stemlen += (bf - 2) * Smflagsep;
+ }
+
+ /*
+ * Loop through every grace group, skipping rests and spaces,
+ * setting the relative vertical coordinates.
+ */
+ setgroupvert(GV_ZERO, savegs_p, ogs_p);
+
+ /*
+ * Loop through every group, skipping rests, spaces and grace groups,
+ * setting the stem length of all nongrace groups.
+ *
+ * WARNING: The code in this loop is similar to stemroom() in
+ * setgrps.c. If you change one, you probably will need to change
+ * the other.
+ */
+ for (gs_p = savegs_p; gs_p != 0; gs_p = gs_p->next) {
+ if (gs_p->grpcont != GC_NOTES)
+ continue;
+ if (gs_p->grpvalue == GV_ZERO)
+ continue;
+ /*
+ * If this is cross staff beaming, don't do anything now. We
+ * can't do anything until the absolute vertical coords are set
+ * in absvert.c.
+ */
+ if (gs_p->beamto != CS_SAME) {
+ continue;
+ }
+
+ /*
+ * If we are at the start of a beamed set of groups, remember
+ * this place. Then, when we find the end of the set, call
+ * setbeam to figure out the equation of the beam and set the
+ * stem lengths.
+ */
+ if (gs_p->beamloc != NOITEM) {
+ if (gs_p->beamloc == STARTITEM)
+ beamst_p = gs_p;
+ if (gs_p->beamloc == ENDITEM)
+ setbeam(beamst_p, nextsimilar(gs_p), ogs_p);
+ continue;
+ }
+
+ /* if we get here, this group is not in a beamed set */
+
+ /* if not affected by CSS, do on normal pass, and only then */
+ /* if affected by CSS, do on CSS pass, and only then */
+ if (css_affects_stemtip(gs_p) != CSSpass) {
+ continue;
+ }
+
+ /*
+ * Only half notes and shorter have stems, but whole and double
+ * whole notes still need to have a pseudo stem length set if
+ * alternation beams are to be drawn between two neighboring
+ * groups, or the group has slashes.
+ */
+ if (gs_p->basictime <= 1 && gs_p->slash_alt == 0)
+ continue; /* no stem and no pseudo stem */
+
+ /*
+ * If the user specified a nonzero stem length, that's only the
+ * part of it that's not between the notes. So add the distance
+ * between the outer notes of the group. But if they specified
+ * 0, leave it as 0.
+ */
+ if (IS_STEMLEN_KNOWN(gs_p->stemlen)) {
+ if (gs_p->stemlen == 0.0)
+ continue;
+
+ gs_p->stemlen *= Staffscale;
+ notedist = gs_p->notelist[0].c[RY] -
+ gs_p->notelist[ gs_p->nnotes - 1 ].c[RY];
+ gs_p->stemlen += notedist;
+ continue;
+ }
+
+ /* if stemlen parm is zero, force length to zero */
+ if (Defstemsteps == 0.0) {
+ gs_p->stemlen = 0.0;
+ continue;
+ }
+
+ /*
+ * Set the stems initially to one octave long (or 5 stepsizes
+ * for cue notes), plus the distance between the highest and
+ * lowest note of the group, except longer for notes with more
+ * than 2 flags or beams. In any case, for normal sized notes,
+ * real stems must reach the center line of the staff in most
+ * cases.
+ */
+ /* find distance between outer notes of the group */
+ notedist = gs_p->notelist[0].c[RY] -
+ gs_p->notelist[ gs_p->nnotes - 1 ].c[RY];
+ /* set len to default length + distance between outer notes */
+ defsteps = Defstemsteps *
+ (allsmall(gs_p, gs_p) == YES ? SM_STEMFACTOR : 1.0);
+ gs_p->stemlen = defsteps * Stepsize + notedist;
+
+ /* add more, if needed, for flags/beams/slashes/alternations */
+ if (gs_p->basictime >= 8)
+ bf = drmo(gs_p->basictime) - 2; /* no. of beams/flags*/
+ else
+ bf = 0; /* none on quarter or longer */
+ bf += abs(gs_p->slash_alt); /* slashes or alternations */
+ if (gs_p->slash_alt > 0 && gs_p->basictime >= 16)
+ bf++; /* slashes need an extra one if 16, 32, ... */
+ if (bf > 2)
+ gs_p->stemlen += (bf - 2) * Flagsep;
+
+ /*
+ * If the note may have flag(s), stem up, and has dot(s), we
+ * must prevent the flag(s) from hitting the dot(s), by
+ * lengthening the stem.
+ */
+ if (gs_p->basictime >= 8 && gs_p->stemdir == UP &&
+ gs_p->dots != 0) {
+ if (gs_p->notelist[0].stepsup % 2 == 0) {
+ /* note is on a line */
+ if (gs_p->basictime == 8)
+ gs_p->stemlen += Stepsize;
+ else
+ gs_p->stemlen += 2 * Stepsize;
+ } else {
+ /* note is on a space */
+ if (gs_p->basictime > 8)
+ gs_p->stemlen += Stepsize;
+ }
+ }
+
+ /*
+ * Real (printed) stems must reach the center line for normal
+ * groups, though they need not for cue groups or voice 3 or
+ * when the stem direction has been forced the "wrong way" or
+ * when all the notes are on another staff.
+ */
+ if (gs_p->basictime >= 2 && gs_p->grpsize == GS_NORMAL &&
+ vno != 2 && stemforced(gs_p, ogs_p) == NO &&
+ NNN(gs_p) > 0) {
+
+ if (gs_p->stemdir == UP && gs_p->notelist[ gs_p->nnotes
+ - 1 ].c[RY] < -(gs_p->stemlen)) {
+ gs_p->stemlen = -gs_p->notelist[ gs_p->nnotes-1
+ ].c[RY];
+ }
+
+ if (gs_p->stemdir == DOWN && gs_p->notelist[ 0 ].c[RY]
+ > gs_p->stemlen) {
+ gs_p->stemlen = gs_p->notelist[ 0 ].c[RY];
+ }
+ }
+ }
+
+ /*
+ * Loop through every nongrace group, skipping rests and spaces,
+ * setting the relative vertical coordinates.
+ */
+ setgroupvert(GV_NORMAL, savegs_p, ogs_p);
+
+ /*
+ * Loop through every group, looking for tuplets. When encountering
+ * the first item in a tuplet, call a subroutine to figure out where
+ * the bracket should go, and based on that alter the RN or RS of
+ * the groups in the tuplet. However, if this is a tuplet whose
+ * number and bracket are not to be printed, don't call the subrountine.
+ * Also, it should not be done when there is cross staff beaming. Mup
+ * does not automatically print tuplet numbers or brackets in CSB sets.
+ */
+ for (gs_p = savegs_p; gs_p != 0; gs_p = gs_p->next) {
+ if ((gs_p->tuploc == STARTITEM || gs_p->tuploc == LONEITEM) &&
+ gs_p->beamto == CS_SAME && gs_p->printtup != PT_NEITHER)
+ settuplet(gs_p, staff_p);
+ }
+}
+\f
+/*
+ * Name: proctablist()
+ *
+ * Abstract: Process linked list of groups on a tablature staff.
+ *
+ * Returns: void
+ *
+ * Description: This function loops through the linked list of groups for one
+ * measure of a tablature staff. It sets the relative vertical
+ * coords of the groups. These coords then get altered to include
+ * "with" lists and tuplet marks.
+ */
+
+static void
+proctablist(mainll_p, vno)
+
+struct MAINLL *mainll_p; /* MLL struct for staff we're dealing with */
+int vno; /* voice we're to deal with, 0 to MAXVOICES-1 */
+
+{
+ struct GRPSYL *gs_p; /* point to first group in a linked list */
+ struct GRPSYL *ogs_p; /* point to first group in other linked list */
+ int stepdiff; /* steps between highest & lowest of a group */
+ int defsteps; /* additional default steps long to make stem*/
+ int bf; /* number of beams/flags (really slashes) */
+
+
+ debug(32, "proctablist file=%s line=%d", mainll_p->inputfile,
+ mainll_p->inputlineno);
+ /* no such thing as cross staff stemming for tab */
+ if (CSSpass == YES) {
+ return;
+ }
+
+ /*
+ * Set pointers to 1st group in our list and in the "other" list, as
+ * appropriate. Voices 1 and 2 (vno=0,1) refer to each other as the
+ * "other" voice. (If there is only one voice, ogs_p is set to voice 2
+ * (vno=1) which is a null pointer.) Voice 3 (vno=2) always ignores
+ * the other voices, so for it, ogs_p is a null pointer.
+ */
+ gs_p = mainll_p->u.staff_p->groups_p[ vno ];
+ ogs_p = vno == 2 ? (struct GRPSYL *)0 :
+ mainll_p->u.staff_p->groups_p[ ! vno ];
+
+ /*
+ * Loop through every group, setting some group vertical coordinates.
+ */
+ for ( ; gs_p != 0; gs_p = gs_p->next) {
+ /*
+ * Just as for nontablature groups, RY is always 0, the center
+ * of the staff, even if it falls outside the group's
+ * rectangle. RN and RS were set in locllnotes() and
+ * intertab() in setnotes.c.
+ */
+ gs_p->c[RY] = 0;
+
+ /*
+ * Slashes and "with" lists are allowed only if there are
+ * frets, so if there aren't any frets, skip the rest.
+ */
+ if (gs_p->grpcont != GC_NOTES || gs_p->nnotes == 0)
+ continue;
+
+ /*
+ * No tab groups have stems, but we still need to set a pseudo
+ * stem length if the group has slashes and otherwise 0.
+ */
+ if (gs_p->slash_alt == 0) {
+ gs_p->stemlen = 0; /* no slashes */
+ } else {
+ /* find distance between outer frets of the group */
+ stepdiff = gs_p->notelist[0].stepsup -
+ gs_p->notelist[ gs_p->nnotes - 1 ].stepsup;
+
+ /* default length + distance between outer notes */
+ defsteps = Defstemsteps * (allsmall(gs_p, gs_p) == YES
+ ? SM_STEMFACTOR : 1.0);
+ gs_p->stemlen = stepdiff * Stepsize * TABRATIO +
+ defsteps * Stepsize;
+
+ bf = abs(gs_p->slash_alt); /* slashes */
+ if (gs_p->basictime >= 16)
+ bf++; /* slashes need extra 1 if 16, 32, ...*/
+ if (bf > 2)
+ gs_p->stemlen += (bf - 2) * Flagsep;
+
+ if (gs_p->stemdir == UP) {
+ gs_p->c[RN] = gs_p->notelist[gs_p->nnotes - 1]
+ .c[RN] + gs_p->stemlen;
+ } else {
+ gs_p->c[RS] = gs_p->notelist[0]
+ .c[RY] - gs_p->stemlen;
+ }
+ }
+
+ /* decrease RS based on "with" lists */
+ expgroup(gs_p, ogs_p);
+ }
+}
+\f
+/*
+ * Name: stemforced()
+ *
+ * Abstract: Did the user force stem(s) to go the wrong way?
+ *
+ * Returns: YES at least one group was forced
+ * NO no groups were forced
+ *
+ * Description: This function figures out whether the user forced *gs_p's stem
+ * to go DOWN for voice 1 or UP for voice 2 when the vscheme and
+ * the other voice would normally prevent it; or if *gs_p is at
+ * the start of a beamed set, it checks this for all groups in
+ * the set.
+ */
+
+static int
+stemforced(gs_p, ogs_p)
+
+struct GRPSYL *gs_p; /* the group we are asking about */
+struct GRPSYL *ogs_p; /* first group in other voice's linked list */
+
+{
+ RATIONAL starttime; /* of the group in question */
+ RATIONAL endtime; /* of the group in question */
+ struct GRPSYL *gs2_p; /* loop through groups */
+
+
+ /* voice 3 never cares, so is never considered to be forced */
+ if (gs_p->vno == 3) {
+ return (NO);
+ }
+
+ /* grace cannot be forced */
+ if (gs_p->grpvalue == GV_ZERO) {
+ return (NO);
+ }
+
+ switch (svpath(gs_p->staffno, VSCHEME)->vscheme) {
+ case V_1:
+ return (NO); /* no forcing is needed in this vscheme */
+ case V_2OPSTEM:
+ case V_3OPSTEM:
+ /*
+ * If and only if a stem is backwards, we are forced. Note
+ * that even for the beamed case, we only have to check one
+ * group, since all stems in the set go the same direction.
+ */
+ if (gs_p->vno == 1 && gs_p->stemdir == DOWN ||
+ gs_p->vno == 2 && gs_p->stemdir == UP) {
+ return (YES);
+ }
+ return (NO);
+ }
+
+ /*
+ * We are in one of the freestem vschemes.
+ */
+
+ /* if the other voice doesn't exist, we know we were not forced */
+ if (ogs_p == 0) {
+ return (NO); /* other voice does not exist */
+ }
+
+ /* if all stems are normal, we are not forced (only need to check 1) */
+ if (gs_p->vno == 1 && gs_p->stemdir == UP ||
+ gs_p->vno == 2 && gs_p->stemdir == DOWN) {
+ return (NO);
+ }
+
+ /* check if the other voice is all spaces during this time */
+
+ /* find start time of *gs_p by summing all previous groups */
+ starttime = Zero;
+ for (gs2_p = gs_p->prev; gs2_p != 0; gs2_p = gs2_p->prev) {
+ starttime = radd(starttime, gs2_p->fulltime);
+ }
+
+ /* find end time of *gs_p (or the whole beamed set) */
+ endtime = starttime;
+ for (gs2_p = gs_p; gs2_p != 0; gs2_p = gs2_p->next) {
+ endtime = radd(endtime, gs2_p->fulltime);
+ if (gs2_p->beamloc == NOITEM || gs2_p->beamloc == ENDITEM &&
+ gs_p->grpvalue != GV_ZERO) {
+ break;
+ }
+ }
+
+ if (hasspace(ogs_p, starttime, endtime) == YES) {
+ return (NO); /* all spaces, forcing was not needed */
+ } else {
+ return (YES); /* notes/rests, we were forced */
+ }
+}
+\f
+/*
+ * Name: setbeam()
+ *
+ * Abstract: Set stem lengths for a beamed set of groups.
+ *
+ * Returns: void
+ *
+ * Description: This function uses linear regression to figure out where the
+ * best place to put the beam is, for a beamed set of groups, or
+ * two groups that are alted together. (Although there are
+ * special cases where the beam needs to be forced horizontal
+ * instead of using linear regression.) But if the user specified
+ * the stem lengths of the first and last group, it just goes with
+ * that, instead of using linear regression. It then sets the
+ * stem lengths for all the groups in the set.
+ *
+ * Groups involved in cross staff beaming should never call here.
+ * That work must be done later in absvert.c.
+ */
+
+static void
+setbeam(start_p, end_p, ogs_p)
+
+struct GRPSYL *start_p; /* first in beamed set */
+struct GRPSYL *end_p; /* after last in beamed set */
+struct GRPSYL *ogs_p; /* first group in other voice's GRPSYL list */
+
+{
+ struct GRPSYL *gs_p; /* loop through the groups in the beamed set */
+ struct GRPSYL *last_p; /* point at last valid group before end_p */
+ float sx, sy; /* sum of x and y coords of notes */
+ float xbar, ybar; /* average x and y coords of notes */
+ float top, bottom; /* numerator & denominator for finding b1 */
+ float temp; /* scratch variable */
+ float startx, endx; /* x coord of first and last note */
+ float starty, endy; /* y coord of first and last note */
+ float b0, b1; /* y intercept and slope */
+ float maxb0, minb0; /* max and min y intercepts */
+ float stemshift; /* x distance of stem from center of note */
+ float deflen; /* default len of a stem, based on basictime */
+ float shortdist; /* amount of stem shortening allowed (inches)*/
+ float x; /* x coord of a stem */
+ int css_affects_beam; /* does CSS affect the position of the beam? */
+ int all_notes_other_staff; /* all notes in all groups on other staff */
+ int one_end_forced; /* is stem len forced on one end only? */
+ int slope_forced; /* is the slope of the beam forced? */
+ float forced_slope; /* slope that the user forced */
+ int bf; /* number of beams/flags */
+ int shortest; /* basictime of shortest note in group */
+ int num; /* number of notes */
+ short *steps; /* stepsup of beamside notes */
+ int patlen; /* length of a pattern of notes */
+ int match; /* does the pattern match? */
+ int k; /* loop variable */
+ int n; /* loop variable */
+
+
+ /*
+ * Find whether CSS affects the position of the beam, and whether all
+ * groups have all their notes on the other staff. css_affects_stemtip
+ * asks (for this beamed case) whether any group's other-staff notes
+ * are stemside; that is, whether the stem points to the other staff,
+ * because then obviously the coord of the stem tip depends on where
+ * those notes are. If all of this group's notes are on the other
+ * staff, you might expect that we would have to regard the stem tip as
+ * affected even if the stem is towards the normal staff. But we
+ * prefer to pretend they aren't, so that we can handle more beamed
+ * sets on the first pass. We fake out those groups (see the comment a
+ * little later). And yet, if all the groups are this way, we do
+ * regard the beam as affected, because then we aren't going to enforce
+ * the rule about stems reaching the middle staff line.
+ */
+ /* first set normal (non-CSS) values */
+ css_affects_beam = NO;
+ all_notes_other_staff = NO;
+ if (CSSused == YES) { /* don't waste time looking if CSS not used */
+ all_notes_other_staff = YES;
+ css_affects_beam = css_affects_stemtip(start_p);
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ if (NNN(gs_p) != 0) {
+ all_notes_other_staff = NO;
+ }
+ }
+ if (all_notes_other_staff == YES) {
+ css_affects_beam = YES;
+ }
+ }
+
+ /*
+ * If the beam is not affected by CSS, handle this beamed set on the
+ * first pass only. If it is affected, handle it on the second
+ * pass only.
+ */
+ if (css_affects_beam != CSSpass) {
+ return;
+ }
+
+ /*
+ * If the beam is "not affected by CSS", there could still be groups
+ * where all the notes are CSS. We fake them out here, setting the
+ * BNOTE's RY an octave from the center line. We need some plausible
+ * value there for finding the beam position. AY hasn't been used yet,
+ * so use it as a holding area. We need to restore RY before returning
+ * from this function.
+ */
+ if (CSSused == YES && CSSpass == NO) {
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ if (NNN(gs_p) == 0) {
+ BNOTE(gs_p).c[AY] = BNOTE(gs_p).c[RY];
+ BNOTE(gs_p).c[RY] = 7 * Stepsize *
+ ((gs_p->stemdir == UP) ? -1.0 : 1.0);
+ }
+ }
+ }
+
+ last_p = 0; /* prevent useless 'used before set' warnings */
+
+ /* find the last valid group */
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ last_p = gs_p;
+ }
+
+ /*
+ * If the user specified the stem length on one end (first or last) but
+ * not the other, remember that fact. In that case we will execute the
+ * normal (both ends unforced) algorithm, but then at the last minute
+ * force the end that was given.
+ */
+ one_end_forced = IS_STEMLEN_KNOWN(start_p->stemlen) !=
+ IS_STEMLEN_KNOWN(last_p->stemlen);
+
+ /*
+ * If the user specified the stem length for the first and last groups,
+ * simply use these values to define where the beam is, and set all the
+ * stem lengths.
+ */
+ if (IS_STEMLEN_KNOWN(start_p->stemlen) &&
+ IS_STEMLEN_KNOWN(last_p->stemlen)) {
+
+ /*
+ * If the first and last groups had stemlen set to zero, force
+ * all groups to have stemlen zero, and return. No beam will
+ * be drawn.
+ */
+ if (start_p->stemlen == 0.0 && last_p->stemlen == 0.0) {
+ for (gs_p = start_p; gs_p != end_p;
+ gs_p = nextsimilar(gs_p)) {
+ gs_p->stemlen = 0.0;
+ }
+ restore_ry(start_p, end_p);
+ return;
+ }
+
+ /* they weren't both zero, so continue on finding the beam */
+ start_p->stemlen *= Staffscale;
+ stemshift = getstemshift(start_p);
+ if (start_p->stemdir == DOWN)
+ stemshift = -stemshift;
+ last_p->stemlen *= Staffscale;
+
+ /* find coords of the ends of the stems on the outer groups */
+ startx = start_p->c[AX] + stemshift;
+ endx = last_p->c[AX] + stemshift;
+ starty = BNOTE(start_p).c[RY] + start_p->stemlen *
+ (start_p->stemdir == UP ? 1.0 : -1.0);
+ endy = BNOTE(last_p).c[RY] + last_p->stemlen *
+ (last_p->stemdir == UP ? 1.0 : -1.0);
+
+ /* find slope and y intercept of line through those points */
+ b1 = (starty - endy) / (startx - endx);
+ b0 = starty - b1 * startx;
+
+ /* loop through all groups, setting stem length */
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ x = gs_p->c[AX] + stemshift; /* X coord of stem */
+
+ /* first set stemlen to beam's Y coord minus note's */
+ gs_p->stemlen = (b0 + b1 * x) - BNOTE(gs_p).c[RY];
+
+ /* if stems are down, reverse it */
+ if (gs_p->stemdir == DOWN)
+ gs_p->stemlen = -(gs_p->stemlen);
+
+ finalstemadjust(gs_p);
+ }
+
+ /* set relative vertical coords of any embedded rests */
+ embedrest(start_p, last_p, b1, b0);
+
+ restore_ry(start_p, end_p);
+ return;
+ }
+
+ /*
+ * If the user forced the beam's angle to some value, find what that is
+ * in terms of slope. Later we will force this value to be used. The
+ * 0.001 is to allow for floating point roundoff error.
+ */
+ if (fabs(start_p->beamslope - NOBEAMANGLE) < 0.001) {
+ slope_forced = NO;
+ forced_slope = 0.0; /* not used, keep lint happy */
+ } else {
+ slope_forced = YES;
+ forced_slope = tan(start_p->beamslope * PI / 180.0);
+ }
+
+ /*
+ * When both end groups have stemlen zero, we set all groups' stemlens
+ * to zero, and no beam will be drawn. Above we handled the case
+ * where the user forced both ends to zero. Here we handle the case
+ * where the ends are defaulting to zero, or one end is defaulting to
+ * zero and the user forced the other one. But don't do this if the
+ * slope is forced.
+ */
+ if (Defstemsteps == 0.0 && ! slope_forced && ( ! one_end_forced ||
+ start_p->stemlen == 0.0 || last_p->stemlen == 0.0)) {
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ gs_p->stemlen = 0.0;
+ }
+ restore_ry(start_p, end_p);
+ return;
+ }
+
+ /*
+ * Use linear regression to find the best-fit line through the centers
+ * of the notes. In this function, we will always be concerned with
+ * the X coord of the group as a whole (disregarding any notes that are
+ * on the "wrong" side of the stem) but the Y coord of the note of the
+ * group that's nearest to the beam (thus the BNOTE macro). The X
+ * coords used are absolute, but the Y coords are relative to the
+ * center line of the staff, since we don't know the absolute Y coords
+ * yet, and it wouldn't affect the result anyway.
+ *
+ * First get sum of x and y coords, to find averages.
+ */
+ sx = sy = 0;
+ num = 0;
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ sx += gs_p->c[AX];
+ sy += BNOTE(gs_p).c[RY];
+ num++; /* count number of notes */
+ }
+
+ xbar = sx / num;
+ ybar = sy / num;
+
+ /* accumulate numerator & denominator of regression formula for b1 */
+ top = bottom = 0;
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ temp = gs_p->c[AX] - xbar;
+ top += temp * (BNOTE(gs_p).c[RY] - ybar);
+ bottom += temp * temp;
+ }
+
+ b1 = top / bottom; /* slope */
+ /*
+ * We could also figure:
+ * b0 = ybar - b1 * xbar; y intercept
+ * to get the equation of the regression line: y = b0 + b1 * x
+ * but we're going to change b0 later anyway. Now, there are certain
+ * cases where we want to override the slope determined by regression,
+ * so revise b1 if that is the case.
+ */
+
+ /* if first and last notes are equal, force horizontal */
+ if (BNOTE(start_p).stepsup == BNOTE(last_p).stepsup)
+ b1 = 0.0;
+
+ /* check for more reasons to force the beam horizontal */
+ if (b1 != 0.0 && num >= 3) {
+ /* get an array of each group's beamside note's stepsup */
+ MALLOCA(short, steps, num);
+ for (n = 0, gs_p = start_p; n < num;
+ n++, gs_p = nextsimilar(gs_p)) {
+ steps[n] = BNOTE(gs_p).stepsup;
+ }
+
+ /*
+ * Check for a repeating pattern of notes. Try every possible
+ * pattern length <= half as long as set. If found, force the
+ * beam horizontal.
+ */
+ for (patlen = num / 2; patlen >= 2; patlen--) {
+ /* must be an integer number of pattern repetitions */
+ if (num % patlen != 0) {
+ continue; /* groups were left over */
+ }
+ /* see if initial pattern repeats perfectly */
+ match = YES;
+ for (n = 0; n < patlen && match == YES; n++) {
+ for (k = n + patlen; k < num; k += patlen) {
+ if (steps[k] != steps[n]) {
+ match = NO;
+ break;
+ }
+ }
+ }
+ /* if all repeats matched, force horizontal & break */
+ if (match == YES) {
+ b1 = 0.0;
+ break;
+ }
+ }
+
+ /*
+ * If still not horizontal, check for the case where all the
+ * beamside notes are the same except for just the first, or
+ * just the last, being different and in the direction
+ * opposite the stemdir. If so, force horizontal.
+ */
+ if (b1 != 0.0) {
+ /* make sure all the inner groups are the same */
+ match = YES;
+ for (n = 2; n < num - 1; n++) {
+ if (steps[n] != steps[1]) {
+ match = NO;
+ break;
+ }
+ }
+ /* if inner groups same, check the other conditions */
+ if (match == YES) {
+ if (start_p->stemdir == DOWN) {
+ if ((steps[0] > steps[1] &&
+ steps[num-1] == steps[1]) ||
+ (steps[0] == steps[1] &&
+ steps[num-1] > steps[1])) {
+ b1 = 0.0;
+ }
+ } else { /* UP */
+ if ((steps[0] < steps[1] &&
+ steps[num-1] == steps[1]) ||
+ (steps[0] == steps[1] &&
+ steps[num-1] < steps[1])) {
+ b1 = 0.0;
+ }
+ }
+ }
+ }
+ FREE(steps);
+ }
+
+ /*
+ * Find half the width of a note head; the stems will need to be
+ * shifted by that amount from the center of the notes so that they
+ * will meet the edge of the notes properly. If the stems are up,
+ * they will be on the right side of (normal) notes, else left. Set
+ * the X positions for the first and last stems. (If these are alted
+ * groups, the noteheadchar may not be 4; but this is close enough.)
+ */
+ stemshift = getstemshift(start_p);
+ if (start_p->stemdir == DOWN)
+ stemshift = -stemshift;
+ startx = start_p->c[AX] + stemshift; /* first group's stem */
+ endx = last_p->c[AX] + stemshift; /* last group's stem */
+
+ /*
+ * The original slope derived by linear regression must be adjusted in
+ * certain ways. First, override it if the user wants that; otherwise
+ * adjust according to the beamslope parameter.
+ */
+ if (slope_forced) {
+ b1 = forced_slope;
+ } else {
+ b1 = adjslope(start_p, b1, NO);
+ }
+
+ /*
+ * Calculate a new y intercept (b0). First pass parallel lines
+ * through each note, and record the maximum and minimum y intercepts
+ * that result.
+ */
+ b0 = BNOTE(start_p).c[RY] - b1 * start_p->c[AX];
+ maxb0 = minb0 = b0; /* init to value for first note */
+ /* look at rest of them */
+ for (gs_p = nextsimilar(start_p); gs_p != end_p;
+ gs_p = nextsimilar(gs_p)) {
+ b0 = BNOTE(gs_p).c[RY] - b1 * gs_p->c[AX];
+ if (b0 > maxb0)
+ maxb0 = b0;
+ else if (b0 < minb0)
+ minb0 = b0;
+ }
+
+ /*
+ * Find the basictime of the shortest note in the group, considering
+ * also any slashes or alternations on it. (Except that slash has a
+ * different meaning on grace groups, and doesn't affect their stem
+ * length.) Then set the default stem length based on that.
+ */
+ shortest = 0;
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ if (gs_p->basictime >= 8)
+ bf = drmo(gs_p->basictime) - 2; /* no. of beams/flags*/
+ else
+ bf = 0; /* none on quarter or longer */
+ if (gs_p->grpvalue == GV_NORMAL)
+ bf += abs(gs_p->slash_alt);/* slashes or alternations */
+ /*
+ * In certain cases where there are accidentals, we need to
+ * artificially increase bf to keep the beams from overlapping
+ * with the accidental.
+ */
+ if (gs_p != start_p && gs_p->stemdir == UP &&
+ gs_p->notelist[0].accidental != '\0' &&
+ gs_p->notelist[0].accidental != 'x' &&
+ b1 > 0 && bf > 1) {
+ bf += 3.5 * b1 * (Stepsize / Flagsep) * ((bf > 1) +
+ (gs_p->notelist[0].accidental == 'B'));
+ }
+ if (bf > shortest)
+ shortest = bf;
+ }
+ if (allsmall(start_p, last_p) == NO) {
+ /* at least one group has a normal size note */
+ deflen = Defstemsteps * Stepsize;
+ if (shortest > 2)
+ deflen += (shortest - 2) * Flagsep;
+ } else {
+ /* all groups have all small notes */
+ deflen = Defstemsteps * SM_STEMFACTOR * Stepsize;
+ if (shortest > 2)
+ deflen += (shortest - 2) * 4.0 * POINT * Staffscale;
+ }
+
+ /*
+ * The outer edge of the beam should be deflen steps away from the
+ * average position of the notes, as defined by the linear regression
+ * line. But don't allow any note to be closer than a certain number
+ * of steps less than that, the number as given by the stemshorten parm.
+ */
+ shortdist = vvpath(start_p->staffno, start_p->vno, STEMSHORTEN)
+ ->stemshorten * Stepsize;
+ if (start_p->stemdir == UP) {
+ if (maxb0 - minb0 > shortdist)
+ b0 = maxb0 + deflen - shortdist;
+ else
+ b0 += deflen;
+ } else { /* DOWN */
+ if (maxb0 - minb0 > shortdist)
+ b0 = minb0 - deflen + shortdist;
+ else
+ b0 -= deflen;
+ }
+
+ /*
+ * Another adjustment may be needed so that all stems will reach the
+ * center line of the staff. (Not to be done for small groups, or when
+ * all notes in all groups are on the other staff [CSS], or when
+ * some stemdirs have been forced wrong way despite the other voice, or
+ * we have alternations and no normal beams, or for voice 3.)
+ */
+ starty = b0 + b1 * startx; /* y coord near left end of beam */
+ endy = b0 + b1 * endx; /* y coord near right end of beam */
+ if (start_p->basictime >= 2 && start_p->grpsize == GS_NORMAL &&
+ stemforced(start_p, ogs_p) == NO &&
+ start_p->vno != 3 && all_notes_other_staff == NO) {
+ if (slope_forced) {
+ /* move both ends the same amount to preserve slope */
+ if (start_p->stemdir == UP) {
+ if (starty < 0) {
+ endy -= starty;
+ starty = 0;
+ }
+ if (endy < 0) {
+ starty -= endy;
+ endy = 0;
+ }
+ } else { /* DOWN */
+ if (starty > 0) {
+ endy -= starty;
+ starty = 0;
+ }
+ if (endy > 0) {
+ starty -= endy;
+ endy = 0;
+ }
+ }
+ } else {
+ /* move just the end(s) that need to be moved */
+ if (start_p->stemdir == UP) {
+ if (starty < 0)
+ starty = 0;
+ if (endy < 0)
+ endy = 0;
+ } else { /* DOWN */
+ if (starty > 0)
+ starty = 0;
+ if (endy > 0)
+ endy = 0;
+ }
+ }
+ }
+
+ /*
+ * If the first and last groups's stems now end at the center line, and
+ * the beam slope used to be nonzero, force one end to be a step beyond
+ * the center line, so that the beam will still have some slope to it.
+ * But don't do this if the user is forcing the beam's slope.
+ */
+ if ( ! slope_forced && fabs(starty) < Stdpad &&
+ fabs(endy) < Stdpad && b1 != 0.0) {
+ if (start_p->stemdir == UP) {
+ if (b1 > 0.0) {
+ endy = Stepsize;
+ } else if (b1 < 0.0) {
+ starty = Stepsize;
+ }
+ } else { /* DOWN */
+ if (b1 > 0.0) {
+ starty = -Stepsize;
+ } else if (b1 < 0.0) {
+ endy = -Stepsize;
+ }
+ }
+ }
+
+ /*
+ * If y at the ends of the beam differs by less than a step (allowing a
+ * fudge factor for roundoff error), force the beam horizontal by
+ * setting one end farther away from the notes. But don't do it if the
+ * user is forcing a particular slope.
+ */
+ if ( ! slope_forced && fabs(starty - endy) < Stepsize - 0.001) {
+ if (start_p->stemdir == UP) {
+ if (starty > endy) {
+ endy = starty;
+ } else {
+ starty = endy;
+ }
+ } else { /* DOWN */
+ if (starty < endy) {
+ endy = starty;
+ } else {
+ starty = endy;
+ }
+ }
+ }
+
+ /* recalculate slope and y intercept from (possibly) new endpoints */
+ b1 = (endy - starty) / (endx - startx); /* slope */
+ b0 = starty - b1 * startx; /* y intercept */
+ temp = b0; /* remember this value for later */
+
+ /* do some additional work for nongrace groups */
+ if (start_p->grpvalue == GV_NORMAL) {
+ /*
+ * If this is not an alted pair, there may be embedded grace
+ * notes, and we may need to lengthen our stems to avoid them.
+ */
+ if (start_p->slash_alt >= 0)
+ b0 = embedgrace(start_p, b1, b0);
+
+ /* may need to lengthen stems to avoid embedded clefs */
+ b0 = embedclef(start_p, b1, b0);
+
+ /* set relative vertical coords of any embedded rests */
+ embedrest(start_p, last_p, b1, b0);
+
+ /*
+ * If there is another voice, we might need to lengthen our
+ * stems so their notes won't run into our beam. If we had
+ * embedded rests, they would also be moved.
+ */
+ b0 = avoidothervoice(start_p, last_p, b1, b0, ogs_p);
+
+ /* update these by the amount the y intercept changed */
+ starty += temp - b0;
+ endy += temp - b0;
+ }
+
+ restore_ry(start_p, end_p);
+
+ /*
+ * If one end's stem len was forced but not the other, now is the time
+ * to apply that forcing. So in effect, we have taken the beam as
+ * determined by the normal algorithm and now we change the vertical
+ * coord of this end. If the slope was also forced, move the other
+ * end by the same amount so that the slope won't change.
+ */
+ if (one_end_forced) {
+ if (IS_STEMLEN_KNOWN(start_p->stemlen)) {
+ start_p->stemlen *= Staffscale;
+ temp = starty;
+ starty = BNOTE(start_p).c[RY] + start_p->stemlen *
+ (start_p->stemdir == UP ? 1.0 : -1.0);
+ if (slope_forced) {
+ endy += starty - temp;
+ }
+ } else {
+ last_p->stemlen *= Staffscale;
+ temp = endy;
+ endy = BNOTE(last_p).c[RY] + last_p->stemlen *
+ (last_p->stemdir == UP ? 1.0 : -1.0);
+ if (slope_forced) {
+ starty += endy - temp;
+ }
+ }
+
+ /* recalculate */
+ b1 = (endy - starty) / (endx - startx); /* slope */
+ b0 = starty - b1 * startx; /* y intercept */
+
+ /*
+ * Re-do embedded rests now that things have moved. As for the
+ * other adjustments above, we can't re-do them because they
+ * may force stem lengths to change. If things collide, too
+ * bad, the user forced the one stem length. It might be
+ * possible to avoid the collision by moving the other end,
+ * but likely not, and it's too late now anyhow.
+ */
+ embedrest(start_p, last_p, b1, b0);
+ }
+
+ /*
+ * At this point we know where to put the main beam (the one needed for
+ * eighth notes). Figure out and set the correct stem lengths for all
+ * of these beamed groups.
+ */
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ x = gs_p->c[AX] + stemshift; /* X coord of stem */
+
+ /* first set stemlen to beam's Y coord minus note's */
+ gs_p->stemlen = (b0 + b1 * x) - BNOTE(gs_p).c[RY];
+
+ /* if stems down, reverse stemlen, should make it positive */
+ if (gs_p->stemdir == DOWN) {
+ gs_p->stemlen = -(gs_p->stemlen);
+ }
+ /* but if negative length, error */
+ if (gs_p->stemlen < 0) {
+ l_ufatal(gs_p->inputfile, gs_p->inputlineno,
+ "stem length was forced negative");
+ }
+
+ finalstemadjust(gs_p);
+ }
+}
+\f
+/*
+ * Name: restore_ry()
+ *
+ * Abstract: Restore RY coordinates if need be.
+ *
+ * Returns: void
+ *
+ * Description: This function undoes what the code near the start of setbeam()
+ * did. But it doesn't have to set AY back, because it is garbage
+ * and will be overwritten later anyway.
+ */
+
+static void
+restore_ry(start_p, end_p)
+
+struct GRPSYL *start_p; /* first in beamed set */
+struct GRPSYL *end_p; /* after last in beamed set */
+
+{
+ struct GRPSYL *gs_p; /* loop through the groups in the beamed set */
+
+
+ if (CSSused == YES && CSSpass == NO) {
+ for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
+ if (NNN(gs_p) == 0) {
+ BNOTE(gs_p).c[RY] = BNOTE(gs_p).c[AY];
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: embedgrace()
+ *
+ * Abstract: Change the Y intercept if necessary for embedded grace groups.
+ *
+ * Returns: new y intercept value (may be no change)
+ *
+ * Description: When grace groups are embedded inside a set of nongrace groups,
+ * the beam(s) for the nongrace may have to be put farther away
+ * from their note heads, so that these beams won't collide with
+ * the grace groups. This function returns the new Y intercept
+ * for the equation of the nongraces' main beam, which accom-
+ * plishes this. When there aren't any embedded grace groups,
+ * or they are in certain positions, this Y intercept will be the
+ * same as the old Y intercept.
+ */
+
+static double
+embedgrace(start_p, b1, b0)
+
+struct GRPSYL *start_p; /* first group in nongrace beamed set */
+double b1; /* slope */
+double b0; /* y intercept */
+
+{
+ struct GRPSYL *gs_p; /* point to grace group being looked at */
+ struct GRPSYL *prev_p; /* point to nongrace group preceding gs_p */
+ struct GRPSYL *next_p; /* point to nongrace group following gs_p */
+ float beamthick; /* total thickness of beams and space between*/
+ float ycross; /* where grace stem would hit nongrace beam */
+
+
+ /*
+ * Loop through all the grace groups that are embedded somewhere
+ * between the first and last groups of this nongrace beamed set.
+ * If their stems point the opposite way, there is no problem. But
+ * if not, we may need to move the main beam(s) out of the way.
+ */
+ for (gs_p = start_p; gs_p->grpvalue == GV_ZERO ||
+ gs_p->beamloc != ENDITEM; gs_p = gs_p->next) {
+ if (gs_p->grpvalue == GV_NORMAL)
+ continue; /* ignore nongrace groups */
+
+ /*
+ * Find the preceding and following nongrace group. Whichever
+ * has the least (slowest) basictime, that determines how many
+ * full beams will connect those two groups. (You take log2 of
+ * it and subtract 2.)
+ */
+ prev_p = prevnongrace(gs_p);
+ next_p = nextnongrace(gs_p);
+
+ /* thickness of relevant beams at right side of grace */
+ beamthick = beamoff(next_p, PB_LEFT, gs_p->c[AE], start_p);
+
+ /*
+ * Find the AX and RY coords of the end of the grace group
+ * stem that is nearest the nongrace beam(s). Then, if this
+ * point would run into or beyond the nongrace beam(s), change
+ * the Y intercept (b0) so that it won't.
+ */
+ ycross = b1 * gs_p->c[AE] + b0;
+ if (start_p->stemdir == UP) {
+ if (ycross - beamthick < gs_p->c[RN])
+ b0 += gs_p->c[RN] - (ycross - beamthick);
+ } else { /* stemdir == DOWN */
+ if (ycross + beamthick > gs_p->c[RS])
+ b0 -= (ycross + beamthick) - gs_p->c[RS];
+ }
+
+ /* thickness of relevant beams at left side of grace */
+ beamthick = beamoff(prev_p, PB_RIGHT, gs_p->c[AW], start_p);
+
+ ycross = b1 * gs_p->c[AW] + b0;
+ if (start_p->stemdir == UP) {
+ if (ycross - beamthick < gs_p->c[RN])
+ b0 += gs_p->c[RN] - (ycross - beamthick);
+ } else { /* stemdir == DOWN */
+ if (ycross + beamthick > gs_p->c[RS])
+ b0 -= (ycross + beamthick) - gs_p->c[RS];
+ }
+ }
+
+ return (b0); /* new (possibly changed) Y intercept */
+}
+\f
+/*
+ * Name: embedclef()
+ *
+ * Abstract: Change the Y intercept if necessary for embedded clefs.
+ *
+ * Returns: new y intercept value (may be no change)
+ *
+ * Description: When clef changes occur before groups in a beamed set, the
+ * beam(s) for the set may have to be put farther away from their
+ * note heads, so that these beams won't collide with the clefs.
+ * This function returns the new Y intercept for the equation of
+ * the nongraces' main beam, which accomplishes this. When there
+ * aren't any embedded clefs, or they are in certain positions,
+ * this Y intercept will be the same as the old Y intercept.
+ */
+
+static double
+embedclef(start_p, b1, b0)
+
+struct GRPSYL *start_p; /* first group in nongrace beamed set */
+double b1; /* slope */
+double b0; /* y intercept */
+
+{
+ struct GRPSYL *gs_p; /* point to group being looked at */
+ struct GRPSYL *pbgs_p; /* group whose partial beams may impact us */
+ float north, south; /* top and bottom edge of a clef */
+ float horizontal; /* left or right edge of a clef */
+ float beamthick; /* total thickness of beams and space between*/
+ float ycross; /* where grace stem would hit nongrace beam */
+
+
+ /*
+ * Loop through all the groups between the first and last groups of
+ * this nongrace beamed set, including the last but not the first, and
+ * including any embedded graces. If any are preceded by a clef, we
+ * may need to move the beam(s) out of the way.
+ */
+ for (gs_p = start_p->next; gs_p != 0 && ! (gs_p->prev->grpvalue ==
+ GV_NORMAL && gs_p->prev->beamloc == ENDITEM);
+ gs_p = gs_p->next) {
+
+ if (gs_p->clef == NOCLEF) {
+ continue; /* ignore groups with no clef */
+ }
+
+ /* find the vertical edges of the clef */
+ (void)clefvert(gs_p->clef, YES, &north, &south);
+ north *= Staffscale;
+ south *= Staffscale;
+
+ /*
+ * Make sure the right side of the clef doesn't collide with
+ * the beams.
+ */
+ /* find right side of the clef */
+ horizontal = gs_p->c[AW] - CLEFPAD * Staffscale;
+
+ /* group whose partial beams we need to worry about */
+ pbgs_p = gs_p->grpvalue == GV_ZERO ? nextnongrace(gs_p) : gs_p;
+
+ /* thickness of relevant beams at right side of clef */
+ beamthick = beamoff(pbgs_p, PB_LEFT, horizontal, start_p);
+
+ /* Find RY where right edge of clef would hit the main beam. If
+ * that edge of clef would hit any beam, change Y intercept. */
+ ycross = b1 * horizontal + b0;
+ if (start_p->stemdir == UP) {
+ if (ycross - beamthick < north) {
+ b0 += north - (ycross - beamthick);
+ }
+ } else { /* stemdir == DOWN */
+ if (ycross + beamthick > south) {
+ b0 -= (ycross + beamthick) - south;
+ }
+ }
+
+ /*
+ * Make sure the left side of the clef doesn't collide with
+ * the beams.
+ */
+ /* find left side of the clef */
+ horizontal -= clefwidth(gs_p->clef, YES) * Staffscale;
+
+ /* group whose partial beams we need to worry about */
+ pbgs_p = prevnongrace(gs_p);
+
+ /* thickness of relevant beams at left side of clef */
+ beamthick = beamoff(pbgs_p, PB_RIGHT, horizontal, start_p);
+
+ /* Find RY where left edge of clef would hit main beam. If
+ * that edge of clef would hit any beam, change Y intercept. */
+ ycross = b1 * horizontal + b0;
+ if (start_p->stemdir == UP) {
+ if (ycross - beamthick < north) {
+ b0 += north - (ycross - beamthick);
+ }
+ } else { /* stemdir == DOWN */
+ if (ycross + beamthick > south) {
+ b0 -= (ycross + beamthick) - south;
+ }
+ }
+ }
+
+ return (b0); /* new (possibly changed) Y intercept */
+}
+\f
+/*
+ * Name: beamoff()
+ *
+ * Abstract: On one side of group, get height of beams and spaces between.
+ *
+ * Returns: height in inches
+ *
+ * Description: This function is called with a nongrace group in beamed set, to
+ * find out how many beams it has on one side of it and how high
+ * they are. If the group is the first or last in the set, the
+ * side must be the interior side. Partial beams are also figured
+ * in, if they might extend far enough to reach the "boundary"
+ * coordinate.
+ */
+
+static double
+beamoff(gs_p, side, boundary, start_p)
+
+struct GRPSYL *gs_p; /* group we are concerned with */
+int side; /* which side of the group, PB_LEFT or PB_RIGHT */
+double boundary; /* X coord of edge of thing that must not collide */
+struct GRPSYL *start_p; /* first group in nongrace beamed set */
+
+{
+ struct GRPSYL *ogs_p; /* nongrace group on "side" side of gs_p */
+ struct GRPSYL *o2gs_p; /* nongrace group on other side of gs_p */
+ int beams; /* number of beams for figuring collision */
+ int minbasic; /* minimum (longest) basictime */
+
+
+ /*
+ * If it's the left side of this group we're worried about, set ogs_p
+ * to the previous nongrace, and o2gs_p to the next. If right, do the
+ * opposite.
+ */
+ if (side == PB_LEFT) {
+ ogs_p = prevnongrace(gs_p);
+ o2gs_p = nextnongrace(gs_p);
+ } else {
+ ogs_p = nextnongrace(gs_p);
+ o2gs_p = prevnongrace(gs_p);
+ }
+
+ /*
+ * Whichever of the two groups {this group, the group on the side
+ * that we're worried about} has the least (slowest) basictime, that
+ * determines how many full beams will connect those two groups. (You
+ * take log2 of it and subtract 2.)
+ */
+ minbasic = MIN(gs_p->basictime, ogs_p->basictime);
+ if (minbasic >= 8) {
+ beams = drmo(MIN(gs_p->basictime, ogs_p->basictime)) - 2;
+ } else {
+ beams = 0; /* must be an alternation */
+ }
+
+ /* add the number of alternation beams, if any */
+ if (gs_p->slash_alt < 0) {
+ beams -= gs_p->slash_alt;
+ }
+
+ /*
+ * If our group needs more beams than the group on the requested side,
+ * and the stem is in the direction where partial beams would stick out
+ * beyond our GRPSYL boundary and the partial beams are long enough to
+ * possibly collide with the thing we're trying to avoid . . .
+ */
+ if (gs_p->basictime > ogs_p->basictime &&
+ (side == PB_LEFT && gs_p->stemdir == DOWN &&
+ gs_p->c[AW] - 5.0 * Stepsize < boundary ||
+ side == PB_RIGHT && gs_p->stemdir == UP &&
+ gs_p->c[AE] + 5.0 * Stepsize > boundary)) {
+ /*
+ * If we are the start or end of this beamed set, or we need
+ * more beams than the group on the other side . . .
+ */
+ if (gs_p->beamloc == STARTITEM || gs_p->beamloc == ENDITEM ||
+ gs_p->basictime > o2gs_p->basictime) {
+ /*
+ * We have partial beam(s); if on the side that matters
+ * to us, reset the number of beams to include partials.
+ */
+ if (pbeamside(gs_p, start_p) == side) {
+ beams = drmo(gs_p->basictime) - 2;
+ }
+ }
+ }
+
+ /*
+ * To get total beam thickness, multiply the size of one beam by the
+ * number of beams. Also add in a small fudge factor.
+ */
+ return (Flagsep * beams + Stepsize / 2.0);
+}
+\f
+/*
+ * Name: embedrest()
+ *
+ * Abstract: Set relative vertical coords of rests embedded in beamed sets.
+ *
+ * Returns: void
+ *
+ * Description: Rests' vertical coords were set in restsyl.c. But when a rest
+ * is embedded in a beamed set, its coords may have to be changed
+ * now so that it fits well.
+ */
+
+static void
+embedrest(start_p, last_p, b1, b0)
+
+struct GRPSYL *start_p; /* first group in nongrace beamed set */
+struct GRPSYL *last_p; /* last group in nongrace beamed set */
+double b1; /* slope */
+double b0; /* y intercept */
+
+{
+ struct GRPSYL *gs_p; /* point to group in the set */
+ struct GRPSYL *gp_p, *gpp_p; /* prev nongrace note, and prev to that */
+ struct GRPSYL *gn_p, *gnn_p; /* next nongrace note, and next to that */
+ int bp, bn; /* beams on gp_p and gn_p */
+ int partial; /* partial beams in our way */
+ char rchar; /* char for the rest */
+ int size; /* font size */
+ float asc, des; /* ascent and descent of a rest */
+ float beamthick; /* total thickness of beams and space between*/
+ float ycross; /* where rest would hit beam */
+ int beams; /* number of beams joining two groups */
+
+
+ /*
+ * Loop through the interior groups of this set, setting relative
+ * vertical coords of rest groups. (Outer groups are never rests.)
+ */
+ for (gs_p = start_p->next; gs_p != last_p; gs_p = gs_p->next) {
+
+ /* skip nonrests */
+ if (gs_p->grpcont != GC_REST)
+ continue;
+
+ /* skip cases where the user is forcing the coords */
+ if (gs_p->restdist != NORESTDIST)
+ continue;
+
+ rchar = restchar(gs_p->basictime);
+ size = (gs_p->grpsize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE);
+ asc = ascent(FONT_MUSIC, size, rchar) * Staffscale;
+ des = descent(FONT_MUSIC, size, rchar) * Staffscale;
+
+
+ /* find prev nongrace note group; will be in this beamed set */
+ for (gp_p = gs_p->prev; gp_p->grpcont != GC_NOTES ||
+ gp_p->grpvalue == GV_ZERO; gp_p = gp_p->prev)
+ ;
+
+ /* find prev nongrace note group to that, if any */
+ for (gpp_p = gp_p->prev; gpp_p != 0 && (gpp_p->grpcont !=
+ GC_NOTES || gpp_p->grpvalue == GV_ZERO);
+ gpp_p= gpp_p->prev)
+ ;
+ /* but if it's not in this beamed set, forget it */
+ if (gpp_p != 0 && gpp_p->beamloc != INITEM &&
+ gpp_p->beamloc != STARTITEM)
+ gpp_p = 0;
+
+
+ /* find next nongrace note group; will be in this beamed set */
+ for (gn_p = gs_p->next; gn_p->grpcont != GC_NOTES ||
+ gn_p->grpvalue == GV_ZERO; gn_p = gn_p->next)
+ ;
+
+ /* find next nongrace note group to that, if any */
+ for (gnn_p = gn_p->next; gnn_p != 0 && (gnn_p->grpcont !=
+ GC_NOTES || gnn_p->grpvalue == GV_ZERO);
+ gnn_p= gnn_p->next)
+ ;
+ /* but if it's not in this beamed set, forget it */
+ if (gnn_p != 0 && gnn_p->beamloc != INITEM &&
+ gnn_p->beamloc != ENDITEM)
+ gnn_p = 0;
+
+
+ /* get number of beams needed by prev and next */
+ bp = numbeams(gp_p->basictime);
+ bn = numbeams(gn_p->basictime);
+
+ partial = 0; /* init to no partial beams */
+
+ /*
+ * If the group just before our rest is notes, and this beamed
+ * set's stems are up, and the prev note needs more beams than
+ * the next note, we may have to deal with partial beams.
+ */
+ if (gs_p->prev->grpcont == GC_NOTES && start_p->stemdir == UP
+ && bp > bn) {
+ if (gpp_p == 0) {
+ /* definitely partial beams on this side */
+ partial = bp - bn;
+ } else {
+ /* maybe partial beams on this side */
+ if (numbeams(gpp_p->basictime) < bp &&
+ pbeamside(gp_p, start_p) == PB_RIGHT)
+ partial = bp - bn;
+ }
+ /* but if far enough away horizontally, we can ignore */
+ if (gs_p->c[AW] - gp_p->c[AE] > 1.5 * Stepsize)
+ partial = 0;
+ }
+
+ /*
+ * If the group just after our rest is notes, and this beamed
+ * set's stems are down, and the next note needs more beams than
+ * the prev note, we may have to deal with partial beams. If
+ * the next group is grace, we might fall into this block, but
+ * that's okay; the next nongrace (gn_p) will be far enough
+ * away that partial will (correctly) be forced back to 0.
+ */
+ if (gs_p->next->grpcont == GC_NOTES && start_p->stemdir == DOWN
+ && bn > bp) {
+ if (gnn_p == 0) {
+ /* definitely partial beams on this side */
+ partial = bn - bp;
+ } else {
+ /* maybe partial beams on this side */
+ if (numbeams(gnn_p->basictime) < bn &&
+ pbeamside(gn_p, start_p) == PB_LEFT)
+ partial = bn - bp;
+ }
+ /* but if far enough away horizontally, we can ignore */
+ if (gn_p->c[AW] - gs_p->c[AE] > 1.5 * Stepsize)
+ partial = 0;
+ }
+
+ /* full beams joining prev and next, plus relevant partials */
+ beams = MIN(bp, bn) + partial;
+
+ /*
+ * To get total beam thickness, multiply the size of one beam
+ * by the number of beams.
+ */
+ beamthick = Flagsep * beams;
+
+ /* find where outer beam hits our rest's X coord */
+ ycross = b1 * gs_p->c[AX] + b0;
+
+ /* find vertical coord, quantizing the results */
+ if (start_p->stemdir == UP) {
+ gs_p->c[RY] = nearestline(ycross - beamthick -
+ asc - Stepsize);
+ } else { /* stemdir == DOWN */
+ gs_p->c[RY] = nearestline(ycross + beamthick +
+ des + Stepsize);
+ }
+
+ gs_p->c[RN] = gs_p->c[RY] + asc;
+ gs_p->c[RS] = gs_p->c[RY] - des;
+ }
+}
+\f
+/*
+ * Name: avoidothervoice()
+ *
+ * Abstract: Change the Y intercept if necessary to avoid the other voice.
+ *
+ * Returns: new y intercept value (may be no change)
+ *
+ * Description: When there is another voice, its groups might collide with our
+ * voice's beams, unless we lengthen our groups' stems. This
+ * function returns the new Y intercept for the equation of the
+ * our voice's main beam, which accomplishes this. When there is
+ * no other voice, or its groups don't interfere with our beam,
+ * this Y intercept will be the same as the old Y intercept.
+ * When it changes, embedded rests' coords need to be changed too.
+ */
+
+static double
+avoidothervoice(start_p, last_p, b1, b0, ogs_p)
+
+struct GRPSYL *start_p; /* first group in nongrace beamed set */
+struct GRPSYL *last_p; /* last group in nongrace beamed set */
+double b1; /* slope */
+double b0; /* y intercept */
+struct GRPSYL *ogs_p; /* first group in the other voice */
+
+{
+ struct GRPSYL *prev_p; /* point to nongrace group preceding gs_p */
+ struct GRPSYL *prev2_p; /* point to nongrace group before that one */
+ struct GRPSYL *next_p; /* point to nongrace group following gs_p */
+ struct GRPSYL *next2_p; /* point to nongrace group after that one */
+ struct GRPSYL *gs_p; /* point to group being looked at */
+ float beamthick; /* total thickness of beams and space between*/
+ float ycross; /* where grace stem would hit nongrace beam */
+ float fary; /* farthest y coord of other voice's group */
+ int beams; /* number of beams joining two nongrace groups*/
+ float thismove; /* how far one item requires the beam to move*/
+ float move; /* distance to move intercept */
+
+
+ move = 0.0; /* init to no move */
+
+ /*
+ * Loop through all the groups in the other voice. (If there is no
+ * other voice, this loop will execute zero times.) If any of its
+ * groups land on or beyond our beam, move our beam farther away so
+ * they don't.
+ */
+ for (gs_p = ogs_p; gs_p != 0; gs_p = gs_p->next) {
+
+ /* spaces and rests can't interfere with anything */
+ if (gs_p->grpcont != GC_NOTES)
+ continue;
+
+ /* if this group is outside our beamed set, ignore it */
+ if (gs_p->c[AX] <= start_p->c[AX] ||
+ gs_p->c[AX] >= last_p->c[AX])
+ continue;
+
+ /*
+ * Find which groups in our set immediately preceed and follow
+ * the other voice's group. These will be prev_p and next_p.
+ */
+ for (prev_p = next_p = start_p;
+ next_p->c[AX] < gs_p->c[AX];
+ prev_p = next_p, next_p = nextnongrace(next_p))
+ ;
+
+ /*
+ * If next_p is lined up with gs_p, and is a note group, that
+ * means these groups were "compatible" (see setgrps.c), and so
+ * there can be no way that we would have to move our beam.
+ * But if next_p is a rest, handle the situation and continue.
+ */
+ if (next_p->c[AX] == gs_p->c[AX]) {
+ if (next_p->grpcont == GC_NOTES)
+ continue; /* compatible, no problem */
+
+ /*
+ * Find the AX and RY coords of the outer edge of the
+ * outer note of the other voice's group that is the
+ * farthest in the direction of our beam. Then, if
+ * this point would run into or beyond the rest, find
+ * how far to move the Y intercept (b0) so that it
+ * won't. Remember the farthest move needed.
+ */
+ if (start_p->stemdir == UP) {
+ fary = gs_p->notelist[0].c[RN] + Stdpad;
+ if (next_p->c[RS] < fary) {
+ thismove = fary - next_p->c[RS];
+ move = MAX(move, thismove);
+ }
+ } else { /* stemdir == DOWN */
+ fary = gs_p->notelist[ gs_p->nnotes-1 ].c[RS]
+ - Stdpad;
+ if (next_p->c[RN] > fary) {
+ thismove = fary - next_p->c[RN];
+ move = MIN(move, thismove);
+ }
+ }
+
+ continue;
+ }
+
+ /*
+ * Find which of prev_p and next_p has the least (slowest)
+ * basictime. That determines how many full beams will connect
+ * those two groups. (You take log2 of it and subtract 2.)
+ * Then add in any alternation beams.
+ */
+ if (prev_p->basictime >= 8)
+ beams = drmo(MIN(prev_p->basictime, next_p->basictime))
+ - 2;
+ else
+ beams = 0;
+
+ if (prev_p->slash_alt < 0)
+ beams -= prev_p->slash_alt;
+
+ /*
+ * Find out if there are partial beams on the left side of the
+ * following group or right side of the preceding group. If
+ * so, that group's basictime may determine the total number of
+ * beams that could interfere with our group, if it's close
+ * enough.
+ */
+ if (prev_p->basictime < next_p->basictime && next_p->stemdir ==
+ DOWN && next_p->c[AX] - gs_p->c[AX] < 5 * Stepsize) {
+
+ /* find nongrace group after "next", if one exists */
+ next2_p = nextnongrace(next_p);
+
+ /* if "next" group has partial beams . . . */
+ if (next2_p == 0 || next_p->beamloc == ENDITEM ||
+ next_p->basictime > next2_p->basictime) {
+
+ /* if on its left side, reset total beams */
+ if (pbeamside(next_p, start_p) == PB_LEFT)
+ beams = drmo(next_p->basictime) - 2;
+ }
+ } else if (prev_p->basictime > next_p->basictime && prev_p->
+ stemdir == UP && gs_p->c[AX] - prev_p->c[AX] < 5 * Stepsize) {
+
+ /* find nongrace group before "prev", if one exists */
+ prev2_p = prevnongrace(prev_p);
+
+ /* if "prev" group has partial beams . . . */
+ if (prev2_p == 0 || prev_p->beamloc == STARTITEM ||
+ prev_p->basictime > prev2_p->basictime) {
+
+ /* if on its right side, reset total beams */
+ if (pbeamside(prev_p, start_p) == PB_RIGHT)
+ beams = drmo(prev_p->basictime) - 2;
+ }
+ }
+
+ beamthick = Flagsep * beams + Stepsize;
+
+ /*
+ * Find the AX and RY coords of the outer edge of the outer
+ * note of the other voice's group that is the farthest in the
+ * direction of our beam. Then, if this point would run into
+ * or beyond the nongrace beam(s), find how much the Y
+ * intercept (b0) would have to move to avoid the collision.
+ * Remember the farthest move found so far.
+ */
+ ycross = b1 * gs_p->c[AX] + b0;
+ if (start_p->stemdir == UP) {
+
+ fary = gs_p->notelist[0].c[RN] + Stdpad;
+ if (ycross - beamthick < fary) {
+ thismove = fary - (ycross - beamthick);
+ move = MAX(move, thismove);
+ }
+
+ } else { /* stemdir == DOWN */
+
+ fary = gs_p->notelist[ gs_p->nnotes-1 ].c[RS] - Stdpad;
+ if (ycross + beamthick > fary) {
+ thismove = fary - (ycross + beamthick);
+ move = MIN(move, thismove);
+ }
+ }
+ }
+
+ if (move == 0.0)
+ return (b0); /* no change; return old intercept */
+
+ /*
+ * If our beamed set has any embedded rests, we want to move the rests
+ * too. We really only have to move rests that the other voice is
+ * bumping into, but it will probably look better to move them all.
+ * We need to move everything by a multiple of 2 stepsizes, since rests
+ * should be positioned that way.
+ */
+ for (gs_p = start_p->next; gs_p != last_p; gs_p = gs_p->next) {
+ /* break out if we find a rest */
+ if (gs_p->grpcont == GC_REST)
+ break;
+ }
+ if (gs_p != last_p) {
+ /*
+ * We found a rest. Round the amount the intercept moved up to
+ * a multiple of 2 stepsizes.
+ */
+ move = (move < 0.0 ? -1.0 : 1.0) * 2.0 * Stepsize *
+ ((int)(fabs(move) / (2.0 * Stepsize)) + 1);
+
+ /* move every embedded rest by this amount */
+ for (gs_p = start_p->next; gs_p != last_p; gs_p = gs_p->next) {
+ if (gs_p->grpcont == GC_REST) {
+ gs_p->c[RN] += move;
+ gs_p->c[RY] += move;
+ gs_p->c[RS] += move;
+ }
+ }
+ }
+
+ return (b0 + move); /* new Y intercept */
+}
+\f
+/*
+ * Name: setgroupvert()
+ *
+ * Abstract: Set RN and RS for each group of given type in a linked list.
+ *
+ * Returns: void
+ *
+ * Description: This function loops through the linked list of groups for one
+ * voice for one measure. It handles either grace groups or non-
+ * grace groups, whichever it is told to do. It sets the RN and
+ * RS for the groups.
+ */
+
+static void
+setgroupvert(grpvalue, firstgs_p, ogs_p)
+
+int grpvalue; /* should we do grace groups or normal groups?*/
+struct GRPSYL *firstgs_p; /* point to first group in a linked list */
+struct GRPSYL *ogs_p; /* point to first group in other linked list */
+
+{
+ struct GRPSYL *gs_p; /* point along groups in a linked list */
+ float outstem; /* the part of the stemlen outside notes of group */
+ float stemtip; /* coord of the end of the stem */
+ float old; /* old group boundary */
+ float delta; /* change in group boundary */
+
+
+ debug(32, "setgroupvert file=%s line=%d grpvalue=%d",
+ firstgs_p->inputfile, firstgs_p->inputlineno, grpvalue);
+ /*
+ * Loop through every group, skipping rests, spaces, and groups of the
+ * wrong type (grace vs. nongrace), setting the relative vertical
+ * coordinates.
+ */
+ for (gs_p = firstgs_p; gs_p != 0; gs_p = gs_p->next) {
+ if (gs_p->grpcont != GC_NOTES)
+ continue;
+ if (gs_p->grpvalue != grpvalue)
+ continue;
+
+ /*
+ * Back in setnotes.c, we set RY to 0, the center line of the
+ * staff. N was set to the top of the highest note, plus
+ * padding, excluding any CSS notes. S is the analogous thing,
+ * below. But if all notes are CSS, N and S were set to 0.
+ */
+
+ /*
+ * Now we want to set the stemlen, as well as we can. For
+ * groups whose step tips are not affected by CSS, we do it in
+ * the non-CSS pass; otherwise we do it in the CSS pass.
+ */
+ if (css_affects_stemtip(gs_p) == CSSpass) {
+
+ /*
+ * If the group has a stem or pseudostem, we do this
+ * work. Extend the appropriate group boundary to
+ * reach to the end of the stem. Do this for all
+ * groups with real stems or pseudostems, excluding
+ * cross staff beaming (where we don't know yet how
+ * long the stems will be and we don't want to include
+ * them in the group boundary anyway, since it would
+ * prevent stem overlapping that we want). That means
+ * half notes or shorter (excluding grace quarter
+ * notes), or anything with slash/alternations.
+ */
+ if (gs_p->beamto == CS_SAME &&
+ (gs_p->basictime >= 2 || gs_p->slash_alt != 0) &&
+ gs_p->stemlen != 0.0) {
+
+ outstem = gs_p->stemlen
+ - (gs_p->notelist[0].c[RY]
+ - gs_p->notelist[gs_p->nnotes-1].c[RY]);
+ /*
+ * In the CSS pass we also have to adjust the
+ * absolute coords, by the same amount as the
+ * relative, since those have been set by now.
+ */
+ if (gs_p->stemdir == UP) {
+ stemtip = gs_p->notelist[0].c[RY]
+ + outstem;
+ old = gs_p->c[RN];
+ gs_p->c[RN] = MAX(stemtip, gs_p->c[RN])
+ + Stdpad;
+ if (CSSpass == YES) {
+ delta = gs_p->c[RN] - old;
+ gs_p->c[AN] += delta;
+ }
+ } else {
+ stemtip = gs_p->notelist[gs_p->nnotes-1]
+ .c[RY] - outstem;
+ old = gs_p->c[RS];
+ gs_p->c[RS] = MIN(stemtip, gs_p->c[RS])
+ - Stdpad;
+ if (CSSpass == YES) {
+ delta = gs_p->c[RS] - old;
+ gs_p->c[AS] += delta;
+
+ }
+ }
+ }
+ }
+
+ if (CSSpass == NO) {
+ /*
+ * Increase RN and decrease RS based on "with" lists.
+ * Do this only in the first pass. This depends on the
+ * fact that "with" lists are always put on the side
+ * away from the other staff, when CSS is involved.
+ */
+ expgroup(gs_p, ogs_p);
+ } else {
+ /*
+ * In the CSS pass, various group boundaries need more
+ * adjustment.
+ */
+ if (gs_p->stemdir == UP) {
+ if (gs_p->stemto == CS_ABOVE && NNN(gs_p) == 0){
+ gs_p->c[RS] = gs_p->notelist[
+ gs_p->nnotes-1].c[RS] - Stdpad;
+ gs_p->c[AS] += gs_p->c[RS];
+ }
+ if (gs_p->stemto == CS_BELOW && NNN(gs_p) == 0){
+ gs_p->c[RN] = gs_p->notelist[
+ gs_p->nnotes-1].c[RY] +
+ gs_p->stemlen;
+ expgroup(gs_p, ogs_p);
+ gs_p->c[AN] = gs_p->c[AY] + gs_p->c[RN];
+ }
+ if (gs_p->stemto == CS_SAME &&
+ gs_p->stemlen > 0) {
+ gs_p->c[RN] = gs_p->notelist
+ [gs_p->nnotes-1].c[RY] + gs_p->stemlen
+ + Stdpad;
+
+ gs_p->c[AN] = gs_p->notelist
+ [gs_p->nnotes-1].c[AY] + gs_p->stemlen
+ + Stdpad;
+ }
+ if (gs_p->stemto == CS_ABOVE &&
+ gs_p->stemlen == 0) {
+ gs_p->c[RN] = gs_p->notelist[0].c[RN]
+ + Stdpad;
+ gs_p->c[AN] = gs_p->notelist[0].c[AN]
+ + Stdpad;
+ }
+ } else {
+ if (gs_p->stemto == CS_BELOW && NNN(gs_p) == 0){
+ gs_p->c[RN] = gs_p->notelist[0].c[RN]
+ + Stdpad;
+ gs_p->c[AN] += gs_p->c[RN];
+ }
+ if (gs_p->stemto == CS_ABOVE && NNN(gs_p) == 0){
+ gs_p->c[RS] = gs_p->notelist[0].c[RY] -
+ gs_p->stemlen;
+ expgroup(gs_p, ogs_p);
+ gs_p->c[AS] = gs_p->c[AY] + gs_p->c[RS];
+ }
+ if (gs_p->stemto == CS_SAME &&
+ gs_p->stemlen > 0) {
+ gs_p->c[RS] = gs_p->notelist[0].c[RY]
+ - gs_p->stemlen - Stdpad;
+
+ gs_p->c[AS] = gs_p->notelist[0].c[AY]
+ - gs_p->stemlen - Stdpad;
+ }
+ if (gs_p->stemto == CS_BELOW &&
+ gs_p->stemlen == 0) {
+ gs_p->c[RS] = gs_p->notelist
+ [gs_p->nnotes-1].c[RS] - Stdpad;
+ gs_p->c[AS] = gs_p->notelist
+ [gs_p->nnotes-1].c[AS] - Stdpad;
+ }
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: settuplet()
+ *
+ * Abstract: Figure out where tuplet bracket goes and change RN and RS.
+ *
+ * Returns: void
+ *
+ * Description: This function is given a pointer to the first GRPSYL in a
+ * tuplet whose bracket is to be printed. It figures out where
+ * the tuplet bracket and number should go, and sets tupextend for
+ * all the groups, to show where the tuplet bracket would go.
+ * Even if the bracket ends up not getting printed, this is needed
+ * for placing the number.
+ */
+
+static void
+settuplet(start_p, staff_p)
+
+struct GRPSYL *start_p; /* first group in the tuplet */
+struct STAFF *staff_p; /* staff the tuplet is on */
+
+{
+ struct GRPSYL *gs_p; /* loop through the groups in the tuplet */
+ struct GRPSYL *last_p; /* point the last group in the tuplet */
+ struct GRPSYL *end_p; /* point beyond the last group in the tuplet */
+ struct NOTE *note_p; /* pointer to an outside note of a group */
+ float sx, sy; /* sum of x and y coords of north or south */
+ float xbar, ybar; /* average x and y coords of north or south */
+ float top, bottom; /* numerator & denominator for finding b1 */
+ float temp; /* scratch variable */
+ float startx, endx; /* x coord of first and last north or south */
+ float starty, endy; /* y coord of first and last north or south */
+ float b0, b1; /* y intercept and slope */
+ float maxb0, minb0; /* max and min y intercepts */
+ float shift; /* x dist bracket reaches beyond end groups */
+ float acceast, accwest; /* horizontal coords of an accidental */
+ float accvert; /* north or south of an accidental */
+ float asc, des, wid; /* ascent, descent, and width of an acc */
+ float numeast, numwest; /* horizontal coords of the tuplet number */
+ float numvert; /* vertical edge of number closest to staff */
+ float height; /* height of the tuplet number */
+ int css_affects_tup; /* does CSS affect any group in the tuplet? */
+ int coord; /* RN or RS, depending on where bracket goes */
+ /* or AN or AS if CSSpass == YES */
+ int halfstaff; /* half the height of staff, in stepsizes */
+ int num; /* number of groups in tuplet */
+ float vert[2]; /* vertical coords of two groups */
+ int n; /* loop variable */
+
+
+ debug(32, "settuplet file=%s line=%d", start_p->inputfile,
+ start_p->inputlineno);
+ /*
+ * If start_p is pointing at a grace group that precedes the first real
+ * group of the tuplet, move start_p forward to the first real group.
+ * Actually, this shouldn't be necessary; the parser is doing it now.
+ */
+ while (start_p->grpvalue == GV_ZERO)
+ start_p = start_p->next;
+
+ /*
+ * Find out which side the tuplet number (and bracket, if needed)
+ * should go on. That determines which coord we pay attention to.
+ * The other determining factor is whether this is the CSS pass.
+ */
+ if (tupdir(start_p, staff_p) == PL_ABOVE) {
+ coord = CSSpass == YES ? AN : RN;
+ } else {
+ coord = CSSpass == YES ? AS : RS;
+ }
+
+ /* find whether CSS affects any group in the set */
+ css_affects_tup = NO;
+ if (CSSused == YES) { /* don't waste time looking if CSS not used */
+ for (gs_p = start_p; gs_p != 0 && ! (gs_p != start_p &&
+ gs_p->prev->tuploc == ENDITEM);
+ gs_p = gs_p->next) {
+ if (gs_p->stemto == CS_ABOVE &&
+ (coord == AN || coord == AN) ||
+ gs_p->stemto == CS_BELOW &&
+ (coord == AS || coord == AS)) {
+ css_affects_tup = YES;
+ break;
+ }
+ }
+ }
+
+ /*
+ * If no groups are affected by CSS, handle this tuplet on the
+ * first pass only. If some are affected, handle it on the second
+ * pass only.
+ */
+ if (css_affects_tup != CSSpass) {
+ return;
+ }
+
+ last_p = 0; /* prevent useless 'used before set' warnings */
+
+ /*
+ * If the first group is STARTITEM, there are multiple groups in the
+ * tuplet. If it is LONEITEM, there is only one.
+ */
+ if (start_p->tuploc == STARTITEM) {
+ /*
+ * Use linear regression to find the best-fit line through the
+ * RN or RS, or AN or AS, of the groups, as the case may be.
+ * The X coords used are absolute, but the Y coords are, in the
+ * normal (non-CSSpass case) relative to the center line of the
+ * staff, since we don't know the absolute Y coords yet, and it
+ * wouldn't affect the result anyway. But if this is the CSS
+ * pass, we do know the absolute vertical coords, and we have
+ * to use them, since we are dealing with two staffs.
+ *
+ * First get sum of x and y coords, to find averages. Remember
+ * where last valid group is. Only nongrace groups can be
+ * tuplet members, although there could be grace groups before
+ * a tuplet member. We ignored any grace group before the
+ * first real tuplet member, but any others must be dealt with.
+ */
+ sx = sy = 0;
+ num = 0;
+ for (gs_p = start_p; gs_p != 0 && ! (gs_p != start_p &&
+ gs_p->prev->tuploc == ENDITEM);
+ gs_p = gs_p->next) {
+ sx += gs_p->c[AX];
+ sy += gs_p->c[coord];
+ num++; /* count number of groups */
+ last_p = gs_p;
+ }
+ /* last_p now points at last valid group */
+
+ end_p = gs_p; /* point end_p beyond last tuplet member */
+
+ xbar = sx / num;
+ ybar = sy / num;
+
+ /* accum numerator & denominator of regression formula for b1 */
+ top = bottom = 0;
+ for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next) {
+ temp = gs_p->c[AX] - xbar;
+ top += temp * (gs_p->c[coord] - ybar);
+ bottom += temp * temp;
+ }
+
+ b1 = top / bottom; /* slope */
+ /*
+ * We could also figure:
+ * b0 = ybar - b1 * xbar; y intercept
+ * to get the equation of the regression line: y = b0 + b1 * x
+ * but we're going to change b0 later anyway. Now, there are
+ * certain cases where we want to override the slope determined
+ * by regression, so revise b1 if that is the case.
+ */
+
+ /* if first and last groups are equal, force horizontal */
+ if (start_p->c[coord] == last_p->c[coord])
+ b1 = 0.0;
+
+ /* if repeating pattern of two coords, force horizontal */
+ if (b1 != 0.0 && num >= 4 && num % 2 == 0) {
+ vert[0] = start_p->c[coord];
+ vert[1] = start_p->next->c[coord];
+ for (n = 0, gs_p = start_p; n < num;
+ n++, gs_p = gs_p->next) {
+ if (n >= 2 && gs_p->c[coord] != vert[n % 2])
+ break;
+ }
+ if (n == num)
+ b1 = 0.0;
+ }
+
+ } else { /* LONEITEM */
+ /*
+ * There's only one group, so there's no need to apply linear
+ * regression. But we need to set up certain variables so that
+ * later code in this function can treat both cases the same.
+ */
+ last_p = start_p; /* point at last tuplet member */
+ end_p = start_p->next; /* point beyond last tuplet member */
+ b1 = 0; /* set horizontal slope */
+ b0 = start_p->c[coord]; /* y intercept based on this group */
+ }
+
+ /*
+ * Find half the width of a note head; the end of the tuplet bracket
+ * reaches that far beyond the X coords of the outer groups. Set
+ * the X positions for these ends.
+ */
+ shift = getstemshift(last_p);
+ startx = start_p->c[AX] - shift; /* start of tuplet bracket */
+ endx = last_p->c[AX] + shift; /* end of tuplet bracket */
+
+ /*
+ * The original line derived by linear regression must be adjusted in
+ * certain ways. First, don't let the slope exceed plus or minus 0.7,
+ * since that would look bad.
+ */
+ if (b1 > 0.7)
+ b1 = 0.7;
+ else if (b1 < -0.7)
+ b1 = -0.7;
+
+ /*
+ * Calculate a new y intercept (b0). First pass parallel lines
+ * through each group's extremity, and record the maximum and minimum
+ * y intercepts that result.
+ */
+ b0 = start_p->c[coord] - b1 * start_p->c[AX];
+ maxb0 = minb0 = b0; /* init to value for first group */
+ /* look at rest of them */
+ for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next) {
+ b0 = gs_p->c[coord] - b1 * gs_p->c[AX];
+ if (b0 > maxb0)
+ maxb0 = b0;
+ else if (b0 < minb0)
+ minb0 = b0;
+ }
+
+ /*
+ * The outer edge of the tuplet bracket, including the number, should
+ * be TUPHEIGHT away from the group that sticks out the farthest.
+ */
+ if (coord == RN || coord == AN) {
+ b0 = maxb0 + Tupheight;
+ } else { /* RS or AS */
+ b0 = minb0 - Tupheight;
+ }
+
+ /*
+ * Calculate the Y positions of the start and end of the bracket from
+ * the X positions, and the slope and Y intercept we have tentatively
+ * chosen. If, however, the bracket is going to fall within the staff,
+ * make adjustments so it won't.
+ */
+ starty = b0 + b1 * startx; /* y coord near left end of beam */
+ endy = b0 + b1 * endx; /* y coord near right end of beam */
+ halfstaff = svpath(staff_p->staffno, STAFFLINES)->stafflines == 5
+ ? 4 : 1;
+
+ if (coord == RN) {
+ if (starty < halfstaff * Stepsize + Tupheight)
+ starty = halfstaff * Stepsize + Tupheight;
+ if (endy < halfstaff * Stepsize + Tupheight)
+ endy = halfstaff * Stepsize + Tupheight;
+ } else if (coord == RS) {
+ if (starty > -halfstaff * Stepsize - Tupheight)
+ starty = -halfstaff * Stepsize - Tupheight;
+ if (endy > -halfstaff * Stepsize - Tupheight)
+ endy = -halfstaff * Stepsize - Tupheight;
+ }
+
+ /*
+ * If y at the ends of the bracket only differs by less than 2 points,
+ * set end equal to the start to avoid a jagged look.
+ */
+ if (endy - starty < 2 * POINT && endy - starty > -2 * POINT) {
+ endy = (starty + endy) / 2.;
+ starty = endy;
+ }
+
+ /* recalculate slope and y intercept from (possibly) new endpoints */
+ b1 = (endy - starty) / (endx - startx); /* slope */
+ b0 = starty - b1 * startx; /* y intercept */
+
+ /*
+ * The vertical extension of accidentals is not included in group
+ * boundaries, and so the calculation of the tuplet bracket's equation
+ * has ignored them so far. In general, this is no problem. If an
+ * accidental touches or slightly crosses that line, who cares? But we
+ * would like to keep it from running into the tuplet number. So scan
+ * through the notes closest to the bracket, checking for accidentals.
+ * (Notes a step or more from there would never really be a problem.)
+ * Also, accidentals on the first group can never be a problem.
+ */
+ (void)tupnumsize(start_p, &numwest, &numeast, &height, staff_p);
+ numvert = (starty + endy) / 2 + (coord == RN || coord == AN ?
+ -height : height) / 2;
+
+ for (gs_p = start_p->next; gs_p != end_p; gs_p = gs_p->next) {
+
+ if (gs_p->grpcont != GC_NOTES)
+ continue;
+
+ note_p = &gs_p->notelist[ coord == RN || coord == AN ?
+ 0 : gs_p->nnotes - 1 ];
+ if (note_p->accidental == '\0')
+ continue;
+
+ /*
+ * The note of this group nearest the bracket has an acci-
+ * dental. Find its horizontal midpoint, and vertical coord
+ * nearest the bracket. Add padding to the vertical coord.
+ */
+ accdimen(note_p, &asc, &des, &wid);
+ asc *= Staffscale;
+ des *= Staffscale;
+ wid *= Staffscale;
+
+ accwest = gs_p->c[AX] + note_p->waccr;
+ acceast = accwest + wid;
+
+ if (coord == RN || coord == AN) {
+ accvert = note_p->c[CSSpass == YES ? AY : RY]
+ + asc + Stepsize;
+ } else {
+ accvert = note_p->c[CSSpass == YES ? AY : RY]
+ - des - Stepsize;
+ }
+
+ /* if acc is completely to the left of the number, try next */
+ if (acceast < numwest)
+ continue;
+
+ /* if acc is completely to the right, get out */
+ if (accwest > numeast)
+ break;
+
+ /*
+ * If acc sticks out beyond the edge of the number, change the
+ * y intercept by that amount to prevent it. Then get out,
+ * since no later groups could be that nearby.
+ */
+ if ((coord == RN || coord == AN) && accvert > numvert ||
+ (coord == RS || coord == AS) && accvert < numvert) {
+ b0 += accvert - numvert;
+ break;
+ }
+ }
+
+ /*
+ * At this point we know where to put the tuplet bracket. Set
+ * tupextend in all the groups, to reach the tuplet bracket.
+ */
+ for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next)
+ gs_p->tupextend = (b0 + b1 * gs_p->c[AX]) - gs_p->c[coord];
+}
+\f
+/*
+ * Name: expgroup()
+ *
+ * Abstract: Decide side for "with" list & expand vertical group vertically.
+ *
+ * Returns: void
+ *
+ * Description: This function decides which side of the group a "with" list
+ * should be put, and calls applywith() to alter the group's
+ * vertical boundaries accordingly.
+ */
+
+static void
+expgroup(gs_p, ogs_p)
+
+struct GRPSYL *gs_p; /* the group to be worked on */
+struct GRPSYL *ogs_p; /* the other group */
+
+{
+ struct GRPSYL *g_p; /* earlier GRPSYLs in *gs_p's list */
+ RATIONAL vtime; /* time preceding this group in measure */
+ int side; /* side to put things on (1=top, -1=bottom) */
+
+
+ side = 0; /* prevent useless 'used before set' warnings */
+
+ /*
+ * Define a chunk of code for the cases where the stem may be allowed
+ * to go either way. It goes opposite the stem for normal, with the
+ * stem for tab.
+ */
+#define FREESTEM \
+ { \
+ if (is_tab_staff(gs_p->staffno) == YES) { \
+ side = -1; /* we know stemdir is DOWN */ \
+ gs_p->normwith = NO; \
+ } else { \
+ side = gs_p->stemdir == UP ? -1 : 1; \
+ gs_p->normwith = YES; \
+ } \
+ }
+
+ /*
+ * Define a chunk of code for the cases where the stem has to go a
+ * certain way, determined by which voice this is, unless forced by the
+ * user. The "with" items are always above a voice acting as voice 1,
+ * and below a voice acting as voice 2.
+ */
+#define FIXEDSTEM \
+ { \
+ if (gs_p->pvno == 1) { \
+ side = 1; \
+ gs_p->normwith = gs_p->stemdir == UP ? NO : YES;\
+ } else { \
+ side = -1; \
+ gs_p->normwith = gs_p->stemdir == DOWN ? NO : YES;\
+ } \
+ }
+
+ /*
+ * If there is cross staff stemming, that consideration overrides all
+ * others. We want to keep the "with" items towards our staff, hoping
+ * they will be less likely to collide with something there.
+ */
+ if (gs_p->stemto != CS_SAME) {
+ if (gs_p->stemto == CS_ABOVE) {
+ gs_p->normwith = gs_p->stemdir == UP ? YES : NO;
+ side = -1;
+ } else { /* CS_BELOW */
+ gs_p->normwith = gs_p->stemdir == UP ? NO : YES;
+ side = 1;
+ }
+ applywith(gs_p, side);
+ return;
+ }
+
+ /*
+ * Switch on vscheme to decide which side of the group the "with"
+ * things will be put on.
+ */
+ switch (svpath(gs_p->staffno, VSCHEME)->vscheme) {
+ case V_1:
+ FREESTEM
+ break;
+
+ case V_2OPSTEM:
+ FIXEDSTEM
+ break;
+
+ case V_2FREESTEM:
+ /*
+ * Figure out where this group starts by adding up the time
+ * values of all previous groups in the measure. Then, treat
+ * this like V_1 or V_2OPSTEM, based on whether the other
+ * voice has space here.
+ */
+ vtime = Zero;
+ for (g_p = gs_p->prev; g_p != 0; g_p = g_p->prev)
+ vtime = radd(vtime, g_p->fulltime);
+
+ if (hasspace(ogs_p, vtime, radd(vtime, gs_p->fulltime))) {
+ FREESTEM
+ } else {
+ FIXEDSTEM
+ }
+ break;
+
+ case V_3OPSTEM:
+ if (gs_p->pvno == 3) {
+ FREESTEM /* voice 3 is always like V_1 */
+ } else {
+ FIXEDSTEM
+ }
+ break;
+
+ case V_3FREESTEM:
+ if (gs_p->pvno == 3) {
+ FREESTEM /* voice 3 is always like V_1 */
+ } else {
+ /* voices 1 and 2 act like V_2FREESTEM */
+ vtime = Zero;
+ for (g_p = gs_p->prev; g_p != 0; g_p = g_p->prev)
+ vtime = radd(vtime, g_p->fulltime);
+
+ if (hasspace(ogs_p, vtime, radd(vtime, gs_p->fulltime))) {
+ FREESTEM
+ } else {
+ FIXEDSTEM
+ }
+ }
+ break;
+ }
+
+ /*
+ * If there is cross staff beaming and the "with" items are to be on
+ * the beam side, we can't do anything yet since we don't know yet
+ * where the beam will be.
+ */
+ if (gs_p->beamto != CS_SAME && gs_p->normwith == NO) {
+ return;
+ }
+
+ applywith(gs_p, side);
+}
+\f
+/*
+ * Name: applywith()
+ *
+ * Abstract: Expand vertical boundaries of group, based on "with" list.
+ *
+ * Returns: void
+ *
+ * Description: This function adds to the RN coord of a group and/or subtracts
+ * from the RS coord, if a "with" list is present.
+ */
+
+static void
+applywith(gs_p, side)
+
+struct GRPSYL *gs_p; /* the group to be worked on */
+int side; /* side to put things on (1=top, -1=bottom) */
+
+{
+ int n; /* loop variable */
+ float hi; /* height of a list item */
+
+
+ /*
+ * Loop through all the "with" items, expanding the N or S coord of
+ * the group. Each item is allowed enough space for its height, or
+ * MINWITHHEIGHT, whichever is greater. In the print phase, items of
+ * height less than MINWITHHEIGHT will be placed so as to avoid staff
+ * lines as much as possible.
+ */
+ for (n = 0; n < gs_p->nwith; n++) {
+ hi = strheight(gs_p->withlist[n]);
+ hi = MAX(hi, Staffscale * MINWITHHEIGHT);
+ if (side == 1)
+ gs_p->c[RN] += hi;
+ else
+ gs_p->c[RS] -= hi;
+ }
+}