chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / setgrps.c
CommitLineData
69695f33
MW
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
18struct 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
24static struct GRPSYL *procallvoices P((struct MAINLL *mll_p,
25 struct GRPSYL *gs_p));
26static void proc1or2voices P((struct MAINLL *mll_p, struct STAFF *staff_p,
27 struct GRPSYL *gs1_p, struct GRPSYL *gs2_p));
28static int compat P((struct NOTEPTRS noteptrs[], struct GRPSYL *gs1_p,
29 struct GRPSYL *gs2_p));
30static int can_overlap P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p));
31static void procsome P((struct NOTEPTRS noteptrs[], struct MAINLL *mll_p,
32 struct STAFF *staff_p, struct GRPSYL *gs1_p,
33 struct GRPSYL *gs2_p));
34static void procgrace P((struct NOTEPTRS noteptrs[], struct MAINLL *mll_p,
35 struct STAFF *staff_p, struct GRPSYL *gsnorm_p));
36static void procbunch P((struct NOTEPTRS noteptrs[], struct MAINLL *mll_p,
37 struct STAFF *staff_p, struct GRPSYL *gs1_p,
38 struct GRPSYL *gs2_p));
39static void doacc P((struct NOTEPTRS noteptrs[], double halfwide,
40 double halfhigh, int collinear));
41static int nextacc P((struct NOTEPTRS noteptrs[], int found));
42static void dodot P((struct STAFF *staff_p, struct GRPSYL *gs1_p,
43 struct GRPSYL *gs2_p, double halfwide, int collinear));
44static 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));
47static void westwith P((struct GRPSYL *gs_p));
48static void eastwith P((struct GRPSYL *gs_p));
49static void csbstempad P((struct MAINLL *mll_p, struct GRPSYL *gs_p));
50static void proctab P((struct MAINLL *mll_p, struct STAFF *staff_p,
51 struct GRPSYL *gs1_p));
52static 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
69void
70setgrps()
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
145static struct GRPSYL *
146procallvoices(mll_p, gs_p)
147
148struct MAINLL *mll_p; /* the MLL item the group is connected to */
149struct 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
320static void
321proc1or2voices(mll_p, staff_p, gs1_p, gs2_p)
322
323struct MAINLL *mll_p; /* the MLL item the group is connected to */
324struct STAFF *staff_p; /* the staff the groups are on */
325register 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
596static int
597compat(noteptrs, gs1_p, gs2_p)
598
599struct NOTEPTRS noteptrs[]; /* array of ptrs to notes to process */
600register 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
720static int
721can_overlap(gs1_p, gs2_p)
722
723struct 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
803static void
804procsome(noteptrs, mll_p, staff_p, gs1_p, gs2_p)
805
806struct NOTEPTRS noteptrs[]; /* array of ptrs to notes to process */
807struct MAINLL *mll_p; /* the MLL item the group is connected to */
808struct STAFF *staff_p; /* the staff the groups are connected to */
809struct 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
835static void
836procgrace(noteptrs, mll_p, staff_p, gsnorm_p)
837
838struct NOTEPTRS noteptrs[]; /* array of ptrs to notes to process */
839struct MAINLL *mll_p; /* the MLL item the group is connected to */
840struct STAFF *staff_p; /* the staff the groups are connected to */
841struct 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
902static void
903procbunch(noteptrs, mll_p, staff_p, gs1_p, gs2_p)
904
905struct NOTEPTRS noteptrs[]; /* array of ptrs to notes to process */
906struct MAINLL *mll_p; /* the MLL item the group is connected to */
907struct STAFF *staff_p; /* the staff the groups are connected to */
908struct 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
1438static void
1439doacc(noteptrs, halfwide, halfhigh, collinear)
1440
1441struct NOTEPTRS noteptrs[]; /* array of ptrs to notes to process */
1442double halfwide; /* half of max of width & height of (notes */
1443double halfhigh; /* in group 1, notes in group 2) */
1444int 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
1854static int
1855nextacc(noteptrs, found)
1856
1857struct NOTEPTRS noteptrs[]; /* array of ptrs to notes to process */
1858int 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
1930static void
1931dodot(staff_p, gs1_p, gs2_p, halfwide, collinear)
1932
1933struct STAFF *staff_p; /* the staff the groups are connected to */
1934register struct GRPSYL *gs1_p, *gs2_p; /* point at group(s) in this hand */
1935double halfwide; /* half of max of width of notes */
1936int 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
2069static void
2070dogrpdot(staff_p, gs_p, ogs_p, halfwide, uppermost, lowermost, push)
2071
2072struct STAFF *staff_p; /* the staff the groups are connected to */
2073register struct GRPSYL *gs_p; /* point at group */
2074struct 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 */
2076double halfwide; /* half of max of width of notes */
2077int uppermost; /* highest step where a dot is permitted */
2078int lowermost; /* lowest step where a dot is permitted */
2079int 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
2301static void
2302westwith(gs_p)
2303
2304struct 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
2340static void
2341eastwith(gs_p)
2342
2343struct 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
2383static void
2384csbstempad(mll_p, gs_p)
2385
2386struct MAINLL *mll_p; /* the MLL item the group is connected to */
2387struct 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
2515static void
2516proctab(mll_p, staff_p, gs_p)
2517
2518struct MAINLL *mll_p; /* the MLL item the group is connected to */
2519struct STAFF *staff_p; /* the staff the group is connected to */
2520struct 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
2646static void
2647noterparen(noteptrs, gs1_p, gs2_p, halfwide, halfhigh, collinear)
2648
2649struct NOTEPTRS noteptrs[]; /* array of ptrs to notes to process */
2650struct GRPSYL *gs1_p, *gs2_p; /* point at group(s) in this hand */
2651double halfwide; /* half of max of width & height of (notes */
2652double halfhigh; /* in group 1, notes in group 2) */
2653int 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}