chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / setgrps.c
1 /* Copyright (c) 1995, 1997, 1998, 1999, 2000, 2001, 2004, 2005, 2006
2  * by Arkkra Enterprises */
3 /* All rights reserved */
4 /*
5  * Name:        setgrps.c
6  *
7  * Description: This file contains functions for setting the relative
8  *              horizontal coordinates of all groups that contain notes
9  *              (grpcont == GC_NOTES) and of all objects in these groups.
10  *              It also sets relative vertical coordinates for the dots
11  *              after notes.
12  */
13
14 #include "defines.h"
15 #include "structs.h"
16 #include "globals.h"
17
18 struct NOTEPTRS {
19         struct NOTE *top_p;     /* point at a note in top group */
20         struct NOTE *bot_p;     /* point at same note in bottom group*/
21         float wid;              /* width of the note head */
22 };
23
24 static struct GRPSYL *procallvoices P((struct MAINLL *mll_p,
25                 struct GRPSYL *gs_p));
26 static void proc1or2voices P((struct MAINLL *mll_p, struct STAFF *staff_p,
27                 struct GRPSYL *gs1_p, struct GRPSYL *gs2_p));
28 static int compat P((struct NOTEPTRS noteptrs[], struct GRPSYL *gs1_p,
29                 struct GRPSYL *gs2_p));
30 static int can_overlap P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p));
31 static void procsome P((struct NOTEPTRS noteptrs[], struct MAINLL *mll_p,
32                 struct STAFF *staff_p, struct GRPSYL *gs1_p,
33                 struct GRPSYL *gs2_p));
34 static void procgrace P((struct NOTEPTRS noteptrs[], struct MAINLL *mll_p,
35                 struct STAFF *staff_p, struct GRPSYL *gsnorm_p));
36 static void procbunch P((struct NOTEPTRS noteptrs[], struct MAINLL *mll_p,
37                 struct STAFF *staff_p, struct GRPSYL *gs1_p,
38                 struct GRPSYL *gs2_p));
39 static void doacc P((struct NOTEPTRS noteptrs[], double halfwide,
40                 double halfhigh, int collinear));
41 static int nextacc P((struct NOTEPTRS noteptrs[], int found));
42 static void dodot P((struct STAFF *staff_p, struct GRPSYL *gs1_p,
43                 struct GRPSYL *gs2_p, double halfwide, int collinear));
44 static void dogrpdot P((struct STAFF *staff_p, struct GRPSYL *gs_p,
45                 struct GRPSYL *ogs_p, double halfwide, int uppermost,
46                 int lowermost, int push));
47 static void westwith P((struct GRPSYL *gs_p));
48 static void eastwith P((struct GRPSYL *gs_p));
49 static void csbstempad P((struct MAINLL *mll_p, struct GRPSYL *gs_p));
50 static void proctab P((struct MAINLL *mll_p, struct STAFF *staff_p,
51                 struct GRPSYL *gs1_p));
52 static void noterparen P((struct NOTEPTRS noteptrs[], struct GRPSYL *gs1_p,
53                 struct GRPSYL *gs2_p, double halfwide, double halfhigh,
54                 int collinear));
55 \f
56 /*
57  * Name:        setgrps()
58  *
59  * Abstract:    Find first group on each staff & call procallvoices to process.
60  *
61  * Returns:     void
62  *
63  * Description: This function goes through the chord lists, and for each chord,
64  *              the list of GRPSYLs hanging off it.  It finds the first group
65  *              on each staff, and calls procallvoices() to set the relative
66  *              horizontal coordinates of all the note groups on that staff.
67  */
68
69 void
70 setgrps()
71
72 {
73         struct CHORD *ch_p;             /* point at a chord */
74         struct GRPSYL *gs1_p;           /* point at a group */
75         struct MAINLL *mainll_p;        /* point at items in main linked list*/
76         struct MAINLL *mstaff_p;        /* for looking for staff */
77
78
79         debug(16, "setgrps");
80         initstructs();          /* clean out old SSV info */
81
82         /*
83          * Loop down the main linked list looking for each chord list
84          * headcell.
85          */
86         for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
87
88                 /* keep SSVs up to date */
89                 if (mainll_p->str == S_SSV)
90                         asgnssv(mainll_p->u.ssv_p);
91
92                 if (mainll_p->str != S_CHHEAD)
93                         continue;       /* skip everything but chord HC */
94
95                 /*
96                  * Loop through each chord in this list.
97                  */
98                 for (ch_p = mainll_p->u.chhead_p->ch_p; ch_p != 0;
99                                         ch_p = ch_p->ch_p) {
100                         /*
101                          * Loop through the linked list of GRPSYLs hanging off
102                          * this chord.  Skip the syllables; just deal with the
103                          * groups.  Upon finding the first group on a staff
104                          * (which could be for any of the voices, since not all
105                          * might be present in this chord), call procallvoices
106                          * to process all the note groups.
107                          */
108                         gs1_p = ch_p->gs_p;
109                         for (;;) {
110                                 /* find first group on a staff */
111                                 while (gs1_p != 0 &&
112                                                 gs1_p->grpsyl == GS_SYLLABLE)
113                                         gs1_p = gs1_p->gs_p;
114                                 if (gs1_p == 0)
115                                         break;
116
117                                 /* find the staff's MLL structure */
118                                 mstaff_p = chmgrp2staffm(mainll_p, gs1_p);
119
120                                 /* set gs1_p to after this staff's groups */
121                                 gs1_p = procallvoices(mstaff_p, gs1_p);
122                         }
123                 }
124         }
125 }
126 \f
127 /*
128  * Name:        procallvoices()
129  *
130  * Abstract:    Process the groups for all the voices on one staff in a chord.
131  *
132  * Returns:     pointer to the first GRPSYL after these groups, 0 if none
133  *
134  * Description: This function is given the GRPSYL for the first (topmost) voice
135  *              that is on this staff in this chord.  It finds what other
136  *              GRPSYLs exist.  For each of them that is for notes (not rests
137  *              or spaces), it calls proc1or2voices() to process them together
138  *              and/or separately, as needed.  This file generally deals only
139  *              with notes, not rests or spaces.  But this function also deals
140  *              with rests to the following extent:  For both notes and rests,
141  *              there are situations where voice 3 should "stand in" for voice 1
142  *              or voice 2.  This function makes those decisions, and sets pvno.
143  */
144
145 static struct GRPSYL *
146 procallvoices(mll_p, gs_p)
147
148 struct MAINLL *mll_p;           /* the MLL item the group is connected to */
149 struct GRPSYL *gs_p;            /* point at first voice on this staff */
150
151 {
152         struct STAFF *staff_p;          /* point at staff */
153         struct GRPSYL *g_p[MAXVOICES];  /* point at note groups */
154         struct GRPSYL *last_p;          /* point at last note group */
155         struct GRPSYL *g2_p[MAXVOICES]; /* point at note and rest groups */
156         struct GRPSYL *gs1_p;           /* remember first group */
157         struct GRPSYL *gs2_p;           /* another GRPSYL pointer */
158         int numnonspace;                /* number of nonspace GRPSYLs */
159         int numgrps;                    /* how many note groups are here */
160         int n;                          /* loop variable, voices processed */
161
162
163         staff_p = mll_p->u.staff_p;
164         numgrps = 0;                    /* no groups found yet */
165         last_p = 0;                     /* no note groups yet */
166         gs1_p = gs_p;                   /* remember first group */
167
168         /* find all groups in this chord on this staff; remember note groups */
169         while (gs_p != 0 && gs_p->staffno == staff_p->staffno &&
170                             gs_p->grpsyl == GS_GROUP) {
171                 gs_p->pvno = gs_p->vno; /* init pseudo voice no. to voice no.*/
172                 if (gs_p->grpcont == GC_NOTES) {
173                         g_p[numgrps++] = gs_p;
174                         last_p = gs_p;
175                 }
176                 gs_p = gs_p->gs_p;
177         }
178
179         /*
180          * Before continuing on to process note groups, change voice 3's pvno
181          * when appropriate.  First find all nonspace groups.
182          */
183         numnonspace = 0;                /* no nonspace groups found yet */
184         gs2_p = gs1_p;
185
186         /* find all nonspace groups in this chord on this staff */
187         while (gs2_p != 0 && gs2_p->staffno == staff_p->staffno &&
188                             gs2_p->grpsyl == GS_GROUP) {
189                 if (gs2_p->grpcont != GC_SPACE) {
190                         g2_p[numnonspace++] = gs2_p;
191                 } else {
192                         /*
193                          * This is a convenient, though somewhat inappropriate,
194                          * place to process grace groups that precede a space
195                          * group.  Ones that precede notes groups will be
196                          * processed in the normal flow, called from procsome.
197                          * They are not allowed before rest groups.
198                          */
199                         struct NOTEPTRS noteptrs[MAXHAND + 1];
200                         procgrace(noteptrs, mll_p, staff_p, gs2_p);
201                 }
202                 gs2_p = gs2_p->gs_p;
203         }
204
205         /*
206          * If the only nonspace voices are 1 and 3, or 2 and 3, and at least
207          * one of them is a rest and this is not a tab staff and "ho" was not
208          * used for either . . .
209          */
210         if (numnonspace == 2 && g2_p[1]->vno == 3 &&
211            (g2_p[0]->grpcont == GC_REST || g2_p[1]->grpcont == GC_REST) &&
212            ! is_tab_staff(staff_p->staffno) && g2_p[0]->ho_usage == HO_NONE &&
213            g2_p[1]->ho_usage == HO_NONE) {
214                 /*
215                  * If v1 is either a rest or stem-up notes and v3 is a rest or
216                  * stem-down notes, let v3 stand in for v2.
217                  */
218                 if (g2_p[0]->vno == 1 && (g2_p[0]->grpcont == GC_NOTES &&
219                     g2_p[0]->stemdir == UP || g2_p[0]->grpcont == GC_REST) &&
220                     (g2_p[1]->grpcont == GC_NOTES && g2_p[1]->stemdir == DOWN ||
221                     g2_p[1]->grpcont == GC_REST)) {
222                         g2_p[1]->pvno = 2;
223                 }
224                 /*
225                  * If v2 is either a rest or stem-down notes and v3 is a rest or
226                  * stem-up notes, let v3 stand in for v1.
227                  */
228                 if (g2_p[0]->vno == 2 && (g2_p[0]->grpcont == GC_NOTES &&
229                     g2_p[0]->stemdir == DOWN || g2_p[0]->grpcont == GC_REST) &&
230                     (g2_p[1]->grpcont == GC_NOTES && g2_p[1]->stemdir == UP ||
231                     g2_p[1]->grpcont == GC_REST)) {
232                         g2_p[1]->pvno = 1;
233                 }
234         }
235
236         /* if there were no note groups on this staff, nothing more to do */
237         if (numgrps == 0)
238                 return (gs_p);
239
240         n = 0;          /* number of voices processed so far */
241
242         /*
243          * If voices 1 and 2 exist and are notes and do not have user specified
244          * horizontal offsets and this is not a tab staff, handle them together.
245          * If both voices 1 and 2 have a group here, they will be the first two
246          * found.  Tab staffs should be handled separately because their voices
247          * never conflict with each other (because of chktabcollision() in
248          * in setnotes.c).  Before checking the offsets, verify that they are
249          * legal and fix if not.
250          */
251         if (numgrps >= 2 && g_p[0]->vno == 1 && g_p[1]->vno == 2 &&
252                         ! is_tab_staff(staff_p->staffno)) {
253
254                 vfyoffset(g_p);         /* verify and fix */
255
256                 if (g_p[0]->ho_usage == HO_NONE && g_p[1]->ho_usage == HO_NONE){
257                         proc1or2voices(mll_p, staff_p, g_p[0], g_p[1]);
258                         n = 2;          /* processed 2 voices */
259                 }
260         }
261
262         /*
263          * Else, if v1 and v3, or v2 and v3, are notes, and only those two
264          * exist, and they do not have user specified horizontal offsets and
265          * this is not a tab staff, and v3's stem dir is compatible, let v3
266          * "stand in" for v1 or v2, as the case may be.  Handle the two voices
267          * together.
268          */
269         else if (numgrps == 2 && numnonspace == 2 &&
270                         ! is_tab_staff(staff_p->staffno) && g_p[0]->ho_usage ==
271                         HO_NONE && g_p[1]->ho_usage == HO_NONE) {
272
273                 if (g_p[0]->vno == 1 && g_p[0]->stemdir == UP &&
274                     g_p[1]->vno == 3 && g_p[1]->stemdir == DOWN) {
275
276                         g_p[1]->pvno = 2;
277                         proc1or2voices(mll_p, staff_p, g_p[0], g_p[1]);
278                         n = 2;          /* processed 2 voices */
279
280                 } else if (g_p[0]->vno == 2 && g_p[0]->stemdir == DOWN &&
281                            g_p[1]->vno == 3 && g_p[1]->stemdir == UP) {
282
283                         g_p[1]->pvno = 1;
284                         proc1or2voices(mll_p, staff_p, g_p[1], g_p[0]);
285                         n = 2;          /* processed 2 voices */
286                 }
287         }
288
289         /* process any remaining voices individually */
290         for ( ; n < numgrps; n++) {
291                 proc1or2voices(mll_p, staff_p, g_p[n], (struct GRPSYL *)0);
292         }
293
294         /* return the first GRPSYL after the groups we processed */
295         return (gs_p);
296 }
297 \f
298 /*
299  * Name:        proc1or2voices()
300  *
301  * Abstract:    Process a single voice, or voices 1 and 2 together.
302  *
303  * Returns:     void
304  *
305  * Description: This function is given pointers to one or two groups on a
306  *              staff.  If it's just one (the second one is a null pointer),
307  *              that group is to be handled alone.  If it is two, they are
308  *              voices 1 and 2, since voice 3 is always handled separately.
309  *              (Except that voice 3 can sometimes "stand in" for v1 or v2.)
310  *              In any case, these are always note groups, not rest or space.
311  *
312  *              The function sets up an array (noteptrs) to point at each
313  *              note in the group(s), figuring out whether the groups overlap
314  *              and, if so, if they are compatible (see below for definition).
315  *              It calls procsome() to set relative horizontal coordinates for
316  *              some notes, which is done either separately for each group or
317  *              both at once, depending on the situation.
318  */
319
320 static void
321 proc1or2voices(mll_p, staff_p, gs1_p, gs2_p)
322
323 struct MAINLL *mll_p;           /* the MLL item the group is connected to */
324 struct STAFF *staff_p;                  /* the staff the groups are on */
325 register struct GRPSYL *gs1_p, *gs2_p;  /* point at groups in this hand */
326
327 {
328         /*
329          * Each structure in this array points at a note.  Notes from gs1_p
330          * are pointed at by top_p, and, when both groups exist, notes
331          * from gs2_p are pointed at by bot_p.  If there's no overlap
332          * between the groups, there won't be any here either.  But if
333          * the groups "share" notes, the shared notes will be pointed
334          * at by both.  If the groups are "incompatible" (must be
335          * drawn shifted horizontally to avoid interference), they will
336          * be done separately and use this array separately, one at a time.
337          * And in that case, notes from both gs1_p and gs2_p will use top_p,
338          * in turn.
339          */
340         struct NOTEPTRS noteptrs[MAXHAND + 1];
341
342         float offset;           /* how far to offset incompatible groups */
343         int num1;               /* number of notes in top group */
344         int n;                  /* loop variable */
345         int incompat;           /* are groups incompatible (special case) */
346
347
348         /*
349          * For mrpt, we have nothing to do except set the horizontal group
350          * coordinates.  If the first group is a measure repeat, so is the
351          * second one, if it exists at all.  We set a very small width, as a
352          * placeholder, because if other staffs have normal notes, we don't
353          * want the first chord to be abnormally wide because of the mrpt
354          * symbol.  (It will be centered in the measure.)  If all the staffs
355          * have mrpt, abshorz.c will ensure that enough space is left for
356          * these symbols.
357          */
358         if (is_mrpt(gs1_p)) {
359                 gs1_p->c[RX] = 0;
360                 gs1_p->c[RE] = TEMPMRPTWIDTH / 2.0;
361                 gs1_p->c[RW] = -TEMPMRPTWIDTH / 2.0;
362
363                 if (gs2_p != 0) {
364                         gs2_p->c[RX] = 0;
365                         gs2_p->c[RE] = TEMPMRPTWIDTH / 2.0;
366                         gs2_p->c[RW] = -TEMPMRPTWIDTH / 2.0;
367                 }
368                 return;
369         }
370
371         /* clear out the array */
372         for (n = 0; n < NUMELEM(noteptrs); n++) {
373                 noteptrs[n].top_p = 0;
374                 noteptrs[n].bot_p = 0;
375                 noteptrs[n].wid = 0.0;
376         }
377
378         num1 = gs1_p->nnotes;
379
380         /* set all the "top" group pointers */
381         for (n = 0; n < num1; n++)
382                 noteptrs[n].top_p = &gs1_p->notelist[n];
383
384         /* if there is no "bottom" group, process the first bunch and quit */
385         if (gs2_p == 0) {
386                 procsome(noteptrs, mll_p, staff_p, gs1_p, (struct GRPSYL *)0);
387
388                 /* if group is rolled, allow room for the roll */
389                 if (gs1_p->roll != NOITEM)
390                         gs1_p->c[RW] -= ROLLPADDING;
391                 return;
392         }
393
394         /*
395          * If the lowest note of the top group is higher than the highest
396          * note of the bottom group, point at all the bottom notes,
397          * process both, and quit.  Exception:  if the inner notes of the
398          * two groups are on neighboring steps, and the top note of the
399          * bottom group is on a line and has a dot, and the top group has
400          * no dots, the groups are to be regarded as if overlapping and
401          * incompatible.  This is because there is no decent way to place
402          * the dots in this case otherwise.  But if, in this neighboring note
403          * situation, there are no problems with dots, the groups can still be
404          * handled together here; their stems will be made collinear.  When
405          * the notes are two or more steps apart, there's no problem at all,
406          * and the groups' X coordinates will line up and equal the chord's.
407          * Another exception ("else if") is that when the stem of either group
408          * has been forced the "wrong way" by the user, we require more
409          * vertical space between the groups.  Since we don't know the stem
410          * lengths yet, we can't do the full job, though.  The user may have to
411          * use "len" or "ho" to avoid a collision.
412          */
413         incompat = NO;
414         if (noteptrs[num1-1].top_p->stepsup > gs2_p->notelist[0].stepsup) {
415                 if (noteptrs[num1-1].top_p->stepsup ==
416                                 gs2_p->notelist[0].stepsup + 1 &&
417                                 gs2_p->notelist[0].stepsup % 2 == 0 &&
418                                 gs2_p->dots == 0 &&
419                                 gs1_p->dots > 0) {
420                         incompat = YES;
421                 } else if ((gs1_p->stemdir == DOWN || gs2_p->stemdir == UP) &&
422                                 noteptrs[num1-1].top_p->stepsup <
423                                 gs2_p->notelist[0].stepsup + 3) {
424                         incompat = YES;
425                 } else {
426                         for (n = 0; n < gs2_p->nnotes; n++)
427                                 noteptrs[num1+n].bot_p = &gs2_p->notelist[n];
428                         procsome(noteptrs, mll_p, staff_p, gs1_p, gs2_p);
429
430                         /* if a group is rolled, allow room for the roll */
431                         if (gs1_p->roll != NOITEM)
432                                 gs1_p->c[RW] -= ROLLPADDING;
433                         if (gs2_p->roll != NOITEM)
434                                 gs2_p->c[RW] -= ROLLPADDING;
435                         return;
436                 }
437         }
438
439         /*
440          * There is overlap between the two groups.  See if they are
441          * compatible (also fills in group 2 in noteptrs).  If so,
442          * process the groups together, and return.
443          */
444         if (incompat == NO && compat(noteptrs, gs1_p, gs2_p) == YES) {
445                 procsome(noteptrs, mll_p, staff_p, gs1_p, gs2_p);
446
447                 /* if a group is rolled, allow room for the roll */
448                 if (gs1_p->roll != NOITEM)
449                         gs1_p->c[RW] -= ROLLPADDING;
450                 if (gs2_p->roll != NOITEM)
451                         gs2_p->c[RW] -= ROLLPADDING;
452                 return;
453         }
454
455         /*
456          * The fact that we are here means the two groups are not compatible,
457          * meaning they overlap but can't share note heads.  Clear the array
458          * of any notes from the second group, in case compat() put some there.
459          */
460         for (n = 0; n < NUMELEM(noteptrs); n++)
461                 noteptrs[n].bot_p = 0;
462
463         /*
464          * It is possible that the groups can at least be given collinear
465          * stems.  For this to be allowed, it must be that the bottom note of
466          * the top group is on the same step as the top note of the bottom
467          * group.  The top group's note can't have dots, the bottom group's
468          * can't have accidentals or a roll, and neither can have parentheses,
469          * because they couldn't be drawn decently.  Neither note can have
470          * another note on a neighboring step.
471          */
472         if (noteptrs[num1-1].top_p->stepsup == gs2_p->notelist[0].stepsup &&
473
474                         gs1_p->dots == 0 &&
475
476                         gs2_p->notelist[0].accidental == '\0' &&
477
478                         gs2_p->roll == NOITEM &&
479
480                         noteptrs[num1-1].top_p->note_has_paren == NO &&
481                         gs2_p->notelist[0].note_has_paren == NO &&
482
483                         (num1 == 1 || noteptrs[num1-2].top_p->stepsup
484                                 > noteptrs[num1-1].top_p->stepsup + 1) &&
485
486                         (gs2_p->nnotes == 1 || gs2_p->notelist[0].stepsup
487                                 > gs2_p->notelist[1].stepsup + 1) ) {
488                 /*
489                  * Since we are not sharing noteheads, the notes of the bottom
490                  * group must be put after the notes of the top group in the
491                  * noteptrs table.  Then process them together.
492                  */
493                 for (n = 0; n < gs2_p->nnotes; n++)
494                         noteptrs[num1+n].bot_p = &gs2_p->notelist[n];
495                 procsome(noteptrs, mll_p, staff_p, gs1_p, gs2_p);
496
497                 /* if top group is rolled, allow room for the roll */
498                 if (gs1_p->roll != NOITEM)
499                         gs1_p->c[RW] -= ROLLPADDING;
500                 return;
501         }
502
503         /*
504          * At this point we know we have to handle the groups separately, and
505          * then place them.  Process the top group now.
506          */
507         procsome(noteptrs, mll_p, staff_p, gs1_p, (struct GRPSYL *)0);
508
509         /*
510          * Clear the top group out of the array, and fill it with just the
511          * bottom group, to process them.  But mark them as if "top", to
512          * simplify procsome().
513          */
514         for (n = 0; n < NUMELEM(noteptrs); n++)
515                 noteptrs[n].top_p = 0;
516
517         /* set all the "top" group pointers even though this is group 2 */
518         for (n = 0; n < gs2_p->nnotes; n++)
519                 noteptrs[n].top_p = &gs2_p->notelist[n];
520
521         procsome(noteptrs, mll_p, staff_p, gs2_p, (struct GRPSYL *)0);
522
523         /*
524          * Now that we've figured out all the relative horizontal coords for
525          * the two groups (and everything in them) separately, we need to
526          * decide how to offset them so they don't overlap.  We'll offset
527          * each the same distance, one right and one left, and apply that
528          * offset to every horizontal coord of the groups.
529          */
530         /*
531          * If the groups can be placed so that their rectangles overlap, do it.
532          * Else if one of the groups is to be rolled and the other is not, the
533          * one to be rolled must be put on the left.  Otherwise, find which
534          * direction gives minimal offset, but bias the results (0.1) to favor
535          * putting the top group towards the left, so that the stems will be
536          * closer to lining up.  Set "offset" to the offset to be applied to
537          * group 1.  Group 2's will be -offset.
538          */
539         if (can_overlap(gs1_p, gs2_p) == YES) {
540                 /* top group goes on right; top's offset > 0 */
541                 if (allsmall(gs1_p, gs1_p) == allsmall(gs2_p, gs2_p)) {
542                         offset = 0.50 * STEPSIZE;
543                 } else {
544                         offset = 0.75 * STEPSIZE;
545                 }
546                 if (gs2_p->roll != NOITEM)
547                         gs2_p->c[RW] -= ROLLPADDING;
548         } else if (gs1_p->roll != NOITEM && gs2_p->roll == NOITEM) {
549                 /* only top group is rolled; it goes on left; its offset < 0 */
550                 offset = ( gs2_p->c[RW] - gs1_p->c[RE] ) / 2;
551                 gs1_p->c[RW] -= ROLLPADDING;
552         } else if (gs1_p->roll == NOITEM && gs2_p->roll != NOITEM) {
553                 /* only bottom is rolled; top goes on right; top's offset > 0 */
554                 offset = ( gs2_p->c[RE] - gs1_p->c[RW] ) / 2;
555                 gs2_p->c[RW] -= ROLLPADDING;
556         } else {
557                 /* either both are rolled or neither is; use other criterion */
558                 if (gs1_p->c[RE] - gs2_p->c[RW] <
559                                         gs2_p->c[RE] - gs1_p->c[RW] + 0.1) {
560                         /* top group goes on left; its offset is negative */
561                         offset = ( gs2_p->c[RW] - gs1_p->c[RE] ) / 2;
562                         if (gs1_p->roll != NOITEM)
563                                 gs1_p->c[RW] -= ROLLPADDING;
564                 } else {
565                         /* top group goes on right; its offset is positive */
566                         offset = ( gs2_p->c[RE] - gs1_p->c[RW] ) / 2;
567                         if (gs2_p->roll != NOITEM)
568                                 gs2_p->c[RW] -= ROLLPADDING;
569                 }
570         }
571
572         /* apply offset to the groups and any preceding grace groups */
573         shiftgs(gs1_p, offset);
574         shiftgs(gs2_p, -offset);
575 }
576 \f
577 /*
578  * Name:        compat()
579  *
580  * Abstract:    Determine whether two groups in a hand are "compatible".
581  *
582  * Returns:     YES or NO
583  *
584  * Description: This function is given pointers to the two groups in a hand,
585  *              in a situation where they overlap.  The noteptrs array has
586  *              just the top group filled in at this point.  The function
587  *              figures out whether the two groups are compatible (see block
588  *              comment below), or whether they must be drawn separately and
589  *              offset horizontally.  While doing this, it fills in the bottom
590  *              group part of noteptrs.  If it returns YES, this has been
591  *              completed.  If it returns NO, this may be partially done,
592  *              and the caller should clear out the partially complete bot_p
593  *              part of noteptrs.
594  */
595
596 static int
597 compat(noteptrs, gs1_p, gs2_p)
598
599 struct NOTEPTRS noteptrs[];             /* array of ptrs to notes to process */
600 register struct GRPSYL *gs1_p, *gs2_p;  /* point at groups in this hand */
601
602 {
603         int num1;               /* number of notes in top group */
604         register int n, k;      /* loop variables */
605
606
607         num1 = gs1_p->nnotes;
608
609         /*
610          * There is overlap between the two groups.  Try to match the bottom
611          * N notes of the top group with the top N notes of the bottom group.
612          * If all N are "compatible", we can "share" these notes.  For two
613          * groups to be compatible, they must meet the following conditions:
614          *      1) both basic time values must be half notes, or both must be
615          *         shorter than half notes
616          *      2) both have no dots or the same number of dots
617          *      3) the bottom N notes of the top group are the same letters
618          *         and octaves as the top N notes of the bottom group
619          *      4) no two of these N notes can be on neighboring letters
620          *      5) for each of the N pairs, the two notes have no accidental
621          *         or the same accidental
622          *      6) for each of the N pairs, the two notes must have the same
623          *         size and headshape
624          */
625         /* check rule 1 */
626         if (gs1_p->basictime < 2  || gs2_p->basictime < 2)
627                 return (NO);
628         if (gs1_p->basictime == 2 && gs2_p->basictime != 2)
629                 return (NO);
630         if (gs1_p->basictime != 2 && gs2_p->basictime == 2)
631                 return (NO);
632
633         /* check rule 2 */
634         if (gs1_p->dots != gs2_p->dots)
635                 return (NO);
636
637         /* check rules 3, 4, 5, and 6 together */
638         /* see if any note in the top group matches the top note in the other*/
639         for (n = 0; n < num1; n++) {
640                 if (noteptrs[n].top_p->stepsup == gs2_p->notelist[0].stepsup)
641                         break;
642         }
643         if (n == num1)
644                 return (NO);            /* didn't find any match */
645
646         /* starting with this note, verify that it and the rest match */
647         for (k = 0; n < num1; k++, n++) {
648                 if (k >= gs2_p->nnotes) /* not enough notes in group 2? */
649                         return (NO);
650                 if (gs2_p->notelist[k].stepsup != noteptrs[n].top_p->stepsup)
651                         return (NO);
652                 if (k > 0 &&
653                 gs2_p->notelist[k-1].stepsup - 1 == gs2_p->notelist[k].stepsup)
654                         return (NO);
655                 if (gs2_p->notelist[k].accidental != noteptrs[n].top_p->accidental)
656                         return (NO);
657                 if (gs2_p->notelist[k].notesize != noteptrs[n].top_p->notesize)
658                         return (NO);
659                 if (gs2_p->notelist[k].headshape != noteptrs[n].top_p->headshape)
660                         return (NO);
661
662                 /* this note matches; set up noteptrs */
663                 noteptrs[n].bot_p = &gs2_p->notelist[k];
664         }
665
666         /*
667          * The fact that we made it to here means all the overlapping notes
668          * matched.  So fill the rest of group 2's note pointers.
669          */
670         for ( ; k < gs2_p->nnotes; k++, n++)
671                 noteptrs[n].bot_p = &gs2_p->notelist[k];
672         /*
673          * It is possible that, although the overlapping notes' headshapes
674          * match, some of the characters are mirrors of each other due to the
675          * opposite stem dir.  In these cases, group 2 rules.  So overwrite the
676          * notes in group 1.  If the lowest note in group 1 has to be changed,
677          * that could affect the RS of group 1, so change that too.
678          * Also, while doing this, if any of these notes or their accs have
679          * parens in one group but not the other, erase those parens.
680          */
681         n -= k;
682         for (k = 0; n < num1; k++, n++) {
683                 gs1_p->notelist[n].headchar = gs2_p->notelist[k].headchar;
684                 gs1_p->notelist[n].headfont = gs2_p->notelist[k].headfont;
685                 gs1_p->notelist[n].c[RN] = gs2_p->notelist[k].c[RN];
686                 gs1_p->notelist[n].c[RS] = gs2_p->notelist[k].c[RS];
687
688                 if (gs1_p->notelist[n].note_has_paren !=
689                     gs2_p->notelist[k].note_has_paren) {
690                         gs1_p->notelist[n].note_has_paren = NO;
691                         gs2_p->notelist[k].note_has_paren = NO;
692                 }
693                 if (gs1_p->notelist[n].acc_has_paren !=
694                     gs2_p->notelist[k].acc_has_paren) {
695                         gs1_p->notelist[n].acc_has_paren = NO;
696                         gs2_p->notelist[k].acc_has_paren = NO;
697                 }
698         }
699         gs1_p->c[RS] = gs2_p->notelist[k - 1].c[RS];
700
701         return (YES);
702 }
703 \f
704 /*
705  * Name:        can_overlap()
706  *
707  * Abstract:    Decides whether incompatible groups' rectangles can overlap.
708  *
709  * Returns:     YES or NO
710  *
711  * Description: This function is given two incompatible groups in a hand.  It
712  *              decides whether they can be placed such that their rectangles
713  *              overlap.  This arrangement is where the first group is to the
714  *              right of the second group, and the stems are about 3 stepsizes
715  *              apart.  The noteheads must be separated enough vertically so
716  *              that they don't collide, and various other things must also be
717  *              true for this to work.
718  */
719
720 static int
721 can_overlap(gs1_p, gs2_p)
722
723 struct GRPSYL *gs1_p, *gs2_p;   /* point at group(s) in this hand */
724
725 {
726         int notedist;           /* steps between two notes (absolute value) */
727         int n, k;               /* loop counters */
728
729
730         /*
731          * First, ensure that no note heads would collide.  We don't yet know
732          * whether any will be on the "wrong" side of their stem.  This is not
733          * too common and would rarely help things, so for now we assume the
734          * worst case, which is that all are on the "correct" side and thus
735          * have the potential of colliding with the other group's notes.
736          */
737         for (n = 0; n < gs1_p->nnotes; n++) {
738                 for (k = 0; k < gs2_p->nnotes; k++) {
739                         notedist = abs(gs1_p->notelist[n].stepsup -
740                                        gs2_p->notelist[k].stepsup);
741
742                         /* never allow closer than 2 steps */
743                         if (notedist < 2)
744                                 return (NO);
745
746                         /* if either is double whole, don't allow less than 3 */
747                         if ((gs1_p->basictime == 0 || gs2_p->basictime == 0) &&
748                                         notedist < 3)
749                                 return (NO);
750                 }
751         }
752
753         /* neither group can have slashes */
754         if (gs1_p->slash_alt > 0 || gs2_p->slash_alt > 0)
755                 return (NO);
756
757         /* the first group can't have accidentals */
758         for (n = 0; n < gs1_p->nnotes; n++) {
759                 if (gs1_p->notelist[n].accidental != '\0')
760                         return (NO);
761         }
762
763         /* the first group can't any preceding grace groups */
764         if (gs1_p->prev != 0 && gs1_p->prev->grpvalue == GV_ZERO)
765                 return (NO);
766
767         /* the first group can't have a roll unless the second group has one */
768         if (gs1_p->roll != NOITEM && gs2_p->roll == NOITEM)
769                 return (NO);
770
771         /* the second group can't have any dots */
772         if (gs2_p->dots > 0)
773                 return (NO);
774
775         /* the second group can't have any flags */
776         if (gs2_p->basictime >= 8 && gs2_p->beamloc == NOITEM)
777                 return (NO);
778
779         /* neither group can have a stem forced the "wrong" way */
780         if (gs1_p->stemdir == DOWN || gs2_p->stemdir == UP)
781                 return (NO);
782
783         /*
784          * At this point we know we can overlap.
785          */
786         return (YES);
787 }
788 \f
789 /*
790  * Name:        procsome()
791  *
792  * Abstract:    Sets coords for group(s) and their associated grace groups.
793  *
794  * Returns:     void
795  *
796  * Description: This function calls procbunch() to set the horizontal coords
797  *              for the given group(s) and their notes, etc.  Then it calls
798  *              procgrace() to deal with any grace groups preceding these
799  *              group(s) and adjust the main group(s)' west coordinates to.
800  *              contain the grace groups.
801  */
802
803 static void
804 procsome(noteptrs, mll_p, staff_p, gs1_p, gs2_p)
805
806 struct NOTEPTRS noteptrs[];     /* array of ptrs to notes to process */
807 struct MAINLL *mll_p;           /* the MLL item the group is connected to */
808 struct STAFF *staff_p;          /* the staff the groups are connected to */
809 struct GRPSYL *gs1_p, *gs2_p;   /* point at group(s) in this hand */
810
811 {
812         /* process the normal group(s) */
813         procbunch(noteptrs, mll_p, staff_p, gs1_p, gs2_p);
814
815         /* process any grace groups preceding first normal group */
816         procgrace(noteptrs, mll_p, staff_p, gs1_p);
817
818         /* process any grace groups preceding second normal group, if exists */
819         if (gs2_p != 0)
820                 procgrace(noteptrs, mll_p, staff_p, gs2_p);
821 }
822 \f
823 /*
824  * Name:        procgrace()
825  *
826  * Abstract:    Sets coords for grace groups and adjusts normal group's west.
827  *
828  * Returns:     void
829  *
830  * Description: This function loops leftward from the given normal group,
831  *              calling procbunch() for each grace group, and adjusting the
832  *              normal group's west coordinate accordingly.
833  */
834
835 static void
836 procgrace(noteptrs, mll_p, staff_p, gsnorm_p)
837
838 struct NOTEPTRS noteptrs[];     /* array of ptrs to notes to process */
839 struct MAINLL *mll_p;           /* the MLL item the group is connected to */
840 struct STAFF *staff_p;          /* the staff the groups are connected to */
841 struct GRPSYL *gsnorm_p;        /* point at the normal group to start from */
842
843 {
844         struct GRPSYL *gs_p;    /* point at a grace group */
845         struct GRPSYL *right_p; /* point at the group to the right of this */
846         int n;                  /* loop variable */
847
848
849         /*
850          * Loop through any grace groups preceding the normal group, working
851          * right to left.  Call procbunch() for each.  Upon return, set
852          * the grace group's x,e,w relative to the normal group's x, and
853          * alter the west coordinate of the normal group to include them.
854          */
855         right_p = gsnorm_p;
856         for (gs_p = gsnorm_p->prev; gs_p != 0 && gs_p->grpvalue == GV_ZERO;
857                                 gs_p = gs_p->prev) {
858                 /* clear noteptrs, and resetup for this grace group */
859                 /* note:  grace groups are always notes, not rests or spaces */
860                 for (n = 0; n < MAXHAND + 1; n++) {
861                         noteptrs[n].top_p = 0;
862                         noteptrs[n].bot_p = 0;
863                 }
864                 /* set all the "top" group pointers */
865                 for (n = 0; n < gs_p->nnotes; n++)
866                         noteptrs[n].top_p = &gs_p->notelist[n];
867
868                 procbunch(noteptrs, mll_p, staff_p, gs_p, (struct GRPSYL *)0);
869
870                 gs_p->c[RX] = right_p->c[RW] - gs_p->c[RE];
871                 gs_p->c[RW] += gs_p->c[RX];
872                 gs_p->c[RE] += gs_p->c[RX];
873
874                 gsnorm_p->c[RW] = gs_p->c[RW];
875                 right_p = gs_p;
876         }
877 }
878 \f
879 /*
880  * Name:        procbunch()
881  *
882  * Abstract:    Sets relative horizontal coords of note heads, accs, & dots.
883  *
884  * Returns:     void
885  *
886  * Description: This function figures out which note heads in the given
887  *              group(s) need to be put on the "wrong" side of the stem to
888  *              avoid overlapping.  Then it sets all note heads' horizontal
889  *              coords.  It calls doacc() to find and store the positions
890  *              for the accidentals, dodot() for the dots.  It sets RW and
891  *              RE for the group(s), also taking flags into consideration.
892  */
893
894 /*
895  * This macro checks the n'th structure in noteptrs.  If the top group has
896  * a note there, it returns a pointer to that note, else it returns the
897  * bottom pointer, which may or may not be 0.
898  */
899 #define GETPTR(n)       (noteptrs[n].top_p != 0 ?               \
900                         noteptrs[n].top_p : noteptrs[n].bot_p)
901
902 static void
903 procbunch(noteptrs, mll_p, staff_p, gs1_p, gs2_p)
904
905 struct NOTEPTRS noteptrs[];     /* array of ptrs to notes to process */
906 struct MAINLL *mll_p;           /* the MLL item the group is connected to */
907 struct STAFF *staff_p;          /* the staff the groups are connected to */
908 struct GRPSYL *gs1_p, *gs2_p;   /* point at group(s) in this hand */
909
910 {
911         int normhead[MAXHAND + 1];      /* position of note heads */
912         float gwide;                    /* width of any note in these groups */
913         float nwide;                    /* width of a particular note */
914         float maxwide;                  /* max of gwide for the two groups */
915         float ghigh;                    /* height of any note in these groups*/
916         float nhigh;                    /* height of a particular note */
917         float g1wide, g2wide;           /* gwide for the two groups */
918         float maxhigh;                  /* max of ghigh for the two groups */
919         float flagwidth;                /* width of a flag */
920         float rh;                       /* relative horizontal of a note */
921         int collinear;                  /* are the 2 groups' stems collinear? */
922         register int k, n;              /* loop variables */
923         int size;
924
925
926         /*
927          * If this is a tablature staff, call a special function to handle it,
928          * and return.  Voices on tab staffs are handled one at a time, so
929          * gs2_p will never be used for them.
930          */
931         if (is_tab_staff(staff_p->staffno)) {
932                 proctab(mll_p, staff_p, gs1_p);
933                 return;
934         }
935
936         collinear = NO;                 /* assume not collinear stems */
937
938         /*
939          * "Normal" position of a note head means to the left of the stem
940          * for an upward stem, and right for downward.  When two notes in a
941          * group are on neighboring letters, one of the note heads has to be
942          * in "abnormal" position so that they don't collide.  Shared
943          * note heads must always be in normal position.  (The fact
944          * that no two of them can be on neighboring letters is enforced
945          * when checking for compatibility of groups.)
946          */
947         /*
948          * See if there are any shared notes first.
949          */
950         for (n = 0; noteptrs[n].top_p != 0; n++) {
951                 if (noteptrs[n].bot_p != 0)
952                         break;          /* found a shared note */
953         }
954
955         if (noteptrs[n].top_p != 0) {
956                 /*
957                  * There are shared notes, and n indexes to the first one
958                  * (starting from the top).  Set this first one to normal.
959                  * First work upwards from there, reversing normality
960                  * whenever there are neighboring notes, setting back to
961                  * normal otherwise.  Then work downwards from there, doing
962                  * the same.
963                  */
964                 normhead[n] = YES;
965                 for (k = n - 1 ; k >= 0; k--) {
966                         if (noteptrs[k+1].top_p->stepsup ==
967                             noteptrs[ k ].top_p->stepsup - 1)
968                                 normhead[k] = ! normhead[k+1];
969                         else
970                                 normhead[k] = YES;
971                 }
972                 for (k = n + 1 ; noteptrs[k].bot_p != 0; k++) {
973                         if (noteptrs[k-1].bot_p->stepsup ==
974                             noteptrs[ k ].bot_p->stepsup + 1)
975                                 normhead[k] = ! normhead[k-1];
976                         else
977                                 normhead[k] = YES;
978                 }
979         } else {
980                 /*
981                  * There are no shared notes.  It may even be that there's only
982                  * one group.  In each group, the note that's opposite the stem
983                  * must be normal, and then we go down the list of other notes
984                  * in the group, reversing normality whenever there are
985                  * neighboring notes, and setting back to normal otherwise.
986                  * There's a special concern if the bottom note of the top
987                  * group is on the neighboring letter to the top note of the
988                  * bottom group, or if it is on the same letter.  In that case,
989                  * we want to offset the groups slightly, such that their stems
990                  * are collinear, so set that flag.
991                  */
992                 /* the first group's stem could go either way */
993                 if (gs1_p->stemdir == UP) {
994                         normhead[n-1] = YES;    /* bottom note normal */
995                         for (k = n - 2; k >= 0; k--) {
996                                 if (noteptrs[k+1].top_p->stepsup ==
997                                     noteptrs[ k ].top_p->stepsup - 1)
998                                         normhead[k] = ! normhead[k+1];
999                                 else
1000                                         normhead[k] = YES;
1001                         }
1002                 } else {        /* stemdir == DOWN */
1003                         normhead[0] = YES;      /* top note normal */
1004                         for (k = 1; k < n; k++) {
1005                                 if (noteptrs[k-1].top_p->stepsup ==
1006                                     noteptrs[ k ].top_p->stepsup + 1)
1007                                         normhead[k] = ! normhead[k-1];
1008                                 else
1009                                         normhead[k] = YES;
1010                         }
1011                 }
1012
1013                 /* the second group's stem (if it exists) must go down */
1014                 if (gs2_p != 0) {
1015                         normhead[n] = YES;      /* top note normal */
1016                         for (k = n + 1; noteptrs[k].bot_p != 0; k++) {
1017                                 if (noteptrs[k-1].bot_p->stepsup ==
1018                                     noteptrs[ k ].bot_p->stepsup + 1)
1019                                         normhead[k] = ! normhead[k-1];
1020                                 else
1021                                         normhead[k] = YES;
1022                         }
1023
1024                         collinear = (noteptrs[n-1].top_p->stepsup <=
1025                                      noteptrs[ n ].bot_p->stepsup + 1);
1026                 }
1027         }
1028
1029         /*
1030          * Set gwide and ghigh to be the biggest values of any note in the top
1031          * group, also storing the width of each note for later use.
1032          */
1033         gwide = ghigh = 0.0;
1034         for (n = 0; noteptrs[n].top_p != 0; n++) {
1035                 size = noteptrs[n].top_p->notesize == GS_NORMAL ?
1036                                 DFLT_SIZE : SMALLSIZE;
1037                 nwide = width(noteptrs[n].top_p->headfont, size,
1038                                 noteptrs[n].top_p->headchar);
1039                 noteptrs[n].wid = nwide;
1040                 if (nwide > gwide) {
1041                         gwide = nwide;
1042                 }
1043                 nhigh = height(noteptrs[n].top_p->headfont, size,
1044                                 noteptrs[n].top_p->headchar);
1045                 if (nhigh > ghigh) {
1046                         ghigh = nhigh;
1047                 }
1048         }
1049
1050         /* remember these values, for comparing to the other group (if any) */
1051         maxwide = g1wide = gwide;       /* widest group so far */
1052         maxhigh = ghigh;                /* highest group so far */
1053
1054         if (gs1_p->basictime <= 1) {
1055                 gs1_p->stemx = 0.0;     /* center the imaginary stem */
1056         } else {
1057                 gs1_p->stemx = gs1_p->stemdir == UP ? gwide / 2 : -gwide / 2;
1058         }
1059
1060         for (n = 0; noteptrs[n].top_p != 0; n++) {
1061                 nwide = noteptrs[n].wid;
1062
1063                 if (normhead[n] == YES) {
1064                         /*
1065                          * The note head is in normal position, so usually its
1066                          * relative x coord is 0, and west and east are half a
1067                          * width off.  But if the note is smaller than the
1068                          * group's max, and there is a stem, and the note is
1069                          * not shared by the other group, the note needs to
1070                          * be off center so that it touches the stem.
1071                          */
1072                         if (nwide != gwide && gs1_p->basictime >= 2 &&
1073                                         noteptrs[n].bot_p == 0) {
1074                                 if (gs1_p->stemdir == UP) {
1075                                         noteptrs[n].top_p->c[RE] = gwide / 2;
1076                                         noteptrs[n].top_p->c[RX] =
1077                                                         gwide / 2 - nwide / 2;
1078                                         noteptrs[n].top_p->c[RW] =
1079                                                         gwide / 2 - nwide;
1080                                 } else {        /* DOWN */
1081                                         noteptrs[n].top_p->c[RW] = -gwide / 2;
1082                                         noteptrs[n].top_p->c[RX] =
1083                                                         -gwide / 2 + nwide / 2;
1084                                         noteptrs[n].top_p->c[RE] =
1085                                                         -gwide / 2 + nwide;
1086                                 }
1087                         } else {
1088                                 noteptrs[n].top_p->c[RX] = 0;
1089                                 noteptrs[n].top_p->c[RW] = -nwide / 2;
1090                                 noteptrs[n].top_p->c[RE] = nwide / 2;
1091                         }
1092                 } else {
1093                         /*
1094                          * The note head is in abnormal position.  Its relative
1095                          * x coord, and west and east, depend on which way the
1096                          * stem is going.  Smaller than normal notes need to
1097                          * be placed differently regardless of whether stemed.
1098                          * In all case, adjust by W_NORMAL*POINT, the width of
1099                          * the stem, so that the note overlays the stem.
1100                          */
1101                         if (nwide != gwide) {
1102                                 if (gs1_p->stemdir == UP) {
1103                                         noteptrs[n].top_p->c[RW] =
1104                                                 gwide / 2 - W_NORMAL * POINT;
1105                                         noteptrs[n].top_p->c[RX] =
1106                                                 gwide / 2 + nwide / 2
1107                                                 - W_NORMAL * POINT;
1108                                         noteptrs[n].top_p->c[RE] =
1109                                                 gwide / 2 + nwide
1110                                                 - W_NORMAL * POINT;
1111                                 } else {        /* DOWN */
1112                                         noteptrs[n].top_p->c[RE] =
1113                                                 W_NORMAL * POINT - gwide / 2;
1114                                         noteptrs[n].top_p->c[RX] =
1115                                                 W_NORMAL * POINT
1116                                                 - gwide / 2 - nwide /2;
1117                                         noteptrs[n].top_p->c[RW] =
1118                                                 W_NORMAL * POINT
1119                                                 - gwide / 2 - nwide;
1120                                 }
1121                         } else {
1122                                 if (gs1_p->stemdir == UP) {
1123                                         noteptrs[n].top_p->c[RX] =
1124                                                 nwide - W_NORMAL * POINT;
1125                                         noteptrs[n].top_p->c[RW] =
1126                                                 nwide * 0.5 - W_NORMAL * POINT;
1127                                         noteptrs[n].top_p->c[RE] =
1128                                                 nwide * 1.5 - W_NORMAL * POINT;
1129                                 } else {        /* DOWN */
1130                                         noteptrs[n].top_p->c[RX] =
1131                                                 W_NORMAL * POINT - nwide;
1132                                         noteptrs[n].top_p->c[RW] =
1133                                                 W_NORMAL * POINT - nwide * 1.5;
1134                                         noteptrs[n].top_p->c[RE] =
1135                                                 W_NORMAL * POINT - nwide * 0.5;
1136                                 }
1137                         }
1138                 }
1139         }
1140
1141         /*
1142          * If there is a bottom group, get note head character width for
1143          * it, find where in noteptrs that group starts, then loop through
1144          * it, setting coords.  While doing this, set the group's
1145          * horizontal coords.
1146          */
1147         g2wide = 0.0;   /* to avoid useless 'used before set' warning */
1148         if (gs2_p != 0) {
1149                 /* skip by notes that are only in the top group */
1150                 for (n = 0; noteptrs[n].bot_p == 0; n++)
1151                         ;
1152                 /*
1153                  * Set gwide and ghigh to be the biggest values of any note in
1154                  * the bottom group, also storing the width of each note for
1155                  * later use.  If the note is shared between groups, the width
1156                  * has already been stored in noteptrs[].wid, so we don't have
1157                  * to recalculate it.
1158                  */
1159                 gwide = ghigh = 0.0;
1160                 for ( ; noteptrs[n].bot_p != 0; n++) {
1161                         size = noteptrs[n].bot_p->notesize == GS_NORMAL ?
1162                                         DFLT_SIZE : SMALLSIZE;
1163                         if (noteptrs[n].wid == 0.0) {
1164                                 nwide = width(noteptrs[n].bot_p->headfont, size,
1165                                                 noteptrs[n].bot_p->headchar);
1166                                 noteptrs[n].wid = nwide;
1167                         } else {
1168                                 nwide = noteptrs[n].wid;
1169                         }
1170                         if (nwide > gwide) {
1171                                 gwide = nwide;
1172                         }
1173                         nhigh = height(noteptrs[n].bot_p->headfont, size,
1174                                         noteptrs[n].bot_p->headchar);
1175                         if (nhigh > ghigh) {
1176                                 ghigh = nhigh;
1177                         }
1178                 }
1179                 g2wide = gwide;
1180                 if (gs2_p->basictime <= 1) {
1181                         gs2_p->stemx = 0.0;     /* center the imaginary stem */
1182                 } else {
1183                         gs2_p->stemx = gs2_p->stemdir == UP ? gwide / 2
1184                                                            : -gwide / 2;
1185                 }
1186
1187                 /* if groups have different note head sizes, adjust maxes */
1188                 if (gwide > maxwide)
1189                         maxwide = gwide;
1190                 if (ghigh > maxhigh)
1191                         maxhigh = ghigh;
1192
1193                 for (n = 0; noteptrs[n].bot_p == 0; n++)
1194                         ;
1195                 for ( ; noteptrs[n].bot_p != 0; n++) {
1196                         nwide = noteptrs[n].wid;
1197
1198                         if (normhead[n] == YES) {
1199                                 /*
1200                                  * The note head is in normal position, so its
1201                                  * relative x coord is 0, and west and east are
1202                                  * half a width off.  But if the note is smaller
1203                                  * than the widest note in the group and there
1204                                  * is a stem, and the note is not shared by the
1205                                  * other group, the note needs to be off center
1206                                  * so that it touches the stem.
1207                                  */
1208                                 if (nwide != gwide && gs2_p->basictime >= 2 &&
1209                                                 noteptrs[n].top_p == 0) {
1210                                         noteptrs[n].bot_p->c[RW] = -gwide / 2;
1211                                         noteptrs[n].bot_p->c[RX] =
1212                                                         -gwide / 2 + nwide / 2;
1213                                         noteptrs[n].bot_p->c[RE] =
1214                                                         -gwide / 2 + nwide;
1215                                 } else {
1216                                         noteptrs[n].bot_p->c[RX] = 0;
1217                                         noteptrs[n].bot_p->c[RW] = -nwide * 0.5;
1218                                         noteptrs[n].bot_p->c[RE] = nwide * 0.5;
1219                                 }
1220                         } else {
1221                                 /*
1222                                  * The note head is in abnormal position.  Its
1223                                  * relative x coord, and west and east, depend
1224                                  * on which way the stem is going, but the
1225                                  * stem must always be down in group 2.  Smaller
1226                                  * than normal notes need to be placed
1227                                  * differently regardless of whether stemed.
1228                                  */
1229                                 if (nwide != gwide) {
1230                                         noteptrs[n].bot_p->c[RE] =
1231                                                 W_NORMAL * POINT - gwide / 2;
1232                                         noteptrs[n].bot_p->c[RX] =
1233                                                 W_NORMAL * POINT
1234                                                 - gwide / 2 - nwide /2;
1235                                         noteptrs[n].bot_p->c[RW] =
1236                                                 W_NORMAL * POINT
1237                                                 - gwide / 2 - nwide;
1238                                 } else {
1239                                         noteptrs[n].bot_p->c[RX] =
1240                                                 W_NORMAL * POINT - nwide;
1241                                         noteptrs[n].bot_p->c[RW] =
1242                                                 W_NORMAL * POINT - nwide * 1.5;
1243                                         noteptrs[n].bot_p->c[RE] =
1244                                                 W_NORMAL * POINT - nwide * 0.5;
1245                                 }
1246                         }
1247                 }
1248         }
1249
1250         /* find position of accidentals */
1251         doacc(noteptrs, maxwide / 2, maxhigh / 2, collinear);
1252
1253         /* find position of dots after notes */
1254         dodot(staff_p, gs1_p, gs2_p, maxwide / 2, collinear);
1255
1256         /* find position of right parentheses around notes */
1257         noterparen(noteptrs, gs1_p, gs2_p, maxwide/2, maxhigh/2, collinear);
1258
1259         /*
1260          * Set RX for the group(s) to 0 for now if stems are offset (the
1261          * normal case), or to the appropriate value if stems are collinear.
1262          * If we only have one group it will thus be set to 0 now, though
1263          * later, if there's an incompatible group next to it, this coord
1264          * and all others will be adjusted.
1265          */
1266         if (collinear) {
1267                 gs1_p->c[RX] = (W_NORMAL * POINT - maxwide) / 2;
1268                 gs2_p->c[RX] = (maxwide - W_NORMAL * POINT) / 2;
1269         } else {
1270                 gs1_p->c[RX] = 0;
1271                 if (gs2_p != 0)
1272                         gs2_p->c[RX] = 0;
1273         }
1274
1275         /*
1276          * Set the western boundaries for the group(s).
1277          */
1278         /*
1279          * Init the group's RW to 0.  Then loop through the notes, finding the
1280          * westernmost thing associated with a note, and leaving the group's RW
1281          * set to that.
1282          */
1283         gs1_p->c[RW] = 0;
1284         for (k = 0; k < gs1_p->nnotes; k++) {
1285                 rh = notehorz(gs1_p, &gs1_p->notelist[k], RW);
1286                 if (rh < gs1_p->c[RW])
1287                         gs1_p->c[RW] = rh;
1288         }
1289         /*
1290          * If the stem is down on a half note or shorter that is to have
1291          * slashes through its stem, make sure there is room for the slashes.
1292          */
1293         if (gs1_p->slash_alt > 0 && gs1_p->stemdir == DOWN &&
1294                         gs1_p->basictime >= 2) {
1295                 gwide = g1wide;
1296                 /* if position of stem minus slash room < current west . . . */
1297                 if (-gwide / 2 - SLASHPAD < gs1_p->c[RW])
1298                         gs1_p->c[RW] = -gwide / 2 - SLASHPAD;
1299         }
1300         westwith(gs1_p);                /* expand RW for "with" list if needbe*/
1301         gs1_p->c[RW] -= gs1_p->padding; /* add user requested padding */
1302
1303         /* add the pad parameter that user wants for this voice */
1304         gs1_p->c[RW] -= vvpath(gs1_p->staffno, gs1_p->vno, PAD)->pad;
1305
1306         csbstempad(mll_p, gs1_p);       /* cross staff beaming may need space */
1307         gs1_p->c[RW] += gs1_p->c[RX];   /* shift by RX, in case RX isn't 0 */
1308
1309         /*
1310          * If group 2 exists, do the same for it.  However, in the slash
1311          * section, we know the stem must be down, so no need to check that.
1312          */
1313         if (gs2_p != 0) {
1314                 gs2_p->c[RW] = 0;
1315                 for (k = 0; k < gs2_p->nnotes; k++) {
1316                         rh = notehorz(gs2_p, &gs2_p->notelist[k], RW);
1317                         if (rh < gs2_p->c[RW])
1318                                 gs2_p->c[RW] = rh;
1319                 }
1320                 if (gs2_p->slash_alt > 0 && gs2_p->basictime >= 2) {
1321                         gwide = g2wide;
1322                         /* if pos of stem minus slash room < current west . .*/
1323                         if (-gwide / 2 - SLASHPAD < gs2_p->c[RW])
1324                                 gs2_p->c[RW] = -gwide / 2 - SLASHPAD;
1325                 }
1326                 westwith(gs2_p);
1327                 gs2_p->c[RW] -= gs2_p->padding;
1328                 gs2_p->c[RW] -= vvpath(gs2_p->staffno, gs2_p->vno, PAD)->pad;
1329                 csbstempad(mll_p, gs2_p);
1330                 gs2_p->c[RW] += gs2_p->c[RX];
1331         }
1332
1333         /*
1334          * Set the eastern boundaries for the group(s).
1335          */
1336         /*
1337          * Init the group's RE to 0.  Then loop through the notes, finding the
1338          * easternmost thing associated with a note, and leaving the group's RE
1339          * set to that.
1340          */
1341         gs1_p->c[RE] = 0;
1342         for (k = 0; k < gs1_p->nnotes; k++) {
1343                 rh = notehorz(gs1_p, &gs1_p->notelist[k], RE);
1344                 if (rh > gs1_p->c[RE])
1345                         gs1_p->c[RE] = rh;
1346         }
1347         /*
1348          * Add in any padding needed for ties, slurs, and bends.  Also add room
1349          * for alternations if there are any.
1350          */
1351         gs1_p->c[RE] += tieslurpad(staff_p, gs1_p);
1352         if (gs1_p->slash_alt < 0 && gs1_p->beamloc == STARTITEM)
1353                 gs1_p->c[RE] += ALTPAD;
1354         /*
1355          * If the stem is up and a flag is needed, and the east boundary
1356          * doesn't yet contain it, adjust the east boundary so the flag will
1357          * fit.
1358          */
1359         if (gs1_p->stemdir == UP && gs1_p->basictime >= 8 &&
1360                                 gs1_p->beamloc == NOITEM) {
1361                 flagwidth = width(FONT_MUSIC, gs1_p->grpsize == GS_NORMAL ?
1362                         DFLT_SIZE : SMALLSIZE, C_UPFLAG);
1363                 if (gs1_p->notelist[0].c[RE] + flagwidth > gs1_p->c[RE])
1364                         gs1_p->c[RE] = gs1_p->notelist[0].c[RE] + flagwidth;
1365         }
1366         /*
1367          * If the stem is up on a half note or shorter that is to have slashes
1368          * through its stem, make sure there's room for the slashes.
1369          */
1370         if (gs1_p->slash_alt > 0 && gs1_p->stemdir == UP &&
1371                         gs1_p->basictime >= 2) {
1372                 gwide = g1wide;
1373                 /* if position of stem plus slash room > current east . . . */
1374                 if (gwide / 2 + SLASHPAD > gs1_p->c[RE])
1375                         gs1_p->c[RE] = gwide / 2 + SLASHPAD;
1376         }
1377         /*
1378          * Expand RE some more if need be to accommodate the "with" list.  Then
1379          * shift it over by RX, in case RX isn't 0.
1380          */
1381         eastwith(gs1_p);
1382         gs1_p->c[RE] += gs1_p->c[RX];
1383
1384         /*
1385          * If group 2 exists, do the same for it.  However, the stem is always
1386          * down, so any flags will always already fit.  For the same reason,
1387          * slashes don't need to be considered.
1388          */
1389         if (gs2_p != 0) {
1390                 gs2_p->c[RE] = 0;
1391                 for (k = 0; k < gs2_p->nnotes; k++) {
1392                         rh = notehorz(gs2_p, &gs2_p->notelist[k], RE);
1393                         if (rh > gs2_p->c[RE])
1394                                 gs2_p->c[RE] = rh;
1395                 }
1396                 gs2_p->c[RE] += tieslurpad(staff_p, gs2_p);
1397                 if (gs2_p->slash_alt < 0 && gs2_p->beamloc == STARTITEM)
1398                         gs2_p->c[RE] += ALTPAD;
1399                 eastwith(gs2_p);
1400                 gs2_p->c[RE] += gs2_p->c[RX];
1401         }
1402 }
1403 \f
1404 /*
1405  * Name:        doacc()
1406  *
1407  * Abstract:    Finds horizontal position for each accidental in group(s).
1408  *
1409  * Returns:     void
1410  *
1411  * Description: This function loops through all the accidentals belonging
1412  *              to notes in the group(s) it is given.  It figures out where
1413  *              to place them horizontally to avoid overlap, and stores the
1414  *              relative west coord of each in NOTE.waccr.  For each group,
1415  *              it uses the appropriate size of accidentals (based on normal
1416  *              versus cue/grace), and places them appropriately, considering
1417  *              also the size of the notes.  However, if there are two groups,
1418  *              the note head sizes could be different.  The halfwide and
1419  *              halfhigh passed in are supposed to be the right size for the
1420  *              bigger of the two sizes, and accidentals will not be packed
1421  *              as tightly against the other notes.  This doesn't hurt, and
1422  *              isn't worth the trouble to do it "right".
1423  *
1424  *              This function takes into account parentheses around accidentals.
1425  *              Its algorithm treats them as part of the accidental.  Also, when
1426  *              there are parentheses around the note, it handles the left
1427  *              parentheses the same way:  if there is also an accidental, it
1428  *              treats it as part of it; otherwise the paren is handled like an
1429  *              accidental itself.
1430  */
1431
1432 /* this fudge factor prevents roundoff error from causing overlap */
1433 #define FUDGE           (.01)
1434
1435 /* when CSS applies to a note or acc, move it by this much */
1436 #define CSS_OFF         (CSS_STEPS * STEPSIZE)
1437
1438 static void
1439 doacc(noteptrs, halfwide, halfhigh, collinear)
1440
1441 struct NOTEPTRS noteptrs[];     /* array of ptrs to notes to process */
1442 double halfwide;                /* half of max of width & height of (notes */
1443 double halfhigh;                /*  in group 1, notes in group 2) */
1444 int collinear;                  /* are stems collinear? */
1445
1446 {
1447         /*
1448          * Each structure in this table represents either a note head that
1449          * is farther left than normal, or an accidental.  A note head
1450          * could be too far left for one of two reasons: either it was
1451          * forced to be on the left ("wrong") side of a stem that points
1452          * down, or it is a normal note in the top group when the stems are
1453          * collinear.  In the collinear case, to make this function easier,
1454          * we start out regarding the bottom group as being normal, and
1455          * the top group as being shifted left one note head, and we figure
1456          * everything relative to the bottom group.  But at the end we adjust
1457          * waccr so that every accidental is relative to its own group, like
1458          * it's supposed to be.
1459          *
1460          * The coordinates define the rectangle that surrounds the note or acc,
1461          * including standard padding, even on note heads, which don't
1462          * normally have padding.  First the notes are put into this table;
1463          * then the accidentals, one at a time, making sure they don't
1464          * overlap things already in the table.
1465          * To see if the accidental being added overlaps, first its north
1466          * and south are tested.  All previous rectangles that are "out of
1467          * its way" vertically are marked not "relevant"; the others are
1468          * marked "relevant".  As positions are tried, right to left, positions
1469          * that fail to avoid overlap are marked "tried".
1470          *
1471          * After the correct position is found for an accidental, there is a
1472          * special case for flats and double flats to take advantage of their
1473          * shape and let them pack tighter.
1474          */
1475         struct {
1476                 float n, s, e, w;       /* boundaries of a rectangle */
1477                 short relevant;         /* is rectangle relevant? */
1478                 short tried;            /* have we tried this one yet? */
1479         } rectab[2 * MAXHAND + 1];      /* enough for all notes & accidentals*/
1480
1481         struct NOTE *note_p;            /* point at a note */
1482         int reclim;                     /* index after last rectangle in tab */
1483         float north, south, east, west; /* relative coords of new accidental */
1484         float accasc, accdesc;          /* ascent & descent of accidental */
1485         float accwidth;                 /* width of new accidental */
1486         float parenwidth;               /* width of note's left parenthesis */
1487         float parenv;                   /* half the vertical size of paren */
1488         float totwidth;                 /* width of acc plus paren */
1489         int overlap;                    /* does our acc overlap existing ones*/
1490         int try;                        /* which element of rectab to try */
1491         int found;                      /* accs/parens found so far */
1492         int k, j;                       /* loop variables */
1493         int size;
1494         float horfn, verfn;             /* horz & vert flat/nat notch sizes */
1495         float savehorfn;                /* save original horfn */
1496
1497
1498         reclim = 0;                     /* table initially empty */
1499
1500         /*
1501          * Loop through noteptrs, finding all notes that are left of normal
1502          * position, entering them in rectab.  Include padding around them.
1503          * First loop through all notes, finding ones that are on the left
1504          * side of a down stem; then, if stems are collinear, loop through
1505          * the top group, finding all normal notes.
1506          */
1507         for (k = 0; (note_p = GETPTR(k)) != 0; k++) {
1508                 if (note_p->c[RX] < 0) {
1509                         rectab[reclim].n = note_p->c[RY] + halfhigh + STDPAD;
1510                         rectab[reclim].s = note_p->c[RY] - halfhigh - STDPAD;
1511                         rectab[reclim].e = note_p->c[RE] + STDPAD;
1512                         rectab[reclim].w = note_p->c[RW] - STDPAD;
1513                         if (note_p->stepsup >= CSS_STEPS / 2) {
1514                                 rectab[reclim].n += CSS_OFF;
1515                                 rectab[reclim].s += CSS_OFF;
1516                         } else if (note_p->stepsup <= -CSS_STEPS / 2) {
1517                                 rectab[reclim].n -= CSS_OFF;
1518                                 rectab[reclim].s -= CSS_OFF;
1519                         }
1520                         reclim++;
1521                 }
1522         }
1523         if (collinear) {
1524                 for (k = 0; (note_p = noteptrs[k].top_p) != 0; k++) {
1525                         if (note_p->c[RX] == 0) {
1526                                 rectab[reclim].n = note_p->c[RY] + halfhigh
1527                                                 + STDPAD;
1528                                 rectab[reclim].s = note_p->c[RY] - halfhigh
1529                                                 - STDPAD;
1530                                 rectab[reclim].e = W_NORMAL * POINT
1531                                                 - halfwide + STDPAD;
1532                                 rectab[reclim].w = W_NORMAL * POINT
1533                                                 - 3 * halfwide - STDPAD;
1534                                 if (note_p->stepsup >= CSS_STEPS / 2) {
1535                                         rectab[reclim].n += CSS_OFF;
1536                                         rectab[reclim].s += CSS_OFF;
1537                                 } else if (note_p->stepsup <= -CSS_STEPS / 2) {
1538                                         rectab[reclim].n -= CSS_OFF;
1539                                         rectab[reclim].s -= CSS_OFF;
1540                                 }
1541                                 reclim++;
1542                         }
1543                 }
1544         }
1545
1546         /* prevent false "may be used before set" lint warning */
1547         verfn = savehorfn = 0.0;
1548
1549         /*
1550          * Loop through all notes, find the ones with accs or parens.  Find
1551          * where the accs and parens will fit, storing that info in waccr, and
1552          * adding them to rectab.  Call a function so that we loop in the
1553          * proper order.
1554          */
1555         for (found = 0, k = nextacc(noteptrs, found);  k != -1;
1556                                 found++, k = nextacc(noteptrs, found)) {
1557                 note_p = GETPTR(k);
1558                 /* get dimensions of accidental if there is one */
1559                 if (note_p->accidental != '\0') {
1560                         accdimen(note_p, &accasc, &accdesc, &accwidth);
1561                 } else {
1562                         accwidth = accasc = accdesc = 0.0;
1563                 }
1564                 /* get dimensions of note's left paren, if there is one */
1565                 if (note_p->note_has_paren == YES) {
1566                         size = (note_p->notesize == GS_NORMAL ?
1567                                         DFLT_SIZE : SMALLSIZE);
1568                         parenwidth = width(FONT_TR, size, '(');
1569                         parenv = height(FONT_TR, size, '(') / 2.0;
1570                 } else {
1571                         parenwidth = parenv = 0.0;
1572                 }
1573                 /* set the north, south, and width of what we have found */
1574                 north = note_p->c[RY] + MAX(accasc, parenv);
1575                 south = note_p->c[RY] - MAX(accdesc, parenv);
1576                 if (note_p->stepsup >= CSS_STEPS / 2) {
1577                         north += CSS_OFF;
1578                         south += CSS_OFF;
1579                 } else if (note_p->stepsup <= -CSS_STEPS / 2) {
1580                         north -= CSS_OFF;
1581                         south -= CSS_OFF;
1582                 }
1583                 totwidth = accwidth + parenwidth;
1584
1585                 /*
1586                  * For each rectangle in rectab, decide whether (based on
1587                  * its vertical coords) it could possibly overlap with our
1588                  * new accidental.  If it's totally above or below ours, it
1589                  * can't.  We allow a slight overlap (FUDGE) so that round
1590                  * off errors don't stop us from packing things as tightly
1591                  * as possible.
1592                  */
1593                 for (j = 0; j < reclim; j++) {
1594                         if (rectab[j].s + FUDGE > north ||
1595                             rectab[j].n < south + FUDGE)
1596                                 rectab[j].relevant = NO;
1597                         else
1598                                 rectab[j].relevant = YES;
1599                 }
1600
1601                 /*
1602                  * Mark that none of the relevant rectangles' boundaries have
1603                  * been tried yet for positioning our acc.
1604                  */
1605                 for (j = 0; j < reclim; j++) {
1606                         if (rectab[j].relevant == YES)
1607                                 rectab[j].tried = NO;
1608                 }
1609
1610                 /*
1611                  * Set up first trial position for this acc., just to the
1612                  * left of normal notes, allowing padding.
1613                  */
1614                 east = - halfwide - STDPAD;
1615                 west = east - totwidth;
1616
1617                 /*
1618                  * Keep trying positions for this acc, working right to
1619                  * left.  When we find one that doesn't overlap an existing
1620                  * rectangle, break.  This has to succeed at some point,
1621                  * at the leftmost rectangle position if not earlier.
1622                  */
1623                 for (;;) {
1624                         overlap = NO;
1625                         for (j = 0; j < reclim; j++) {
1626                                 /* ignore ones too far north or south */
1627                                 if (rectab[j].relevant == NO)
1628                                         continue;
1629
1630                                 /* if all west or east, okay; else overlap */
1631                                 if (rectab[j].w + FUDGE <= east &&
1632                                     rectab[j].e >= west + FUDGE) {
1633                                         overlap = YES;
1634                                         break;
1635                                 }
1636                         }
1637
1638                         /* if no rectangle overlapped, we found a valid place*/
1639                         if (overlap == NO)
1640                                 break;
1641
1642                         /*
1643                          * Something overlapped, so we have to try again.
1644                          * Find the eastermost relevant west rectangle boundary
1645                          * that hasn't been tried already, to use as the next
1646                          * trial position for our acc's east.
1647                          */
1648                         try = -1;
1649                         for (j = 0; j < reclim; j++) {
1650                                 /* ignore ones too far north or south */
1651                                 if (rectab[j].relevant == NO ||
1652                                     rectab[j].tried == YES)
1653                                         continue;
1654
1655                                 /*
1656                                  * If this is the first relevant one we haven't
1657                                  * tried, or if this is farther east than the
1658                                  * easternmost so far, save it as being the
1659                                  * new easternmost so far.
1660                                  */
1661                                 if (try == -1 || rectab[j].w > rectab[try].w)
1662                                         try = j;
1663                         }
1664
1665                         if (try == -1)
1666                                 pfatal("bug in doacc()");
1667
1668                         /*
1669                          * Mark this one as having been tried (for next time
1670                          * around, if necessary).  Set new trial values for
1671                          * east and west of our acc.
1672                          */
1673                         rectab[try].tried = YES;
1674                         east = rectab[try].w;
1675                         west = east - totwidth;
1676
1677                 } /* end of while loop trying positions for this acc */
1678
1679                 /*
1680                  * We found the correct position for the new acc.  However, for
1681                  * flats, double flats & nats, we would like a notch to be taken
1682                  * out of the upper right corner of their rectangle, in effect,
1683                  * since there's nothing there but white space.  This can only
1684                  * be done if the acc is not already right next to the group.
1685                  */
1686                 if (note_p->accidental == '&' || note_p->accidental == 'B' ||
1687                                         note_p->accidental == 'n') {
1688                         /* get notch size; if paren, add width to horz */
1689                         if (note_p->accidental == 'n') {
1690                                 horfn = 1.4 * STEPSIZE; /* horizontal notch */
1691                                 verfn = 1.6 * STEPSIZE; /* vertical notch */
1692                         } else {
1693                                 horfn = 1.5 * STEPSIZE; /* horizontal notch */
1694                                 verfn = 2.8 * STEPSIZE; /* vertical notch */
1695                         }
1696                         if (note_p->notesize == GS_SMALL) {
1697                                 horfn *= SM_FACTOR;
1698                                 verfn *= SM_FACTOR;
1699                         }
1700                         if (note_p->acc_has_paren) {
1701                                 size = (note_p->notesize == GS_NORMAL ?
1702                                                 DFLT_SIZE : SMALLSIZE);
1703                                 horfn += width(FONT_TR, size, ')');
1704                         }
1705                         savehorfn = horfn;      /* may need it later */
1706                         /*
1707                          * If notch width is bigger than the max possible dist
1708                          * we could move the acc (we would overwrite the note),
1709                          * reduce it to be the space available.
1710                          */
1711                         if (horfn > - east - halfwide - STDPAD)
1712                                 horfn = - east - halfwide - STDPAD;
1713
1714                         /* only attempt the shift if > 0 width available */
1715                         if (horfn > 0.0) {
1716                                 /*
1717                                  * The useable notch size is horfn by verfn.
1718                                  * We'd like to move the acc to the right by
1719                                  * horfn.  We can only do this if the space is
1720                                  * unoccupied that is immediately to the right
1721                                  * of the acc, of width = horfn and height =
1722                                  * (height of acc) - verfn.  (If only part of
1723                                  * that space is available, we won't bother
1724                                  * trying to use it.)  So check whether any
1725                                  * existing rectangle overlaps that space.
1726                                  */
1727                                 overlap = NO;
1728                                 for (j = 0; j < reclim; j++) {
1729                                         if (rectab[j].s + FUDGE <= north - verfn &&
1730                                             rectab[j].n - FUDGE >= south &&
1731                                             rectab[j].w + FUDGE <= east + horfn &&
1732                                             rectab[j].e - FUDGE >= east) {
1733                                                 overlap = YES;
1734                                                 break;
1735                                         }
1736                                 }
1737                                 /*
1738                                  * If the space is free, move the acc to the
1739                                  * right by HORFN.
1740                                  */
1741                                 if (overlap == NO) {
1742                                         west += horfn;
1743                                         east += horfn;
1744                                 } else {
1745                                         /*
1746                                          * All right, let's try again with 1/2
1747                                          * of the previous horfn.
1748                                          */
1749                                         horfn /= 2.0;
1750                                         overlap = NO;
1751                                         for (j = 0; j < reclim; j++) {
1752                                                 if (rectab[j].s + FUDGE <= north - verfn &&
1753                                                     rectab[j].n - FUDGE >= south &&
1754                                                     rectab[j].w + FUDGE <= east + horfn &&
1755                                                     rectab[j].e - FUDGE >= east) {
1756                                                         overlap = YES;
1757                                                         break;
1758                                                 }
1759                                         }
1760                                         if (overlap == NO) {
1761                                                 west += horfn;
1762                                                 east += horfn;
1763                                         }
1764                                 }
1765                         }
1766                 }
1767
1768                 /*
1769                  * We have the final position for the new acc.  Enter it into
1770                  * rectab.  But for naturals, we don't want to reserve the
1771                  * lower left corner, where there is nothing but white space;
1772                  * so in that case, put two overlapping entries in rectab to
1773                  * account for the rest of the space.  Naturals are symmetrical,
1774                  * so we can use the same horfn and verfn as were calculated
1775                  * above for the upper right corner.
1776                  */
1777                 if (note_p->accidental == 'n') {
1778                         /* upper part of natural */
1779                         rectab[reclim].n = north;
1780                         rectab[reclim].s = south + verfn;
1781                         rectab[reclim].e = east;
1782                         rectab[reclim].w = west;
1783                         reclim++;
1784
1785                         /* right hand part of natural */
1786                         rectab[reclim].n = north;
1787                         rectab[reclim].s = south;
1788                         rectab[reclim].e = east;
1789                         rectab[reclim].w = west + savehorfn;
1790                 } else {
1791                         /* some other accidental; reserve the whole rectangle*/
1792                         rectab[reclim].n = north;
1793                         rectab[reclim].s = south;
1794                         rectab[reclim].e = east;
1795                         rectab[reclim].w = west;
1796                 }
1797                 reclim++;
1798
1799                 /*
1800                  * Store the acc's west in waccr in the NOTE structure for
1801                  * whichever groups have this note.  Store wlparen when there
1802                  * is a left paren on the note.
1803                  */
1804                 if (noteptrs[k].top_p != 0) {
1805                         if (note_p->note_has_paren == YES)
1806                                 noteptrs[k].top_p->wlparen = west;
1807                         if (note_p->accidental != '\0')
1808                                 noteptrs[k].top_p->waccr = west + parenwidth;
1809                 }
1810                 if (noteptrs[k].bot_p != 0) {
1811                         if (note_p->note_has_paren == YES)
1812                                 noteptrs[k].bot_p->wlparen = west;
1813                         if (note_p->accidental != '\0')
1814                                 noteptrs[k].bot_p->waccr = west + parenwidth;
1815                 }
1816
1817         } /* end of loop for each accidental */
1818
1819         /*
1820          * Finally, if the stems were collinear, we have to adjust waccr for
1821          * all the notes of the top group, so that it's relative to the top
1822          * group instead of the bottom group.
1823          */
1824         if (collinear) {
1825                 for (k = 0; noteptrs[k].top_p != 0; k++) {
1826                         if (noteptrs[k].top_p->note_has_paren == YES)
1827                                 noteptrs[k].top_p->wlparen += 2 * halfwide
1828                                                 - W_NORMAL * POINT;
1829                         if (noteptrs[k].top_p->accidental != '\0')
1830                                 noteptrs[k].top_p->waccr += 2 * halfwide
1831                                                 - W_NORMAL * POINT;
1832                 }
1833         }
1834 }
1835 \f
1836 /*
1837  * Name:        nextacc()
1838  *
1839  * Abstract:    Find the next note that has an accidental to be processed.
1840  *
1841  * Returns:     Index to the NOTE, or -1 if no more.
1842  *
1843  * Description: This function is called by doacc(), to return in the correct
1844  *              order the notes that have accidentals to be processed.
1845  *              (Actually, a note is to be processed not only if it has an
1846  *              accidental, but also if it has parentheses.)  The first time in
1847  *              here, count is 0, and it looks for the first eligible note (top
1848  *              down).  The next time, count is 1, and it looks for the bottom-
1849  *              most eligible note.  After that, it goes through the inner
1850  *              notes, top down.  In the great majority of cases, this will
1851  *              result in the most desirable packing of accidentals.
1852  */
1853
1854 static int
1855 nextacc(noteptrs, found)
1856
1857 struct NOTEPTRS noteptrs[];     /* array of ptrs to notes to process */
1858 int found;                      /* no. of accidentals found already */
1859
1860 {
1861         struct NOTE *note_p;    /* point at a note */
1862         static int previdx;     /* idx to note chosen the last time in here */
1863         static int lastidx;     /* idx to the bottommost note chosen */
1864         int n;                  /* loop counter */
1865
1866
1867         /*
1868          * If this is the first call for this group(s), find the topmost
1869          * eligible note.
1870          */
1871         if (found == 0) {
1872                 for (n = 0; (note_p = GETPTR(n)) != 0; n++) {
1873                         if (note_p->accidental != '\0' ||
1874                                         note_p->note_has_paren == YES) {
1875                                 previdx = n;    /* remember it for next time */
1876                                 return (n);
1877                         }
1878                 }
1879                 return (-1);    /* no notes have acc or parens */
1880         }
1881
1882         /*
1883          * If this is the second call, find the bottom of the list, then look
1884          * backwards for the last eligible note.  Stop before finding the first
1885          * note again.
1886          */
1887         if (found == 1) {
1888                 /* find the slot beyond the last note */
1889                 for (n = 0; (note_p = GETPTR(n)) != 0; n++) {
1890                         ;
1891                 }
1892                 /* search from last note going backwards */
1893                 for (n-- ; n > previdx; n--) {
1894                         note_p = GETPTR(n);
1895                         if (note_p->accidental != '\0' ||
1896                                         note_p->note_has_paren == YES) {
1897                                 lastidx = n;    /* remember it for next time */
1898                                 return (n);
1899                         }
1900                 }
1901                 return (-1);    /* only 1 note has acc or parens */
1902         }
1903
1904         /*
1905          * Third or later call:  Scan inner notes top to bottom.
1906          */
1907         for (n = previdx + 1; n < lastidx; n++) {
1908                 note_p = GETPTR(n);
1909                 if (note_p->accidental != '\0' ||
1910                                 note_p->note_has_paren == YES) {
1911                         previdx = n;
1912                         return (n);
1913                 }
1914         }
1915         return (-1);            /* all eligible notes were already found */
1916 }
1917 \f
1918 /*
1919  * Name:        dodot()
1920  *
1921  * Abstract:    Finds horizontal and vertical positions of dots.
1922  *
1923  * Returns:     void
1924  *
1925  * Description: This function figures out the limitations on where dots
1926  *              can be put, for each group, and calls dogrpdot() for each
1927  *              group that has dots, to figure their positions.
1928  */
1929
1930 static void
1931 dodot(staff_p, gs1_p, gs2_p, halfwide, collinear)
1932
1933 struct STAFF *staff_p;          /* the staff the groups are connected to */
1934 register struct GRPSYL *gs1_p, *gs2_p;  /* point at group(s) in this hand */
1935 double halfwide;                        /* half of max of width of notes */
1936 int collinear;                          /* are stems collinear? */
1937
1938 {
1939         /* the highest and lowest values of steps above the middle staff */
1940         /* line that a dot is allowed to be for the given group */
1941         int uppermost, lowermost;
1942
1943         int lowtopidx;          /* index to lowest note of top group */
1944         int push;               /* steps to protruding note */
1945         register int k;         /* loop variable */
1946
1947
1948         lowtopidx = gs1_p->nnotes - 1;  /* for convenience */
1949
1950         /*
1951          * For each group that needs dots, set the outer limits of where
1952          * they are allowed.  If the other group doesn't need dots, we
1953          * have to be careful to keep them out of its way.  Otherwise,
1954          * don't worry about that; let them fall on top of each other if
1955          * that would happen.
1956          */
1957
1958         /*
1959          * If the first group needs dots, find out how high and low they are
1960          * allowed to be.  Also find out if nearby notes in the other group
1961          * could be in the way of dots.  Call dogrpdot() with this info to
1962          * find their positions.
1963          */
1964         if (gs1_p->dots > 0) {
1965                 /* upper limit is always as described above */
1966                 uppermost = gs1_p->notelist[0].stepsup;
1967                 if (uppermost % 2 == 0)         /* line note */
1968                         uppermost++;
1969
1970                 /* set lower limit as if no other group */
1971                 lowermost = gs1_p->notelist[lowtopidx].stepsup;
1972                 if (lowermost % 2 == 0)         /* line note */
1973                         lowermost--;
1974
1975                 /* but adjust if the other group exists & would interfere */
1976                 if (gs2_p != 0 && gs2_p->dots == 0 || collinear) {
1977                         if (lowermost <= gs2_p->notelist[0].stepsup)
1978                                 lowermost += 2;
1979                 }
1980
1981                 /*
1982                  * If the stems are collinear, bottom group notes that are
1983                  * in normal position for that group protrude to the right
1984                  * relative to the top group.  From top down, search for notes
1985                  * in the bottom group that are like this.  Set push to the
1986                  * first one.  If none are found, let push be 1000 to be out of
1987                  * the way.  In setting horizontal dot positions, dogrpdot()
1988                  * needs to know this.
1989                  */
1990                 push = 1000;
1991                 if ( gs2_p != 0 && collinear ) {
1992                         for (k = 0; k < gs2_p->nnotes; k++) {
1993                                 if (gs2_p->notelist[k].c[RX] == 0) {
1994                                         push = gs2_p->notelist[k].stepsup;
1995                                         break;
1996                                 }
1997                         }
1998                 }
1999
2000                 /* do top group's dots */
2001                 dogrpdot(staff_p, gs1_p, (struct GRPSYL *)0, halfwide,
2002                                 uppermost, lowermost, push);
2003         }
2004
2005         /*
2006          * If the second group exists and needs dots, find out how high and
2007          * low they are allowed to be, and find their positions.
2008          */
2009         if (gs2_p != 0 && gs2_p->dots > 0) {
2010                 /* set upper limit as if no other group */
2011                 uppermost = gs2_p->notelist[0].stepsup;
2012                 if (uppermost % 2 == 0)         /* line note */
2013                         uppermost++;
2014
2015                 /* but adjust if the other group would interfere */
2016                 if (gs1_p->dots == 0 || collinear) {
2017                         if (uppermost >= gs1_p->notelist[lowtopidx].stepsup)
2018                                 uppermost -= 2;
2019                 }
2020
2021                 /* lower limit is always as described above */
2022                 lowermost = gs2_p->notelist[ gs2_p->nnotes - 1 ].stepsup;
2023                 if (lowermost % 2 == 0)         /* line note */
2024                         lowermost--;
2025
2026                 /*
2027                  * Unless the stems are collinear, in which case no problem,
2028                  * from bottom up, search for notes in the top group that
2029                  * protrude towards the right.  Set push to the first one.
2030                  * If none are found, let push be 1000 to be out of the way.
2031                  * In setting horizontal dot positions, dogrpdot() needs to
2032                  * know this.
2033                  */
2034                 push = 1000;
2035                 if ( ! collinear ) {
2036                         for (k = lowtopidx; k >= 0; k--) {
2037                                 if (gs1_p->notelist[k].c[RX] > 0) {
2038                                         push = gs1_p->notelist[k].stepsup;
2039                                         break;
2040                                 }
2041                         }
2042                 }
2043
2044                 /* do bottom group's dots */
2045                 dogrpdot(staff_p, gs2_p, gs1_p, halfwide, uppermost, lowermost,
2046                                 push);
2047         }
2048 }
2049 \f
2050 /*
2051  * Name:        dogrpdot()
2052  *
2053  * Abstract:    Finds horizontal and vertical positions of dots for one group.
2054  *
2055  * Returns:     void
2056  *
2057  * Description: This function loops through all the notes belonging to the
2058  *              given group, setting the coords of the dots relative to it.
2059  */
2060
2061 /* recover dotsteps from ydotr, avoiding roundoff error */
2062 #define DOTSTEPS(ydotr) (                               \
2063         ydotr > 0.0 ?                                   \
2064                 (int)((ydotr + 0.001) / STEPSIZE)       \
2065         :                                               \
2066                 -(int)((-ydotr + 0.001) / STEPSIZE)     \
2067 )
2068
2069 static void
2070 dogrpdot(staff_p, gs_p, ogs_p, halfwide, uppermost, lowermost, push)
2071
2072 struct STAFF *staff_p;          /* the staff the groups are connected to */
2073 register struct GRPSYL *gs_p;   /* point at group */
2074 struct GRPSYL *ogs_p;           /* if we're doing group 1 and 2 together, and
2075                                  * gs_p is group 2, ogs_p is group 1, else 0 */
2076 double halfwide;                /* half of max of width of notes */
2077 int uppermost;                  /* highest step where a dot is permitted */
2078 int lowermost;                  /* lowest step where a dot is permitted */
2079 int push;                       /* avoid protruding note at this position */
2080
2081 {
2082         float dotwidth;         /* width of a dot (includes padding) */
2083         int normhorz;           /* use normal horizontal dot position? */
2084         int notesteps;          /* steps note is above center line of staff */
2085         int dotsteps;           /* steps dot is above center line of staff */
2086         register int n, k;      /* loop variables */
2087
2088
2089         /* until proven otherwise, assume normal horizontal dot position */
2090         normhorz = YES;
2091
2092         /*
2093          * The rules for vertical positioning of dots are as follows.
2094          * For space notes, dots will be put in the same space.  For line
2095          * notes we'd like them to be in the space directly above, except for
2096          * voice 2 in vscheme=2o,3o or 2f,3f when voice 1 is not space, in
2097          * which case we'd like them to be in the space below.  But if notes in
2098          * a group are jammed onto neighboring steps, we may need to put some
2099          * line note dots on the space below regardless; and we may
2100          * even have to let some dots land on top of each other.  But in
2101          * any case, never exceed the uppermost/lowermost bounds, which
2102          * would interfere with the other group.
2103          *
2104          * The rules for horizontal positioning of dots are as follows.
2105          * If the note on the dot's space, or either neighboring line,
2106          * is in abnormal position to the right, the dot must be put
2107          * farther right than normal.  The parameter "push" is the nearest
2108          * note from the other group that protrudes this way.  And the dots
2109          * of all the notes have to line up, so if any one has this problem,
2110          * they must all be moved.
2111          */
2112
2113         /*
2114          * Loop through all notes in the group, setting dot positions.  At
2115          * the top of the loop, "dotsteps" is the previous dot, but by the
2116          * end it gets set to the current dot.
2117          */
2118         dotsteps = uppermost + 2;       /* pretend previous dot was here */
2119
2120         for (n = 0; n < gs_p->nnotes; n++) {
2121
2122                 notesteps = gs_p->notelist[n].stepsup;
2123
2124                 if (notesteps % 2 == 0) {
2125                         /*
2126                          * This note is on a line.  If the dot cannot be put
2127                          * above the line, or if doing that would overlay the
2128                          * previous dot and we are allowed to put it below
2129                          * the line, then put it below the line.  Else, put
2130                          * it above the line.  Notice that we're putting the
2131                          * dot in the space above if at all possible; later on,
2132                          * we'll make adjustments for voice 2 if appropriate.
2133                          */
2134                         if (notesteps + 1 > uppermost ||
2135                            (notesteps + 1 == dotsteps &&
2136                             notesteps - 1 >= lowermost)) {
2137                                 dotsteps = notesteps - 1;
2138                         } else {
2139                                 dotsteps = notesteps + 1;
2140                         }
2141                 } else {
2142                         /*
2143                          * This note is on a space.  The dot must be put in
2144                          * this same space, regardless of anything else.
2145                          */
2146                         dotsteps = notesteps;
2147                 }
2148
2149                 /* set relative y coord based on step position */
2150                 gs_p->notelist[n].ydotr = dotsteps * STEPSIZE;
2151
2152                 /*
2153                  * Now see if this dot forces abnormal positioning.  "Push" may
2154                  * indicate a protruding note in the other group.  If this
2155                  * note is within 1 step of our dot, use abnormal positioning
2156                  * for the dot.  Else if the stem is down, all dots can be
2157                  * normal.  Else, we have to search for protruding notes to
2158                  * see where the dot can be.
2159                  */
2160                 if (normhorz == YES) {
2161                         if (abs(dotsteps - push) <= 1) {
2162                                 normhorz = NO;
2163                         } else if (gs_p->stemdir == UP) {
2164                                 for (k = 0; k < gs_p->nnotes; k++) {
2165                                         notesteps = gs_p->notelist[k].stepsup;
2166
2167                                         if (gs_p->notelist[k].c[RE] >halfwide &&
2168                                             notesteps <= dotsteps + 1 &&
2169                                             notesteps >= dotsteps - 1) {
2170
2171                                                 normhorz = NO;
2172                                                 break;
2173                                         }
2174                                 }
2175                         }
2176                 }
2177         }
2178
2179         /*
2180          * Set horizontal dot positions, relative to the group.  STDPAD is
2181          * needed because notehead characters don't include padding.  The
2182          * abnormal case adds in one more notehead width, minus the width
2183          * of the stem.  Since the dots for all notes line up vertically,
2184          * xdotr is in GRPSYL instead of in each NOTE.
2185          */
2186         dotwidth = width(FONT_MUSIC, DFLT_SIZE, C_DOT);
2187         gs_p->xdotr = halfwide + STDPAD + dotwidth / 2;
2188         if (normhorz == NO) {
2189                 gs_p->xdotr += 2 * halfwide - W_NORMAL * POINT;
2190         }
2191
2192         /*
2193          * If this is voice 2, we may need to adjust the vertical position of
2194          * nonshared line notes.  The same should happen if this is voice 3
2195          * "standing in" for voice 2.
2196          */
2197         if (gs_p->pvno == 2) {
2198                 int trymove;            /* try to move dots? */
2199                 int vscheme;            /* voice scheme */
2200                 RATIONAL vtime;         /* time so far in this measure */
2201                 int prevdotsteps;       /* Y distance of prev note's dot */
2202                 struct GRPSYL *pgs_p;   /* point along GRPSYL list */
2203                 int onotesteps;         /* lowest note of voice 1 */
2204
2205                 trymove = NO;           /* first assume leave them alone */
2206                 vscheme = svpath(gs_p->staffno, VSCHEME)->vscheme;
2207                 if (vscheme == V_2OPSTEM || vscheme == V_3OPSTEM) {
2208                         /* always try to move if 2o or 3o */
2209                         trymove = YES;
2210                 } else {
2211                         /* 2f or 3f; move iff voice 1 is not all spaces here */
2212                         vtime = Zero;   /* add up time of preceding groups */
2213                         for (pgs_p = gs_p->prev; pgs_p != 0;
2214                                         pgs_p = pgs_p->prev) {
2215                                 vtime = radd(vtime, pgs_p->fulltime);
2216                         }
2217                         if ( ! hasspace(staff_p->groups_p[0], vtime,
2218                                         radd(vtime, gs_p->fulltime))) {
2219                                 /* not all space during duration of our group*/
2220                                 trymove = YES;
2221                         }
2222                 }
2223
2224                 if (trymove == YES) {
2225                         /*
2226                          * We need to try to move the dots of line notes from
2227                          * the space above them to the space below them.  We
2228                          * will work from bottom to top.  Initially, pretend
2229                          * that the previous note is way low out of the way.
2230                          * If a voice 1 group was being handled along with our
2231                          * group, find the stepsup of its lowest note.
2232                          */
2233                         prevdotsteps = -1000;
2234                         if (ogs_p != 0) {
2235                                 onotesteps = ogs_p->notelist[
2236                                                 ogs_p->nnotes - 1].stepsup;
2237                         } else {
2238                                 onotesteps = 0; /* for lint; set before used */
2239                         }
2240                         for (n = gs_p->nnotes - 1; n >= 0; n--) {
2241                                 notesteps = gs_p->notelist[n].stepsup;
2242                                 /*
2243                                  * We want to stop if we run into notes shared
2244                                  * by group 1 if it exists.  ( > is defensive).
2245                                  */
2246                                 if (ogs_p != 0 && notesteps >= onotesteps)
2247                                         break;
2248                                 /*
2249                                  * Recover our dotsteps from our dots coord
2250                                  * calculated earlier in this function.  Then,
2251                                  * consider moving our dot only if we are a
2252                                  * line note and our dot is currently in the
2253                                  * space above.  (It could already be below,
2254                                  * do to tightly packed notes.)
2255                                  */
2256                                 dotsteps = DOTSTEPS(gs_p->notelist[n].ydotr);
2257                                 if (notesteps % 2 == 0 &&
2258                                                 dotsteps == notesteps + 1) {
2259                                         /*
2260                                          * If the previous (lower) note is at
2261                                          * least 2 steps away, we can certainly
2262                                          * move our dot.  But also move it if
2263                                          * we are the top note of group 2, and
2264                                          * group 1 exists and has a note 2 steps
2265                                          * away, and they don't have a dot at
2266                                          * the same horz position; because our
2267                                          * dot would be confusing if above.  If
2268                                          * it make our dot land on top of the
2269                                          * previous note's dot, tough.
2270                                          */
2271                                         if (prevdotsteps < notesteps - 1 ||
2272                                             n == 0 && ogs_p != 0 &&
2273                                             notesteps + 2 == onotesteps &&
2274                                             ogs_p->xdotr != gs_p->xdotr) {
2275
2276                                                 dotsteps -= 2;
2277                                                 gs_p->notelist[n].ydotr -=
2278                                                         2.0 * STEPSIZE;
2279                                         }
2280                                 }
2281                                 prevdotsteps = dotsteps;
2282                         }
2283                 }
2284         }
2285 }
2286 \f
2287 /*
2288  * Name:        westwith()
2289  *
2290  * Abstract:    Adjust west coord of a group to allow for its "with" lists.
2291  *
2292  * Returns:     void
2293  *
2294  * Description: This function is given a GRPSYL whose relative horizontal
2295  *              coords are set, relative to the center of the group, except
2296  *              that "with" lists have not yet been considered.  It alters
2297  *              gs_p->c[RW] if need be so that the group's rectangle includes
2298  *              all "with" lists.
2299  */
2300
2301 static void
2302 westwith(gs_p)
2303
2304 struct GRPSYL *gs_p;            /* point at this group */
2305
2306 {
2307         int n;                  /* loop through the "with" list items */
2308         int font, size;         /* of the chars in the "with" list item */
2309         int first_char;         /* first char of string to print */
2310         char *str_p;            /* point into the item */
2311         float x_offset;         /* half the width of the first char in item */
2312
2313
2314         for (n = 0; n < gs_p->nwith; n++) {
2315                 /* should center first character on x */
2316                 font = gs_p->withlist[n][0];
2317                 size = gs_p->withlist[n][1];
2318                 str_p = gs_p->withlist[n] + 2;
2319                 first_char = next_str_char(&str_p, &font, &size);
2320                 x_offset = width(font, size, first_char) / 2.0;
2321                 if (-x_offset < gs_p->c[RW])
2322                         gs_p->c[RW] = -x_offset;
2323         }
2324 }
2325 \f
2326 /*
2327  * Name:        eastwith()
2328  *
2329  * Abstract:    Adjust east coord of a group to allow for its "with" lists.
2330  *
2331  * Returns:     void
2332  *
2333  * Description: This function is given a GRPSYL whose relative horizontal
2334  *              coords are set, relative to the center of the group, except
2335  *              that "with" lists have not yet been considered.  It alters
2336  *              gs_p->c[RE] if need be so that the group's rectangle includes
2337  *              all "with" lists.
2338  */
2339
2340 static void
2341 eastwith(gs_p)
2342
2343 struct GRPSYL *gs_p;            /* point at this group */
2344
2345 {
2346         int n;                  /* loop through the "with" list items */
2347         int font, size;         /* of the chars in the "with" list item */
2348         int first_char;         /* first char of string to print */
2349         char *str_p;            /* point into the item */
2350         float x_offset;         /* half the width of the first char in item */
2351
2352
2353         for (n = 0; n < gs_p->nwith; n++) {
2354                 /* should center first character on x */
2355                 font = gs_p->withlist[n][0];
2356                 size = gs_p->withlist[n][1];
2357                 str_p = gs_p->withlist[n] + 2;
2358                 first_char = next_str_char(&str_p, &font, &size);
2359                 x_offset = strwidth(gs_p->withlist[n]) -
2360                                 width(font, size, first_char) / 2.0;
2361                 if (x_offset > gs_p->c[RE])
2362                         gs_p->c[RE] = x_offset;
2363         }
2364 }
2365 \f
2366 /*
2367  * Name:        csbstempad()
2368  *
2369  * Abstract:    Pad a group's RW for cross staff beaming if need be.
2370  *
2371  * Returns:     void
2372  *
2373  * Description: In cross staff beamed groups, where the beams are between the
2374  *              staffs, and a note on the bottom staff is followed by a note on
2375  *              the top staff, and the first note has no dots or anything else
2376  *              that would force more space after it, and the top note has no
2377  *              accidentals, graces, or anything that would force more space
2378  *              before it, the stems of the two groups can be very close
2379  *              together, too close.  This function checks for that case, and
2380  *              when found, adds padding to the left of the top group.
2381  */
2382
2383 static void
2384 csbstempad(mll_p, gs_p)
2385
2386 struct MAINLL *mll_p;           /* the MLL item the group is connected to */
2387 struct GRPSYL *gs_p;            /* point at the top staff's group */
2388
2389 {
2390         struct GRPSYL *gs2_p;           /* point at various GRPSYLs */
2391         struct CHORD *ch_p, *pch_p;     /* our chord and preceding chord */
2392         struct MAINLL *m2_p;            /* loop through MLL */
2393         int k;                          /* loop through notelist */
2394         int found;                      /* have we found our group? */
2395
2396
2397         /* if this group is not a candidate for this, return */
2398         if (gs_p->beamto != CS_BELOW)   /* must be CSB beamed with below */
2399                 return;
2400         if (gs_p->stemdir == UP)        /* stem must be down */
2401                 return;
2402         if (gs_p->beamloc == STARTITEM) /* must not be first item in CSB */
2403                 return;
2404         if (gs_p->prev == 0)            /* (defensive) */
2405                 return;
2406         if (gs_p->prev->grpcont != GC_SPACE)    /* prev must be a space */
2407                 return;
2408
2409         /*
2410          * The notes should all have the same RW (even cues) unless a note is
2411          * on the "wrong" side of the stem, because they are all supposed to
2412          * touch the stem.  In the latter case, there's already enough space in
2413          * the group to the left of the stem, so return.
2414          */
2415         for (k = 1; k < gs_p->nnotes; k++) {
2416                 if (ABSDIFF(gs_p->notelist[k].c[RW], gs_p->notelist[0].c[RW])
2417                                 > FUDGE)
2418                         return;
2419         }
2420
2421         /*
2422          * If there's anything to the left of the notes' RWs (the stem
2423          * position), it should be enough space, so return.
2424          */
2425         if (gs_p->c[RW] < gs_p->notelist[0].c[RW] - STDPAD - FUDGE)
2426                 return;
2427
2428         /* find the chord headcell for this measure */
2429         for (m2_p = mll_p->prev; m2_p->str != S_CHHEAD; m2_p = m2_p->prev)
2430                 ;
2431         /*
2432          * Loop through the chords.  For each chord, loop through all its
2433          * groups, trying to find our group.  It should be found.  At the point
2434          * it is found, pch_p will point to the chord preceding the one that
2435          * contains our group.
2436          */
2437         found = NO;
2438         pch_p = 0;      /* to avoid useless 'used before set' warning */
2439         for (ch_p = m2_p->u.chhead_p->ch_p; ch_p != 0;
2440                                 pch_p = ch_p, ch_p = ch_p->ch_p) {
2441                 for (gs2_p = ch_p->gs_p; gs2_p != 0; gs2_p = gs2_p->gs_p) {
2442                         if (gs2_p == gs_p) {
2443                                 found = YES;
2444                                 break;
2445                         }
2446                 }
2447                 if (found == YES)
2448                         break;
2449         }
2450         if (found == NO)        /* defensive; this should never happen */
2451                 return;
2452
2453         /* find next visible staff after our staff */
2454         for (m2_p = mll_p->next; m2_p->str == S_STAFF &&
2455                         m2_p->u.staff_p->visible == NO; m2_p = m2_p->next)
2456                 ;
2457         if (m2_p->str != S_STAFF)       /* defensive; should not happen */
2458                 return;
2459
2460         /*
2461          * Loop down the preceding chord, looking for a group that is on the
2462          * next visible staff after our staff and is CSB'ed to the staff above.
2463          */
2464         for (gs2_p = pch_p->gs_p; gs2_p != 0; gs2_p = gs2_p->gs_p) {
2465
2466                 if (gs2_p->staffno == m2_p->u.staff_p->staffno &&
2467                                 gs2_p->beamto == CS_ABOVE) {
2468                         /*
2469                          * We found such a group; it must be the only one.
2470                          * Check that it meets the conditions.
2471                          */
2472                         if (gs2_p->stemdir == DOWN)
2473                                 return;
2474                         /*
2475                          * The notes need to all have the same RE, analogous to
2476                          * the earlier check on gs_p's RW.
2477                          */
2478                         for (k = 1; k < gs2_p->nnotes; k++) {
2479                                 if (ABSDIFF(gs2_p->notelist[k].c[RE], gs2_p->
2480                                                 notelist[0].c[RE]) > FUDGE)
2481                                         return;
2482                         }
2483                         /*
2484                          * If there's anything to the right of the notes' REs,
2485                          * there's already enough space.
2486                          */
2487                         if (gs2_p->c[RE] > gs2_p->notelist[0].c[RE] +
2488                                         STDPAD + FUDGE)
2489                                 return;
2490
2491                         /*
2492                          * FINALLY!  We have established the need for more
2493                          * space.  Append it to our group's RW.
2494                          */
2495                         gs_p->c[RW] -= STEPSIZE;
2496                         return;
2497                 }
2498         }
2499
2500         /* didn't find one; shouldn't happen, but just return */
2501 }
2502 \f
2503 /*
2504  * Name:        proctab()
2505  *
2506  * Abstract:    Sets relative horizontal coords of fret numbers.
2507  *
2508  * Returns:     void
2509  *
2510  * Description: This function sets all the horizontal coords of "notes" on a
2511  *              tablature staff, which are actually fret numbers.  It sets RW
2512  *              and RE for the group, too.  They also take bends into account.
2513  */
2514
2515 static void
2516 proctab(mll_p, staff_p, gs_p)
2517
2518 struct MAINLL *mll_p;           /* the MLL item the group is connected to */
2519 struct STAFF *staff_p;          /* the staff the group is connected to */
2520 struct GRPSYL *gs_p;            /* point at this group */
2521
2522 {
2523         int n;                  /* loop through the "notes" in the group */
2524         float halfwide;         /* half the width of a fret or bend number */
2525         float maxhalffret;      /* half the max width of a fret number */
2526         float maxhalfbend;      /* half the max width of a bend number */
2527         float maxbend;          /* width of a bend number that sticks right */
2528         struct GRPSYL *prevgs_p;/* point at previous group */
2529         int center;             /* should bend string be centered? */
2530         int k;                  /* loop variable */
2531
2532
2533         maxhalffret = 0.0;
2534         maxhalfbend = 0.0;
2535         maxbend = 0.0;
2536
2537         prevgs_p = prevgrpsyl(gs_p, &mll_p);    /* in case we need it */
2538
2539         /* loop though all frets and bends in this group */
2540         for (n = 0; n < gs_p->nnotes; n++) {
2541                 /*
2542                  * If there is a fret, find half the width of that number.  It
2543                  * should be centered on the center of the group.  Keep track
2544                  * of the maximum width so far.  Allow 1.5*STDPAD on each side
2545                  * of the fret number, since we don't ever want the numbers so
2546                  * close that they look like one number.
2547                  */
2548                 if (gs_p->notelist[n].FRETNO != NOFRET) {
2549                         halfwide = strwidth(fret_string(&gs_p->notelist[n],
2550                                         gs_p)) / 2.0;
2551                         gs_p->notelist[n].c[RX] = 0.0;
2552                         gs_p->notelist[n].c[RE] = halfwide;
2553                         gs_p->notelist[n].c[RW] = -halfwide;
2554                         maxhalffret = MAX(halfwide + 1.5*STDPAD, maxhalffret);
2555                 }
2556
2557                 /*
2558                  * If there is a bend, figure out if it's the normal situation
2559                  * (centered on the group's X) or the the case where its left
2560                  * edge should be at the group's X (the case of a continuation
2561                  * bend where the previous group's bend was higher).  In the
2562                  * latter case, the string had to be shifted to avoid colliding
2563                  * with the arrow coming down from the previous group.
2564                  */
2565                 if (HASREALBEND(gs_p->notelist[n])) {
2566                         center = YES;   /* first assume normal */
2567
2568                         /* search previous group, if any, for a bend */
2569                         if (prevgs_p != 0) {
2570                                 for (k = 0; k < prevgs_p->nnotes; k++) {
2571                                         if (HASREALBEND(prevgs_p->notelist[k]))
2572                                                 break;
2573                                 }
2574                                 /*
2575                                  * If previous group had a bend and its
2576                                  * distance was higher than the current group,
2577                                  * we have the special case.
2578                                  */
2579                                 if (k < prevgs_p->nnotes &&
2580                                     GT( ratbend(&prevgs_p->notelist[k]),
2581                                     ratbend(&gs_p->notelist[n]) ) ) {
2582                                         center = NO;
2583                                 }
2584                         }
2585                         if (center == YES) {
2586                                 /*
2587                                  * Normal case of a bend string: centered at
2588                                  * group's X.  Maintain maxhalfbend as the
2589                                  * the widest so far.
2590                                  */
2591                                 halfwide = strwidth(bend_string(
2592                                                 &gs_p->notelist[n])) / 2.0;
2593                                 maxhalfbend = MAX(halfwide, maxhalfbend);
2594                         } else {
2595                                 /*
2596                                  * A bend string that has its left edge at the
2597                                  * group's X.  There can only be one such,
2598                                  * since multiple continuation bends are not
2599                                  * allowed (other than releases).
2600                                  */
2601                                 maxbend = strwidth(bend_string(
2602                                                 &gs_p->notelist[n]));
2603                         }
2604                 }
2605         }
2606
2607         /*
2608          * Set the group's relative horizontal coordinates.  On the east, add
2609          * extra room if there are ties or slurs.  On the west, add any user
2610          * requested padding.  Also adjust for "with" lists.  They can extend
2611          * into tie/slur padding, but not into user requested padding.
2612          */
2613         gs_p->c[RX] = 0.0;
2614
2615         gs_p->c[RW] = -MAX(maxhalffret, maxhalfbend);
2616         westwith(gs_p);
2617         gs_p->c[RW] -= gs_p->padding;
2618         gs_p->c[RW] -= vvpath(gs_p->staffno, gs_p->vno, PAD)->pad;
2619
2620         maxhalffret += tieslurpad(staff_p, gs_p);
2621         gs_p->c[RE] = MAX(MAX(maxhalffret, maxhalfbend), maxbend);
2622         eastwith(gs_p);
2623 }
2624 \f
2625 /*
2626  * Name:        noterparen()
2627  *
2628  * Abstract:    Finds horizontal position notes' right parentheses.
2629  *
2630  * Returns:     void
2631  *
2632  * Description: If any of the notes in the given group(s) are to have
2633  *              parentheses around them, this function finds the horizontal
2634  *              positions of the right parentheses.  The left ones were done
2635  *              in doacc() along with accidentals.  For each group, it uses
2636  *              the appropriate size of parentheses (based on normal versus
2637  *              cue/grace), and places them appropriately, considering also
2638  *              the size of the notes.  However, if there are two groups,
2639  *              the note head sizes could be different.  The halfwide and
2640  *              halfhigh passed in are supposed to be the right size for the
2641  *              bigger of the two sizes, and accidentals will not be packed
2642  *              as tightly against the other notes.  This doesn't hurt, and
2643  *              isn't worth the trouble to do it "right".
2644  */
2645
2646 static void
2647 noterparen(noteptrs, gs1_p, gs2_p, halfwide, halfhigh, collinear)
2648
2649 struct NOTEPTRS noteptrs[];     /* array of ptrs to notes to process */
2650 struct GRPSYL *gs1_p, *gs2_p;   /* point at group(s) in this hand */
2651 double halfwide;                /* half of max of width & height of (notes */
2652 double halfhigh;                /*  in group 1, notes in group 2) */
2653 int collinear;                  /* are stems collinear? */
2654
2655 {
2656         /*
2657          * Each structure in this table represents either a note head that is
2658          * farther right than normal, note dot(s), or right paren.  A note head
2659          * could be too far right for one of two reasons: either it was
2660          * forced to be on the right ("wrong") side of a stem that points
2661          * up, or it is a normal note in the bottom group when the stems are
2662          * collinear.  In the collinear case, to make this function easier,
2663          * we start out regarding the top group as being normal, and
2664          * the bottom group as being shifted right one note head, and we figure
2665          * everything relative to the top group.  But at the end we adjust
2666          * so that every parenthesis is relative to its own group, like
2667          * it's supposed to be.
2668          *
2669          * The coordinates define the rectangle that surrounds the note, dot(s),
2670          * or paren, including standard padding, even on note heads, which don't
2671          * normally have padding.  First the notes and dots are put into this
2672          * table, just one rectangle for a sequence of dots; then the right
2673          * parens one at a time, making sure they don't overlap things already
2674          * in the table.
2675          *
2676          * To see if the parenthesis being added overlaps, first its north
2677          * and south are tested.  All previous rectangles that are "out of
2678          * its way" vertically are marked not "relevant"; the others are
2679          * marked "relevant".  As positions are tried, left to right, positions
2680          * that fail to avoid overlap are marked "tried".
2681          */
2682         struct {
2683                 float n, s, e, w;       /* boundaries of a rectangle */
2684                 short relevant;         /* is rectangle relevant? */
2685                 short tried;            /* have we tried this one yet? */
2686         } rectab[2 * MAXHAND + 1];      /* enough for all notes & accidentals*/
2687
2688         struct NOTE *note_p;            /* point at a note */
2689         int reclim;                     /* index after last rectangle in tab */
2690         int parensexist;                /* does any note have parens? */
2691         float north, south, east, west; /* relative coords of new accidental */
2692         float parenwidth;               /* width of note's left parenthesis */
2693         float parenv;                   /* half the vertical size of paren */
2694         float dotoff;                   /* additional offset caused by dots */
2695         float dotoff1, dotoff2;         /* same, for groups 1 and 2 */
2696         int overlap;                    /* does our acc overlap existing ones*/
2697         int try;                        /* which element of rectab to try */
2698         int k, j;                       /* loop variables */
2699         int size;
2700
2701
2702         /*
2703          * If no notes have parentheses, we can get out because there is
2704          * nothing to do.
2705          */
2706         parensexist = NO;               /* init to no parens */
2707         for (k = 0; (note_p = GETPTR(k)) != 0; k++) {
2708                 if (note_p->note_has_paren == YES)
2709                         parensexist = YES;
2710         }
2711         if (parensexist == NO)
2712                 return;
2713
2714         reclim = 0;                     /* table initially empty */
2715
2716         /* set up dot offsets for both groups, zero if no dots */
2717         dotoff1 = gs1_p->dots * (width(FONT_MUSIC,DFLT_SIZE,C_DOT) + 2*STDPAD);
2718         dotoff2 = 0.0;          /* prevent useless 'used before set' warning */
2719         if (gs2_p != 0) {
2720                 dotoff2 = gs2_p->dots * (width(FONT_MUSIC, DFLT_SIZE, C_DOT) +
2721                                 2 * STDPAD);
2722         }
2723
2724         /*
2725          * Loop through noteptrs, loading rectab with all the things that are
2726          * already present that are to the right of the baseline.
2727          */
2728         for (k = 0; (note_p = GETPTR(k)) != 0; k++) {
2729                 /*
2730                  * If note exists in top group, use its dot offset, else use
2731                  * bottom's.  If it's in both, the results would be the same.
2732                  */
2733                 if (noteptrs[k].top_p != 0)
2734                         dotoff = dotoff1;
2735                 else
2736                         dotoff = dotoff2;
2737
2738                 /* if note is right of normal position, put it in the table */
2739                 if (note_p->c[RX] > 0) {
2740                         rectab[reclim].n = note_p->c[RY] + halfhigh + STDPAD;
2741                         rectab[reclim].s = note_p->c[RY] - halfhigh - STDPAD;
2742                         rectab[reclim].e = note_p->c[RE] + STDPAD;
2743                         rectab[reclim].w = note_p->c[RW] - STDPAD;
2744                         reclim++;
2745                 }
2746
2747                 /* if collinear, bottom group's notes go into table if normal */
2748                 if (collinear && noteptrs[k].bot_p != 0) {
2749                         if (note_p->c[RX] == 0) {
2750                                 rectab[reclim].n = note_p->c[RY] + halfhigh
2751                                                 + STDPAD;
2752                                 rectab[reclim].s = note_p->c[RY] - halfhigh
2753                                                 - STDPAD;
2754                                 rectab[reclim].e = W_NORMAL * POINT
2755                                                 + 3 * halfwide + STDPAD;
2756                                 rectab[reclim].w = W_NORMAL * POINT
2757                                                 + halfwide - STDPAD;
2758                                 reclim++;
2759                         }
2760                 }
2761
2762                 /* if this group has dots, do rectangle for dots */
2763                 if (dotoff > 0) {
2764                         rectab[reclim].n = note_p->ydotr + STDPAD;
2765                         rectab[reclim].s = note_p->ydotr - STDPAD;
2766                         if (noteptrs[k].top_p != 0)
2767                                 rectab[reclim].e = gs1_p->xdotr + dotoff;
2768                         else
2769                                 rectab[reclim].e = gs2_p->xdotr + dotoff;
2770                         rectab[reclim].w = 0;
2771                         reclim++;
2772                 }
2773         }
2774
2775         /*
2776          * Loop through all parentheses, finding where they will fit, storing
2777          * that info in erparen, and adding them to rectab.
2778          */
2779         for (k = 0; (note_p = GETPTR(k)) != 0; k++) {
2780
2781                 /* if no parens around the note, skip the note */
2782                 if (note_p->note_has_paren == NO)
2783                         continue;
2784
2785                 /* get dimensions of note's right paren */
2786                 size = (note_p->notesize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE);
2787                 parenwidth = width(FONT_TR, size, ')');
2788                 parenv = height(FONT_TR, size, ')') / 2.0;
2789
2790                 /* set the north and south of the paren */
2791                 north = note_p->c[RY] + parenv;
2792                 south = note_p->c[RY] - parenv;
2793
2794                 /*
2795                  * For each rectangle in rectab, decide whether (based on
2796                  * its vertical coords) it could possibly overlap with our
2797                  * new paren.  If it's totally above or below ours, it
2798                  * can't.  We allow a slight overlap (FUDGE) so that round
2799                  * off errors don't stop us from packing things as tightly
2800                  * as possible.
2801                  */
2802                 for (j = 0; j < reclim; j++) {
2803                         if (rectab[j].s + FUDGE > north ||
2804                             rectab[j].n < south + FUDGE)
2805                                 rectab[j].relevant = NO;
2806                         else
2807                                 rectab[j].relevant = YES;
2808                 }
2809
2810                 /*
2811                  * Mark that none of the relevant rectangles' boundaries have
2812                  * been tried yet for positioning our paren.
2813                  */
2814                 for (j = 0; j < reclim; j++) {
2815                         if (rectab[j].relevant == YES)
2816                                 rectab[j].tried = NO;
2817                 }
2818
2819                 /*
2820                  * Set up first trial position for this paren, just to the
2821                  * right of normal notes, allowing padding.
2822                  */
2823                 west = halfwide + STDPAD;
2824                 east = west + parenwidth;
2825
2826                 /*
2827                  * Keep trying positions for this paren, working left to
2828                  * right.  When we find one that doesn't overlap an existing
2829                  * rectangle, break.  This has to succeed at some point,
2830                  * at the rightmost rectangle position if not earlier.
2831                  */
2832                 for (;;) {
2833                         overlap = NO;
2834                         for (j = 0; j < reclim; j++) {
2835                                 /* ignore ones too far north or south */
2836                                 if (rectab[j].relevant == NO)
2837                                         continue;
2838
2839                                 /* if all west or east, okay; else overlap */
2840                                 if (rectab[j].w + FUDGE <= east &&
2841                                     rectab[j].e >= west + FUDGE) {
2842                                         overlap = YES;
2843                                         break;
2844                                 }
2845                         }
2846
2847                         /* if no rectangle overlapped, we found a valid place*/
2848                         if (overlap == NO)
2849                                 break;
2850
2851                         /*
2852                          * Something overlapped, so we have to try again.
2853                          * Find the westermost relevant east rectangle boundary
2854                          * that hasn't been tried already, to use as the next
2855                          * trial position for our paren's west.
2856                          */
2857                         try = -1;
2858                         for (j = 0; j < reclim; j++) {
2859                                 /* ignore ones too far north or south */
2860                                 if (rectab[j].relevant == NO ||
2861                                     rectab[j].tried == YES)
2862                                         continue;
2863
2864                                 /*
2865                                  * If this is the first relevant one we haven't
2866                                  * tried, or if this is farther west than the
2867                                  * westernmost so far, save it as being the
2868                                  * new westernmost so far.
2869                                  */
2870                                 if (try == -1 || rectab[j].e < rectab[try].e)
2871                                         try = j;
2872                         }
2873
2874                         if (try == -1)
2875                                 pfatal("bug in noterparen()");
2876
2877                         /*
2878                          * Mark this one as having been tried (for next time
2879                          * around, if necessary).  Set new trial values for
2880                          * east and west of our paren.
2881                          */
2882                         rectab[try].tried = YES;
2883                         west = rectab[try].e;
2884                         east = west + parenwidth;
2885
2886                 } /* end of while loop trying positions for this acc */
2887
2888                 /*
2889                  * We have the final position for the new paren.  Enter it into
2890                  * rectab.  Store its east in erparen in the NOTE structure for
2891                  * whichever groups have this note.
2892                  */
2893                 rectab[reclim].n = north;
2894                 rectab[reclim].s = south;
2895                 rectab[reclim].e = east;
2896                 rectab[reclim].w = west;
2897                 reclim++;
2898                 if (noteptrs[k].top_p != 0) {
2899                         noteptrs[k].top_p->erparen = east;
2900                 }
2901                 if (noteptrs[k].bot_p != 0) {
2902                         noteptrs[k].bot_p->erparen = east;
2903                 }
2904
2905         } /* end of loop for each accidental */
2906
2907         /*
2908          * Finally, if the stems were collinear, we have to adjust erparen for
2909          * all the notes of the bottom group, so that it's relative to the
2910          * bottom group instead of the top group.
2911          */
2912         if (collinear) {
2913                 for (k = 0; (note_p = GETPTR(k)) != 0; k++) {
2914                         if (noteptrs[k].bot_p != 0) {
2915                                 noteptrs[k].bot_p->erparen -= 2 * halfwide
2916                                                 - W_NORMAL * POINT;
2917                         }
2918                 }
2919         }
2920 }