chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / beamstem.c
CommitLineData
69695f33
MW
1/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 by Arkkra Enterprises */
2/* All rights reserved */
3/*
4 * Name: beamstem.c
5 *
6 * Description: This file contains functions for setting lengths of note
7 * stems, which also involves beaming considerations.
8 */
9
10#include "defines.h"
11#include "structs.h"
12#include "globals.h"
13
14/*
15 * Several functions need to know the value of the "stemlen" parameter, so
16 * instead of them all calling vvpath, define a holding place here.
17 */
18static float Defstemsteps;
19
20static void proclist P((struct MAINLL *mainll_p, int vno));
21static void proctablist P((struct MAINLL *mainll_p, int vno));
22static int stemforced P((struct GRPSYL *gs_p, struct GRPSYL *ogs_p));
23static void setbeam P((struct GRPSYL *start_p, struct GRPSYL *end_p,
24 struct GRPSYL *ogs_p));
25static void restore_ry P((struct GRPSYL *start_p, struct GRPSYL *end_p));
26static double embedgrace P((struct GRPSYL *start_p, double b1, double b0));
27static double embedclef P((struct GRPSYL *start_p, double b1, double b0));
28static double beamoff P((struct GRPSYL *gs_p, int side, double boundary,
29 struct GRPSYL *start_p));
30static void embedrest P((struct GRPSYL *start_p, struct GRPSYL *last_p,
31 double b1, double b0));
32static double avoidothervoice P((struct GRPSYL *start_p, struct GRPSYL *last_p,
33 double b1, double b0, struct GRPSYL *ogs_p));
34static void setgroupvert P((int, struct GRPSYL *, struct GRPSYL *));
35static void settuplet P((struct GRPSYL *start_p, struct STAFF *staff_p));
36static void expgroup P((struct GRPSYL *gs_p, struct GRPSYL *ogs_p));
37static void applywith P((struct GRPSYL *gs_p, int side));
38\f
39/*
40 * Name: beamstem()
41 *
42 * Abstract: Set stem lengths for all notes that have stems or slash/alt.
43 *
44 * Returns: void
45 *
46 * Description: This function loops through the main linked list. For each
47 * linked list of groups on each visible staff, it calls proclist
48 * to set stem lengths.
49 */
50
51void
52beamstem()
53
54{
55 register struct MAINLL *mainll_p; /* point along main linked list */
56 int n; /* loop variable */
57
58
59 debug(16, "beamstem CSSpass=%d", CSSpass);
60 initstructs(); /* clean out old SSV info */
61
62 /*
63 * Loop once for each item in the main linked list. Apply any SSVs
64 * that are found.
65 */
66 for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
67 if (mainll_p->str == S_SSV) {
68
69 asgnssv(mainll_p->u.ssv_p);
70
71 } else if (mainll_p->str == S_STAFF &&
72 mainll_p->u.staff_p->visible == YES &&
73 ! is_mrpt(mainll_p->u.staff_p->groups_p[0])) {
74 /*
75 * For this visible staff, call a subroutine to process
76 * each list of groups on it.
77 */
78 for (n = 0; n < MAXVOICES; n++) {
79 if (mainll_p->u.staff_p->groups_p[n] != 0) {
80 /* set global default stem steps */
81 Defstemsteps = vvpath(mainll_p->
82 u.staff_p->staffno,
83 n + 1, STEMLEN)->stemlen;
84 if (is_tab_staff(mainll_p->u.staff_p->
85 staffno)) {
86 proctablist(mainll_p, n);
87 } else {
88 proclist(mainll_p, n);
89 }
90 }
91 }
92 }
93 }
94}
95\f
96/*
97 * Name: proclist()
98 *
99 * Abstract: Process linked list of groups.
100 *
101 * Returns: void
102 *
103 * Description: This function loops through the linked list of groups for one
104 * voice for one measure, first handling the grace groups, then
105 * doing a second loop for the nongrace groups. For each non-
106 * beamed note that needs it, it sets the stem length. For each
107 * beamed group, it calls setbeam to figure out the equation
108 * of the beam, and set the stem lengths accordingly. It also
109 * sets the relative vertical coords of the groups. These coords
110 * then get altered to include "with" lists and tuplet marks.
111 */
112
113static void
114proclist(mainll_p, vno)
115
116struct MAINLL *mainll_p; /* MLL struct for staff we're dealing with */
117int vno; /* voice we're to deal with, 0 to MAXVOICES-1 */
118
119{
120 struct GRPSYL *gs_p; /* point to first group in a linked list */
121 struct GRPSYL *ogs_p; /* point to first group in other linked list */
122 struct STAFF *staff_p; /* point to the staff it's connected to */
123 struct GRPSYL *savegs_p;/* save incoming gs_p */
124 struct GRPSYL *beamst_p;/* point at first group of a beamed set */
125 float notedist; /* distance between outer notes of a group */
126 float defsteps; /* additional default steps long to make stem*/
127 int bf; /* number of beams/flags */
128
129
130 debug(32, "proclist file=%s line=%d vno=%d", mainll_p->inputfile,
131 mainll_p->inputlineno, vno);
132 /*
133 * Set pointers to 1st group in our list and in the "other" list, as
134 * appropriate. Voices 1 and 2 (vno=0,1) refer to each other as the
135 * "other" voice. (If there is only one voice, ogs_p is set to voice 2
136 * (vno=1) which is a null pointer.) Voice 3 (vno=2) always ignores
137 * the other voices, so for it, ogs_p is a null pointer.
138 */
139 gs_p = mainll_p->u.staff_p->groups_p[ vno ];
140 ogs_p = vno == 2 ? (struct GRPSYL *)0 :
141 mainll_p->u.staff_p->groups_p[ ! vno ];
142
143 staff_p = mainll_p->u.staff_p; /* also point at staff */
144
145 /* set globals like Staffscale for use by the rest of the file */
146 set_staffscale(staff_p->staffno);
147
148 beamst_p = 0; /* prevent useless 'used before set' warnings */
149
150 /*
151 * Loop through every group, skipping rests, spaces, and nongrace
152 * groups, setting the stem length of grace groups.
153 */
154 for (savegs_p = gs_p; gs_p != 0; gs_p = gs_p->next) {
155 if (gs_p->grpcont != GC_NOTES)
156 continue;
157 if (gs_p->grpvalue == GV_NORMAL)
158 continue;
159
160 /*
161 * If we are at the start of a beamed set of groups, remember
162 * this place. Then, when we find the end of the set, call
163 * setbeam to figure out the equation of the beam and set the
164 * stem lengths.
165 */
166 if (gs_p->beamloc != NOITEM) {
167 if (gs_p->beamloc == STARTITEM)
168 beamst_p = gs_p;
169 if (gs_p->beamloc == ENDITEM)
170 setbeam(beamst_p, nextsimilar(gs_p), ogs_p);
171
172 continue;
173 }
174
175 /* if we get here, this group is not in a beamed set */
176
177 /* if not affected by CSS, do on normal pass, and only then */
178 /* if affected by CSS, do on CSS pass, and only then */
179 if (css_affects_stemtip(gs_p) != CSSpass) {
180 continue;
181 }
182
183 /*
184 * If the user specified a nonzero stem length, that's only the
185 * part of it that's not between the notes. So add the distance
186 * between the outer notes of the group. However, if they
187 * specified 0, they should get no stem.
188 */
189 if (IS_STEMLEN_KNOWN(gs_p->stemlen)) {
190 if (gs_p->stemlen != 0.0) {
191 gs_p->stemlen *= Staffscale;
192 notedist = gs_p->notelist[0].c[RY] - gs_p->
193 notelist[ gs_p->nnotes - 1 ].c[RY];
194 gs_p->stemlen += notedist;
195 }
196 continue;
197 }
198
199 /*
200 * Grace quarter notes default to just a note head and no stem.
201 * So set their stem length to 0.
202 */
203 if (gs_p->basictime == 4) {
204 gs_p->stemlen = 0;
205 continue;
206 }
207
208 /*
209 * If stemlen parm is zero, force length to zero. This will
210 * look bad for non-quarter notes, but that's what they
211 * asked for.
212 */
213 if (Defstemsteps == 0.0) {
214 gs_p->stemlen = 0.0;
215 continue;
216 }
217
218 /*
219 * Set the stems to the requested length, plus the distance
220 * between the highest and lowest note of the group, except
221 * longer for notes with more than 2 flags or beams. Unlike
222 * nongrace groups, stems need not reach the center line of
223 * the staff.
224 */
225 /* find distance between outer notes of the group */
226 notedist = gs_p->notelist[0].c[RY] -
227 gs_p->notelist[ gs_p->nnotes - 1 ].c[RY];
228
229 /* set len to default length + distance between outer notes */
230 gs_p->stemlen = (Defstemsteps * SM_STEMFACTOR) * Stepsize +
231 notedist;
232
233 bf = drmo(gs_p->basictime) - 2; /* no. of beams/flags */
234 if (bf > 2)
235 gs_p->stemlen += (bf - 2) * Smflagsep;
236 }
237
238 /*
239 * Loop through every grace group, skipping rests and spaces,
240 * setting the relative vertical coordinates.
241 */
242 setgroupvert(GV_ZERO, savegs_p, ogs_p);
243
244 /*
245 * Loop through every group, skipping rests, spaces and grace groups,
246 * setting the stem length of all nongrace groups.
247 *
248 * WARNING: The code in this loop is similar to stemroom() in
249 * setgrps.c. If you change one, you probably will need to change
250 * the other.
251 */
252 for (gs_p = savegs_p; gs_p != 0; gs_p = gs_p->next) {
253 if (gs_p->grpcont != GC_NOTES)
254 continue;
255 if (gs_p->grpvalue == GV_ZERO)
256 continue;
257 /*
258 * If this is cross staff beaming, don't do anything now. We
259 * can't do anything until the absolute vertical coords are set
260 * in absvert.c.
261 */
262 if (gs_p->beamto != CS_SAME) {
263 continue;
264 }
265
266 /*
267 * If we are at the start of a beamed set of groups, remember
268 * this place. Then, when we find the end of the set, call
269 * setbeam to figure out the equation of the beam and set the
270 * stem lengths.
271 */
272 if (gs_p->beamloc != NOITEM) {
273 if (gs_p->beamloc == STARTITEM)
274 beamst_p = gs_p;
275 if (gs_p->beamloc == ENDITEM)
276 setbeam(beamst_p, nextsimilar(gs_p), ogs_p);
277 continue;
278 }
279
280 /* if we get here, this group is not in a beamed set */
281
282 /* if not affected by CSS, do on normal pass, and only then */
283 /* if affected by CSS, do on CSS pass, and only then */
284 if (css_affects_stemtip(gs_p) != CSSpass) {
285 continue;
286 }
287
288 /*
289 * Only half notes and shorter have stems, but whole and double
290 * whole notes still need to have a pseudo stem length set if
291 * alternation beams are to be drawn between two neighboring
292 * groups, or the group has slashes.
293 */
294 if (gs_p->basictime <= 1 && gs_p->slash_alt == 0)
295 continue; /* no stem and no pseudo stem */
296
297 /*
298 * If the user specified a nonzero stem length, that's only the
299 * part of it that's not between the notes. So add the distance
300 * between the outer notes of the group. But if they specified
301 * 0, leave it as 0.
302 */
303 if (IS_STEMLEN_KNOWN(gs_p->stemlen)) {
304 if (gs_p->stemlen == 0.0)
305 continue;
306
307 gs_p->stemlen *= Staffscale;
308 notedist = gs_p->notelist[0].c[RY] -
309 gs_p->notelist[ gs_p->nnotes - 1 ].c[RY];
310 gs_p->stemlen += notedist;
311 continue;
312 }
313
314 /* if stemlen parm is zero, force length to zero */
315 if (Defstemsteps == 0.0) {
316 gs_p->stemlen = 0.0;
317 continue;
318 }
319
320 /*
321 * Set the stems initially to one octave long (or 5 stepsizes
322 * for cue notes), plus the distance between the highest and
323 * lowest note of the group, except longer for notes with more
324 * than 2 flags or beams. In any case, for normal sized notes,
325 * real stems must reach the center line of the staff in most
326 * cases.
327 */
328 /* find distance between outer notes of the group */
329 notedist = gs_p->notelist[0].c[RY] -
330 gs_p->notelist[ gs_p->nnotes - 1 ].c[RY];
331 /* set len to default length + distance between outer notes */
332 defsteps = Defstemsteps *
333 (allsmall(gs_p, gs_p) == YES ? SM_STEMFACTOR : 1.0);
334 gs_p->stemlen = defsteps * Stepsize + notedist;
335
336 /* add more, if needed, for flags/beams/slashes/alternations */
337 if (gs_p->basictime >= 8)
338 bf = drmo(gs_p->basictime) - 2; /* no. of beams/flags*/
339 else
340 bf = 0; /* none on quarter or longer */
341 bf += abs(gs_p->slash_alt); /* slashes or alternations */
342 if (gs_p->slash_alt > 0 && gs_p->basictime >= 16)
343 bf++; /* slashes need an extra one if 16, 32, ... */
344 if (bf > 2)
345 gs_p->stemlen += (bf - 2) * Flagsep;
346
347 /*
348 * If the note may have flag(s), stem up, and has dot(s), we
349 * must prevent the flag(s) from hitting the dot(s), by
350 * lengthening the stem.
351 */
352 if (gs_p->basictime >= 8 && gs_p->stemdir == UP &&
353 gs_p->dots != 0) {
354 if (gs_p->notelist[0].stepsup % 2 == 0) {
355 /* note is on a line */
356 if (gs_p->basictime == 8)
357 gs_p->stemlen += Stepsize;
358 else
359 gs_p->stemlen += 2 * Stepsize;
360 } else {
361 /* note is on a space */
362 if (gs_p->basictime > 8)
363 gs_p->stemlen += Stepsize;
364 }
365 }
366
367 /*
368 * Real (printed) stems must reach the center line for normal
369 * groups, though they need not for cue groups or voice 3 or
370 * when the stem direction has been forced the "wrong way" or
371 * when all the notes are on another staff.
372 */
373 if (gs_p->basictime >= 2 && gs_p->grpsize == GS_NORMAL &&
374 vno != 2 && stemforced(gs_p, ogs_p) == NO &&
375 NNN(gs_p) > 0) {
376
377 if (gs_p->stemdir == UP && gs_p->notelist[ gs_p->nnotes
378 - 1 ].c[RY] < -(gs_p->stemlen)) {
379 gs_p->stemlen = -gs_p->notelist[ gs_p->nnotes-1
380 ].c[RY];
381 }
382
383 if (gs_p->stemdir == DOWN && gs_p->notelist[ 0 ].c[RY]
384 > gs_p->stemlen) {
385 gs_p->stemlen = gs_p->notelist[ 0 ].c[RY];
386 }
387 }
388 }
389
390 /*
391 * Loop through every nongrace group, skipping rests and spaces,
392 * setting the relative vertical coordinates.
393 */
394 setgroupvert(GV_NORMAL, savegs_p, ogs_p);
395
396 /*
397 * Loop through every group, looking for tuplets. When encountering
398 * the first item in a tuplet, call a subroutine to figure out where
399 * the bracket should go, and based on that alter the RN or RS of
400 * the groups in the tuplet. However, if this is a tuplet whose
401 * number and bracket are not to be printed, don't call the subrountine.
402 * Also, it should not be done when there is cross staff beaming. Mup
403 * does not automatically print tuplet numbers or brackets in CSB sets.
404 */
405 for (gs_p = savegs_p; gs_p != 0; gs_p = gs_p->next) {
406 if ((gs_p->tuploc == STARTITEM || gs_p->tuploc == LONEITEM) &&
407 gs_p->beamto == CS_SAME && gs_p->printtup != PT_NEITHER)
408 settuplet(gs_p, staff_p);
409 }
410}
411\f
412/*
413 * Name: proctablist()
414 *
415 * Abstract: Process linked list of groups on a tablature staff.
416 *
417 * Returns: void
418 *
419 * Description: This function loops through the linked list of groups for one
420 * measure of a tablature staff. It sets the relative vertical
421 * coords of the groups. These coords then get altered to include
422 * "with" lists and tuplet marks.
423 */
424
425static void
426proctablist(mainll_p, vno)
427
428struct MAINLL *mainll_p; /* MLL struct for staff we're dealing with */
429int vno; /* voice we're to deal with, 0 to MAXVOICES-1 */
430
431{
432 struct GRPSYL *gs_p; /* point to first group in a linked list */
433 struct GRPSYL *ogs_p; /* point to first group in other linked list */
434 int stepdiff; /* steps between highest & lowest of a group */
435 int defsteps; /* additional default steps long to make stem*/
436 int bf; /* number of beams/flags (really slashes) */
437
438
439 debug(32, "proctablist file=%s line=%d", mainll_p->inputfile,
440 mainll_p->inputlineno);
441 /* no such thing as cross staff stemming for tab */
442 if (CSSpass == YES) {
443 return;
444 }
445
446 /*
447 * Set pointers to 1st group in our list and in the "other" list, as
448 * appropriate. Voices 1 and 2 (vno=0,1) refer to each other as the
449 * "other" voice. (If there is only one voice, ogs_p is set to voice 2
450 * (vno=1) which is a null pointer.) Voice 3 (vno=2) always ignores
451 * the other voices, so for it, ogs_p is a null pointer.
452 */
453 gs_p = mainll_p->u.staff_p->groups_p[ vno ];
454 ogs_p = vno == 2 ? (struct GRPSYL *)0 :
455 mainll_p->u.staff_p->groups_p[ ! vno ];
456
457 /*
458 * Loop through every group, setting some group vertical coordinates.
459 */
460 for ( ; gs_p != 0; gs_p = gs_p->next) {
461 /*
462 * Just as for nontablature groups, RY is always 0, the center
463 * of the staff, even if it falls outside the group's
464 * rectangle. RN and RS were set in locllnotes() and
465 * intertab() in setnotes.c.
466 */
467 gs_p->c[RY] = 0;
468
469 /*
470 * Slashes and "with" lists are allowed only if there are
471 * frets, so if there aren't any frets, skip the rest.
472 */
473 if (gs_p->grpcont != GC_NOTES || gs_p->nnotes == 0)
474 continue;
475
476 /*
477 * No tab groups have stems, but we still need to set a pseudo
478 * stem length if the group has slashes and otherwise 0.
479 */
480 if (gs_p->slash_alt == 0) {
481 gs_p->stemlen = 0; /* no slashes */
482 } else {
483 /* find distance between outer frets of the group */
484 stepdiff = gs_p->notelist[0].stepsup -
485 gs_p->notelist[ gs_p->nnotes - 1 ].stepsup;
486
487 /* default length + distance between outer notes */
488 defsteps = Defstemsteps * (allsmall(gs_p, gs_p) == YES
489 ? SM_STEMFACTOR : 1.0);
490 gs_p->stemlen = stepdiff * Stepsize * TABRATIO +
491 defsteps * Stepsize;
492
493 bf = abs(gs_p->slash_alt); /* slashes */
494 if (gs_p->basictime >= 16)
495 bf++; /* slashes need extra 1 if 16, 32, ...*/
496 if (bf > 2)
497 gs_p->stemlen += (bf - 2) * Flagsep;
498
499 if (gs_p->stemdir == UP) {
500 gs_p->c[RN] = gs_p->notelist[gs_p->nnotes - 1]
501 .c[RN] + gs_p->stemlen;
502 } else {
503 gs_p->c[RS] = gs_p->notelist[0]
504 .c[RY] - gs_p->stemlen;
505 }
506 }
507
508 /* decrease RS based on "with" lists */
509 expgroup(gs_p, ogs_p);
510 }
511}
512\f
513/*
514 * Name: stemforced()
515 *
516 * Abstract: Did the user force stem(s) to go the wrong way?
517 *
518 * Returns: YES at least one group was forced
519 * NO no groups were forced
520 *
521 * Description: This function figures out whether the user forced *gs_p's stem
522 * to go DOWN for voice 1 or UP for voice 2 when the vscheme and
523 * the other voice would normally prevent it; or if *gs_p is at
524 * the start of a beamed set, it checks this for all groups in
525 * the set.
526 */
527
528static int
529stemforced(gs_p, ogs_p)
530
531struct GRPSYL *gs_p; /* the group we are asking about */
532struct GRPSYL *ogs_p; /* first group in other voice's linked list */
533
534{
535 RATIONAL starttime; /* of the group in question */
536 RATIONAL endtime; /* of the group in question */
537 struct GRPSYL *gs2_p; /* loop through groups */
538
539
540 /* voice 3 never cares, so is never considered to be forced */
541 if (gs_p->vno == 3) {
542 return (NO);
543 }
544
545 /* grace cannot be forced */
546 if (gs_p->grpvalue == GV_ZERO) {
547 return (NO);
548 }
549
550 switch (svpath(gs_p->staffno, VSCHEME)->vscheme) {
551 case V_1:
552 return (NO); /* no forcing is needed in this vscheme */
553 case V_2OPSTEM:
554 case V_3OPSTEM:
555 /*
556 * If and only if a stem is backwards, we are forced. Note
557 * that even for the beamed case, we only have to check one
558 * group, since all stems in the set go the same direction.
559 */
560 if (gs_p->vno == 1 && gs_p->stemdir == DOWN ||
561 gs_p->vno == 2 && gs_p->stemdir == UP) {
562 return (YES);
563 }
564 return (NO);
565 }
566
567 /*
568 * We are in one of the freestem vschemes.
569 */
570
571 /* if the other voice doesn't exist, we know we were not forced */
572 if (ogs_p == 0) {
573 return (NO); /* other voice does not exist */
574 }
575
576 /* if all stems are normal, we are not forced (only need to check 1) */
577 if (gs_p->vno == 1 && gs_p->stemdir == UP ||
578 gs_p->vno == 2 && gs_p->stemdir == DOWN) {
579 return (NO);
580 }
581
582 /* check if the other voice is all spaces during this time */
583
584 /* find start time of *gs_p by summing all previous groups */
585 starttime = Zero;
586 for (gs2_p = gs_p->prev; gs2_p != 0; gs2_p = gs2_p->prev) {
587 starttime = radd(starttime, gs2_p->fulltime);
588 }
589
590 /* find end time of *gs_p (or the whole beamed set) */
591 endtime = starttime;
592 for (gs2_p = gs_p; gs2_p != 0; gs2_p = gs2_p->next) {
593 endtime = radd(endtime, gs2_p->fulltime);
594 if (gs2_p->beamloc == NOITEM || gs2_p->beamloc == ENDITEM &&
595 gs_p->grpvalue != GV_ZERO) {
596 break;
597 }
598 }
599
600 if (hasspace(ogs_p, starttime, endtime) == YES) {
601 return (NO); /* all spaces, forcing was not needed */
602 } else {
603 return (YES); /* notes/rests, we were forced */
604 }
605}
606\f
607/*
608 * Name: setbeam()
609 *
610 * Abstract: Set stem lengths for a beamed set of groups.
611 *
612 * Returns: void
613 *
614 * Description: This function uses linear regression to figure out where the
615 * best place to put the beam is, for a beamed set of groups, or
616 * two groups that are alted together. (Although there are
617 * special cases where the beam needs to be forced horizontal
618 * instead of using linear regression.) But if the user specified
619 * the stem lengths of the first and last group, it just goes with
620 * that, instead of using linear regression. It then sets the
621 * stem lengths for all the groups in the set.
622 *
623 * Groups involved in cross staff beaming should never call here.
624 * That work must be done later in absvert.c.
625 */
626
627static void
628setbeam(start_p, end_p, ogs_p)
629
630struct GRPSYL *start_p; /* first in beamed set */
631struct GRPSYL *end_p; /* after last in beamed set */
632struct GRPSYL *ogs_p; /* first group in other voice's GRPSYL list */
633
634{
635 struct GRPSYL *gs_p; /* loop through the groups in the beamed set */
636 struct GRPSYL *last_p; /* point at last valid group before end_p */
637 float sx, sy; /* sum of x and y coords of notes */
638 float xbar, ybar; /* average x and y coords of notes */
639 float top, bottom; /* numerator & denominator for finding b1 */
640 float temp; /* scratch variable */
641 float startx, endx; /* x coord of first and last note */
642 float starty, endy; /* y coord of first and last note */
643 float b0, b1; /* y intercept and slope */
644 float maxb0, minb0; /* max and min y intercepts */
645 float stemshift; /* x distance of stem from center of note */
646 float deflen; /* default len of a stem, based on basictime */
647 float shortdist; /* amount of stem shortening allowed (inches)*/
648 float x; /* x coord of a stem */
649 int css_affects_beam; /* does CSS affect the position of the beam? */
650 int all_notes_other_staff; /* all notes in all groups on other staff */
651 int one_end_forced; /* is stem len forced on one end only? */
652 int slope_forced; /* is the slope of the beam forced? */
653 float forced_slope; /* slope that the user forced */
654 int bf; /* number of beams/flags */
655 int shortest; /* basictime of shortest note in group */
656 int num; /* number of notes */
657 short *steps; /* stepsup of beamside notes */
658 int patlen; /* length of a pattern of notes */
659 int match; /* does the pattern match? */
660 int k; /* loop variable */
661 int n; /* loop variable */
662
663
664 /*
665 * Find whether CSS affects the position of the beam, and whether all
666 * groups have all their notes on the other staff. css_affects_stemtip
667 * asks (for this beamed case) whether any group's other-staff notes
668 * are stemside; that is, whether the stem points to the other staff,
669 * because then obviously the coord of the stem tip depends on where
670 * those notes are. If all of this group's notes are on the other
671 * staff, you might expect that we would have to regard the stem tip as
672 * affected even if the stem is towards the normal staff. But we
673 * prefer to pretend they aren't, so that we can handle more beamed
674 * sets on the first pass. We fake out those groups (see the comment a
675 * little later). And yet, if all the groups are this way, we do
676 * regard the beam as affected, because then we aren't going to enforce
677 * the rule about stems reaching the middle staff line.
678 */
679 /* first set normal (non-CSS) values */
680 css_affects_beam = NO;
681 all_notes_other_staff = NO;
682 if (CSSused == YES) { /* don't waste time looking if CSS not used */
683 all_notes_other_staff = YES;
684 css_affects_beam = css_affects_stemtip(start_p);
685 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
686 if (NNN(gs_p) != 0) {
687 all_notes_other_staff = NO;
688 }
689 }
690 if (all_notes_other_staff == YES) {
691 css_affects_beam = YES;
692 }
693 }
694
695 /*
696 * If the beam is not affected by CSS, handle this beamed set on the
697 * first pass only. If it is affected, handle it on the second
698 * pass only.
699 */
700 if (css_affects_beam != CSSpass) {
701 return;
702 }
703
704 /*
705 * If the beam is "not affected by CSS", there could still be groups
706 * where all the notes are CSS. We fake them out here, setting the
707 * BNOTE's RY an octave from the center line. We need some plausible
708 * value there for finding the beam position. AY hasn't been used yet,
709 * so use it as a holding area. We need to restore RY before returning
710 * from this function.
711 */
712 if (CSSused == YES && CSSpass == NO) {
713 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
714 if (NNN(gs_p) == 0) {
715 BNOTE(gs_p).c[AY] = BNOTE(gs_p).c[RY];
716 BNOTE(gs_p).c[RY] = 7 * Stepsize *
717 ((gs_p->stemdir == UP) ? -1.0 : 1.0);
718 }
719 }
720 }
721
722 last_p = 0; /* prevent useless 'used before set' warnings */
723
724 /* find the last valid group */
725 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
726 last_p = gs_p;
727 }
728
729 /*
730 * If the user specified the stem length on one end (first or last) but
731 * not the other, remember that fact. In that case we will execute the
732 * normal (both ends unforced) algorithm, but then at the last minute
733 * force the end that was given.
734 */
735 one_end_forced = IS_STEMLEN_KNOWN(start_p->stemlen) !=
736 IS_STEMLEN_KNOWN(last_p->stemlen);
737
738 /*
739 * If the user specified the stem length for the first and last groups,
740 * simply use these values to define where the beam is, and set all the
741 * stem lengths.
742 */
743 if (IS_STEMLEN_KNOWN(start_p->stemlen) &&
744 IS_STEMLEN_KNOWN(last_p->stemlen)) {
745
746 /*
747 * If the first and last groups had stemlen set to zero, force
748 * all groups to have stemlen zero, and return. No beam will
749 * be drawn.
750 */
751 if (start_p->stemlen == 0.0 && last_p->stemlen == 0.0) {
752 for (gs_p = start_p; gs_p != end_p;
753 gs_p = nextsimilar(gs_p)) {
754 gs_p->stemlen = 0.0;
755 }
756 restore_ry(start_p, end_p);
757 return;
758 }
759
760 /* they weren't both zero, so continue on finding the beam */
761 start_p->stemlen *= Staffscale;
762 stemshift = getstemshift(start_p);
763 if (start_p->stemdir == DOWN)
764 stemshift = -stemshift;
765 last_p->stemlen *= Staffscale;
766
767 /* find coords of the ends of the stems on the outer groups */
768 startx = start_p->c[AX] + stemshift;
769 endx = last_p->c[AX] + stemshift;
770 starty = BNOTE(start_p).c[RY] + start_p->stemlen *
771 (start_p->stemdir == UP ? 1.0 : -1.0);
772 endy = BNOTE(last_p).c[RY] + last_p->stemlen *
773 (last_p->stemdir == UP ? 1.0 : -1.0);
774
775 /* find slope and y intercept of line through those points */
776 b1 = (starty - endy) / (startx - endx);
777 b0 = starty - b1 * startx;
778
779 /* loop through all groups, setting stem length */
780 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
781 x = gs_p->c[AX] + stemshift; /* X coord of stem */
782
783 /* first set stemlen to beam's Y coord minus note's */
784 gs_p->stemlen = (b0 + b1 * x) - BNOTE(gs_p).c[RY];
785
786 /* if stems are down, reverse it */
787 if (gs_p->stemdir == DOWN)
788 gs_p->stemlen = -(gs_p->stemlen);
789
790 finalstemadjust(gs_p);
791 }
792
793 /* set relative vertical coords of any embedded rests */
794 embedrest(start_p, last_p, b1, b0);
795
796 restore_ry(start_p, end_p);
797 return;
798 }
799
800 /*
801 * If the user forced the beam's angle to some value, find what that is
802 * in terms of slope. Later we will force this value to be used. The
803 * 0.001 is to allow for floating point roundoff error.
804 */
805 if (fabs(start_p->beamslope - NOBEAMANGLE) < 0.001) {
806 slope_forced = NO;
807 forced_slope = 0.0; /* not used, keep lint happy */
808 } else {
809 slope_forced = YES;
810 forced_slope = tan(start_p->beamslope * PI / 180.0);
811 }
812
813 /*
814 * When both end groups have stemlen zero, we set all groups' stemlens
815 * to zero, and no beam will be drawn. Above we handled the case
816 * where the user forced both ends to zero. Here we handle the case
817 * where the ends are defaulting to zero, or one end is defaulting to
818 * zero and the user forced the other one. But don't do this if the
819 * slope is forced.
820 */
821 if (Defstemsteps == 0.0 && ! slope_forced && ( ! one_end_forced ||
822 start_p->stemlen == 0.0 || last_p->stemlen == 0.0)) {
823 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
824 gs_p->stemlen = 0.0;
825 }
826 restore_ry(start_p, end_p);
827 return;
828 }
829
830 /*
831 * Use linear regression to find the best-fit line through the centers
832 * of the notes. In this function, we will always be concerned with
833 * the X coord of the group as a whole (disregarding any notes that are
834 * on the "wrong" side of the stem) but the Y coord of the note of the
835 * group that's nearest to the beam (thus the BNOTE macro). The X
836 * coords used are absolute, but the Y coords are relative to the
837 * center line of the staff, since we don't know the absolute Y coords
838 * yet, and it wouldn't affect the result anyway.
839 *
840 * First get sum of x and y coords, to find averages.
841 */
842 sx = sy = 0;
843 num = 0;
844 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
845 sx += gs_p->c[AX];
846 sy += BNOTE(gs_p).c[RY];
847 num++; /* count number of notes */
848 }
849
850 xbar = sx / num;
851 ybar = sy / num;
852
853 /* accumulate numerator & denominator of regression formula for b1 */
854 top = bottom = 0;
855 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
856 temp = gs_p->c[AX] - xbar;
857 top += temp * (BNOTE(gs_p).c[RY] - ybar);
858 bottom += temp * temp;
859 }
860
861 b1 = top / bottom; /* slope */
862 /*
863 * We could also figure:
864 * b0 = ybar - b1 * xbar; y intercept
865 * to get the equation of the regression line: y = b0 + b1 * x
866 * but we're going to change b0 later anyway. Now, there are certain
867 * cases where we want to override the slope determined by regression,
868 * so revise b1 if that is the case.
869 */
870
871 /* if first and last notes are equal, force horizontal */
872 if (BNOTE(start_p).stepsup == BNOTE(last_p).stepsup)
873 b1 = 0.0;
874
875 /* check for more reasons to force the beam horizontal */
876 if (b1 != 0.0 && num >= 3) {
877 /* get an array of each group's beamside note's stepsup */
878 MALLOCA(short, steps, num);
879 for (n = 0, gs_p = start_p; n < num;
880 n++, gs_p = nextsimilar(gs_p)) {
881 steps[n] = BNOTE(gs_p).stepsup;
882 }
883
884 /*
885 * Check for a repeating pattern of notes. Try every possible
886 * pattern length <= half as long as set. If found, force the
887 * beam horizontal.
888 */
889 for (patlen = num / 2; patlen >= 2; patlen--) {
890 /* must be an integer number of pattern repetitions */
891 if (num % patlen != 0) {
892 continue; /* groups were left over */
893 }
894 /* see if initial pattern repeats perfectly */
895 match = YES;
896 for (n = 0; n < patlen && match == YES; n++) {
897 for (k = n + patlen; k < num; k += patlen) {
898 if (steps[k] != steps[n]) {
899 match = NO;
900 break;
901 }
902 }
903 }
904 /* if all repeats matched, force horizontal & break */
905 if (match == YES) {
906 b1 = 0.0;
907 break;
908 }
909 }
910
911 /*
912 * If still not horizontal, check for the case where all the
913 * beamside notes are the same except for just the first, or
914 * just the last, being different and in the direction
915 * opposite the stemdir. If so, force horizontal.
916 */
917 if (b1 != 0.0) {
918 /* make sure all the inner groups are the same */
919 match = YES;
920 for (n = 2; n < num - 1; n++) {
921 if (steps[n] != steps[1]) {
922 match = NO;
923 break;
924 }
925 }
926 /* if inner groups same, check the other conditions */
927 if (match == YES) {
928 if (start_p->stemdir == DOWN) {
929 if ((steps[0] > steps[1] &&
930 steps[num-1] == steps[1]) ||
931 (steps[0] == steps[1] &&
932 steps[num-1] > steps[1])) {
933 b1 = 0.0;
934 }
935 } else { /* UP */
936 if ((steps[0] < steps[1] &&
937 steps[num-1] == steps[1]) ||
938 (steps[0] == steps[1] &&
939 steps[num-1] < steps[1])) {
940 b1 = 0.0;
941 }
942 }
943 }
944 }
945 FREE(steps);
946 }
947
948 /*
949 * Find half the width of a note head; the stems will need to be
950 * shifted by that amount from the center of the notes so that they
951 * will meet the edge of the notes properly. If the stems are up,
952 * they will be on the right side of (normal) notes, else left. Set
953 * the X positions for the first and last stems. (If these are alted
954 * groups, the noteheadchar may not be 4; but this is close enough.)
955 */
956 stemshift = getstemshift(start_p);
957 if (start_p->stemdir == DOWN)
958 stemshift = -stemshift;
959 startx = start_p->c[AX] + stemshift; /* first group's stem */
960 endx = last_p->c[AX] + stemshift; /* last group's stem */
961
962 /*
963 * The original slope derived by linear regression must be adjusted in
964 * certain ways. First, override it if the user wants that; otherwise
965 * adjust according to the beamslope parameter.
966 */
967 if (slope_forced) {
968 b1 = forced_slope;
969 } else {
970 b1 = adjslope(start_p, b1, NO);
971 }
972
973 /*
974 * Calculate a new y intercept (b0). First pass parallel lines
975 * through each note, and record the maximum and minimum y intercepts
976 * that result.
977 */
978 b0 = BNOTE(start_p).c[RY] - b1 * start_p->c[AX];
979 maxb0 = minb0 = b0; /* init to value for first note */
980 /* look at rest of them */
981 for (gs_p = nextsimilar(start_p); gs_p != end_p;
982 gs_p = nextsimilar(gs_p)) {
983 b0 = BNOTE(gs_p).c[RY] - b1 * gs_p->c[AX];
984 if (b0 > maxb0)
985 maxb0 = b0;
986 else if (b0 < minb0)
987 minb0 = b0;
988 }
989
990 /*
991 * Find the basictime of the shortest note in the group, considering
992 * also any slashes or alternations on it. (Except that slash has a
993 * different meaning on grace groups, and doesn't affect their stem
994 * length.) Then set the default stem length based on that.
995 */
996 shortest = 0;
997 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
998 if (gs_p->basictime >= 8)
999 bf = drmo(gs_p->basictime) - 2; /* no. of beams/flags*/
1000 else
1001 bf = 0; /* none on quarter or longer */
1002 if (gs_p->grpvalue == GV_NORMAL)
1003 bf += abs(gs_p->slash_alt);/* slashes or alternations */
1004 /*
1005 * In certain cases where there are accidentals, we need to
1006 * artificially increase bf to keep the beams from overlapping
1007 * with the accidental.
1008 */
1009 if (gs_p != start_p && gs_p->stemdir == UP &&
1010 gs_p->notelist[0].accidental != '\0' &&
1011 gs_p->notelist[0].accidental != 'x' &&
1012 b1 > 0 && bf > 1) {
1013 bf += 3.5 * b1 * (Stepsize / Flagsep) * ((bf > 1) +
1014 (gs_p->notelist[0].accidental == 'B'));
1015 }
1016 if (bf > shortest)
1017 shortest = bf;
1018 }
1019 if (allsmall(start_p, last_p) == NO) {
1020 /* at least one group has a normal size note */
1021 deflen = Defstemsteps * Stepsize;
1022 if (shortest > 2)
1023 deflen += (shortest - 2) * Flagsep;
1024 } else {
1025 /* all groups have all small notes */
1026 deflen = Defstemsteps * SM_STEMFACTOR * Stepsize;
1027 if (shortest > 2)
1028 deflen += (shortest - 2) * 4.0 * POINT * Staffscale;
1029 }
1030
1031 /*
1032 * The outer edge of the beam should be deflen steps away from the
1033 * average position of the notes, as defined by the linear regression
1034 * line. But don't allow any note to be closer than a certain number
1035 * of steps less than that, the number as given by the stemshorten parm.
1036 */
1037 shortdist = vvpath(start_p->staffno, start_p->vno, STEMSHORTEN)
1038 ->stemshorten * Stepsize;
1039 if (start_p->stemdir == UP) {
1040 if (maxb0 - minb0 > shortdist)
1041 b0 = maxb0 + deflen - shortdist;
1042 else
1043 b0 += deflen;
1044 } else { /* DOWN */
1045 if (maxb0 - minb0 > shortdist)
1046 b0 = minb0 - deflen + shortdist;
1047 else
1048 b0 -= deflen;
1049 }
1050
1051 /*
1052 * Another adjustment may be needed so that all stems will reach the
1053 * center line of the staff. (Not to be done for small groups, or when
1054 * all notes in all groups are on the other staff [CSS], or when
1055 * some stemdirs have been forced wrong way despite the other voice, or
1056 * we have alternations and no normal beams, or for voice 3.)
1057 */
1058 starty = b0 + b1 * startx; /* y coord near left end of beam */
1059 endy = b0 + b1 * endx; /* y coord near right end of beam */
1060 if (start_p->basictime >= 2 && start_p->grpsize == GS_NORMAL &&
1061 stemforced(start_p, ogs_p) == NO &&
1062 start_p->vno != 3 && all_notes_other_staff == NO) {
1063 if (slope_forced) {
1064 /* move both ends the same amount to preserve slope */
1065 if (start_p->stemdir == UP) {
1066 if (starty < 0) {
1067 endy -= starty;
1068 starty = 0;
1069 }
1070 if (endy < 0) {
1071 starty -= endy;
1072 endy = 0;
1073 }
1074 } else { /* DOWN */
1075 if (starty > 0) {
1076 endy -= starty;
1077 starty = 0;
1078 }
1079 if (endy > 0) {
1080 starty -= endy;
1081 endy = 0;
1082 }
1083 }
1084 } else {
1085 /* move just the end(s) that need to be moved */
1086 if (start_p->stemdir == UP) {
1087 if (starty < 0)
1088 starty = 0;
1089 if (endy < 0)
1090 endy = 0;
1091 } else { /* DOWN */
1092 if (starty > 0)
1093 starty = 0;
1094 if (endy > 0)
1095 endy = 0;
1096 }
1097 }
1098 }
1099
1100 /*
1101 * If the first and last groups's stems now end at the center line, and
1102 * the beam slope used to be nonzero, force one end to be a step beyond
1103 * the center line, so that the beam will still have some slope to it.
1104 * But don't do this if the user is forcing the beam's slope.
1105 */
1106 if ( ! slope_forced && fabs(starty) < Stdpad &&
1107 fabs(endy) < Stdpad && b1 != 0.0) {
1108 if (start_p->stemdir == UP) {
1109 if (b1 > 0.0) {
1110 endy = Stepsize;
1111 } else if (b1 < 0.0) {
1112 starty = Stepsize;
1113 }
1114 } else { /* DOWN */
1115 if (b1 > 0.0) {
1116 starty = -Stepsize;
1117 } else if (b1 < 0.0) {
1118 endy = -Stepsize;
1119 }
1120 }
1121 }
1122
1123 /*
1124 * If y at the ends of the beam differs by less than a step (allowing a
1125 * fudge factor for roundoff error), force the beam horizontal by
1126 * setting one end farther away from the notes. But don't do it if the
1127 * user is forcing a particular slope.
1128 */
1129 if ( ! slope_forced && fabs(starty - endy) < Stepsize - 0.001) {
1130 if (start_p->stemdir == UP) {
1131 if (starty > endy) {
1132 endy = starty;
1133 } else {
1134 starty = endy;
1135 }
1136 } else { /* DOWN */
1137 if (starty < endy) {
1138 endy = starty;
1139 } else {
1140 starty = endy;
1141 }
1142 }
1143 }
1144
1145 /* recalculate slope and y intercept from (possibly) new endpoints */
1146 b1 = (endy - starty) / (endx - startx); /* slope */
1147 b0 = starty - b1 * startx; /* y intercept */
1148 temp = b0; /* remember this value for later */
1149
1150 /* do some additional work for nongrace groups */
1151 if (start_p->grpvalue == GV_NORMAL) {
1152 /*
1153 * If this is not an alted pair, there may be embedded grace
1154 * notes, and we may need to lengthen our stems to avoid them.
1155 */
1156 if (start_p->slash_alt >= 0)
1157 b0 = embedgrace(start_p, b1, b0);
1158
1159 /* may need to lengthen stems to avoid embedded clefs */
1160 b0 = embedclef(start_p, b1, b0);
1161
1162 /* set relative vertical coords of any embedded rests */
1163 embedrest(start_p, last_p, b1, b0);
1164
1165 /*
1166 * If there is another voice, we might need to lengthen our
1167 * stems so their notes won't run into our beam. If we had
1168 * embedded rests, they would also be moved.
1169 */
1170 b0 = avoidothervoice(start_p, last_p, b1, b0, ogs_p);
1171
1172 /* update these by the amount the y intercept changed */
1173 starty += temp - b0;
1174 endy += temp - b0;
1175 }
1176
1177 restore_ry(start_p, end_p);
1178
1179 /*
1180 * If one end's stem len was forced but not the other, now is the time
1181 * to apply that forcing. So in effect, we have taken the beam as
1182 * determined by the normal algorithm and now we change the vertical
1183 * coord of this end. If the slope was also forced, move the other
1184 * end by the same amount so that the slope won't change.
1185 */
1186 if (one_end_forced) {
1187 if (IS_STEMLEN_KNOWN(start_p->stemlen)) {
1188 start_p->stemlen *= Staffscale;
1189 temp = starty;
1190 starty = BNOTE(start_p).c[RY] + start_p->stemlen *
1191 (start_p->stemdir == UP ? 1.0 : -1.0);
1192 if (slope_forced) {
1193 endy += starty - temp;
1194 }
1195 } else {
1196 last_p->stemlen *= Staffscale;
1197 temp = endy;
1198 endy = BNOTE(last_p).c[RY] + last_p->stemlen *
1199 (last_p->stemdir == UP ? 1.0 : -1.0);
1200 if (slope_forced) {
1201 starty += endy - temp;
1202 }
1203 }
1204
1205 /* recalculate */
1206 b1 = (endy - starty) / (endx - startx); /* slope */
1207 b0 = starty - b1 * startx; /* y intercept */
1208
1209 /*
1210 * Re-do embedded rests now that things have moved. As for the
1211 * other adjustments above, we can't re-do them because they
1212 * may force stem lengths to change. If things collide, too
1213 * bad, the user forced the one stem length. It might be
1214 * possible to avoid the collision by moving the other end,
1215 * but likely not, and it's too late now anyhow.
1216 */
1217 embedrest(start_p, last_p, b1, b0);
1218 }
1219
1220 /*
1221 * At this point we know where to put the main beam (the one needed for
1222 * eighth notes). Figure out and set the correct stem lengths for all
1223 * of these beamed groups.
1224 */
1225 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
1226 x = gs_p->c[AX] + stemshift; /* X coord of stem */
1227
1228 /* first set stemlen to beam's Y coord minus note's */
1229 gs_p->stemlen = (b0 + b1 * x) - BNOTE(gs_p).c[RY];
1230
1231 /* if stems down, reverse stemlen, should make it positive */
1232 if (gs_p->stemdir == DOWN) {
1233 gs_p->stemlen = -(gs_p->stemlen);
1234 }
1235 /* but if negative length, error */
1236 if (gs_p->stemlen < 0) {
1237 l_ufatal(gs_p->inputfile, gs_p->inputlineno,
1238 "stem length was forced negative");
1239 }
1240
1241 finalstemadjust(gs_p);
1242 }
1243}
1244\f
1245/*
1246 * Name: restore_ry()
1247 *
1248 * Abstract: Restore RY coordinates if need be.
1249 *
1250 * Returns: void
1251 *
1252 * Description: This function undoes what the code near the start of setbeam()
1253 * did. But it doesn't have to set AY back, because it is garbage
1254 * and will be overwritten later anyway.
1255 */
1256
1257static void
1258restore_ry(start_p, end_p)
1259
1260struct GRPSYL *start_p; /* first in beamed set */
1261struct GRPSYL *end_p; /* after last in beamed set */
1262
1263{
1264 struct GRPSYL *gs_p; /* loop through the groups in the beamed set */
1265
1266
1267 if (CSSused == YES && CSSpass == NO) {
1268 for (gs_p = start_p; gs_p != end_p; gs_p = nextsimilar(gs_p)) {
1269 if (NNN(gs_p) == 0) {
1270 BNOTE(gs_p).c[RY] = BNOTE(gs_p).c[AY];
1271 }
1272 }
1273 }
1274}
1275\f
1276/*
1277 * Name: embedgrace()
1278 *
1279 * Abstract: Change the Y intercept if necessary for embedded grace groups.
1280 *
1281 * Returns: new y intercept value (may be no change)
1282 *
1283 * Description: When grace groups are embedded inside a set of nongrace groups,
1284 * the beam(s) for the nongrace may have to be put farther away
1285 * from their note heads, so that these beams won't collide with
1286 * the grace groups. This function returns the new Y intercept
1287 * for the equation of the nongraces' main beam, which accom-
1288 * plishes this. When there aren't any embedded grace groups,
1289 * or they are in certain positions, this Y intercept will be the
1290 * same as the old Y intercept.
1291 */
1292
1293static double
1294embedgrace(start_p, b1, b0)
1295
1296struct GRPSYL *start_p; /* first group in nongrace beamed set */
1297double b1; /* slope */
1298double b0; /* y intercept */
1299
1300{
1301 struct GRPSYL *gs_p; /* point to grace group being looked at */
1302 struct GRPSYL *prev_p; /* point to nongrace group preceding gs_p */
1303 struct GRPSYL *next_p; /* point to nongrace group following gs_p */
1304 float beamthick; /* total thickness of beams and space between*/
1305 float ycross; /* where grace stem would hit nongrace beam */
1306
1307
1308 /*
1309 * Loop through all the grace groups that are embedded somewhere
1310 * between the first and last groups of this nongrace beamed set.
1311 * If their stems point the opposite way, there is no problem. But
1312 * if not, we may need to move the main beam(s) out of the way.
1313 */
1314 for (gs_p = start_p; gs_p->grpvalue == GV_ZERO ||
1315 gs_p->beamloc != ENDITEM; gs_p = gs_p->next) {
1316 if (gs_p->grpvalue == GV_NORMAL)
1317 continue; /* ignore nongrace groups */
1318
1319 /*
1320 * Find the preceding and following nongrace group. Whichever
1321 * has the least (slowest) basictime, that determines how many
1322 * full beams will connect those two groups. (You take log2 of
1323 * it and subtract 2.)
1324 */
1325 prev_p = prevnongrace(gs_p);
1326 next_p = nextnongrace(gs_p);
1327
1328 /* thickness of relevant beams at right side of grace */
1329 beamthick = beamoff(next_p, PB_LEFT, gs_p->c[AE], start_p);
1330
1331 /*
1332 * Find the AX and RY coords of the end of the grace group
1333 * stem that is nearest the nongrace beam(s). Then, if this
1334 * point would run into or beyond the nongrace beam(s), change
1335 * the Y intercept (b0) so that it won't.
1336 */
1337 ycross = b1 * gs_p->c[AE] + b0;
1338 if (start_p->stemdir == UP) {
1339 if (ycross - beamthick < gs_p->c[RN])
1340 b0 += gs_p->c[RN] - (ycross - beamthick);
1341 } else { /* stemdir == DOWN */
1342 if (ycross + beamthick > gs_p->c[RS])
1343 b0 -= (ycross + beamthick) - gs_p->c[RS];
1344 }
1345
1346 /* thickness of relevant beams at left side of grace */
1347 beamthick = beamoff(prev_p, PB_RIGHT, gs_p->c[AW], start_p);
1348
1349 ycross = b1 * gs_p->c[AW] + b0;
1350 if (start_p->stemdir == UP) {
1351 if (ycross - beamthick < gs_p->c[RN])
1352 b0 += gs_p->c[RN] - (ycross - beamthick);
1353 } else { /* stemdir == DOWN */
1354 if (ycross + beamthick > gs_p->c[RS])
1355 b0 -= (ycross + beamthick) - gs_p->c[RS];
1356 }
1357 }
1358
1359 return (b0); /* new (possibly changed) Y intercept */
1360}
1361\f
1362/*
1363 * Name: embedclef()
1364 *
1365 * Abstract: Change the Y intercept if necessary for embedded clefs.
1366 *
1367 * Returns: new y intercept value (may be no change)
1368 *
1369 * Description: When clef changes occur before groups in a beamed set, the
1370 * beam(s) for the set may have to be put farther away from their
1371 * note heads, so that these beams won't collide with the clefs.
1372 * This function returns the new Y intercept for the equation of
1373 * the nongraces' main beam, which accomplishes this. When there
1374 * aren't any embedded clefs, or they are in certain positions,
1375 * this Y intercept will be the same as the old Y intercept.
1376 */
1377
1378static double
1379embedclef(start_p, b1, b0)
1380
1381struct GRPSYL *start_p; /* first group in nongrace beamed set */
1382double b1; /* slope */
1383double b0; /* y intercept */
1384
1385{
1386 struct GRPSYL *gs_p; /* point to group being looked at */
1387 struct GRPSYL *pbgs_p; /* group whose partial beams may impact us */
1388 float north, south; /* top and bottom edge of a clef */
1389 float horizontal; /* left or right edge of a clef */
1390 float beamthick; /* total thickness of beams and space between*/
1391 float ycross; /* where grace stem would hit nongrace beam */
1392
1393
1394 /*
1395 * Loop through all the groups between the first and last groups of
1396 * this nongrace beamed set, including the last but not the first, and
1397 * including any embedded graces. If any are preceded by a clef, we
1398 * may need to move the beam(s) out of the way.
1399 */
1400 for (gs_p = start_p->next; gs_p != 0 && ! (gs_p->prev->grpvalue ==
1401 GV_NORMAL && gs_p->prev->beamloc == ENDITEM);
1402 gs_p = gs_p->next) {
1403
1404 if (gs_p->clef == NOCLEF) {
1405 continue; /* ignore groups with no clef */
1406 }
1407
1408 /* find the vertical edges of the clef */
1409 (void)clefvert(gs_p->clef, YES, &north, &south);
1410 north *= Staffscale;
1411 south *= Staffscale;
1412
1413 /*
1414 * Make sure the right side of the clef doesn't collide with
1415 * the beams.
1416 */
1417 /* find right side of the clef */
1418 horizontal = gs_p->c[AW] - CLEFPAD * Staffscale;
1419
1420 /* group whose partial beams we need to worry about */
1421 pbgs_p = gs_p->grpvalue == GV_ZERO ? nextnongrace(gs_p) : gs_p;
1422
1423 /* thickness of relevant beams at right side of clef */
1424 beamthick = beamoff(pbgs_p, PB_LEFT, horizontal, start_p);
1425
1426 /* Find RY where right edge of clef would hit the main beam. If
1427 * that edge of clef would hit any beam, change Y intercept. */
1428 ycross = b1 * horizontal + b0;
1429 if (start_p->stemdir == UP) {
1430 if (ycross - beamthick < north) {
1431 b0 += north - (ycross - beamthick);
1432 }
1433 } else { /* stemdir == DOWN */
1434 if (ycross + beamthick > south) {
1435 b0 -= (ycross + beamthick) - south;
1436 }
1437 }
1438
1439 /*
1440 * Make sure the left side of the clef doesn't collide with
1441 * the beams.
1442 */
1443 /* find left side of the clef */
1444 horizontal -= clefwidth(gs_p->clef, YES) * Staffscale;
1445
1446 /* group whose partial beams we need to worry about */
1447 pbgs_p = prevnongrace(gs_p);
1448
1449 /* thickness of relevant beams at left side of clef */
1450 beamthick = beamoff(pbgs_p, PB_RIGHT, horizontal, start_p);
1451
1452 /* Find RY where left edge of clef would hit main beam. If
1453 * that edge of clef would hit any beam, change Y intercept. */
1454 ycross = b1 * horizontal + b0;
1455 if (start_p->stemdir == UP) {
1456 if (ycross - beamthick < north) {
1457 b0 += north - (ycross - beamthick);
1458 }
1459 } else { /* stemdir == DOWN */
1460 if (ycross + beamthick > south) {
1461 b0 -= (ycross + beamthick) - south;
1462 }
1463 }
1464 }
1465
1466 return (b0); /* new (possibly changed) Y intercept */
1467}
1468\f
1469/*
1470 * Name: beamoff()
1471 *
1472 * Abstract: On one side of group, get height of beams and spaces between.
1473 *
1474 * Returns: height in inches
1475 *
1476 * Description: This function is called with a nongrace group in beamed set, to
1477 * find out how many beams it has on one side of it and how high
1478 * they are. If the group is the first or last in the set, the
1479 * side must be the interior side. Partial beams are also figured
1480 * in, if they might extend far enough to reach the "boundary"
1481 * coordinate.
1482 */
1483
1484static double
1485beamoff(gs_p, side, boundary, start_p)
1486
1487struct GRPSYL *gs_p; /* group we are concerned with */
1488int side; /* which side of the group, PB_LEFT or PB_RIGHT */
1489double boundary; /* X coord of edge of thing that must not collide */
1490struct GRPSYL *start_p; /* first group in nongrace beamed set */
1491
1492{
1493 struct GRPSYL *ogs_p; /* nongrace group on "side" side of gs_p */
1494 struct GRPSYL *o2gs_p; /* nongrace group on other side of gs_p */
1495 int beams; /* number of beams for figuring collision */
1496 int minbasic; /* minimum (longest) basictime */
1497
1498
1499 /*
1500 * If it's the left side of this group we're worried about, set ogs_p
1501 * to the previous nongrace, and o2gs_p to the next. If right, do the
1502 * opposite.
1503 */
1504 if (side == PB_LEFT) {
1505 ogs_p = prevnongrace(gs_p);
1506 o2gs_p = nextnongrace(gs_p);
1507 } else {
1508 ogs_p = nextnongrace(gs_p);
1509 o2gs_p = prevnongrace(gs_p);
1510 }
1511
1512 /*
1513 * Whichever of the two groups {this group, the group on the side
1514 * that we're worried about} has the least (slowest) basictime, that
1515 * determines how many full beams will connect those two groups. (You
1516 * take log2 of it and subtract 2.)
1517 */
1518 minbasic = MIN(gs_p->basictime, ogs_p->basictime);
1519 if (minbasic >= 8) {
1520 beams = drmo(MIN(gs_p->basictime, ogs_p->basictime)) - 2;
1521 } else {
1522 beams = 0; /* must be an alternation */
1523 }
1524
1525 /* add the number of alternation beams, if any */
1526 if (gs_p->slash_alt < 0) {
1527 beams -= gs_p->slash_alt;
1528 }
1529
1530 /*
1531 * If our group needs more beams than the group on the requested side,
1532 * and the stem is in the direction where partial beams would stick out
1533 * beyond our GRPSYL boundary and the partial beams are long enough to
1534 * possibly collide with the thing we're trying to avoid . . .
1535 */
1536 if (gs_p->basictime > ogs_p->basictime &&
1537 (side == PB_LEFT && gs_p->stemdir == DOWN &&
1538 gs_p->c[AW] - 5.0 * Stepsize < boundary ||
1539 side == PB_RIGHT && gs_p->stemdir == UP &&
1540 gs_p->c[AE] + 5.0 * Stepsize > boundary)) {
1541 /*
1542 * If we are the start or end of this beamed set, or we need
1543 * more beams than the group on the other side . . .
1544 */
1545 if (gs_p->beamloc == STARTITEM || gs_p->beamloc == ENDITEM ||
1546 gs_p->basictime > o2gs_p->basictime) {
1547 /*
1548 * We have partial beam(s); if on the side that matters
1549 * to us, reset the number of beams to include partials.
1550 */
1551 if (pbeamside(gs_p, start_p) == side) {
1552 beams = drmo(gs_p->basictime) - 2;
1553 }
1554 }
1555 }
1556
1557 /*
1558 * To get total beam thickness, multiply the size of one beam by the
1559 * number of beams. Also add in a small fudge factor.
1560 */
1561 return (Flagsep * beams + Stepsize / 2.0);
1562}
1563\f
1564/*
1565 * Name: embedrest()
1566 *
1567 * Abstract: Set relative vertical coords of rests embedded in beamed sets.
1568 *
1569 * Returns: void
1570 *
1571 * Description: Rests' vertical coords were set in restsyl.c. But when a rest
1572 * is embedded in a beamed set, its coords may have to be changed
1573 * now so that it fits well.
1574 */
1575
1576static void
1577embedrest(start_p, last_p, b1, b0)
1578
1579struct GRPSYL *start_p; /* first group in nongrace beamed set */
1580struct GRPSYL *last_p; /* last group in nongrace beamed set */
1581double b1; /* slope */
1582double b0; /* y intercept */
1583
1584{
1585 struct GRPSYL *gs_p; /* point to group in the set */
1586 struct GRPSYL *gp_p, *gpp_p; /* prev nongrace note, and prev to that */
1587 struct GRPSYL *gn_p, *gnn_p; /* next nongrace note, and next to that */
1588 int bp, bn; /* beams on gp_p and gn_p */
1589 int partial; /* partial beams in our way */
1590 char rchar; /* char for the rest */
1591 int size; /* font size */
1592 float asc, des; /* ascent and descent of a rest */
1593 float beamthick; /* total thickness of beams and space between*/
1594 float ycross; /* where rest would hit beam */
1595 int beams; /* number of beams joining two groups */
1596
1597
1598 /*
1599 * Loop through the interior groups of this set, setting relative
1600 * vertical coords of rest groups. (Outer groups are never rests.)
1601 */
1602 for (gs_p = start_p->next; gs_p != last_p; gs_p = gs_p->next) {
1603
1604 /* skip nonrests */
1605 if (gs_p->grpcont != GC_REST)
1606 continue;
1607
1608 /* skip cases where the user is forcing the coords */
1609 if (gs_p->restdist != NORESTDIST)
1610 continue;
1611
1612 rchar = restchar(gs_p->basictime);
1613 size = (gs_p->grpsize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE);
1614 asc = ascent(FONT_MUSIC, size, rchar) * Staffscale;
1615 des = descent(FONT_MUSIC, size, rchar) * Staffscale;
1616
1617
1618 /* find prev nongrace note group; will be in this beamed set */
1619 for (gp_p = gs_p->prev; gp_p->grpcont != GC_NOTES ||
1620 gp_p->grpvalue == GV_ZERO; gp_p = gp_p->prev)
1621 ;
1622
1623 /* find prev nongrace note group to that, if any */
1624 for (gpp_p = gp_p->prev; gpp_p != 0 && (gpp_p->grpcont !=
1625 GC_NOTES || gpp_p->grpvalue == GV_ZERO);
1626 gpp_p= gpp_p->prev)
1627 ;
1628 /* but if it's not in this beamed set, forget it */
1629 if (gpp_p != 0 && gpp_p->beamloc != INITEM &&
1630 gpp_p->beamloc != STARTITEM)
1631 gpp_p = 0;
1632
1633
1634 /* find next nongrace note group; will be in this beamed set */
1635 for (gn_p = gs_p->next; gn_p->grpcont != GC_NOTES ||
1636 gn_p->grpvalue == GV_ZERO; gn_p = gn_p->next)
1637 ;
1638
1639 /* find next nongrace note group to that, if any */
1640 for (gnn_p = gn_p->next; gnn_p != 0 && (gnn_p->grpcont !=
1641 GC_NOTES || gnn_p->grpvalue == GV_ZERO);
1642 gnn_p= gnn_p->next)
1643 ;
1644 /* but if it's not in this beamed set, forget it */
1645 if (gnn_p != 0 && gnn_p->beamloc != INITEM &&
1646 gnn_p->beamloc != ENDITEM)
1647 gnn_p = 0;
1648
1649
1650 /* get number of beams needed by prev and next */
1651 bp = numbeams(gp_p->basictime);
1652 bn = numbeams(gn_p->basictime);
1653
1654 partial = 0; /* init to no partial beams */
1655
1656 /*
1657 * If the group just before our rest is notes, and this beamed
1658 * set's stems are up, and the prev note needs more beams than
1659 * the next note, we may have to deal with partial beams.
1660 */
1661 if (gs_p->prev->grpcont == GC_NOTES && start_p->stemdir == UP
1662 && bp > bn) {
1663 if (gpp_p == 0) {
1664 /* definitely partial beams on this side */
1665 partial = bp - bn;
1666 } else {
1667 /* maybe partial beams on this side */
1668 if (numbeams(gpp_p->basictime) < bp &&
1669 pbeamside(gp_p, start_p) == PB_RIGHT)
1670 partial = bp - bn;
1671 }
1672 /* but if far enough away horizontally, we can ignore */
1673 if (gs_p->c[AW] - gp_p->c[AE] > 1.5 * Stepsize)
1674 partial = 0;
1675 }
1676
1677 /*
1678 * If the group just after our rest is notes, and this beamed
1679 * set's stems are down, and the next note needs more beams than
1680 * the prev note, we may have to deal with partial beams. If
1681 * the next group is grace, we might fall into this block, but
1682 * that's okay; the next nongrace (gn_p) will be far enough
1683 * away that partial will (correctly) be forced back to 0.
1684 */
1685 if (gs_p->next->grpcont == GC_NOTES && start_p->stemdir == DOWN
1686 && bn > bp) {
1687 if (gnn_p == 0) {
1688 /* definitely partial beams on this side */
1689 partial = bn - bp;
1690 } else {
1691 /* maybe partial beams on this side */
1692 if (numbeams(gnn_p->basictime) < bn &&
1693 pbeamside(gn_p, start_p) == PB_LEFT)
1694 partial = bn - bp;
1695 }
1696 /* but if far enough away horizontally, we can ignore */
1697 if (gn_p->c[AW] - gs_p->c[AE] > 1.5 * Stepsize)
1698 partial = 0;
1699 }
1700
1701 /* full beams joining prev and next, plus relevant partials */
1702 beams = MIN(bp, bn) + partial;
1703
1704 /*
1705 * To get total beam thickness, multiply the size of one beam
1706 * by the number of beams.
1707 */
1708 beamthick = Flagsep * beams;
1709
1710 /* find where outer beam hits our rest's X coord */
1711 ycross = b1 * gs_p->c[AX] + b0;
1712
1713 /* find vertical coord, quantizing the results */
1714 if (start_p->stemdir == UP) {
1715 gs_p->c[RY] = nearestline(ycross - beamthick -
1716 asc - Stepsize);
1717 } else { /* stemdir == DOWN */
1718 gs_p->c[RY] = nearestline(ycross + beamthick +
1719 des + Stepsize);
1720 }
1721
1722 gs_p->c[RN] = gs_p->c[RY] + asc;
1723 gs_p->c[RS] = gs_p->c[RY] - des;
1724 }
1725}
1726\f
1727/*
1728 * Name: avoidothervoice()
1729 *
1730 * Abstract: Change the Y intercept if necessary to avoid the other voice.
1731 *
1732 * Returns: new y intercept value (may be no change)
1733 *
1734 * Description: When there is another voice, its groups might collide with our
1735 * voice's beams, unless we lengthen our groups' stems. This
1736 * function returns the new Y intercept for the equation of the
1737 * our voice's main beam, which accomplishes this. When there is
1738 * no other voice, or its groups don't interfere with our beam,
1739 * this Y intercept will be the same as the old Y intercept.
1740 * When it changes, embedded rests' coords need to be changed too.
1741 */
1742
1743static double
1744avoidothervoice(start_p, last_p, b1, b0, ogs_p)
1745
1746struct GRPSYL *start_p; /* first group in nongrace beamed set */
1747struct GRPSYL *last_p; /* last group in nongrace beamed set */
1748double b1; /* slope */
1749double b0; /* y intercept */
1750struct GRPSYL *ogs_p; /* first group in the other voice */
1751
1752{
1753 struct GRPSYL *prev_p; /* point to nongrace group preceding gs_p */
1754 struct GRPSYL *prev2_p; /* point to nongrace group before that one */
1755 struct GRPSYL *next_p; /* point to nongrace group following gs_p */
1756 struct GRPSYL *next2_p; /* point to nongrace group after that one */
1757 struct GRPSYL *gs_p; /* point to group being looked at */
1758 float beamthick; /* total thickness of beams and space between*/
1759 float ycross; /* where grace stem would hit nongrace beam */
1760 float fary; /* farthest y coord of other voice's group */
1761 int beams; /* number of beams joining two nongrace groups*/
1762 float thismove; /* how far one item requires the beam to move*/
1763 float move; /* distance to move intercept */
1764
1765
1766 move = 0.0; /* init to no move */
1767
1768 /*
1769 * Loop through all the groups in the other voice. (If there is no
1770 * other voice, this loop will execute zero times.) If any of its
1771 * groups land on or beyond our beam, move our beam farther away so
1772 * they don't.
1773 */
1774 for (gs_p = ogs_p; gs_p != 0; gs_p = gs_p->next) {
1775
1776 /* spaces and rests can't interfere with anything */
1777 if (gs_p->grpcont != GC_NOTES)
1778 continue;
1779
1780 /* if this group is outside our beamed set, ignore it */
1781 if (gs_p->c[AX] <= start_p->c[AX] ||
1782 gs_p->c[AX] >= last_p->c[AX])
1783 continue;
1784
1785 /*
1786 * Find which groups in our set immediately preceed and follow
1787 * the other voice's group. These will be prev_p and next_p.
1788 */
1789 for (prev_p = next_p = start_p;
1790 next_p->c[AX] < gs_p->c[AX];
1791 prev_p = next_p, next_p = nextnongrace(next_p))
1792 ;
1793
1794 /*
1795 * If next_p is lined up with gs_p, and is a note group, that
1796 * means these groups were "compatible" (see setgrps.c), and so
1797 * there can be no way that we would have to move our beam.
1798 * But if next_p is a rest, handle the situation and continue.
1799 */
1800 if (next_p->c[AX] == gs_p->c[AX]) {
1801 if (next_p->grpcont == GC_NOTES)
1802 continue; /* compatible, no problem */
1803
1804 /*
1805 * Find the AX and RY coords of the outer edge of the
1806 * outer note of the other voice's group that is the
1807 * farthest in the direction of our beam. Then, if
1808 * this point would run into or beyond the rest, find
1809 * how far to move the Y intercept (b0) so that it
1810 * won't. Remember the farthest move needed.
1811 */
1812 if (start_p->stemdir == UP) {
1813 fary = gs_p->notelist[0].c[RN] + Stdpad;
1814 if (next_p->c[RS] < fary) {
1815 thismove = fary - next_p->c[RS];
1816 move = MAX(move, thismove);
1817 }
1818 } else { /* stemdir == DOWN */
1819 fary = gs_p->notelist[ gs_p->nnotes-1 ].c[RS]
1820 - Stdpad;
1821 if (next_p->c[RN] > fary) {
1822 thismove = fary - next_p->c[RN];
1823 move = MIN(move, thismove);
1824 }
1825 }
1826
1827 continue;
1828 }
1829
1830 /*
1831 * Find which of prev_p and next_p has the least (slowest)
1832 * basictime. That determines how many full beams will connect
1833 * those two groups. (You take log2 of it and subtract 2.)
1834 * Then add in any alternation beams.
1835 */
1836 if (prev_p->basictime >= 8)
1837 beams = drmo(MIN(prev_p->basictime, next_p->basictime))
1838 - 2;
1839 else
1840 beams = 0;
1841
1842 if (prev_p->slash_alt < 0)
1843 beams -= prev_p->slash_alt;
1844
1845 /*
1846 * Find out if there are partial beams on the left side of the
1847 * following group or right side of the preceding group. If
1848 * so, that group's basictime may determine the total number of
1849 * beams that could interfere with our group, if it's close
1850 * enough.
1851 */
1852 if (prev_p->basictime < next_p->basictime && next_p->stemdir ==
1853 DOWN && next_p->c[AX] - gs_p->c[AX] < 5 * Stepsize) {
1854
1855 /* find nongrace group after "next", if one exists */
1856 next2_p = nextnongrace(next_p);
1857
1858 /* if "next" group has partial beams . . . */
1859 if (next2_p == 0 || next_p->beamloc == ENDITEM ||
1860 next_p->basictime > next2_p->basictime) {
1861
1862 /* if on its left side, reset total beams */
1863 if (pbeamside(next_p, start_p) == PB_LEFT)
1864 beams = drmo(next_p->basictime) - 2;
1865 }
1866 } else if (prev_p->basictime > next_p->basictime && prev_p->
1867 stemdir == UP && gs_p->c[AX] - prev_p->c[AX] < 5 * Stepsize) {
1868
1869 /* find nongrace group before "prev", if one exists */
1870 prev2_p = prevnongrace(prev_p);
1871
1872 /* if "prev" group has partial beams . . . */
1873 if (prev2_p == 0 || prev_p->beamloc == STARTITEM ||
1874 prev_p->basictime > prev2_p->basictime) {
1875
1876 /* if on its right side, reset total beams */
1877 if (pbeamside(prev_p, start_p) == PB_RIGHT)
1878 beams = drmo(prev_p->basictime) - 2;
1879 }
1880 }
1881
1882 beamthick = Flagsep * beams + Stepsize;
1883
1884 /*
1885 * Find the AX and RY coords of the outer edge of the outer
1886 * note of the other voice's group that is the farthest in the
1887 * direction of our beam. Then, if this point would run into
1888 * or beyond the nongrace beam(s), find how much the Y
1889 * intercept (b0) would have to move to avoid the collision.
1890 * Remember the farthest move found so far.
1891 */
1892 ycross = b1 * gs_p->c[AX] + b0;
1893 if (start_p->stemdir == UP) {
1894
1895 fary = gs_p->notelist[0].c[RN] + Stdpad;
1896 if (ycross - beamthick < fary) {
1897 thismove = fary - (ycross - beamthick);
1898 move = MAX(move, thismove);
1899 }
1900
1901 } else { /* stemdir == DOWN */
1902
1903 fary = gs_p->notelist[ gs_p->nnotes-1 ].c[RS] - Stdpad;
1904 if (ycross + beamthick > fary) {
1905 thismove = fary - (ycross + beamthick);
1906 move = MIN(move, thismove);
1907 }
1908 }
1909 }
1910
1911 if (move == 0.0)
1912 return (b0); /* no change; return old intercept */
1913
1914 /*
1915 * If our beamed set has any embedded rests, we want to move the rests
1916 * too. We really only have to move rests that the other voice is
1917 * bumping into, but it will probably look better to move them all.
1918 * We need to move everything by a multiple of 2 stepsizes, since rests
1919 * should be positioned that way.
1920 */
1921 for (gs_p = start_p->next; gs_p != last_p; gs_p = gs_p->next) {
1922 /* break out if we find a rest */
1923 if (gs_p->grpcont == GC_REST)
1924 break;
1925 }
1926 if (gs_p != last_p) {
1927 /*
1928 * We found a rest. Round the amount the intercept moved up to
1929 * a multiple of 2 stepsizes.
1930 */
1931 move = (move < 0.0 ? -1.0 : 1.0) * 2.0 * Stepsize *
1932 ((int)(fabs(move) / (2.0 * Stepsize)) + 1);
1933
1934 /* move every embedded rest by this amount */
1935 for (gs_p = start_p->next; gs_p != last_p; gs_p = gs_p->next) {
1936 if (gs_p->grpcont == GC_REST) {
1937 gs_p->c[RN] += move;
1938 gs_p->c[RY] += move;
1939 gs_p->c[RS] += move;
1940 }
1941 }
1942 }
1943
1944 return (b0 + move); /* new Y intercept */
1945}
1946\f
1947/*
1948 * Name: setgroupvert()
1949 *
1950 * Abstract: Set RN and RS for each group of given type in a linked list.
1951 *
1952 * Returns: void
1953 *
1954 * Description: This function loops through the linked list of groups for one
1955 * voice for one measure. It handles either grace groups or non-
1956 * grace groups, whichever it is told to do. It sets the RN and
1957 * RS for the groups.
1958 */
1959
1960static void
1961setgroupvert(grpvalue, firstgs_p, ogs_p)
1962
1963int grpvalue; /* should we do grace groups or normal groups?*/
1964struct GRPSYL *firstgs_p; /* point to first group in a linked list */
1965struct GRPSYL *ogs_p; /* point to first group in other linked list */
1966
1967{
1968 struct GRPSYL *gs_p; /* point along groups in a linked list */
1969 float outstem; /* the part of the stemlen outside notes of group */
1970 float stemtip; /* coord of the end of the stem */
1971 float old; /* old group boundary */
1972 float delta; /* change in group boundary */
1973
1974
1975 debug(32, "setgroupvert file=%s line=%d grpvalue=%d",
1976 firstgs_p->inputfile, firstgs_p->inputlineno, grpvalue);
1977 /*
1978 * Loop through every group, skipping rests, spaces, and groups of the
1979 * wrong type (grace vs. nongrace), setting the relative vertical
1980 * coordinates.
1981 */
1982 for (gs_p = firstgs_p; gs_p != 0; gs_p = gs_p->next) {
1983 if (gs_p->grpcont != GC_NOTES)
1984 continue;
1985 if (gs_p->grpvalue != grpvalue)
1986 continue;
1987
1988 /*
1989 * Back in setnotes.c, we set RY to 0, the center line of the
1990 * staff. N was set to the top of the highest note, plus
1991 * padding, excluding any CSS notes. S is the analogous thing,
1992 * below. But if all notes are CSS, N and S were set to 0.
1993 */
1994
1995 /*
1996 * Now we want to set the stemlen, as well as we can. For
1997 * groups whose step tips are not affected by CSS, we do it in
1998 * the non-CSS pass; otherwise we do it in the CSS pass.
1999 */
2000 if (css_affects_stemtip(gs_p) == CSSpass) {
2001
2002 /*
2003 * If the group has a stem or pseudostem, we do this
2004 * work. Extend the appropriate group boundary to
2005 * reach to the end of the stem. Do this for all
2006 * groups with real stems or pseudostems, excluding
2007 * cross staff beaming (where we don't know yet how
2008 * long the stems will be and we don't want to include
2009 * them in the group boundary anyway, since it would
2010 * prevent stem overlapping that we want). That means
2011 * half notes or shorter (excluding grace quarter
2012 * notes), or anything with slash/alternations.
2013 */
2014 if (gs_p->beamto == CS_SAME &&
2015 (gs_p->basictime >= 2 || gs_p->slash_alt != 0) &&
2016 gs_p->stemlen != 0.0) {
2017
2018 outstem = gs_p->stemlen
2019 - (gs_p->notelist[0].c[RY]
2020 - gs_p->notelist[gs_p->nnotes-1].c[RY]);
2021 /*
2022 * In the CSS pass we also have to adjust the
2023 * absolute coords, by the same amount as the
2024 * relative, since those have been set by now.
2025 */
2026 if (gs_p->stemdir == UP) {
2027 stemtip = gs_p->notelist[0].c[RY]
2028 + outstem;
2029 old = gs_p->c[RN];
2030 gs_p->c[RN] = MAX(stemtip, gs_p->c[RN])
2031 + Stdpad;
2032 if (CSSpass == YES) {
2033 delta = gs_p->c[RN] - old;
2034 gs_p->c[AN] += delta;
2035 }
2036 } else {
2037 stemtip = gs_p->notelist[gs_p->nnotes-1]
2038 .c[RY] - outstem;
2039 old = gs_p->c[RS];
2040 gs_p->c[RS] = MIN(stemtip, gs_p->c[RS])
2041 - Stdpad;
2042 if (CSSpass == YES) {
2043 delta = gs_p->c[RS] - old;
2044 gs_p->c[AS] += delta;
2045
2046 }
2047 }
2048 }
2049 }
2050
2051 if (CSSpass == NO) {
2052 /*
2053 * Increase RN and decrease RS based on "with" lists.
2054 * Do this only in the first pass. This depends on the
2055 * fact that "with" lists are always put on the side
2056 * away from the other staff, when CSS is involved.
2057 */
2058 expgroup(gs_p, ogs_p);
2059 } else {
2060 /*
2061 * In the CSS pass, various group boundaries need more
2062 * adjustment.
2063 */
2064 if (gs_p->stemdir == UP) {
2065 if (gs_p->stemto == CS_ABOVE && NNN(gs_p) == 0){
2066 gs_p->c[RS] = gs_p->notelist[
2067 gs_p->nnotes-1].c[RS] - Stdpad;
2068 gs_p->c[AS] += gs_p->c[RS];
2069 }
2070 if (gs_p->stemto == CS_BELOW && NNN(gs_p) == 0){
2071 gs_p->c[RN] = gs_p->notelist[
2072 gs_p->nnotes-1].c[RY] +
2073 gs_p->stemlen;
2074 expgroup(gs_p, ogs_p);
2075 gs_p->c[AN] = gs_p->c[AY] + gs_p->c[RN];
2076 }
2077 if (gs_p->stemto == CS_SAME &&
2078 gs_p->stemlen > 0) {
2079 gs_p->c[RN] = gs_p->notelist
2080 [gs_p->nnotes-1].c[RY] + gs_p->stemlen
2081 + Stdpad;
2082
2083 gs_p->c[AN] = gs_p->notelist
2084 [gs_p->nnotes-1].c[AY] + gs_p->stemlen
2085 + Stdpad;
2086 }
2087 if (gs_p->stemto == CS_ABOVE &&
2088 gs_p->stemlen == 0) {
2089 gs_p->c[RN] = gs_p->notelist[0].c[RN]
2090 + Stdpad;
2091 gs_p->c[AN] = gs_p->notelist[0].c[AN]
2092 + Stdpad;
2093 }
2094 } else {
2095 if (gs_p->stemto == CS_BELOW && NNN(gs_p) == 0){
2096 gs_p->c[RN] = gs_p->notelist[0].c[RN]
2097 + Stdpad;
2098 gs_p->c[AN] += gs_p->c[RN];
2099 }
2100 if (gs_p->stemto == CS_ABOVE && NNN(gs_p) == 0){
2101 gs_p->c[RS] = gs_p->notelist[0].c[RY] -
2102 gs_p->stemlen;
2103 expgroup(gs_p, ogs_p);
2104 gs_p->c[AS] = gs_p->c[AY] + gs_p->c[RS];
2105 }
2106 if (gs_p->stemto == CS_SAME &&
2107 gs_p->stemlen > 0) {
2108 gs_p->c[RS] = gs_p->notelist[0].c[RY]
2109 - gs_p->stemlen - Stdpad;
2110
2111 gs_p->c[AS] = gs_p->notelist[0].c[AY]
2112 - gs_p->stemlen - Stdpad;
2113 }
2114 if (gs_p->stemto == CS_BELOW &&
2115 gs_p->stemlen == 0) {
2116 gs_p->c[RS] = gs_p->notelist
2117 [gs_p->nnotes-1].c[RS] - Stdpad;
2118 gs_p->c[AS] = gs_p->notelist
2119 [gs_p->nnotes-1].c[AS] - Stdpad;
2120 }
2121 }
2122 }
2123 }
2124}
2125\f
2126/*
2127 * Name: settuplet()
2128 *
2129 * Abstract: Figure out where tuplet bracket goes and change RN and RS.
2130 *
2131 * Returns: void
2132 *
2133 * Description: This function is given a pointer to the first GRPSYL in a
2134 * tuplet whose bracket is to be printed. It figures out where
2135 * the tuplet bracket and number should go, and sets tupextend for
2136 * all the groups, to show where the tuplet bracket would go.
2137 * Even if the bracket ends up not getting printed, this is needed
2138 * for placing the number.
2139 */
2140
2141static void
2142settuplet(start_p, staff_p)
2143
2144struct GRPSYL *start_p; /* first group in the tuplet */
2145struct STAFF *staff_p; /* staff the tuplet is on */
2146
2147{
2148 struct GRPSYL *gs_p; /* loop through the groups in the tuplet */
2149 struct GRPSYL *last_p; /* point the last group in the tuplet */
2150 struct GRPSYL *end_p; /* point beyond the last group in the tuplet */
2151 struct NOTE *note_p; /* pointer to an outside note of a group */
2152 float sx, sy; /* sum of x and y coords of north or south */
2153 float xbar, ybar; /* average x and y coords of north or south */
2154 float top, bottom; /* numerator & denominator for finding b1 */
2155 float temp; /* scratch variable */
2156 float startx, endx; /* x coord of first and last north or south */
2157 float starty, endy; /* y coord of first and last north or south */
2158 float b0, b1; /* y intercept and slope */
2159 float maxb0, minb0; /* max and min y intercepts */
2160 float shift; /* x dist bracket reaches beyond end groups */
2161 float acceast, accwest; /* horizontal coords of an accidental */
2162 float accvert; /* north or south of an accidental */
2163 float asc, des, wid; /* ascent, descent, and width of an acc */
2164 float numeast, numwest; /* horizontal coords of the tuplet number */
2165 float numvert; /* vertical edge of number closest to staff */
2166 float height; /* height of the tuplet number */
2167 int css_affects_tup; /* does CSS affect any group in the tuplet? */
2168 int coord; /* RN or RS, depending on where bracket goes */
2169 /* or AN or AS if CSSpass == YES */
2170 int halfstaff; /* half the height of staff, in stepsizes */
2171 int num; /* number of groups in tuplet */
2172 float vert[2]; /* vertical coords of two groups */
2173 int n; /* loop variable */
2174
2175
2176 debug(32, "settuplet file=%s line=%d", start_p->inputfile,
2177 start_p->inputlineno);
2178 /*
2179 * If start_p is pointing at a grace group that precedes the first real
2180 * group of the tuplet, move start_p forward to the first real group.
2181 * Actually, this shouldn't be necessary; the parser is doing it now.
2182 */
2183 while (start_p->grpvalue == GV_ZERO)
2184 start_p = start_p->next;
2185
2186 /*
2187 * Find out which side the tuplet number (and bracket, if needed)
2188 * should go on. That determines which coord we pay attention to.
2189 * The other determining factor is whether this is the CSS pass.
2190 */
2191 if (tupdir(start_p, staff_p) == PL_ABOVE) {
2192 coord = CSSpass == YES ? AN : RN;
2193 } else {
2194 coord = CSSpass == YES ? AS : RS;
2195 }
2196
2197 /* find whether CSS affects any group in the set */
2198 css_affects_tup = NO;
2199 if (CSSused == YES) { /* don't waste time looking if CSS not used */
2200 for (gs_p = start_p; gs_p != 0 && ! (gs_p != start_p &&
2201 gs_p->prev->tuploc == ENDITEM);
2202 gs_p = gs_p->next) {
2203 if (gs_p->stemto == CS_ABOVE &&
2204 (coord == AN || coord == AN) ||
2205 gs_p->stemto == CS_BELOW &&
2206 (coord == AS || coord == AS)) {
2207 css_affects_tup = YES;
2208 break;
2209 }
2210 }
2211 }
2212
2213 /*
2214 * If no groups are affected by CSS, handle this tuplet on the
2215 * first pass only. If some are affected, handle it on the second
2216 * pass only.
2217 */
2218 if (css_affects_tup != CSSpass) {
2219 return;
2220 }
2221
2222 last_p = 0; /* prevent useless 'used before set' warnings */
2223
2224 /*
2225 * If the first group is STARTITEM, there are multiple groups in the
2226 * tuplet. If it is LONEITEM, there is only one.
2227 */
2228 if (start_p->tuploc == STARTITEM) {
2229 /*
2230 * Use linear regression to find the best-fit line through the
2231 * RN or RS, or AN or AS, of the groups, as the case may be.
2232 * The X coords used are absolute, but the Y coords are, in the
2233 * normal (non-CSSpass case) relative to the center line of the
2234 * staff, since we don't know the absolute Y coords yet, and it
2235 * wouldn't affect the result anyway. But if this is the CSS
2236 * pass, we do know the absolute vertical coords, and we have
2237 * to use them, since we are dealing with two staffs.
2238 *
2239 * First get sum of x and y coords, to find averages. Remember
2240 * where last valid group is. Only nongrace groups can be
2241 * tuplet members, although there could be grace groups before
2242 * a tuplet member. We ignored any grace group before the
2243 * first real tuplet member, but any others must be dealt with.
2244 */
2245 sx = sy = 0;
2246 num = 0;
2247 for (gs_p = start_p; gs_p != 0 && ! (gs_p != start_p &&
2248 gs_p->prev->tuploc == ENDITEM);
2249 gs_p = gs_p->next) {
2250 sx += gs_p->c[AX];
2251 sy += gs_p->c[coord];
2252 num++; /* count number of groups */
2253 last_p = gs_p;
2254 }
2255 /* last_p now points at last valid group */
2256
2257 end_p = gs_p; /* point end_p beyond last tuplet member */
2258
2259 xbar = sx / num;
2260 ybar = sy / num;
2261
2262 /* accum numerator & denominator of regression formula for b1 */
2263 top = bottom = 0;
2264 for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next) {
2265 temp = gs_p->c[AX] - xbar;
2266 top += temp * (gs_p->c[coord] - ybar);
2267 bottom += temp * temp;
2268 }
2269
2270 b1 = top / bottom; /* slope */
2271 /*
2272 * We could also figure:
2273 * b0 = ybar - b1 * xbar; y intercept
2274 * to get the equation of the regression line: y = b0 + b1 * x
2275 * but we're going to change b0 later anyway. Now, there are
2276 * certain cases where we want to override the slope determined
2277 * by regression, so revise b1 if that is the case.
2278 */
2279
2280 /* if first and last groups are equal, force horizontal */
2281 if (start_p->c[coord] == last_p->c[coord])
2282 b1 = 0.0;
2283
2284 /* if repeating pattern of two coords, force horizontal */
2285 if (b1 != 0.0 && num >= 4 && num % 2 == 0) {
2286 vert[0] = start_p->c[coord];
2287 vert[1] = start_p->next->c[coord];
2288 for (n = 0, gs_p = start_p; n < num;
2289 n++, gs_p = gs_p->next) {
2290 if (n >= 2 && gs_p->c[coord] != vert[n % 2])
2291 break;
2292 }
2293 if (n == num)
2294 b1 = 0.0;
2295 }
2296
2297 } else { /* LONEITEM */
2298 /*
2299 * There's only one group, so there's no need to apply linear
2300 * regression. But we need to set up certain variables so that
2301 * later code in this function can treat both cases the same.
2302 */
2303 last_p = start_p; /* point at last tuplet member */
2304 end_p = start_p->next; /* point beyond last tuplet member */
2305 b1 = 0; /* set horizontal slope */
2306 b0 = start_p->c[coord]; /* y intercept based on this group */
2307 }
2308
2309 /*
2310 * Find half the width of a note head; the end of the tuplet bracket
2311 * reaches that far beyond the X coords of the outer groups. Set
2312 * the X positions for these ends.
2313 */
2314 shift = getstemshift(last_p);
2315 startx = start_p->c[AX] - shift; /* start of tuplet bracket */
2316 endx = last_p->c[AX] + shift; /* end of tuplet bracket */
2317
2318 /*
2319 * The original line derived by linear regression must be adjusted in
2320 * certain ways. First, don't let the slope exceed plus or minus 0.7,
2321 * since that would look bad.
2322 */
2323 if (b1 > 0.7)
2324 b1 = 0.7;
2325 else if (b1 < -0.7)
2326 b1 = -0.7;
2327
2328 /*
2329 * Calculate a new y intercept (b0). First pass parallel lines
2330 * through each group's extremity, and record the maximum and minimum
2331 * y intercepts that result.
2332 */
2333 b0 = start_p->c[coord] - b1 * start_p->c[AX];
2334 maxb0 = minb0 = b0; /* init to value for first group */
2335 /* look at rest of them */
2336 for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next) {
2337 b0 = gs_p->c[coord] - b1 * gs_p->c[AX];
2338 if (b0 > maxb0)
2339 maxb0 = b0;
2340 else if (b0 < minb0)
2341 minb0 = b0;
2342 }
2343
2344 /*
2345 * The outer edge of the tuplet bracket, including the number, should
2346 * be TUPHEIGHT away from the group that sticks out the farthest.
2347 */
2348 if (coord == RN || coord == AN) {
2349 b0 = maxb0 + Tupheight;
2350 } else { /* RS or AS */
2351 b0 = minb0 - Tupheight;
2352 }
2353
2354 /*
2355 * Calculate the Y positions of the start and end of the bracket from
2356 * the X positions, and the slope and Y intercept we have tentatively
2357 * chosen. If, however, the bracket is going to fall within the staff,
2358 * make adjustments so it won't.
2359 */
2360 starty = b0 + b1 * startx; /* y coord near left end of beam */
2361 endy = b0 + b1 * endx; /* y coord near right end of beam */
2362 halfstaff = svpath(staff_p->staffno, STAFFLINES)->stafflines == 5
2363 ? 4 : 1;
2364
2365 if (coord == RN) {
2366 if (starty < halfstaff * Stepsize + Tupheight)
2367 starty = halfstaff * Stepsize + Tupheight;
2368 if (endy < halfstaff * Stepsize + Tupheight)
2369 endy = halfstaff * Stepsize + Tupheight;
2370 } else if (coord == RS) {
2371 if (starty > -halfstaff * Stepsize - Tupheight)
2372 starty = -halfstaff * Stepsize - Tupheight;
2373 if (endy > -halfstaff * Stepsize - Tupheight)
2374 endy = -halfstaff * Stepsize - Tupheight;
2375 }
2376
2377 /*
2378 * If y at the ends of the bracket only differs by less than 2 points,
2379 * set end equal to the start to avoid a jagged look.
2380 */
2381 if (endy - starty < 2 * POINT && endy - starty > -2 * POINT) {
2382 endy = (starty + endy) / 2.;
2383 starty = endy;
2384 }
2385
2386 /* recalculate slope and y intercept from (possibly) new endpoints */
2387 b1 = (endy - starty) / (endx - startx); /* slope */
2388 b0 = starty - b1 * startx; /* y intercept */
2389
2390 /*
2391 * The vertical extension of accidentals is not included in group
2392 * boundaries, and so the calculation of the tuplet bracket's equation
2393 * has ignored them so far. In general, this is no problem. If an
2394 * accidental touches or slightly crosses that line, who cares? But we
2395 * would like to keep it from running into the tuplet number. So scan
2396 * through the notes closest to the bracket, checking for accidentals.
2397 * (Notes a step or more from there would never really be a problem.)
2398 * Also, accidentals on the first group can never be a problem.
2399 */
2400 (void)tupnumsize(start_p, &numwest, &numeast, &height, staff_p);
2401 numvert = (starty + endy) / 2 + (coord == RN || coord == AN ?
2402 -height : height) / 2;
2403
2404 for (gs_p = start_p->next; gs_p != end_p; gs_p = gs_p->next) {
2405
2406 if (gs_p->grpcont != GC_NOTES)
2407 continue;
2408
2409 note_p = &gs_p->notelist[ coord == RN || coord == AN ?
2410 0 : gs_p->nnotes - 1 ];
2411 if (note_p->accidental == '\0')
2412 continue;
2413
2414 /*
2415 * The note of this group nearest the bracket has an acci-
2416 * dental. Find its horizontal midpoint, and vertical coord
2417 * nearest the bracket. Add padding to the vertical coord.
2418 */
2419 accdimen(note_p, &asc, &des, &wid);
2420 asc *= Staffscale;
2421 des *= Staffscale;
2422 wid *= Staffscale;
2423
2424 accwest = gs_p->c[AX] + note_p->waccr;
2425 acceast = accwest + wid;
2426
2427 if (coord == RN || coord == AN) {
2428 accvert = note_p->c[CSSpass == YES ? AY : RY]
2429 + asc + Stepsize;
2430 } else {
2431 accvert = note_p->c[CSSpass == YES ? AY : RY]
2432 - des - Stepsize;
2433 }
2434
2435 /* if acc is completely to the left of the number, try next */
2436 if (acceast < numwest)
2437 continue;
2438
2439 /* if acc is completely to the right, get out */
2440 if (accwest > numeast)
2441 break;
2442
2443 /*
2444 * If acc sticks out beyond the edge of the number, change the
2445 * y intercept by that amount to prevent it. Then get out,
2446 * since no later groups could be that nearby.
2447 */
2448 if ((coord == RN || coord == AN) && accvert > numvert ||
2449 (coord == RS || coord == AS) && accvert < numvert) {
2450 b0 += accvert - numvert;
2451 break;
2452 }
2453 }
2454
2455 /*
2456 * At this point we know where to put the tuplet bracket. Set
2457 * tupextend in all the groups, to reach the tuplet bracket.
2458 */
2459 for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next)
2460 gs_p->tupextend = (b0 + b1 * gs_p->c[AX]) - gs_p->c[coord];
2461}
2462\f
2463/*
2464 * Name: expgroup()
2465 *
2466 * Abstract: Decide side for "with" list & expand vertical group vertically.
2467 *
2468 * Returns: void
2469 *
2470 * Description: This function decides which side of the group a "with" list
2471 * should be put, and calls applywith() to alter the group's
2472 * vertical boundaries accordingly.
2473 */
2474
2475static void
2476expgroup(gs_p, ogs_p)
2477
2478struct GRPSYL *gs_p; /* the group to be worked on */
2479struct GRPSYL *ogs_p; /* the other group */
2480
2481{
2482 struct GRPSYL *g_p; /* earlier GRPSYLs in *gs_p's list */
2483 RATIONAL vtime; /* time preceding this group in measure */
2484 int side; /* side to put things on (1=top, -1=bottom) */
2485
2486
2487 side = 0; /* prevent useless 'used before set' warnings */
2488
2489 /*
2490 * Define a chunk of code for the cases where the stem may be allowed
2491 * to go either way. It goes opposite the stem for normal, with the
2492 * stem for tab.
2493 */
2494#define FREESTEM \
2495 { \
2496 if (is_tab_staff(gs_p->staffno) == YES) { \
2497 side = -1; /* we know stemdir is DOWN */ \
2498 gs_p->normwith = NO; \
2499 } else { \
2500 side = gs_p->stemdir == UP ? -1 : 1; \
2501 gs_p->normwith = YES; \
2502 } \
2503 }
2504
2505 /*
2506 * Define a chunk of code for the cases where the stem has to go a
2507 * certain way, determined by which voice this is, unless forced by the
2508 * user. The "with" items are always above a voice acting as voice 1,
2509 * and below a voice acting as voice 2.
2510 */
2511#define FIXEDSTEM \
2512 { \
2513 if (gs_p->pvno == 1) { \
2514 side = 1; \
2515 gs_p->normwith = gs_p->stemdir == UP ? NO : YES;\
2516 } else { \
2517 side = -1; \
2518 gs_p->normwith = gs_p->stemdir == DOWN ? NO : YES;\
2519 } \
2520 }
2521
2522 /*
2523 * If there is cross staff stemming, that consideration overrides all
2524 * others. We want to keep the "with" items towards our staff, hoping
2525 * they will be less likely to collide with something there.
2526 */
2527 if (gs_p->stemto != CS_SAME) {
2528 if (gs_p->stemto == CS_ABOVE) {
2529 gs_p->normwith = gs_p->stemdir == UP ? YES : NO;
2530 side = -1;
2531 } else { /* CS_BELOW */
2532 gs_p->normwith = gs_p->stemdir == UP ? NO : YES;
2533 side = 1;
2534 }
2535 applywith(gs_p, side);
2536 return;
2537 }
2538
2539 /*
2540 * Switch on vscheme to decide which side of the group the "with"
2541 * things will be put on.
2542 */
2543 switch (svpath(gs_p->staffno, VSCHEME)->vscheme) {
2544 case V_1:
2545 FREESTEM
2546 break;
2547
2548 case V_2OPSTEM:
2549 FIXEDSTEM
2550 break;
2551
2552 case V_2FREESTEM:
2553 /*
2554 * Figure out where this group starts by adding up the time
2555 * values of all previous groups in the measure. Then, treat
2556 * this like V_1 or V_2OPSTEM, based on whether the other
2557 * voice has space here.
2558 */
2559 vtime = Zero;
2560 for (g_p = gs_p->prev; g_p != 0; g_p = g_p->prev)
2561 vtime = radd(vtime, g_p->fulltime);
2562
2563 if (hasspace(ogs_p, vtime, radd(vtime, gs_p->fulltime))) {
2564 FREESTEM
2565 } else {
2566 FIXEDSTEM
2567 }
2568 break;
2569
2570 case V_3OPSTEM:
2571 if (gs_p->pvno == 3) {
2572 FREESTEM /* voice 3 is always like V_1 */
2573 } else {
2574 FIXEDSTEM
2575 }
2576 break;
2577
2578 case V_3FREESTEM:
2579 if (gs_p->pvno == 3) {
2580 FREESTEM /* voice 3 is always like V_1 */
2581 } else {
2582 /* voices 1 and 2 act like V_2FREESTEM */
2583 vtime = Zero;
2584 for (g_p = gs_p->prev; g_p != 0; g_p = g_p->prev)
2585 vtime = radd(vtime, g_p->fulltime);
2586
2587 if (hasspace(ogs_p, vtime, radd(vtime, gs_p->fulltime))) {
2588 FREESTEM
2589 } else {
2590 FIXEDSTEM
2591 }
2592 }
2593 break;
2594 }
2595
2596 /*
2597 * If there is cross staff beaming and the "with" items are to be on
2598 * the beam side, we can't do anything yet since we don't know yet
2599 * where the beam will be.
2600 */
2601 if (gs_p->beamto != CS_SAME && gs_p->normwith == NO) {
2602 return;
2603 }
2604
2605 applywith(gs_p, side);
2606}
2607\f
2608/*
2609 * Name: applywith()
2610 *
2611 * Abstract: Expand vertical boundaries of group, based on "with" list.
2612 *
2613 * Returns: void
2614 *
2615 * Description: This function adds to the RN coord of a group and/or subtracts
2616 * from the RS coord, if a "with" list is present.
2617 */
2618
2619static void
2620applywith(gs_p, side)
2621
2622struct GRPSYL *gs_p; /* the group to be worked on */
2623int side; /* side to put things on (1=top, -1=bottom) */
2624
2625{
2626 int n; /* loop variable */
2627 float hi; /* height of a list item */
2628
2629
2630 /*
2631 * Loop through all the "with" items, expanding the N or S coord of
2632 * the group. Each item is allowed enough space for its height, or
2633 * MINWITHHEIGHT, whichever is greater. In the print phase, items of
2634 * height less than MINWITHHEIGHT will be placed so as to avoid staff
2635 * lines as much as possible.
2636 */
2637 for (n = 0; n < gs_p->nwith; n++) {
2638 hi = strheight(gs_p->withlist[n]);
2639 hi = MAX(hi, Staffscale * MINWITHHEIGHT);
2640 if (side == 1)
2641 gs_p->c[RN] += hi;
2642 else
2643 gs_p->c[RS] -= hi;
2644 }
2645}