Commit | Line | Data |
---|---|---|
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 | */ | |
18 | static float Defstemsteps; | |
19 | ||
20 | static void proclist P((struct MAINLL *mainll_p, int vno)); | |
21 | static void proctablist P((struct MAINLL *mainll_p, int vno)); | |
22 | static int stemforced P((struct GRPSYL *gs_p, struct GRPSYL *ogs_p)); | |
23 | static void setbeam P((struct GRPSYL *start_p, struct GRPSYL *end_p, | |
24 | struct GRPSYL *ogs_p)); | |
25 | static void restore_ry P((struct GRPSYL *start_p, struct GRPSYL *end_p)); | |
26 | static double embedgrace P((struct GRPSYL *start_p, double b1, double b0)); | |
27 | static double embedclef P((struct GRPSYL *start_p, double b1, double b0)); | |
28 | static double beamoff P((struct GRPSYL *gs_p, int side, double boundary, | |
29 | struct GRPSYL *start_p)); | |
30 | static void embedrest P((struct GRPSYL *start_p, struct GRPSYL *last_p, | |
31 | double b1, double b0)); | |
32 | static double avoidothervoice P((struct GRPSYL *start_p, struct GRPSYL *last_p, | |
33 | double b1, double b0, struct GRPSYL *ogs_p)); | |
34 | static void setgroupvert P((int, struct GRPSYL *, struct GRPSYL *)); | |
35 | static void settuplet P((struct GRPSYL *start_p, struct STAFF *staff_p)); | |
36 | static void expgroup P((struct GRPSYL *gs_p, struct GRPSYL *ogs_p)); | |
37 | static 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 | ||
51 | void | |
52 | beamstem() | |
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 | ||
113 | static void | |
114 | proclist(mainll_p, vno) | |
115 | ||
116 | struct MAINLL *mainll_p; /* MLL struct for staff we're dealing with */ | |
117 | int 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 | ||
425 | static void | |
426 | proctablist(mainll_p, vno) | |
427 | ||
428 | struct MAINLL *mainll_p; /* MLL struct for staff we're dealing with */ | |
429 | int 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 | ||
528 | static int | |
529 | stemforced(gs_p, ogs_p) | |
530 | ||
531 | struct GRPSYL *gs_p; /* the group we are asking about */ | |
532 | struct 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 | ||
627 | static void | |
628 | setbeam(start_p, end_p, ogs_p) | |
629 | ||
630 | struct GRPSYL *start_p; /* first in beamed set */ | |
631 | struct GRPSYL *end_p; /* after last in beamed set */ | |
632 | struct 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 | ||
1257 | static void | |
1258 | restore_ry(start_p, end_p) | |
1259 | ||
1260 | struct GRPSYL *start_p; /* first in beamed set */ | |
1261 | struct 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 | ||
1293 | static double | |
1294 | embedgrace(start_p, b1, b0) | |
1295 | ||
1296 | struct GRPSYL *start_p; /* first group in nongrace beamed set */ | |
1297 | double b1; /* slope */ | |
1298 | double 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 | ||
1378 | static double | |
1379 | embedclef(start_p, b1, b0) | |
1380 | ||
1381 | struct GRPSYL *start_p; /* first group in nongrace beamed set */ | |
1382 | double b1; /* slope */ | |
1383 | double 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 | ||
1484 | static double | |
1485 | beamoff(gs_p, side, boundary, start_p) | |
1486 | ||
1487 | struct GRPSYL *gs_p; /* group we are concerned with */ | |
1488 | int side; /* which side of the group, PB_LEFT or PB_RIGHT */ | |
1489 | double boundary; /* X coord of edge of thing that must not collide */ | |
1490 | struct 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 | ||
1576 | static void | |
1577 | embedrest(start_p, last_p, b1, b0) | |
1578 | ||
1579 | struct GRPSYL *start_p; /* first group in nongrace beamed set */ | |
1580 | struct GRPSYL *last_p; /* last group in nongrace beamed set */ | |
1581 | double b1; /* slope */ | |
1582 | double 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 | ||
1743 | static double | |
1744 | avoidothervoice(start_p, last_p, b1, b0, ogs_p) | |
1745 | ||
1746 | struct GRPSYL *start_p; /* first group in nongrace beamed set */ | |
1747 | struct GRPSYL *last_p; /* last group in nongrace beamed set */ | |
1748 | double b1; /* slope */ | |
1749 | double b0; /* y intercept */ | |
1750 | struct 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 | ||
1960 | static void | |
1961 | setgroupvert(grpvalue, firstgs_p, ogs_p) | |
1962 | ||
1963 | int grpvalue; /* should we do grace groups or normal groups?*/ | |
1964 | struct GRPSYL *firstgs_p; /* point to first group in a linked list */ | |
1965 | struct 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 | ||
2141 | static void | |
2142 | settuplet(start_p, staff_p) | |
2143 | ||
2144 | struct GRPSYL *start_p; /* first group in the tuplet */ | |
2145 | struct 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 | ||
2475 | static void | |
2476 | expgroup(gs_p, ogs_p) | |
2477 | ||
2478 | struct GRPSYL *gs_p; /* the group to be worked on */ | |
2479 | struct 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 | ||
2619 | static void | |
2620 | applywith(gs_p, side) | |
2621 | ||
2622 | struct GRPSYL *gs_p; /* the group to be worked on */ | |
2623 | int 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 | } |