Commit | Line | Data |
---|---|---|
69695f33 MW |
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 | ||
41 | static void relscore P((struct MAINLL *mllfeed_p)); | |
42 | static void relstaff P((struct MAINLL *feed_p, int s1, int s2, double botoff, | |
43 | double betweendist)); | |
44 | static void posscores P((void)); | |
45 | static void abspage P((struct MAINLL *page_p, float cursep[], float maxsep[], | |
46 | float curpad[], float maxpad[], int totscores, | |
47 | double remheight, double y_start)); | |
48 | static void absstaff P((struct FEED *feed_p, struct STAFF *staff_p)); | |
49 | static double grids_atend P((double vertavail, int firstpage, | |
50 | struct FEED *mfeed_p, struct FEED *gfeed_p)); | |
51 | static int compgrids P((const void *g1_p_p, const void *g2_p_p)); | |
52 | static void proc_css P((void)); | |
53 | static void one_css P((struct STAFF *ts_p, struct STAFF *os_p, | |
54 | struct GRPSYL *tg_p, RATIONAL time)); | |
55 | static void horzavoid P((void)); | |
56 | static void avoidone P((struct MAINLL *mainll_p, struct GRPSYL *cssg_p, | |
57 | RATIONAL time)); | |
58 | static void set_csb_stems P((void)); | |
59 | static void onecsb P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p)); | |
60 | static 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)); | |
65 | static 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)); | |
70 | static 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)); | |
74 | static struct GRPSYL *nextcsb P((struct GRPSYL *gs_p)); | |
75 | static 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 | ||
93 | void | |
94 | absvert() | |
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 | ||
160 | static void | |
161 | relscore(mllfeed_p) | |
162 | ||
163 | struct 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 | ||
384 | static void | |
385 | relstaff(feed_p, s1, s2, botoff, betweendist) | |
386 | ||
387 | struct MAINLL *feed_p; /* pointer to FEED for this score */ | |
388 | int s1; /* number of top staff */ | |
389 | int s2; /* number of bottom staff */ | |
390 | double botoff; /* center line of bottom, relative to score */ | |
391 | double 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 | ||
471 | static void | |
472 | posscores() | |
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 | ||
959 | static void | |
960 | abspage(page_p, cursep, maxsep, curpad, maxpad, totscores, remheight, | |
961 | y_start) | |
962 | ||
963 | struct MAINLL *page_p; /* point at first FEED for this page */ | |
964 | float cursep[]; /* this score's top line to above score's bottom line */ | |
965 | float maxsep[]; /* the max we'd like to expand cursep to */ | |
966 | float curpad[]; /* white pad between this score and above score */ | |
967 | float maxpad[]; /* the max we'd like to expand curpad to */ | |
968 | int totscores; /* number of scores on this page */ | |
969 | double remheight; /* extra vertical space available, to be distributed */ | |
970 | double 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 | ||
1332 | static void | |
1333 | absstaff(feed_p, staff_p) | |
1334 | ||
1335 | struct FEED *feed_p; /* FEED for the score we're on */ | |
1336 | struct 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 | ||
1413 | static double | |
1414 | grids_atend(vertavail, firstpage, mfeed_p, gfeed_p) | |
1415 | ||
1416 | double vertavail; /* space available for grids and spreading out scores*/ | |
1417 | int firstpage; /* is this first page (there's only 1 page of music)?*/ | |
1418 | struct FEED *mfeed_p; /* FEED at start of last music page */ | |
1419 | struct 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 | ||
1609 | static int | |
1610 | compgrids(g1_p_p, g2_p_p) | |
1611 | ||
1612 | #ifdef __STDC__ | |
1613 | const void *g1_p_p; /* the two grid pointers to compare */ | |
1614 | const void *g2_p_p; | |
1615 | #else | |
1616 | char *g1_p_p; /* the two grid pointers to compare */ | |
1617 | char *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 | ||
1705 | static void | |
1706 | proc_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 | ||
1947 | static void | |
1948 | one_css(ts_p, os_p, tg_p, time) | |
1949 | ||
1950 | struct STAFF *ts_p; /* This Staff, the normal one for the grpsyl */ | |
1951 | struct STAFF *os_p; /* Other Staff that the grpsyl has notes on */ | |
1952 | struct GRPSYL *tg_p; /* This Grpsyl */ | |
1953 | RATIONAL 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 | ||
2064 | static void | |
2065 | horzavoid() | |
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 | ||
2107 | static void | |
2108 | avoidone(mainll_p, cssg_p, time) | |
2109 | ||
2110 | struct MAINLL *mainll_p; /* the MLL for our group's staff */ | |
2111 | struct GRPSYL *cssg_p; /* the CSS group we are working on */ | |
2112 | RATIONAL 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 | ||
2328 | static void | |
2329 | set_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 | ||
2443 | static void | |
2444 | onecsb(start1_p, start2_p) | |
2445 | ||
2446 | struct GRPSYL *start1_p; /* first GRPSYL on top staff */ | |
2447 | struct 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 | ||
2855 | static int | |
2856 | calcline(start1_p, end1_p, start2_p, end2_p, first_p, last_p, topdir, botdir, | |
2857 | b0_p, b1_p) | |
2858 | ||
2859 | struct GRPSYL *start1_p; /* first group in first voice */ | |
2860 | struct GRPSYL *start2_p; /* first group in second voice */ | |
2861 | struct GRPSYL *end1_p; /* last group in first voice */ | |
2862 | struct GRPSYL *end2_p; /* last group in second voice */ | |
2863 | struct GRPSYL *first_p; /* first note group in either voice */ | |
2864 | struct GRPSYL *last_p; /* last note group in either voice */ | |
2865 | int topdir, botdir; /* stem directions of top and bottom voices */ | |
2866 | float *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 | ||
3000 | static void | |
3001 | samedir(first_p, last_p, start1_p, start2_p, end1_p, b0_p, b1_p, deflen, | |
3002 | one_end_forced, slope_forced, forced_slope) | |
3003 | ||
3004 | struct GRPSYL *first_p, *last_p; /* first and last note groups in CSB */ | |
3005 | struct GRPSYL *start1_p, *start2_p; /* first groups of 1st & 2nd voices */ | |
3006 | struct GRPSYL *end1_p; /* last group of 1st voice */ | |
3007 | float *b0_p, *b1_p; /* y intercept and slope */ | |
3008 | double deflen; /* default len of a stem, based on group size*/ | |
3009 | int one_end_forced; /* is stem len forced on one end only? */ | |
3010 | int slope_forced; /* is the slope of the beam forced? */ | |
3011 | double 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 | ||
3231 | static void | |
3232 | oppodir(first_p, last_p, start1_p, start2_p, b0_p, b1_p, deflen, | |
3233 | one_end_forced, slope_forced, forced_slope) | |
3234 | ||
3235 | struct GRPSYL *first_p, *last_p; /* first and last note groups in CSB */ | |
3236 | struct GRPSYL *start1_p, *start2_p; /* first groups of 1st & 2nd voices */ | |
3237 | float *b0_p, *b1_p; /* y intercept and slope */ | |
3238 | double deflen; /* default len of a stem, based on group size*/ | |
3239 | int one_end_forced; /* is stem len forced on one end only? */ | |
3240 | int slope_forced; /* is the slope of the beam forced? */ | |
3241 | double 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 | ||
3371 | static struct GRPSYL * | |
3372 | nextcsb(gs_p) | |
3373 | ||
3374 | struct 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 | ||
3406 | static struct GRPSYL * | |
3407 | nxtbmnote(gs_p, first_p, endnext_p) | |
3408 | ||
3409 | struct GRPSYL *gs_p; /* current group, must be in a CSB */ | |
3410 | struct GRPSYL *first_p; /* first group in top staff of the CSB */ | |
3411 | struct 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 | } |