--- /dev/null
+/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 by Arkkra Enterprises */
+/* All rights reserved */
+/*
+ * Name: relvert.c
+ *
+ * Description: This file contains functions for setting all remaining
+ * relative vertical coordinates.
+ */
+
+#include <string.h>
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+/* how many rectangles to malloc initially and at each realloc if needed */
+#define RECTCHUNK (100)
+
+/* rectangle structure; see procscore() prologue for explanation of its use */
+struct RECTAB {
+ float n, s, e, w; /* boundaries of a rectangle */
+ /* horz coords are absolute; vertical coords */
+ /* are relative to center staff line */
+ /* (baseline for "between") */
+ short relevant; /* is rectangle relevant? */
+ short tried; /* have we tried this one yet? */
+};
+static struct RECTAB *rectab; /* ptr to malloc'ed and realloc'ed array */
+
+/* this fudge factor prevents roundoff error from causing overlap */
+#define FUDGE (0.001)
+
+/* these symbols tell certain subroutines which things to work on */
+#define DO_OTHERS 0 /* default */
+#define DO_PHRASE 1
+
+
+static int reclim; /* index after last rectangle in rectab */
+
+static void procstaff P((struct MAINLL *mainll_p, int s));
+static void dostaff P((int s, int place));
+static void dogroups P((struct MAINLL *start_p, int s, int place));
+static void llgrps P((struct STAFF *staff_p, struct GRPSYL *gs_p, int place));
+static void dobeamalt P((struct MAINLL *start_p, int s, int place));
+static void onebeamalt P((struct GRPSYL *gs_p));
+static double getstemendvert P((struct GRPSYL *gs_p));
+static void linerects P((double x1, double y1, double x2, double y2, int side,
+ double halfstaff));
+static void docurve P((struct MAINLL *start_p, int s, int place,
+ int do_which));
+static void curverect P((int s, struct STUFF *stuff_p, double halfstaff));
+static void curvepiecerect P((double x1, double y1, double x2, double y2,
+ double halfstaff));
+static void dotuplet P((struct MAINLL *start_p, int s, int place));
+static void onetuplet P((struct STAFF *staff_p, struct GRPSYL *start_p,
+ int place));
+static void domiscstuff P((struct MAINLL *start_p, int s, int place,
+ unsigned long do_which));
+static void dolyrics P((struct MAINLL *start_p, int s, int place));
+static void getvsize P((struct MAINLL *start_p, int s, int place, int v,
+ float *asc_p, float *des_p));
+static void setsylvert P((struct MAINLL *start_p, int s, int place, int v,
+ double baseline));
+static void dopedal P((struct MAINLL *start_p, int s));
+static void doendings P((struct MAINLL *start_p, int s));
+static void storeend P((struct MAINLL *start_p, struct MAINLL *end_p, int s));
+static void dorehears P((struct MAINLL *start_p, int s));
+static double stackit P((double west, double east, double height, double dist,
+ int place));
+static void inc_reclim P((void));
+\f
+/*
+ * Name: relvert()
+ *
+ * Abstract: Set all relative vertical coords not already set.
+ *
+ * Returns: void
+ *
+ * Description: This function sets all remaining relative vertical coords.
+ * It calls procstaff() once for each staff in each score to
+ * do this.
+ */
+
+void
+relvert()
+
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ struct MAINLL *end_p; /* point at end of a piece of MLL */
+ struct MAINLL *m2_p; /* another pointer along MLL */
+ int s; /* staff number */
+ int gotbar; /* was a bar found in this chunk? */
+
+
+ debug(16, "relvert");
+ /*
+ * Find each section of the main linked list, delimited by FEEDs.
+ * For each such section, call procstaff() for each visible staff.
+ * Keep SSVs up to date so that we always know what staffs are visible.
+ */
+ initstructs(); /* clean out old SSV info */
+
+ /* skip anything before first FEED first */
+ for (mainll_p = Mainllhc_p; mainll_p->str != S_FEED;
+ mainll_p = mainll_p->next) {
+ if (mainll_p->str == S_SSV)
+ asgnssv(mainll_p->u.ssv_p);
+ }
+
+ /*
+ * Initially allocate RECTCHUNK rectangles. If we find we need more at
+ * some point, we'll realloc to get more.
+ */
+ MALLOC(RECTAB, rectab, RECTCHUNK);
+
+ for (;;) {
+ /*
+ * Find end of this chunk. If it has no bars in it, this must
+ * either be the end of the MLL and there was a final feed
+ * after all the music data, or else this is a block. Either
+ * way, there is no need to process this chunk.
+ */
+ gotbar = NO;
+ for (end_p = mainll_p->next; end_p != 0 &&
+ end_p->str != S_FEED; end_p = end_p->next) {
+ if (end_p->str == S_BAR)
+ gotbar = YES;
+ }
+ if (gotbar == NO) {
+ if (end_p == 0)
+ break; /* end of MLL, get out */
+
+ /* update SSVs to beginning of next score */
+ for (m2_p = mainll_p->next; m2_p != end_p;
+ m2_p = m2_p->next) {
+ if (m2_p->str == S_SSV)
+ asgnssv(m2_p->u.ssv_p);
+ }
+
+ mainll_p = end_p; /* block, skip by it */
+ continue;
+ }
+
+ for (s = 1; s <= Score.staffs; s++) {
+ if (svpath(s, VISIBLE)->visible == YES)
+ procstaff(mainll_p, s);
+ }
+
+ /* update SSVs to beginning of next score */
+ for (m2_p = mainll_p->next; m2_p != end_p; m2_p = m2_p->next) {
+ if (m2_p->str == S_SSV)
+ asgnssv(m2_p->u.ssv_p);
+ }
+
+ if (end_p == 0)
+ break;
+ mainll_p = end_p;
+ }
+
+ FREE(rectab);
+}
+\f
+/*
+ * Name: procstaff()
+ *
+ * Abstract: Set all relative vertical coords for a staff in one score.
+ *
+ * Returns: void
+ *
+ * Description: This function sets all remaining relative vertical coords
+ * for a given staff of a given score.
+ */
+
+static void
+procstaff(start_p, s)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* the staff we are to work on */
+
+{
+ struct MAINLL *mainll_p;/* point along main linked list */
+ char *order; /* point at a subarray in markorder */
+ int stk; /* stacking order number */
+ int mk; /* mark type */
+ unsigned long do_which; /* bit map of which mark types to do */
+ float north, south; /* relative coords of staff */
+ float hb; /* height of "between" objects */
+ int k; /* loop variable */
+
+
+ debug(32, "procstaff file=%s line=%d s=%d", start_p->inputfile,
+ start_p->inputlineno, s);
+
+ /* set globals like Staffscale for use by the rest of the file */
+ set_staffscale(s);
+
+ /*
+ * Each structure in rectab[] represents something to be drawn that
+ * is associated with this staff, beginning with the staff itself.
+ * The coordinates define the rectangle that surrounds the object.
+ * The rectangles' edges are horizontal and vertical. So if an object
+ * (like a slanted beam) doesn't fit well in such a recangle, multiple
+ * rectangles are used to enclose pieces of it, as in integration in
+ * calculus.
+ *
+ * The first part of this function does this for things that are above
+ * the staff. The second part does it for things that are below it.
+ * The third part does it for items that are to be centered (if
+ * possible) between two staffs. In the first two parts, rectangles
+ * are added to the table one at a time, working outwards from the
+ * staff. In the third part, they are piled on an imaginary baseline.
+ *
+ * Some objects (like note groups) already have an assigned position.
+ * and their rectangles are simply added to the table, regardless of
+ * whether they overlap preexisting rectangles.
+ *
+ * Some objects (like phrase marks) get their positions figured out
+ * now, by some unique algorithm that doesn't make use of the table of
+ * rectangles, and then their rectangles are added to the table, again
+ * not worrying about overlap with preexisting rectangles.
+ *
+ * Some objects (like "stuff" to be printed) make use of the table to
+ * figure out where their rectangles should be placed. They are placed
+ * as close to the staff (or baseline, for "between") as is possible
+ * without overlapping preexisting rectangles (or, in the case of
+ * chords, getting closer to the staff than allowed by "chorddist"; or
+ * in the case of rom, ital, bold, boldital, or rehearsal marks, closer
+ * than "dist"; or in the case of dynamics, closer than "dyndist").
+ * (And some things have their own "dist" to override these parameters,
+ * and the optional ability to force a distance regardless of overlap.)
+ * To see if the rectangle being added overlaps, first its east and
+ * west are tested. All previous rectangles that are "out of its way"
+ * horizontally are marked not "relevant"; the others are marked
+ * "relevant". As positions are tried, working outwards, positions
+ * that fail to avoid overlap are marked "tried". (For chords, and
+ * rom/ital/bold/boldital, previous rectangles that are closer to the
+ * staff than the stuff is allowed to come anyhow are pre-marked as if
+ * "tried".)
+ */
+
+ /*
+ * Fill rectab for the objects above this staff.
+ */
+ reclim = 0; /* rectab is initially empty */
+
+ dostaff(s, PL_ABOVE);
+ dogroups(start_p, s, PL_ABOVE);
+ dobeamalt(start_p, s, PL_ABOVE);
+ docurve(start_p, s, PL_ABOVE, DO_OTHERS);
+ dotuplet(start_p, s, PL_ABOVE);
+ docurve(start_p, s, PL_ABOVE, DO_PHRASE);
+
+ /* get stacking order of the user-controllable mark types */
+ order = svpath(s, ABOVEORDER)->markorder[PL_ABOVE];
+
+ /* loop on each possible stacking order number */
+ for (stk = 1; stk <= NUM_MARK; stk++) {
+
+ /* set bit map for each mark type that has this order number */
+ do_which = 0;
+ for (mk = 0; mk < NUM_MARK; mk++) {
+ if (order[mk] == stk) {
+ do_which |= (1L << mk);
+ }
+ }
+ /* if no marks, we're done; stacking orders are contiguous */
+ if (do_which == 0)
+ break;
+
+ /*
+ * Some mark types must have a unique order number, not shared
+ * with any others. For each of them, do a case statement to
+ * call their subroutine. The other ones all share the same
+ * subroutine, so call it in the default to do the mark types
+ * listed in the bit map.
+ */
+ switch (do_which) {
+ case 1L << MK_LYRICS:
+ dolyrics(start_p, s, PL_ABOVE);
+ break;
+ case 1L << MK_ENDING:
+ doendings(start_p, s);
+ break;
+ case 1L << MK_REHEARSAL:
+ dorehears(start_p, s);
+ break;
+ case 1L << MK_PEDAL:
+ break; /* ignore for above */
+ default:
+ domiscstuff(start_p, s, PL_ABOVE, do_which);
+ break;
+ }
+ }
+
+ /*
+ * Find the northernmost rectangle, for setting the staff's north.
+ * But don't let north be so close that things sticking out might
+ * almost touch another staff. Staffs smaller than a regular 5 line
+ * staff will still be given as much space. In any case, we want at
+ * least 3 stepsizes of white space.
+ */
+ north = staffvertspace(s) / 2.0 + 3.0 * Stepsize;
+ for (k = 0; k < reclim; k++) {
+ if (rectab[k].n > north)
+ north = rectab[k].n;
+ }
+
+ /*
+ * Fill rectab for the objects below this staff.
+ */
+ reclim = 0; /* rectab is initially empty */
+
+ dostaff(s, PL_BELOW);
+ dogroups(start_p, s, PL_BELOW);
+ dobeamalt(start_p, s, PL_BELOW);
+ docurve(start_p, s, PL_BELOW, DO_OTHERS);
+ dotuplet(start_p, s, PL_BELOW);
+ docurve(start_p, s, PL_BELOW, DO_PHRASE);
+
+ /* get stacking order of the user-controllable mark types */
+ order = svpath(s, BELOWORDER)->markorder[PL_BELOW];
+
+ /* loop on each possible stacking order number */
+ for (stk = 1; stk <= NUM_MARK; stk++) {
+
+ /* set bit map for each mark type that has this order number */
+ do_which = 0;
+ for (mk = 0; mk < NUM_MARK; mk++) {
+ if (order[mk] == stk) {
+ do_which |= (1L << mk);
+ }
+ }
+ /* if no marks, we're done; stacking orders are contiguous */
+ if (do_which == 0)
+ break;
+
+ /*
+ * Some mark types must have a unique order number, not shared
+ * with any others. For each of them, do a case statement to
+ * call their subroutine. The other ones all share the same
+ * subroutine, so call it in the default to do the mark types
+ * listed in the bit map.
+ */
+ switch (do_which) {
+ case 1L << MK_LYRICS:
+ dolyrics(start_p, s, PL_BELOW);
+ break;
+ case 1L << MK_ENDING:
+ case 1L << MK_REHEARSAL:
+ break; /* ignore for below */
+ case 1L << MK_PEDAL:
+ dopedal(start_p, s);
+ break;
+ default:
+ domiscstuff(start_p, s, PL_BELOW, do_which);
+ break;
+ }
+ }
+
+ /*
+ * Find the southernmost rectangle, for setting the staff's south.
+ * But don't let south be so close that things sticking out might
+ * almost touch another staff. Staffs smaller than a regular 5 line
+ * staff will still be given as much space. In any case, we want at
+ * least 3 stepsizes of white space.
+ */
+ south = -(staffvertspace(s) / 2.0 + 3.0 * Stepsize);
+ for (k = 0; k < reclim; k++) {
+ if (rectab[k].s < south)
+ south = rectab[k].s;
+ }
+
+ /*
+ * Fill rectab for the objects between this staff and the one below.
+ */
+ reclim = 0; /* rectab is initially empty */
+
+ /* set up baseline, a rectangle of height 0 spanning the page */
+ rectab[reclim].w = 0;
+ rectab[reclim].e = PGWIDTH;
+ rectab[reclim].n = 0;
+ rectab[reclim].s = 0;
+ inc_reclim();
+
+
+ /* get stacking order of the user-controllable mark types */
+ order = svpath(s, BETWEENORDER)->markorder[PL_BETWEEN];
+
+ /* loop on each possible stacking order number */
+ for (stk = 1; stk <= NUM_MARK; stk++) {
+
+ /* set bit map for each mark type that has this order number */
+ do_which = 0;
+ for (mk = 0; mk < NUM_MARK; mk++) {
+ if (order[mk] == stk) {
+ do_which |= (1L << mk);
+ }
+ }
+ /* if no marks, we're done; stacking orders are contiguous */
+ if (do_which == 0)
+ break;
+
+ /*
+ * Some mark types must have a unique order number, not shared
+ * with any others. For each of them, do a case statement to
+ * call their subroutine. The other ones all share the same
+ * subroutine, so call it in the default to do the mark types
+ * listed in the bit map.
+ */
+ switch (do_which) {
+ case 1L << MK_LYRICS:
+ dolyrics(start_p, s, PL_BETWEEN);
+ break;
+ case 1L << MK_ENDING:
+ case 1L << MK_REHEARSAL:
+ case 1L << MK_PEDAL:
+ break; /* ignore for between */
+ default:
+ domiscstuff(start_p, s, PL_BETWEEN, do_which);
+ break;
+ }
+ }
+
+ /*
+ * Find the northernmost rectangle, for finding the height of these
+ * objects between.
+ */
+ hb = 0;
+ for (k = 0; k < reclim; k++) {
+ if (rectab[k].n > hb)
+ hb = rectab[k].n;
+ }
+
+ /*
+ * Set the relative north and south of every STAFF structure for this
+ * staff number on this score. (There's one per measure.) While
+ * we're at it, set RX to 0, in case anyone cares. Set the height of
+ * "between" objects in each STAFF, too.
+ */
+ for (mainll_p = start_p->next; mainll_p != 0 &&
+ mainll_p->str != S_FEED; mainll_p = mainll_p->next) {
+
+ if (mainll_p->str == S_STAFF &&
+ mainll_p->u.staff_p->staffno == s) {
+
+ mainll_p->u.staff_p->c[RN] = north;
+ mainll_p->u.staff_p->c[RX] = 0;
+ mainll_p->u.staff_p->c[RS] = south;
+ mainll_p->u.staff_p->heightbetween = hb;
+ }
+ }
+}
+\f
+/*
+ * Name: dostaff()
+ *
+ * Abstract: Set up the rectangle for the staff itself.
+ *
+ * Returns: void
+ *
+ * Description: This function puts into rectab the rectangle for the staff
+ * itself. The staff's relative vertical coords are not set now,
+ * though, because they must later be set to include all the
+ * objects associated with the staff.
+ */
+
+static void
+dostaff(s, place)
+
+int s; /* staff number */
+int place; /* above or below? */
+
+{
+ debug(32, "dostaff s=%d place=%d", s, place);
+ /*
+ * Use the full page width, even though the staff will not actually
+ * reach the edges, due to margins, etc. This way nothing will ever
+ * fall beyond this base rectangle. Put a STDPAD of padding around
+ * it vertically.
+ */
+ rectab[reclim].w = 0;
+ rectab[reclim].e = PGWIDTH;
+
+ if (place == PL_ABOVE) {
+ rectab[reclim].n = halfstaffhi(s) + Stdpad;
+ rectab[reclim].s = 0;
+ } else { /* PL_BELOW */
+ rectab[reclim].n = 0;
+ rectab[reclim].s = -(halfstaffhi(s) + Stdpad);
+ }
+
+ inc_reclim();
+}
+\f
+/*
+ * Name: dogroups()
+ *
+ * Abstract: Set up rectangles & relative vert coords for staff's groups.
+ *
+ * Returns: void
+ *
+ * Description: This function puts into rectab the rectangles for each group on
+ * this staff. The groups' relative vertical coords were already
+ * set in proclist() in beamstem.c.
+ */
+
+static void
+dogroups(start_p, s, place)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+int place; /* above or below? */
+
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ int v; /* voice number */
+
+
+ debug(32, "dogroups file=%s line=%d s=%d place=%d", start_p->inputfile,
+ start_p->inputlineno, s, place);
+ /*
+ * Loop through this score's part of the MLL.
+ */
+ for (mainll_p = start_p->next; mainll_p != 0 &&
+ mainll_p->str != S_FEED; mainll_p = mainll_p->next) {
+ /*
+ * Whenever we find a structure for this staff (another
+ * measure of this staff), call llgrps() for each voice.
+ * If some voice doesn't exist, llgrps() will get a
+ * null pointer and just return.
+ */
+ if (mainll_p->str == S_STAFF &&
+ mainll_p->u.staff_p->staffno == s) {
+
+ for (v = 0; v < MAXVOICES; v++)
+ llgrps(mainll_p->u.staff_p,
+ mainll_p->u.staff_p->groups_p[v], place);
+ }
+ }
+}
+\f
+/*
+ * Name: llgrps()
+ *
+ * Abstract: Set up rectangles for note and rest groups.
+ *
+ * Returns: void
+ *
+ * Description: This function puts rectangles into rectab for all groups in
+ * this measure of this voice, for groups consisting of notes or
+ * rests.
+ */
+
+static void
+llgrps(staff_p, first_p, place)
+
+struct STAFF *staff_p; /* point to the staff */
+struct GRPSYL *first_p; /* point to first group */
+int place; /* above or below? */
+
+{
+ struct GRPSYL *gs_p; /* point at a group */
+ struct NOTE *note_p; /* point at a note */
+ double mx, my_offset, mheight, mwidth; /* multirest number coords */
+ int n; /* loop through notelist */
+ float asc, des, wid; /* ascent, descent, and width of acc */
+
+
+ /*
+ * For each group that is notes or a rest, put a rectangle into rectab.
+ * However, on tablature staffs, don't do this for rests, since they
+ * aren't printed there.
+ */
+ for (gs_p = first_p; gs_p != 0; gs_p = gs_p->next) {
+ if (gs_p->grpcont == GC_SPACE)
+ continue;
+
+ if (gs_p->grpcont == GC_REST && is_tab_staff(gs_p->staffno))
+ continue;
+
+ if (place == PL_ABOVE && (
+ gs_p->basictime < -1 && svpath(staff_p->staffno,
+ PRINTMULTNUM)->printmultnum == YES ||
+ is_mrpt(gs_p) && svpath(staff_p->staffno,
+ NUMBERMRPT)->numbermrpt == YES
+ )) {
+ /*
+ * Special case for multirests and measure repeats.
+ * The rest or mrpt symbol itself is inside the staff,
+ * so we don't have to worry about it. But we need to
+ * make a rectangle for the number, if the number is
+ * to be printed.
+ */
+ (void)mrnum(staff_p, &mx, &my_offset, &mheight,
+ &mwidth);
+ rectab[reclim].w = mx;
+ rectab[reclim].e = mx + mwidth;
+ rectab[reclim].n = my_offset + mheight;
+ rectab[reclim].s = 0;
+
+ inc_reclim();
+ continue;
+ }
+
+ /* for "below", no rectangles are needed for multirests */
+ if (gs_p->basictime < -1)
+ continue;
+
+ /*
+ * We have a normal note or rest group. Make a rectangle for
+ * it, making sure it reaches the center staff line.
+ */
+ rectab[reclim].w = gs_p->c[AW];
+ rectab[reclim].e = gs_p->c[AE];
+
+ if (place == PL_ABOVE) {
+ rectab[reclim].n = MAX(gs_p->c[RN], 0);
+ rectab[reclim].s = 0;
+ } else { /* PL_BELOW */
+ rectab[reclim].n = 0;
+ rectab[reclim].s = MIN(gs_p->c[RS], 0);
+ }
+
+ inc_reclim();
+
+ /* if a clef precedes this group, make a rectangle for it */
+ if (gs_p->clef != NOCLEF) {
+ float north, south; /* clef coords */
+
+ rectab[reclim].e = gs_p->c[AW] - Staffscale * CLEFPAD;
+ rectab[reclim].w = rectab[reclim].e - Staffscale *
+ clefwidth(gs_p->clef, YES);
+ (void)clefvert(gs_p->clef, YES, &north, &south);
+ rectab[reclim].n = north * Staffscale;
+ rectab[reclim].s = south * Staffscale;
+
+ inc_reclim();
+ }
+
+ /*
+ * An additional rectangle is needed for each note that has an
+ * accidental. This is because although the east/west group
+ * boundaries include any accidentals, the north/south
+ * boundaries ingore them. It needs to be this way because,
+ * for other reasons, like ties, we want the north/south group
+ * boundaries to consider only the note heads. But for general
+ * stuff, the accidentals should also be considered. The
+ * rectangles added below take care of this.
+ * Similarly, if the top or bottom note is on a line and has a
+ * dot in the space away from the group, it needs a rectangle.
+ */
+ if (gs_p->grpcont == GC_NOTES &&
+ ! is_tab_staff(gs_p->staffno)) {
+ for (n = 0; n < gs_p->nnotes; n++) {
+ note_p = &gs_p->notelist[n];
+
+ if (gs_p->dots != 0 &&
+ note_p->stepsup % 2 == 0 &&
+ (n == 0 && note_p->ydotr > 0.0 ||
+ n == gs_p->nnotes - 1 && note_p->ydotr < 0.0)){
+ float radius; /* of a dot, + pad */
+ radius = Stdpad + Staffscale *
+ ascent(FONT_MUSIC, (note_p->
+ notesize == GS_NORMAL ?
+ DFLT_SIZE : SMALLSIZE), C_DOT);
+ rectab[reclim].n = gs_p->c[RY] +
+ note_p->ydotr + radius;
+ rectab[reclim].s = gs_p->c[RY] +
+ note_p->ydotr - radius;
+ rectab[reclim].w = gs_p->c[AX] +
+ gs_p->xdotr - radius;
+ rectab[reclim].e = gs_p->c[AX] +
+ gs_p->xdotr + radius +
+ (gs_p->dots - 1) * 2.0 *
+ (radius + Stdpad);
+ inc_reclim();
+ }
+
+ if (note_p->accidental == '\0')
+ continue;
+
+ /* this note has an acc; create a rectangle */
+ accdimen(note_p, &asc, &des, &wid);
+ asc *= Staffscale;
+ des *= Staffscale;
+ wid *= Staffscale;
+
+ rectab[reclim].w = gs_p->c[AX] + note_p->waccr;
+ rectab[reclim].e = rectab[reclim].w + wid;
+ rectab[reclim].n = note_p->c[RY] + asc;
+ rectab[reclim].s = note_p->c[RY] - des;
+
+ inc_reclim();
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: dobeamalt()
+ *
+ * Abstract: Set up rectangles for beams and alternation bars.
+ *
+ * Returns: void
+ *
+ * Description: This function puts into rectab rectangles for each beam or
+ * alternation bar on this staff in this score, where the thing
+ * is on the "place" side of the notes.
+ */
+
+static void
+dobeamalt(start_p, s, place)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+int place; /* above or below? */
+
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ struct GRPSYL *gs_p; /* point along a GRPSYL linked list */
+ int v; /* voice number */
+
+
+ debug(32, "dobeamalt file=%s line=%d s=%d place=%d", start_p->inputfile,
+ start_p->inputlineno, s, place);
+ /*
+ * Loop through this score's part of the MLL.
+ */
+ for (mainll_p = start_p->next; mainll_p != 0 &&
+ mainll_p->str != S_FEED; mainll_p = mainll_p->next) {
+ /*
+ * Whenever we find a structure for this staff (another
+ * measure of this staff), loop through its voices.
+ */
+ if (mainll_p->str == S_STAFF &&
+ mainll_p->u.staff_p->staffno == s) {
+
+ for (v = 0; v < MAXVOICES; v++) {
+ for (gs_p = mainll_p->u.staff_p->groups_p[v];
+ gs_p != 0; gs_p = gs_p->next) {
+ /*
+ * Whenever we find the first group of
+ * a nongrace beamed or alted set with
+ * the stem direction on the side we
+ * are dealing with, call onebeamalt()
+ * to put rectangle(s) in rectab.
+ * But not for cross staff beams.
+ * Grace groups are included in the
+ * following nongrace group's rectangle
+ * already.
+ */
+ if (gs_p->grpcont == GC_NOTES &&
+ gs_p->grpvalue == GV_NORMAL &&
+ gs_p->beamloc == STARTITEM &&
+ gs_p->beamto == CS_SAME) {
+
+ if (place == PL_ABOVE &&
+ gs_p->stemdir == UP ||
+ place == PL_BELOW &&
+ gs_p->stemdir == DOWN)
+
+ onebeamalt(gs_p);
+ }
+ }
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: onebeamalt()
+ *
+ * Abstract: Set up rectangle(s) for one beam or alternation bar.
+ *
+ * Returns: void
+ *
+ * Description: This function puts zero or more rectangles in rectab for the
+ * beam or alternation that starts at the given group. The longer
+ * and more slanted the beam/alternation is, the more rectangles
+ * will be necessary to enclose it without wasting a lot of space.
+ * If the beam/alt lies within the staff, there's no need to make
+ * any rectangles. All rectangles' inner edges are the center
+ * staff line.
+ */
+
+static void
+onebeamalt(gs_p)
+
+struct GRPSYL *gs_p; /* initially points to first group */
+
+{
+ float stemshift; /* how far a stem is from its group's X */
+ float x1, y1; /* coords of left end of beam/alt */
+ float x2, y2; /* coords of right end of beam/alt */
+
+
+ /*
+ * Set coords of the ends of the beam/alt. We are given the first
+ * group, but must search forward to the end to find the last group,
+ * being careful to ignore embedded grace groups. We adjust the X
+ * coords (for groups that can have stems) because stems are offset
+ * from their group's X. The Y coords can't always be based on the
+ * group boundaries, because there might be "with" lists on the
+ * abnormal (beam) side, and they don't affect the position of the beam.
+ */
+ x1 = gs_p->c[AX];
+ y1 = getstemendvert(gs_p);
+
+ while (gs_p != 0 && (gs_p->grpvalue == GV_ZERO ||
+ gs_p->beamloc != ENDITEM))
+ gs_p = gs_p->next;
+ if (gs_p == 0)
+ pfatal("beam or alt group has no ENDITEM");
+
+ x2 = gs_p->c[AX];
+ y2 = getstemendvert(gs_p);
+
+ stemshift = getstemshift(gs_p);
+
+ if (gs_p->basictime >= 2) {
+ /* the groups have stems (if first one does, others must too)*/
+ if (gs_p->stemdir == UP) {
+ x1 += stemshift;
+ x2 += stemshift;
+ } else {
+ x1 -= stemshift;
+ x2 -= stemshift;
+ }
+ }
+
+ /* make zero or more rectangles for this beam/alt */
+ linerects(x1, y1, x2, y2, gs_p->stemdir, halfstaffhi(gs_p->staffno));
+}
+\f
+/*
+ * Name: getstemendvert()
+ *
+ * Abstract: Find the vertical coord of the end of a stem.
+ *
+ * Returns: void
+ *
+ * Description: This function is given a GRPSYL of a group that has either a
+ * real, visible stem, or an invisible one (alt). If finds
+ * the relative vertical coordinate of the end of the stems
+ * farthest from the note head(s).
+ */
+
+static double
+getstemendvert(gs_p)
+
+struct GRPSYL *gs_p; /* the group in question */
+
+{
+ double y; /* the answer */
+
+
+ if (gs_p->nwith == 0 || gs_p->normwith == YES) {
+ /*
+ * Either there is no "with" list, or it's on the notes' end
+ * of the stem. So we can use the group boundary.
+ */
+ y = gs_p->stemdir == UP ? gs_p->c[RN] : gs_p->c[RS];
+ } else {
+ /*
+ * There is a "with" list at this end of the stem. Find where
+ * the end of the stem is by applying the stem's length to the
+ * farthest note on the opposite side.
+ */
+ if (gs_p->stemdir == UP)
+ y = gs_p->notelist[ gs_p->nnotes - 1 ].c[RY] +
+ gs_p->stemlen;
+ else
+ y = gs_p->notelist[ 0 ].c[RY] - gs_p->stemlen;
+ }
+
+ /* counteract the stem shortening that was done in finalstemadjust() */
+ if (gs_p->beamloc != NOITEM) {
+ if (gs_p->stemdir == UP) {
+ y += (W_WIDE * Stdpad / 2.0);
+ } else {
+ y -= (W_WIDE * Stdpad / 2.0);
+ }
+ }
+
+ return (y);
+}
+\f
+/*
+ * Name: linerects()
+ *
+ * Abstract: Set up rectangle(s) to contain a (possibly) slanted line.
+ *
+ * Returns: void
+ *
+ * Description: This function puts zero or more rectangles in rectab to contain
+ * a (possibly) slanted line. The longer and more slanted the
+ * line is, the more rectangles will be necessary to enclose it
+ * without wasting a lot of space. If the line lies within the
+ * staff, there's no need to make any rectangles. All rectangles'
+ * inner edges are the center staff line.
+ */
+
+static void
+linerects(x1, y1, x2, y2, side, halfstaff)
+
+double x1, y1; /* coords of left end of line */
+double x2, y2; /* coords of right end of line */
+int side; /* side to favor, UP or DOWN */
+double halfstaff; /* half the staff height */
+
+{
+ float slope, yintercept;/* of a line a STDPAD beyond beam/alt */
+ float deltax; /* width of one rectangle */
+ float leftx, rightx; /* X coord of sides of a rectangle */
+
+
+ /* if line is within staff, no need for any rectangles */
+ if (fabs(y1) < halfstaff && fabs(y2) < halfstaff)
+ return;
+
+ /*
+ * If this beam/alt is level, make one big rectangle, and get out.
+ */
+ if (y1 == y2) {
+ rectab[reclim].w = x1;
+ rectab[reclim].e = x2;
+ if (side == UP) {
+ rectab[reclim].n = y1;
+ rectab[reclim].s = 0;
+ } else {
+ rectab[reclim].n = 0;
+ rectab[reclim].s = y1;
+ }
+ inc_reclim();
+ return;
+ }
+
+ /*
+ * We may need multiple rectangles. Make them narrow enough so that
+ * the change in Y across the width of one is one STEPSIZE. The
+ * rightmost one will probably be narrower, using whatever room
+ * remains. The equation of our line is y = slope * x + yintercept.
+ */
+ slope = (y1 - y2) / (x1 - x2);
+ yintercept = y1 - slope * x1;
+ deltax = Stepsize / fabs(slope);
+
+ for (leftx = x1; leftx < x2; leftx += deltax) {
+ rightx = MIN(x2, leftx + deltax);
+ rectab[reclim].w = leftx;
+ rectab[reclim].e = rightx;
+ if (side == UP) {
+ rectab[reclim].n = slope * (slope > 0 ? rightx : leftx)
+ + yintercept;
+ rectab[reclim].s = 0;
+ } else {
+ rectab[reclim].n = 0;
+ rectab[reclim].s = slope * (slope > 0 ? leftx : rightx)
+ + yintercept;
+ }
+ inc_reclim();
+ }
+}
+\f
+/*
+ * Name: docurve()
+ *
+ * Abstract: Get point list and set up rectangles for tie/slur/bend/phrase.
+ *
+ * Returns: void
+ *
+ * Description: This function goes through all ties, slurs, bends, phrases for
+ * staff. The first time it is called for a staff (which is for
+ * place "above") it calls a function to set up the curve list.
+ * Whichever time it is called, it calls a function to put
+ * rectangles in rectab.
+ */
+
+static void
+docurve(start_p, s, place, do_which)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+int place; /* above or below? */
+int do_which; /* which stuff types are to be handled */
+
+{
+ struct MAINLL *mainll_p; /* loop through main linked list */
+ struct STUFF *stuff_p; /* point along a STUFF list */
+ float halfstaff; /* half the staff height */
+
+
+ debug(32, "docurve file=%s line=%d s=%d place=%d do_which=%d",
+ start_p->inputfile, start_p->inputlineno, s, place, do_which);
+ halfstaff = halfstaffhi(s);
+
+ /*
+ * Loop through this score's part of the MLL, looking for matching
+ * staffs.
+ */
+ for (mainll_p = start_p->next; mainll_p != 0 &&
+ mainll_p->str != S_FEED; mainll_p = mainll_p->next) {
+
+ if (mainll_p->str != S_STAFF ||
+ mainll_p->u.staff_p->staffno != s)
+ continue;
+
+ /* loop through each stuff of the indicated type */
+ for (stuff_p = mainll_p->u.staff_p->stuff_p;
+ stuff_p != 0; stuff_p = stuff_p->next){
+
+ switch (stuff_p->stuff_type) {
+ case ST_TIESLUR:
+ case ST_TABSLUR:
+ case ST_BEND:
+ case ST_PHRASE:
+ break; /* docurve works on these */
+ default:
+ continue; /* for some other function */
+ }
+
+ /*
+ * If we are to do phrases and this is not a phrase, or
+ * vice versa, skip this.
+ */
+ if ((do_which == DO_PHRASE) !=
+ (stuff_p->stuff_type == ST_PHRASE))
+ continue;
+
+ /*
+ * When we're in here the first time (for PL_ABOVE),
+ * call a function to set up the curve list. For
+ * everything but ST_PHRASE it also sets "place".
+ */
+ if (place == PL_ABOVE) {
+ switch (stuff_p->stuff_type) {
+ case ST_TIESLUR:
+ /* don't call tieslur_points now if the
+ * positions of the tie/slur's endpoints
+ * would change later due to CSS */
+ if (css_affects_tieslurbend(stuff_p,
+ mainll_p) == YES) {
+ break;
+ }
+ tieslur_points(mainll_p, stuff_p);
+ break;
+ case ST_TABSLUR:
+ tabslur_points(mainll_p, stuff_p);
+ break;
+ case ST_BEND:
+ /* don't call bend_points now if the
+ * positions of the bend's endpoints
+ * would change later due to CSS */
+ if (css_affects_tieslurbend(stuff_p,
+ mainll_p) == YES) {
+ break;
+ }
+ bend_points(mainll_p, stuff_p);
+ break;
+ case ST_PHRASE:
+ /* don't call phrase_points now if the
+ * positions of the phrase's endpoints
+ * would change later due to CSS */
+ if (css_affects_phrase(stuff_p,
+ mainll_p) == YES) {
+ break;
+ }
+ phrase_points(mainll_p, stuff_p);
+ break;
+ }
+ }
+
+ /*
+ * Make rectangles no matter what side of the staff the
+ * curve is supposed to be on, because, depending on
+ * how high or low the notes are, rectangles may be
+ * needed even on the opposite side you'd expect.
+ */
+ if (stuff_p->crvlist_p != 0) {
+ curverect(s, stuff_p, halfstaff);
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: curverect()
+ *
+ * Abstract: Put rectangles in rectab for a tie, slur, bend, or phrase.
+ *
+ * Returns: void
+ *
+ * Description: This function puts rectangles in rectab for a tie, slur, bend,
+ * or phrase. Each segment of the curve gets one or more
+ * rectangles, depending on how long and how slanted it is. To do
+ * this, we call curvepiecerect().
+ */
+
+static void
+curverect(s, stuff_p, halfstaff)
+
+int s; /* staff number */
+struct STUFF *stuff_p; /* the curve's STUFF */
+double halfstaff; /* half the staff height */
+
+{
+ struct CRVLIST *point_p; /* point at a phrase point */
+ float x1, y1; /* coords of left end of a segment */
+ float x2, y2; /* coords of right end of a segment */
+ float midx, midy; /* middle of one segment of a curve */
+
+
+ /*
+ * Loop through the curve list. For each pair of neighboring points,
+ * there is a segment of the curve. For items that are actually
+ * straight line segments, call curvepiecerect() once. But for actual
+ * curves, find the midpoint, and call curvepiecerect() for each half.
+ * This way we more closely approximate the real curve.
+ */
+ for (point_p = stuff_p->crvlist_p; point_p->next != 0;
+ point_p = point_p->next) {
+
+ x1 = point_p->x;
+ y1 = point_p->y;
+ x2 = point_p->next->x;
+ y2 = point_p->next->y;
+
+ if (stuff_p->stuff_type == ST_BEND ||
+ stuff_p->stuff_type == ST_TABSLUR) {
+ /* bend, or slur on tab or tabnote */
+ curvepiecerect(x1, y1, x2, y2, halfstaff);
+ } else {
+ /* a real curve */
+ midx = (x1 + x2) / 2.0;
+ midy = curve_y_at_x(stuff_p->crvlist_p, midx);
+ curvepiecerect(x1, y1, midx, midy, halfstaff);
+ curvepiecerect(midx, midy, x2, y2, halfstaff);
+ }
+ }
+}
+\f
+/*
+ * Name: curvepiecerect()
+ *
+ * Abstract: Put rects in rectab for a piece of a tie, slur, bend, or phrase.
+ *
+ * Returns: void
+ *
+ * Description: This function puts rectangles in rectab for one piece of a
+ * curve. The piece gets one or more rectangles, depending on how
+ * long and how slanted it is.
+ */
+
+static void
+curvepiecerect(x1, y1, x2, y2, halfstaff)
+
+double x1, y1; /* coords of left end of the piece */
+double x2, y2; /* coords of right end of the piece */
+double halfstaff; /* half the staff height */
+
+{
+ float slope, yintercept;/* of a line a segment */
+ float deltax; /* width of one rectangle */
+ float leftx, rightx; /* X coord of sides of a rectangle */
+
+
+ /* if whole piece is within the staff, no rectangles are needed */
+ if (fabs(y1) < halfstaff && fabs(y2) < halfstaff)
+ return;
+
+ /*
+ * If this piece is level, make 1 big rectangle, and continue.
+ */
+ if (y1 == y2) {
+ rectab[reclim].w = x1;
+ rectab[reclim].e = x2;
+ rectab[reclim].n = MAX(y1 + 2 * Stdpad, 0.0);
+ rectab[reclim].s = MIN(y1 - 2 * Stdpad, 0.0);
+ inc_reclim();
+ return;
+ }
+
+ /*
+ * We may need multiple rectangles. Make them narrow enough so that
+ * the change in Y across the width of one is one Stepsize. The
+ * rightmost one will probably be narrower, using whatever room
+ * remains. The equation of our line is
+ * y = slope * x + yintercept
+ * Initially each rectangle only includes its segment (plus padding),
+ * but then we extend it to reach the center line of the staff.
+ */
+ slope = (y1 - y2) / (x1 - x2);
+ yintercept = y1 - slope * x1;
+ deltax = Stepsize / fabs(slope);
+
+ for (leftx = x1; leftx < x2; leftx += deltax) {
+ rightx = MIN(x2, leftx + deltax);
+
+ rectab[reclim].w = leftx;
+ rectab[reclim].e = rightx;
+
+ /*
+ * For north and south boundaries, use the side of the rect
+ * that sticks out more, to err on the side of making the rect
+ * big enough. Also add in padding, to 1) allow for the fact
+ * that the real curve probably bulges out beyond our segment
+ * approximation, and 2) because we don't want anything
+ * actually touching the curve.
+ */
+ rectab[reclim].n = slope * (slope > 0.0 ? rightx : leftx) +
+ yintercept + 2.0 * Stdpad;
+ rectab[reclim].s = slope * (slope < 0.0 ? rightx : leftx) +
+ yintercept - 2.0 * Stdpad;
+
+ /* rectangle must reach the center line of the staff */
+ if (rectab[reclim].n < 0.0)
+ rectab[reclim].n = 0.0;
+ if (rectab[reclim].s > 0.0)
+ rectab[reclim].s = 0.0;
+
+ inc_reclim();
+ }
+}
+\f
+/*
+ * Name: dotuplet()
+ *
+ * Abstract: Set up rectangles for tuplet brackets.
+ *
+ * Returns: void
+ *
+ * Description: This function puts into rectab rectangles for each tuplet
+ * bracket on this staff in this score, where the thing is on
+ * the "place" side of the notes.
+ */
+
+
+static void
+dotuplet(start_p, s, place)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+int place; /* above or below? */
+
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ struct GRPSYL *gs_p; /* point along a GRPSYL linked list */
+ int v; /* voice number */
+
+
+ debug(32, "dotuplet file=%s line=%d s=%d place=%d", start_p->inputfile,
+ start_p->inputlineno, s, place);
+
+ /* tuplet brackets are never printed on tablature staffs */
+ if (is_tab_staff(s))
+ return;
+
+ /*
+ * Loop through this score's part of the MLL.
+ */
+ for (mainll_p = start_p->next; mainll_p != 0 &&
+ mainll_p->str != S_FEED; mainll_p = mainll_p->next) {
+ /*
+ * Whenever we find a structure for this staff (another
+ * measure of this staff), loop through its voices.
+ */
+ if (mainll_p->str == S_STAFF &&
+ mainll_p->u.staff_p->staffno == s) {
+
+ for (v = 0; v < MAXVOICES; v++) {
+ for (gs_p = mainll_p->u.staff_p->groups_p[v];
+ gs_p != 0; gs_p = gs_p->next) {
+ /*
+ * Whenever we find the first group of
+ * a tuplet with a bracket on the
+ * "place" side of the group, call
+ * onetuplet() to put rectangle(s) in
+ * rectab.
+ */
+ if ((gs_p->tuploc == STARTITEM ||
+ gs_p->tuploc == LONEITEM) &&
+ gs_p->printtup != PT_NEITHER) {
+
+ if (tupdir(gs_p, mainll_p->u.
+ staff_p) == place)
+
+ onetuplet(mainll_p->u.
+ staff_p, gs_p, place);
+ }
+ }
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: onetuplet()
+ *
+ * Abstract: Set up rectangle(s) for one tuplet bracket or number.
+ *
+ * Returns: void
+ *
+ * Description: If this tuplet is not going to be given a bracket (like because
+ * its notes are already beamed), this function just makes one
+ * rectangle, for the number. Otherwise, this function puts zero
+ * or more rectangles in rectab for the tuplet that starts at the
+ * given group. The longer and more slanted the tuplet bracket
+ * is, the more rectangles will be necessary to enclose it without
+ * wasting a lot of space. All rectangles' inner edges are the
+ * center staff line.
+ */
+
+static void
+onetuplet(staff_p, start_p, place)
+
+struct STAFF *staff_p; /* point to the staff we're on */
+struct GRPSYL *start_p; /* points to first group in tuplet */
+int place; /* above or below? */
+
+{
+ struct GRPSYL *gs_p; /* point to a group in tuplet */
+ float stemshift; /* how far a stem is from its group's X */
+ float x1, y1; /* coords of left end of beam/alt */
+ float x2, y2; /* coords of right end of beam/alt */
+ float numeast, numwest; /* horizontal coords of the tuplet number */
+ float height; /* height of the tuplet number */
+
+
+ /*
+ * Set coords of the ends of the tuplet. We are given the first
+ * group, but must search forward to the end to find the last group,
+ * being careful to ignore embedded grace groups. We adjust the X
+ * coords because brackets reach beyond their group's X.
+ */
+ x1 = start_p->c[AX];
+ y1 = (place == PL_ABOVE ? start_p->c[RN] : start_p->c[RS])
+ + start_p->tupextend;
+
+ for (gs_p = start_p; gs_p != 0 && (gs_p->grpvalue == GV_ZERO ||
+ gs_p->tuploc != ENDITEM && gs_p->tuploc != LONEITEM);
+ gs_p = gs_p->next)
+ ;
+ if (gs_p == 0)
+ pfatal("tuplet has no ENDITEM");
+
+ x2 = gs_p->c[AX];
+ y2 = (place == PL_ABOVE ? gs_p->c[RN] : gs_p->c[RS]) + gs_p->tupextend;
+
+ /*
+ * If there is not going to be a bracket, create one rectangle for the
+ * tuplet number, and return.
+ */
+ if (tupgetsbrack(start_p) == NO) {
+ (void)tupnumsize(start_p, &numwest, &numeast, &height, staff_p);
+ rectab[reclim].n = (y1 + y2) / 2 + height / 2;
+ rectab[reclim].s = (y1 + y2) / 2 - height / 2;
+ rectab[reclim].w = numwest;
+ rectab[reclim].e = numeast;
+
+ inc_reclim();
+ return;
+ }
+
+ /* there is going to be a bracket; extend x coords to reach to end */
+ stemshift = getstemshift(gs_p);
+
+ x1 -= stemshift;
+ x2 += stemshift;
+
+ /* make zero or more rectangles for this bracket */
+ linerects(x1, y1, x2, y2, place == PL_ABOVE ? UP : DOWN,
+ halfstaffhi(gs_p->staffno));
+}
+\f
+/*
+ * Name: domiscstuff()
+ *
+ * Abstract: Set up rectangles and vert coords for miscellaneous STUFF.
+ *
+ * Returns: void
+ *
+ * Description: This function puts into rectab a rectangle for each STUFF
+ * structure in the "place" relationship to the given staff on
+ * this score, except for stuff types that have special,
+ * dedicated functions for their type. It also sets their
+ * relative vertical coordinates.
+ */
+
+static void
+domiscstuff(start_p, s, place, do_which)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+int place; /* above, below, or between? */
+unsigned long do_which; /* which stuff types are to be handled */
+
+{
+ struct MAINLL *mainll_p; /* loop through main linked list */
+ struct STUFF *stuff_p; /* point along a STUFF list */
+ float high; /* height of a rectangle */
+ float len; /* length of a cresc/descresc */
+ float lowpart; /* dist between stuff's Y and S */
+ float dist; /* how close chord can get to staff */
+ int stype; /* stuff type */
+
+
+ debug(32, "domiscstuff file=%s line=%d s=%d place=%d do_which=%ld",
+ start_p->inputfile, start_p->inputlineno, s, place, do_which);
+ /*
+ * Loop through this score's part of the MLL. Whenever we find a
+ * structure for this staff (another measure), loop through its
+ * STUFF list, dealing with each STUFF that is above, below, or
+ * between, as specified by "place".
+ */
+ for (mainll_p = start_p->next; mainll_p != 0 &&
+ mainll_p->str != S_FEED; mainll_p = mainll_p->next) {
+
+ if (mainll_p->str != S_STAFF ||
+ mainll_p->u.staff_p->staffno != s) {
+ continue;
+ }
+
+ for (stuff_p = mainll_p->u.staff_p->stuff_p;
+ stuff_p != 0; stuff_p = stuff_p->next) {
+
+ if (stuff_p->place != place) {
+ continue;
+ }
+
+ stype = stuff_p->stuff_type;
+
+ /* if wrong type for this pass, exit */
+ if (stype == ST_MUSSYM) {
+ if ((do_which & (1L << MK_MUSSYM)) == 0)
+ continue;
+ } else if (stype == ST_OCTAVE) {
+ if ((do_which & (1L << MK_OCTAVE)) == 0)
+ continue;
+ } else if (stype != ST_PHRASE &&
+ stuff_p->modifier == TM_DYN) {
+ if ((do_which & (1L << MK_DYN)) == 0)
+ continue;
+ } else if (stype != ST_PHRASE &&
+ IS_CHORDLIKE(stuff_p->modifier)) {
+ if ((do_which & (1L << MK_CHORD)) == 0)
+ continue;
+ } else if (IS_TEXT(stype)) {
+ if ((do_which & (1L << MK_OTHERTEXT)) == 0)
+ continue;
+ }
+
+ /*
+ * We found a "stuff" that needs to be positioned.
+ * First find its total height, and the height of the
+ * part of it below its Y coord.
+ */
+ /* avoid 'used before set' warning */
+ high = lowpart = 0.0;
+
+ /* handle various types differently */
+ switch (stype) {
+ case ST_PEDAL:
+ case ST_PHRASE:
+ case ST_TIESLUR:
+ case ST_TABSLUR:
+ case ST_BEND:
+ case ST_MIDI:
+ /* don't handle these types here; */
+ /* they have their own subroutines */
+ continue;
+
+ case ST_OCTAVE:
+ case ST_ROM:
+ case ST_BOLD:
+ case ST_ITAL:
+ case ST_BOLDITAL:
+ case ST_MUSSYM:
+ /* high is string's height */
+ high = strheight( stuff_p->string);
+ lowpart = strdescent( stuff_p->string);
+
+ /*
+ * If a chord grid is to be printed under the
+ * string, the Y and N of the stuff remain
+ * unchanged, but its S is lowered by the total
+ * height of the grid. So add its height to
+ * both "high" and "lowpart".
+ */
+ if (stuff_p->modifier == TM_CHORD && svpath(s,
+ GRIDSWHEREUSED)->gridswhereused == YES) {
+ struct GRID *grid_p;
+ float gnorth, gsouth;
+
+ grid_p = findgrid(stuff_p->string);
+ /* if none, skip this; stuff.c warned*/
+ if (grid_p == 0)
+ break;
+
+ gridsize(grid_p, stuff_p->all ? 0 :
+ mainll_p->u.staff_p->staffno,
+ &gnorth, &gsouth,
+ (float *)0, (float *)0);
+
+ high += gnorth - gsouth;
+ lowpart += gnorth - gsouth;
+ }
+ break;
+
+ case ST_CRESC:
+ case ST_DECRESC:
+ /* height depends on length */
+ len = stuff_p->c[AE] - stuff_p->c[AW];
+
+ if (len < 0.5)
+ high = 2.00 * STEPSIZE + 2 * STDPAD;
+ else if (len < 2.0)
+ high = 2.67 * STEPSIZE + 2 * STDPAD;
+ else
+ high = 3.33 * STEPSIZE + 2 * STDPAD;
+
+ if (stuff_p->all)
+ high *= Score.staffscale;
+ else
+ high *= Staffscale;
+
+ lowpart = high / 2;
+
+ break;
+
+ default:
+ pfatal("unknown stuff type (%d)", stype);
+ }
+
+ /*
+ * Now find "dist", the minimum distance it should be
+ * put from the staff.
+ */
+ if (stuff_p->dist_usage == SD_NONE) {
+ /*
+ * The user didn't specify the dist, so we get
+ * it from the appropriate parameter or hard-
+ * coded value, as the case may be. For
+ * parameters, if the stuff belongs to the
+ * score as a whole ("all"), use the Score
+ * value instead of svpath.
+ */
+ /* if "dyn", fake to use same logic as cresc */
+ if (stuff_p->modifier == TM_DYN)
+ stype = ST_CRESC;
+ switch (stype) {
+ case ST_ROM:
+ case ST_BOLD:
+ case ST_ITAL:
+ case ST_BOLDITAL:
+ if (stuff_p->all) {
+ if (IS_CHORDLIKE(
+ stuff_p->modifier)) {
+ dist =
+ halfstaffhi(s) +
+ STEPSIZE *
+ Score.staffscale *
+ Score.chorddist;
+ } else {
+ dist =
+ halfstaffhi(s) +
+ STEPSIZE *
+ Score.staffscale *
+ Score.dist;
+ }
+ } else {
+ if (IS_CHORDLIKE(
+ stuff_p->modifier)) {
+ dist =
+ halfstaffhi(s) +
+ Stepsize *
+ svpath(s, CHORDDIST)->
+ chorddist;
+ } else {
+ dist =
+ halfstaffhi(s) +
+ Stepsize *
+ svpath(s, DIST)->dist;
+ }
+ }
+ break;
+ case ST_CRESC:
+ case ST_DECRESC:
+ if (stuff_p->all) {
+ dist = halfstaffhi(s) +
+ STEPSIZE * Score.staffscale *
+ Score.dyndist;
+ } else {
+ dist = halfstaffhi(s) +
+ Stepsize * svpath(s,
+ DYNDIST)->dyndist;
+ }
+ break;
+ default:
+ dist = 0;
+ break;
+ }
+ } else {
+ /* the user specified the dist, so use that */
+ if (stuff_p->all) {
+ dist = halfstaffhi(s) +
+ STEPSIZE * stuff_p->dist;
+ } else {
+ dist = halfstaffhi(s) +
+ Stepsize * stuff_p->dist;
+ }
+ }
+
+ if (stuff_p->dist_usage == SD_FORCE) {
+ /*
+ * The user is forcing this dist, so don't
+ * stack; just put it there. Note: the user
+ * cannot specify "dist" for "between" items.
+ */
+ if (stuff_p->place == PL_ABOVE) {
+ rectab[reclim].n = dist + high;
+ rectab[reclim].s = dist;
+ stuff_p->c[RS] = dist;
+ } else { /* PL_BELOW */
+ rectab[reclim].n = -dist;
+ rectab[reclim].s = -dist - high;
+ stuff_p->c[RS] = -dist - high;
+ }
+ rectab[reclim].e = stuff_p->c[AE];
+ rectab[reclim].w = stuff_p->c[AW];
+ inc_reclim();
+ } else {
+ /*
+ * Stack the usual way. For the case of
+ * "between", stackit() will ignore "dist".
+ */
+ stuff_p->c[RS] = stackit(stuff_p->c[AW],
+ stuff_p->c[AE], high, dist, place);
+ }
+
+ stuff_p->c[RN] = stuff_p->c[RS] + high;
+ stuff_p->c[RY] = stuff_p->c[RS] + lowpart;
+ }
+ }
+}
+\f
+/*
+ * Name: dolyrics()
+ *
+ * Abstract: Set up rectangles and vert coords for lyrics.
+ *
+ * Returns: void
+ *
+ * Description: This function puts into rectab a rectangle for each verse in
+ * the "place" relationship to the given staff on this score.
+ */
+
+static void
+dolyrics(start_p, s, place)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+int place; /* above, below, or between? */
+
+{
+ int *versenums; /* malloc'ed array of verse numbers in score */
+ struct MAINLL *mainll_p;/* point along main linked list */
+ struct STAFF *staff_p; /* point at a staff structure */
+ struct GRPSYL *gs_p; /* point at a syllable */
+ float protrude; /* farthest protrusion of rectangle */
+ int vfound; /* number of verse numbers found in score */
+ int v; /* verse number */
+ int begin, end, delta; /* for looping over verses in proper order */
+ float dist; /* how close lyrics can get to staff */
+ float farwest, fareast; /* farthest east and west of any syllable */
+ float baseline; /* baseline of a verse of syllables */
+ float maxasc, maxdes; /* max ascent & descent of syllables */
+ int gotverse0; /* is there a verse 0 (centered verse)? */
+ int gototherverse; /* is there a normal verse (not 0)? */
+ int n, k, j; /* loop variables */
+
+
+ debug(32, "dolyrics file=%s line=%d s=%d place=%d", start_p->inputfile,
+ start_p->inputlineno, s, place);
+ /* if there are no lyrics in this song, get out now */
+ if (Maxverses == 0)
+ return;
+
+ /*
+ * Allocate an array containing room for all the verse numbers used in
+ * this score. Maxverses is the number of verse numbers used in the
+ * whole user input, so this will certainly be enough.
+ */
+ MALLOCA(int, versenums, Maxverses);
+
+ /*
+ * Loop through this score's part of the MLL, noting whether verse 0
+ * (the centered verse) and/or other verses exist on the "place" side
+ * of the staff. We have to find this out before actually processing
+ * the verses, because verse 0 is to be treated as a normal verse if
+ * and only if there are no other verses.
+ */
+ gotverse0 = NO;
+ gototherverse = NO;
+ for (mainll_p = start_p->next; mainll_p != 0 &&
+ mainll_p->str != S_FEED; mainll_p = mainll_p->next) {
+ /*
+ * Whenever we find a structure for this staff (another
+ * measure of this staff), loop through its verse headcells.
+ */
+ if (mainll_p->str == S_STAFF &&
+ mainll_p->u.staff_p->staffno == s) {
+ staff_p = mainll_p->u.staff_p;
+ for (n = 0; n < staff_p->nsyllists; n++) {
+ if (staff_p->sylplace[n] == place) {
+ if (staff_p->syls_p[n]->vno == 0)
+ gotverse0 = YES;
+ else
+ gototherverse = YES;
+ }
+ }
+ }
+ }
+
+ /* if no verses, get out now */
+ if (gotverse0 == NO && gototherverse == 0) {
+ FREE(versenums);
+ return;
+ }
+
+ /*
+ * Loop through this score's part of the MLL, recording all the verse
+ * numbers that occur on the "place" side of the staff in versenums[].
+ * Verse 0 may or may not be included, depending on the above results.
+ * Also set farwest and fareast.
+ */
+ vfound = 0; /* no verses have been found yet */
+ farwest = PGWIDTH; /* init it all the way east */
+ fareast = 0; /* init it all the way west */
+ for (mainll_p = start_p->next; mainll_p != 0 &&
+ mainll_p->str != S_FEED; mainll_p = mainll_p->next) {
+ /*
+ * Whenever we find a structure for this staff (another
+ * measure of this staff), loop through its verse headcells.
+ */
+ if (mainll_p->str == S_STAFF &&
+ mainll_p->u.staff_p->staffno == s) {
+
+ staff_p = mainll_p->u.staff_p;
+
+ for (n = 0; n < staff_p->nsyllists; n++) {
+
+ if (staff_p->sylplace[n] == place) {
+ /*
+ * We found a verse number. Search the
+ * the array to see if it's already
+ * been found. If not, insert it into
+ * versenums[] in the right place, so
+ * that they'll end up being in order
+ * (actually, reverse order).
+ */
+ v = staff_p->syls_p[n]->vno;
+ /* ignore verse 0 if others exist */
+ if (v == 0 && gototherverse == YES)
+ continue;
+ for (k = 0; k < vfound &&
+ v < versenums[k]; k++) {
+ ;
+ }
+ if (k == vfound || v > versenums[k]) {
+ for (j = vfound; j > k; j--) {
+ versenums[j] =
+ versenums[j-1];
+ }
+ versenums[k] = v;
+ vfound++; /* found one more */
+ }
+
+ /*
+ * If any syl sticks out farther than
+ * any previous one, extend farwest or
+ * fareast.
+ */
+ for (gs_p = staff_p->syls_p[n];
+ gs_p != 0; gs_p = gs_p->next) {
+
+ if (gs_p->c[AW] < farwest)
+ farwest = gs_p->c[AW];
+ if (gs_p->c[AE] > fareast)
+ fareast = gs_p->c[AE];
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Enclose all the syllables of all the verses (of this place) in one
+ * big rectangle. Pad on west and east by 8 step sizes. Pretend the
+ * rectangle is PGHEIGHT high. We don't actually know yet how high
+ * it is, and this will prevent it from getting between the staff and
+ * anything else. Later in this function we will correct the entry
+ * that stackit put in rectab, to reflect the true height. For above
+ * and below cases, don't let it get any closer than 2 step sizes to
+ * the staff. The half-height of a one-line staff is regarded as 1
+ * instead of the true 0, to give a little breathing room.
+ */
+ if (place == PL_BETWEEN)
+ dist = 0;
+ else
+ dist = halfstaffhi(s) + 2.0 * Stepsize;
+
+ (void)stackit(farwest - 8 * STEPSIZE, fareast + 8 * STEPSIZE, PGHEIGHT,
+ dist, place);
+
+ /*
+ * Find the greatest protrusion of any currently existing rectangle
+ * that horizontally is within the span of our new rectangle. That's
+ * the same as the top or bottom of the new rectangle.
+ */
+ if (place == PL_BELOW)
+ protrude = rectab[reclim - 1].n;
+ else
+ protrude = rectab[reclim - 1].s;
+
+ /*
+ * Loop through the verses, from the inside out. setting the relative
+ * vertical coords of their syllables. When necessary, we also insert
+ * new syllables on the next score for continuing underscores.
+ */
+ if (place == PL_BELOW) { /* work downward from staff */
+ begin = vfound - 1; /* first verse number */
+ end = -1; /* beyond last verse number */
+ delta = -1;
+ } else { /* above and between both work upwards from bottom */
+ begin = 0; /* last verse number */
+ end = vfound; /* before first verse number */
+ delta = 1;
+ }
+ for (n = begin; n != end; n += delta) {
+ /*
+ * Find the farthest any syllable ascends and descends from the
+ * baseline of the verse.
+ */
+ getvsize(start_p, s, place, versenums[n], &maxasc, &maxdes);
+
+ /*
+ * Set the baseline for this verse, based on where we're
+ * pushing up against (the last verse we did, or earlier
+ * things), and how far this verse sticks out.
+ */
+ if (place == PL_BELOW)
+ baseline = protrude - maxasc;
+ else /* above or between */
+ baseline = protrude + maxdes;
+
+ /* set syllables' vertical coords; continue underscores */
+ setsylvert(start_p, s, place, versenums[n], baseline);
+
+ /* set new lower bound, for next time through loop */
+ if (place == PL_BELOW)
+ protrude = baseline - maxdes;
+ else /* above or between */
+ protrude = baseline + maxasc;
+
+ } /* for every verse */
+
+ /*
+ * If there was a verse 0 (centered verse) and also normal verses, then
+ * in the above code we have handled only the normal verses, and we now
+ * need to handle verse 0.
+ */
+ if (gotverse0 == YES && gototherverse == YES) {
+ float mid; /* RY of the middle of the normal verses */
+ struct RECTAB rec; /* one rectangle */
+
+ /* get ascent and descent of verse 0 */
+ getvsize(start_p, s, place, 0, &maxasc, &maxdes);
+
+ /*
+ * We will use stackit's "dist" mechanism to try to get verse 0
+ * to line up with the center of the other verses. The last
+ * rectangle in rectab is currently the normal verses', but the
+ * one coord isn't really set right yet. Fortunately, the
+ * "protrude" variable is what we need for that coord.
+ */
+ if (place == PL_BELOW) {
+ mid = (rectab[reclim - 1].n + protrude) / 2.0;
+ dist = -mid - (maxasc + maxdes) / 2.0;
+ } else {
+ mid = (protrude + rectab[reclim - 1].s) / 2.0;
+ dist = mid - (maxasc + maxdes) / 2.0;
+ }
+
+ /*
+ * Find the easternmost and westernmost points of verse 0.
+ * It's easier to loop through all the syllables than to try to
+ * find the first and last syllables on the line.
+ */
+ farwest = PGWIDTH; /* init it all the way east */
+ fareast = 0; /* init it all the way west */
+ for (mainll_p = start_p->next;
+ mainll_p != 0 && mainll_p->str != S_FEED;
+ mainll_p = mainll_p->next) {
+
+ if (mainll_p->str != S_STAFF ||
+ mainll_p->u.staff_p->staffno != s)
+ continue;
+
+ staff_p = mainll_p->u.staff_p;
+ for (n = 0; n < staff_p->nsyllists; n++) {
+ if (staff_p->sylplace[n] == place &&
+ staff_p->syls_p[n]->vno == 0) {
+ for (gs_p = staff_p->syls_p[n];
+ gs_p != 0; gs_p = gs_p->next) {
+
+ if (gs_p->c[AW] < farwest)
+ farwest = gs_p->c[AW];
+ if (gs_p->c[AE] > fareast)
+ fareast = gs_p->c[AE];
+ }
+ }
+ }
+ }
+
+ /*
+ * Squeeze the regular verses' rectangle to zero so that it
+ * won't affect verse 0's. We hope they wouldn't interfere
+ * anyway, but the +8 and -8 might make them. The regular
+ * verses' rectangle will be corrected later anyway.
+ */
+ rectab[reclim - 1].n = rectab[reclim - 1].s = 0;
+
+ /*
+ * Stack verse 0's rectangle and set its baseline. We have to
+ * play games with "place", because for "between" stackit
+ * ignores "dist", but we need it to use "dist".
+ */
+ baseline = stackit(farwest - 8 * STEPSIZE,
+ fareast + 8 * STEPSIZE, maxasc + maxdes, dist,
+ place == PL_BETWEEN ? PL_ABOVE : place) + maxdes;
+
+ /*
+ * Switch verse 0's rectangle and the normal verses' so that
+ * the later code can always use reclim-1 for the normal.
+ */
+ rec = rectab[reclim - 2];
+ rectab[reclim - 2] = rectab[reclim - 1];
+ rectab[reclim - 1] = rec;
+
+ setsylvert(start_p, s, place, 0, baseline);
+ }
+
+ /*
+ * Now that we know how high this rectangle really is, correct it in
+ * rectab. Make it reach the center of the staff/baseline, to prevent
+ * anything later from getting in between there.
+ */
+ if (place == PL_BELOW) {
+ rectab[reclim - 1].n = 0;
+ rectab[reclim - 1].s = protrude;
+ } else { /* above or between */
+ rectab[reclim - 1].n = protrude;
+ rectab[reclim - 1].s = 0;
+ }
+
+ FREE(versenums);
+}
+\f
+/*
+ * Name: getvsize()
+ *
+ * Abstract: Get the maximum ascent and descent for a verse on a score.
+ *
+ * Returns: void
+ *
+ * Description: This function returns (through pointers) the maximum ascent and
+ * descent of a verse on this score. Usually this is the standard
+ * ascent and descent of the font, but it could be greater if
+ * there are font or size changes inside some syllable.
+ */
+
+static void
+getvsize(start_p, s, place, v, maxasc_p, maxdes_p)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+int place; /* above, below, or between? */
+int v; /* verse number */
+float *maxasc_p, *maxdes_p; /* ascent and descent to be returned */
+
+{
+ int lyricsfont; /* that is set for this staff */
+ int lyricssize; /* that is set for this staff */
+ float asc, des; /* max ascent & descent of syllables */
+ struct MAINLL *mainll_p;/* point along main linked list */
+ struct STAFF *staff_p; /* point at a staff structure */
+ struct GRPSYL *gs_p; /* point at a syllable */
+ int k; /* loop variable */
+
+
+ /*
+ * Get the standard max ascent and descent for any syllable.
+ */
+ lyricsfont = svpath(s, LYRICSFONT)->lyricsfont;
+ lyricssize = svpath(s, LYRICSSIZE)->lyricssize;
+ *maxasc_p = fontascent(lyricsfont, lyricssize) * Staffscale;
+ *maxdes_p = fontdescent(lyricsfont, lyricssize) * Staffscale;
+
+ /*
+ * Find the farthest any syllable ascends and descends from the
+ * baseline of the verse. Start with the standard amount for this font
+ * size. If the loop finds any weird syllable with bigger characters
+ * embedded, they will be increased.
+ */
+ for (mainll_p = start_p->next; mainll_p != 0 && mainll_p->str
+ != S_FEED; mainll_p = mainll_p->next) {
+
+ if (mainll_p->str != S_STAFF ||
+ mainll_p->u.staff_p->staffno != s)
+ continue;
+
+ /* found a STAFF of the number we're dealing with */
+ staff_p = mainll_p->u.staff_p;
+
+ /*
+ * See if this verse is present in this staff,
+ * and if so, loop through it.
+ */
+ for (k = 0; k < staff_p->nsyllists; k++) {
+
+ if (staff_p->sylplace[k] == place &&
+ staff_p->syls_p[k]->vno == v) {
+
+ for (gs_p = staff_p->syls_p[k]; gs_p != 0;
+ gs_p = gs_p->next) {
+ /*
+ * If asc or des is greater
+ * for this syl, save it.
+ */
+ asc = strascent(gs_p->syl);
+
+ des = strdescent(gs_p->syl);
+
+ if (asc > *maxasc_p)
+ *maxasc_p = asc;
+ if (des > *maxdes_p)
+ *maxdes_p = des;
+ }
+
+ /* no need to look any more */
+ break;
+ }
+ }
+ } /* for every MLL stucture in score */
+}
+\f
+/*
+ * Name: setsylvert()
+ *
+ * Abstract: Set the maximum ascent and descent for a verse on a score.
+ *
+ * Returns: void
+ *
+ * Description: This function, using the given baseline, sets the relative
+ * vertical coords of each syllable in the verse on this score.
+ * If there are any nonnull syllables, it calls a function to
+ * continue underscores if need be.
+ */
+
+static void
+setsylvert(start_p, s, place, v, baseline)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+int place; /* above, below, or between? */
+int v; /* verse number */
+double baseline; /* baseline of a verse of syllables */
+
+{
+ struct MAINLL *mainll_p;/* point along main linked list */
+ struct STAFF *staff_p; /* point at a staff structure */
+ struct GRPSYL *gs_p; /* point at a syllable */
+ struct MAINLL *laststaff_p; /* point last staff that has a syllable */
+ struct GRPSYL *lastgs_p;/* point at last nonnull syllable in a verse */
+ int k; /* loop variable */
+
+
+ /*
+ * Loop through all these syllables as before, setting their relative
+ * vertical coords.
+ */
+ lastgs_p = 0; /* set later to last nonnull syl, if exists */
+ laststaff_p = 0; /* set later to staff containing lastgs_p */
+
+ for (mainll_p = start_p->next; mainll_p != 0 && mainll_p->str
+ != S_FEED; mainll_p = mainll_p->next) {
+
+ if (mainll_p->str != S_STAFF ||
+ mainll_p->u.staff_p->staffno != s)
+ continue;
+
+ /* found a STAFF of the number we're dealing with */
+ staff_p = mainll_p->u.staff_p;
+
+ /*
+ * See if this verse is present in this staff,
+ * and if so, loop through it.
+ */
+ for (k = 0; k < staff_p->nsyllists; k++) {
+
+ if (staff_p->sylplace[k] == place &&
+ staff_p->syls_p[k]->vno == v) {
+
+ for (gs_p = staff_p->syls_p[k]; gs_p != 0;
+ gs_p = gs_p->next) {
+
+ if (gs_p->syl == 0) {
+ continue;
+ }
+
+ gs_p->c[RY] = baseline;
+
+ gs_p->c[RN] = baseline
+ + strascent(gs_p->syl);
+
+ gs_p->c[RS] = baseline
+ - strdescent(gs_p->syl);
+
+ /* remember last nonnull syl */
+ if (gs_p->syl[0] != '\0') {
+ lastgs_p = gs_p;
+ laststaff_p = mainll_p;
+ }
+ }
+ }
+ }
+ } /* for every MLL stucture in score */
+
+ /*
+ * At this point, if this score has any nonnull syllables for
+ * this verse, lastgs_p points at the last one and laststaff_p
+ * points at its STAFF. If that last syllable ends in '_' or
+ * '-', we may need to continue this character onto the next
+ * score, so call a function to do that.
+ */
+ if (lastgs_p != 0 && has_extender(lastgs_p->syl))
+ cont_extender(laststaff_p, place, v);
+}
+\f
+/*
+ * Name: dopedal()
+ *
+ * Abstract: Set a rectangle for pedal marks, if there are any.
+ *
+ * Returns: void
+ *
+ * Description: This function puts a rectangle into rectab for pedal marks, if
+ * there are any on this score. It also sets their relative
+ * vertical coordinates.
+ */
+
+static void
+dopedal(start_p, s)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+
+{
+ struct MAINLL *mainll_p; /* loop through main linked list */
+ struct STUFF *stuff_p; /* point along a STUFF list */
+ float protrude; /* farthest protrusion of rectangle */
+ float lowpoint; /* the lowest any mark goes */
+ float asc; /* ascent of a pedal mark */
+ float hi; /* height of a pedal mark */
+ int k; /* loop variable */
+
+
+ debug(32, "dopedal file=%s line=%d s=%d", start_p->inputfile,
+ start_p->inputlineno, s);
+ /*
+ * Find the greatest protrusion of any currently existing rectangle.
+ */
+ protrude = 0;
+ for (k = 0; k < reclim; k++) {
+ if (rectab[k].s < protrude)
+ protrude = rectab[k].s;
+ }
+
+ lowpoint = 0;
+
+ /*
+ * Loop through this score's part of the MLL. Whenever we find a
+ * structure for this staff (another measure), loop through its
+ * STUFF list, setting coords for each pedal mark.
+ */
+ for (mainll_p = start_p->next; mainll_p != 0 &&
+ mainll_p->str != S_FEED; mainll_p = mainll_p->next) {
+
+ if (mainll_p->str != S_STAFF ||
+ mainll_p->u.staff_p->staffno != s)
+ continue;
+
+ for (stuff_p = mainll_p->u.staff_p->stuff_p;
+ stuff_p != 0; stuff_p = stuff_p->next) {
+
+ if (stuff_p->stuff_type != ST_PEDAL)
+ continue;
+
+ /*
+ * Whichever pedal character this is, always use
+ * C_BEGPED if pedstyle is P_LINE and the "Ped." string
+ * for the other cases. For the former, all three
+ * characters are the same height; and for the latter,
+ * this string is taller than the "*". This also
+ * handles the pedal continuation situation.
+ */
+ stuff_p->c[RN] = protrude;
+ if (svpath(s, PEDSTYLE)->pedstyle == P_LINE) {
+ asc = ascent(FONT_MUSIC, DFLT_SIZE, C_BEGPED);
+ hi = height(FONT_MUSIC, DFLT_SIZE, C_BEGPED);
+ } else { /* P_PEDSTAR or P_ALTPEDSTAR */
+ asc = strascent(Ped_start);
+ hi = strheight(Ped_start);
+ }
+ if (stuff_p->all) {
+ asc *= Score.staffscale;
+ hi *= Score.staffscale;
+ } else {
+ asc *= Staffscale;
+ hi *= Staffscale;
+ }
+ stuff_p->c[RY] = protrude - asc;
+ stuff_p->c[RS] = protrude - hi;
+
+ if (stuff_p->c[RS] < lowpoint)
+ lowpoint = stuff_p->c[RS];
+ }
+ }
+
+ /*
+ * If we found pedal mark(s), put one big rectangle in rectab, spanning
+ * the width of the page.
+ */
+ if (lowpoint < 0) {
+ rectab[reclim].n = protrude;
+ rectab[reclim].s = lowpoint;
+ rectab[reclim].w = 0;
+ rectab[reclim].e = PGWIDTH;
+
+ inc_reclim();
+ }
+}
+\f
+/*
+ * Name: doendings()
+ *
+ * Abstract: Set up rectangles and vert coords for ending marks.
+ *
+ * Returns: void
+ *
+ * Description: This function puts into rectab rectangles for ending marks.
+ * Also, MARKCOORD structures get linked to BARs for them.
+ */
+
+static void
+doendings(start_p, s)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+
+{
+ struct MAINLL *mainll_p;/* point along main linked list */
+ struct BAR *bar_p; /* point at a bar or pseudobar on this score */
+
+
+ debug(32, "doendings file=%s line=%d s=%d", start_p->inputfile,
+ start_p->inputlineno, s);
+ /* if endings are not to be drawn over this staff, get out */
+ if (has_ending(s) == NO)
+ return;
+
+ /* point at pseudobar in clefsig that immediately follows this feed */
+ mainll_p = start_p->next;
+ bar_p = mainll_p->u.clefsig_p->bar_p;
+
+ /*
+ * If an ending starts at the pseudobar, or is continuing on from the
+ * previous score, handle it, along with any following continguous ones.
+ */
+ if (bar_p->endingloc != NOITEM) {
+ /*
+ * Search forward for the end of this ending (or following
+ * contiguous ones), or the end of the score, whichever comes
+ * first.
+ */
+ while ( ! (mainll_p->str == S_BAR &&
+ mainll_p->u.bar_p->endingloc == ENDITEM)
+ && mainll_p->str != S_FEED) {
+
+ mainll_p = mainll_p->next;
+ }
+
+ /* handle ending(s) from start to this bar or feed */
+ storeend(start_p, mainll_p, s);
+
+ /* if feed, there's nothing more to look for */
+ if (mainll_p->str == S_FEED)
+ return;
+
+ /* point after this bar at end of this ending(s) */
+ mainll_p = mainll_p->next;
+ }
+
+ /*
+ * Search the rest of the score for contiguous groups of endings.
+ */
+ while (mainll_p != 0 && mainll_p->str != S_FEED) {
+
+ /* find another bar; return if there aren't any more */
+ while (mainll_p != 0 && mainll_p->str != S_BAR &&
+ mainll_p->str != S_FEED)
+ mainll_p = mainll_p->next;
+ if (mainll_p == 0 || mainll_p->str == S_FEED)
+ return;
+
+ /*
+ * We found another bar. If it isn't associated with an
+ * ending, point beyond it and continue to go look for the
+ * next bar.
+ */
+ if (mainll_p->u.bar_p->endingloc == NOITEM) {
+ mainll_p = mainll_p->next;
+ continue;
+ }
+
+ /*
+ * This bar is the start of an ending. Search forward for the
+ * end of this ending (or following contiguous ones), or the
+ * end of the score, whichever comes first.
+ */
+ start_p = mainll_p;
+ while ( ! (mainll_p->str == S_BAR &&
+ mainll_p->u.bar_p->endingloc == ENDITEM)
+ && mainll_p->str != S_FEED) {
+
+ mainll_p = mainll_p->next;
+ }
+
+ /* handle ending(s) from start to this bar or feed */
+ storeend(start_p, mainll_p, s);
+
+ /* if feed, there's nothing more to look for */
+ if (mainll_p->str == S_FEED)
+ return;
+
+ /* point after this bar at end of this ending */
+ mainll_p = mainll_p->next;
+ }
+}
+\f
+/*
+ * Name: storeend()
+ *
+ * Abstract: Set up rectangles and vert coords for contiguous endings.
+ *
+ * Returns: void
+ *
+ * Description: This function is given the starting and ending bars of a group
+ * of continguous ending marks on a staff. The starting "bar"
+ * may be the pseudobar at the start of the score; and the ending
+ * bar may be the end of the score. This function applies stackit
+ * to them as a unit. It adds another rectangle to rectab to
+ * prevent anything later from getting in between the ending(s)
+ * and the staff. Then, for the starting bar of each ending in
+ * the group, it allocates a MARKCOORD structure.
+ */
+
+static void
+storeend(start_p, end_p, s)
+
+struct MAINLL *start_p; /* the start of these ending(s) */
+struct MAINLL *end_p; /* the end of these ending(s) */
+int s; /* staff number */
+
+{
+ struct MAINLL *mainll_p;/* point along main linked list */
+ struct BAR *bar_p; /* point at a bar or pseudobar on this score */
+ struct MARKCOORD *mark_p; /* we allocate these for bars to point at */
+ float west, east; /* extremities of group of ending(s) */
+ float south; /* their bottom boundary */
+
+
+ /*
+ * Find the west and east boundaries of the ending(s).
+ */
+ if (start_p->str == S_FEED)
+ west = start_p->next->u.clefsig_p->bar_p->c[AX]; /* pseudobar */
+ else
+ west = start_p->u.bar_p->c[AX]; /* normal bar */
+
+ if (end_p->str == S_FEED)
+ east = PGWIDTH - eff_rightmargin(end_p); /* end of score */
+ else
+ east = end_p->u.bar_p->c[AX]; /* normal bar */
+
+ /* make a rectangle out of the ending(s) and find where they go */
+ south = stackit(west, east, ENDINGHEIGHT, (double)0.0, PL_ABOVE);
+
+ /*
+ * Superimpose another rectangle on top of the one stackit put there;
+ * one that reaches down to the staff. This ensures that nothing later
+ * will get between the ending(s) and the staff.
+ */
+ rectab[reclim].n = south + ENDINGHEIGHT;
+ rectab[reclim].s = 0;
+ rectab[reclim].e = east;
+ rectab[reclim].w = west;
+ inc_reclim();
+
+ /*
+ * If the pseudobar has an ending, calloc a markcoord structure and put
+ * it in the pseudobar's linked list of them.
+ */
+ if (start_p->str == S_FEED) {
+ bar_p = start_p->next->u.clefsig_p->bar_p;
+ CALLOC(MARKCOORD, mark_p, 1);
+ mark_p->next = bar_p->ending_p;
+ bar_p->ending_p = mark_p;
+ mark_p->staffno = (short)s;
+ mark_p->ry = south;
+ }
+
+ /*
+ * Loop through this part of the score. Wherever there is a bar that
+ * is the start of an ending, calloc a markcoord structure and put it
+ * in the bar's linked list of them.
+ */
+ for (mainll_p = start_p; mainll_p != end_p; mainll_p = mainll_p->next) {
+ if (mainll_p->str != S_BAR)
+ continue;
+ bar_p = mainll_p->u.bar_p;
+ if (bar_p->endingloc != STARTITEM)
+ continue;
+ CALLOC(MARKCOORD, mark_p, 1);
+ mark_p->next = bar_p->ending_p;
+ bar_p->ending_p = mark_p;
+ mark_p->staffno = (short)s;
+ mark_p->ry = south;
+ }
+}
+\f
+/*
+ * Name: dorehears()
+ *
+ * Abstract: Set up rectangles and vert coords for rehearsal marks.
+ *
+ * Returns: void
+ *
+ * Description: This function puts into rectab rectangles for rehearsal marks.
+ * Also, MARKCOORD structures get linked to BARs for them.
+ */
+
+static void
+dorehears(start_p, s)
+
+struct MAINLL *start_p; /* FEED at the start of this score */
+int s; /* staff number */
+
+{
+ struct MAINLL *mainll_p;/* point along main linked list */
+ struct BAR *bar_p; /* point at a bar or pseudobar on this score */
+ struct MARKCOORD *mark_p; /* we allocate these for bars to point at */
+ float west, east; /* of a rehearsal mark */
+ float south; /* of a rehearsal mark */
+ float height; /* of a rehearsal mark */
+ float dist; /* distance from center of staff */
+ int dopseudo; /* do the pseudobar's rehearsal mark? */
+ char *reh_string; /* string for the reh mark */
+
+
+ debug(32, "dorehears file=%s line=%d s=%d", start_p->inputfile,
+ start_p->inputlineno, s);
+ /* if rehearsal marks are not to be drawn over this staff, get out */
+ if (has_ending(s) == NO)
+ return;
+
+ /* point at pseudobar in clefsig that immediately follows this feed */
+ mainll_p = start_p->next;
+ bar_p = mainll_p->u.clefsig_p->bar_p;
+
+ /* if there's a rehearsal mark at the pseudobar, note that fact */
+ if (bar_p->reh_type != REH_NONE)
+ dopseudo = YES;
+ else
+ dopseudo = NO;
+
+ /*
+ * Loop through the score, dealing with the pseudobar (if it has a
+ * rehearsal mark), and all real bars that have a rehearsal mark.
+ */
+ for ( ; mainll_p != 0 && mainll_p->str != S_FEED;
+ mainll_p = mainll_p->next) {
+
+ if (dopseudo == YES || mainll_p->str == S_BAR &&
+ mainll_p->u.bar_p->reh_type != REH_NONE){
+ if (dopseudo == YES)
+ dopseudo = NO;
+ else
+ bar_p = mainll_p->u.bar_p;
+
+ /*
+ * Find the size of the rehearsal label, including 6
+ * more points to allow for the box around it. Make
+ * its first character be centered over the bar line.
+ * Place it by using stackit.
+ */
+ reh_string = get_reh_string(bar_p->reh_string, s);
+ height = strheight(reh_string);
+ west = bar_p->c[AX] - left_width(reh_string);
+ east = west + strwidth(reh_string);
+
+ if (bar_p->dist_usage == SD_NONE) {
+ /* get the usual dist */
+ dist = svpath(s, DIST)->dist;
+ } else {
+ /* override with this bar's dist */
+ dist = bar_p->dist;
+ }
+ /* convert to inches from center of staff */
+ dist = halfstaffhi(s) + STEPSIZE * dist;
+
+ if (bar_p->dist_usage == SD_FORCE) {
+ /*
+ * The user is forcing this dist, so don't
+ * stack; just put it there.
+ */
+ south = dist;
+ rectab[reclim].n = south + height;
+ rectab[reclim].s = south;
+ rectab[reclim].e = east;
+ rectab[reclim].w = west;
+ inc_reclim();
+ } else {
+ /* stack the usual way */
+ south = stackit(west, east, height, dist,
+ PL_ABOVE);
+ }
+
+ /*
+ * Allocate and link a MARKCOORD, and put the necessary
+ * info in it.
+ */
+ CALLOC(MARKCOORD, mark_p, 1);
+ mark_p->next = bar_p->reh_p;
+ bar_p->reh_p = mark_p;
+ mark_p->staffno = (short)s;
+ mark_p->ry = south + strdescent(reh_string);
+ }
+ }
+}
+\f
+/*
+ * Name: stackit()
+ *
+ * Abstract: Place a rectangle and add it to rectab.
+ *
+ * Returns: south boundary of the new rectangle
+ *
+ * Description: This function puts the given rectangle into rectab. It is put
+ * as close to the staff or baseline as is possible without
+ * overlapping rectangles already in rectab, and without letting
+ * it get any closer to the staff/baseline than "dist" STEPSIZE.
+ */
+
+static double
+stackit(west, east, height, dist, place)
+
+double west; /* west edge of the new rectangle */
+double east; /* east edge of the new rectangle */
+double height; /* height of the new rectangle */
+double dist; /* min dist from item to center line of staff*/
+int place; /* above, below, or between? */
+
+{
+ float north, south; /* trial boundaries for new rectangle */
+ int try; /* which element of rectab to try */
+ int overlap; /* does our rectangle overlap existing ones? */
+ int j; /* loop variable */
+
+
+ /*
+ * For each rectangle in rectab, decide whether (based on
+ * its horizontal coords) it could possibly overlap with our
+ * new rectangle. If it's totally left or right of ours, it
+ * can't. We allow a slight overlap (FUDGE) so that round
+ * off errors don't stop us from packing things as tightly
+ * as possible.
+ */
+ for (j = 0; j < reclim; j++) {
+ if (rectab[j].w + FUDGE > east ||
+ rectab[j].e < west + FUDGE)
+ rectab[j].relevant = NO;
+ else
+ rectab[j].relevant = YES;
+ }
+
+ /*
+ * Set up first trial position for this rectangle: "dist" inches
+ * away from the center line of the staff. For "between", it always
+ * starts at the baseline.
+ */
+ north = south = 0.0; /* prevent useless 'used before set' warning */
+ switch (place) {
+ case PL_BELOW:
+ /* work downward from staff, allowing "dist" distance */
+ north = -dist;
+ south = north - height;
+ break;
+ case PL_ABOVE:
+ /* work upward from staff, allowing "dist" distance */
+ south = dist;
+ north = south + height;
+ break;
+ case PL_BETWEEN:
+ /* work upward from baseline */
+ south = 0;
+ north = height;
+ break;
+ }
+
+ /*
+ * Mark the "tried" field for all relevant rectangles. This says
+ * whether we have already tried using their boundaries for positioning
+ * our rectangle. Any rectangle that is closer to the staff/baseline
+ * than we want to allow, we mark as if we have tried it already.
+ */
+ for (j = 0; j < reclim; j++) {
+ if (rectab[j].relevant == YES) {
+ if (place == PL_BELOW && rectab[j].s > north ||
+ place != PL_BELOW && rectab[j].n < south)
+ rectab[j].tried = YES;
+ else
+ rectab[j].tried = NO;
+ }
+ }
+
+ /*
+ * Keep trying positions for this rectangle, working outwards from the
+ * first trial position. When we find one that doesn't overlap an
+ * existing rectangle, break. This has to succeed at some point, at
+ * at the outermost rectangle position if not earlier.
+ */
+ for (;;) {
+ overlap = NO;
+ for (j = 0; j < reclim; j++) {
+ /* ignore ones too far east or west */
+ if (rectab[j].relevant == NO)
+ continue;
+
+ /* if all south or north, okay; else overlap */
+ if (rectab[j].s + FUDGE <= north &&
+ rectab[j].n >= south + FUDGE) {
+ overlap = YES;
+ break;
+ }
+ }
+
+ /* if no rectangle overlapped, we found a valid place */
+ if (overlap == NO)
+ break;
+
+ /*
+ * Something overlapped, so we have to try again. Find the
+ * innermost relevant outer rectangle boundary that hasn't been
+ * tried already, to use as the next trial position for our
+ * rectangle's inner boundary.
+ */
+ try = -1;
+ for (j = 0; j < reclim; j++) {
+ /* ignore ones too far east or west */
+ if (rectab[j].relevant == NO || rectab[j].tried == YES)
+ continue;
+
+ /*
+ * If this is the first relevant one we haven't tried,
+ * or if this is farther in than the innermost so far,
+ * save it as being the new innermost so far.
+ */
+ if (place == PL_BELOW) {
+ if (try == -1 || rectab[j].s > rectab[try].s)
+ try = j;
+ } else {
+ if (try == -1 || rectab[j].n < rectab[try].n)
+ try = j;
+ }
+ }
+
+ if (try == -1)
+ pfatal("bug in stackit()");
+
+ /*
+ * Mark this one as having been tried (for next time around, if
+ * necessary). Set new trial values for north and south of our
+ * rectangle.
+ */
+ rectab[try].tried = YES;
+ if (place == PL_BELOW) {
+ north = rectab[try].s;
+ south = north - height;
+ } else {
+ south = rectab[try].n;
+ north = south + height;
+ }
+
+ } /* end of while loop trying positions for this rectangle */
+
+ /*
+ * We found the correct position for the new rectangle. Enter it
+ * into rectab.
+ */
+ rectab[reclim].n = north;
+ rectab[reclim].s = south;
+ rectab[reclim].e = east;
+ rectab[reclim].w = west;
+
+ inc_reclim();
+
+ return (south);
+}
+\f
+/*
+ * Name: inc_reclim()
+ *
+ * Abstract: Increment no. of rectangles, and realloc more if we run out.
+ *
+ * Returns: void
+ *
+ * Description: This function increments reclim, the index into rectab. If it
+ * finds that rectab[reclim] is now beyond the end of the space
+ * that's been allocated, it does a realloc to get more space.
+ */
+
+static void
+inc_reclim()
+{
+ /* when first called, relvert will have allocated this many */
+ static int rectabsize = RECTCHUNK;
+
+
+ reclim++;
+
+ /* if rectab[reclim] is still valid, no need to allocate more */
+ if (reclim < rectabsize)
+ return;
+
+ /* must allocate another chunk of rectangles */
+ rectabsize += RECTCHUNK;
+ REALLOC(RECTAB, rectab, rectabsize);
+}