chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / absvert.c
... / ...
CommitLineData
1/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
2 * 2005, 2006 by Arkkra Enterprises */
3/* All rights reserved */
4/*
5 * Name: absvert.c
6 *
7 * Description: This file contains functions for setting all absolute
8 * vertical coordinates.
9 */
10
11#include "defines.h"
12#include "structs.h"
13#include "globals.h"
14
15/*
16 * Define the maximum number of scores that could ever fit on a page, when all
17 * staffs and scores are packed as tightly as possible. The 8 * STEPSIZE is
18 * the height of the five lines of a staff, and the other factor in the
19 * denominator is the minimum distance between staffs or scores, whichever is
20 * smaller. If a staff has less than 5 lines, it is still given as much space
21 * as a 5 line staff, so that's why we can use 8 * STEPSIZE here as the
22 * smallest possible staff size.
23 */
24#define MAXSCORES ( (int)(PGHEIGHT / \
25 (MINSTFSCALE * STEPSIZE * (8 + MIN(MINMINSTSEP, MINMINSCSEP)))) + 1 )
26
27#define FUDGE 0.001 /* fudge factor for round off error */
28
29/* determine what clef, if any, will be printed on a staff */
30#define CLEF2PRINT(staffno) \
31 (svpath(staffno, STAFFLINES)->printclef == SS_NORMAL ? \
32 svpath(staffno, CLEF)->clef : NOCLEF)
33
34/* define amount of horz and vert padding between at-end grids */
35#define HPADGRID (2.0 * STEPSIZE)
36#define VPADGRID (2.0 * STEPSIZE)
37
38/* maximum length of a chord name that we care about for sorting purposes */
39#define MAXCHNAME (100)
40
41static void relscore P((struct MAINLL *mllfeed_p));
42static void relstaff P((struct MAINLL *feed_p, int s1, int s2, double botoff,
43 double betweendist));
44static void posscores P((void));
45static void abspage P((struct MAINLL *page_p, float cursep[], float maxsep[],
46 float curpad[], float maxpad[], int totscores,
47 double remheight, double y_start));
48static void absstaff P((struct FEED *feed_p, struct STAFF *staff_p));
49static double grids_atend P((double vertavail, int firstpage,
50 struct FEED *mfeed_p, struct FEED *gfeed_p));
51static int compgrids P((const void *g1_p_p, const void *g2_p_p));
52static void proc_css P((void));
53static void one_css P((struct STAFF *ts_p, struct STAFF *os_p,
54 struct GRPSYL *tg_p, RATIONAL time));
55static void horzavoid P((void));
56static void avoidone P((struct MAINLL *mainll_p, struct GRPSYL *cssg_p,
57 RATIONAL time));
58static void set_csb_stems P((void));
59static void onecsb P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p));
60static int calcline P((struct GRPSYL *start1_p, struct GRPSYL *end1_p,
61 struct GRPSYL *start2_p, struct GRPSYL *end2_p,
62 struct GRPSYL *first_p, struct GRPSYL *last_p,
63 int topdir, int botdir,
64 float *b0_p, float *b1_p));
65static void samedir P((struct GRPSYL *first_p, struct GRPSYL *last_p,
66 struct GRPSYL *start1_p, struct GRPSYL *start2_p,
67 struct GRPSYL *end1_p, float *b0_p, float *b1_p,
68 double deflen, int one_end_forced, int slope_forced,
69 double forced_slope));
70static void oppodir P((struct GRPSYL *first_p, struct GRPSYL *last_p,
71 struct GRPSYL *start1_p, struct GRPSYL *start2_p,
72 float *b0_p, float *b1_p, double deflen, int one_end_forced,
73 int slope_forced, double forced_slope));
74static struct GRPSYL *nextcsb P((struct GRPSYL *gs_p));
75static struct GRPSYL *nxtbmnote P((struct GRPSYL *gs_p, struct GRPSYL *first_p,
76 struct GRPSYL *endnext_p));
77\f
78/*
79 * Name: absvert()
80 *
81 * Abstract: Set all absolute vertical coordinates.
82 *
83 * Returns: void
84 *
85 * Description: This function sets all absolute vertical coordinates. First it
86 * calls relscore() for each score, to position the staffs in that
87 * score relative to the score. Then it calls posscores() to
88 * decide how many scores to put on each page, and set all the
89 * absolute coordinates. Finally it completes the work for
90 * cross staff stemming (CSS) and cross staff beaming (CSB).
91 */
92
93void
94absvert()
95
96{
97 struct MAINLL *mainll_p; /* point along main linked list */
98
99
100 debug(16, "absvert");
101 /*
102 * Find each section of the main linked list, delimited by FEEDs. For
103 * each such section, call relscore() to fix the score internally
104 * (relative to itself, all staffs and between stuff). Keep SSVs
105 * up to date so that we always know what the user requested
106 * separations are.
107 */
108 initstructs(); /* clean out old SSV info */
109
110 for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
111 switch (mainll_p->str) {
112 case S_SSV:
113 asgnssv(mainll_p->u.ssv_p);
114 break;
115
116 case S_FEED:
117 relscore(mainll_p);
118 break;
119 }
120 }
121
122 /*
123 * Position the scores on the pages, setting all absolute vertical
124 * coordinates.
125 */
126 posscores();
127
128 /*
129 * Process groups that have cross staff stemming, if there were any.
130 */
131 if (CSSused == YES) {
132 proc_css();
133 }
134
135 /*
136 * Set stem lengths for groups involved in cross staff beaming, if
137 * there were any.
138 */
139 if (CSBused == YES) {
140 set_csb_stems();
141 }
142}
143\f
144/*
145 * Name: relscore()
146 *
147 * Abstract: Set certain relative coords to be relative to score.
148 *
149 * Returns: void
150 *
151 * Description: This function loops through the part of the main linked list
152 * for this score. It adjusts the relative vertical coords of
153 * STAFFs, and also of GRPSYLs (syllables) and STUFFs of the
154 * things that are "between" staffs. In the end, the STAFFs will
155 * be relative to the score (FEED), and the between things will
156 * be relative to the staff above them. Yes, I suppose this
157 * belongs in relvert.c, but relvert.c has enough work to do.
158 */
159
160static void
161relscore(mllfeed_p)
162
163struct MAINLL *mllfeed_p; /* FEED at start of this score */
164
165{
166 struct MAINLL *mainll_p;/* point along main linked list */
167 struct STAFF *cstaff_p; /* point at current staff */
168 struct STAFF *pstaff_p; /* point at previous staff */
169 struct FEED *feed_p; /* point at FEED structure itself */
170 float cstaffoffset; /* current staff offset from score */
171 float staffdist; /* dist between prev & cur staff inner lines*/
172 float halfnonbetween; /* (staffdist - heightbetween) / 2 */
173 float betweendist; /* from prev staff center line to base line */
174 float prevhalf; /* half the height of previous staff */
175 float curhalf; /* half the height of current staff */
176 float limit; /* smallest dist allowed between inner lines */
177 float needed; /* dist between inner lines to avoid collis */
178 int prevclef; /* clef on the previous staff */
179 float prevscale; /* staffscale of the previous staff */
180 float spad; /* staffpad (inches) below previous staff */
181 float clefroom; /* room for clefs and/or measure numbers */
182 static int first = YES; /* is this the first score in the song? */
183
184
185 debug(32, "relscore file=%s line=%d", mllfeed_p->inputfile,
186 mllfeed_p->inputlineno);
187 feed_p = mllfeed_p->u.feed_p;
188
189 /*
190 * If this score is actually a block, all we have to do is set the
191 * relative vertical coords of the FEED. We set RY to be the center.
192 */
193 if (mllfeed_p->next != 0 && mllfeed_p->next->str == S_BLOCKHEAD) {
194 feed_p->c[RN] = mllfeed_p->next->u.blockhead_p->height / 2.0;
195 feed_p->c[RY] = 0; /* RY is always 0 */
196 feed_p->c[RS] = -feed_p->c[RN];
197 feed_p->lastdist = 0.0;
198 return;
199 }
200
201 /*
202 * Find the first STAFF in this score (will be in first measure).
203 */
204 for (mainll_p = mllfeed_p->next; mainll_p != 0 &&
205 mainll_p->str != S_FEED && mainll_p->str != S_STAFF;
206 mainll_p = mainll_p->next)
207 ;
208 if (mainll_p == 0 || mainll_p->str != S_STAFF)
209 return; /* ignore items when there's a feed at end of song */
210
211 /* init variables for main loop */
212 cstaffoffset = 0; /* top staff Y == score Y */
213 pstaff_p = 0; /* there is no previous staff */
214 prevclef = NOCLEF;
215 prevscale = 1.0;
216 spad = 0.0; /* keep lint happy; will be set before used */
217
218 /*
219 * Loop through all STAFF structures in the first measure of this
220 * score. Skip invisible ones. cstaff_p always points at the staff
221 * we are working on, and pstaff_p always points to the previous
222 * visible staff (so is 0 while we are working on the first visible
223 * staff of the score). For each visible staff except the first, we
224 * figure out how far down it should be from the one above it, and
225 * set its relative vertical coords relative to the score. Also, we
226 * figure out where to put the things that are "between" this staff
227 * and the one above, and set them relative to the above staff.
228 */
229 for ( ; mainll_p->str == S_STAFF; mainll_p = mainll_p->next) {
230
231 cstaff_p = mainll_p->u.staff_p;
232
233 /*
234 * If this staff is invisible, ignore it completely.
235 */
236 if (cstaff_p->visible == NO)
237 continue;
238
239 /*
240 * If it's the first visible staff, there are no coords to set,
241 * since its offset is 0 and the "between" objects below it
242 * will be handled by the next loop. Also set first and last
243 * visible staff numbers in the FEED in this loop, and the
244 * relative vertical coords of the score.
245 */
246 if (pstaff_p == 0) {
247 /* set first visible staff number */
248 feed_p->firstvis = cstaff_p->staffno;
249
250 /* feed's RN is same as first visible staff's RN */
251 feed_p->c[RN] = cstaff_p->c[RN];
252 feed_p->c[RY] = 0; /* RY is always 0 */
253
254 /* these next 3 will be changed later if more staffs */
255 feed_p->c[RS] = cstaff_p->c[RS];
256 feed_p->lastvis = cstaff_p->staffno;
257 feed_p->lastdist = cstaff_p->c[RY] - cstaff_p->c[RS] -
258 staffvertspace(cstaff_p->staffno) / 2.0;
259
260 pstaff_p = cstaff_p; /* previous visible staff */
261 prevclef = CLEF2PRINT(pstaff_p->staffno);
262 prevscale = svpath(pstaff_p->staffno, STAFFSCALE)->
263 staffscale;
264 spad = svpath(pstaff_p->staffno, STAFFPAD)->staffpad
265 * STEPSIZE * prevscale;
266 continue; /* no coords to set */
267 }
268
269 /* set half the height of the previous and current staffs */
270 prevhalf = staffvertspace(pstaff_p->staffno) / 2.0;
271 curhalf = staffvertspace(cstaff_p->staffno) / 2.0;
272
273 /*
274 * The space needed between the bottom line of the previous
275 * staff and the top line of the current staff to avoid
276 * collisions is how far up from the current staff things
277 * stick, plus how far down from the previous staff things
278 * stick, plus the height of anything "between" the two.
279 * To this we add spad for extra padding (overlap if negative).
280 */
281 needed = (cstaff_p->c[RN] - curhalf) +
282 ((pstaff_p->c[RY] - pstaff_p->c[RS]) - prevhalf) +
283 pstaff_p->heightbetween + spad;
284 /*
285 * Set the distance between those two lines to be what the
286 * user requested, or what was calculated above as "needed",
287 * whichever is greater. Set halfnonbetween to be half of
288 * this result, minus half the height of the "between" items.
289 */
290 /* never closer than this */
291 limit = svpath(pstaff_p->staffno,MINSTSEP)->minstsep * STEPSIZE;
292 clefroom = clefspace(prevclef, prevscale,
293 CLEF2PRINT(cstaff_p->staffno),
294 svpath(cstaff_p->staffno, STAFFSCALE)->staffscale,
295 Score.measnum == YES && has_ending(cstaff_p->staffno)
296 && first == NO);
297 limit = MAX(limit, clefroom);
298
299 staffdist = MAX(limit, needed); /* between prev & current */
300
301 /*
302 * Find half the room between the inner staff lines that is not
303 * going to be used by the "between" items. But pretend that
304 * the "between" items are bigger by "spad" than they really
305 * are, so that half of staffpad will go on each side of them.
306 */
307 halfnonbetween = (staffdist - (pstaff_p->heightbetween + spad))
308 / 2.0;
309
310 /* set cstaffoffset for relative to score */
311 cstaffoffset -= (prevhalf + staffdist + curhalf);
312
313 /*
314 * The "between" items are currently placed relative to a base
315 * line that they were piled onto. We would like to center
316 * them between the staffs, but if one staff sticks out more
317 * than the other, it may not be possible. Center as close as
318 * possible. betweendist is how far the base line is from the
319 * center line of the previous staff.
320 */
321 if ((pstaff_p->c[RY] - pstaff_p->c[RS]) - prevhalf >
322 halfnonbetween) {
323 /*
324 * The top staff sticks down far enough that we have
325 * to put the "between" items below center. Jam them
326 * against the top staff.
327 */
328 betweendist = (pstaff_p->c[RY] - pstaff_p->c[RS]) +
329 pstaff_p->heightbetween + spad;
330 } else if (cstaff_p->c[RN] - curhalf > halfnonbetween) {
331 /*
332 * The bottom staff sticks up far enough that we have
333 * to put the "between" items above center. Jam them
334 * against the bottom staff.
335 */
336 betweendist = (prevhalf + staffdist + curhalf) -
337 cstaff_p->c[RN];
338 } else {
339 /*
340 * There is room to center the between items.
341 */
342 betweendist = prevhalf + staffdist - halfnonbetween;
343 }
344
345 /* change baseline of padding to actual baseline */
346 betweendist -= spad / 2.0;
347
348 /*
349 * For all STAFF structures of these staff numbers in this
350 * score, change relative coords as described below.
351 */
352 relstaff(mllfeed_p, pstaff_p->staffno, cstaff_p->staffno,
353 cstaffoffset, betweendist);
354
355 /* last loop iteration leaves right value in these variables */
356 feed_p->lastvis = cstaff_p->staffno;
357 feed_p->c[RS] = cstaff_p->c[RS];
358 feed_p->lastdist = cstaff_p->c[RY] - cstaff_p->c[RS] - curhalf;
359
360 pstaff_p = cstaff_p;
361 prevclef = CLEF2PRINT(pstaff_p->staffno);
362 prevscale = svpath(pstaff_p->staffno, STAFFSCALE)->staffscale;
363 spad = svpath(pstaff_p->staffno, STAFFPAD)->staffpad
364 * STEPSIZE * prevscale;
365 }
366
367 first = NO; /* next score will not be the first */
368}
369\f
370/*
371 * Name: relstaff()
372 *
373 * Abstract: Set certain relative coords to be relative to score.
374 *
375 * Returns: void
376 *
377 * Description: This function is given two staff structures for consecutive
378 * visible staffs. For all STAFF structures of these staff
379 * numbers in this score, set the bottom staff's coords relative
380 * to the score, and set the "between" items' coords (for what's
381 * between top and bottom staff) relative to the top staff.
382 */
383
384static void
385relstaff(feed_p, s1, s2, botoff, betweendist)
386
387struct MAINLL *feed_p; /* pointer to FEED for this score */
388int s1; /* number of top staff */
389int s2; /* number of bottom staff */
390double botoff; /* center line of bottom, relative to score */
391double betweendist; /* center line of top to base line of between*/
392
393{
394 struct MAINLL *mainll_p;/* point along main linked list */
395 struct STAFF *staff_p; /* pointer to a staff */
396 struct GRPSYL *syl_p; /* pointer to a syllable */
397 struct STUFF *stuff_p; /* pointer to stuff to draw */
398 int n; /* loop variable */
399
400
401 debug(32, "relstaff file=%s line=%d s1=%d s2=%d botoff=%f betweendist=%f",
402 feed_p->inputfile, feed_p->inputlineno, s1, s2,
403 (float)botoff, (float)betweendist);
404 /*
405 * Loop through the section of the main linked list for this score,
406 * looking for every STAFF for one of the two given staffs.
407 */
408 for (mainll_p = feed_p->next; mainll_p != 0 && mainll_p->str != S_FEED;
409 mainll_p = mainll_p->next) {
410
411 if (mainll_p->str == S_STAFF &&
412 mainll_p->u.staff_p->staffno == s1) {
413
414 staff_p = mainll_p->u.staff_p;
415
416 /*
417 * Subtract betweendist from all relative coords of
418 * "between" items hanging off this staff, to make them
419 * relative to this staff instead of the base line.
420 */
421 for (n = 0; n < staff_p->nsyllists; n++) {
422 if (staff_p->sylplace[n] == PL_BETWEEN) {
423 for (syl_p = staff_p->syls_p[n];
424 syl_p != 0;
425 syl_p = syl_p->next) {
426 syl_p->c[RN] -= betweendist;
427 syl_p->c[RY] -= betweendist;
428 syl_p->c[RS] -= betweendist;
429 }
430 }
431 }
432 for (stuff_p = staff_p->stuff_p; stuff_p != 0;
433 stuff_p = stuff_p->next) {
434 if (stuff_p->place == PL_BETWEEN) {
435 stuff_p->c[RN] -= betweendist;
436 stuff_p->c[RY] -= betweendist;
437 stuff_p->c[RS] -= betweendist;
438 }
439 }
440 }
441
442 if (mainll_p->str == S_STAFF &&
443 mainll_p->u.staff_p->staffno == s2) {
444
445 staff_p = mainll_p->u.staff_p;
446
447 /*
448 * Make this staff relative to the score instead of
449 * relative to its own center line.
450 */
451 staff_p->c[RN] += botoff;
452 staff_p->c[RY] = botoff;
453 staff_p->c[RS] += botoff;
454 }
455 }
456}
457\f
458/*
459 * Name: posscores()
460 *
461 * Abstract: Place which scores on which pages, and set all vertical coords.
462 *
463 * Returns: void
464 *
465 * Description: This function decides how many scores are going to fit on each
466 * page, based on how big they are and how much minimum space the
467 * user wants put between them. It calls abspage() for each page
468 * to do final positioning and coordinate setting.
469 */
470
471static void
472posscores()
473
474{
475 struct MAINLL *mainll_p;/* point along main LL */
476 struct TIMEDSSV *tssv_p;/* point along timed SSV lists */
477 struct MAINLL *page_p; /* point at first FEED of a page */
478 struct MAINLL *ppage_p; /* point at first FEED of previous page */
479 struct MAINLL *gridpage_p; /* point at FEED for grids-at-end */
480 struct MAINLL *origpage_p; /* remember original page_p */
481 struct FEED *cfeed_p; /* point at current scorefeed */
482 struct FEED *pfeed_p; /* point at previous scorefeed */
483 float availheight; /* available height on page (middle window) */
484 float remheight; /* remaining height on page */
485 float y_start; /* where y begins (at top of _win) */
486 float limit; /* smallest distance allowed between scores */
487 int prevclef; /* clef on last visible staff of prev score */
488 float clefroom; /* room for clefs and/or measure numbers */
489 float excess; /* extra room needed for top score */
490 float abovetopline; /* dist from top line of score to top of score*/
491 float ink; /* distance ink extends between inner lines */
492 float padding; /* space between farthest extents */
493 float scoreheight; /* height of current score */
494 float topheight, botheight; /* height of a "top" or "bot" block */
495 int aftertitle; /* is this the page after a title page? */
496 int firstpage; /* are we working on the first page? */
497 int totscores; /* number of scores on a page */
498
499 /* the following are all in inches, unlike scorepad/scoresep parms */
500 float curminpad; /* current minscpad */
501 float curmaxpad; /* current maxscpad */
502 float *curpad; /* malloc: pad above each score */
503 float *maxpad; /* malloc: maxscpad above each score */
504 float curminsep; /* current minscsep */
505 float curmaxsep; /* current maxscsep */
506 float *cursep; /* malloc: sep above each score */
507 float *maxsep; /* malloc: maxscsep above each score */
508
509 int is_block; /* is there a block after this FEED? */
510 struct BLOCKHEAD *rememtop2_p, *remembot2_p; /* remember most current*/
511 struct BLOCKHEAD *head_p; /* point at Header or Header2 */
512 struct BLOCKHEAD *foot_p; /* point at Footer or Footer2 */
513
514
515 debug(16, "posscores");
516 /*
517 * In each of these arrays, array[idx] refers to distance below score
518 * number idx on a page, numbering the scores from 1 to N. For sep,
519 * only indices 1 through N-1 are used. For pad, indices 0 through N
520 * are used, where 0 means above the first score and N below the last.
521 * The "sep" arrays are for distances between the outermost staff lines
522 * of neighboring scores. The "pad" arrays are for distances between
523 * the outermost thing sticking out of those scores. The "above"
524 * arrays are for distance currently allocated. The "max" arrays are
525 * for the max limits we impose (when we can).
526 */
527 MALLOCA(float, cursep, MAXSCORES);
528 MALLOCA(float, curpad, MAXSCORES + 1);
529 MALLOCA(float, maxsep, MAXSCORES);
530 MALLOCA(float, maxpad, MAXSCORES + 1);
531
532 initstructs(); /* init SSVs */
533
534 /* the following need to be initialized for the coming loop */
535 curminsep = Score.minscsep * STEPSIZE;
536 curminpad = Score.minscpad * STEPSIZE;
537 curmaxsep = Score.maxscsep * STEPSIZE;
538 curmaxpad = Score.maxscpad * STEPSIZE;
539 pfeed_p = 0;
540 firstpage = YES;
541 mainll_p = Mainllhc_p;
542 rememtop2_p = remembot2_p = 0;
543
544 /* the following don't really need to be initialized; we're doing it */
545 /* just to prevent useless 'used before set' warnings */
546 page_p = 0;
547 ppage_p = 0;
548 remheight = 0;
549 y_start = 0;
550 totscores = 0;
551 prevclef = NOCLEF;
552 botheight = 0.0;
553 foot_p = 0;
554
555 /*
556 * Loop through the main linked list, looking at each feed. Assuming
557 * the scores are packed as tightly as allowed, see how many will fit
558 * on each page. Whenever a page fills up, call abspage() to
559 * distribute the extra white space as well as possible and set all
560 * the absolute vertical coords for that page. At the end, call it
561 * again for the last page.
562 */
563 while (mainll_p != 0) {
564 switch (mainll_p->str) {
565 case S_FEED:
566 break; /* go handle this score */
567 case S_SSV:
568 /* apply, and reset vars in case some changed */
569 asgnssv(mainll_p->u.ssv_p);
570 curminsep = Score.minscsep * STEPSIZE;
571 curmaxsep = Score.maxscsep * STEPSIZE;
572 curminpad = Score.minscpad * STEPSIZE;
573 curmaxpad = Score.maxscpad * STEPSIZE;
574 mainll_p = mainll_p->next;
575 continue;
576 case S_BAR:
577 /* apply timed SSVs; they won't affect the above
578 * variables, but they could affect clef, which we
579 * will need later */
580 for (tssv_p = mainll_p->u.bar_p->timedssv_p;
581 tssv_p != 0; tssv_p = tssv_p->next) {
582 asgnssv(&tssv_p->ssv);
583 }
584 mainll_p = mainll_p->next;
585 continue;
586 default:
587 mainll_p = mainll_p->next;
588 continue;
589 }
590
591 /* if there is nothing after this FEED, break out */
592 if (mainll_p->next == 0) {
593 break;
594 }
595
596 cfeed_p = mainll_p->u.feed_p; /* set convenient pointer */
597
598 /*
599 * If firstpage is set, normally there would be no pagefeed,
600 * because the first FEED on that page is marked as a pagefeed
601 * only if the user requested it. If they did, that means
602 * there was a title page with no music on it. We need to
603 * remember this fact, so that we know to use header2/footer2
604 * instead of header/footer. Only the title page would use
605 * header/footer.
606 */
607 aftertitle = firstpage == YES && cfeed_p->pagefeed == YES;
608
609 /* see if there is a block after this feed */
610 is_block = mainll_p->next != 0 &&
611 mainll_p->next->str == S_BLOCKHEAD;
612
613 scoreheight = cfeed_p->c[RN] - cfeed_p->c[RS];
614
615 if (pfeed_p == 0) {
616 /*
617 * We are at the top of a page. Point at the header
618 * and footer that apply. Note that if the header or
619 * footer is unused, its height will be 0.
620 */
621 if (firstpage == YES && aftertitle == NO) {
622 head_p = &Header;
623 foot_p = &Footer;
624 } else {
625 head_p = &Header2;
626 foot_p = &Footer2;
627 }
628
629 /* if not the first page, set pagefeed */
630 if (firstpage == NO) {
631 cfeed_p->pagefeed = YES;
632 }
633
634 /* remember most recent settings of top2 and bot2 */
635 if (cfeed_p->top2_p != 0) {
636 rememtop2_p = cfeed_p->top2_p;
637 }
638 if (cfeed_p->bot2_p != 0) {
639 remembot2_p = cfeed_p->bot2_p;
640 }
641
642 /*
643 * Decide what is to be printed at the top and
644 * bottom (inside the header(2)/footer(2) if any).
645 * On the first page and at every pagefeed where top_p
646 * is set, that is to be used, so leave it alone.
647 * Otherwise use the most recent top2_p setting, so
648 * save the value into top_p. Later in this function,
649 * and also in the print phase, top_p is used, not
650 * top2_p, with exception of grids-at-end pages.
651 */
652 if (firstpage == NO && cfeed_p->top_p == 0) {
653 cfeed_p->top_p = rememtop2_p;
654 }
655 /* analogous for bottom */
656 if (firstpage == NO && cfeed_p->bot_p == 0) {
657 cfeed_p->bot_p = remembot2_p;
658 }
659
660 /* set height of "top" & "bot" if they exist, else 0 */
661 topheight = cfeed_p->top_p != 0 ?
662 cfeed_p->top_p->height : 0.0;
663 botheight = cfeed_p->bot_p != 0 ?
664 cfeed_p->bot_p->height : 0.0;
665
666 /*
667 * Remove these items' size from the space available
668 * for music, and set music's starting point.
669 */
670 availheight = PGHEIGHT - EFF_TOPMARGIN - EFF_BOTMARGIN
671 - head_p->height - foot_p->height
672 - topheight - botheight;
673
674 y_start = PGHEIGHT - EFF_TOPMARGIN
675 - head_p->height - topheight;
676
677 /*
678 * If a header or top exists on this page, we need to
679 * have pad below it. Since we're initially packing as
680 * tightly as possible, assume the minimum. Reduce the
681 * available room by that amount. Analogous for
682 * footer/bottom.
683 */
684 if (head_p->height + topheight > 0.0) {
685 availheight -= curminpad;
686 }
687 if (foot_p->height + botheight > 0.0) {
688 availheight -= curminpad;
689 }
690
691 /* increase score's RN and scoreheight if need be */
692 if (is_block) {
693 /*
694 * Blocks have no clef or measure number, but
695 * clefspace() still will return a little
696 * something for padding, so add that in.
697 */
698 excess = clefspace(NOCLEF, 1.0, NOCLEF, 1.0,NO);
699 cfeed_p->c[RN] += excess;
700 scoreheight += excess;
701 } else {
702 /*
703 * If clef (and measure number if that is to be
704 * printed) stick up higher than anything else,
705 * adjust the size of the score to allow for it.
706 */
707 clefroom = clefspace(NOCLEF, 1.0,
708 CLEF2PRINT(cfeed_p->firstvis), 1.0,
709 Score.measnum == YES &&firstpage == NO);
710 abovetopline = cfeed_p->c[RN] -
711 staffvertspace(cfeed_p->firstvis) / 2.0;
712 excess = clefroom - abovetopline;
713 if (excess > 0.0) {
714 cfeed_p->c[RN] += excess;
715 scoreheight += excess;
716 }
717 }
718
719 if (scoreheight > availheight) {
720 if (Score.units == INCHES) {
721 ufatal("score is too high (%.2f inches) to fit on one page (limit %.2f)",
722 scoreheight * Score.scale_factor,
723 availheight * Score.scale_factor);
724 } else {
725 ufatal("score is too high (%.2f cm) to fit on one page (limit %.2f)",
726 scoreheight * Score.scale_factor *
727 CMPERINCH, availheight *
728 Score.scale_factor * CMPERINCH);
729 }
730 }
731
732 /*
733 * Set pad above the top score. If there is a header
734 * or top, use the values from scorepad. If not, force
735 * both to 0, so that none will be allowed.
736 */
737 if (head_p->height + topheight > 0.0) {
738 curpad[0] = curminpad;
739 maxpad[0] = curmaxpad;
740 } else {
741 curpad[0] = 0.0;
742 maxpad[0] = 0.0;
743 }
744
745 remheight = availheight - scoreheight;
746 totscores = 1;
747 pfeed_p = cfeed_p;
748 ppage_p = page_p;
749 page_p = mainll_p;
750 mainll_p = mainll_p->next;
751 firstpage = NO;
752 if (is_block)
753 prevclef = NOCLEF;
754 else
755 prevclef = CLEF2PRINT(pfeed_p->lastvis);
756
757 } else {
758
759 /*
760 * This will be the second or later score on this page,
761 * if it fits, and the user did not request a manual
762 * pagefeed. Figure out what the minimum padding can
763 * be between this score and the previous. "ink" is
764 * the distance things on the bottom visible staff of
765 * the previous score extend from its bottom line down,
766 * plus the distance things on the top visible staff of
767 * the current score extend from its top line up.
768 * curminpad is the minimum white space the user wants
769 * to allow between scores.
770 */
771 if (is_block) {
772 ink = pfeed_p->lastdist;
773 clefroom = clefspace(prevclef, 1.0, NOCLEF,
774 1.0, NO);
775 } else {
776 ink = pfeed_p->lastdist + (cfeed_p->c[RN] -
777 staffvertspace(cfeed_p->firstvis)/2.0);
778 clefroom = clefspace(prevclef, 1.0,
779 CLEF2PRINT(cfeed_p->firstvis), 1.0,
780 Score.measnum);
781 }
782 limit = MAX(curminsep, clefroom);
783 if (ink < limit - curminpad) {
784 padding = limit - ink;
785 } else {
786 padding = curminpad;
787 }
788
789 if (padding + scoreheight <= remheight &&
790 cfeed_p->pagefeed == NO) {
791 /* this score fits on this page */
792 remheight -= padding + scoreheight;
793 cursep[totscores] = ink + padding;
794 maxsep[totscores] = curmaxsep;
795 curpad[totscores] = padding;
796 maxpad[totscores] = curmaxpad;
797 totscores++;
798 pfeed_p = cfeed_p;
799 mainll_p = mainll_p->next;
800 if (is_block)
801 prevclef = NOCLEF;
802 else
803 prevclef = CLEF2PRINT(pfeed_p->lastvis);
804 } else {
805 /* the score does not fit */
806 /*
807 * Set pad below the bottom score. If there is
808 * a footer or bottom, use the values from
809 * scorepad. If not, force both to 0, so that
810 * none will be allowed.
811 */
812 if (foot_p->height + botheight > 0.0) {
813 curpad[totscores] = curminpad;
814 maxpad[totscores] = curmaxpad;
815 } else {
816 curpad[totscores] = 0.0;
817 maxpad[totscores] = 0.0;
818 }
819
820 abspage(page_p, cursep, maxsep, curpad,
821 maxpad, totscores,
822 remheight, y_start);
823 pfeed_p = 0;
824 }
825 }
826 }
827
828 /* in case it changes, remember the original page_p */
829 origpage_p = page_p;
830
831 /* find out what is after the last FEED */
832 if (page_p->next != 0 && (page_p->next->str == S_CLEFSIG ||
833 page_p->next->str == S_BLOCKHEAD)) {
834 /*
835 * The last top-of-page feed has music/block(s) after it. Let
836 * page_p continue to point at it, and for now let gridpage_p
837 * be null.
838 */
839 gridpage_p = 0;
840 } else {
841 /*
842 * The last top-of-page feed is after all music/blocks. Point
843 * page_p at the previous one, and use this one for gridpage_p.
844 */
845 gridpage_p = page_p;
846 page_p = ppage_p;
847 }
848
849 /*
850 * Before distributing the scores on the last page, if there are chord
851 * grids to be printed at the end, find whether they fit on this page
852 * (their height doesn't exceed remheight minus white). If so, the
853 * subroutine places them at the bottom and returns their height. If
854 * they don't fit, it returns zero and puts them on a separate page.
855 */
856 if (Atend_info.grids_used > 0) {
857 float gridheight;
858
859 /*
860 * In case grids need to go on later page(s), we need to make
861 * sure there is a FEED at the end of the MLL. Its top_p and
862 * bot_p will be used on the first grid page, and top2_p and
863 * bot2_p will be used on later pages.
864 */
865 if (gridpage_p == 0) {
866 /* find last thing in MLL that's not LINE/CURVE/PRHEAD*/
867 for (mainll_p = Mainlltc_p;
868 mainll_p->str == S_LINE ||
869 mainll_p->str == S_CURVE ||
870 mainll_p->str == S_PRHEAD;
871 mainll_p = mainll_p->prev)
872 ;
873 if (mainll_p->str == S_FEED) {
874 /* FEED, so reuse for gridpage FEED */
875 /* (it wasn't a top-of-page FEED before) */
876 gridpage_p = mainll_p;
877 } else {
878 /* alloc new FEED to be used for grid pages */
879 gridpage_p = newMAINLLstruct(S_FEED, -1);
880 insertMAINLL(gridpage_p, Mainlltc_p);
881 }
882
883 /*
884 * Both the first and later grid pages should use what
885 * is currently remembered for top2 and bot2.
886 */
887 gridpage_p->u.feed_p->top_p =
888 gridpage_p->u.feed_p->top2_p = rememtop2_p;
889 gridpage_p->u.feed_p->bot_p =
890 gridpage_p->u.feed_p->bot2_p = remembot2_p;
891 } else {
892 /* set pointers that are not already set */
893 if (gridpage_p->u.feed_p->top2_p == 0) {
894 gridpage_p->u.feed_p->top2_p = rememtop2_p;
895 }
896 if (gridpage_p->u.feed_p->top_p == 0) {
897 gridpage_p->u.feed_p->top_p =
898 gridpage_p->u.feed_p->top2_p;
899 }
900 if (gridpage_p->u.feed_p->bot2_p == 0) {
901 gridpage_p->u.feed_p->bot2_p = remembot2_p;
902 }
903 if (gridpage_p->u.feed_p->bot_p == 0) {
904 gridpage_p->u.feed_p->bot_p =
905 gridpage_p->u.feed_p->bot2_p;
906 }
907 }
908
909 /*
910 * (remheight - curminpad) is how much space is available on the
911 * last page for grids. firstpage is needed to know whether
912 * to use Header or Header2 (etc.) in calculations. The next
913 * two parms are needed for finding the correct top and bottom
914 * sizes for the last music page, and any grid-only pages.
915 */
916 gridheight = grids_atend(remheight - curminpad, firstpage,
917 page_p->u.feed_p, gridpage_p->u.feed_p);
918
919 if (gridheight > 0.0) {
920 /* reduce remaining height by grids and curminpad */
921 remheight -= gridheight + curminpad;
922 }
923 }
924
925 /*
926 * Set pad below the bottom score. If there is a footer
927 * or bottom, use the values from scorepad. If not, force
928 * both to 0, so that none will be allowed.
929 */
930 if (foot_p->height + botheight > 0.0) {
931 curpad[totscores] = curminpad;
932 maxpad[totscores] = curmaxpad;
933 } else {
934 curpad[totscores] = 0.0;
935 maxpad[totscores] = 0.0;
936 }
937
938 abspage(origpage_p, cursep, maxsep, curpad, maxpad, totscores,
939 remheight, y_start);
940
941 FREE(cursep);
942 FREE(maxsep);
943 FREE(curpad);
944 FREE(maxpad);
945}
946\f
947/*
948 * Name: abspage()
949 *
950 * Abstract: Set all absolute vertical coordinates on a page.
951 *
952 * Returns: void
953 *
954 * Description: This function positions the scores on this page as well as
955 * possible, and then sets all the absolute vertical coordinates
956 * for the scores and everything in them.
957 */
958
959static void
960abspage(page_p, cursep, maxsep, curpad, maxpad, totscores, remheight,
961 y_start)
962
963struct MAINLL *page_p; /* point at first FEED for this page */
964float cursep[]; /* this score's top line to above score's bottom line */
965float maxsep[]; /* the max we'd like to expand cursep to */
966float curpad[]; /* white pad between this score and above score */
967float maxpad[]; /* the max we'd like to expand curpad to */
968int totscores; /* number of scores on this page */
969double remheight; /* extra vertical space available, to be distributed */
970double y_start; /* Y coord of top of first score (before padding) */
971
972{
973 struct MAINLL *mainll_p;/* point along main LL */
974 struct FEED *feed_p; /* point at a score feed on this page */
975 struct CHORD *ch_p; /* point at a chord on this page */
976 struct STAFF *staff_p; /* point at a staff on this page */
977 float min; /* smallest number in curpad or cursep */
978 float min2; /* second smallest number in curpad or sep */
979 float share; /* space to add to the min numbers each loop */
980 int mins; /* how many numbers are tied for min */
981 int n; /* loop variable */
982 int *is_min; /* pointer to array malloc'ed below */
983 int *hit_max; /* pointer to array malloc'ed below */
984 int allmax; /* have all scores used the max sep allowed? */
985
986
987 debug(32,"abspage file=%s line=%d totscores=%d remheight=%f y_start=%f",
988 page_p->inputfile, page_p->inputlineno, totscores,
989 (float)remheight, (float)y_start);
990 /*
991 * Array to hold which of the distances in curpad or cursep are
992 * minimal.
993 */
994 MALLOCA(int, is_min, MAXSCORES + 1);
995 /*
996 * Malloc an array to hold YES or NO as to whether this score's
997 * curpad or cursep has reached the maximum allowed.
998 */
999 MALLOCA(int, hit_max, MAXSCORES + 1);
1000
1001 /*
1002 * The current values in curpad[] and cursep[] are for the case of
1003 * the scores being packed as tightly as the stuff sticking out of them
1004 * and the user's specification of minscpad and minscsep allow.
1005 * maxpad[] and maxsep[] have the values of maxscpad and maxscsep
1006 * above each. Now we need to spread the score out, distributing
1007 * remheight appropriately.
1008 */
1009 /*
1010 * First, "smooth out" curpad[], so that the numbers in it will be as
1011 * equal as possible, subject to maxpad[], but ignoring maxsep[].
1012 */
1013 while (remheight > FUDGE) {
1014 /*
1015 * For each score, remember in hit_max whether its curpad
1016 * meets or exceeds the max pad allowed. The fudge factor is
1017 * so we'll pretend we made it, even if there is roundoff
1018 * error. If all scores' curpads have reached that, we're
1019 * done, so break out.
1020 */
1021 allmax = YES;
1022 for (n = 0; n <= totscores; n++) {
1023 if (curpad[n] >= maxpad[n] - FUDGE) {
1024 hit_max[n] = YES;
1025 } else {
1026 hit_max[n] = NO;
1027 allmax = NO;
1028 }
1029 }
1030 if (allmax == YES) {
1031 break;
1032 }
1033
1034 /*
1035 * Find the smallest curpad among scores that haven't hit
1036 * their max.
1037 */
1038 min = 1000;
1039 for (n = 0; n <= totscores; n++) {
1040 if (hit_max[n] == NO && curpad[n] < min)
1041 min = curpad[n];
1042 }
1043
1044 mins = 0; /* number of curpads tied for min */
1045 min2 = 1000; /* second smallest curpad value */
1046
1047 /*
1048 * In this loop, mark which of the curpads are tied for the
1049 * "min" value, and count how many are tied (mins). Also, find
1050 * the second smallest value (min2). All this is done only for
1051 * scores that haven't hit their max.
1052 */
1053 for (n = 0; n <= totscores; n++) {
1054 if (hit_max[n] == NO) {
1055 if (curpad[n] == min) {
1056 is_min[n] = YES;
1057 mins++;
1058 } else {
1059 is_min[n] = NO;
1060 if (curpad[n] < min2) {
1061 min2 = curpad[n];
1062 }
1063 }
1064 }
1065 }
1066
1067 /*
1068 * Don't let min2 exceed the maxpad of any eligible score.
1069 * That way, when we spread the scores out to min2, we won't be
1070 * spreading any of them beyond where they are allowed to go.
1071 * In the next loop, ones that have reached their limit will
1072 * get hit_max[] == YES, while other scores can continue to be
1073 * spread more.
1074 */
1075 for (n = 0; n <= totscores; n++) {
1076 if (hit_max[n] == NO && min2 > maxpad[n]) {
1077 min2 = maxpad[n];
1078 }
1079 }
1080
1081 /*
1082 * We're going to add to all those minimum curpads, either
1083 * using up all of remheight, or bringing them up equal to
1084 * min2, whichever is lower. We add the same amount to the
1085 * curseps, since they change by the same amount as we move
1086 * a score.
1087 */
1088 share = remheight / mins;
1089 if (share > min2 - min) {
1090 share = min2 - min;
1091 }
1092 for (n = 0; n <= totscores; n++) {
1093 if (hit_max[n] == NO && is_min[n] == YES) {
1094 curpad[n] += share;
1095 cursep[n] += share;
1096 }
1097 }
1098
1099 /* decrement remheight by the amount we just used */
1100 remheight -= mins * share;
1101 }
1102
1103 /*
1104 * "Smooth out" cursep[], so that the numbers in it will be as
1105 * equal as possible, subject to maxsep[], but ignoring maxpad[].
1106 * If there is only one score, the first "for" loop won't execute, and
1107 * we'll break out.
1108 */
1109 while (remheight > FUDGE) {
1110 /*
1111 * For each score, remember in hit_max whether its cursep
1112 * meets or exceeds the max sep allowed. The fudge factor is
1113 * so we'll pretend we made it, even if there is roundoff
1114 * error. If all scores' curseps have reached that, we're
1115 * done, so break out.
1116 */
1117 allmax = YES;
1118 for (n = 1; n < totscores; n++) {
1119 if (cursep[n] >= maxsep[n] - FUDGE) {
1120 hit_max[n] = YES;
1121 } else {
1122 hit_max[n] = NO;
1123 allmax = NO;
1124 }
1125 }
1126 if (allmax == YES) {
1127 break;
1128 }
1129
1130 /*
1131 * Find the smallest cursep among scores that haven't hit
1132 * their max.
1133 */
1134 min = 1000;
1135 for (n = 1; n < totscores; n++) {
1136 if (hit_max[n] == NO && cursep[n] < min)
1137 min = cursep[n];
1138 }
1139
1140 mins = 0; /* number of curseps tied for min */
1141 min2 = 1000; /* second smallest cursep value */
1142
1143 /*
1144 * In this loop, mark which of the curseps are tied for the
1145 * "min" value, and count how many are tied (mins). Also, find
1146 * the second smallest value (min2). All this is done only for
1147 * scores that haven't hit their max.
1148 */
1149 for (n = 1; n < totscores; n++) {
1150 if (hit_max[n] == NO) {
1151 if (cursep[n] == min) {
1152 is_min[n] = YES;
1153 mins++;
1154 } else {
1155 is_min[n] = NO;
1156 if (cursep[n] < min2) {
1157 min2 = cursep[n];
1158 }
1159 }
1160 }
1161 }
1162
1163 /*
1164 * Don't let min2 exceed the maxsep of any eligible score.
1165 * That way, when we spread the scores out to min2, we won't be
1166 * spreading any of them beyond where they are allowed to go.
1167 * In the next loop, ones that have reached their limit will
1168 * get hit_max[] == YES, while other scores can continue to be
1169 * spread more.
1170 */
1171 for (n = 1; n < totscores; n++) {
1172 if (hit_max[n] == NO && min2 > maxsep[n]) {
1173 min2 = maxsep[n];
1174 }
1175 }
1176
1177 /*
1178 * We're going to add to all those minimum curseps, either
1179 * using up all of remheight, or bringing them up equal to
1180 * min2, whichever is lower.
1181 */
1182 share = remheight / mins;
1183 if (share > min2 - min) {
1184 share = min2 - min;
1185 }
1186 for (n = 1; n < totscores; n++) {
1187 if (hit_max[n] == NO && is_min[n] == YES) {
1188 cursep[n] += share;
1189 }
1190 }
1191
1192 /* decrement remheight by the amount we just used */
1193 remheight -= mins * share;
1194 }
1195
1196 /* move to top of first score */
1197 y_start -= curpad[0];
1198
1199 feed_p = 0; /* flag that we haven't seen the first FEED yet */
1200
1201 /*
1202 * Loop through the main linked list for this page, setting all
1203 * absolute vertical coordinates.
1204 */
1205 for (mainll_p = page_p, n = 0; mainll_p != 0 && ! (n == totscores &&
1206 mainll_p->str == S_FEED); mainll_p = mainll_p->next) {
1207
1208 switch (mainll_p->str) {
1209 case S_SSV:
1210 /* by end of page, SSVs will be up to date for there */
1211 asgnssv(mainll_p->u.ssv_p);
1212 break;
1213
1214 case S_FEED:
1215 /*
1216 * If this is the first FEED on the page, and what
1217 * follows is music (not a block), move to the top line
1218 * of the first score.
1219 */
1220 if (feed_p == 0 && IS_CLEFSIG_FEED(mainll_p)) {
1221 y_start = y_start - page_p->u.feed_p->c[RN] +
1222 staffvertspace(page_p->u.feed_p->firstvis)/2.0;
1223 }
1224
1225 /*
1226 * Set the score's absolute coordinates. The feed_p
1227 * pointer will be used by other cases in later loops.
1228 */
1229 feed_p = mainll_p->u.feed_p;
1230
1231 /* if next is 0, this is a trailing feed, and it */
1232 /* really has no meaningful coords */
1233 if (mainll_p->next == 0)
1234 continue;
1235
1236 if (mainll_p->next->str == S_BLOCKHEAD) {
1237 /* move from top of block to middle of block */
1238 y_start -= feed_p->c[RN];
1239 } else {
1240 /* move from top line of score to middle of
1241 * first staff */
1242 y_start -= staffvertspace(feed_p->firstvis)/2.0;
1243 }
1244
1245 feed_p->c[AN] = y_start + feed_p->c[RN];
1246 feed_p->c[AY] = y_start;
1247 feed_p->c[AS] = y_start + feed_p->c[RS];
1248
1249 /* unless last score, set up y_start for next one */
1250 if (n < totscores - 1) {
1251 /* top line of next score */
1252 y_start = y_start + feed_p->c[RS] +
1253 feed_p->lastdist - cursep[n + 1];
1254 }
1255
1256 n++;
1257 break;
1258
1259 case S_CHHEAD:
1260 /*
1261 * Set each chord's absolute coordinates the same as
1262 * the feed. These are pretty arbitrary, since they
1263 * are using only for drawing boxes with the MUP_BB
1264 * environment variable.
1265 */
1266 for (ch_p = mainll_p->u.chhead_p->ch_p; ch_p != 0;
1267 ch_p = ch_p->ch_p) {
1268 ch_p->c[AN] = feed_p->c[AN];
1269 ch_p->c[AY] = feed_p->c[AY];
1270 ch_p->c[AS] = feed_p->c[AS];
1271 }
1272 break;
1273
1274 case S_BAR:
1275 /*
1276 * Set absolute N, Y, and S for the bar line. Y can be
1277 * copied from the score's Y; they are both the center
1278 * line of the top visible staff. But the score's N
1279 * S can stick out, based on the groups present,
1280 * whereas the bar line's N is the top line of the top
1281 * staff, and its S is the bottom line of the bottom
1282 * staff.
1283 */
1284 mainll_p->u.bar_p->c[AN] = feed_p->c[AY] +
1285 halfstaffhi(feed_p->firstvis);
1286 mainll_p->u.bar_p->c[AY] = feed_p->c[AY];
1287 mainll_p->u.bar_p->c[AS] = feed_p->c[AS] +
1288 feed_p->lastdist;
1289 break;
1290
1291 case S_CLEFSIG:
1292 /*
1293 * If the clefsig doesn't contain a pseudo bar, just
1294 * break. But otherwise, set this bar's coords just
1295 * like a normal bar.
1296 */
1297 if (mainll_p->u.clefsig_p->bar_p == 0)
1298 break;
1299 mainll_p->u.clefsig_p->bar_p->c[AN] = feed_p->c[AY] +
1300 halfstaffhi(feed_p->firstvis);
1301 mainll_p->u.clefsig_p->bar_p->c[AY] = feed_p->c[AY];
1302 mainll_p->u.clefsig_p->bar_p->c[AS] = feed_p->c[AS] +
1303 feed_p->lastdist - halfstaffhi(feed_p->lastvis);
1304 break;
1305
1306 case S_STAFF:
1307 /* if visible, set all abs vertical coords on staff */
1308 staff_p = mainll_p->u.staff_p;
1309 if (staff_p->visible == YES)
1310 absstaff(feed_p, staff_p);
1311 break;
1312 }
1313
1314 }
1315
1316 FREE(is_min);
1317 FREE(hit_max);
1318}
1319\f
1320/*
1321 * Name: absstaff()
1322 *
1323 * Abstract: Set all absolute vertical coordinates for a STAFF structure.
1324 *
1325 * Returns: void
1326 *
1327 * Description: This function sets all the absolute vertical coords for a
1328 * STAFF structure; those of the staff itself, and those of
1329 * everything hanging off it.
1330 */
1331
1332static void
1333absstaff(feed_p, staff_p)
1334
1335struct FEED *feed_p; /* FEED for the score we're on */
1336struct STAFF *staff_p; /* the staff to be set */
1337
1338{
1339 struct GRPSYL *gs_p; /* point at a group of syllable */
1340 struct STUFF *stuff_p; /* point at a STUFF structure */
1341 struct CRVLIST *pp_p; /* point at a coord for phrase point */
1342 int v; /* index to voices or verses */
1343 int n; /* loop variable */
1344
1345
1346 debug(32, "absstaff file=%s line=%d", staff_p->groups_p[0]->inputfile,
1347 staff_p->groups_p[0]->inputlineno);
1348 /* set the staff's own coords */
1349 staff_p->c[AN] = feed_p->c[AY] + staff_p->c[RN];
1350 staff_p->c[AY] = feed_p->c[AY] + staff_p->c[RY];
1351 staff_p->c[AS] = feed_p->c[AY] + staff_p->c[RS];
1352
1353 /* do the voice(s) */
1354 for (v = 0; v < MAXVOICES; v++) {
1355 for (gs_p = staff_p->groups_p[v]; gs_p != 0; gs_p = gs_p->next){
1356 gs_p->c[AY] = staff_p->c[AY] + gs_p->c[RY];
1357 gs_p->c[AN] = staff_p->c[AY] + gs_p->c[RN];
1358 gs_p->c[AS] = staff_p->c[AY] + gs_p->c[RS];
1359
1360 /* if it's a group with notes, do the notes too */
1361 if (gs_p->grpcont == GC_NOTES) {
1362 for (n = 0; n < gs_p->nnotes; n++) {
1363 gs_p->notelist[n].c[AY] = staff_p->c[AY]
1364 + gs_p->notelist[n].c[RY];
1365 gs_p->notelist[n].c[AN] = staff_p->c[AY]
1366 + gs_p->notelist[n].c[RN];
1367 gs_p->notelist[n].c[AS] = staff_p->c[AY]
1368 + gs_p->notelist[n].c[RS];
1369 }
1370 }
1371 }
1372 }
1373
1374 /* do the verse(s) */
1375 for (v = 0; v < staff_p->nsyllists; v++) {
1376 for (gs_p = staff_p->syls_p[v]; gs_p != 0; gs_p = gs_p->next){
1377 gs_p->c[AY] = staff_p->c[AY] + gs_p->c[RY];
1378 gs_p->c[AN] = staff_p->c[AY] + gs_p->c[RN];
1379 gs_p->c[AS] = staff_p->c[AY] + gs_p->c[RS];
1380 }
1381 }
1382
1383 /* do the stuff */
1384 for (stuff_p = staff_p->stuff_p; stuff_p != 0; stuff_p = stuff_p->next){
1385 stuff_p->c[AY] = staff_p->c[AY] + stuff_p->c[RY];
1386 stuff_p->c[AN] = staff_p->c[AY] + stuff_p->c[RN];
1387 stuff_p->c[AS] = staff_p->c[AY] + stuff_p->c[RS];
1388
1389 /* if it's a phrase/tie/slur, do the phrase points too */
1390 if (stuff_p->stuff_type == ST_PHRASE ||
1391 stuff_p->stuff_type == ST_TIESLUR ||
1392 stuff_p->stuff_type == ST_TABSLUR ||
1393 stuff_p->stuff_type == ST_BEND) {
1394 for (pp_p = stuff_p->crvlist_p; pp_p != 0;
1395 pp_p = pp_p->next)
1396 pp_p->y += staff_p->c[AY];
1397 }
1398 }
1399}
1400\f
1401/*
1402 * Name: grids_atend()
1403 *
1404 * Abstract: Determine placement of chord grids to be printed at the end.
1405 *
1406 * Returns: height of all the grids printed on this page
1407 *
1408 * Description: This function determines the placement of chord grids that are
1409 * to be printed at the end of the song, and sets up the data in
1410 * Atend_info accordingly.
1411 */
1412
1413static double
1414grids_atend(vertavail, firstpage, mfeed_p, gfeed_p)
1415
1416double vertavail; /* space available for grids and spreading out scores*/
1417int firstpage; /* is this first page (there's only 1 page of music)?*/
1418struct FEED *mfeed_p; /* FEED at start of last music page */
1419struct FEED *gfeed_p; /* FEED applying to grid-only pages (may be same) */
1420
1421{
1422 struct GRID *grid_p; /* point at a grid */
1423 int ngrids; /* no. of grids used */
1424 float north, south, east, west; /* coords for one grid */
1425 float farnorth, farsouth, fareast, farwest; /* farthest for any grid */
1426 float hstrwid; /* half the width of chord string */
1427 float havail; /* horizonal space available */
1428 int inrow; /* no. of grids in one row */
1429 int nrows; /* no. of rows of grids */
1430 float totalheight; /* of all the rows */
1431 float white; /* scorepad in inches */
1432 float upheight; /* height of header + top */
1433 float downheight; /* height of bottom + footer */
1434
1435
1436 debug(32, "grids_atend vertavail=%f", (float)vertavail);
1437
1438 /* malloc array of pointers to the grids that were used */
1439 MALLOCA(struct GRID *, Atend_info.grid_p, Atend_info.grids_used);
1440
1441 /*
1442 * Set pointers to the grids that were used. While doing this, find
1443 * the farthest extent of any grid, for each of the 4 directions. The
1444 * size of the chord string must also be considered in this.
1445 */
1446 ngrids = 0;
1447 farnorth = farsouth = fareast = farwest = 0.0;
1448 for (grid_p = 0; (grid_p = nextgrid(grid_p)) != 0; ) {
1449 if (grid_p->used == NO)
1450 continue;
1451 Atend_info.grid_p[ngrids++] = grid_p;
1452 gridsize(grid_p, -1, &north, &south, &east, &west);
1453 north += strheight(grid_p->name);
1454 hstrwid = strwidth(grid_p->name) / 2.0;
1455 if (north > farnorth)
1456 farnorth = north;
1457 if (south < farsouth)
1458 farsouth = south;
1459 if (hstrwid > east)
1460 east = hstrwid;
1461 if (east > fareast)
1462 fareast = east;
1463 if (-hstrwid < west)
1464 west = -hstrwid;
1465 if (west < farwest)
1466 farwest = west;
1467 }
1468
1469 /* sort the pointers by grid name */
1470 qsort((char *)Atend_info.grid_p, ngrids, sizeof (struct GRID *),
1471 compgrids);
1472
1473 /* horizontal available width to use */
1474 havail = PGWIDTH - eff_leftmargin((struct MAINLL *)0)
1475 - eff_rightmargin((struct MAINLL *)0);
1476
1477 /*
1478 * Find max we could put in one row, allowing padding. Note that we do
1479 * not try to optimize the packing at all: the biggest grid coord in
1480 * any direction is what we use. The "padding" to the right of the
1481 * rightmost grid is not needed, so let it hang into the margin.
1482 */
1483 inrow = (havail + HPADGRID) / (fareast - farwest + HPADGRID);
1484 if (inrow == 0) {
1485 ufatal("chord grid is too wide to fit on a page");
1486 }
1487
1488 /* this determines how many rows there will be; it will not change */
1489 nrows = (ngrids + inrow - 1) / inrow;
1490
1491 /*
1492 * It could be that the last row would be far from full. So attempt to
1493 * spread the grids more equally between rows.
1494 */
1495 while (nrows > 1 && inrow > 1) {
1496 inrow--; /* try one less grid per row */
1497 if ((ngrids + inrow - 1) / inrow > nrows) {
1498 /* whoops, no. of rows increased, so undo last decr. */
1499 inrow++;
1500 break;
1501 }
1502 }
1503
1504 Atend_info.grids_per_row = inrow;
1505
1506 /* spread them out appropriately */
1507 Atend_info.horz_sep = havail / (nrows == 1 ? ngrids : inrow);
1508
1509 /*
1510 * Normally, the first grid's X is as far from the left margin as the
1511 * last (on that line) grid's X is from the right margin. But if any
1512 * grids have "N fr", fareast may be bigger than -farwest. So move
1513 * everything to the left by half the difference.
1514 */
1515 Atend_info.firstgrid_x = eff_leftmargin((struct MAINLL *)0) +
1516 Atend_info.horz_sep / 2.0 - (fareast + farwest) / 2.0;
1517
1518 /*
1519 * Base the vertical separation on the maximum case plus padding. Of
1520 * course, no padding is needed below the bottom row, so subtract it.
1521 */
1522 Atend_info.vert_sep = farnorth - farsouth + VPADGRID;
1523 totalheight = nrows * Atend_info.vert_sep - VPADGRID;
1524
1525 white = Score.minscpad * STEPSIZE;
1526
1527 if (totalheight <= vertavail && gfeed_p->pagefeed == NO) {
1528 /*
1529 * It fits on the last page of music. Set the absolute coord
1530 * so that it rests above the footer and/or bottom block (if
1531 * any) and bottom margin.
1532 */
1533 Atend_info.firstgrid_y = EFF_BOTMARGIN + totalheight - farnorth;
1534
1535 downheight = (firstpage == YES ? &Footer : &Footer2)->height +
1536 (mfeed_p->bot_p != 0 ? mfeed_p->bot_p->height : 0.0);
1537 if (downheight > 0) {
1538 Atend_info.firstgrid_y += downheight + white;
1539 }
1540
1541 Atend_info.rows_per_page = nrows;
1542
1543 return (totalheight);
1544 }
1545
1546 /*
1547 * All grids must go on later page(s). Find how much height must be
1548 * reserved for header/top and bottom/footer on those pages. Since
1549 * this cannot be the first page, we always use Header2 and Footer2.
1550 */
1551 upheight = Header2.height +
1552 (gfeed_p->top_p != 0 ? gfeed_p->top_p->height : 0.0);
1553 downheight = Footer2.height +
1554 (gfeed_p->bot_p != 0 ? gfeed_p->bot_p->height : 0.0);
1555
1556 /* make the grid page FEED a pagefeed, in case it isn't already */
1557 gfeed_p->pagefeed = YES;
1558
1559 /*
1560 * It will have to go on other page(s). Set the absolute coord to put
1561 * it at the top.
1562 */
1563 Atend_info.separate_page = YES;
1564 Atend_info.firstgrid_y = PGHEIGHT - EFF_TOPMARGIN -
1565 upheight - farnorth;
1566 if (upheight > 0) {
1567 Atend_info.firstgrid_y -= white;
1568 }
1569
1570 /* reset vertavail to the amount of space on a whole page */
1571 vertavail = PGHEIGHT - EFF_TOPMARGIN - EFF_BOTMARGIN;
1572 if (upheight > 0)
1573 vertavail -= upheight + white;
1574 if (downheight > 0)
1575 vertavail -= downheight + white;
1576
1577 /* find number of rows per page; must be at least 1 */
1578 Atend_info.rows_per_page = (vertavail + VPADGRID) / Atend_info.vert_sep;
1579 if (Atend_info.rows_per_page == 0)
1580 ufatal("chords grids are too high to fit on a page");
1581
1582 /*
1583 * If there is at least 1 full page, spread the rows out evenly. The
1584 * same spacing will be used on later pages, even though the last page
1585 * may not be full. That's okay.
1586 */
1587 if (nrows >= Atend_info.rows_per_page) {
1588 Atend_info.vert_sep = (vertavail + VPADGRID) /
1589 Atend_info.rows_per_page;
1590 }
1591
1592 return (0.0); /* nothing goes on the last page of music */
1593}
1594\f
1595/*
1596 * Name: compgrids()
1597 *
1598 * Abstract: Compare grid names; used by qsort.
1599 *
1600 * Returns: negative or positive
1601 *
1602 * Description: This function returns its result based on whether the grid
1603 * pointed to by g1_p should precede or follow g2_p. It uses
1604 * their names in alphabetical order, basically, but it also
1605 * understands accidentals. They will never be equal because the
1606 * grids are all unique.
1607 */
1608
1609static int
1610compgrids(g1_p_p, g2_p_p)
1611
1612#ifdef __STDC__
1613const void *g1_p_p; /* the two grid pointers to compare */
1614const void *g2_p_p;
1615#else
1616char *g1_p_p; /* the two grid pointers to compare */
1617char *g2_p_p;
1618#endif
1619
1620{
1621 char *name[2]; /* pointers into first and second names */
1622 char *asc_ptr; /* point at the first name in ASCII */
1623 char chbuff[MAXCHNAME]; /* hold the ASCII name of the first chord */
1624 int accnum[2]; /* accidental number, -2 to 2 (&& to x) */
1625 int ridx[2]; /* index to rest of string */
1626 int k; /* loop variable */
1627
1628
1629 /*
1630 * Translate the chords names to the way the user entered them (as
1631 * closely as possible). Since ascii_str() overwrites the same static
1632 * area each time, we have to copy the first name to our own buffer.
1633 * Rather than wasting time using malloc(), just put it in a fixed
1634 * buffer. If someone has an absurd name longer than MAXCHNAME, just
1635 * cut it off.
1636 */
1637 asc_ptr = ascii_str((*(struct GRID **)g1_p_p)->name, YES, NO, TM_CHORD);
1638 if ((int)strlen(asc_ptr) < MAXCHNAME) {
1639 (void)strcpy(chbuff, asc_ptr);
1640 } else {
1641 (void)strncpy(chbuff, asc_ptr, MAXCHNAME - 1);
1642 chbuff[MAXCHNAME - 1] = '\0';
1643 }
1644 name[0] = chbuff;
1645 name[1] = ascii_str((*(struct GRID **)g2_p_p)->name, YES, NO, TM_CHORD);
1646
1647 /*
1648 * If chord letters differ, return based on that. For bizarre cases
1649 * like letters not A through G, or null string, que sera sera.
1650 */
1651 if (name[0][0] != name[1][0])
1652 return (name[0][0] - name[1][0]);
1653
1654 /*
1655 * The first chars (presumably chord letters) were the same. They
1656 * can't be \0 because then the whole strings would be equal (null
1657 * string) but we know chord names are unique. For each name, set a
1658 * number for its accidental, and index to what follows, if anything.
1659 */
1660 for (k = 0; k < 2; k++) {
1661 switch (name[k][1]) {
1662 case '&':
1663 if (name[k][2] == '&') {
1664 accnum[k] = -2; /* double flat */
1665 ridx[k] = 3;
1666 } else {
1667 accnum[k] = -1; /* flat */
1668 ridx[k] = 2;
1669 }
1670 break;
1671 case '#':
1672 accnum[k] = 1; /* sharp */
1673 ridx[k] = 2;
1674 break;
1675 case 'x':
1676 accnum[k] = 2; /* double sharp */
1677 ridx[k] = 2;
1678 break;
1679 default:
1680 accnum[k] = 0; /* no acc is like a natural */
1681 ridx[k] = 1;
1682 break;
1683 }
1684 }
1685
1686 /* if accidentals differ, that rules */
1687 if (accnum[0] != accnum[1])
1688 return (accnum[0] - accnum[1]);
1689
1690 /* else the rest of it decides */
1691 return (strcmp(&name[0][ridx[0]], &name[1][ridx[1]]));
1692}
1693\f
1694/*
1695 * Name: proc_css()
1696 *
1697 * Abstract: Process groups involved with cross staff stemming.
1698 *
1699 * Returns: void
1700 *
1701 * Description: This function does all the remaining work necessary for groups
1702 * involved in cross staff stemming.
1703 */
1704
1705static void
1706proc_css()
1707
1708{
1709 struct MAINLL *mainll_p; /* point along main LL */
1710 struct MAINLL *prevvis_p; /* previous visible staff */
1711 struct MAINLL *nextvis_p; /* next visible staff */
1712 struct TIMEDSSV *tssv_p; /* point along a timed SSV list */
1713 struct STAFF *thisstaff_p; /* point at a staff */
1714 struct GRPSYL *thisg_p; /* point at a group */
1715 struct STUFF *stuff_p; /* point at a stuff structure */
1716 struct CRVLIST *pp_p; /* point at a coord for phrase point */
1717 RATIONAL vtime; /* start time of groups */
1718 int vidx; /* voice index */
1719
1720
1721 debug(16, "proc_css");
1722 initstructs(); /* clean out old SSV info */
1723
1724 /*
1725 * Loop through the whole MLL, looking for visible staffs, and keeping
1726 * SSVs up to date (including midmeasure SSVs, since CSS notes are
1727 * affected by clef changes).
1728 */
1729 prevvis_p = 0;
1730 for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
1731
1732 switch (mainll_p->str) {
1733 case S_STAFF:
1734 thisstaff_p = mainll_p->u.staff_p;
1735 /* if staff is invisible, skip it */
1736 if (thisstaff_p->visible == NO) {
1737 continue;
1738 }
1739 break; /* go handle this visible staff */
1740 case S_SSV:
1741 /* assign normal SSV */
1742 asgnssv(mainll_p->u.ssv_p);
1743 continue;
1744 case S_BAR:
1745 /* assign preceding measure's timed SSVs */
1746 for (tssv_p = mainll_p->u.bar_p->timedssv_p;
1747 tssv_p != 0;
1748 tssv_p = tssv_p->next) {
1749 asgnssv(&tssv_p->ssv);
1750 }
1751 /* FALL THROUGH */
1752 default:
1753 /* set prev to null in preparation for next measure */
1754 prevvis_p = 0;
1755 continue;
1756 }
1757
1758 /* look for next visible staff, skipping invisible */
1759 for (nextvis_p = mainll_p->next; nextvis_p != 0 &&
1760 nextvis_p->str == S_STAFF &&
1761 nextvis_p->u.staff_p->visible == NO;
1762 nextvis_p = nextvis_p->next) {
1763 ;
1764 }
1765 /* if no more visible staffs in score, set next to null */
1766 if (nextvis_p != 0 && nextvis_p->str != S_STAFF) {
1767 nextvis_p = 0;
1768 }
1769
1770 /*
1771 * thisstaff_p is a visible staff, and prevvis_p and nextvis_p
1772 * are the MLL structs for the previous and next visible staffs,
1773 * if they exist. Loop through the voices on the this staff.
1774 */
1775 for (vidx = 0; vidx < MAXVOICES; vidx++) {
1776 /*
1777 * Loop through the groups of this voice, keeping track
1778 * of the elapsed time, looking for groups that have
1779 * CSS, and calling one_css() for them.
1780 */
1781 vtime = Zero;
1782 for (thisg_p = thisstaff_p->groups_p[vidx]; thisg_p !=0;
1783 vtime = radd(vtime, thisg_p->fulltime),
1784 thisg_p = thisg_p->next) {
1785
1786 switch (thisg_p->stemto) {
1787 case CS_SAME:
1788 continue;
1789 case CS_ABOVE:
1790 if (prevvis_p == 0) {
1791 l_ufatal(mainll_p->inputfile,
1792 mainll_p->inputlineno,
1793 "cannot cross staff stem 'with above' from top visible staff");
1794 }
1795 one_css(thisstaff_p,
1796 prevvis_p->u.staff_p,
1797 thisg_p, vtime);
1798 break;
1799 case CS_BELOW:
1800 if (nextvis_p == 0) {
1801 l_ufatal(mainll_p->inputfile,
1802 mainll_p->inputlineno,
1803 "cannot cross staff stem 'with below' from bottom visible staff");
1804 }
1805 one_css(thisstaff_p,
1806 nextvis_p->u.staff_p,
1807 thisg_p, vtime);
1808 break;
1809 }
1810 }
1811 }
1812
1813 prevvis_p = mainll_p;
1814 }
1815
1816 /*
1817 * Now we have to call beamstem() again, to do the work that it
1818 * couldn't do before on groups affected by CSS.
1819 */
1820 CSSpass = YES;
1821 beamstem();
1822
1823 /*
1824 * Do "horizontal avoidance": moving CSS groups sideways if necessary
1825 * because they would collide with groups on the other staff.
1826 */
1827 horzavoid();
1828
1829 /*
1830 * Back in relvert.c, we skipped placing tie/slur/bend/phrases whose
1831 * endpoint groups were affected by CSS. Now that we know where the
1832 * final group boundaries are, we set up the coords for these items.
1833 * tieslur_points and phrase_points destroy groups' AN and AS, and
1834 * depends on them starting out as zero. So zero them now and restore
1835 * them later. Because these items can cross bar lines, we need
1836 * to zap all of these coords in this first loop, and have a separate
1837 * loop to do the main work (and restore the groups' coords).
1838 */
1839 for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
1840 if (mainll_p->str != S_STAFF) {
1841 continue;
1842 }
1843 thisstaff_p = mainll_p->u.staff_p;
1844
1845 for (vidx = 0; vidx < MAXVOICES; vidx++) {
1846 for (thisg_p = thisstaff_p->groups_p[vidx];
1847 thisg_p != 0; thisg_p = thisg_p->next) {
1848 thisg_p->c[AN] = 0.0;
1849 thisg_p->c[AS] = 0.0;
1850 }
1851 }
1852 }
1853
1854 for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
1855 if (mainll_p->str != S_STAFF) {
1856 continue;
1857 }
1858 thisstaff_p = mainll_p->u.staff_p;
1859
1860 /*
1861 * Find and handle every tie/slur/bend/phrase starting in this
1862 * staff.
1863 */
1864 for (stuff_p = thisstaff_p->stuff_p;
1865 stuff_p != 0; stuff_p = stuff_p->next) {
1866 switch (stuff_p->stuff_type) {
1867 case ST_PHRASE:
1868 if (css_affects_phrase(stuff_p,
1869 mainll_p) == YES) {
1870 phrase_points(mainll_p, stuff_p);
1871
1872 stuff_p->c[AY] = thisstaff_p->c[AY]
1873 + stuff_p->c[RY];
1874 stuff_p->c[AN] = thisstaff_p->c[AY]
1875 + stuff_p->c[RN];
1876 stuff_p->c[AS] = thisstaff_p->c[AY]
1877 + stuff_p->c[RS];
1878
1879 /* do the phrase points too */
1880 for (pp_p = stuff_p->crvlist_p;
1881 pp_p != 0; pp_p = pp_p->next) {
1882
1883 pp_p->y += thisstaff_p->c[AY];
1884 }
1885 }
1886 break;
1887 case ST_TIESLUR:
1888 case ST_BEND:
1889 if (css_affects_tieslurbend(stuff_p,
1890 mainll_p) == YES) {
1891 if (stuff_p->stuff_type == ST_TIESLUR) {
1892 tieslur_points(mainll_p, stuff_p);
1893 } else {
1894 bend_points(mainll_p, stuff_p);
1895 }
1896
1897 stuff_p->c[AY] = thisstaff_p->c[AY]
1898 + stuff_p->c[RY];
1899 stuff_p->c[AN] = thisstaff_p->c[AY]
1900 + stuff_p->c[RN];
1901 stuff_p->c[AS] = thisstaff_p->c[AY]
1902 + stuff_p->c[RS];
1903
1904 /* do the tie/slur/bend points too */
1905 for (pp_p = stuff_p->crvlist_p;
1906 pp_p != 0; pp_p = pp_p->next) {
1907
1908 pp_p->y += thisstaff_p->c[AY];
1909 }
1910 }
1911 break;
1912 }
1913 }
1914
1915 /*
1916 * phrase_points destroys groups' AN and AS. And some code in
1917 * the second pass of beamstem.c doesn't set the absolute
1918 * coords of groups. So go through now and set the absolute
1919 * coords of all groups.
1920 */
1921 for (vidx = 0; vidx < MAXVOICES; vidx++) {
1922 for (thisg_p = thisstaff_p->groups_p[vidx];
1923 thisg_p != 0; thisg_p = thisg_p->next) {
1924 thisg_p->c[AN] = thisstaff_p->c[AY]
1925 + thisg_p->c[RN];
1926 thisg_p->c[AY] = thisstaff_p->c[AY]
1927 + thisg_p->c[RY];
1928 thisg_p->c[AS] = thisstaff_p->c[AY]
1929 + thisg_p->c[RS];
1930 }
1931 }
1932 }
1933}
1934\f
1935/*
1936 * Name: one_css()
1937 *
1938 * Abstract: Process one group involved with cross staff stemming.
1939 *
1940 * Returns: void
1941 *
1942 * Description: This function processes one CSS group. It moves the CSS notes
1943 * in the group to fall into the correct place on the other staff.
1944 * When necessary, it also adjusts the group boundary.
1945 */
1946
1947static void
1948one_css(ts_p, os_p, tg_p, time)
1949
1950struct STAFF *ts_p; /* This Staff, the normal one for the grpsyl */
1951struct STAFF *os_p; /* Other Staff that the grpsyl has notes on */
1952struct GRPSYL *tg_p; /* This Grpsyl */
1953RATIONAL time; /* time offset of this grpsyl */
1954
1955{
1956 struct GRPSYL *og_p; /* Other Grpsyl (some grpsyl on other staff) */
1957 int foundclef; /* found a clef change on other staff? */
1958 RATIONAL cleftime; /* time at which the last clef change happens*/
1959 RATIONAL tt; /* temporary time variable */
1960 float offset; /* distance from old note position to new */
1961 int upfromc4; /* steps up from middle C */
1962 int clef; /* clef in force on other staff */
1963 int vidx; /* voice index */
1964 int n; /* loop variable */
1965
1966
1967 /*
1968 * Set globals like Staffscale according our staff. The parse phase
1969 * ensures that the two staffs have the same staffscale.
1970 */
1971 set_staffscale(ts_p->staffno);
1972
1973 /*
1974 * We need to find out what clef is in force on the other staff. We
1975 * start with the current value; but it may change midmeasure. We
1976 * can't just use the timed SSVs, because there are weird cases
1977 * where the clef got put farther to the right (because the clef was
1978 * changed before rests or spaces). So we have to search all the
1979 * voices for clefs. We look for the rightmost clef that does not
1980 * exceed the given time value.
1981 */
1982 /* find clef in force on other staff at start of this measure */
1983 clef = svpath(os_p->staffno, CLEF)->clef;
1984 foundclef = NO;
1985 cleftime = Zero;
1986 for (vidx = 0; vidx < MAXVOICES; vidx++) {
1987 tt = Zero;
1988 for (og_p = os_p->groups_p[vidx]; og_p != 0 && LE(tt, time);
1989 og_p = og_p->next) {
1990 /* if group has a clef, and either it's the first group
1991 * found to have one or it's later than the latest such
1992 * group found so far . . . */
1993 if (og_p->clef != NOCLEF &&
1994 (foundclef == NO || GT(tt, cleftime))) {
1995 foundclef = YES;
1996 clef = og_p->clef; /* remember this clef*/
1997 cleftime = tt; /* and when it was */
1998 }
1999 tt = radd(tt, og_p->fulltime);
2000 }
2001 }
2002
2003 /*
2004 * Everything that has to move will move by the same offset. Calculate
2005 * it, using the first CSS note. First find its stepsup on the new
2006 * staff, like setnotes.c does for the normal staff. Subtract new
2007 * minus old vertical positions.
2008 */
2009 n = FCNI(tg_p);
2010 upfromc4 = (tg_p->notelist[n].octave - 4) * 7 +
2011 Letshift[ tg_p->notelist[n].letter - 'a' ];
2012 tg_p->notelist[n].stepsup = upfromc4 + clef - ALTO;
2013 offset = (os_p->c[AY] + tg_p->notelist[n].stepsup * Stepsize) -
2014 tg_p->notelist[n].c[AY];
2015
2016 /* move all the CSS notes and their dots */
2017 for ( ; n <= LCNI(tg_p); n++) {
2018 upfromc4 = (tg_p->notelist[n].octave - 4) * 7 +
2019 Letshift[ tg_p->notelist[n].letter - 'a' ];
2020 tg_p->notelist[n].stepsup = upfromc4 + clef - ALTO;
2021 tg_p->notelist[n].c[RN] += offset;
2022 tg_p->notelist[n].c[RY] += offset;
2023 tg_p->notelist[n].c[RS] += offset;
2024 tg_p->notelist[n].c[AN] += offset;
2025 tg_p->notelist[n].c[AY] += offset;
2026 tg_p->notelist[n].c[AS] += offset;
2027 if (tg_p->dots > 0) {
2028 tg_p->notelist[n].ydotr += offset;
2029 }
2030 }
2031
2032 /*
2033 * If the CSS note(s) were not on the stemside, stemlen and group
2034 * boundaries were set already in beamstem.c, but we need to fix them
2035 * here to account for moving the CSS notes.
2036 */
2037 if (STEMSIDE_CSS(tg_p) == NO) {
2038 if (tg_p->stemlen != 0.0) {
2039 tg_p->stemlen += fabs(offset);
2040 }
2041 if (tg_p->stemdir == UP) {
2042 tg_p->c[RS] = tg_p->notelist[tg_p->nnotes - 1].c[RS]
2043 - Stdpad;
2044 tg_p->c[AS] = tg_p->notelist[tg_p->nnotes - 1].c[AS]
2045 - Stdpad;
2046 } else {
2047 tg_p->c[RN] = tg_p->notelist[0].c[RN] + Stdpad;
2048 tg_p->c[AN] = tg_p->notelist[0].c[AN] + Stdpad;
2049 }
2050 }
2051}
2052\f
2053/*
2054 * Name: horzavoid()
2055 *
2056 * Abstract: Move CSS groups horizontally to avoid collisions on other staff.
2057 *
2058 * Returns: void
2059 *
2060 * Description: This function goes through the MLL, and for each CSS group,
2061 * calls a function to do horizontal avoidance.
2062 */
2063
2064static void
2065horzavoid()
2066
2067{
2068 struct MAINLL *mainll_p; /* point along main LL */
2069 struct GRPSYL *gs_p; /* point at a group */
2070 int vidx; /* voice index */
2071 RATIONAL time; /* start time of a group */
2072
2073
2074 for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
2075 if (mainll_p->str != S_STAFF) {
2076 continue;
2077 }
2078
2079 for (vidx = 0; vidx < MAXVOICES; vidx++) {
2080 time = Zero;
2081 for (gs_p = mainll_p->u.staff_p->groups_p[vidx];
2082 gs_p != 0; gs_p = gs_p->next) {
2083 if (gs_p->stemto != CS_SAME) {
2084 avoidone(mainll_p, gs_p, time);
2085 }
2086 time = radd(time, gs_p->fulltime);
2087 }
2088 }
2089 }
2090}
2091\f
2092/*
2093 * Name: avoidone()
2094 *
2095 * Abstract: Move CSS group horizontally to avoid collisions on other staff.
2096 *
2097 * Returns: void
2098 *
2099 * Description: This function finds whether the given group collides with any
2100 * groups on the other staff. If so, it moves that group, along
2101 * with all other groups on its staff and their preceding grace
2102 * groups, to the right enough so that the group no longer
2103 * collides. But it won't move it so far that it would collide
2104 * with a later group on its own staff.
2105 */
2106
2107static void
2108avoidone(mainll_p, cssg_p, time)
2109
2110struct MAINLL *mainll_p; /* the MLL for our group's staff */
2111struct GRPSYL *cssg_p; /* the CSS group we are working on */
2112RATIONAL time; /* time offset of this group */
2113
2114{
2115 struct MAINLL *mll_p; /* point along main LL */
2116 int otherstaffno; /* staff where the CSS notes are */
2117 struct GRPSYL *gs_p; /* point along grpsyl lists */
2118 struct GRPSYL *gs2_p; /* another pointer along grpsyl lists */
2119 struct CHORD *ch_p; /* point at chord we're in */
2120 float movedist; /* distance to move groups */
2121 float otherhorz; /* east boundary of groups on other staff */
2122 float slope; /* slope of a beam */
2123 float deltax; /* change in X coord of stem tip */
2124 int gotone; /* flag variable */
2125 int n; /* loop variable */
2126
2127
2128 /* never move the group if the user is forcing it with "ho" */
2129 if (cssg_p->ho_usage != HO_NONE) {
2130 return;
2131 }
2132
2133 /*
2134 * Find the other staff's number.
2135 */
2136 if (cssg_p->stemto == CS_ABOVE) {
2137 for (mll_p = mainll_p->prev; mll_p != 0 && mll_p->str == S_STAFF
2138 && mll_p->u.staff_p->visible == NO; mll_p = mll_p->prev) {
2139 ;
2140 }
2141 } else {
2142 for (mll_p = mainll_p->next; mll_p != 0 && mll_p->str == S_STAFF
2143 && mll_p->u.staff_p->visible == NO; mll_p = mll_p->next) {
2144 ;
2145 }
2146 }
2147 if (mll_p == 0 || mll_p->str != S_STAFF) {
2148 pfatal("missing staff in avoidone");
2149 }
2150 otherstaffno = mll_p->u.staff_p->staffno;
2151
2152 /*
2153 * Find what groups, if any, the other staff has at this time value.
2154 * First we find the GPRSYL at which the search begins.
2155 */
2156 if (cssg_p->stemto == CS_ABOVE) {
2157 /*
2158 * We will start the search at this first grpsyl in the chord.
2159 */
2160 ch_p = gs2ch(mainll_p, cssg_p);
2161 gs_p = ch_p->gs_p;
2162 } else {
2163 /*
2164 * We will start the search at our group, or if it is grace,
2165 * the main group that follows.
2166 */
2167 for (gs_p = cssg_p; gs_p->grpvalue == GV_ZERO;
2168 gs_p = gs_p->next) {
2169 ;
2170 }
2171 ch_p = 0; /* remember we don't know the chord */
2172 }
2173
2174 /* find the first GRPSYL, if any, on the other staff at this time */
2175 for ( ; gs_p != 0 && gs_p->staffno < otherstaffno; gs_p = gs_p->gs_p) {
2176 ;
2177 }
2178
2179 /* if no groups on the other staff, there is no need to move anything */
2180 if (gs_p == 0 || gs_p->grpsyl == GS_SYLLABLE ||
2181 gs_p->staffno > otherstaffno) {
2182 return;
2183 }
2184
2185 /*
2186 * Find the easternmost extent of any group on the other staff that
2187 * extends far enough vertically to run into our group. We don't care
2188 * about grace groups, because they are on the west side, and we are
2189 * going to move our group to the east side.
2190 */
2191 gotone = NO;
2192 otherhorz = 0.0; /* avoid "used before set" warning */
2193 for ( ; gs_p != 0 && gs_p->grpsyl == GS_GROUP &&
2194 gs_p->staffno == otherstaffno; gs_p = gs_p->gs_p) {
2195 /* spaces never interfere; mr and mrpt rarely do, and their
2196 * coords make them seem really wide, so ignore them too */
2197 if (gs_p->grpcont == GC_SPACE || gs_p->is_meas == YES) {
2198 continue;
2199 }
2200 if (cssg_p->stemto == CS_ABOVE && cssg_p->c[AN] <= gs_p->c[AS]){
2201 continue;
2202 }
2203 if (cssg_p->stemto == CS_BELOW && cssg_p->c[AS] >= gs_p->c[AN]){
2204 continue;
2205 }
2206 if (gotone == NO || gs_p->c[AE] > otherhorz) {
2207 otherhorz = gs_p->c[AE];
2208 gotone = YES;
2209 }
2210 }
2211
2212 /*
2213 * If our group doesn't reach the other staff's groups vertically,
2214 * there is no need to move anything.
2215 */
2216 if (gotone == NO) {
2217 return;
2218 }
2219
2220 /*
2221 * Find how far we'd need to move our group to the right to be beyond
2222 * any of the other staff's groups. If somehow that is not positive,
2223 * there is no need to move.
2224 */
2225 movedist = otherhorz - cssg_p->c[AW];
2226 if (movedist <= 0.0) {
2227 return;
2228 }
2229
2230 /* find the first nongrace group at this time on our staff */
2231 if (cssg_p->vno == 1) {
2232 for (gs_p = cssg_p; gs_p->grpvalue == GV_ZERO;
2233 gs_p = gs_p->next) {
2234 ;
2235 }
2236 } else {
2237 if (ch_p == 0) {
2238 ch_p = gs2ch(mainll_p, cssg_p);
2239 }
2240 /* find the first GRPSYL, if any, on our staff at this time */
2241 for (gs_p = ch_p->gs_p; gs_p != 0 && gs_p->staffno <
2242 cssg_p->staffno; gs_p = gs_p->gs_p) {
2243 ;
2244 }
2245 }
2246
2247 /*
2248 * For each group on this staff in this chord, and for all their
2249 * preceding grace groups, move them to the east. Adjust stem lengths
2250 * of beamed groups.
2251 */
2252 for ( ; gs_p != 0 && gs_p->grpsyl == GS_GROUP &&
2253 gs_p->staffno == cssg_p->staffno; gs_p = gs_p->gs_p) {
2254
2255 /* never move the group if the user is forcing it with "ho" */
2256 if (gs_p->ho_usage != HO_NONE) {
2257 continue;
2258 }
2259
2260 /*
2261 * If the group is beamed and the beam is not horizontal, the
2262 * stem length needs to be changed so it will meet the beam.
2263 */
2264 if (gs_p->beamloc != NOITEM && gs_p->grpcont == GC_NOTES) {
2265 /*
2266 * Find a neighboring group in the beamed set so we can
2267 * find the beam's slope. The prev group is already
2268 * corrected; our group and the next group haven't been
2269 * moved yet; so the stems of all 3 are currently
2270 * touching the beam and are valid for finding slope.
2271 */
2272 if (gs_p->beamloc == STARTITEM) {
2273 gs2_p = nextsimilar(gs_p);
2274 } else {
2275 gs2_p = prevsimilar(gs_p);
2276 }
2277 slope = (find_y_stem(gs2_p) - find_y_stem(gs_p)) /
2278 (find_x_stem(gs2_p) - find_x_stem(gs_p));
2279
2280 deltax = slope * movedist;
2281
2282 if (gs_p->stemdir == UP) {
2283 gs_p->stemlen += deltax;
2284 gs_p->c[RN] += deltax;
2285 gs_p->c[AN] += deltax;
2286 } else {
2287 gs_p->stemlen -= deltax;
2288 gs_p->c[RS] += deltax;
2289 gs_p->c[AS] += deltax;
2290 }
2291 }
2292
2293 /*
2294 * Always do our group (a nongrace group), then loop
2295 * additionally for all preceding graces.
2296 */
2297 gs2_p = gs_p;
2298 do {
2299 gs2_p->c[AW] += movedist;
2300 gs2_p->c[AX] += movedist;
2301 gs2_p->c[AE] += movedist;
2302
2303 /* if it's a group with notes, do the notes too */
2304 if (gs2_p->grpcont == GC_NOTES) {
2305 for (n = 0; n < gs2_p->nnotes; n++) {
2306 gs2_p->notelist[n].c[AW] += movedist;
2307 gs2_p->notelist[n].c[AX] += movedist;
2308 gs2_p->notelist[n].c[AE] += movedist;
2309 }
2310 }
2311
2312 gs2_p = gs2_p->prev;
2313 } while (gs2_p != 0 && gs2_p->grpvalue == GV_ZERO);
2314 }
2315}
2316\f
2317/*
2318 * Name: set_csb_stems()
2319 *
2320 * Abstract: Set stem lengths for groups involved in cross staff beaming.
2321 *
2322 * Returns: void
2323 *
2324 * Description: This function searches the MLL for cross staff beaming places.
2325 * For each one, it calls onecsb() to set the stem lengths.
2326 */
2327
2328static void
2329set_csb_stems()
2330
2331{
2332 struct MAINLL *mainll_p; /* point along main LL */
2333 struct MAINLL *mll_p; /* point along main LL again */
2334 struct STAFF *staff1_p, *staff2_p; /* point at top and bottom staffs */
2335 struct GRPSYL *gs1_p, *gs2_p; /* point at top and bottom groups */
2336 int v, bv; /* loop thru voices, top and bottom */
2337 RATIONAL vtime1, vtime2; /* start time of groups */
2338
2339
2340 debug(16, "set_csb_stems");
2341 initstructs(); /* clean out old SSV info */
2342
2343 /*
2344 * Loop through the whole MLL, looking for visible staffs that are
2345 * not the last visible staff in their score. Then find cross staff
2346 * beamings and call a function to set stem lengths.
2347 */
2348 for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
2349 /* apply SSVs to keep staffscale up to date */
2350 if (mainll_p->str == S_SSV) {
2351 asgnssv(mainll_p->u.ssv_p);
2352 continue;
2353 }
2354
2355 if (mainll_p->str != S_STAFF)
2356 continue;
2357
2358 /* if staff is invisible, skip it */
2359 staff1_p = mainll_p->u.staff_p;
2360 if (staff1_p->visible == NO)
2361 continue;
2362
2363 /* look for next visible staff, skipping invisible */
2364 for (mll_p = mainll_p->next; mll_p != 0 && mll_p->str ==
2365 S_STAFF && mll_p->u.staff_p->visible == NO;
2366 mll_p = mll_p->next)
2367 ;
2368 /* if no more visible staffs in score, skip */
2369 if (mll_p == 0 || mll_p->str != S_STAFF)
2370 continue;
2371
2372 staff2_p = mll_p->u.staff_p;
2373
2374 /*
2375 * staff1_p and staff2_p are two neighboring visible staffs
2376 * (possibly with invisible ones in between). Loop through the
2377 * voices on the top staff. For ones that don't exist, their
2378 * pointers will be 0 and the inside loop will do nothing.
2379 */
2380 for (v = 0; v < MAXVOICES; v++) {
2381 /*
2382 * Loop through the groups of this voice, keeping track
2383 * of the elapsed time, looking for the first group of
2384 * each CSB set that is joined with the staff below.
2385 * It could be any of the voices on the staff below.
2386 * The parser deals with any checks concerning voices
2387 * being in the way of each other.
2388 */
2389 vtime1 = Zero;
2390 for (gs1_p = staff1_p->groups_p[v]; gs1_p != 0;
2391 vtime1 = radd(vtime1, gs1_p->fulltime),
2392 gs1_p = gs1_p->next) {
2393
2394 if (gs1_p->beamto != CS_BELOW ||
2395 gs1_p->beamloc != STARTITEM)
2396 continue;
2397
2398 for (bv = 0; bv < MAXVOICES; bv++) {
2399 vtime2 = Zero;
2400 for (gs2_p = staff2_p->groups_p[bv];
2401 gs2_p != 0 &&
2402 (LT(vtime2, vtime1) ||
2403 gs2_p->grpvalue ==
2404 GV_ZERO);
2405 gs2_p = gs2_p->next) {
2406 vtime2 = radd(vtime2,
2407 gs2_p->fulltime);
2408 }
2409 if (gs2_p != 0 && EQ(vtime2, vtime1) &&
2410 gs2_p->beamto == CS_ABOVE &&
2411 gs2_p->beamloc == STARTITEM) {
2412
2413 onecsb(gs1_p, gs2_p);
2414 }
2415 }
2416 }
2417 }
2418 }
2419}
2420\f
2421/*
2422 * Name: onecsb()
2423 *
2424 * Abstract: Set stem lengths for one instance of cross staff beaming.
2425 *
2426 * Returns: void
2427 *
2428 * Description: This function finds the stem directions on the two staffs of
2429 * a CSB and the first and last groups of it that are note groups.
2430 * If the user didn't specify the stem lengths for those outer
2431 * groups (which determines the equation of the beams), it calls a
2432 * function to decide what the equation should be; otherwise it
2433 * finds the equation in-line. Then it sets all the groups' stem
2434 * lengths.
2435 */
2436
2437/*
2438 * Given the STARTITEM group of a CSB (whether notes or space), return the
2439 * first CSB group that is notes. Embedded grace groups are not part of CSB.
2440 */
2441#define FIRSTCSB(gs_p) (gs_p->grpcont == GC_NOTES ? gs_p : nextcsb(gs_p))
2442
2443static void
2444onecsb(start1_p, start2_p)
2445
2446struct GRPSYL *start1_p; /* first GRPSYL on top staff */
2447struct GRPSYL *start2_p; /* first GRPSYL on bottom staff */
2448
2449{
2450 struct GRPSYL *gs_p; /* point at a group */
2451 int topdir, botdir; /* stem directions of the two lists */
2452 struct GRPSYL *end1_p, *end2_p; /* ending group in each list */
2453 struct GRPSYL *first_p, *last_p;/* first and last note groups in CSB */
2454 float firstx, lastx; /* x coords of end of stems */
2455 float firsty, lasty; /* y coords of stems */
2456 float b0, b1; /* y intercept and slope of the beam */
2457 float stemshift; /* x distance of stem from center of note */
2458 float x; /* x coord of a stem */
2459 float outstem; /* the part of the stemlen outside notes of group */
2460 float hi; /* height of a "with" list item */
2461 int n; /* loop variable */
2462
2463
2464 /*
2465 * Set globals like Staffscale for use by the rest of the file. The
2466 * parse phase ensures that the two staffs have the same staffscale.
2467 */
2468 set_staffscale(start1_p->staffno);
2469
2470 topdir = botdir = UP; /* prevent useless 'used before set' warnings */
2471
2472 /*
2473 * Find stemdir of the top groups. (They will be consistent; that was
2474 * enforced in dobunch().) Set end1_p to the last group.
2475 */
2476 for (gs_p = FIRSTCSB(start1_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
2477 if (gs_p->grpcont == GC_NOTES)
2478 topdir = gs_p->stemdir;
2479 }
2480 for (end1_p = start1_p; end1_p != 0 && end1_p->beamloc != ENDITEM;
2481 end1_p = nextnongrace(end1_p))
2482 ;
2483 if (end1_p == 0)
2484 pfatal("no ENDITEM in beamed set (onecsb[1])");
2485
2486 /* do the same for the bottom groups */
2487 for (gs_p = FIRSTCSB(start2_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
2488 if (gs_p->grpcont == GC_NOTES)
2489 botdir = gs_p->stemdir;
2490 }
2491 for (end2_p = start2_p; end2_p != 0 && end2_p->beamloc != ENDITEM;
2492 end2_p = nextnongrace(end2_p))
2493 ;
2494 if (end2_p == 0)
2495 pfatal("no ENDITEM in beamed set (onecsb[2])");
2496
2497 if (topdir == UP && botdir == DOWN) {
2498 l_ufatal(start2_p->inputfile, start2_p->inputlineno,
2499 "when beaming across staffs, cannot have stems up on top staff and down on bottom");
2500 }
2501
2502 /*
2503 * Set first_p and last_p to the first and last note groups, whichever
2504 * staff(s) they are on.
2505 */
2506 first_p = start1_p->grpcont == GC_NOTES ? start1_p : start2_p;
2507 last_p = end1_p->grpcont == GC_NOTES ? end1_p : end2_p;
2508
2509 /*
2510 * Find half the width of a note head; the stems will need to be
2511 * shifted by that amount from the center of the notes so that they
2512 * will meet the edge of the notes properly.
2513 */
2514 stemshift = getstemshift(first_p);
2515
2516
2517 /*
2518 * The user must either specify a stem length for both first and last
2519 * groups, or neither. (The parse phase enforces that.) If neither,
2520 * call a function to determine a line for a beam. It sets b0 and b1
2521 * for that line.
2522 */
2523 if (IS_STEMLEN_UNKNOWN(first_p->stemlen) ||
2524 IS_STEMLEN_UNKNOWN(last_p->stemlen)) {
2525 /*
2526 * User did not provide both outer stem lengths. Find the best
2527 * line. But if the stemlen parm was zero, we get back "NO",
2528 * and we set all stems to zero.
2529 */
2530 if (calcline(start1_p, end1_p, start2_p, end2_p, first_p,
2531 last_p, topdir, botdir, &b0, &b1) == NO) {
2532 for (gs_p = first_p; gs_p != end1_p->next;
2533 gs_p = nxtbmnote(gs_p, start1_p, end1_p->next)) {
2534 gs_p->stemlen = 0.0;
2535 }
2536 return;
2537 }
2538 } else {
2539 /*
2540 * User provided outer stem lengths. If they are zero, force
2541 * all groups to zero and get out. There will be no stems and
2542 * no beams.
2543 */
2544 if (first_p->stemlen == 0.0 && last_p->stemlen == 0.0) {
2545 for (gs_p = first_p; gs_p != end1_p->next;
2546 gs_p = nxtbmnote(gs_p, start1_p, end1_p->next)) {
2547 gs_p->stemlen = 0.0;
2548 }
2549 return;
2550 }
2551
2552 /*
2553 * User provided outer stem lengths; calculate b0 and b1.
2554 * First get Y coords of endpoints of first and last stems.
2555 */
2556 first_p->stemlen *= Staffscale;
2557 last_p->stemlen *= Staffscale;
2558 firsty = first_p->stemdir == UP ?
2559 first_p->notelist[0].c[AY] + first_p->stemlen :
2560 first_p->notelist[ first_p->nnotes - 1 ].c[AY]
2561 - first_p->stemlen;
2562 lasty = last_p->stemdir == UP ?
2563 last_p->notelist[0].c[AY] + last_p->stemlen :
2564 last_p->notelist[ last_p->nnotes - 1 ].c[AY]
2565 - last_p->stemlen;
2566 /*
2567 * If first and last are opposite, adjust the right end of
2568 * the line.
2569 */
2570 if (first_p->stemdir != last_p->stemdir)
2571 lasty += end_bm_offset(start1_p, last_p, 8);
2572
2573 /* get X coords; calculate b0 and b1 */
2574 firstx = first_p->c[AX] + stemshift *
2575 (first_p->stemdir == DOWN ? -1 : 1);
2576 lastx = last_p->c[AX] + stemshift *
2577 (last_p->stemdir == DOWN ? -1 : 1);
2578 b1 = (lasty - firsty) / (lastx - firstx); /* slope */
2579 b0 = firsty - b1 * firstx; /* y intercept */
2580 }
2581
2582
2583 /*
2584 * At this point we know the equation for the beams. Figure out and
2585 * set the correct stem lengths for all of these beamed groups.
2586 */
2587 if (topdir == botdir) { /* all stems have the same direction */
2588 if (first_p->stemdir == DOWN)
2589 stemshift = -stemshift;
2590
2591 /* loop through the top staff's groups */
2592 for (gs_p = FIRSTCSB(start1_p); gs_p != 0; gs_p=nextcsb(gs_p)){
2593 x = gs_p->c[AX] + stemshift;
2594
2595 /* first set stemlen to beam's Y coord minus note's */
2596 gs_p->stemlen = (b0 + b1 * x) - BNOTE(gs_p).c[AY];
2597
2598 /* if stems are down, reverse it */
2599 if (gs_p->stemdir == DOWN)
2600 gs_p->stemlen = -(gs_p->stemlen);
2601
2602 finalstemadjust(gs_p);
2603 }
2604 /* loop through the bottom staff's groups */
2605 for (gs_p = FIRSTCSB(start2_p); gs_p != 0; gs_p=nextcsb(gs_p)){
2606 x = gs_p->c[AX] + stemshift;
2607
2608 /* first set stemlen to beam's Y coord minus note's */
2609 gs_p->stemlen = (b0 + b1 * x) - BNOTE(gs_p).c[AY];
2610
2611 /* if stems are down, reverse it */
2612 if (gs_p->stemdir == DOWN)
2613 gs_p->stemlen = -(gs_p->stemlen);
2614
2615 /* if negative (note on wrong side of beam), error */
2616 if (gs_p->stemlen < 0) {
2617 l_ufatal(gs_p->inputfile, gs_p->inputlineno,
2618 "stem length was forced negative");
2619 }
2620
2621 finalstemadjust(gs_p);
2622 }
2623 } else { /* topdir != botdir; some stems have different dir */
2624
2625 struct GRPSYL *prev_p; /* previous CSB group */
2626 struct GRPSYL *firstsub_p; /* first group of a subbeam */
2627 struct GRPSYL *lastsub_p; /* last group of a subbeam */
2628 struct GRPSYL *sub_p; /* a group in a subbeam */
2629 int minbeams; /* no. of beams all share */
2630 int beams; /* no. of beams of a group */
2631 int slowbasic; /* slowest basictime in CSB */
2632 int fastbasic; /* fastest basictime in CSB */
2633 int basic; /* a basictime value */
2634 float bhigh; /* height of beams */
2635 float extra; /* amount to lengthen all stems by */
2636
2637
2638 /*
2639 * Find the minimum number of beams of the groups in the CSB
2640 * set. That will be the number of beams that they all share.
2641 */
2642 minbeams = 999; /* way more than there could ever be */
2643 for (gs_p = first_p; gs_p != end1_p->next;
2644 gs_p = nxtbmnote(gs_p, start1_p, end1_p->next)){
2645 beams = drmo(gs_p->basictime) - 2;
2646 if (beams < minbeams)
2647 minbeams = beams;
2648 }
2649
2650 /*
2651 * Find height of all the beams: the distance between the
2652 * centers of the outer beams. This should agree with
2653 * the numbers in prntdata.c.
2654 */
2655 bhigh = (minbeams - 1) * Staffscale *
2656 (first_p->grpsize == GS_NORMAL ? FLAGSEP : 4.0 * POINT);
2657
2658 /*
2659 * Change the y intercept such that the first stem is lengthened
2660 * by half of this height. The line is at the outer beam, from
2661 * the perspective of the first group.
2662 */
2663 b0 += first_p->stemdir == UP ? bhigh / 2.0 : -bhigh / 2.0;
2664
2665 /*
2666 * First set stem lengths to reach the line of the main beam.
2667 * At this point, we don't yet include the distance between the
2668 * notes of multinote groups. While we're at it, find the
2669 * slowest basictime of any group in the CSB set.
2670 * Also find the fastest basictime.
2671 */
2672 slowbasic = 1024; /* faster than any could be */
2673 fastbasic = 8; /* slowest that any could be */
2674 /* loop through the top staff's groups: all stems down */
2675 for (gs_p = FIRSTCSB(start1_p); gs_p != 0; gs_p=nextcsb(gs_p)){
2676 x = gs_p->c[AX] - stemshift;
2677
2678 /* first set stemlen to note's Y coord minus beam's */
2679 gs_p->stemlen = gs_p->notelist[ gs_p->nnotes - 1 ].
2680 c[AY] - (b0 + b1 * x);
2681
2682 slowbasic = MIN(slowbasic, gs_p->basictime);
2683 fastbasic = MAX(fastbasic, gs_p->basictime);
2684 }
2685 /* loop through the bottom staff's groups; all stems up */
2686 for (gs_p = FIRSTCSB(start2_p); gs_p != 0; gs_p=nextcsb(gs_p)){
2687 x = gs_p->c[AX] + stemshift;
2688
2689 /* first set stemlen to beam's Y coord minus note's */
2690 gs_p->stemlen = (b0 + b1 * x) - gs_p->notelist[0].c[AY];
2691
2692 slowbasic = MIN(slowbasic, gs_p->basictime);
2693 fastbasic = MAX(fastbasic, gs_p->basictime);
2694 }
2695
2696 /*
2697 * Find the minimum number of beams (based on the slowest
2698 * basictime) and subtract 1 to find the number of additional
2699 * beams that all groups share beyond the first beam. Multiply
2700 * by the distance the centers of neighboring beams.
2701 */
2702 extra = ((drmo(slowbasic) - 2) - 1) * Staffscale *
2703 (first_p->grpsize == GS_NORMAL ? FLAGSEP : 4.0 * POINT);
2704
2705 /*
2706 * For each group with stemdir opposite to that of the first
2707 * group, lengthen its stemlen by that amount.
2708 */
2709 for (gs_p = first_p; gs_p != end1_p->next; gs_p =
2710 nxtbmnote(gs_p, start1_p, end1_p->next)) {
2711
2712 if (gs_p->stemdir != first_p->stemdir)
2713 gs_p->stemlen += extra;
2714 }
2715
2716 /*
2717 * Loop for each basictime being used that is shorter than the
2718 * longest one; that is, for each level of subbeam that is
2719 * needed anywhere.
2720 */
2721 for (basic = slowbasic * 2; basic <= fastbasic; basic *= 2) {
2722
2723 /* loop through all note groups in the CSB */
2724 for (prev_p = 0, gs_p = first_p;
2725 gs_p != end1_p->next;
2726 prev_p = gs_p, gs_p = nxtbmnote(gs_p, start1_p,
2727 end1_p->next)) {
2728 /*
2729 * If this group has at least as fast a basic-
2730 * time as the one we're now dealing with, and
2731 * the previous group doesn't (or there is no
2732 * previous group), a new subbeam must begin
2733 * here (or it could be just a partial beam).
2734 * If not, "continue" here.
2735 */
2736 if (gs_p->basictime < basic || (gs_p != first_p
2737 && prev_p->basictime >= basic)){
2738 continue;
2739 }
2740
2741 /* point at the start of this subbeam */
2742 firstsub_p = gs_p;
2743
2744 /*
2745 * Set lastsub_p to right end of the subbeam,
2746 * the group right before the basictime becomes
2747 * slower than the level we are dealing with.
2748 */
2749 for (lastsub_p = sub_p = firstsub_p; sub_p !=
2750 end1_p->next; sub_p = nxtbmnote(sub_p,
2751 start1_p, end1_p->next)) {
2752
2753 if (sub_p == 0 ||
2754 sub_p->basictime < basic) {
2755 break;
2756 }
2757 lastsub_p = sub_p;
2758 }
2759
2760 /*
2761 * Loop through subbeam, lengthening the stems
2762 * of all the note groups whose stem direction
2763 * is opposite to the first group's. Lengthen
2764 * them enough for one more beam.
2765 */
2766 for (sub_p = firstsub_p; sub_p != end1_p->next;
2767 sub_p = nxtbmnote(sub_p, start1_p,
2768 end1_p->next)) {
2769
2770 if (sub_p->stemdir != firstsub_p->
2771 stemdir) {
2772 sub_p->stemlen +=
2773 (sub_p->grpsize == GS_NORMAL ?
2774 FLAGSEP : 4.0 * POINT) *
2775 Staffscale;
2776 }
2777
2778 if (sub_p == lastsub_p) {
2779 break;
2780 }
2781 }
2782 }
2783 }
2784
2785 /* adjust all stems in the CSB */
2786 for (gs_p = first_p;
2787 gs_p != end1_p->next;
2788 gs_p = nxtbmnote(gs_p, start1_p, end1_p->next)) {
2789
2790 /* if negative (note on wrong side of beam), error */
2791 if (gs_p->stemlen < 0) {
2792 l_ufatal(gs_p->inputfile, gs_p->inputlineno,
2793 "stem length was forced negative");
2794 }
2795
2796 /* add distance between outer notes of group */
2797 gs_p->stemlen += (gs_p->notelist[0].stepsup -
2798 gs_p->notelist[ gs_p->nnotes - 1 ].stepsup) * Stepsize;
2799 }
2800
2801 }
2802
2803 /*
2804 * In beamstem.c, setgroupvert() expanded the north and south
2805 * boundaries of groups to allow for stems (except for CSB groups) and
2806 * "with" items (except for CSB where normwith was NO). The exceptions
2807 * were because in those cases we needed to know the stem lengths and
2808 * we didn't yet. Well, now we know. So do the job here.
2809 *
2810 * The extension for the stem is the length of the exterior part of it
2811 * minus half the size of the stem side note (about a STEPSIZE), since
2812 * the note itself is already included in the group boundary. Each
2813 * "with" item is allowed enough space for its height, or MINWITHHEIGHT,
2814 * whichever is greater. In the print phase, items of height less than
2815 * MINWITHHEIGHT will be placed so as to avoid staff lines as much as
2816 * possible.
2817 */
2818 for (gs_p = first_p; gs_p != end1_p->next; gs_p = nxtbmnote(gs_p,
2819 start1_p, end1_p->next)) {
2820 outstem = gs_p->stemlen
2821 - (gs_p->notelist[0].c[RY]
2822 - gs_p->notelist[ gs_p->nnotes - 1 ].c[RY]);
2823 if (gs_p->stemdir == UP)
2824 gs_p->c[AN] += outstem - Stepsize;
2825 else
2826 gs_p->c[AS] -= outstem - Stepsize;
2827
2828 if (gs_p->normwith == NO) {
2829 for (n = 0; n < gs_p->nwith; n++) {
2830 hi = strheight(gs_p->withlist[n]);
2831 hi = MAX(hi, Staffscale * MINWITHHEIGHT);
2832 if (gs_p->stemdir == UP)
2833 gs_p->c[AN] += hi;
2834 else
2835 gs_p->c[AS] -= hi;
2836 }
2837 }
2838 }
2839}
2840\f
2841/*
2842 * Name: calcline()
2843 *
2844 * Abstract: Calculate the equation of the line for the beams of a CSB set.
2845 *
2846 * Returns: YES if an equation was calculated, NO if there are no stems.
2847 *
2848 * Description: This function uses linear regression to figure out where the
2849 * best place to put the beam is, for a CSB set. Then, based on
2850 * whether the stems on the two staffs have the same direction, it
2851 * calls the appropriate function to adjust the results of the
2852 * linear regression as needed.
2853 */
2854
2855static int
2856calcline(start1_p, end1_p, start2_p, end2_p, first_p, last_p, topdir, botdir,
2857 b0_p, b1_p)
2858
2859struct GRPSYL *start1_p; /* first group in first voice */
2860struct GRPSYL *start2_p; /* first group in second voice */
2861struct GRPSYL *end1_p; /* last group in first voice */
2862struct GRPSYL *end2_p; /* last group in second voice */
2863struct GRPSYL *first_p; /* first note group in either voice */
2864struct GRPSYL *last_p; /* last note group in either voice */
2865int topdir, botdir; /* stem directions of top and bottom voices */
2866float *b0_p, *b1_p; /* y intercept and slope to return */
2867
2868{
2869 float defstemsteps; /* default stem length */
2870 int one_end_forced; /* is stem len forced on one end only? */
2871 int slope_forced; /* is the slope of the beam forced? */
2872 float forced_slope; /* slope that the user forced */
2873 struct GRPSYL *gs_p; /* loop through the groups in the beamed set */
2874 float sx, sy; /* sum of x and y coords of notes */
2875 float xbar, ybar; /* average x and y coords of notes */
2876 float top, bottom; /* numerator & denominator for finding b1 */
2877 float temp; /* scratch variable */
2878 float b0, b1; /* y intercept and slope */
2879 float deflen; /* default len of a stem, based on basictime */
2880 int num; /* number of notes */
2881
2882
2883 if (fabs(first_p->beamslope - NOBEAMANGLE) < 0.001) {
2884 slope_forced = NO;
2885 forced_slope = 0.0; /* not used, keep lint happy */
2886 } else {
2887 slope_forced = YES;
2888 forced_slope = tan(first_p->beamslope * PI / 180.0);
2889 }
2890 one_end_forced = IS_STEMLEN_KNOWN(first_p->stemlen) !=
2891 IS_STEMLEN_KNOWN(last_p->stemlen);
2892
2893 /*
2894 * Find how long we'd like stems to be, ignoring for the moment groups
2895 * that need to be longer due to multiple beams.
2896 */
2897 /* average default stems lengths of the two voices */
2898 defstemsteps = (vvpath(start1_p->staffno, start1_p->vno, STEMLEN)->
2899 stemlen +
2900 vvpath(start2_p->staffno, start2_p->vno, STEMLEN)->
2901 stemlen) / 2.0;
2902 /* if this is zero, both stemlens must be zero, so no stems */
2903 if (defstemsteps == 0.0 && ! slope_forced && ( ! one_end_forced ||
2904 first_p->stemlen == 0.0 || last_p->stemlen == 0.0)) {
2905 return (NO);
2906 }
2907 if (allsmall(start1_p, end1_p) == NO ||
2908 allsmall(start2_p, end2_p) == NO) {
2909 /* at least one group has a normal size note */
2910 deflen = defstemsteps * Stepsize;
2911 } else {
2912 /* all groups have all small notes */
2913 deflen = defstemsteps * SM_STEMFACTOR * Stepsize;
2914 }
2915
2916 /*
2917 * Use linear regression to find the best-fit line through where the
2918 * ends of the stems would be if they were the standard length. In
2919 * setbeam() where a similar thing was done for non-CSB beams, we used
2920 * the centers of the notes, which was okay because at this point in
2921 * the game we're really just interested in finding the slope. But
2922 * in CSB, sometimes the stems of the two staffs go in opposite
2923 * directions, so we really need to consider the ends of the stems.
2924 *
2925 * In this function, we will always be concerned with the X coord of
2926 * the group as a whole (disregarding any notes that are on the "wrong"
2927 * side of the stem) but the Y coord of the note of the group that's
2928 * nearest to the beam (thus the BNOTE macro).
2929 *
2930 * First get sum of x and y coords, to find averages.
2931 */
2932 sx = sy = 0;
2933 num = 0;
2934 for (gs_p = FIRSTCSB(start1_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
2935 sx += gs_p->c[AX];
2936 sy += BNOTE(gs_p).c[AY] + (topdir == UP ? deflen : -deflen);
2937 num++; /* count number of notes */
2938 }
2939 for (gs_p = FIRSTCSB(start2_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
2940 sx += gs_p->c[AX];
2941 sy += BNOTE(gs_p).c[AY] + (botdir == UP ? deflen : -deflen);
2942 num++; /* count number of notes */
2943 }
2944
2945 xbar = sx / num;
2946 ybar = sy / num;
2947
2948 /* accumulate numerator & denominator of regression formula for b1 */
2949 top = bottom = 0;
2950 for (gs_p = FIRSTCSB(start1_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
2951 temp = gs_p->c[AX] - xbar;
2952 top += temp * (BNOTE(gs_p).c[AY] +
2953 (topdir == UP ? deflen : -deflen) - ybar);
2954 bottom += temp * temp;
2955 }
2956 for (gs_p = FIRSTCSB(start2_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
2957 temp = gs_p->c[AX] - xbar;
2958 top += temp * (BNOTE(gs_p).c[AY] +
2959 (botdir == UP ? deflen : -deflen) - ybar);
2960 bottom += temp * temp;
2961 }
2962
2963 b1 = top / bottom; /* slope */
2964 b0 = ybar - b1 * xbar; /* y intercept */
2965
2966 /* equation of regression line: y = b0 + b1 * x */
2967
2968 if (topdir == botdir) {
2969 samedir(first_p, last_p, start1_p, start2_p, end1_p, &b0, &b1,
2970 deflen, one_end_forced, slope_forced,
2971 forced_slope);
2972 } else {
2973 oppodir(first_p, last_p, start1_p, start2_p, &b0, &b1, deflen,
2974 one_end_forced, slope_forced, forced_slope);
2975 }
2976
2977 /* return the calculated slope and intercept */
2978 *b0_p = b0;
2979 *b1_p = b1;
2980
2981 return (YES);
2982}
2983\f
2984/*
2985 * Name: samedir()
2986 *
2987 * Abstract: Adjust b0 and b1 when stems are all the same direction.
2988 *
2989 * Returns: void
2990 *
2991 * Description: This function is used in the case that the stems on the two
2992 * staffs of the CSB have the same direction. It is given the
2993 * y intercept and slope of the beam as calculated by linear
2994 * regression. It adjusts these values if need be. The algorithm
2995 * is similar to the one in setbeam() in beamstem.c. But here we
2996 * have to deal with two linked lists of groups, and we don't have
2997 * to deal with grace notes or alternations.
2998 */
2999
3000static void
3001samedir(first_p, last_p, start1_p, start2_p, end1_p, b0_p, b1_p, deflen,
3002 one_end_forced, slope_forced, forced_slope)
3003
3004struct GRPSYL *first_p, *last_p; /* first and last note groups in CSB */
3005struct GRPSYL *start1_p, *start2_p; /* first groups of 1st & 2nd voices */
3006struct GRPSYL *end1_p; /* last group of 1st voice */
3007float *b0_p, *b1_p; /* y intercept and slope */
3008double deflen; /* default len of a stem, based on group size*/
3009int one_end_forced; /* is stem len forced on one end only? */
3010int slope_forced; /* is the slope of the beam forced? */
3011double forced_slope; /* slope that the user forced */
3012
3013{
3014 struct GRPSYL *gs_p; /* loop through the groups in the beamed set */
3015 float firstx, lastx; /* x coord of first & last note (end of stem)*/
3016 float firsty, lasty; /* y coord of first & last note (end of stem)*/
3017 float maxb0, minb0; /* max and min y intercepts */
3018 float stemshift; /* x distance of stem from center of note */
3019 float b0, b1; /* working copy of y intercept and slope */
3020 float temp; /* temp variable */
3021 float shortdist; /* amount of stem shortening allowed (inches)*/
3022 int bf; /* number of beams/flags */
3023 int shortest; /* basictime of shortest note in group */
3024
3025
3026 /* set working copies from the original values */
3027 b0 = *b0_p;
3028 b1 = *b1_p;
3029
3030 /*
3031 * Find half the width of a note head; the stems will need to be
3032 * shifted by that amount from the center of the notes so that they
3033 * will meet the edge of the notes properly. If the stems are up,
3034 * they will be on the right side of (normal) notes, else left. Set
3035 * the X positions for the first and last stems.
3036 */
3037 stemshift = getstemshift(first_p);
3038 if (first_p->stemdir == DOWN)
3039 stemshift = -stemshift;
3040 firstx = first_p->c[AX] + stemshift; /* first group's stem */
3041 lastx = last_p->c[AX] + stemshift; /* last group's stem */
3042
3043 /*
3044 * The original line derived by linear regression must be adjusted in
3045 * certain ways. First, override it if the user wants that; otherwise
3046 * adjust according to the beamslope parameter.
3047 */
3048 if (slope_forced) {
3049 b1 = forced_slope;
3050 } else {
3051 b1 = adjslope(start1_p, b1, NO);
3052 }
3053
3054 /*
3055 * Calculate a new y intercept (b0). First pass parallel lines
3056 * through each note, and record the maximum and minimum y intercepts
3057 * that result.
3058 */
3059 b0 = BNOTE(first_p).c[AY] - b1 * first_p->c[AX];
3060 maxb0 = minb0 = b0; /* init to value for first note */
3061 /* look at rest of them on each of the two staffs */
3062 for (gs_p = FIRSTCSB(start1_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
3063 b0 = BNOTE(gs_p).c[AY] - b1 * gs_p->c[AX];
3064 if (b0 > maxb0)
3065 maxb0 = b0;
3066 else if (b0 < minb0)
3067 minb0 = b0;
3068 }
3069 for (gs_p = FIRSTCSB(start2_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
3070 b0 = BNOTE(gs_p).c[AY] - b1 * gs_p->c[AX];
3071 if (b0 > maxb0)
3072 maxb0 = b0;
3073 else if (b0 < minb0)
3074 minb0 = b0;
3075 }
3076
3077 /*
3078 * Find the basictime of the shortest note in the CSB set, considering
3079 * also any slashes on it. Then update the default stem length based
3080 * on that.
3081 */
3082 shortest = 0;
3083 for (gs_p = first_p; gs_p != end1_p->next; gs_p = nxtbmnote(gs_p,
3084 start1_p, end1_p->next)) {
3085 bf = drmo(gs_p->basictime) - 2; /* no. of beams/flags */
3086 bf += abs(gs_p->slash_alt); /* slashes */
3087 /*
3088 * In certain cases where there are accidentals, we need to
3089 * artificially increase bf to keep the beams from overlapping
3090 * with the accidental.
3091 */
3092 if (gs_p != first_p && gs_p->stemdir == UP &&
3093 gs_p->notelist[0].accidental != '\0' &&
3094 gs_p->notelist[0].accidental != 'x' &&
3095 b1 > 0 && bf > 1) {
3096 bf += 3.5 * b1 * (STEPSIZE / FLAGSEP) * ((bf > 1) +
3097 (gs_p->notelist[0].accidental == 'B'));
3098 }
3099 if (bf > shortest)
3100 shortest = bf;
3101 }
3102
3103 if (shortest > 2) {
3104 /* don't use "==" due to floating point roundoff error */
3105 if (deflen > 6 * Stepsize) {
3106 /* at least one group has a normal size note */
3107 deflen += (shortest - 2) * Flagsep;
3108 } else {
3109 /* all groups have all small notes */
3110 deflen += (shortest - 2) * 4.0 * POINT * Staffscale;
3111 }
3112 }
3113
3114 /*
3115 * The outer edge of the beam should be deflen steps away from the
3116 * average position of the notes, as defined by the linear regression
3117 * line. But don't allow any note to be closer than a certain number
3118 * of steps less than that, the number as given by the stemshorten parm.
3119 * We use the average of the two stemshorten values for the two voices.
3120 */
3121 shortdist = (vvpath(start1_p->staffno, start1_p->vno, STEMSHORTEN)
3122 ->stemshorten +
3123 vvpath(start2_p->staffno, start2_p->vno, STEMSHORTEN)
3124 ->stemshorten) / 2.0 * Stepsize;
3125 if (first_p->stemdir == UP) {
3126 if (maxb0 - minb0 > shortdist)
3127 b0 = maxb0 + deflen - shortdist;
3128 else
3129 b0 += deflen;
3130 } else { /* DOWN */
3131 if (maxb0 - minb0 > shortdist)
3132 b0 = minb0 - deflen + shortdist;
3133 else
3134 b0 -= deflen;
3135 }
3136
3137 firsty = b0 + b1 * firstx; /* y coord near left end of beam */
3138 lasty = b0 + b1 * lastx; /* y coord near right end of beam */
3139
3140 /*
3141 * At this point, like setbeam(), we could force the stems of notes
3142 * that are pointing to the center of their staffs to reach that center
3143 * line. But it's questionable whether that should be done in cross
3144 * staff beaming situations. We choose not to.
3145 */
3146
3147 /*
3148 * If y at the ends of the beam differs by less than a step (allowing a
3149 * fudge factor for roundoff error), force the beam horizontal by
3150 * setting one end farther away from the notes. But don't do it if the
3151 * user is forcing a particular slope.
3152 */
3153 if ( ! slope_forced && fabs(firsty - lasty) < Stepsize - 0.001) {
3154 if (first_p->stemdir == UP) {
3155 if (firsty > lasty) {
3156 lasty = firsty;
3157 } else {
3158 firsty = lasty;
3159 }
3160 } else { /* DOWN */
3161 if (firsty < lasty) {
3162 lasty = firsty;
3163 } else {
3164 firsty = lasty;
3165 }
3166 }
3167 }
3168
3169 /* recalculate slope and y intercept from (possibly) new endpoints */
3170 b1 = (lasty - firsty) / (lastx - firstx); /* slope */
3171 b0 = firsty - b1 * firstx; /* y intercept */
3172
3173 /*
3174 * At this point, like setbeam(), we could do the equivalent of
3175 * embedgrace() and avoidothervoice(). But those functions themselves
3176 * wouldn't work here as they are, and/or we don't have the necessary
3177 * info handy for calling them. These problems are fairly rare, on top
3178 * of cross staff beaming already being fairly rare. If something
3179 * collides, the user can always manually set the stem lengths.
3180 */
3181
3182 /*
3183 * If one end's stem len was forced but not the other, now is the time
3184 * to apply that forcing. So in effect, we have taken the beam as
3185 * determined by the normal algorithm and now we change the vertical
3186 * coord of this end. If the slope was also forced, move the other
3187 * end by the same amount so that the slope won't change.
3188 */
3189 if (one_end_forced) {
3190 if (IS_STEMLEN_KNOWN(first_p->stemlen)) {
3191 first_p->stemlen *= Staffscale;
3192 temp = firsty;
3193 firsty = BNOTE(first_p).c[AY] + first_p->stemlen *
3194 (first_p->stemdir == UP ? 1.0 : -1.0);
3195 if (slope_forced) {
3196 lasty += firsty - temp;
3197 }
3198 } else {
3199 last_p->stemlen *= Staffscale;
3200 temp = lasty;
3201 lasty = BNOTE(last_p).c[AY] + last_p->stemlen *
3202 (last_p->stemdir == UP ? 1.0 : -1.0);
3203 if (slope_forced) {
3204 firsty += lasty - temp;
3205 }
3206 }
3207
3208 /* recalculate */
3209 b1 = (lasty - firsty) / (lastx - firstx); /* slope */
3210 b0 = firsty - b1 * firstx; /* y intercept */
3211 }
3212
3213 /* send back the newly calculated values */
3214 *b0_p = b0;
3215 *b1_p = b1;
3216}
3217\f
3218/*
3219 * Name: oppodir()
3220 *
3221 * Abstract: Adjust b0 and b1 when stems are in opposite directions.
3222 *
3223 * Returns: void
3224 *
3225 * Description: This function is used in the case that the stems on the two
3226 * staffs of the CSB all have opposite directions. It is given
3227 * the y intercept and slope of the beam as calculated by linear
3228 * regression. It adjusts these values if need be.
3229 */
3230
3231static void
3232oppodir(first_p, last_p, start1_p, start2_p, b0_p, b1_p, deflen,
3233 one_end_forced, slope_forced, forced_slope)
3234
3235struct GRPSYL *first_p, *last_p; /* first and last note groups in CSB */
3236struct GRPSYL *start1_p, *start2_p; /* first groups of 1st & 2nd voices */
3237float *b0_p, *b1_p; /* y intercept and slope */
3238double deflen; /* default len of a stem, based on group size*/
3239int one_end_forced; /* is stem len forced on one end only? */
3240int slope_forced; /* is the slope of the beam forced? */
3241double forced_slope; /* slope that the user forced */
3242
3243{
3244 struct GRPSYL *gs_p; /* loop through the groups in the beamed set */
3245 float firstx, lastx; /* x coord of first & last note (end of stem)*/
3246 float firsty, lasty; /* y coord of first & last note (end of stem)*/
3247 float maxb0, minb0; /* max and min y intercepts */
3248 float stemshift; /* x distance of stem from center of note */
3249 float b0, b1; /* working copy of y intercept and slope */
3250 float temp; /* temp variable */
3251
3252
3253 /* set working copies from the original values */
3254 b0 = *b0_p;
3255 b1 = *b1_p;
3256
3257 /*
3258 * Find half the width of a note head; the stems will need to be
3259 * shifted by that amount from the center of the notes so that they
3260 * will meet the edge of the notes properly. If the stems are up,
3261 * they will be on the right side of (normal) notes, else left. Set
3262 * the X positions for the first and last stems.
3263 */
3264 stemshift = getstemshift(first_p);
3265 if (first_p->stemdir == DOWN)
3266 stemshift = -stemshift;
3267 firstx = first_p->c[AX] + stemshift; /* first group's stem */
3268 lastx = last_p->c[AX] + stemshift; /* last group's stem */
3269
3270 /*
3271 * The original line derived by linear regression must be adjusted in
3272 * certain ways. First, override it if the user wants that; otherwise
3273 * adjust according to the beamslope parameter.
3274 */
3275 if (slope_forced) {
3276 b1 = forced_slope;
3277 } else {
3278 b1 = adjslope(start1_p, b1, YES);
3279 }
3280
3281 /*
3282 * Calculate a new y intercept (b0). First pass parallel lines
3283 * through each note, and record the minimum y intercept for the top
3284 * staff and the maximum for the bottom staff that result.
3285 */
3286 minb0 = 1000.0; /* init way positive */
3287 /* look at rest of them on each of the two staffs */
3288 for (gs_p = FIRSTCSB(start1_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
3289 b0 = BNOTE(gs_p).c[AY] - b1 * gs_p->c[AX];
3290 if (b0 < minb0)
3291 minb0 = b0;
3292 }
3293 maxb0 = -1000.0; /* init way negative */
3294 for (gs_p = FIRSTCSB(start2_p); gs_p != 0; gs_p = nextcsb(gs_p)) {
3295 b0 = BNOTE(gs_p).c[AY] - b1 * gs_p->c[AX];
3296 if (b0 > maxb0)
3297 maxb0 = b0;
3298 }
3299
3300 /*
3301 * Make the y intercept be the average of these. That means the top
3302 * staff's shortest stem will be equal in length to the bottom staff's.
3303 */
3304 b0 = (maxb0 + minb0) / 2.0;
3305
3306 firsty = b0 + b1 * firstx; /* y coord near left end of beam */
3307 lasty = b0 + b1 * lastx; /* y coord near right end of beam */
3308
3309 /*
3310 * If y at the ends of the beam differs by less than a step (allowing a
3311 * fudge factor for roundoff error), force the beam horizontal,
3312 * averaging the two values.
3313 */
3314 if ( ! slope_forced && fabs(firsty - lasty) < Stepsize - 0.001) {
3315 lasty = (firsty + lasty) / 2.;
3316 firsty = lasty;
3317 }
3318
3319 /* recalculate slope and y intercept from (possibly) new endpoints */
3320 b1 = (lasty - firsty) / (lastx - firstx); /* slope */
3321 b0 = firsty - b1 * firstx; /* y intercept */
3322
3323 /*
3324 * If one end's stem len was forced but not the other, now is the time
3325 * to apply that forcing. So in effect, we have taken the beam as
3326 * determined by the normal algorithm and now we change the vertical
3327 * coord of this end. If the slope was also forced, move the other
3328 * end by the same amount so that the slope won't change.
3329 */
3330 if (one_end_forced) {
3331 if (IS_STEMLEN_KNOWN(first_p->stemlen)) {
3332 first_p->stemlen *= Staffscale;
3333 temp = firsty;
3334 firsty = BNOTE(first_p).c[AY] + first_p->stemlen *
3335 (first_p->stemdir == UP ? 1.0 : -1.0);
3336 if (slope_forced) {
3337 lasty += firsty - temp;
3338 }
3339 } else {
3340 last_p->stemlen *= Staffscale;
3341 temp = lasty;
3342 lasty = BNOTE(last_p).c[AY] + last_p->stemlen *
3343 (last_p->stemdir == UP ? 1.0 : -1.0);
3344 if (slope_forced) {
3345 firsty += lasty - temp;
3346 }
3347 }
3348
3349 /* recalculate */
3350 b1 = (lasty - firsty) / (lastx - firstx); /* slope */
3351 b0 = firsty - b1 * firstx; /* y intercept */
3352 }
3353
3354 /* send back the newly calculated values */
3355 *b0_p = b0;
3356 *b1_p = b1;
3357}
3358\f
3359/*
3360 * Name: nextcsb()
3361 *
3362 * Abstract: Find the next note group on this staff in this CSB.
3363 *
3364 * Returns: pointer to next note group in CSB on this staff, 0 if none
3365 *
3366 * Description: This function looks for the next group on this staff that is
3367 * still in this CSB set (therefore nongrace), and contains notes
3368 * (not a space).
3369 */
3370
3371static struct GRPSYL *
3372nextcsb(gs_p)
3373
3374struct GRPSYL *gs_p; /* current group, must be in a CSB */
3375
3376{
3377 /* if we are already at the last group in the set, no next group */
3378 if (gs_p->beamloc == ENDITEM)
3379 return (0);
3380
3381 /* loop forward, considering only nongrace groups */
3382 for (gs_p = nextnongrace(gs_p); gs_p != 0; gs_p = nextnongrace(gs_p)) {
3383 /* if we find a note group, return it */
3384 if (gs_p->grpcont == GC_NOTES)
3385 return (gs_p);
3386 /* must be a space (rests not allowed); if enditem, give up */
3387 if (gs_p->beamloc == ENDITEM)
3388 return (0);
3389 }
3390
3391 return (0); /* hit the end of the measure (shouldn't happen) */
3392}
3393\f
3394/*
3395 * Name: nxtbmnote()
3396 *
3397 * Abstract: Find the next note group in this CSB (this staff or the other).
3398 *
3399 * Returns: pointer to next note group in CSB, endnext_p if none
3400 *
3401 * Description: This function looks for the next group that is still in this
3402 * CSB set (therefore nongrace), and contains notes (not a space
3403 * or a rest), whichever staff it may be on.
3404 */
3405
3406static struct GRPSYL *
3407nxtbmnote(gs_p, first_p, endnext_p)
3408
3409struct GRPSYL *gs_p; /* current group, must be in a CSB */
3410struct GRPSYL *first_p; /* first group in top staff of the CSB */
3411struct GRPSYL *endnext_p; /* what to return if we hit the end */
3412
3413{
3414 /*
3415 * Keep finding the next nonspace group, until we hit the end or we
3416 * find one that is not a rest.
3417 */
3418 do {
3419 gs_p = nxtbmgrp(gs_p, first_p, endnext_p);
3420 } while (gs_p != endnext_p && gs_p->grpcont != GC_NOTES);
3421 return (gs_p);
3422}