chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / relvert.c
CommitLineData
69695f33
MW
1/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 by Arkkra Enterprises */
2/* All rights reserved */
3/*
4 * Name: 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 */
19struct 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};
27static 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
37static int reclim; /* index after last rectangle in rectab */
38
39static void procstaff P((struct MAINLL *mainll_p, int s));
40static void dostaff P((int s, int place));
41static void dogroups P((struct MAINLL *start_p, int s, int place));
42static void llgrps P((struct STAFF *staff_p, struct GRPSYL *gs_p, int place));
43static void dobeamalt P((struct MAINLL *start_p, int s, int place));
44static void onebeamalt P((struct GRPSYL *gs_p));
45static double getstemendvert P((struct GRPSYL *gs_p));
46static void linerects P((double x1, double y1, double x2, double y2, int side,
47 double halfstaff));
48static void docurve P((struct MAINLL *start_p, int s, int place,
49 int do_which));
50static void curverect P((int s, struct STUFF *stuff_p, double halfstaff));
51static void curvepiecerect P((double x1, double y1, double x2, double y2,
52 double halfstaff));
53static void dotuplet P((struct MAINLL *start_p, int s, int place));
54static void onetuplet P((struct STAFF *staff_p, struct GRPSYL *start_p,
55 int place));
56static void domiscstuff P((struct MAINLL *start_p, int s, int place,
57 unsigned long do_which));
58static void dolyrics P((struct MAINLL *start_p, int s, int place));
59static void getvsize P((struct MAINLL *start_p, int s, int place, int v,
60 float *asc_p, float *des_p));
61static void setsylvert P((struct MAINLL *start_p, int s, int place, int v,
62 double baseline));
63static void dopedal P((struct MAINLL *start_p, int s));
64static void doendings P((struct MAINLL *start_p, int s));
65static void storeend P((struct MAINLL *start_p, struct MAINLL *end_p, int s));
66static void dorehears P((struct MAINLL *start_p, int s));
67static double stackit P((double west, double east, double height, double dist,
68 int place));
69static 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
83void
84relvert()
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
173static void
174procstaff(start_p, s)
175
176struct MAINLL *start_p; /* FEED at the start of this score */
177int 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
466static void
467dostaff(s, place)
468
469int s; /* staff number */
470int 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
506static void
507dogroups(start_p, s, place)
508
509struct MAINLL *start_p; /* FEED at the start of this score */
510int s; /* staff number */
511int 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
553static void
554llgrps(staff_p, first_p, place)
555
556struct STAFF *staff_p; /* point to the staff */
557struct GRPSYL *first_p; /* point to first group */
558int 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
710static void
711dobeamalt(start_p, s, place)
712
713struct MAINLL *start_p; /* FEED at the start of this score */
714int s; /* staff number */
715int 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
785static void
786onebeamalt(gs_p)
787
788struct 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
847static double
848getstemendvert(gs_p)
849
850struct 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
902static void
903linerects(x1, y1, x2, y2, side, halfstaff)
904
905double x1, y1; /* coords of left end of line */
906double x2, y2; /* coords of right end of line */
907int side; /* side to favor, UP or DOWN */
908double 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
978static void
979docurve(start_p, s, place, do_which)
980
981struct MAINLL *start_p; /* FEED at the start of this score */
982int s; /* staff number */
983int place; /* above or below? */
984int 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
1098static void
1099curverect(s, stuff_p, halfstaff)
1100
1101int s; /* staff number */
1102struct STUFF *stuff_p; /* the curve's STUFF */
1103double 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
1153static void
1154curvepiecerect(x1, y1, x2, y2, halfstaff)
1155
1156double x1, y1; /* coords of left end of the piece */
1157double x2, y2; /* coords of right end of the piece */
1158double 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
1237static void
1238dotuplet(start_p, s, place)
1239
1240struct MAINLL *start_p; /* FEED at the start of this score */
1241int s; /* staff number */
1242int 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
1312static void
1313onetuplet(staff_p, start_p, place)
1314
1315struct STAFF *staff_p; /* point to the staff we're on */
1316struct GRPSYL *start_p; /* points to first group in tuplet */
1317int 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
1388static void
1389domiscstuff(start_p, s, place, do_which)
1390
1391struct MAINLL *start_p; /* FEED at the start of this score */
1392int s; /* staff number */
1393int place; /* above, below, or between? */
1394unsigned 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
1656static void
1657dolyrics(start_p, s, place)
1658
1659struct MAINLL *start_p; /* FEED at the start of this score */
1660int s; /* staff number */
1661int 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
1981static void
1982getvsize(start_p, s, place, v, maxasc_p, maxdes_p)
1983
1984struct MAINLL *start_p; /* FEED at the start of this score */
1985int s; /* staff number */
1986int place; /* above, below, or between? */
1987int v; /* verse number */
1988float *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
2069static void
2070setsylvert(start_p, s, place, v, baseline)
2071
2072struct MAINLL *start_p; /* FEED at the start of this score */
2073int s; /* staff number */
2074int place; /* above, below, or between? */
2075int v; /* verse number */
2076double 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
2161static void
2162dopedal(start_p, s)
2163
2164struct MAINLL *start_p; /* FEED at the start of this score */
2165int 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
2264static void
2265doendings(start_p, s)
2266
2267struct MAINLL *start_p; /* FEED at the start of this score */
2268int 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
2377static void
2378storeend(start_p, end_p, s)
2379
2380struct MAINLL *start_p; /* the start of these ending(s) */
2381struct MAINLL *end_p; /* the end of these ending(s) */
2382int 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
2462static void
2463dorehears(start_p, s)
2464
2465struct MAINLL *start_p; /* FEED at the start of this score */
2466int 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
2574static double
2575stackit(west, east, height, dist, place)
2576
2577double west; /* west edge of the new rectangle */
2578double east; /* east edge of the new rectangle */
2579double height; /* height of the new rectangle */
2580double dist; /* min dist from item to center line of staff*/
2581int 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
2742static void
2743inc_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}