chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / prntmisc.c
1
2 /* Copyright (c) 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006 by Arkkra Enterprises */
3 /* All rights reserved */
4
5 /* functions for printing endings, pedal marks, phrase marks, etc */
6
7 #include "defines.h"
8 #include "structs.h"
9 #include "globals.h"
10
11 /* width of tapered curve at endpoint relative to maximum width */
12 #define TAPERWID        (0.4)
13
14
15 /*
16  * Define a structure for storing points of a curve, whether generated (like
17  * for phrase marks) or user-specified.  It also stores information used in
18  * calculating Bezier curves that will be drawn between each pair of
19  * neighboring points.  The "control points" are points 1 and 2 for the
20  * Bezier curve going from this point to the next point.  These structures
21  * get their x and y filled in from CURVE or CRVLIST structures.
22  */
23 struct CURVEINFO {
24         float x, y;     /* point's coords */
25         float len;      /* length of line segment from here to next point */
26         float ang;      /* angle between the 2 segments at this point */
27         int bend;       /* bend direction at point: 1=clockwise, -1=counter */
28         float slopetan; /* slope of line "tangent" to this point if axes are */
29                         /* rotated such that segment starting here is horiz. */
30         float x1, y1;   /* control point 1 for segment starting at this point */
31         float x2, y2;   /* control point 2 for segment starting at this point */
32 };
33
34 /* static functions */
35 static void do_endings P((struct MAINLL *first_p, struct MAINLL *last_p,
36                 char *endlabel, int carryout));
37 static void draw_ending P((int staffno, double ry, struct MAINLL *first_p,
38                 struct MAINLL *last_p, char *endlabel, int carryout));
39 static void pr_endlabel P((double x, double y, char *label));
40 static void pr_end_line P((double begin_x, double end_x, double y,
41                 int carryout));
42 static int is_top_visible_in_range P((int staffno, int top));
43 static void calccurve P((struct CURVEINFO v[], int num));
44 static void findcontrol P((struct CURVEINFO v[], int num));
45
46 \f
47
48 /* whenever we hit a FEED, draw any endings associated with the score */
49
50 void
51 pr_endings(main_feed_p)
52
53 struct MAINLL *main_feed_p;     /* FEED */
54
55 {
56         static char *endlabel = (char *) 0;/* ending label if had to carry over
57                                          * to next score */
58         struct MAINLL *curr_p;          /* where we are in main list */
59         struct MAINLL *first_p;         /* where an ending begins */
60         struct MAINLL *last_bar_p = 0;  /* points to last bar on score so far */
61
62
63         debug(512, "pr_endings");
64
65         first_p = (struct MAINLL *) 0;
66
67         /* go through the entire score line. For every set of measures that have
68          * endings, draw them. */
69         for (curr_p = main_feed_p->next; curr_p != (struct MAINLL *) 0;
70                                         curr_p = curr_p->next) {
71
72                 /* go just to end of current score */
73                 if (curr_p->str == S_FEED) {
74                         break;
75                 }
76
77                 /* if there is a pseudo bar, see if it is in an ending */
78                 if (curr_p->str == S_CLEFSIG) {
79                         if (curr_p->u.clefsig_p->bar_p != (struct BAR *) 0 &&
80                                         curr_p->u.clefsig_p->bar_p->endingloc
81                                         != NOITEM) {
82                                 first_p = curr_p;
83                                 endlabel = curr_p->u.clefsig_p->bar_p->endinglabel;
84                         }
85                         continue;
86                 }
87
88                 /* for each bar, check its endingloc and act accordingly */
89                 else if (curr_p->str == S_BAR) {
90
91                         switch(curr_p->u.bar_p->endingloc) {
92
93                         case ENDITEM:
94                                 if (first_p == (struct MAINLL *) 0) {
95                                         pfatal("ending without beginning");
96                                 }
97                                 /*FALLTHRU*/
98                         case NOITEM:
99                                 /* if we were doing an ending, we reached the
100                                  * end of it, so handle it */
101                                 if (first_p != (struct MAINLL *) 0) {
102                                         /* it doesn't seem like it should be
103                                          * possible to get inside this IF for
104                                          * the NOITEM case, but it doesn't
105                                          * hurt anything to have to code as it
106                                          * is, and I don't want to change it for
107                                          * fear it would break some obscure
108                                          * circumstance I've forgotten about */
109                                         do_endings(first_p, curr_p, endlabel, NO);
110                                         endlabel = (char *) 0;
111                                         first_p = (struct MAINLL *) 0;
112                                 }
113                                 break;
114
115                         case STARTITEM:
116                                 /* if we are also implictly ending a previous
117                                  * ending, do that first. In any case, keep
118                                  * track of where this ending begins. */
119                                 if (first_p != (struct MAINLL *) 0) {
120                                         do_endings(first_p, curr_p,
121                                                                 endlabel, NO);
122                                 }
123
124                                 first_p = curr_p;
125                                 endlabel = curr_p->u.bar_p->endinglabel;
126
127                                 break;
128
129                         case INITEM:
130                                 break;
131
132                         default:
133                                 pfatal("bad endingloc value");
134                                 /*NOTREACHED*/
135                                 break;
136                         }
137                         last_bar_p = curr_p;
138                 }
139         }
140
141         /* we must be at the end of the score. If we are in the middle
142          * of an ending, draw this score's portion of it now */
143         if ( (first_p != (struct MAINLL *) 0)
144                                 && (last_bar_p != (struct MAINLL *) 0)) {
145                 do_endings(first_p, last_bar_p, endlabel,
146                                 last_bar_p->u.bar_p->endingloc
147                                 == INITEM ? YES : NO);
148         }
149 }
150 \f
151
152 /* now that we have identified an ending, print it above each
153  * staff that is supposed to get endings */
154
155 static void
156 do_endings(first_p, last_p, endlabel, carryout)
157
158 struct MAINLL *first_p; /* where to begin drawing endings */
159 struct MAINLL *last_p;  /* where to end the endings */
160 char *endlabel;         /* if ending has a label, this will be
161                          * that label, otherwise NULL */
162 int carryout;           /* YES if will carry over to next staff */
163
164 {
165         struct MARKCOORD *markc_p;      /* info about where to draw ending */
166
167
168         /* for each staff that is supposed to have endings,
169          * draw the ending */
170         if (first_p->str == S_CLEFSIG) {
171                 /* pseudo-bar */
172                 markc_p = first_p->u.clefsig_p->bar_p->ending_p;
173         }
174         else {
175                 /* normal bar */
176                 markc_p = first_p->u.bar_p->ending_p;
177         }
178
179         /* if this is an endending and the bar line at the end is an ordinary
180          * bar or an invisbar, then set the carryout flag so that the final
181          * vertical line doesn't get drawn. */
182         if (last_p->u.bar_p->endingloc == ENDITEM
183                                 && (last_p->u.bar_p->bartype == SINGLEBAR ||
184                                 last_p->u.bar_p->bartype == INVISBAR)) {
185                 /* But if this is the very end of the piece,
186                  * then we *do* want to draw the final vertical */
187                 struct MAINLL* mll_p;
188                 for (mll_p = last_p->next; mll_p != 0; mll_p = mll_p->next) {
189                         if (mll_p->str == S_STAFF) {
190                                 break;
191                         }
192                 }
193                 if (mll_p != 0) {
194                         carryout = YES;
195                 }
196         }
197
198         /* draw an ending for each item in MARKCOORD list */
199         for (   ; markc_p != (struct MARKCOORD *) 0; markc_p = markc_p->next) {
200                 draw_ending(markc_p->staffno, (double) markc_p->ry,
201                                         first_p, last_p, endlabel, carryout);
202         }
203 }
204 \f
205
206 /* draw ending marks over specified staff */
207
208 static void
209 draw_ending(staffno, ry, first_p, last_p, endlabel, carryout)
210
211 int staffno;            /* which staff to draw over */
212 double ry;              /* relative y */
213 struct MAINLL *first_p; /* draw ending starting from here */
214 struct MAINLL *last_p;  /* ending end here */
215 char *endlabel;         /* if has label, this is the label, else is NULL */
216 int carryout;           /* if YES, will carry over to next score */
217
218 {
219         float begin_x;          /* x coord of beginning and end of ending */
220         float y;                /* vertical location of ending */
221
222
223         /* first_p can point to either a CLEFSIG (if carrying over an
224          * ending) or to a BAR. Find appropriate x coordinate */
225         switch (first_p->str) {
226
227         case S_CLEFSIG:
228                 begin_x = first_p->u.clefsig_p->bar_p->c[AX];
229                 break;
230
231         case S_BAR:
232                 begin_x = first_p->u.bar_p->c[AX];
233                 break;
234
235         default:
236                 /* shut up compilers that erroneously thinks begin_x
237                  * could get used without being set. */
238                 begin_x = 0.0;
239                 pfatal("bad struct type passed to draw_ending");
240                 /*NOTREACHED*/
241                 break;
242         }
243
244         /* get vertical position */
245         y = Staffs_y[staffno] + ry;
246
247         /* now we know where to put it, so draw it */
248         do_linetype(L_NORMAL);
249
250         /* print the beginning vertical and label now if appropriate */
251         if (endlabel != (char *) 0) {
252                 pr_endlabel( (double) begin_x, (double) y, endlabel);
253         }
254
255         pr_end_line ( (double) begin_x, (double) last_p->u.bar_p->c[AX],
256                                                 (double) y, carryout);
257 }
258 \f
259
260 /* print label at the beginning of an ending, along with the vertical line
261  * to the left of the label */
262
263 static void
264 pr_endlabel(x, y, label)
265
266 double x;       /* coordinate of beginning of ending */
267 double y;
268 char *label;    /* the ending label or NULL */
269
270 {
271         /* if there is a label, this is the beginning of an ending, so
272          * print a vertical line followed by the label */
273         if (label != (char *) 0) {
274                 x += (2.0 * STDPAD);
275                 draw_line(x, y + STDPAD, x, y + ENDINGHEIGHT - STDPAD);
276                 pr_string(x + (3.0 * STDPAD), y + (2.0 * STDPAD), label, J_LEFT,
277                                         (char *) 0, -1);
278         }
279 }
280 \f
281
282 /* print horizontal line above ending, possibly with ending vertical line */
283
284 static void
285 pr_end_line(begin_x, end_x, y, carryout)
286
287 double begin_x; /* horizontal coordinates of ending line */
288 double end_x;
289 double y;       /* vertical position */
290 int carryout;   /* if YES, continued on next score, so no end vertical */
291
292 {
293         /* adjust to allow a little padding */
294         begin_x += (2.0 * STDPAD);
295         end_x -= (2.0 * STDPAD);
296         y += ENDINGHEIGHT - STDPAD;
297
298         /* draw the horizontal line above the ending */
299         draw_line(begin_x, y, end_x, y);
300
301         /* if the ending ends here, draw vertical line at end */
302         if (carryout == NO) {
303                 draw_line(end_x, y, end_x, y - ENDINGHEIGHT + (2.0 * STDPAD));
304         }
305 }
306 \f
307
308 /* function to tell whether a given staff should have ending put on it. Returns
309  * YES if it does, NO if it doesn't */
310
311 int
312 has_ending(staffno)
313
314 int staffno;            /* which staff to check */
315
316 {
317         register int s;         /* index through barstlist */
318
319
320         /* if staff is invisible, ending doesn't count */
321         if ( svpath(staffno, VISIBLE)->visible == NO) {
322                 return(NO);
323         }
324  
325         switch ( Score.endingstyle ) {
326
327         case ENDING_TOP:
328                 /* if there is an earlier staff that is visible, then no
329                  * ending on this one. Otherwise there is */
330                 return( is_top_visible_in_range(staffno, 1) );
331
332         case ENDING_BARRED:
333                 /* go through barstlist. If this
334                  * staff is within a range and any staffs above it are
335                  * invisible at the moment, it gets an ending. */
336                 for (s = 0; s < Score.nbarst;  s++) {
337
338                         if ((staffno >= Score.barstlist[s].top) && (staffno
339                                                 <= Score.barstlist[s].bottom)) {
340
341                                 return( is_top_visible_in_range(staffno,
342                                                 Score.barstlist[s].top));
343                         }
344                 }
345
346                 /* if wasn't in any of the ranges, then it must be barred
347                  * by itself */
348                 return(YES);
349
350         case ENDING_GROUPED:
351                 /* go through brace and bracket list. If the top visible
352                  * of any of them match the given score, it gets an ending */
353                 for (s = 0; s < Score.nbrace; s++) {
354
355                         if ((staffno >= Score.bracelist[s].topstaff)
356                                         && (staffno
357                                         <= Score.bracelist[s].botstaff)) {
358
359                                 return( is_top_visible_in_range(staffno,
360                                         Score.bracelist[s].topstaff));
361                         }
362                 }
363
364                 for (s = 0; s < Score.nbrack; s++) {
365
366                         if ((staffno >= Score.bracklist[s].topstaff)
367                                         && (staffno
368                                         <= Score.bracklist[s].botstaff)) {
369
370                                 return( is_top_visible_in_range(staffno,
371                                         Score.bracklist[s].topstaff));
372                         }
373                 }
374
375                 /* wasn't in either list, so it probably shouldn't have an
376                  * ending. However, if it happens to be the top staff, we
377                  * better put one on anyway, because the top staff should
378                  * always get an ending. */
379                 return( is_top_visible_in_range(staffno, 1) );
380
381         default:
382                 pfatal("unknown endingstyle");
383         }
384         /*NOTREACHED*/
385         return(NO);
386 }
387 \f
388
389 /* given a staff number and the top of a range of staffs, return YES if the
390  * given staff is the top visible staff in the range, otherwise return NO.
391  * Assume that staffno itself is for a visible staff */
392
393 int
394 is_top_visible_in_range(staffno, top)
395
396 int staffno;    /* which staff to check */
397 int top;        /* top of range to check */
398
399 {
400         for (staffno--; staffno >= top; staffno--) {
401                 if ( svpath(staffno, VISIBLE)->visible == YES) {
402                         /* something above it is visible */
403                         return(NO);
404                 }
405         }
406         return(YES);
407 }
408 \f
409
410 /* functions for printing piano pedals marks */
411
412 /* keep track of where last coordinate of pedal mark was for each staff.
413  * If no pedal currently on a staff, set to 0.0 */
414 static float Last_ped_x[MAXSTAFFS + 1];
415 static float Last_ped_y[MAXSTAFFS + 1];
416
417
418 /* return the distance to offset the 'P' of "Ped." from the group X to
419  * center it on the group. The first time called, calculate the width,
420  * after that, just return it */
421
422 double
423 ped_offset()
424
425 {
426         static double width = 0.0;
427         char pstr[4];
428
429         if (width == 0.0) {
430                 /* first time, make string with just P and get width of that */
431                 (void) strncpy(pstr, Ped_start, 3);
432                 pstr[3] = '\0';
433                 width = strwidth(pstr) / 2.0;
434         }
435         return(width);
436 }
437 \f
438
439 /* when we encounter a ST_PEDAL, print the pedal character and save the
440  * east boundary as the last pedal x value, for later use. If is endped,
441  * set this last pedal x value to 0.0 */
442
443 void
444 pr_ped_char(stuff_p, staffno)
445
446 struct STUFF *stuff_p;          /* pedal info */
447 int staffno;                    /* which staff */
448
449 {
450         int font;
451         int size;
452         char *string;
453         int pedstyle;   /* P_PEDSTAR or P_ALTPEDSTAR or P_LINE */
454         int pedchar;    /* pedal music character code */
455         double overlap; /* to avoid tiny gaps in pedal line due to roundoff */
456         char *adj_pstart;       /* Ped_start adjusted for Staffscale */
457         char *adj_pstop;        /* Ped_stop adjusted for Staffscale */
458
459
460         if (stuff_p->string == (char *) 0) {
461                 /* must be a pedal mark carried over from a previous
462                  * score. Just need to save away the coordinate. */
463                 Last_ped_x[staffno] = stuff_p->c[AX];
464                 Last_ped_y[staffno] = stuff_p->c[AY];
465                 return;
466         }
467
468         pedstyle = svpath(staffno, PEDSTYLE)->pedstyle;
469
470         /* extract the pedal character to be printed */
471         font = stuff_p->string[0];
472         size = stuff_p->string[1];
473         string = stuff_p->string + 2;
474         pedchar = next_str_char(&string, &font, &size) & 0xff;
475
476         /* overlap lines just slightly with pedal characters, to compensate
477          * for any rounding of the bounding box which might cause a tiny
478          * gap to appear between the line and the pedal character */
479         overlap = Stdpad / 3.0;
480
481         /* draw line from previous pedal character, if any, to this one */
482         if (pedstyle == P_LINE && Last_ped_x[staffno] != 0.0) {
483                 if (stuff_p->c[AW] + overlap - Last_ped_x[staffno] > 0) {
484                         do_linetype(L_NORMAL);
485                         draw_line(Last_ped_x[staffno], stuff_p->c[AY],
486                                 stuff_p->c[AW] + overlap, stuff_p->c[AY]);
487                 }
488         }
489
490         Last_ped_y[staffno] = stuff_p->c[AY];
491
492         switch (pedchar) {
493
494         case C_BEGPED:
495                 if (Last_ped_x[staffno] != 0.0) {
496                         /* This used to be a pfatal, because it should
497                          * never happen. But it can happen
498                          * due to user error: if user does something like
499                          *      pedal ....
500                          *      repeatend
501                          *      ....
502                          *      pedal ...
503                          *      repeatend
504                          * Having two repeatends without an intervening
505                          * repeatstart is illegal.
506                          * It has never shown up as a true pfatal in millions
507                          * of test runs, so if it is ever hit,
508                          * it's probably the user error case.
509                          */
510                         ufatal("got begin pedal when already doing pedal, staff %d", staffno);
511                 }
512                 Last_ped_x[staffno] = stuff_p->c[AE] - overlap;
513                 break;
514                 
515         case C_PEDAL:
516                 if (Last_ped_x[staffno] == 0.0) {
517                         pfatal("got pedal without begped, staff %d", staffno);
518                 }
519                 Last_ped_x[staffno] = stuff_p->c[AE] - overlap;
520                 break;
521
522         case C_ENDPED:
523                 if (Last_ped_x[staffno] == 0.0) {
524                         pfatal("got endped without begped, staff %d", staffno);
525                 }
526                 Last_ped_x[staffno] = 0.0;
527                 break;
528                 
529         default:
530                 pfatal("bad character 0x%x in pedal string", pedchar);
531                 /*NOTREACHED*/
532                 break;
533         }
534
535         /* now print the appropriate pedal character */
536         if (pedstyle == P_LINE) {
537                 /* We used to print the pedal characters from FONT_MUSIC,
538                  * but Ghostscript sometimes misaligned them with the
539                  * pedal lines, so now we draw the characters "manually."
540                  */
541                 do_linetype(L_NORMAL);
542                 switch (pedchar) {
543                 case C_BEGPED:
544                         draw_line(stuff_p->c[AX], stuff_p->c[AN],
545                                 stuff_p->c[AX], stuff_p->c[AY]);
546                         draw_line(stuff_p->c[AX], stuff_p->c[AY],
547                                 stuff_p->c[AE] + overlap, stuff_p->c[AY]);
548                         break;
549                 case C_PEDAL:
550                         draw_line(stuff_p->c[AW], stuff_p->c[AY],
551                                 stuff_p->c[AX], stuff_p->c[AN]);
552                         draw_line(stuff_p->c[AX], stuff_p->c[AN],
553                                 stuff_p->c[AE], stuff_p->c[AY]);
554                         break;
555                 case C_ENDPED:
556                         draw_line(stuff_p->c[AW] - overlap, stuff_p->c[AY],
557                                 stuff_p->c[AX], stuff_p->c[AY]);
558                         draw_line(stuff_p->c[AX], stuff_p->c[AN],
559                                 stuff_p->c[AX], stuff_p->c[AY]);
560                         break;
561
562                 }
563                         
564         }
565         else {
566                 /* If we need to adjust for Staffscale, make a temp copy */
567                 if (Staffscale != 1.0) {
568                         adj_pstart = copy_string(Ped_start + 2,
569                                 (int) Ped_start[0],
570                                 adj_size( (int) Ped_start[1], Staffscale,
571                                 (char *) 0, -1));
572                         adj_pstop = copy_string(Ped_stop + 2,
573                                 (int) Ped_stop[0],
574                                 adj_size( (int) Ped_stop[1], Staffscale,
575                                 (char *) 0, -1));
576                 }
577                 else {
578                         adj_pstart = Ped_start;
579                         adj_pstop = Ped_stop;
580                 }
581
582                 /* In alt pedstar style, a PEDAL is treated exactly like
583                  * a BEGPED, so pretend that's what we got. */
584                 if (pedstyle == P_ALTPEDSTAR && pedchar == C_PEDAL) {
585                         pedchar = C_BEGPED;
586                 }
587
588                 switch (pedchar) {
589
590                 case C_BEGPED:
591                         pr_string(stuff_p->c[AX] - (strwidth(adj_pstart) / 2.0),
592                                         stuff_p->c[AY], adj_pstart,
593                                         J_CENTER, stuff_p->inputfile,
594                                         stuff_p->inputlineno);
595                         break;
596
597                 case C_PEDAL:
598                         pr_string(stuff_p->c[AX] - strwidth(adj_pstop)
599                                         - ped_offset() * Staffscale,
600                                         stuff_p->c[AY], adj_pstop,
601                                         J_RIGHT, stuff_p->inputfile,
602                                         stuff_p->inputlineno);
603                         pr_string(stuff_p->c[AX] - ped_offset() * Staffscale,
604                                         stuff_p->c[AY], adj_pstart,
605                                         J_LEFT, stuff_p->inputfile,
606                                         stuff_p->inputlineno);
607                         break;
608
609                 case C_ENDPED:
610                         pr_string(stuff_p->c[AX] - (strwidth(adj_pstop) / 2.0),
611                                         stuff_p->c[AY], adj_pstop,
612                                         J_CENTER, stuff_p->inputfile,
613                                         stuff_p->inputlineno);
614                         break;
615
616                 default:
617                         pfatal("bad character 0x%x in pedal string", pedchar);
618                         /*NOTREACHED*/
619                         break;
620                 }
621
622                 /* If we had to make a temp copy to account for Staffscale,
623                  * free the temp copy. */
624                 if (Staffscale != 1.0) {
625                         FREE(adj_pstart);
626                         FREE(adj_pstop);
627                 }
628         }
629 }
630 \f
631
632 /* when we hit a bar line, extend any pedal marks to the bar line. Since things
633  * are stored in units of bars, easier to do this than keep track of the
634  * entire length and have to worry about page feeds, etc. This is just for
635  * normal bars, not pseudo-bars. They are handled separately. */
636
637 void
638 pr_ped_bar(mll_p, bar_p)
639
640 struct MAINLL *mll_p;   /* print pedal mark up to bar hangs off of here */
641 struct BAR *bar_p;      /* print pedal marks up to this bar */
642
643 {
644         register int s;
645         float endadj;           /* adjustment for endings */
646
647
648         /* for each staff that has pedal marks pending, draw an extension
649          * line to where this bar line is and reset Last_ped_x */
650         for (s = 1; s <= Score.staffs; s++) {
651
652                 if (Last_ped_x[s] != 0.0) {
653
654                         if (Last_ped_y[s] <= 0.0) {
655                                 pfatal("don't have y coordinate for drawing pedal mark");
656                         }
657
658                         endadj = 0.0;
659                         if (svpath(s, PEDSTYLE)->pedstyle == P_LINE) {
660                                 do_linetype(L_NORMAL);
661                                 if (Ped_snapshot[0] == YES &&
662                                                 bar_p->endingloc == STARTITEM) {
663
664                                         /* going into 2nd ending, so shorten
665                                          * this pedal to not reach bar */
666                                         endadj = (2.0 * STEPSIZE);
667                                         /* if line length is positive,  
668                                          * draw it */
669                                         if (bar_p->c[AX] - endadj
670                                                         > Last_ped_x[s]) {
671
672                                                 draw_line(Last_ped_x[s],
673                                                         Last_ped_y[s],
674                                                         bar_p->c[AX] - endadj,
675                                                         Last_ped_y[s]);
676                                         }
677                                 }
678                                 else {
679                                         if (bar_p->c[AX] - STDPAD >
680                                                         Last_ped_x[s]) {
681                                                 draw_line(Last_ped_x[s],
682                                                                 Last_ped_y[s],
683                                                                 bar_p->c[AX]
684                                                                 - STDPAD,
685                                                                 Last_ped_y[s]);
686                                                 endadj = -STDPAD;
687                                         }
688                                         else {
689                                                 endadj = -(bar_p->c[AX]
690                                                         - Last_ped_x[s]);
691                                         }
692                                 }
693                         }
694
695                         Last_ped_x[s] = bar_p->c[AX] + endadj;
696                 }
697         }
698         saveped(mll_p, bar_p);
699 }
700 \f
701
702 /* handle pedal going into endings. When we hit a first ending, save the
703  * state of the pedal for all staffs. On subsequent endings in the set,
704  * reset the pedal state to what it was at the beginning of the first ending.
705  * At the endending, go back to normal operation. */
706
707 void
708 saveped(mll_p, bar_p)
709
710 struct MAINLL *mll_p;   /* bar is connected here */
711 struct BAR *bar_p;
712
713 {
714         register int s;         /* staff index */
715
716
717         if (mll_p == (struct MAINLL *) 0) {
718                 pfatal("null pointer in saveped");
719         }
720
721         if (bar_p->endingloc == STARTITEM) {
722
723                 if (Ped_snapshot[0] == YES) {
724
725                         /* starting 2nd ending: restore pedal state as it was
726                          * at beginning of first ending */
727                         for (s = 1; s <= Score.staffs; s++) {
728                                 if (Ped_snapshot[s] == YES) {
729                                         Last_ped_x[s] = bar_p->c[AX]
730                                                         + (2.0 * STEPSIZE);
731                                 }
732                                 else {
733                                         Last_ped_x[s] = 0.0;
734                                 }
735                         }
736                 }
737
738                 else {
739                         /* starting a set of endings,
740                          * need to save pedal state at this
741                          * point so we can carry it into subsequent endings */
742                         for (s = 1; s <= Score.staffs; s++) {
743                                 /* set to YES if pedal is on */
744                                 Ped_snapshot[s] = (Last_ped_x[s] == 0.0 ? NO : YES);
745                         }
746                         /* make sure any remaining staffs are set to pedal off,
747                          * in case user increases the number of staffs
748                          * during the endings... */
749                         for (   ; s <= MAXSTAFFS; s++) {
750                                 Ped_snapshot[s] = NO;
751                         }
752
753                         /* mark that we now have a snapshot */
754                         Ped_snapshot[0] = YES;
755                 }
756         }
757
758         else if (bar_p->endingloc == ENDITEM) {
759                 /* at end of endings, discard snapshot of pedal states.
760                  * However, we have to make sure this is really the end of
761                  * endings, and not just a bar that was marked as end
762                  * because the start of the next was moved from here to
763                  * the pseudo bar. So we search forward, if we find a
764                  * clefsig with pseudo-bar before finding a chhead,
765                  * and that pseudo bar endingloc is STARTITEM, then this
766                  * isn't really the end of endings, and should be ignored. */
767                 for (   ; mll_p != (struct MAINLL *) 0; mll_p = mll_p->next) {
768                         if (mll_p->str == S_CHHEAD) {
769                                 /* is end of endings */
770                                 break;
771                         }
772                         else if (mll_p->str == S_CLEFSIG &&
773                                         mll_p->u.clefsig_p->bar_p !=
774                                         (struct BAR *) 0 &&
775                                         mll_p->u.clefsig_p->bar_p->endingloc
776                                         == STARTITEM) {
777                                 /* not really end of endings */
778                                 return;
779                         }
780                 }
781                 Ped_snapshot[0] = NO;
782         }
783 }
784 \f
785
786 /* given a list of phrase mark curve coordinates, print the curve */
787 /* output each x,y, coordinate pair, then the number of coordinates and
788  * finally the "curve" function name */
789
790 void
791 pr_phrase(crvlist_p, linetype, tapered, staffno)
792
793 struct CRVLIST *crvlist_p;      /* the curve to print */
794 int linetype;                   /* if not tapered, may be L_DOTTED or L_DASHED*/
795 int tapered;                    /* YES or NO */
796 int staffno;                    /* which staff, to get staffscale */
797
798 {
799         int n;
800         struct CRVLIST *c_p;
801         float *xlist,* ylist;
802
803         /* count up number of coordinates */
804         for (n = 0, c_p = crvlist_p; c_p != (struct CRVLIST *) 0;
805                                         c_p = c_p->next) {
806                 n++;
807         }
808
809         MALLOCA(float, xlist, n);
810         MALLOCA(float, ylist, n);
811         for (n = 0, c_p = crvlist_p; c_p != (struct CRVLIST *) 0;
812                                         c_p = c_p->next, n++) {
813                 xlist[n] = c_p->x;
814                 ylist[n] = c_p->y;
815         }
816         if (tapered == NO) {
817                 do_linetype(linetype);
818         }
819         pr_allcurve(xlist, ylist, n,
820                 svpath(staffno, STAFFSCALE)->staffscale * W_MEDIUM / PPI,
821                 tapered);
822         FREE(xlist);
823         FREE(ylist);
824 }
825 \f
826 /*
827  * Name:        pr_allcurve()
828  *
829  * Abstract:    Print a curve, either generated (e.g., tie) or user-defined.
830  *
831  * Returns:     void
832  *
833  * Description: This function is given an array of CURVEINFOs, one for each
834  *              point of a curve, where x and y have been filled in.  It fills
835  *              the rest of the items in the structures, and prints PostScript
836  *              commands for drawing the curve.  If the curve is to be dashed
837  *              or dotted, the calling function must put out the PostScript
838  *              commands for that.  It does not handle "wavy".
839  */
840
841
842 void
843 pr_allcurve(x, y, num, cwid, tapered)
844
845 float x[], y[];         /* coordinates of the curve's points */
846 int num;                /* number of elements (points) in the array */
847 double cwid;            /* (max) width of the curve, in inches */
848 int tapered;            /* YES or NO */
849
850 {
851         struct CURVEINFO *v;    /* malloc structs for holding point info */
852         float *slen;            /* malloc length of each segment */
853         float *xoff;            /* malloc x offset of curve boundary from mid*/
854         float *yoff;            /* malloc y offset of curve boundary from mid*/
855         float *off;             /* malloc total offset of curve boundary */
856         float totlen;           /* len of curve, along segments */
857         float maxplace;         /* distance from end where a tapered curve */
858                                 /* reaches its maximum thickness */
859         float cumlen, remlen, fromend;  /* used in tapering curve */
860         float dx, dy;           /* for finding x and y offsets */
861         float temp;             /* temp variable */
862         int n;                  /* loop through points */
863
864
865         /*
866          * If the curve is not to be tapered, calculate Bezier curves joining
867          * these points, and stroke the resulting path.
868          */
869         if (tapered == NO) {
870                 /* load coords into structures, and calculate control points */
871                 MALLOC(CURVEINFO, v, num);
872                 for (n = 0; n < num; n++) {
873                         v[n].x = x[n];
874                         v[n].y = y[n];
875                 }
876                 calccurve(v, num);
877
878                 /* output results in PostScript */
879                 do_moveto(v[0].x, v[0].y);
880
881                 for (n = 0; n < num - 1; n++) {
882                         do_curveto( v[n].x1, v[n].y1, v[n].x2, v[n].y2,
883                                         v[n+1].x, v[n+1].y);
884                 }
885                 do_stroke();
886
887                 FREE(v);
888                 return;
889         }
890
891         /*
892          * The curve is to be tapered.  We're going to draw two series of
893          * Bezier curves, forming the boundaries of the whole curve, and then
894          * fill.  Note that this will always result in a solid curve,
895          * regardless of any earlier request for dashes or dots.
896          */
897         /* first allocate the arrays we're going to need */
898         MALLOC(CURVEINFO, v, num);
899         MALLOCA(float, slen, num);
900         MALLOCA(float, xoff, num);
901         MALLOCA(float, yoff, num);
902         MALLOCA(float, off, num);
903
904         /* find and save len of each segment, and accumulate total len */
905         totlen = 0;
906         for (n = 0; n < num - 1; n++) {
907                 slen[n] = sqrt( (double) (SQUARED(x[n+1] - x[n]) +
908                                 SQUARED(y[n+1] - y[n]) ) );
909                 totlen += slen[n];
910         }
911
912         /*
913          * Tapering occurs up to a max of 1/3 inches from the end of a curve.
914          * maxplace is set up such that it is normally the distance from the
915          * end where the max thickness is attained.  However, if a curve is
916          * shorter than 2/3 inches, it will never attain this max thickness.
917          */
918         if (totlen > 0.5) {
919                 maxplace = 1.0/3.0;
920         } else {
921                 maxplace = totlen * (2.0/3.0);
922         }
923
924         cumlen = 0.0;           /* none accumulated so far */
925         remlen = totlen;        /* all of it remains */
926         for (n = 0; n < num; n++) {
927                 /* whichever end this point is closer to, note distance */
928                 if (cumlen < remlen) {
929                         fromend = cumlen;
930                 }
931                 else {
932                         fromend = remlen;
933                 }
934
935                 /* set the offset for this point for achieving tapering */
936                 if (fromend > maxplace) {
937                         off[n] = cwid / 2.0;
938                 } else {
939                         float taperwid;
940                         /*
941                          * For curves longer than 2/3, taperwid should be only
942                          * half of TAPERWID; for zero length curves, it should
943                          * be full TAPERWID; in between, adjust linearly.  Then,
944                          * at the ends, the width is taperwid times the full
945                          * standard thickness (the middle of a long curve); and
946                          * ramp up linearly towards 1/3 inches from the end.
947                          */
948                         taperwid = totlen > 2.0/3.0 ? TAPERWID / 2.0 :
949                                         TAPERWID * (1.0 - 0.75 * totlen);
950                         off[n] = (cwid / 2.0) *
951                         ((1.0 - taperwid) * fromend / maxplace + taperwid);
952                 }
953
954                 /*
955                  * Break offset into x and y components, based on the slope
956                  * between the two surrounding points.  For the endpoints,
957                  * there are not two surrounding points, so use the slope of
958                  * the neighboring segment.
959                  */
960                 /*
961                  * First get deltas; x and y are switched and sign reversed
962                  * on one, because we're concerned with the line perpendicular
963                  * to the line joining the two points.  (Its slope is the
964                  * negative inverse.)  Only the ratio dx/dy matters, not the
965                  * values.
966                  */
967                 if (n == 0) {
968                         dx = y[1] - y[0];
969                         dy = x[0] - x[1];
970                 } else if (n == num - 1) {
971                         dx = y[num-1] - y[num-2];
972                         dy = x[num-2] - x[num-1];
973                 } else {
974                         dx = y[n+1] - y[n-1];
975                         dy = x[n-1] - x[n+1];
976                 }
977
978                 /* get hypotenuse of something */
979                 temp = off[n] / sqrt( (double) (SQUARED(dx) + SQUARED(dy)) );
980
981                 /* get x and y offsets; may need to switch signs */
982                 xoff[n] = fabs(temp * dx);
983                 yoff[n] = fabs(temp * dy);
984                 if (dx > 0)
985                         xoff[n] *= -1;
986                 if (dy > 0)
987                         yoff[n] *= -1;
988
989                 /* update cumulative and remaining length if not at end */
990                 if (n < num - 1) {
991                         cumlen += slen[n];
992                         remlen -= slen[n];
993                 }
994         }
995
996         /*
997          * Load coords into structures, and calculate control points, for one
998          * boundary of the curve.
999          */
1000         for (n = 0; n < num; n++) {
1001                 v[n].x = x[n] + xoff[n];
1002                 v[n].y = y[n] + yoff[n];
1003         }
1004         calccurve(v, num);
1005
1006         /*
1007          * Move to the center of the curve's thickness at the beginning point.
1008          * Draw a perpendicular line to the curve's boundary.  Then generate
1009          * the curves that form this side's boundary.
1010          */
1011         do_moveto(x[0], y[0]);
1012         do_line(v[0].x, v[0].y);
1013         for (n = 0; n < num - 1; n++) {
1014                 do_curveto( v[n].x1, v[n].y1, v[n].x2, v[n].y2,
1015                                 v[n+1].x, v[n+1].y);
1016         }
1017
1018         /*
1019          * Load coords into structures, and calculate control points, for the
1020          * other boundary of the curve.  We're going to do this side in
1021          * reverse, back to the beginning.
1022          */
1023         for (n = 0; n < num; n++) {
1024                 v[n].x = x[num - 1 - n] - xoff[num - 1 - n];
1025                 v[n].y = y[num - 1 - n] - yoff[num - 1 - n];
1026         }
1027         calccurve(v, num);
1028
1029         /*
1030          * Draw a line across the curve's thickness at this end.  Then
1031          * generate the curves that form this side's boundary.
1032          */
1033         do_line( v[0].x, v[0].y);
1034         for (n = 0; n < num - 1; n++) {
1035                 do_curveto( v[n].x1, v[n].y1, v[n].x2, v[n].y2,
1036                                 v[n+1].x, v[n+1].y);
1037         }
1038
1039         /* fill to form the full, solid curve */
1040         do_fill();
1041
1042         FREE(slen);
1043         FREE(xoff);
1044         FREE(yoff);
1045         FREE(off);
1046         FREE(v);
1047 }
1048 \f
1049 /*
1050  * Name:        calccurve()
1051  *
1052  * Abstract:    Calculate info for drawing cubic arcs through a curve's points.
1053  *
1054  * Returns:     void
1055  *
1056  * Description: This function is given an array of CURVEINFOs, one for each
1057  *              point of a curve, where x and y have been filled in.  It fills
1058  *              in the rest of the items in the structures.  Specifically, in
1059  *              each structure, (x1, y1) and (x2, y2) are the control points
1060  *              for drawing a Bezier curve (using curveto) from this point to
1061  *              the next one.  These points are chosen in such a way that the
1062  *              slopes at the end of one curve and the start of the next are
1063  *              equal, to avoid a sharp corner.  Also, the angles this slope
1064  *              forms with straight line segments connecting the points are
1065  *              equal.  The angle at the start of the first curve is set equal
1066  *              to the angle at the end of it, and the angle at the end of the
1067  *              last curve is set equal to the angle at the beginning of it.
1068  *              The other fields in the CURVEINFOs are set but aren't useful to
1069  *              the caller.  In the last point's CURVEINFO, some of the fields
1070  *              are not used (including x1, y1, x2, y2).
1071  */
1072
1073
1074 static void
1075 calccurve(v, num)
1076
1077 struct CURVEINFO v[];   /* array of curve points, x and y must be filled in */
1078 int num;                /* number of elements (points) in the array */
1079
1080 {
1081         int n;                          /* loop through the points */
1082         float temp, delx, dely;         /* temp variables */
1083         float slope, intercept;         /* for equation of a segment */
1084
1085
1086         /* find the length of each segment connecting neighboring points */
1087         for (n = 0; n < num - 1; n++) {
1088                 /* use Pythagorean theorem; put result in 1st point's "len" */
1089                 delx = v[n+1].x - v[n].x;
1090                 dely = v[n+1].y - v[n].y;
1091                 v[n].len = sqrt( (double) (SQUARED(delx) + SQUARED(dely)) );
1092                 if (v[n].len == 0)
1093                         ufatal("two curve points are equal");
1094         }
1095
1096         /* find the angle at each point other than the endpoints */
1097         for (n = 1; n < num - 1; n++) {
1098                 /*
1099                  * Use the law of cosines on the triangle formed by this point
1100                  * and the preceding and following points.  First get the delta
1101                  * from the preceding point to the following point.
1102                  */
1103                 delx = v[n+1].x - v[n-1].x;
1104                 dely = v[n+1].y - v[n-1].y;
1105
1106                 /*
1107                  * The law of cosines:  c^2  =  a^2  +  b^2  -  2 a b cos(C).
1108                  * Solve this for the cosine of our point's angle (angle "C").
1109                  */
1110                 temp = ( SQUARED(v[n-1].len) + SQUARED(v[n].len) -
1111                         (SQUARED(delx) + SQUARED(dely)) ) /
1112                         ( 2.0 * v[n-1].len * v[n].len );
1113
1114                 /* if angle is 180, should be -1, but guard against roundoff */
1115                 if (temp < -1)
1116                         temp = -1;      /* should have been exactly -1 */
1117
1118                 /* if angle is 0, this is not allowed in our curve */
1119                 if (temp >= 1)
1120                         ufatal("curve bends all the way back on itself");
1121
1122                 v[n].ang = acos(temp);
1123         }
1124
1125         /* set the bend direction at each point other than the endpoints */
1126         for (n = 1; n < num - 1; n++) {
1127                 /* handle special case where previous segment is vertical */
1128                 if (v[n-1].x == v[n].x) {
1129                         if (v[n-1].y < v[n].y) {
1130                                 if (v[n+1].x >= v[n].x)
1131                                         v[n].bend = 1;
1132                                 else
1133                                         v[n].bend = -1;
1134                         } else {
1135                                 if (v[n+1].x >= v[n].x)
1136                                         v[n].bend = -1;
1137                                 else
1138                                         v[n].bend = 1;
1139                         }
1140                         continue;       /* go to next loop iteration */
1141                 }
1142
1143                 /*
1144                  * Find the equation of the previous segment.  Plug the
1145                  * following point's x into that equation to get where its y
1146                  * would have been if the angle were 180.  Comparing that y to
1147                  * the actual y, we can determine the bend direction.
1148                  */
1149                 slope = (v[n].y - v[n-1].y) / (v[n].x - v[n-1].x);
1150                 intercept = v[n-1].y - slope * v[n-1].x;
1151                 temp = slope * v[n+1].x + intercept;
1152
1153                 if (v[n].x > v[n-1].x) {
1154                         if (v[n+1].y < temp)
1155                                 v[n].bend = 1;
1156                         else
1157                                 v[n].bend = -1;
1158                 } else {
1159                         if (v[n+1].y < temp)
1160                                 v[n].bend = -1;
1161                         else
1162                                 v[n].bend = 1;
1163                 }
1164         }
1165
1166         /*
1167          * At the endpoints, there is only one segment, so no angle or bend
1168          * direction is defined.  But we need to have something.  So we semi-
1169          * arbitrarily set these to the same value as their neighboring points.
1170          */
1171         v[0].ang = v[1].ang;
1172         v[0].bend = v[1].bend;
1173         v[num-1].ang = v[num-2].ang;
1174         v[num-1].bend = v[num-2].bend;
1175
1176         /*
1177          * For all points, set the slope of the line tangent to the curves
1178          * we're going to draw, in the coordinate system where the segment
1179          * starting at this point is horizontal.  (This is the coordinate
1180          * system that findcontrol() uses.)  Since the angle between segments
1181          * is not allowed to be 0, this slope is never vertical (infinity).
1182          */
1183         for (n = 0; n < num; n++)
1184                 v[n].slopetan = -v[n].bend * tan( v[n].ang / 2 + PI / 2);
1185
1186         /*
1187          * For each segment, calculate control points to define a Bezier curve
1188          * connecting the endpoints, according to the specifications.
1189          */
1190         for (n = 0; n < num - 1; n++)
1191                 findcontrol(v, n);
1192 }
1193 \f
1194 /*
1195  * Name:        findcontrol()
1196  *
1197  * Abstract:    Find Bezier control points for one segment of the curve.
1198  *
1199  * Returns:     void
1200  *
1201  * Description: This function is given an array of CURVEINFOs, one for each
1202  *              point, with everything filled in except the control points.  It
1203  *              calculates them and fills them in.
1204  */
1205
1206
1207 static void
1208 findcontrol(v, n)
1209
1210 struct CURVEINFO v[];
1211 int n;
1212
1213 {
1214         float costheta, sintheta;       /* for rotating axes by theta */
1215
1216         /*
1217          * All of the following variables refer to the rotated/translated
1218          * position of the segment (see comment below).  Point 0 is the
1219          * starting point, point 3 is the ending point, and points 1 and 2
1220          * are the Bezier control points.
1221          */
1222         float x1, x2, y1, y2;   /* control points */
1223         float x3;               /* end point (y3 is always 0) */
1224         float slope0, slope3;   /* slope of tangent lines at endpoints */
1225         float b, c;             /* some coefficients of cubic y = f(x) */
1226         float cx, by, cy;       /* Bezier coefficients */
1227
1228
1229         /*
1230          * Rotate and translate the axes so that the starting point (point 0)
1231          * is at the origin, and the ending point (3) is on the positive
1232          * x axis.  Their coords are (0, 0) and (v[n].len, 0).  We are going
1233          * to find a cubic equation that intersects the endpoints, and has the
1234          * necessary slope at those points such that the tangent line's slope
1235          * is halfway between horizontal (this segment) and the slope of the
1236          * neighboring segment.  The equation is
1237          *      y  =  a x^3  +  b x^2  +  c x  +  d
1238          * so the slope is
1239          *      y' =  3 a x^2  +  2 b x  +  c
1240          * By plugging the two points into these, you get 4 equations in the 4
1241          * unknowns a, b, c, d.
1242          */
1243         x3 = v[n].len;
1244
1245         /* find the slope of the tangent lines at the first & second points */
1246         slope0 = v[n].slopetan;
1247         slope3 = -v[n+1].slopetan;
1248
1249         /* set values of a, b, c (d turns out to be always 0) */
1250         /* a = (slope0 + slope3) / SQUARED(x3); don't really need this one */
1251         b = (-2 * slope0 - slope3) / x3;
1252         c = slope0;
1253
1254         /*
1255          * For Bezier version of this, let x = t / x3, and for y, plug this
1256          * into the cubic we have found.  This gives us the Bezier coeff.:
1257          *      x = ax t^3  +  bx t^2  +  cx t  +  x0
1258          *      y = ay t^3  +  by t^2  +  cy t  +  y0
1259          */
1260         /* ax and bx are always 0 */
1261         cx = x3;
1262         /* ay = a * CUBED(x3);          this one is not needed */
1263         by = b * SQUARED(x3);
1264         cy = c * x3;
1265
1266         /* get control points 1 and 2 from Bezier coefficients & endpoints */
1267         x1 = cx / 3;
1268         y1 = cy / 3;
1269         x2 = x1 + cx / 3;
1270         y2 = y1 + (by + cy) / 3;
1271
1272         /*
1273          * Rotate and translate the axes back to where they really were.  Store
1274          * these real positions of the control points.
1275          */
1276         costheta = (v[n+1].x - v[n].x) / v[n].len;
1277         sintheta = (v[n+1].y - v[n].y) / v[n].len;
1278
1279         v[n].x1 = v[n].x + x1 * costheta - y1 * sintheta;
1280         v[n].x2 = v[n].x + x2 * costheta - y2 * sintheta;
1281         v[n].y1 = v[n].y + y1 * costheta + x1 * sintheta;
1282         v[n].y2 = v[n].y + y2 * costheta + x2 * sintheta;
1283 }
1284 \f
1285
1286 /* draw a V-shaped bend indicator by drawing two line segments */
1287
1288 void
1289 pr_bend(crvlist_p)
1290
1291 struct CRVLIST *crvlist_p;
1292
1293 {
1294         if (crvlist_p == (struct CRVLIST *) 0
1295                         || crvlist_p->next == (struct CRVLIST *) 0
1296                         || crvlist_p->next->next == (struct CRVLIST *) 0) {
1297                 pfatal("invalid bend crvlist");
1298         }
1299
1300         do_linetype(L_NORMAL);
1301         draw_line(crvlist_p->x, crvlist_p->y, crvlist_p->next->x, crvlist_p->next->y);
1302         draw_line(crvlist_p->next->x, crvlist_p->next->y,
1303                         crvlist_p->next->next->x, crvlist_p->next->next->y);
1304 }
1305 \f
1306
1307 /* draw a slide for a tab or tabnote staff. Slides are stored internally
1308  * like slurs. Here we just draw a line between the appropriate coordinates */
1309
1310 void
1311 pr_tabslur(crvlist_p, ts_style)
1312
1313 struct CRVLIST *crvlist_p;
1314 int ts_style;
1315
1316 {
1317         if (crvlist_p == (struct CRVLIST *) 0
1318                                 || crvlist_p->next == (struct CRVLIST *) 0) {
1319                 pfatal("invalid tabslur crvlist");
1320         }
1321
1322         do_linetype(ts_style);
1323         draw_line(crvlist_p->x, crvlist_p->y, crvlist_p->next->x, crvlist_p->next->y);
1324 }
1325 \f
1326
1327 /* print a small curve to indicate a 1/4 step bend on a tabnote */
1328
1329 void
1330 pr_sm_bend(x, y)
1331
1332 double x, y;    /* where to start the curve. This is the bottom left end */
1333
1334 {
1335         float xlist[4], ylist[4];       /* coordinates of the curve */
1336
1337
1338         /* fill in the relative horizontal and vertical offsets. These
1339          * are hand picked to give a nice looking curve */
1340         xlist[0] = x;
1341         ylist[0] = y;
1342         xlist[1] = x + 0.5 * STEPSIZE;
1343         ylist[1] = y + 0.2 * STEPSIZE;
1344         xlist[2] = x + 1.2 * STEPSIZE;
1345         ylist[2] = y + 1.2 * STEPSIZE;
1346         xlist[3] = x + 1.3 * STEPSIZE;
1347         ylist[3] = y + 1.75 * STEPSIZE;
1348
1349         /* now print the curve */
1350         pr_allcurve(xlist, ylist, 4, W_NORMAL, NO);
1351 }
1352 \f
1353
1354 /* Print 'atend' grids */
1355
1356 void
1357 pr_atend()
1358
1359 {
1360         float x;                /* of first grid of row */
1361         float y;                /* of row; top line of grid */
1362         float gridx;            /* x of grid being printed */
1363         float north;            /* of the grid */
1364         float space;            /* distance between grid lines */
1365         struct GRID *grid_p;
1366         int g;                  /* index through grid_p array */
1367         int staff = -1;         /* always -1 to indicate atend. Using a
1368                                  * variable rather than hard-coding where
1369                                  * used just on general principles. */
1370         int column;             /* how many columns printed so far in row */
1371         int rows_to_print;      /* how many rows to print per page. */
1372         struct MAINLL *main_feed_p;     /* for getting top/bottom blocks */
1373         struct FEED *feed_p;    /* for getting top/bottom blocks */
1374
1375
1376         x = Atend_info.firstgrid_x;
1377         y = Atend_info.firstgrid_y;
1378         rows_to_print = Atend_info.rows_per_page;
1379         space = gridspace(staff);
1380         column = 0;
1381
1382         /* Find the last FEED. We use that to get top/bottom blocks */
1383         for (main_feed_p = Mainlltc_p; main_feed_p->str != S_FEED;
1384                                         main_feed_p= main_feed_p->prev) {
1385                 ;
1386         }
1387         feed_p = main_feed_p->u.feed_p;
1388         for (g = 0; g < Atend_info.grids_used; g++) {
1389                 grid_p = Atend_info.grid_p[g];
1390                 gridsize(grid_p, staff, &north, (float *) 0, (float *) 0, (float *) 0);
1391                 /* calculate horizontal position of this grid */
1392                 gridx = x + column * Atend_info.horz_sep;
1393
1394                 /* print the name of the grid */
1395                 pr_string(gridx - strwidth(grid_p->name) / 2.0,
1396                                 y + north + strdescent(grid_p->name),
1397                                 grid_p->name, J_LEFT, (char *) 0, -1);
1398
1399                 /* print the grid itself */
1400                 do_grid(gridx - space * (grid_p->numstr - 1) / 2.0,
1401                                         y, space, grid_p, staff);
1402
1403                 if (++column >= Atend_info.grids_per_row &&
1404                                 g < Atend_info.grids_used - 1) {
1405                         /* move to next row */
1406                         column = 0;
1407                         y -= Atend_info.vert_sep;
1408                         rows_to_print--;
1409                         if (Atend_info.separate_page == YES &&
1410                                                 rows_to_print <= 0) {
1411                                 rows_to_print = Atend_info.rows_per_page;
1412                                 y = Atend_info.firstgrid_y;
1413                                 /* print top/bottom blocks, if any */
1414                                 /* use *2 blocks for any subsequent pages */
1415                                 feed_p->top_p = feed_p->top2_p;
1416                                 feed_p->bot_p = feed_p->bot2_p;
1417                                 pr_feed(main_feed_p);
1418                         }
1419                 }
1420         }
1421 }