Commit | Line | Data |
---|---|---|
69695f33 MW |
1 | /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 by Arkkra Enterprises */ |
2 | /* All rights reserved */ | |
3 | /* | |
4 | * Name: relvert.c | |
5 | * | |
6 | * Description: This file contains functions for setting all remaining | |
7 | * relative vertical coordinates. | |
8 | */ | |
9 | ||
10 | #include <string.h> | |
11 | #include "defines.h" | |
12 | #include "structs.h" | |
13 | #include "globals.h" | |
14 | ||
15 | /* how many rectangles to malloc initially and at each realloc if needed */ | |
16 | #define RECTCHUNK (100) | |
17 | ||
18 | /* rectangle structure; see procscore() prologue for explanation of its use */ | |
19 | struct RECTAB { | |
20 | float n, s, e, w; /* boundaries of a rectangle */ | |
21 | /* horz coords are absolute; vertical coords */ | |
22 | /* are relative to center staff line */ | |
23 | /* (baseline for "between") */ | |
24 | short relevant; /* is rectangle relevant? */ | |
25 | short tried; /* have we tried this one yet? */ | |
26 | }; | |
27 | static struct RECTAB *rectab; /* ptr to malloc'ed and realloc'ed array */ | |
28 | ||
29 | /* this fudge factor prevents roundoff error from causing overlap */ | |
30 | #define FUDGE (0.001) | |
31 | ||
32 | /* these symbols tell certain subroutines which things to work on */ | |
33 | #define DO_OTHERS 0 /* default */ | |
34 | #define DO_PHRASE 1 | |
35 | ||
36 | ||
37 | static int reclim; /* index after last rectangle in rectab */ | |
38 | ||
39 | static void procstaff P((struct MAINLL *mainll_p, int s)); | |
40 | static void dostaff P((int s, int place)); | |
41 | static void dogroups P((struct MAINLL *start_p, int s, int place)); | |
42 | static void llgrps P((struct STAFF *staff_p, struct GRPSYL *gs_p, int place)); | |
43 | static void dobeamalt P((struct MAINLL *start_p, int s, int place)); | |
44 | static void onebeamalt P((struct GRPSYL *gs_p)); | |
45 | static double getstemendvert P((struct GRPSYL *gs_p)); | |
46 | static void linerects P((double x1, double y1, double x2, double y2, int side, | |
47 | double halfstaff)); | |
48 | static void docurve P((struct MAINLL *start_p, int s, int place, | |
49 | int do_which)); | |
50 | static void curverect P((int s, struct STUFF *stuff_p, double halfstaff)); | |
51 | static void curvepiecerect P((double x1, double y1, double x2, double y2, | |
52 | double halfstaff)); | |
53 | static void dotuplet P((struct MAINLL *start_p, int s, int place)); | |
54 | static void onetuplet P((struct STAFF *staff_p, struct GRPSYL *start_p, | |
55 | int place)); | |
56 | static void domiscstuff P((struct MAINLL *start_p, int s, int place, | |
57 | unsigned long do_which)); | |
58 | static void dolyrics P((struct MAINLL *start_p, int s, int place)); | |
59 | static void getvsize P((struct MAINLL *start_p, int s, int place, int v, | |
60 | float *asc_p, float *des_p)); | |
61 | static void setsylvert P((struct MAINLL *start_p, int s, int place, int v, | |
62 | double baseline)); | |
63 | static void dopedal P((struct MAINLL *start_p, int s)); | |
64 | static void doendings P((struct MAINLL *start_p, int s)); | |
65 | static void storeend P((struct MAINLL *start_p, struct MAINLL *end_p, int s)); | |
66 | static void dorehears P((struct MAINLL *start_p, int s)); | |
67 | static double stackit P((double west, double east, double height, double dist, | |
68 | int place)); | |
69 | static void inc_reclim P((void)); | |
70 | \f | |
71 | /* | |
72 | * Name: relvert() | |
73 | * | |
74 | * Abstract: Set all relative vertical coords not already set. | |
75 | * | |
76 | * Returns: void | |
77 | * | |
78 | * Description: This function sets all remaining relative vertical coords. | |
79 | * It calls procstaff() once for each staff in each score to | |
80 | * do this. | |
81 | */ | |
82 | ||
83 | void | |
84 | relvert() | |
85 | ||
86 | { | |
87 | struct MAINLL *mainll_p; /* point along main linked list */ | |
88 | struct MAINLL *end_p; /* point at end of a piece of MLL */ | |
89 | struct MAINLL *m2_p; /* another pointer along MLL */ | |
90 | int s; /* staff number */ | |
91 | int gotbar; /* was a bar found in this chunk? */ | |
92 | ||
93 | ||
94 | debug(16, "relvert"); | |
95 | /* | |
96 | * Find each section of the main linked list, delimited by FEEDs. | |
97 | * For each such section, call procstaff() for each visible staff. | |
98 | * Keep SSVs up to date so that we always know what staffs are visible. | |
99 | */ | |
100 | initstructs(); /* clean out old SSV info */ | |
101 | ||
102 | /* skip anything before first FEED first */ | |
103 | for (mainll_p = Mainllhc_p; mainll_p->str != S_FEED; | |
104 | mainll_p = mainll_p->next) { | |
105 | if (mainll_p->str == S_SSV) | |
106 | asgnssv(mainll_p->u.ssv_p); | |
107 | } | |
108 | ||
109 | /* | |
110 | * Initially allocate RECTCHUNK rectangles. If we find we need more at | |
111 | * some point, we'll realloc to get more. | |
112 | */ | |
113 | MALLOC(RECTAB, rectab, RECTCHUNK); | |
114 | ||
115 | for (;;) { | |
116 | /* | |
117 | * Find end of this chunk. If it has no bars in it, this must | |
118 | * either be the end of the MLL and there was a final feed | |
119 | * after all the music data, or else this is a block. Either | |
120 | * way, there is no need to process this chunk. | |
121 | */ | |
122 | gotbar = NO; | |
123 | for (end_p = mainll_p->next; end_p != 0 && | |
124 | end_p->str != S_FEED; end_p = end_p->next) { | |
125 | if (end_p->str == S_BAR) | |
126 | gotbar = YES; | |
127 | } | |
128 | if (gotbar == NO) { | |
129 | if (end_p == 0) | |
130 | break; /* end of MLL, get out */ | |
131 | ||
132 | /* update SSVs to beginning of next score */ | |
133 | for (m2_p = mainll_p->next; m2_p != end_p; | |
134 | m2_p = m2_p->next) { | |
135 | if (m2_p->str == S_SSV) | |
136 | asgnssv(m2_p->u.ssv_p); | |
137 | } | |
138 | ||
139 | mainll_p = end_p; /* block, skip by it */ | |
140 | continue; | |
141 | } | |
142 | ||
143 | for (s = 1; s <= Score.staffs; s++) { | |
144 | if (svpath(s, VISIBLE)->visible == YES) | |
145 | procstaff(mainll_p, s); | |
146 | } | |
147 | ||
148 | /* update SSVs to beginning of next score */ | |
149 | for (m2_p = mainll_p->next; m2_p != end_p; m2_p = m2_p->next) { | |
150 | if (m2_p->str == S_SSV) | |
151 | asgnssv(m2_p->u.ssv_p); | |
152 | } | |
153 | ||
154 | if (end_p == 0) | |
155 | break; | |
156 | mainll_p = end_p; | |
157 | } | |
158 | ||
159 | FREE(rectab); | |
160 | } | |
161 | \f | |
162 | /* | |
163 | * Name: procstaff() | |
164 | * | |
165 | * Abstract: Set all relative vertical coords for a staff in one score. | |
166 | * | |
167 | * Returns: void | |
168 | * | |
169 | * Description: This function sets all remaining relative vertical coords | |
170 | * for a given staff of a given score. | |
171 | */ | |
172 | ||
173 | static void | |
174 | procstaff(start_p, s) | |
175 | ||
176 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
177 | int s; /* the staff we are to work on */ | |
178 | ||
179 | { | |
180 | struct MAINLL *mainll_p;/* point along main linked list */ | |
181 | char *order; /* point at a subarray in markorder */ | |
182 | int stk; /* stacking order number */ | |
183 | int mk; /* mark type */ | |
184 | unsigned long do_which; /* bit map of which mark types to do */ | |
185 | float north, south; /* relative coords of staff */ | |
186 | float hb; /* height of "between" objects */ | |
187 | int k; /* loop variable */ | |
188 | ||
189 | ||
190 | debug(32, "procstaff file=%s line=%d s=%d", start_p->inputfile, | |
191 | start_p->inputlineno, s); | |
192 | ||
193 | /* set globals like Staffscale for use by the rest of the file */ | |
194 | set_staffscale(s); | |
195 | ||
196 | /* | |
197 | * Each structure in rectab[] represents something to be drawn that | |
198 | * is associated with this staff, beginning with the staff itself. | |
199 | * The coordinates define the rectangle that surrounds the object. | |
200 | * The rectangles' edges are horizontal and vertical. So if an object | |
201 | * (like a slanted beam) doesn't fit well in such a recangle, multiple | |
202 | * rectangles are used to enclose pieces of it, as in integration in | |
203 | * calculus. | |
204 | * | |
205 | * The first part of this function does this for things that are above | |
206 | * the staff. The second part does it for things that are below it. | |
207 | * The third part does it for items that are to be centered (if | |
208 | * possible) between two staffs. In the first two parts, rectangles | |
209 | * are added to the table one at a time, working outwards from the | |
210 | * staff. In the third part, they are piled on an imaginary baseline. | |
211 | * | |
212 | * Some objects (like note groups) already have an assigned position. | |
213 | * and their rectangles are simply added to the table, regardless of | |
214 | * whether they overlap preexisting rectangles. | |
215 | * | |
216 | * Some objects (like phrase marks) get their positions figured out | |
217 | * now, by some unique algorithm that doesn't make use of the table of | |
218 | * rectangles, and then their rectangles are added to the table, again | |
219 | * not worrying about overlap with preexisting rectangles. | |
220 | * | |
221 | * Some objects (like "stuff" to be printed) make use of the table to | |
222 | * figure out where their rectangles should be placed. They are placed | |
223 | * as close to the staff (or baseline, for "between") as is possible | |
224 | * without overlapping preexisting rectangles (or, in the case of | |
225 | * chords, getting closer to the staff than allowed by "chorddist"; or | |
226 | * in the case of rom, ital, bold, boldital, or rehearsal marks, closer | |
227 | * than "dist"; or in the case of dynamics, closer than "dyndist"). | |
228 | * (And some things have their own "dist" to override these parameters, | |
229 | * and the optional ability to force a distance regardless of overlap.) | |
230 | * To see if the rectangle being added overlaps, first its east and | |
231 | * west are tested. All previous rectangles that are "out of its way" | |
232 | * horizontally are marked not "relevant"; the others are marked | |
233 | * "relevant". As positions are tried, working outwards, positions | |
234 | * that fail to avoid overlap are marked "tried". (For chords, and | |
235 | * rom/ital/bold/boldital, previous rectangles that are closer to the | |
236 | * staff than the stuff is allowed to come anyhow are pre-marked as if | |
237 | * "tried".) | |
238 | */ | |
239 | ||
240 | /* | |
241 | * Fill rectab for the objects above this staff. | |
242 | */ | |
243 | reclim = 0; /* rectab is initially empty */ | |
244 | ||
245 | dostaff(s, PL_ABOVE); | |
246 | dogroups(start_p, s, PL_ABOVE); | |
247 | dobeamalt(start_p, s, PL_ABOVE); | |
248 | docurve(start_p, s, PL_ABOVE, DO_OTHERS); | |
249 | dotuplet(start_p, s, PL_ABOVE); | |
250 | docurve(start_p, s, PL_ABOVE, DO_PHRASE); | |
251 | ||
252 | /* get stacking order of the user-controllable mark types */ | |
253 | order = svpath(s, ABOVEORDER)->markorder[PL_ABOVE]; | |
254 | ||
255 | /* loop on each possible stacking order number */ | |
256 | for (stk = 1; stk <= NUM_MARK; stk++) { | |
257 | ||
258 | /* set bit map for each mark type that has this order number */ | |
259 | do_which = 0; | |
260 | for (mk = 0; mk < NUM_MARK; mk++) { | |
261 | if (order[mk] == stk) { | |
262 | do_which |= (1L << mk); | |
263 | } | |
264 | } | |
265 | /* if no marks, we're done; stacking orders are contiguous */ | |
266 | if (do_which == 0) | |
267 | break; | |
268 | ||
269 | /* | |
270 | * Some mark types must have a unique order number, not shared | |
271 | * with any others. For each of them, do a case statement to | |
272 | * call their subroutine. The other ones all share the same | |
273 | * subroutine, so call it in the default to do the mark types | |
274 | * listed in the bit map. | |
275 | */ | |
276 | switch (do_which) { | |
277 | case 1L << MK_LYRICS: | |
278 | dolyrics(start_p, s, PL_ABOVE); | |
279 | break; | |
280 | case 1L << MK_ENDING: | |
281 | doendings(start_p, s); | |
282 | break; | |
283 | case 1L << MK_REHEARSAL: | |
284 | dorehears(start_p, s); | |
285 | break; | |
286 | case 1L << MK_PEDAL: | |
287 | break; /* ignore for above */ | |
288 | default: | |
289 | domiscstuff(start_p, s, PL_ABOVE, do_which); | |
290 | break; | |
291 | } | |
292 | } | |
293 | ||
294 | /* | |
295 | * Find the northernmost rectangle, for setting the staff's north. | |
296 | * But don't let north be so close that things sticking out might | |
297 | * almost touch another staff. Staffs smaller than a regular 5 line | |
298 | * staff will still be given as much space. In any case, we want at | |
299 | * least 3 stepsizes of white space. | |
300 | */ | |
301 | north = staffvertspace(s) / 2.0 + 3.0 * Stepsize; | |
302 | for (k = 0; k < reclim; k++) { | |
303 | if (rectab[k].n > north) | |
304 | north = rectab[k].n; | |
305 | } | |
306 | ||
307 | /* | |
308 | * Fill rectab for the objects below this staff. | |
309 | */ | |
310 | reclim = 0; /* rectab is initially empty */ | |
311 | ||
312 | dostaff(s, PL_BELOW); | |
313 | dogroups(start_p, s, PL_BELOW); | |
314 | dobeamalt(start_p, s, PL_BELOW); | |
315 | docurve(start_p, s, PL_BELOW, DO_OTHERS); | |
316 | dotuplet(start_p, s, PL_BELOW); | |
317 | docurve(start_p, s, PL_BELOW, DO_PHRASE); | |
318 | ||
319 | /* get stacking order of the user-controllable mark types */ | |
320 | order = svpath(s, BELOWORDER)->markorder[PL_BELOW]; | |
321 | ||
322 | /* loop on each possible stacking order number */ | |
323 | for (stk = 1; stk <= NUM_MARK; stk++) { | |
324 | ||
325 | /* set bit map for each mark type that has this order number */ | |
326 | do_which = 0; | |
327 | for (mk = 0; mk < NUM_MARK; mk++) { | |
328 | if (order[mk] == stk) { | |
329 | do_which |= (1L << mk); | |
330 | } | |
331 | } | |
332 | /* if no marks, we're done; stacking orders are contiguous */ | |
333 | if (do_which == 0) | |
334 | break; | |
335 | ||
336 | /* | |
337 | * Some mark types must have a unique order number, not shared | |
338 | * with any others. For each of them, do a case statement to | |
339 | * call their subroutine. The other ones all share the same | |
340 | * subroutine, so call it in the default to do the mark types | |
341 | * listed in the bit map. | |
342 | */ | |
343 | switch (do_which) { | |
344 | case 1L << MK_LYRICS: | |
345 | dolyrics(start_p, s, PL_BELOW); | |
346 | break; | |
347 | case 1L << MK_ENDING: | |
348 | case 1L << MK_REHEARSAL: | |
349 | break; /* ignore for below */ | |
350 | case 1L << MK_PEDAL: | |
351 | dopedal(start_p, s); | |
352 | break; | |
353 | default: | |
354 | domiscstuff(start_p, s, PL_BELOW, do_which); | |
355 | break; | |
356 | } | |
357 | } | |
358 | ||
359 | /* | |
360 | * Find the southernmost rectangle, for setting the staff's south. | |
361 | * But don't let south be so close that things sticking out might | |
362 | * almost touch another staff. Staffs smaller than a regular 5 line | |
363 | * staff will still be given as much space. In any case, we want at | |
364 | * least 3 stepsizes of white space. | |
365 | */ | |
366 | south = -(staffvertspace(s) / 2.0 + 3.0 * Stepsize); | |
367 | for (k = 0; k < reclim; k++) { | |
368 | if (rectab[k].s < south) | |
369 | south = rectab[k].s; | |
370 | } | |
371 | ||
372 | /* | |
373 | * Fill rectab for the objects between this staff and the one below. | |
374 | */ | |
375 | reclim = 0; /* rectab is initially empty */ | |
376 | ||
377 | /* set up baseline, a rectangle of height 0 spanning the page */ | |
378 | rectab[reclim].w = 0; | |
379 | rectab[reclim].e = PGWIDTH; | |
380 | rectab[reclim].n = 0; | |
381 | rectab[reclim].s = 0; | |
382 | inc_reclim(); | |
383 | ||
384 | ||
385 | /* get stacking order of the user-controllable mark types */ | |
386 | order = svpath(s, BETWEENORDER)->markorder[PL_BETWEEN]; | |
387 | ||
388 | /* loop on each possible stacking order number */ | |
389 | for (stk = 1; stk <= NUM_MARK; stk++) { | |
390 | ||
391 | /* set bit map for each mark type that has this order number */ | |
392 | do_which = 0; | |
393 | for (mk = 0; mk < NUM_MARK; mk++) { | |
394 | if (order[mk] == stk) { | |
395 | do_which |= (1L << mk); | |
396 | } | |
397 | } | |
398 | /* if no marks, we're done; stacking orders are contiguous */ | |
399 | if (do_which == 0) | |
400 | break; | |
401 | ||
402 | /* | |
403 | * Some mark types must have a unique order number, not shared | |
404 | * with any others. For each of them, do a case statement to | |
405 | * call their subroutine. The other ones all share the same | |
406 | * subroutine, so call it in the default to do the mark types | |
407 | * listed in the bit map. | |
408 | */ | |
409 | switch (do_which) { | |
410 | case 1L << MK_LYRICS: | |
411 | dolyrics(start_p, s, PL_BETWEEN); | |
412 | break; | |
413 | case 1L << MK_ENDING: | |
414 | case 1L << MK_REHEARSAL: | |
415 | case 1L << MK_PEDAL: | |
416 | break; /* ignore for between */ | |
417 | default: | |
418 | domiscstuff(start_p, s, PL_BETWEEN, do_which); | |
419 | break; | |
420 | } | |
421 | } | |
422 | ||
423 | /* | |
424 | * Find the northernmost rectangle, for finding the height of these | |
425 | * objects between. | |
426 | */ | |
427 | hb = 0; | |
428 | for (k = 0; k < reclim; k++) { | |
429 | if (rectab[k].n > hb) | |
430 | hb = rectab[k].n; | |
431 | } | |
432 | ||
433 | /* | |
434 | * Set the relative north and south of every STAFF structure for this | |
435 | * staff number on this score. (There's one per measure.) While | |
436 | * we're at it, set RX to 0, in case anyone cares. Set the height of | |
437 | * "between" objects in each STAFF, too. | |
438 | */ | |
439 | for (mainll_p = start_p->next; mainll_p != 0 && | |
440 | mainll_p->str != S_FEED; mainll_p = mainll_p->next) { | |
441 | ||
442 | if (mainll_p->str == S_STAFF && | |
443 | mainll_p->u.staff_p->staffno == s) { | |
444 | ||
445 | mainll_p->u.staff_p->c[RN] = north; | |
446 | mainll_p->u.staff_p->c[RX] = 0; | |
447 | mainll_p->u.staff_p->c[RS] = south; | |
448 | mainll_p->u.staff_p->heightbetween = hb; | |
449 | } | |
450 | } | |
451 | } | |
452 | \f | |
453 | /* | |
454 | * Name: dostaff() | |
455 | * | |
456 | * Abstract: Set up the rectangle for the staff itself. | |
457 | * | |
458 | * Returns: void | |
459 | * | |
460 | * Description: This function puts into rectab the rectangle for the staff | |
461 | * itself. The staff's relative vertical coords are not set now, | |
462 | * though, because they must later be set to include all the | |
463 | * objects associated with the staff. | |
464 | */ | |
465 | ||
466 | static void | |
467 | dostaff(s, place) | |
468 | ||
469 | int s; /* staff number */ | |
470 | int place; /* above or below? */ | |
471 | ||
472 | { | |
473 | debug(32, "dostaff s=%d place=%d", s, place); | |
474 | /* | |
475 | * Use the full page width, even though the staff will not actually | |
476 | * reach the edges, due to margins, etc. This way nothing will ever | |
477 | * fall beyond this base rectangle. Put a STDPAD of padding around | |
478 | * it vertically. | |
479 | */ | |
480 | rectab[reclim].w = 0; | |
481 | rectab[reclim].e = PGWIDTH; | |
482 | ||
483 | if (place == PL_ABOVE) { | |
484 | rectab[reclim].n = halfstaffhi(s) + Stdpad; | |
485 | rectab[reclim].s = 0; | |
486 | } else { /* PL_BELOW */ | |
487 | rectab[reclim].n = 0; | |
488 | rectab[reclim].s = -(halfstaffhi(s) + Stdpad); | |
489 | } | |
490 | ||
491 | inc_reclim(); | |
492 | } | |
493 | \f | |
494 | /* | |
495 | * Name: dogroups() | |
496 | * | |
497 | * Abstract: Set up rectangles & relative vert coords for staff's groups. | |
498 | * | |
499 | * Returns: void | |
500 | * | |
501 | * Description: This function puts into rectab the rectangles for each group on | |
502 | * this staff. The groups' relative vertical coords were already | |
503 | * set in proclist() in beamstem.c. | |
504 | */ | |
505 | ||
506 | static void | |
507 | dogroups(start_p, s, place) | |
508 | ||
509 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
510 | int s; /* staff number */ | |
511 | int place; /* above or below? */ | |
512 | ||
513 | { | |
514 | struct MAINLL *mainll_p; /* point along main linked list */ | |
515 | int v; /* voice number */ | |
516 | ||
517 | ||
518 | debug(32, "dogroups file=%s line=%d s=%d place=%d", start_p->inputfile, | |
519 | start_p->inputlineno, s, place); | |
520 | /* | |
521 | * Loop through this score's part of the MLL. | |
522 | */ | |
523 | for (mainll_p = start_p->next; mainll_p != 0 && | |
524 | mainll_p->str != S_FEED; mainll_p = mainll_p->next) { | |
525 | /* | |
526 | * Whenever we find a structure for this staff (another | |
527 | * measure of this staff), call llgrps() for each voice. | |
528 | * If some voice doesn't exist, llgrps() will get a | |
529 | * null pointer and just return. | |
530 | */ | |
531 | if (mainll_p->str == S_STAFF && | |
532 | mainll_p->u.staff_p->staffno == s) { | |
533 | ||
534 | for (v = 0; v < MAXVOICES; v++) | |
535 | llgrps(mainll_p->u.staff_p, | |
536 | mainll_p->u.staff_p->groups_p[v], place); | |
537 | } | |
538 | } | |
539 | } | |
540 | \f | |
541 | /* | |
542 | * Name: llgrps() | |
543 | * | |
544 | * Abstract: Set up rectangles for note and rest groups. | |
545 | * | |
546 | * Returns: void | |
547 | * | |
548 | * Description: This function puts rectangles into rectab for all groups in | |
549 | * this measure of this voice, for groups consisting of notes or | |
550 | * rests. | |
551 | */ | |
552 | ||
553 | static void | |
554 | llgrps(staff_p, first_p, place) | |
555 | ||
556 | struct STAFF *staff_p; /* point to the staff */ | |
557 | struct GRPSYL *first_p; /* point to first group */ | |
558 | int place; /* above or below? */ | |
559 | ||
560 | { | |
561 | struct GRPSYL *gs_p; /* point at a group */ | |
562 | struct NOTE *note_p; /* point at a note */ | |
563 | double mx, my_offset, mheight, mwidth; /* multirest number coords */ | |
564 | int n; /* loop through notelist */ | |
565 | float asc, des, wid; /* ascent, descent, and width of acc */ | |
566 | ||
567 | ||
568 | /* | |
569 | * For each group that is notes or a rest, put a rectangle into rectab. | |
570 | * However, on tablature staffs, don't do this for rests, since they | |
571 | * aren't printed there. | |
572 | */ | |
573 | for (gs_p = first_p; gs_p != 0; gs_p = gs_p->next) { | |
574 | if (gs_p->grpcont == GC_SPACE) | |
575 | continue; | |
576 | ||
577 | if (gs_p->grpcont == GC_REST && is_tab_staff(gs_p->staffno)) | |
578 | continue; | |
579 | ||
580 | if (place == PL_ABOVE && ( | |
581 | gs_p->basictime < -1 && svpath(staff_p->staffno, | |
582 | PRINTMULTNUM)->printmultnum == YES || | |
583 | is_mrpt(gs_p) && svpath(staff_p->staffno, | |
584 | NUMBERMRPT)->numbermrpt == YES | |
585 | )) { | |
586 | /* | |
587 | * Special case for multirests and measure repeats. | |
588 | * The rest or mrpt symbol itself is inside the staff, | |
589 | * so we don't have to worry about it. But we need to | |
590 | * make a rectangle for the number, if the number is | |
591 | * to be printed. | |
592 | */ | |
593 | (void)mrnum(staff_p, &mx, &my_offset, &mheight, | |
594 | &mwidth); | |
595 | rectab[reclim].w = mx; | |
596 | rectab[reclim].e = mx + mwidth; | |
597 | rectab[reclim].n = my_offset + mheight; | |
598 | rectab[reclim].s = 0; | |
599 | ||
600 | inc_reclim(); | |
601 | continue; | |
602 | } | |
603 | ||
604 | /* for "below", no rectangles are needed for multirests */ | |
605 | if (gs_p->basictime < -1) | |
606 | continue; | |
607 | ||
608 | /* | |
609 | * We have a normal note or rest group. Make a rectangle for | |
610 | * it, making sure it reaches the center staff line. | |
611 | */ | |
612 | rectab[reclim].w = gs_p->c[AW]; | |
613 | rectab[reclim].e = gs_p->c[AE]; | |
614 | ||
615 | if (place == PL_ABOVE) { | |
616 | rectab[reclim].n = MAX(gs_p->c[RN], 0); | |
617 | rectab[reclim].s = 0; | |
618 | } else { /* PL_BELOW */ | |
619 | rectab[reclim].n = 0; | |
620 | rectab[reclim].s = MIN(gs_p->c[RS], 0); | |
621 | } | |
622 | ||
623 | inc_reclim(); | |
624 | ||
625 | /* if a clef precedes this group, make a rectangle for it */ | |
626 | if (gs_p->clef != NOCLEF) { | |
627 | float north, south; /* clef coords */ | |
628 | ||
629 | rectab[reclim].e = gs_p->c[AW] - Staffscale * CLEFPAD; | |
630 | rectab[reclim].w = rectab[reclim].e - Staffscale * | |
631 | clefwidth(gs_p->clef, YES); | |
632 | (void)clefvert(gs_p->clef, YES, &north, &south); | |
633 | rectab[reclim].n = north * Staffscale; | |
634 | rectab[reclim].s = south * Staffscale; | |
635 | ||
636 | inc_reclim(); | |
637 | } | |
638 | ||
639 | /* | |
640 | * An additional rectangle is needed for each note that has an | |
641 | * accidental. This is because although the east/west group | |
642 | * boundaries include any accidentals, the north/south | |
643 | * boundaries ingore them. It needs to be this way because, | |
644 | * for other reasons, like ties, we want the north/south group | |
645 | * boundaries to consider only the note heads. But for general | |
646 | * stuff, the accidentals should also be considered. The | |
647 | * rectangles added below take care of this. | |
648 | * Similarly, if the top or bottom note is on a line and has a | |
649 | * dot in the space away from the group, it needs a rectangle. | |
650 | */ | |
651 | if (gs_p->grpcont == GC_NOTES && | |
652 | ! is_tab_staff(gs_p->staffno)) { | |
653 | for (n = 0; n < gs_p->nnotes; n++) { | |
654 | note_p = &gs_p->notelist[n]; | |
655 | ||
656 | if (gs_p->dots != 0 && | |
657 | note_p->stepsup % 2 == 0 && | |
658 | (n == 0 && note_p->ydotr > 0.0 || | |
659 | n == gs_p->nnotes - 1 && note_p->ydotr < 0.0)){ | |
660 | float radius; /* of a dot, + pad */ | |
661 | radius = Stdpad + Staffscale * | |
662 | ascent(FONT_MUSIC, (note_p-> | |
663 | notesize == GS_NORMAL ? | |
664 | DFLT_SIZE : SMALLSIZE), C_DOT); | |
665 | rectab[reclim].n = gs_p->c[RY] + | |
666 | note_p->ydotr + radius; | |
667 | rectab[reclim].s = gs_p->c[RY] + | |
668 | note_p->ydotr - radius; | |
669 | rectab[reclim].w = gs_p->c[AX] + | |
670 | gs_p->xdotr - radius; | |
671 | rectab[reclim].e = gs_p->c[AX] + | |
672 | gs_p->xdotr + radius + | |
673 | (gs_p->dots - 1) * 2.0 * | |
674 | (radius + Stdpad); | |
675 | inc_reclim(); | |
676 | } | |
677 | ||
678 | if (note_p->accidental == '\0') | |
679 | continue; | |
680 | ||
681 | /* this note has an acc; create a rectangle */ | |
682 | accdimen(note_p, &asc, &des, &wid); | |
683 | asc *= Staffscale; | |
684 | des *= Staffscale; | |
685 | wid *= Staffscale; | |
686 | ||
687 | rectab[reclim].w = gs_p->c[AX] + note_p->waccr; | |
688 | rectab[reclim].e = rectab[reclim].w + wid; | |
689 | rectab[reclim].n = note_p->c[RY] + asc; | |
690 | rectab[reclim].s = note_p->c[RY] - des; | |
691 | ||
692 | inc_reclim(); | |
693 | } | |
694 | } | |
695 | } | |
696 | } | |
697 | \f | |
698 | /* | |
699 | * Name: dobeamalt() | |
700 | * | |
701 | * Abstract: Set up rectangles for beams and alternation bars. | |
702 | * | |
703 | * Returns: void | |
704 | * | |
705 | * Description: This function puts into rectab rectangles for each beam or | |
706 | * alternation bar on this staff in this score, where the thing | |
707 | * is on the "place" side of the notes. | |
708 | */ | |
709 | ||
710 | static void | |
711 | dobeamalt(start_p, s, place) | |
712 | ||
713 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
714 | int s; /* staff number */ | |
715 | int place; /* above or below? */ | |
716 | ||
717 | { | |
718 | struct MAINLL *mainll_p; /* point along main linked list */ | |
719 | struct GRPSYL *gs_p; /* point along a GRPSYL linked list */ | |
720 | int v; /* voice number */ | |
721 | ||
722 | ||
723 | debug(32, "dobeamalt file=%s line=%d s=%d place=%d", start_p->inputfile, | |
724 | start_p->inputlineno, s, place); | |
725 | /* | |
726 | * Loop through this score's part of the MLL. | |
727 | */ | |
728 | for (mainll_p = start_p->next; mainll_p != 0 && | |
729 | mainll_p->str != S_FEED; mainll_p = mainll_p->next) { | |
730 | /* | |
731 | * Whenever we find a structure for this staff (another | |
732 | * measure of this staff), loop through its voices. | |
733 | */ | |
734 | if (mainll_p->str == S_STAFF && | |
735 | mainll_p->u.staff_p->staffno == s) { | |
736 | ||
737 | for (v = 0; v < MAXVOICES; v++) { | |
738 | for (gs_p = mainll_p->u.staff_p->groups_p[v]; | |
739 | gs_p != 0; gs_p = gs_p->next) { | |
740 | /* | |
741 | * Whenever we find the first group of | |
742 | * a nongrace beamed or alted set with | |
743 | * the stem direction on the side we | |
744 | * are dealing with, call onebeamalt() | |
745 | * to put rectangle(s) in rectab. | |
746 | * But not for cross staff beams. | |
747 | * Grace groups are included in the | |
748 | * following nongrace group's rectangle | |
749 | * already. | |
750 | */ | |
751 | if (gs_p->grpcont == GC_NOTES && | |
752 | gs_p->grpvalue == GV_NORMAL && | |
753 | gs_p->beamloc == STARTITEM && | |
754 | gs_p->beamto == CS_SAME) { | |
755 | ||
756 | if (place == PL_ABOVE && | |
757 | gs_p->stemdir == UP || | |
758 | place == PL_BELOW && | |
759 | gs_p->stemdir == DOWN) | |
760 | ||
761 | onebeamalt(gs_p); | |
762 | } | |
763 | } | |
764 | } | |
765 | } | |
766 | } | |
767 | } | |
768 | \f | |
769 | /* | |
770 | * Name: onebeamalt() | |
771 | * | |
772 | * Abstract: Set up rectangle(s) for one beam or alternation bar. | |
773 | * | |
774 | * Returns: void | |
775 | * | |
776 | * Description: This function puts zero or more rectangles in rectab for the | |
777 | * beam or alternation that starts at the given group. The longer | |
778 | * and more slanted the beam/alternation is, the more rectangles | |
779 | * will be necessary to enclose it without wasting a lot of space. | |
780 | * If the beam/alt lies within the staff, there's no need to make | |
781 | * any rectangles. All rectangles' inner edges are the center | |
782 | * staff line. | |
783 | */ | |
784 | ||
785 | static void | |
786 | onebeamalt(gs_p) | |
787 | ||
788 | struct GRPSYL *gs_p; /* initially points to first group */ | |
789 | ||
790 | { | |
791 | float stemshift; /* how far a stem is from its group's X */ | |
792 | float x1, y1; /* coords of left end of beam/alt */ | |
793 | float x2, y2; /* coords of right end of beam/alt */ | |
794 | ||
795 | ||
796 | /* | |
797 | * Set coords of the ends of the beam/alt. We are given the first | |
798 | * group, but must search forward to the end to find the last group, | |
799 | * being careful to ignore embedded grace groups. We adjust the X | |
800 | * coords (for groups that can have stems) because stems are offset | |
801 | * from their group's X. The Y coords can't always be based on the | |
802 | * group boundaries, because there might be "with" lists on the | |
803 | * abnormal (beam) side, and they don't affect the position of the beam. | |
804 | */ | |
805 | x1 = gs_p->c[AX]; | |
806 | y1 = getstemendvert(gs_p); | |
807 | ||
808 | while (gs_p != 0 && (gs_p->grpvalue == GV_ZERO || | |
809 | gs_p->beamloc != ENDITEM)) | |
810 | gs_p = gs_p->next; | |
811 | if (gs_p == 0) | |
812 | pfatal("beam or alt group has no ENDITEM"); | |
813 | ||
814 | x2 = gs_p->c[AX]; | |
815 | y2 = getstemendvert(gs_p); | |
816 | ||
817 | stemshift = getstemshift(gs_p); | |
818 | ||
819 | if (gs_p->basictime >= 2) { | |
820 | /* the groups have stems (if first one does, others must too)*/ | |
821 | if (gs_p->stemdir == UP) { | |
822 | x1 += stemshift; | |
823 | x2 += stemshift; | |
824 | } else { | |
825 | x1 -= stemshift; | |
826 | x2 -= stemshift; | |
827 | } | |
828 | } | |
829 | ||
830 | /* make zero or more rectangles for this beam/alt */ | |
831 | linerects(x1, y1, x2, y2, gs_p->stemdir, halfstaffhi(gs_p->staffno)); | |
832 | } | |
833 | \f | |
834 | /* | |
835 | * Name: getstemendvert() | |
836 | * | |
837 | * Abstract: Find the vertical coord of the end of a stem. | |
838 | * | |
839 | * Returns: void | |
840 | * | |
841 | * Description: This function is given a GRPSYL of a group that has either a | |
842 | * real, visible stem, or an invisible one (alt). If finds | |
843 | * the relative vertical coordinate of the end of the stems | |
844 | * farthest from the note head(s). | |
845 | */ | |
846 | ||
847 | static double | |
848 | getstemendvert(gs_p) | |
849 | ||
850 | struct GRPSYL *gs_p; /* the group in question */ | |
851 | ||
852 | { | |
853 | double y; /* the answer */ | |
854 | ||
855 | ||
856 | if (gs_p->nwith == 0 || gs_p->normwith == YES) { | |
857 | /* | |
858 | * Either there is no "with" list, or it's on the notes' end | |
859 | * of the stem. So we can use the group boundary. | |
860 | */ | |
861 | y = gs_p->stemdir == UP ? gs_p->c[RN] : gs_p->c[RS]; | |
862 | } else { | |
863 | /* | |
864 | * There is a "with" list at this end of the stem. Find where | |
865 | * the end of the stem is by applying the stem's length to the | |
866 | * farthest note on the opposite side. | |
867 | */ | |
868 | if (gs_p->stemdir == UP) | |
869 | y = gs_p->notelist[ gs_p->nnotes - 1 ].c[RY] + | |
870 | gs_p->stemlen; | |
871 | else | |
872 | y = gs_p->notelist[ 0 ].c[RY] - gs_p->stemlen; | |
873 | } | |
874 | ||
875 | /* counteract the stem shortening that was done in finalstemadjust() */ | |
876 | if (gs_p->beamloc != NOITEM) { | |
877 | if (gs_p->stemdir == UP) { | |
878 | y += (W_WIDE * Stdpad / 2.0); | |
879 | } else { | |
880 | y -= (W_WIDE * Stdpad / 2.0); | |
881 | } | |
882 | } | |
883 | ||
884 | return (y); | |
885 | } | |
886 | \f | |
887 | /* | |
888 | * Name: linerects() | |
889 | * | |
890 | * Abstract: Set up rectangle(s) to contain a (possibly) slanted line. | |
891 | * | |
892 | * Returns: void | |
893 | * | |
894 | * Description: This function puts zero or more rectangles in rectab to contain | |
895 | * a (possibly) slanted line. The longer and more slanted the | |
896 | * line is, the more rectangles will be necessary to enclose it | |
897 | * without wasting a lot of space. If the line lies within the | |
898 | * staff, there's no need to make any rectangles. All rectangles' | |
899 | * inner edges are the center staff line. | |
900 | */ | |
901 | ||
902 | static void | |
903 | linerects(x1, y1, x2, y2, side, halfstaff) | |
904 | ||
905 | double x1, y1; /* coords of left end of line */ | |
906 | double x2, y2; /* coords of right end of line */ | |
907 | int side; /* side to favor, UP or DOWN */ | |
908 | double halfstaff; /* half the staff height */ | |
909 | ||
910 | { | |
911 | float slope, yintercept;/* of a line a STDPAD beyond beam/alt */ | |
912 | float deltax; /* width of one rectangle */ | |
913 | float leftx, rightx; /* X coord of sides of a rectangle */ | |
914 | ||
915 | ||
916 | /* if line is within staff, no need for any rectangles */ | |
917 | if (fabs(y1) < halfstaff && fabs(y2) < halfstaff) | |
918 | return; | |
919 | ||
920 | /* | |
921 | * If this beam/alt is level, make one big rectangle, and get out. | |
922 | */ | |
923 | if (y1 == y2) { | |
924 | rectab[reclim].w = x1; | |
925 | rectab[reclim].e = x2; | |
926 | if (side == UP) { | |
927 | rectab[reclim].n = y1; | |
928 | rectab[reclim].s = 0; | |
929 | } else { | |
930 | rectab[reclim].n = 0; | |
931 | rectab[reclim].s = y1; | |
932 | } | |
933 | inc_reclim(); | |
934 | return; | |
935 | } | |
936 | ||
937 | /* | |
938 | * We may need multiple rectangles. Make them narrow enough so that | |
939 | * the change in Y across the width of one is one STEPSIZE. The | |
940 | * rightmost one will probably be narrower, using whatever room | |
941 | * remains. The equation of our line is y = slope * x + yintercept. | |
942 | */ | |
943 | slope = (y1 - y2) / (x1 - x2); | |
944 | yintercept = y1 - slope * x1; | |
945 | deltax = Stepsize / fabs(slope); | |
946 | ||
947 | for (leftx = x1; leftx < x2; leftx += deltax) { | |
948 | rightx = MIN(x2, leftx + deltax); | |
949 | rectab[reclim].w = leftx; | |
950 | rectab[reclim].e = rightx; | |
951 | if (side == UP) { | |
952 | rectab[reclim].n = slope * (slope > 0 ? rightx : leftx) | |
953 | + yintercept; | |
954 | rectab[reclim].s = 0; | |
955 | } else { | |
956 | rectab[reclim].n = 0; | |
957 | rectab[reclim].s = slope * (slope > 0 ? leftx : rightx) | |
958 | + yintercept; | |
959 | } | |
960 | inc_reclim(); | |
961 | } | |
962 | } | |
963 | \f | |
964 | /* | |
965 | * Name: docurve() | |
966 | * | |
967 | * Abstract: Get point list and set up rectangles for tie/slur/bend/phrase. | |
968 | * | |
969 | * Returns: void | |
970 | * | |
971 | * Description: This function goes through all ties, slurs, bends, phrases for | |
972 | * staff. The first time it is called for a staff (which is for | |
973 | * place "above") it calls a function to set up the curve list. | |
974 | * Whichever time it is called, it calls a function to put | |
975 | * rectangles in rectab. | |
976 | */ | |
977 | ||
978 | static void | |
979 | docurve(start_p, s, place, do_which) | |
980 | ||
981 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
982 | int s; /* staff number */ | |
983 | int place; /* above or below? */ | |
984 | int do_which; /* which stuff types are to be handled */ | |
985 | ||
986 | { | |
987 | struct MAINLL *mainll_p; /* loop through main linked list */ | |
988 | struct STUFF *stuff_p; /* point along a STUFF list */ | |
989 | float halfstaff; /* half the staff height */ | |
990 | ||
991 | ||
992 | debug(32, "docurve file=%s line=%d s=%d place=%d do_which=%d", | |
993 | start_p->inputfile, start_p->inputlineno, s, place, do_which); | |
994 | halfstaff = halfstaffhi(s); | |
995 | ||
996 | /* | |
997 | * Loop through this score's part of the MLL, looking for matching | |
998 | * staffs. | |
999 | */ | |
1000 | for (mainll_p = start_p->next; mainll_p != 0 && | |
1001 | mainll_p->str != S_FEED; mainll_p = mainll_p->next) { | |
1002 | ||
1003 | if (mainll_p->str != S_STAFF || | |
1004 | mainll_p->u.staff_p->staffno != s) | |
1005 | continue; | |
1006 | ||
1007 | /* loop through each stuff of the indicated type */ | |
1008 | for (stuff_p = mainll_p->u.staff_p->stuff_p; | |
1009 | stuff_p != 0; stuff_p = stuff_p->next){ | |
1010 | ||
1011 | switch (stuff_p->stuff_type) { | |
1012 | case ST_TIESLUR: | |
1013 | case ST_TABSLUR: | |
1014 | case ST_BEND: | |
1015 | case ST_PHRASE: | |
1016 | break; /* docurve works on these */ | |
1017 | default: | |
1018 | continue; /* for some other function */ | |
1019 | } | |
1020 | ||
1021 | /* | |
1022 | * If we are to do phrases and this is not a phrase, or | |
1023 | * vice versa, skip this. | |
1024 | */ | |
1025 | if ((do_which == DO_PHRASE) != | |
1026 | (stuff_p->stuff_type == ST_PHRASE)) | |
1027 | continue; | |
1028 | ||
1029 | /* | |
1030 | * When we're in here the first time (for PL_ABOVE), | |
1031 | * call a function to set up the curve list. For | |
1032 | * everything but ST_PHRASE it also sets "place". | |
1033 | */ | |
1034 | if (place == PL_ABOVE) { | |
1035 | switch (stuff_p->stuff_type) { | |
1036 | case ST_TIESLUR: | |
1037 | /* don't call tieslur_points now if the | |
1038 | * positions of the tie/slur's endpoints | |
1039 | * would change later due to CSS */ | |
1040 | if (css_affects_tieslurbend(stuff_p, | |
1041 | mainll_p) == YES) { | |
1042 | break; | |
1043 | } | |
1044 | tieslur_points(mainll_p, stuff_p); | |
1045 | break; | |
1046 | case ST_TABSLUR: | |
1047 | tabslur_points(mainll_p, stuff_p); | |
1048 | break; | |
1049 | case ST_BEND: | |
1050 | /* don't call bend_points now if the | |
1051 | * positions of the bend's endpoints | |
1052 | * would change later due to CSS */ | |
1053 | if (css_affects_tieslurbend(stuff_p, | |
1054 | mainll_p) == YES) { | |
1055 | break; | |
1056 | } | |
1057 | bend_points(mainll_p, stuff_p); | |
1058 | break; | |
1059 | case ST_PHRASE: | |
1060 | /* don't call phrase_points now if the | |
1061 | * positions of the phrase's endpoints | |
1062 | * would change later due to CSS */ | |
1063 | if (css_affects_phrase(stuff_p, | |
1064 | mainll_p) == YES) { | |
1065 | break; | |
1066 | } | |
1067 | phrase_points(mainll_p, stuff_p); | |
1068 | break; | |
1069 | } | |
1070 | } | |
1071 | ||
1072 | /* | |
1073 | * Make rectangles no matter what side of the staff the | |
1074 | * curve is supposed to be on, because, depending on | |
1075 | * how high or low the notes are, rectangles may be | |
1076 | * needed even on the opposite side you'd expect. | |
1077 | */ | |
1078 | if (stuff_p->crvlist_p != 0) { | |
1079 | curverect(s, stuff_p, halfstaff); | |
1080 | } | |
1081 | } | |
1082 | } | |
1083 | } | |
1084 | \f | |
1085 | /* | |
1086 | * Name: curverect() | |
1087 | * | |
1088 | * Abstract: Put rectangles in rectab for a tie, slur, bend, or phrase. | |
1089 | * | |
1090 | * Returns: void | |
1091 | * | |
1092 | * Description: This function puts rectangles in rectab for a tie, slur, bend, | |
1093 | * or phrase. Each segment of the curve gets one or more | |
1094 | * rectangles, depending on how long and how slanted it is. To do | |
1095 | * this, we call curvepiecerect(). | |
1096 | */ | |
1097 | ||
1098 | static void | |
1099 | curverect(s, stuff_p, halfstaff) | |
1100 | ||
1101 | int s; /* staff number */ | |
1102 | struct STUFF *stuff_p; /* the curve's STUFF */ | |
1103 | double halfstaff; /* half the staff height */ | |
1104 | ||
1105 | { | |
1106 | struct CRVLIST *point_p; /* point at a phrase point */ | |
1107 | float x1, y1; /* coords of left end of a segment */ | |
1108 | float x2, y2; /* coords of right end of a segment */ | |
1109 | float midx, midy; /* middle of one segment of a curve */ | |
1110 | ||
1111 | ||
1112 | /* | |
1113 | * Loop through the curve list. For each pair of neighboring points, | |
1114 | * there is a segment of the curve. For items that are actually | |
1115 | * straight line segments, call curvepiecerect() once. But for actual | |
1116 | * curves, find the midpoint, and call curvepiecerect() for each half. | |
1117 | * This way we more closely approximate the real curve. | |
1118 | */ | |
1119 | for (point_p = stuff_p->crvlist_p; point_p->next != 0; | |
1120 | point_p = point_p->next) { | |
1121 | ||
1122 | x1 = point_p->x; | |
1123 | y1 = point_p->y; | |
1124 | x2 = point_p->next->x; | |
1125 | y2 = point_p->next->y; | |
1126 | ||
1127 | if (stuff_p->stuff_type == ST_BEND || | |
1128 | stuff_p->stuff_type == ST_TABSLUR) { | |
1129 | /* bend, or slur on tab or tabnote */ | |
1130 | curvepiecerect(x1, y1, x2, y2, halfstaff); | |
1131 | } else { | |
1132 | /* a real curve */ | |
1133 | midx = (x1 + x2) / 2.0; | |
1134 | midy = curve_y_at_x(stuff_p->crvlist_p, midx); | |
1135 | curvepiecerect(x1, y1, midx, midy, halfstaff); | |
1136 | curvepiecerect(midx, midy, x2, y2, halfstaff); | |
1137 | } | |
1138 | } | |
1139 | } | |
1140 | \f | |
1141 | /* | |
1142 | * Name: curvepiecerect() | |
1143 | * | |
1144 | * Abstract: Put rects in rectab for a piece of a tie, slur, bend, or phrase. | |
1145 | * | |
1146 | * Returns: void | |
1147 | * | |
1148 | * Description: This function puts rectangles in rectab for one piece of a | |
1149 | * curve. The piece gets one or more rectangles, depending on how | |
1150 | * long and how slanted it is. | |
1151 | */ | |
1152 | ||
1153 | static void | |
1154 | curvepiecerect(x1, y1, x2, y2, halfstaff) | |
1155 | ||
1156 | double x1, y1; /* coords of left end of the piece */ | |
1157 | double x2, y2; /* coords of right end of the piece */ | |
1158 | double halfstaff; /* half the staff height */ | |
1159 | ||
1160 | { | |
1161 | float slope, yintercept;/* of a line a segment */ | |
1162 | float deltax; /* width of one rectangle */ | |
1163 | float leftx, rightx; /* X coord of sides of a rectangle */ | |
1164 | ||
1165 | ||
1166 | /* if whole piece is within the staff, no rectangles are needed */ | |
1167 | if (fabs(y1) < halfstaff && fabs(y2) < halfstaff) | |
1168 | return; | |
1169 | ||
1170 | /* | |
1171 | * If this piece is level, make 1 big rectangle, and continue. | |
1172 | */ | |
1173 | if (y1 == y2) { | |
1174 | rectab[reclim].w = x1; | |
1175 | rectab[reclim].e = x2; | |
1176 | rectab[reclim].n = MAX(y1 + 2 * Stdpad, 0.0); | |
1177 | rectab[reclim].s = MIN(y1 - 2 * Stdpad, 0.0); | |
1178 | inc_reclim(); | |
1179 | return; | |
1180 | } | |
1181 | ||
1182 | /* | |
1183 | * We may need multiple rectangles. Make them narrow enough so that | |
1184 | * the change in Y across the width of one is one Stepsize. The | |
1185 | * rightmost one will probably be narrower, using whatever room | |
1186 | * remains. The equation of our line is | |
1187 | * y = slope * x + yintercept | |
1188 | * Initially each rectangle only includes its segment (plus padding), | |
1189 | * but then we extend it to reach the center line of the staff. | |
1190 | */ | |
1191 | slope = (y1 - y2) / (x1 - x2); | |
1192 | yintercept = y1 - slope * x1; | |
1193 | deltax = Stepsize / fabs(slope); | |
1194 | ||
1195 | for (leftx = x1; leftx < x2; leftx += deltax) { | |
1196 | rightx = MIN(x2, leftx + deltax); | |
1197 | ||
1198 | rectab[reclim].w = leftx; | |
1199 | rectab[reclim].e = rightx; | |
1200 | ||
1201 | /* | |
1202 | * For north and south boundaries, use the side of the rect | |
1203 | * that sticks out more, to err on the side of making the rect | |
1204 | * big enough. Also add in padding, to 1) allow for the fact | |
1205 | * that the real curve probably bulges out beyond our segment | |
1206 | * approximation, and 2) because we don't want anything | |
1207 | * actually touching the curve. | |
1208 | */ | |
1209 | rectab[reclim].n = slope * (slope > 0.0 ? rightx : leftx) + | |
1210 | yintercept + 2.0 * Stdpad; | |
1211 | rectab[reclim].s = slope * (slope < 0.0 ? rightx : leftx) + | |
1212 | yintercept - 2.0 * Stdpad; | |
1213 | ||
1214 | /* rectangle must reach the center line of the staff */ | |
1215 | if (rectab[reclim].n < 0.0) | |
1216 | rectab[reclim].n = 0.0; | |
1217 | if (rectab[reclim].s > 0.0) | |
1218 | rectab[reclim].s = 0.0; | |
1219 | ||
1220 | inc_reclim(); | |
1221 | } | |
1222 | } | |
1223 | \f | |
1224 | /* | |
1225 | * Name: dotuplet() | |
1226 | * | |
1227 | * Abstract: Set up rectangles for tuplet brackets. | |
1228 | * | |
1229 | * Returns: void | |
1230 | * | |
1231 | * Description: This function puts into rectab rectangles for each tuplet | |
1232 | * bracket on this staff in this score, where the thing is on | |
1233 | * the "place" side of the notes. | |
1234 | */ | |
1235 | ||
1236 | ||
1237 | static void | |
1238 | dotuplet(start_p, s, place) | |
1239 | ||
1240 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
1241 | int s; /* staff number */ | |
1242 | int place; /* above or below? */ | |
1243 | ||
1244 | { | |
1245 | struct MAINLL *mainll_p; /* point along main linked list */ | |
1246 | struct GRPSYL *gs_p; /* point along a GRPSYL linked list */ | |
1247 | int v; /* voice number */ | |
1248 | ||
1249 | ||
1250 | debug(32, "dotuplet file=%s line=%d s=%d place=%d", start_p->inputfile, | |
1251 | start_p->inputlineno, s, place); | |
1252 | ||
1253 | /* tuplet brackets are never printed on tablature staffs */ | |
1254 | if (is_tab_staff(s)) | |
1255 | return; | |
1256 | ||
1257 | /* | |
1258 | * Loop through this score's part of the MLL. | |
1259 | */ | |
1260 | for (mainll_p = start_p->next; mainll_p != 0 && | |
1261 | mainll_p->str != S_FEED; mainll_p = mainll_p->next) { | |
1262 | /* | |
1263 | * Whenever we find a structure for this staff (another | |
1264 | * measure of this staff), loop through its voices. | |
1265 | */ | |
1266 | if (mainll_p->str == S_STAFF && | |
1267 | mainll_p->u.staff_p->staffno == s) { | |
1268 | ||
1269 | for (v = 0; v < MAXVOICES; v++) { | |
1270 | for (gs_p = mainll_p->u.staff_p->groups_p[v]; | |
1271 | gs_p != 0; gs_p = gs_p->next) { | |
1272 | /* | |
1273 | * Whenever we find the first group of | |
1274 | * a tuplet with a bracket on the | |
1275 | * "place" side of the group, call | |
1276 | * onetuplet() to put rectangle(s) in | |
1277 | * rectab. | |
1278 | */ | |
1279 | if ((gs_p->tuploc == STARTITEM || | |
1280 | gs_p->tuploc == LONEITEM) && | |
1281 | gs_p->printtup != PT_NEITHER) { | |
1282 | ||
1283 | if (tupdir(gs_p, mainll_p->u. | |
1284 | staff_p) == place) | |
1285 | ||
1286 | onetuplet(mainll_p->u. | |
1287 | staff_p, gs_p, place); | |
1288 | } | |
1289 | } | |
1290 | } | |
1291 | } | |
1292 | } | |
1293 | } | |
1294 | \f | |
1295 | /* | |
1296 | * Name: onetuplet() | |
1297 | * | |
1298 | * Abstract: Set up rectangle(s) for one tuplet bracket or number. | |
1299 | * | |
1300 | * Returns: void | |
1301 | * | |
1302 | * Description: If this tuplet is not going to be given a bracket (like because | |
1303 | * its notes are already beamed), this function just makes one | |
1304 | * rectangle, for the number. Otherwise, this function puts zero | |
1305 | * or more rectangles in rectab for the tuplet that starts at the | |
1306 | * given group. The longer and more slanted the tuplet bracket | |
1307 | * is, the more rectangles will be necessary to enclose it without | |
1308 | * wasting a lot of space. All rectangles' inner edges are the | |
1309 | * center staff line. | |
1310 | */ | |
1311 | ||
1312 | static void | |
1313 | onetuplet(staff_p, start_p, place) | |
1314 | ||
1315 | struct STAFF *staff_p; /* point to the staff we're on */ | |
1316 | struct GRPSYL *start_p; /* points to first group in tuplet */ | |
1317 | int place; /* above or below? */ | |
1318 | ||
1319 | { | |
1320 | struct GRPSYL *gs_p; /* point to a group in tuplet */ | |
1321 | float stemshift; /* how far a stem is from its group's X */ | |
1322 | float x1, y1; /* coords of left end of beam/alt */ | |
1323 | float x2, y2; /* coords of right end of beam/alt */ | |
1324 | float numeast, numwest; /* horizontal coords of the tuplet number */ | |
1325 | float height; /* height of the tuplet number */ | |
1326 | ||
1327 | ||
1328 | /* | |
1329 | * Set coords of the ends of the tuplet. We are given the first | |
1330 | * group, but must search forward to the end to find the last group, | |
1331 | * being careful to ignore embedded grace groups. We adjust the X | |
1332 | * coords because brackets reach beyond their group's X. | |
1333 | */ | |
1334 | x1 = start_p->c[AX]; | |
1335 | y1 = (place == PL_ABOVE ? start_p->c[RN] : start_p->c[RS]) | |
1336 | + start_p->tupextend; | |
1337 | ||
1338 | for (gs_p = start_p; gs_p != 0 && (gs_p->grpvalue == GV_ZERO || | |
1339 | gs_p->tuploc != ENDITEM && gs_p->tuploc != LONEITEM); | |
1340 | gs_p = gs_p->next) | |
1341 | ; | |
1342 | if (gs_p == 0) | |
1343 | pfatal("tuplet has no ENDITEM"); | |
1344 | ||
1345 | x2 = gs_p->c[AX]; | |
1346 | y2 = (place == PL_ABOVE ? gs_p->c[RN] : gs_p->c[RS]) + gs_p->tupextend; | |
1347 | ||
1348 | /* | |
1349 | * If there is not going to be a bracket, create one rectangle for the | |
1350 | * tuplet number, and return. | |
1351 | */ | |
1352 | if (tupgetsbrack(start_p) == NO) { | |
1353 | (void)tupnumsize(start_p, &numwest, &numeast, &height, staff_p); | |
1354 | rectab[reclim].n = (y1 + y2) / 2 + height / 2; | |
1355 | rectab[reclim].s = (y1 + y2) / 2 - height / 2; | |
1356 | rectab[reclim].w = numwest; | |
1357 | rectab[reclim].e = numeast; | |
1358 | ||
1359 | inc_reclim(); | |
1360 | return; | |
1361 | } | |
1362 | ||
1363 | /* there is going to be a bracket; extend x coords to reach to end */ | |
1364 | stemshift = getstemshift(gs_p); | |
1365 | ||
1366 | x1 -= stemshift; | |
1367 | x2 += stemshift; | |
1368 | ||
1369 | /* make zero or more rectangles for this bracket */ | |
1370 | linerects(x1, y1, x2, y2, place == PL_ABOVE ? UP : DOWN, | |
1371 | halfstaffhi(gs_p->staffno)); | |
1372 | } | |
1373 | \f | |
1374 | /* | |
1375 | * Name: domiscstuff() | |
1376 | * | |
1377 | * Abstract: Set up rectangles and vert coords for miscellaneous STUFF. | |
1378 | * | |
1379 | * Returns: void | |
1380 | * | |
1381 | * Description: This function puts into rectab a rectangle for each STUFF | |
1382 | * structure in the "place" relationship to the given staff on | |
1383 | * this score, except for stuff types that have special, | |
1384 | * dedicated functions for their type. It also sets their | |
1385 | * relative vertical coordinates. | |
1386 | */ | |
1387 | ||
1388 | static void | |
1389 | domiscstuff(start_p, s, place, do_which) | |
1390 | ||
1391 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
1392 | int s; /* staff number */ | |
1393 | int place; /* above, below, or between? */ | |
1394 | unsigned long do_which; /* which stuff types are to be handled */ | |
1395 | ||
1396 | { | |
1397 | struct MAINLL *mainll_p; /* loop through main linked list */ | |
1398 | struct STUFF *stuff_p; /* point along a STUFF list */ | |
1399 | float high; /* height of a rectangle */ | |
1400 | float len; /* length of a cresc/descresc */ | |
1401 | float lowpart; /* dist between stuff's Y and S */ | |
1402 | float dist; /* how close chord can get to staff */ | |
1403 | int stype; /* stuff type */ | |
1404 | ||
1405 | ||
1406 | debug(32, "domiscstuff file=%s line=%d s=%d place=%d do_which=%ld", | |
1407 | start_p->inputfile, start_p->inputlineno, s, place, do_which); | |
1408 | /* | |
1409 | * Loop through this score's part of the MLL. Whenever we find a | |
1410 | * structure for this staff (another measure), loop through its | |
1411 | * STUFF list, dealing with each STUFF that is above, below, or | |
1412 | * between, as specified by "place". | |
1413 | */ | |
1414 | for (mainll_p = start_p->next; mainll_p != 0 && | |
1415 | mainll_p->str != S_FEED; mainll_p = mainll_p->next) { | |
1416 | ||
1417 | if (mainll_p->str != S_STAFF || | |
1418 | mainll_p->u.staff_p->staffno != s) { | |
1419 | continue; | |
1420 | } | |
1421 | ||
1422 | for (stuff_p = mainll_p->u.staff_p->stuff_p; | |
1423 | stuff_p != 0; stuff_p = stuff_p->next) { | |
1424 | ||
1425 | if (stuff_p->place != place) { | |
1426 | continue; | |
1427 | } | |
1428 | ||
1429 | stype = stuff_p->stuff_type; | |
1430 | ||
1431 | /* if wrong type for this pass, exit */ | |
1432 | if (stype == ST_MUSSYM) { | |
1433 | if ((do_which & (1L << MK_MUSSYM)) == 0) | |
1434 | continue; | |
1435 | } else if (stype == ST_OCTAVE) { | |
1436 | if ((do_which & (1L << MK_OCTAVE)) == 0) | |
1437 | continue; | |
1438 | } else if (stype != ST_PHRASE && | |
1439 | stuff_p->modifier == TM_DYN) { | |
1440 | if ((do_which & (1L << MK_DYN)) == 0) | |
1441 | continue; | |
1442 | } else if (stype != ST_PHRASE && | |
1443 | IS_CHORDLIKE(stuff_p->modifier)) { | |
1444 | if ((do_which & (1L << MK_CHORD)) == 0) | |
1445 | continue; | |
1446 | } else if (IS_TEXT(stype)) { | |
1447 | if ((do_which & (1L << MK_OTHERTEXT)) == 0) | |
1448 | continue; | |
1449 | } | |
1450 | ||
1451 | /* | |
1452 | * We found a "stuff" that needs to be positioned. | |
1453 | * First find its total height, and the height of the | |
1454 | * part of it below its Y coord. | |
1455 | */ | |
1456 | /* avoid 'used before set' warning */ | |
1457 | high = lowpart = 0.0; | |
1458 | ||
1459 | /* handle various types differently */ | |
1460 | switch (stype) { | |
1461 | case ST_PEDAL: | |
1462 | case ST_PHRASE: | |
1463 | case ST_TIESLUR: | |
1464 | case ST_TABSLUR: | |
1465 | case ST_BEND: | |
1466 | case ST_MIDI: | |
1467 | /* don't handle these types here; */ | |
1468 | /* they have their own subroutines */ | |
1469 | continue; | |
1470 | ||
1471 | case ST_OCTAVE: | |
1472 | case ST_ROM: | |
1473 | case ST_BOLD: | |
1474 | case ST_ITAL: | |
1475 | case ST_BOLDITAL: | |
1476 | case ST_MUSSYM: | |
1477 | /* high is string's height */ | |
1478 | high = strheight( stuff_p->string); | |
1479 | lowpart = strdescent( stuff_p->string); | |
1480 | ||
1481 | /* | |
1482 | * If a chord grid is to be printed under the | |
1483 | * string, the Y and N of the stuff remain | |
1484 | * unchanged, but its S is lowered by the total | |
1485 | * height of the grid. So add its height to | |
1486 | * both "high" and "lowpart". | |
1487 | */ | |
1488 | if (stuff_p->modifier == TM_CHORD && svpath(s, | |
1489 | GRIDSWHEREUSED)->gridswhereused == YES) { | |
1490 | struct GRID *grid_p; | |
1491 | float gnorth, gsouth; | |
1492 | ||
1493 | grid_p = findgrid(stuff_p->string); | |
1494 | /* if none, skip this; stuff.c warned*/ | |
1495 | if (grid_p == 0) | |
1496 | break; | |
1497 | ||
1498 | gridsize(grid_p, stuff_p->all ? 0 : | |
1499 | mainll_p->u.staff_p->staffno, | |
1500 | &gnorth, &gsouth, | |
1501 | (float *)0, (float *)0); | |
1502 | ||
1503 | high += gnorth - gsouth; | |
1504 | lowpart += gnorth - gsouth; | |
1505 | } | |
1506 | break; | |
1507 | ||
1508 | case ST_CRESC: | |
1509 | case ST_DECRESC: | |
1510 | /* height depends on length */ | |
1511 | len = stuff_p->c[AE] - stuff_p->c[AW]; | |
1512 | ||
1513 | if (len < 0.5) | |
1514 | high = 2.00 * STEPSIZE + 2 * STDPAD; | |
1515 | else if (len < 2.0) | |
1516 | high = 2.67 * STEPSIZE + 2 * STDPAD; | |
1517 | else | |
1518 | high = 3.33 * STEPSIZE + 2 * STDPAD; | |
1519 | ||
1520 | if (stuff_p->all) | |
1521 | high *= Score.staffscale; | |
1522 | else | |
1523 | high *= Staffscale; | |
1524 | ||
1525 | lowpart = high / 2; | |
1526 | ||
1527 | break; | |
1528 | ||
1529 | default: | |
1530 | pfatal("unknown stuff type (%d)", stype); | |
1531 | } | |
1532 | ||
1533 | /* | |
1534 | * Now find "dist", the minimum distance it should be | |
1535 | * put from the staff. | |
1536 | */ | |
1537 | if (stuff_p->dist_usage == SD_NONE) { | |
1538 | /* | |
1539 | * The user didn't specify the dist, so we get | |
1540 | * it from the appropriate parameter or hard- | |
1541 | * coded value, as the case may be. For | |
1542 | * parameters, if the stuff belongs to the | |
1543 | * score as a whole ("all"), use the Score | |
1544 | * value instead of svpath. | |
1545 | */ | |
1546 | /* if "dyn", fake to use same logic as cresc */ | |
1547 | if (stuff_p->modifier == TM_DYN) | |
1548 | stype = ST_CRESC; | |
1549 | switch (stype) { | |
1550 | case ST_ROM: | |
1551 | case ST_BOLD: | |
1552 | case ST_ITAL: | |
1553 | case ST_BOLDITAL: | |
1554 | if (stuff_p->all) { | |
1555 | if (IS_CHORDLIKE( | |
1556 | stuff_p->modifier)) { | |
1557 | dist = | |
1558 | halfstaffhi(s) + | |
1559 | STEPSIZE * | |
1560 | Score.staffscale * | |
1561 | Score.chorddist; | |
1562 | } else { | |
1563 | dist = | |
1564 | halfstaffhi(s) + | |
1565 | STEPSIZE * | |
1566 | Score.staffscale * | |
1567 | Score.dist; | |
1568 | } | |
1569 | } else { | |
1570 | if (IS_CHORDLIKE( | |
1571 | stuff_p->modifier)) { | |
1572 | dist = | |
1573 | halfstaffhi(s) + | |
1574 | Stepsize * | |
1575 | svpath(s, CHORDDIST)-> | |
1576 | chorddist; | |
1577 | } else { | |
1578 | dist = | |
1579 | halfstaffhi(s) + | |
1580 | Stepsize * | |
1581 | svpath(s, DIST)->dist; | |
1582 | } | |
1583 | } | |
1584 | break; | |
1585 | case ST_CRESC: | |
1586 | case ST_DECRESC: | |
1587 | if (stuff_p->all) { | |
1588 | dist = halfstaffhi(s) + | |
1589 | STEPSIZE * Score.staffscale * | |
1590 | Score.dyndist; | |
1591 | } else { | |
1592 | dist = halfstaffhi(s) + | |
1593 | Stepsize * svpath(s, | |
1594 | DYNDIST)->dyndist; | |
1595 | } | |
1596 | break; | |
1597 | default: | |
1598 | dist = 0; | |
1599 | break; | |
1600 | } | |
1601 | } else { | |
1602 | /* the user specified the dist, so use that */ | |
1603 | if (stuff_p->all) { | |
1604 | dist = halfstaffhi(s) + | |
1605 | STEPSIZE * stuff_p->dist; | |
1606 | } else { | |
1607 | dist = halfstaffhi(s) + | |
1608 | Stepsize * stuff_p->dist; | |
1609 | } | |
1610 | } | |
1611 | ||
1612 | if (stuff_p->dist_usage == SD_FORCE) { | |
1613 | /* | |
1614 | * The user is forcing this dist, so don't | |
1615 | * stack; just put it there. Note: the user | |
1616 | * cannot specify "dist" for "between" items. | |
1617 | */ | |
1618 | if (stuff_p->place == PL_ABOVE) { | |
1619 | rectab[reclim].n = dist + high; | |
1620 | rectab[reclim].s = dist; | |
1621 | stuff_p->c[RS] = dist; | |
1622 | } else { /* PL_BELOW */ | |
1623 | rectab[reclim].n = -dist; | |
1624 | rectab[reclim].s = -dist - high; | |
1625 | stuff_p->c[RS] = -dist - high; | |
1626 | } | |
1627 | rectab[reclim].e = stuff_p->c[AE]; | |
1628 | rectab[reclim].w = stuff_p->c[AW]; | |
1629 | inc_reclim(); | |
1630 | } else { | |
1631 | /* | |
1632 | * Stack the usual way. For the case of | |
1633 | * "between", stackit() will ignore "dist". | |
1634 | */ | |
1635 | stuff_p->c[RS] = stackit(stuff_p->c[AW], | |
1636 | stuff_p->c[AE], high, dist, place); | |
1637 | } | |
1638 | ||
1639 | stuff_p->c[RN] = stuff_p->c[RS] + high; | |
1640 | stuff_p->c[RY] = stuff_p->c[RS] + lowpart; | |
1641 | } | |
1642 | } | |
1643 | } | |
1644 | \f | |
1645 | /* | |
1646 | * Name: dolyrics() | |
1647 | * | |
1648 | * Abstract: Set up rectangles and vert coords for lyrics. | |
1649 | * | |
1650 | * Returns: void | |
1651 | * | |
1652 | * Description: This function puts into rectab a rectangle for each verse in | |
1653 | * the "place" relationship to the given staff on this score. | |
1654 | */ | |
1655 | ||
1656 | static void | |
1657 | dolyrics(start_p, s, place) | |
1658 | ||
1659 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
1660 | int s; /* staff number */ | |
1661 | int place; /* above, below, or between? */ | |
1662 | ||
1663 | { | |
1664 | int *versenums; /* malloc'ed array of verse numbers in score */ | |
1665 | struct MAINLL *mainll_p;/* point along main linked list */ | |
1666 | struct STAFF *staff_p; /* point at a staff structure */ | |
1667 | struct GRPSYL *gs_p; /* point at a syllable */ | |
1668 | float protrude; /* farthest protrusion of rectangle */ | |
1669 | int vfound; /* number of verse numbers found in score */ | |
1670 | int v; /* verse number */ | |
1671 | int begin, end, delta; /* for looping over verses in proper order */ | |
1672 | float dist; /* how close lyrics can get to staff */ | |
1673 | float farwest, fareast; /* farthest east and west of any syllable */ | |
1674 | float baseline; /* baseline of a verse of syllables */ | |
1675 | float maxasc, maxdes; /* max ascent & descent of syllables */ | |
1676 | int gotverse0; /* is there a verse 0 (centered verse)? */ | |
1677 | int gototherverse; /* is there a normal verse (not 0)? */ | |
1678 | int n, k, j; /* loop variables */ | |
1679 | ||
1680 | ||
1681 | debug(32, "dolyrics file=%s line=%d s=%d place=%d", start_p->inputfile, | |
1682 | start_p->inputlineno, s, place); | |
1683 | /* if there are no lyrics in this song, get out now */ | |
1684 | if (Maxverses == 0) | |
1685 | return; | |
1686 | ||
1687 | /* | |
1688 | * Allocate an array containing room for all the verse numbers used in | |
1689 | * this score. Maxverses is the number of verse numbers used in the | |
1690 | * whole user input, so this will certainly be enough. | |
1691 | */ | |
1692 | MALLOCA(int, versenums, Maxverses); | |
1693 | ||
1694 | /* | |
1695 | * Loop through this score's part of the MLL, noting whether verse 0 | |
1696 | * (the centered verse) and/or other verses exist on the "place" side | |
1697 | * of the staff. We have to find this out before actually processing | |
1698 | * the verses, because verse 0 is to be treated as a normal verse if | |
1699 | * and only if there are no other verses. | |
1700 | */ | |
1701 | gotverse0 = NO; | |
1702 | gototherverse = NO; | |
1703 | for (mainll_p = start_p->next; mainll_p != 0 && | |
1704 | mainll_p->str != S_FEED; mainll_p = mainll_p->next) { | |
1705 | /* | |
1706 | * Whenever we find a structure for this staff (another | |
1707 | * measure of this staff), loop through its verse headcells. | |
1708 | */ | |
1709 | if (mainll_p->str == S_STAFF && | |
1710 | mainll_p->u.staff_p->staffno == s) { | |
1711 | staff_p = mainll_p->u.staff_p; | |
1712 | for (n = 0; n < staff_p->nsyllists; n++) { | |
1713 | if (staff_p->sylplace[n] == place) { | |
1714 | if (staff_p->syls_p[n]->vno == 0) | |
1715 | gotverse0 = YES; | |
1716 | else | |
1717 | gototherverse = YES; | |
1718 | } | |
1719 | } | |
1720 | } | |
1721 | } | |
1722 | ||
1723 | /* if no verses, get out now */ | |
1724 | if (gotverse0 == NO && gototherverse == 0) { | |
1725 | FREE(versenums); | |
1726 | return; | |
1727 | } | |
1728 | ||
1729 | /* | |
1730 | * Loop through this score's part of the MLL, recording all the verse | |
1731 | * numbers that occur on the "place" side of the staff in versenums[]. | |
1732 | * Verse 0 may or may not be included, depending on the above results. | |
1733 | * Also set farwest and fareast. | |
1734 | */ | |
1735 | vfound = 0; /* no verses have been found yet */ | |
1736 | farwest = PGWIDTH; /* init it all the way east */ | |
1737 | fareast = 0; /* init it all the way west */ | |
1738 | for (mainll_p = start_p->next; mainll_p != 0 && | |
1739 | mainll_p->str != S_FEED; mainll_p = mainll_p->next) { | |
1740 | /* | |
1741 | * Whenever we find a structure for this staff (another | |
1742 | * measure of this staff), loop through its verse headcells. | |
1743 | */ | |
1744 | if (mainll_p->str == S_STAFF && | |
1745 | mainll_p->u.staff_p->staffno == s) { | |
1746 | ||
1747 | staff_p = mainll_p->u.staff_p; | |
1748 | ||
1749 | for (n = 0; n < staff_p->nsyllists; n++) { | |
1750 | ||
1751 | if (staff_p->sylplace[n] == place) { | |
1752 | /* | |
1753 | * We found a verse number. Search the | |
1754 | * the array to see if it's already | |
1755 | * been found. If not, insert it into | |
1756 | * versenums[] in the right place, so | |
1757 | * that they'll end up being in order | |
1758 | * (actually, reverse order). | |
1759 | */ | |
1760 | v = staff_p->syls_p[n]->vno; | |
1761 | /* ignore verse 0 if others exist */ | |
1762 | if (v == 0 && gototherverse == YES) | |
1763 | continue; | |
1764 | for (k = 0; k < vfound && | |
1765 | v < versenums[k]; k++) { | |
1766 | ; | |
1767 | } | |
1768 | if (k == vfound || v > versenums[k]) { | |
1769 | for (j = vfound; j > k; j--) { | |
1770 | versenums[j] = | |
1771 | versenums[j-1]; | |
1772 | } | |
1773 | versenums[k] = v; | |
1774 | vfound++; /* found one more */ | |
1775 | } | |
1776 | ||
1777 | /* | |
1778 | * If any syl sticks out farther than | |
1779 | * any previous one, extend farwest or | |
1780 | * fareast. | |
1781 | */ | |
1782 | for (gs_p = staff_p->syls_p[n]; | |
1783 | gs_p != 0; gs_p = gs_p->next) { | |
1784 | ||
1785 | if (gs_p->c[AW] < farwest) | |
1786 | farwest = gs_p->c[AW]; | |
1787 | if (gs_p->c[AE] > fareast) | |
1788 | fareast = gs_p->c[AE]; | |
1789 | } | |
1790 | } | |
1791 | } | |
1792 | } | |
1793 | } | |
1794 | ||
1795 | /* | |
1796 | * Enclose all the syllables of all the verses (of this place) in one | |
1797 | * big rectangle. Pad on west and east by 8 step sizes. Pretend the | |
1798 | * rectangle is PGHEIGHT high. We don't actually know yet how high | |
1799 | * it is, and this will prevent it from getting between the staff and | |
1800 | * anything else. Later in this function we will correct the entry | |
1801 | * that stackit put in rectab, to reflect the true height. For above | |
1802 | * and below cases, don't let it get any closer than 2 step sizes to | |
1803 | * the staff. The half-height of a one-line staff is regarded as 1 | |
1804 | * instead of the true 0, to give a little breathing room. | |
1805 | */ | |
1806 | if (place == PL_BETWEEN) | |
1807 | dist = 0; | |
1808 | else | |
1809 | dist = halfstaffhi(s) + 2.0 * Stepsize; | |
1810 | ||
1811 | (void)stackit(farwest - 8 * STEPSIZE, fareast + 8 * STEPSIZE, PGHEIGHT, | |
1812 | dist, place); | |
1813 | ||
1814 | /* | |
1815 | * Find the greatest protrusion of any currently existing rectangle | |
1816 | * that horizontally is within the span of our new rectangle. That's | |
1817 | * the same as the top or bottom of the new rectangle. | |
1818 | */ | |
1819 | if (place == PL_BELOW) | |
1820 | protrude = rectab[reclim - 1].n; | |
1821 | else | |
1822 | protrude = rectab[reclim - 1].s; | |
1823 | ||
1824 | /* | |
1825 | * Loop through the verses, from the inside out. setting the relative | |
1826 | * vertical coords of their syllables. When necessary, we also insert | |
1827 | * new syllables on the next score for continuing underscores. | |
1828 | */ | |
1829 | if (place == PL_BELOW) { /* work downward from staff */ | |
1830 | begin = vfound - 1; /* first verse number */ | |
1831 | end = -1; /* beyond last verse number */ | |
1832 | delta = -1; | |
1833 | } else { /* above and between both work upwards from bottom */ | |
1834 | begin = 0; /* last verse number */ | |
1835 | end = vfound; /* before first verse number */ | |
1836 | delta = 1; | |
1837 | } | |
1838 | for (n = begin; n != end; n += delta) { | |
1839 | /* | |
1840 | * Find the farthest any syllable ascends and descends from the | |
1841 | * baseline of the verse. | |
1842 | */ | |
1843 | getvsize(start_p, s, place, versenums[n], &maxasc, &maxdes); | |
1844 | ||
1845 | /* | |
1846 | * Set the baseline for this verse, based on where we're | |
1847 | * pushing up against (the last verse we did, or earlier | |
1848 | * things), and how far this verse sticks out. | |
1849 | */ | |
1850 | if (place == PL_BELOW) | |
1851 | baseline = protrude - maxasc; | |
1852 | else /* above or between */ | |
1853 | baseline = protrude + maxdes; | |
1854 | ||
1855 | /* set syllables' vertical coords; continue underscores */ | |
1856 | setsylvert(start_p, s, place, versenums[n], baseline); | |
1857 | ||
1858 | /* set new lower bound, for next time through loop */ | |
1859 | if (place == PL_BELOW) | |
1860 | protrude = baseline - maxdes; | |
1861 | else /* above or between */ | |
1862 | protrude = baseline + maxasc; | |
1863 | ||
1864 | } /* for every verse */ | |
1865 | ||
1866 | /* | |
1867 | * If there was a verse 0 (centered verse) and also normal verses, then | |
1868 | * in the above code we have handled only the normal verses, and we now | |
1869 | * need to handle verse 0. | |
1870 | */ | |
1871 | if (gotverse0 == YES && gototherverse == YES) { | |
1872 | float mid; /* RY of the middle of the normal verses */ | |
1873 | struct RECTAB rec; /* one rectangle */ | |
1874 | ||
1875 | /* get ascent and descent of verse 0 */ | |
1876 | getvsize(start_p, s, place, 0, &maxasc, &maxdes); | |
1877 | ||
1878 | /* | |
1879 | * We will use stackit's "dist" mechanism to try to get verse 0 | |
1880 | * to line up with the center of the other verses. The last | |
1881 | * rectangle in rectab is currently the normal verses', but the | |
1882 | * one coord isn't really set right yet. Fortunately, the | |
1883 | * "protrude" variable is what we need for that coord. | |
1884 | */ | |
1885 | if (place == PL_BELOW) { | |
1886 | mid = (rectab[reclim - 1].n + protrude) / 2.0; | |
1887 | dist = -mid - (maxasc + maxdes) / 2.0; | |
1888 | } else { | |
1889 | mid = (protrude + rectab[reclim - 1].s) / 2.0; | |
1890 | dist = mid - (maxasc + maxdes) / 2.0; | |
1891 | } | |
1892 | ||
1893 | /* | |
1894 | * Find the easternmost and westernmost points of verse 0. | |
1895 | * It's easier to loop through all the syllables than to try to | |
1896 | * find the first and last syllables on the line. | |
1897 | */ | |
1898 | farwest = PGWIDTH; /* init it all the way east */ | |
1899 | fareast = 0; /* init it all the way west */ | |
1900 | for (mainll_p = start_p->next; | |
1901 | mainll_p != 0 && mainll_p->str != S_FEED; | |
1902 | mainll_p = mainll_p->next) { | |
1903 | ||
1904 | if (mainll_p->str != S_STAFF || | |
1905 | mainll_p->u.staff_p->staffno != s) | |
1906 | continue; | |
1907 | ||
1908 | staff_p = mainll_p->u.staff_p; | |
1909 | for (n = 0; n < staff_p->nsyllists; n++) { | |
1910 | if (staff_p->sylplace[n] == place && | |
1911 | staff_p->syls_p[n]->vno == 0) { | |
1912 | for (gs_p = staff_p->syls_p[n]; | |
1913 | gs_p != 0; gs_p = gs_p->next) { | |
1914 | ||
1915 | if (gs_p->c[AW] < farwest) | |
1916 | farwest = gs_p->c[AW]; | |
1917 | if (gs_p->c[AE] > fareast) | |
1918 | fareast = gs_p->c[AE]; | |
1919 | } | |
1920 | } | |
1921 | } | |
1922 | } | |
1923 | ||
1924 | /* | |
1925 | * Squeeze the regular verses' rectangle to zero so that it | |
1926 | * won't affect verse 0's. We hope they wouldn't interfere | |
1927 | * anyway, but the +8 and -8 might make them. The regular | |
1928 | * verses' rectangle will be corrected later anyway. | |
1929 | */ | |
1930 | rectab[reclim - 1].n = rectab[reclim - 1].s = 0; | |
1931 | ||
1932 | /* | |
1933 | * Stack verse 0's rectangle and set its baseline. We have to | |
1934 | * play games with "place", because for "between" stackit | |
1935 | * ignores "dist", but we need it to use "dist". | |
1936 | */ | |
1937 | baseline = stackit(farwest - 8 * STEPSIZE, | |
1938 | fareast + 8 * STEPSIZE, maxasc + maxdes, dist, | |
1939 | place == PL_BETWEEN ? PL_ABOVE : place) + maxdes; | |
1940 | ||
1941 | /* | |
1942 | * Switch verse 0's rectangle and the normal verses' so that | |
1943 | * the later code can always use reclim-1 for the normal. | |
1944 | */ | |
1945 | rec = rectab[reclim - 2]; | |
1946 | rectab[reclim - 2] = rectab[reclim - 1]; | |
1947 | rectab[reclim - 1] = rec; | |
1948 | ||
1949 | setsylvert(start_p, s, place, 0, baseline); | |
1950 | } | |
1951 | ||
1952 | /* | |
1953 | * Now that we know how high this rectangle really is, correct it in | |
1954 | * rectab. Make it reach the center of the staff/baseline, to prevent | |
1955 | * anything later from getting in between there. | |
1956 | */ | |
1957 | if (place == PL_BELOW) { | |
1958 | rectab[reclim - 1].n = 0; | |
1959 | rectab[reclim - 1].s = protrude; | |
1960 | } else { /* above or between */ | |
1961 | rectab[reclim - 1].n = protrude; | |
1962 | rectab[reclim - 1].s = 0; | |
1963 | } | |
1964 | ||
1965 | FREE(versenums); | |
1966 | } | |
1967 | \f | |
1968 | /* | |
1969 | * Name: getvsize() | |
1970 | * | |
1971 | * Abstract: Get the maximum ascent and descent for a verse on a score. | |
1972 | * | |
1973 | * Returns: void | |
1974 | * | |
1975 | * Description: This function returns (through pointers) the maximum ascent and | |
1976 | * descent of a verse on this score. Usually this is the standard | |
1977 | * ascent and descent of the font, but it could be greater if | |
1978 | * there are font or size changes inside some syllable. | |
1979 | */ | |
1980 | ||
1981 | static void | |
1982 | getvsize(start_p, s, place, v, maxasc_p, maxdes_p) | |
1983 | ||
1984 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
1985 | int s; /* staff number */ | |
1986 | int place; /* above, below, or between? */ | |
1987 | int v; /* verse number */ | |
1988 | float *maxasc_p, *maxdes_p; /* ascent and descent to be returned */ | |
1989 | ||
1990 | { | |
1991 | int lyricsfont; /* that is set for this staff */ | |
1992 | int lyricssize; /* that is set for this staff */ | |
1993 | float asc, des; /* max ascent & descent of syllables */ | |
1994 | struct MAINLL *mainll_p;/* point along main linked list */ | |
1995 | struct STAFF *staff_p; /* point at a staff structure */ | |
1996 | struct GRPSYL *gs_p; /* point at a syllable */ | |
1997 | int k; /* loop variable */ | |
1998 | ||
1999 | ||
2000 | /* | |
2001 | * Get the standard max ascent and descent for any syllable. | |
2002 | */ | |
2003 | lyricsfont = svpath(s, LYRICSFONT)->lyricsfont; | |
2004 | lyricssize = svpath(s, LYRICSSIZE)->lyricssize; | |
2005 | *maxasc_p = fontascent(lyricsfont, lyricssize) * Staffscale; | |
2006 | *maxdes_p = fontdescent(lyricsfont, lyricssize) * Staffscale; | |
2007 | ||
2008 | /* | |
2009 | * Find the farthest any syllable ascends and descends from the | |
2010 | * baseline of the verse. Start with the standard amount for this font | |
2011 | * size. If the loop finds any weird syllable with bigger characters | |
2012 | * embedded, they will be increased. | |
2013 | */ | |
2014 | for (mainll_p = start_p->next; mainll_p != 0 && mainll_p->str | |
2015 | != S_FEED; mainll_p = mainll_p->next) { | |
2016 | ||
2017 | if (mainll_p->str != S_STAFF || | |
2018 | mainll_p->u.staff_p->staffno != s) | |
2019 | continue; | |
2020 | ||
2021 | /* found a STAFF of the number we're dealing with */ | |
2022 | staff_p = mainll_p->u.staff_p; | |
2023 | ||
2024 | /* | |
2025 | * See if this verse is present in this staff, | |
2026 | * and if so, loop through it. | |
2027 | */ | |
2028 | for (k = 0; k < staff_p->nsyllists; k++) { | |
2029 | ||
2030 | if (staff_p->sylplace[k] == place && | |
2031 | staff_p->syls_p[k]->vno == v) { | |
2032 | ||
2033 | for (gs_p = staff_p->syls_p[k]; gs_p != 0; | |
2034 | gs_p = gs_p->next) { | |
2035 | /* | |
2036 | * If asc or des is greater | |
2037 | * for this syl, save it. | |
2038 | */ | |
2039 | asc = strascent(gs_p->syl); | |
2040 | ||
2041 | des = strdescent(gs_p->syl); | |
2042 | ||
2043 | if (asc > *maxasc_p) | |
2044 | *maxasc_p = asc; | |
2045 | if (des > *maxdes_p) | |
2046 | *maxdes_p = des; | |
2047 | } | |
2048 | ||
2049 | /* no need to look any more */ | |
2050 | break; | |
2051 | } | |
2052 | } | |
2053 | } /* for every MLL stucture in score */ | |
2054 | } | |
2055 | \f | |
2056 | /* | |
2057 | * Name: setsylvert() | |
2058 | * | |
2059 | * Abstract: Set the maximum ascent and descent for a verse on a score. | |
2060 | * | |
2061 | * Returns: void | |
2062 | * | |
2063 | * Description: This function, using the given baseline, sets the relative | |
2064 | * vertical coords of each syllable in the verse on this score. | |
2065 | * If there are any nonnull syllables, it calls a function to | |
2066 | * continue underscores if need be. | |
2067 | */ | |
2068 | ||
2069 | static void | |
2070 | setsylvert(start_p, s, place, v, baseline) | |
2071 | ||
2072 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
2073 | int s; /* staff number */ | |
2074 | int place; /* above, below, or between? */ | |
2075 | int v; /* verse number */ | |
2076 | double baseline; /* baseline of a verse of syllables */ | |
2077 | ||
2078 | { | |
2079 | struct MAINLL *mainll_p;/* point along main linked list */ | |
2080 | struct STAFF *staff_p; /* point at a staff structure */ | |
2081 | struct GRPSYL *gs_p; /* point at a syllable */ | |
2082 | struct MAINLL *laststaff_p; /* point last staff that has a syllable */ | |
2083 | struct GRPSYL *lastgs_p;/* point at last nonnull syllable in a verse */ | |
2084 | int k; /* loop variable */ | |
2085 | ||
2086 | ||
2087 | /* | |
2088 | * Loop through all these syllables as before, setting their relative | |
2089 | * vertical coords. | |
2090 | */ | |
2091 | lastgs_p = 0; /* set later to last nonnull syl, if exists */ | |
2092 | laststaff_p = 0; /* set later to staff containing lastgs_p */ | |
2093 | ||
2094 | for (mainll_p = start_p->next; mainll_p != 0 && mainll_p->str | |
2095 | != S_FEED; mainll_p = mainll_p->next) { | |
2096 | ||
2097 | if (mainll_p->str != S_STAFF || | |
2098 | mainll_p->u.staff_p->staffno != s) | |
2099 | continue; | |
2100 | ||
2101 | /* found a STAFF of the number we're dealing with */ | |
2102 | staff_p = mainll_p->u.staff_p; | |
2103 | ||
2104 | /* | |
2105 | * See if this verse is present in this staff, | |
2106 | * and if so, loop through it. | |
2107 | */ | |
2108 | for (k = 0; k < staff_p->nsyllists; k++) { | |
2109 | ||
2110 | if (staff_p->sylplace[k] == place && | |
2111 | staff_p->syls_p[k]->vno == v) { | |
2112 | ||
2113 | for (gs_p = staff_p->syls_p[k]; gs_p != 0; | |
2114 | gs_p = gs_p->next) { | |
2115 | ||
2116 | if (gs_p->syl == 0) { | |
2117 | continue; | |
2118 | } | |
2119 | ||
2120 | gs_p->c[RY] = baseline; | |
2121 | ||
2122 | gs_p->c[RN] = baseline | |
2123 | + strascent(gs_p->syl); | |
2124 | ||
2125 | gs_p->c[RS] = baseline | |
2126 | - strdescent(gs_p->syl); | |
2127 | ||
2128 | /* remember last nonnull syl */ | |
2129 | if (gs_p->syl[0] != '\0') { | |
2130 | lastgs_p = gs_p; | |
2131 | laststaff_p = mainll_p; | |
2132 | } | |
2133 | } | |
2134 | } | |
2135 | } | |
2136 | } /* for every MLL stucture in score */ | |
2137 | ||
2138 | /* | |
2139 | * At this point, if this score has any nonnull syllables for | |
2140 | * this verse, lastgs_p points at the last one and laststaff_p | |
2141 | * points at its STAFF. If that last syllable ends in '_' or | |
2142 | * '-', we may need to continue this character onto the next | |
2143 | * score, so call a function to do that. | |
2144 | */ | |
2145 | if (lastgs_p != 0 && has_extender(lastgs_p->syl)) | |
2146 | cont_extender(laststaff_p, place, v); | |
2147 | } | |
2148 | \f | |
2149 | /* | |
2150 | * Name: dopedal() | |
2151 | * | |
2152 | * Abstract: Set a rectangle for pedal marks, if there are any. | |
2153 | * | |
2154 | * Returns: void | |
2155 | * | |
2156 | * Description: This function puts a rectangle into rectab for pedal marks, if | |
2157 | * there are any on this score. It also sets their relative | |
2158 | * vertical coordinates. | |
2159 | */ | |
2160 | ||
2161 | static void | |
2162 | dopedal(start_p, s) | |
2163 | ||
2164 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
2165 | int s; /* staff number */ | |
2166 | ||
2167 | { | |
2168 | struct MAINLL *mainll_p; /* loop through main linked list */ | |
2169 | struct STUFF *stuff_p; /* point along a STUFF list */ | |
2170 | float protrude; /* farthest protrusion of rectangle */ | |
2171 | float lowpoint; /* the lowest any mark goes */ | |
2172 | float asc; /* ascent of a pedal mark */ | |
2173 | float hi; /* height of a pedal mark */ | |
2174 | int k; /* loop variable */ | |
2175 | ||
2176 | ||
2177 | debug(32, "dopedal file=%s line=%d s=%d", start_p->inputfile, | |
2178 | start_p->inputlineno, s); | |
2179 | /* | |
2180 | * Find the greatest protrusion of any currently existing rectangle. | |
2181 | */ | |
2182 | protrude = 0; | |
2183 | for (k = 0; k < reclim; k++) { | |
2184 | if (rectab[k].s < protrude) | |
2185 | protrude = rectab[k].s; | |
2186 | } | |
2187 | ||
2188 | lowpoint = 0; | |
2189 | ||
2190 | /* | |
2191 | * Loop through this score's part of the MLL. Whenever we find a | |
2192 | * structure for this staff (another measure), loop through its | |
2193 | * STUFF list, setting coords for each pedal mark. | |
2194 | */ | |
2195 | for (mainll_p = start_p->next; mainll_p != 0 && | |
2196 | mainll_p->str != S_FEED; mainll_p = mainll_p->next) { | |
2197 | ||
2198 | if (mainll_p->str != S_STAFF || | |
2199 | mainll_p->u.staff_p->staffno != s) | |
2200 | continue; | |
2201 | ||
2202 | for (stuff_p = mainll_p->u.staff_p->stuff_p; | |
2203 | stuff_p != 0; stuff_p = stuff_p->next) { | |
2204 | ||
2205 | if (stuff_p->stuff_type != ST_PEDAL) | |
2206 | continue; | |
2207 | ||
2208 | /* | |
2209 | * Whichever pedal character this is, always use | |
2210 | * C_BEGPED if pedstyle is P_LINE and the "Ped." string | |
2211 | * for the other cases. For the former, all three | |
2212 | * characters are the same height; and for the latter, | |
2213 | * this string is taller than the "*". This also | |
2214 | * handles the pedal continuation situation. | |
2215 | */ | |
2216 | stuff_p->c[RN] = protrude; | |
2217 | if (svpath(s, PEDSTYLE)->pedstyle == P_LINE) { | |
2218 | asc = ascent(FONT_MUSIC, DFLT_SIZE, C_BEGPED); | |
2219 | hi = height(FONT_MUSIC, DFLT_SIZE, C_BEGPED); | |
2220 | } else { /* P_PEDSTAR or P_ALTPEDSTAR */ | |
2221 | asc = strascent(Ped_start); | |
2222 | hi = strheight(Ped_start); | |
2223 | } | |
2224 | if (stuff_p->all) { | |
2225 | asc *= Score.staffscale; | |
2226 | hi *= Score.staffscale; | |
2227 | } else { | |
2228 | asc *= Staffscale; | |
2229 | hi *= Staffscale; | |
2230 | } | |
2231 | stuff_p->c[RY] = protrude - asc; | |
2232 | stuff_p->c[RS] = protrude - hi; | |
2233 | ||
2234 | if (stuff_p->c[RS] < lowpoint) | |
2235 | lowpoint = stuff_p->c[RS]; | |
2236 | } | |
2237 | } | |
2238 | ||
2239 | /* | |
2240 | * If we found pedal mark(s), put one big rectangle in rectab, spanning | |
2241 | * the width of the page. | |
2242 | */ | |
2243 | if (lowpoint < 0) { | |
2244 | rectab[reclim].n = protrude; | |
2245 | rectab[reclim].s = lowpoint; | |
2246 | rectab[reclim].w = 0; | |
2247 | rectab[reclim].e = PGWIDTH; | |
2248 | ||
2249 | inc_reclim(); | |
2250 | } | |
2251 | } | |
2252 | \f | |
2253 | /* | |
2254 | * Name: doendings() | |
2255 | * | |
2256 | * Abstract: Set up rectangles and vert coords for ending marks. | |
2257 | * | |
2258 | * Returns: void | |
2259 | * | |
2260 | * Description: This function puts into rectab rectangles for ending marks. | |
2261 | * Also, MARKCOORD structures get linked to BARs for them. | |
2262 | */ | |
2263 | ||
2264 | static void | |
2265 | doendings(start_p, s) | |
2266 | ||
2267 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
2268 | int s; /* staff number */ | |
2269 | ||
2270 | { | |
2271 | struct MAINLL *mainll_p;/* point along main linked list */ | |
2272 | struct BAR *bar_p; /* point at a bar or pseudobar on this score */ | |
2273 | ||
2274 | ||
2275 | debug(32, "doendings file=%s line=%d s=%d", start_p->inputfile, | |
2276 | start_p->inputlineno, s); | |
2277 | /* if endings are not to be drawn over this staff, get out */ | |
2278 | if (has_ending(s) == NO) | |
2279 | return; | |
2280 | ||
2281 | /* point at pseudobar in clefsig that immediately follows this feed */ | |
2282 | mainll_p = start_p->next; | |
2283 | bar_p = mainll_p->u.clefsig_p->bar_p; | |
2284 | ||
2285 | /* | |
2286 | * If an ending starts at the pseudobar, or is continuing on from the | |
2287 | * previous score, handle it, along with any following continguous ones. | |
2288 | */ | |
2289 | if (bar_p->endingloc != NOITEM) { | |
2290 | /* | |
2291 | * Search forward for the end of this ending (or following | |
2292 | * contiguous ones), or the end of the score, whichever comes | |
2293 | * first. | |
2294 | */ | |
2295 | while ( ! (mainll_p->str == S_BAR && | |
2296 | mainll_p->u.bar_p->endingloc == ENDITEM) | |
2297 | && mainll_p->str != S_FEED) { | |
2298 | ||
2299 | mainll_p = mainll_p->next; | |
2300 | } | |
2301 | ||
2302 | /* handle ending(s) from start to this bar or feed */ | |
2303 | storeend(start_p, mainll_p, s); | |
2304 | ||
2305 | /* if feed, there's nothing more to look for */ | |
2306 | if (mainll_p->str == S_FEED) | |
2307 | return; | |
2308 | ||
2309 | /* point after this bar at end of this ending(s) */ | |
2310 | mainll_p = mainll_p->next; | |
2311 | } | |
2312 | ||
2313 | /* | |
2314 | * Search the rest of the score for contiguous groups of endings. | |
2315 | */ | |
2316 | while (mainll_p != 0 && mainll_p->str != S_FEED) { | |
2317 | ||
2318 | /* find another bar; return if there aren't any more */ | |
2319 | while (mainll_p != 0 && mainll_p->str != S_BAR && | |
2320 | mainll_p->str != S_FEED) | |
2321 | mainll_p = mainll_p->next; | |
2322 | if (mainll_p == 0 || mainll_p->str == S_FEED) | |
2323 | return; | |
2324 | ||
2325 | /* | |
2326 | * We found another bar. If it isn't associated with an | |
2327 | * ending, point beyond it and continue to go look for the | |
2328 | * next bar. | |
2329 | */ | |
2330 | if (mainll_p->u.bar_p->endingloc == NOITEM) { | |
2331 | mainll_p = mainll_p->next; | |
2332 | continue; | |
2333 | } | |
2334 | ||
2335 | /* | |
2336 | * This bar is the start of an ending. Search forward for the | |
2337 | * end of this ending (or following contiguous ones), or the | |
2338 | * end of the score, whichever comes first. | |
2339 | */ | |
2340 | start_p = mainll_p; | |
2341 | while ( ! (mainll_p->str == S_BAR && | |
2342 | mainll_p->u.bar_p->endingloc == ENDITEM) | |
2343 | && mainll_p->str != S_FEED) { | |
2344 | ||
2345 | mainll_p = mainll_p->next; | |
2346 | } | |
2347 | ||
2348 | /* handle ending(s) from start to this bar or feed */ | |
2349 | storeend(start_p, mainll_p, s); | |
2350 | ||
2351 | /* if feed, there's nothing more to look for */ | |
2352 | if (mainll_p->str == S_FEED) | |
2353 | return; | |
2354 | ||
2355 | /* point after this bar at end of this ending */ | |
2356 | mainll_p = mainll_p->next; | |
2357 | } | |
2358 | } | |
2359 | \f | |
2360 | /* | |
2361 | * Name: storeend() | |
2362 | * | |
2363 | * Abstract: Set up rectangles and vert coords for contiguous endings. | |
2364 | * | |
2365 | * Returns: void | |
2366 | * | |
2367 | * Description: This function is given the starting and ending bars of a group | |
2368 | * of continguous ending marks on a staff. The starting "bar" | |
2369 | * may be the pseudobar at the start of the score; and the ending | |
2370 | * bar may be the end of the score. This function applies stackit | |
2371 | * to them as a unit. It adds another rectangle to rectab to | |
2372 | * prevent anything later from getting in between the ending(s) | |
2373 | * and the staff. Then, for the starting bar of each ending in | |
2374 | * the group, it allocates a MARKCOORD structure. | |
2375 | */ | |
2376 | ||
2377 | static void | |
2378 | storeend(start_p, end_p, s) | |
2379 | ||
2380 | struct MAINLL *start_p; /* the start of these ending(s) */ | |
2381 | struct MAINLL *end_p; /* the end of these ending(s) */ | |
2382 | int s; /* staff number */ | |
2383 | ||
2384 | { | |
2385 | struct MAINLL *mainll_p;/* point along main linked list */ | |
2386 | struct BAR *bar_p; /* point at a bar or pseudobar on this score */ | |
2387 | struct MARKCOORD *mark_p; /* we allocate these for bars to point at */ | |
2388 | float west, east; /* extremities of group of ending(s) */ | |
2389 | float south; /* their bottom boundary */ | |
2390 | ||
2391 | ||
2392 | /* | |
2393 | * Find the west and east boundaries of the ending(s). | |
2394 | */ | |
2395 | if (start_p->str == S_FEED) | |
2396 | west = start_p->next->u.clefsig_p->bar_p->c[AX]; /* pseudobar */ | |
2397 | else | |
2398 | west = start_p->u.bar_p->c[AX]; /* normal bar */ | |
2399 | ||
2400 | if (end_p->str == S_FEED) | |
2401 | east = PGWIDTH - eff_rightmargin(end_p); /* end of score */ | |
2402 | else | |
2403 | east = end_p->u.bar_p->c[AX]; /* normal bar */ | |
2404 | ||
2405 | /* make a rectangle out of the ending(s) and find where they go */ | |
2406 | south = stackit(west, east, ENDINGHEIGHT, (double)0.0, PL_ABOVE); | |
2407 | ||
2408 | /* | |
2409 | * Superimpose another rectangle on top of the one stackit put there; | |
2410 | * one that reaches down to the staff. This ensures that nothing later | |
2411 | * will get between the ending(s) and the staff. | |
2412 | */ | |
2413 | rectab[reclim].n = south + ENDINGHEIGHT; | |
2414 | rectab[reclim].s = 0; | |
2415 | rectab[reclim].e = east; | |
2416 | rectab[reclim].w = west; | |
2417 | inc_reclim(); | |
2418 | ||
2419 | /* | |
2420 | * If the pseudobar has an ending, calloc a markcoord structure and put | |
2421 | * it in the pseudobar's linked list of them. | |
2422 | */ | |
2423 | if (start_p->str == S_FEED) { | |
2424 | bar_p = start_p->next->u.clefsig_p->bar_p; | |
2425 | CALLOC(MARKCOORD, mark_p, 1); | |
2426 | mark_p->next = bar_p->ending_p; | |
2427 | bar_p->ending_p = mark_p; | |
2428 | mark_p->staffno = (short)s; | |
2429 | mark_p->ry = south; | |
2430 | } | |
2431 | ||
2432 | /* | |
2433 | * Loop through this part of the score. Wherever there is a bar that | |
2434 | * is the start of an ending, calloc a markcoord structure and put it | |
2435 | * in the bar's linked list of them. | |
2436 | */ | |
2437 | for (mainll_p = start_p; mainll_p != end_p; mainll_p = mainll_p->next) { | |
2438 | if (mainll_p->str != S_BAR) | |
2439 | continue; | |
2440 | bar_p = mainll_p->u.bar_p; | |
2441 | if (bar_p->endingloc != STARTITEM) | |
2442 | continue; | |
2443 | CALLOC(MARKCOORD, mark_p, 1); | |
2444 | mark_p->next = bar_p->ending_p; | |
2445 | bar_p->ending_p = mark_p; | |
2446 | mark_p->staffno = (short)s; | |
2447 | mark_p->ry = south; | |
2448 | } | |
2449 | } | |
2450 | \f | |
2451 | /* | |
2452 | * Name: dorehears() | |
2453 | * | |
2454 | * Abstract: Set up rectangles and vert coords for rehearsal marks. | |
2455 | * | |
2456 | * Returns: void | |
2457 | * | |
2458 | * Description: This function puts into rectab rectangles for rehearsal marks. | |
2459 | * Also, MARKCOORD structures get linked to BARs for them. | |
2460 | */ | |
2461 | ||
2462 | static void | |
2463 | dorehears(start_p, s) | |
2464 | ||
2465 | struct MAINLL *start_p; /* FEED at the start of this score */ | |
2466 | int s; /* staff number */ | |
2467 | ||
2468 | { | |
2469 | struct MAINLL *mainll_p;/* point along main linked list */ | |
2470 | struct BAR *bar_p; /* point at a bar or pseudobar on this score */ | |
2471 | struct MARKCOORD *mark_p; /* we allocate these for bars to point at */ | |
2472 | float west, east; /* of a rehearsal mark */ | |
2473 | float south; /* of a rehearsal mark */ | |
2474 | float height; /* of a rehearsal mark */ | |
2475 | float dist; /* distance from center of staff */ | |
2476 | int dopseudo; /* do the pseudobar's rehearsal mark? */ | |
2477 | char *reh_string; /* string for the reh mark */ | |
2478 | ||
2479 | ||
2480 | debug(32, "dorehears file=%s line=%d s=%d", start_p->inputfile, | |
2481 | start_p->inputlineno, s); | |
2482 | /* if rehearsal marks are not to be drawn over this staff, get out */ | |
2483 | if (has_ending(s) == NO) | |
2484 | return; | |
2485 | ||
2486 | /* point at pseudobar in clefsig that immediately follows this feed */ | |
2487 | mainll_p = start_p->next; | |
2488 | bar_p = mainll_p->u.clefsig_p->bar_p; | |
2489 | ||
2490 | /* if there's a rehearsal mark at the pseudobar, note that fact */ | |
2491 | if (bar_p->reh_type != REH_NONE) | |
2492 | dopseudo = YES; | |
2493 | else | |
2494 | dopseudo = NO; | |
2495 | ||
2496 | /* | |
2497 | * Loop through the score, dealing with the pseudobar (if it has a | |
2498 | * rehearsal mark), and all real bars that have a rehearsal mark. | |
2499 | */ | |
2500 | for ( ; mainll_p != 0 && mainll_p->str != S_FEED; | |
2501 | mainll_p = mainll_p->next) { | |
2502 | ||
2503 | if (dopseudo == YES || mainll_p->str == S_BAR && | |
2504 | mainll_p->u.bar_p->reh_type != REH_NONE){ | |
2505 | if (dopseudo == YES) | |
2506 | dopseudo = NO; | |
2507 | else | |
2508 | bar_p = mainll_p->u.bar_p; | |
2509 | ||
2510 | /* | |
2511 | * Find the size of the rehearsal label, including 6 | |
2512 | * more points to allow for the box around it. Make | |
2513 | * its first character be centered over the bar line. | |
2514 | * Place it by using stackit. | |
2515 | */ | |
2516 | reh_string = get_reh_string(bar_p->reh_string, s); | |
2517 | height = strheight(reh_string); | |
2518 | west = bar_p->c[AX] - left_width(reh_string); | |
2519 | east = west + strwidth(reh_string); | |
2520 | ||
2521 | if (bar_p->dist_usage == SD_NONE) { | |
2522 | /* get the usual dist */ | |
2523 | dist = svpath(s, DIST)->dist; | |
2524 | } else { | |
2525 | /* override with this bar's dist */ | |
2526 | dist = bar_p->dist; | |
2527 | } | |
2528 | /* convert to inches from center of staff */ | |
2529 | dist = halfstaffhi(s) + STEPSIZE * dist; | |
2530 | ||
2531 | if (bar_p->dist_usage == SD_FORCE) { | |
2532 | /* | |
2533 | * The user is forcing this dist, so don't | |
2534 | * stack; just put it there. | |
2535 | */ | |
2536 | south = dist; | |
2537 | rectab[reclim].n = south + height; | |
2538 | rectab[reclim].s = south; | |
2539 | rectab[reclim].e = east; | |
2540 | rectab[reclim].w = west; | |
2541 | inc_reclim(); | |
2542 | } else { | |
2543 | /* stack the usual way */ | |
2544 | south = stackit(west, east, height, dist, | |
2545 | PL_ABOVE); | |
2546 | } | |
2547 | ||
2548 | /* | |
2549 | * Allocate and link a MARKCOORD, and put the necessary | |
2550 | * info in it. | |
2551 | */ | |
2552 | CALLOC(MARKCOORD, mark_p, 1); | |
2553 | mark_p->next = bar_p->reh_p; | |
2554 | bar_p->reh_p = mark_p; | |
2555 | mark_p->staffno = (short)s; | |
2556 | mark_p->ry = south + strdescent(reh_string); | |
2557 | } | |
2558 | } | |
2559 | } | |
2560 | \f | |
2561 | /* | |
2562 | * Name: stackit() | |
2563 | * | |
2564 | * Abstract: Place a rectangle and add it to rectab. | |
2565 | * | |
2566 | * Returns: south boundary of the new rectangle | |
2567 | * | |
2568 | * Description: This function puts the given rectangle into rectab. It is put | |
2569 | * as close to the staff or baseline as is possible without | |
2570 | * overlapping rectangles already in rectab, and without letting | |
2571 | * it get any closer to the staff/baseline than "dist" STEPSIZE. | |
2572 | */ | |
2573 | ||
2574 | static double | |
2575 | stackit(west, east, height, dist, place) | |
2576 | ||
2577 | double west; /* west edge of the new rectangle */ | |
2578 | double east; /* east edge of the new rectangle */ | |
2579 | double height; /* height of the new rectangle */ | |
2580 | double dist; /* min dist from item to center line of staff*/ | |
2581 | int place; /* above, below, or between? */ | |
2582 | ||
2583 | { | |
2584 | float north, south; /* trial boundaries for new rectangle */ | |
2585 | int try; /* which element of rectab to try */ | |
2586 | int overlap; /* does our rectangle overlap existing ones? */ | |
2587 | int j; /* loop variable */ | |
2588 | ||
2589 | ||
2590 | /* | |
2591 | * For each rectangle in rectab, decide whether (based on | |
2592 | * its horizontal coords) it could possibly overlap with our | |
2593 | * new rectangle. If it's totally left or right of ours, it | |
2594 | * can't. We allow a slight overlap (FUDGE) so that round | |
2595 | * off errors don't stop us from packing things as tightly | |
2596 | * as possible. | |
2597 | */ | |
2598 | for (j = 0; j < reclim; j++) { | |
2599 | if (rectab[j].w + FUDGE > east || | |
2600 | rectab[j].e < west + FUDGE) | |
2601 | rectab[j].relevant = NO; | |
2602 | else | |
2603 | rectab[j].relevant = YES; | |
2604 | } | |
2605 | ||
2606 | /* | |
2607 | * Set up first trial position for this rectangle: "dist" inches | |
2608 | * away from the center line of the staff. For "between", it always | |
2609 | * starts at the baseline. | |
2610 | */ | |
2611 | north = south = 0.0; /* prevent useless 'used before set' warning */ | |
2612 | switch (place) { | |
2613 | case PL_BELOW: | |
2614 | /* work downward from staff, allowing "dist" distance */ | |
2615 | north = -dist; | |
2616 | south = north - height; | |
2617 | break; | |
2618 | case PL_ABOVE: | |
2619 | /* work upward from staff, allowing "dist" distance */ | |
2620 | south = dist; | |
2621 | north = south + height; | |
2622 | break; | |
2623 | case PL_BETWEEN: | |
2624 | /* work upward from baseline */ | |
2625 | south = 0; | |
2626 | north = height; | |
2627 | break; | |
2628 | } | |
2629 | ||
2630 | /* | |
2631 | * Mark the "tried" field for all relevant rectangles. This says | |
2632 | * whether we have already tried using their boundaries for positioning | |
2633 | * our rectangle. Any rectangle that is closer to the staff/baseline | |
2634 | * than we want to allow, we mark as if we have tried it already. | |
2635 | */ | |
2636 | for (j = 0; j < reclim; j++) { | |
2637 | if (rectab[j].relevant == YES) { | |
2638 | if (place == PL_BELOW && rectab[j].s > north || | |
2639 | place != PL_BELOW && rectab[j].n < south) | |
2640 | rectab[j].tried = YES; | |
2641 | else | |
2642 | rectab[j].tried = NO; | |
2643 | } | |
2644 | } | |
2645 | ||
2646 | /* | |
2647 | * Keep trying positions for this rectangle, working outwards from the | |
2648 | * first trial position. When we find one that doesn't overlap an | |
2649 | * existing rectangle, break. This has to succeed at some point, at | |
2650 | * at the outermost rectangle position if not earlier. | |
2651 | */ | |
2652 | for (;;) { | |
2653 | overlap = NO; | |
2654 | for (j = 0; j < reclim; j++) { | |
2655 | /* ignore ones too far east or west */ | |
2656 | if (rectab[j].relevant == NO) | |
2657 | continue; | |
2658 | ||
2659 | /* if all south or north, okay; else overlap */ | |
2660 | if (rectab[j].s + FUDGE <= north && | |
2661 | rectab[j].n >= south + FUDGE) { | |
2662 | overlap = YES; | |
2663 | break; | |
2664 | } | |
2665 | } | |
2666 | ||
2667 | /* if no rectangle overlapped, we found a valid place */ | |
2668 | if (overlap == NO) | |
2669 | break; | |
2670 | ||
2671 | /* | |
2672 | * Something overlapped, so we have to try again. Find the | |
2673 | * innermost relevant outer rectangle boundary that hasn't been | |
2674 | * tried already, to use as the next trial position for our | |
2675 | * rectangle's inner boundary. | |
2676 | */ | |
2677 | try = -1; | |
2678 | for (j = 0; j < reclim; j++) { | |
2679 | /* ignore ones too far east or west */ | |
2680 | if (rectab[j].relevant == NO || rectab[j].tried == YES) | |
2681 | continue; | |
2682 | ||
2683 | /* | |
2684 | * If this is the first relevant one we haven't tried, | |
2685 | * or if this is farther in than the innermost so far, | |
2686 | * save it as being the new innermost so far. | |
2687 | */ | |
2688 | if (place == PL_BELOW) { | |
2689 | if (try == -1 || rectab[j].s > rectab[try].s) | |
2690 | try = j; | |
2691 | } else { | |
2692 | if (try == -1 || rectab[j].n < rectab[try].n) | |
2693 | try = j; | |
2694 | } | |
2695 | } | |
2696 | ||
2697 | if (try == -1) | |
2698 | pfatal("bug in stackit()"); | |
2699 | ||
2700 | /* | |
2701 | * Mark this one as having been tried (for next time around, if | |
2702 | * necessary). Set new trial values for north and south of our | |
2703 | * rectangle. | |
2704 | */ | |
2705 | rectab[try].tried = YES; | |
2706 | if (place == PL_BELOW) { | |
2707 | north = rectab[try].s; | |
2708 | south = north - height; | |
2709 | } else { | |
2710 | south = rectab[try].n; | |
2711 | north = south + height; | |
2712 | } | |
2713 | ||
2714 | } /* end of while loop trying positions for this rectangle */ | |
2715 | ||
2716 | /* | |
2717 | * We found the correct position for the new rectangle. Enter it | |
2718 | * into rectab. | |
2719 | */ | |
2720 | rectab[reclim].n = north; | |
2721 | rectab[reclim].s = south; | |
2722 | rectab[reclim].e = east; | |
2723 | rectab[reclim].w = west; | |
2724 | ||
2725 | inc_reclim(); | |
2726 | ||
2727 | return (south); | |
2728 | } | |
2729 | \f | |
2730 | /* | |
2731 | * Name: inc_reclim() | |
2732 | * | |
2733 | * Abstract: Increment no. of rectangles, and realloc more if we run out. | |
2734 | * | |
2735 | * Returns: void | |
2736 | * | |
2737 | * Description: This function increments reclim, the index into rectab. If it | |
2738 | * finds that rectab[reclim] is now beyond the end of the space | |
2739 | * that's been allocated, it does a realloc to get more space. | |
2740 | */ | |
2741 | ||
2742 | static void | |
2743 | inc_reclim() | |
2744 | { | |
2745 | /* when first called, relvert will have allocated this many */ | |
2746 | static int rectabsize = RECTCHUNK; | |
2747 | ||
2748 | ||
2749 | reclim++; | |
2750 | ||
2751 | /* if rectab[reclim] is still valid, no need to allocate more */ | |
2752 | if (reclim < rectabsize) | |
2753 | return; | |
2754 | ||
2755 | /* must allocate another chunk of rectangles */ | |
2756 | rectabsize += RECTCHUNK; | |
2757 | REALLOC(RECTAB, rectab, rectabsize); | |
2758 | } |