2 /* Copyright (c) 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006 by Arkkra Enterprises */
3 /* All rights reserved */
5 /* functions for printing endings, pedal marks, phrase marks, etc */
11 /* width of tapered curve at endpoint relative to maximum width */
12 #define TAPERWID (0.4)
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.
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 */
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
,
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
));
48 /* whenever we hit a FEED, draw any endings associated with the score */
51 pr_endings(main_feed_p
)
53 struct MAINLL
*main_feed_p
; /* FEED */
56 static char *endlabel
= (char *) 0;/* ending label if had to carry over
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 */
63 debug(512, "pr_endings");
65 first_p
= (struct MAINLL
*) 0;
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
) {
72 /* go just to end of current score */
73 if (curr_p
->str
== S_FEED
) {
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
83 endlabel
= curr_p
->u
.clefsig_p
->bar_p
->endinglabel
;
88 /* for each bar, check its endingloc and act accordingly */
89 else if (curr_p
->str
== S_BAR
) {
91 switch(curr_p
->u
.bar_p
->endingloc
) {
94 if (first_p
== (struct MAINLL
*) 0) {
95 pfatal("ending without beginning");
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;
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
,
125 endlabel
= curr_p
->u
.bar_p
->endinglabel
;
133 pfatal("bad endingloc value");
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
);
152 /* now that we have identified an ending, print it above each
153 * staff that is supposed to get endings */
156 do_endings(first_p
, last_p
, endlabel
, carryout
)
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 */
165 struct MARKCOORD
*markc_p
; /* info about where to draw ending */
168 /* for each staff that is supposed to have endings,
170 if (first_p
->str
== S_CLEFSIG
) {
172 markc_p
= first_p
->u
.clefsig_p
->bar_p
->ending_p
;
176 markc_p
= first_p
->u
.bar_p
->ending_p
;
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
) {
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
);
206 /* draw ending marks over specified staff */
209 draw_ending(staffno
, ry
, first_p
, last_p
, endlabel
, carryout
)
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 */
219 float begin_x
; /* x coord of beginning and end of ending */
220 float y
; /* vertical location of ending */
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
) {
228 begin_x
= first_p
->u
.clefsig_p
->bar_p
->c
[AX
];
232 begin_x
= first_p
->u
.bar_p
->c
[AX
];
236 /* shut up compilers that erroneously thinks begin_x
237 * could get used without being set. */
239 pfatal("bad struct type passed to draw_ending");
244 /* get vertical position */
245 y
= Staffs_y
[staffno
] + ry
;
247 /* now we know where to put it, so draw it */
248 do_linetype(L_NORMAL
);
250 /* print the beginning vertical and label now if appropriate */
251 if (endlabel
!= (char *) 0) {
252 pr_endlabel( (double) begin_x
, (double) y
, endlabel
);
255 pr_end_line ( (double) begin_x
, (double) last_p
->u
.bar_p
->c
[AX
],
256 (double) y
, carryout
);
260 /* print label at the beginning of an ending, along with the vertical line
261 * to the left of the label */
264 pr_endlabel(x
, y
, label
)
266 double x
; /* coordinate of beginning of ending */
268 char *label
; /* the ending label or NULL */
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) {
275 draw_line(x
, y
+ STDPAD
, x
, y
+ ENDINGHEIGHT
- STDPAD
);
276 pr_string(x
+ (3.0 * STDPAD
), y
+ (2.0 * STDPAD
), label
, J_LEFT
,
282 /* print horizontal line above ending, possibly with ending vertical line */
285 pr_end_line(begin_x
, end_x
, y
, carryout
)
287 double begin_x
; /* horizontal coordinates of ending line */
289 double y
; /* vertical position */
290 int carryout
; /* if YES, continued on next score, so no end vertical */
293 /* adjust to allow a little padding */
294 begin_x
+= (2.0 * STDPAD
);
295 end_x
-= (2.0 * STDPAD
);
296 y
+= ENDINGHEIGHT
- STDPAD
;
298 /* draw the horizontal line above the ending */
299 draw_line(begin_x
, y
, end_x
, y
);
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
));
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 */
314 int staffno
; /* which staff to check */
317 register int s
; /* index through barstlist */
320 /* if staff is invisible, ending doesn't count */
321 if ( svpath(staffno
, VISIBLE
)->visible
== NO
) {
325 switch ( Score
.endingstyle
) {
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) );
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
++) {
338 if ((staffno
>= Score
.barstlist
[s
].top
) && (staffno
339 <= Score
.barstlist
[s
].bottom
)) {
341 return( is_top_visible_in_range(staffno
,
342 Score
.barstlist
[s
].top
));
346 /* if wasn't in any of the ranges, then it must be barred
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
++) {
355 if ((staffno
>= Score
.bracelist
[s
].topstaff
)
357 <= Score
.bracelist
[s
].botstaff
)) {
359 return( is_top_visible_in_range(staffno
,
360 Score
.bracelist
[s
].topstaff
));
364 for (s
= 0; s
< Score
.nbrack
; s
++) {
366 if ((staffno
>= Score
.bracklist
[s
].topstaff
)
368 <= Score
.bracklist
[s
].botstaff
)) {
370 return( is_top_visible_in_range(staffno
,
371 Score
.bracklist
[s
].topstaff
));
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) );
382 pfatal("unknown endingstyle");
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 */
394 is_top_visible_in_range(staffno
, top
)
396 int staffno
; /* which staff to check */
397 int top
; /* top of range to check */
400 for (staffno
--; staffno
>= top
; staffno
--) {
401 if ( svpath(staffno
, VISIBLE
)->visible
== YES
) {
402 /* something above it is visible */
410 /* functions for printing piano pedals marks */
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];
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 */
426 static double width
= 0.0;
430 /* first time, make string with just P and get width of that */
431 (void) strncpy(pstr
, Ped_start
, 3);
433 width
= strwidth(pstr
) / 2.0;
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 */
444 pr_ped_char(stuff_p
, staffno
)
446 struct STUFF
*stuff_p
; /* pedal info */
447 int staffno
; /* which staff */
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 */
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
];
468 pedstyle
= svpath(staffno
, PEDSTYLE
)->pedstyle
;
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;
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;
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
]);
490 Last_ped_y
[staffno
] = stuff_p
->c
[AY
];
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
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.
510 ufatal("got begin pedal when already doing pedal, staff %d", staffno
);
512 Last_ped_x
[staffno
] = stuff_p
->c
[AE
] - overlap
;
516 if (Last_ped_x
[staffno
] == 0.0) {
517 pfatal("got pedal without begped, staff %d", staffno
);
519 Last_ped_x
[staffno
] = stuff_p
->c
[AE
] - overlap
;
523 if (Last_ped_x
[staffno
] == 0.0) {
524 pfatal("got endped without begped, staff %d", staffno
);
526 Last_ped_x
[staffno
] = 0.0;
530 pfatal("bad character 0x%x in pedal string", pedchar
);
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."
541 do_linetype(L_NORMAL
);
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
]);
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
]);
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
]);
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,
570 adj_size( (int) Ped_start
[1], Staffscale
,
572 adj_pstop
= copy_string(Ped_stop
+ 2,
574 adj_size( (int) Ped_stop
[1], Staffscale
,
578 adj_pstart
= Ped_start
;
579 adj_pstop
= Ped_stop
;
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
) {
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
);
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
);
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
);
617 pfatal("bad character 0x%x in pedal string", pedchar
);
622 /* If we had to make a temp copy to account for Staffscale,
623 * free the temp copy. */
624 if (Staffscale
!= 1.0) {
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. */
638 pr_ped_bar(mll_p
, bar_p
)
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 */
645 float endadj
; /* adjustment for endings */
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
++) {
652 if (Last_ped_x
[s
] != 0.0) {
654 if (Last_ped_y
[s
] <= 0.0) {
655 pfatal("don't have y coordinate for drawing pedal mark");
659 if (svpath(s
, PEDSTYLE
)->pedstyle
== P_LINE
) {
660 do_linetype(L_NORMAL
);
661 if (Ped_snapshot
[0] == YES
&&
662 bar_p
->endingloc
== STARTITEM
) {
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,
669 if (bar_p
->c
[AX
] - endadj
672 draw_line(Last_ped_x
[s
],
674 bar_p
->c
[AX
] - endadj
,
679 if (bar_p
->c
[AX
] - STDPAD
>
681 draw_line(Last_ped_x
[s
],
689 endadj
= -(bar_p
->c
[AX
]
695 Last_ped_x
[s
] = bar_p
->c
[AX
] + endadj
;
698 saveped(mll_p
, bar_p
);
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. */
708 saveped(mll_p
, bar_p
)
710 struct MAINLL
*mll_p
; /* bar is connected here */
714 register int s
; /* staff index */
717 if (mll_p
== (struct MAINLL
*) 0) {
718 pfatal("null pointer in saveped");
721 if (bar_p
->endingloc
== STARTITEM
) {
723 if (Ped_snapshot
[0] == YES
) {
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
]
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
);
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
;
753 /* mark that we now have a snapshot */
754 Ped_snapshot
[0] = YES
;
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 */
772 else if (mll_p
->str
== S_CLEFSIG
&&
773 mll_p
->u
.clefsig_p
->bar_p
!=
775 mll_p
->u
.clefsig_p
->bar_p
->endingloc
777 /* not really end of endings */
781 Ped_snapshot
[0] = NO
;
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 */
791 pr_phrase(crvlist_p
, linetype
, tapered
, staffno
)
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 */
801 float *xlist
,* ylist
;
803 /* count up number of coordinates */
804 for (n
= 0, c_p
= crvlist_p
; c_p
!= (struct CRVLIST
*) 0;
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
++) {
817 do_linetype(linetype
);
819 pr_allcurve(xlist
, ylist
, n
,
820 svpath(staffno
, STAFFSCALE
)->staffscale
* W_MEDIUM
/ PPI
,
827 * Name: pr_allcurve()
829 * Abstract: Print a curve, either generated (e.g., tie) or user-defined.
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".
843 pr_allcurve(x
, y
, num
, cwid
, tapered
)
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 */
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 */
866 * If the curve is not to be tapered, calculate Bezier curves joining
867 * these points, and stroke the resulting path.
870 /* load coords into structures, and calculate control points */
871 MALLOC(CURVEINFO
, v
, num
);
872 for (n
= 0; n
< num
; n
++) {
878 /* output results in PostScript */
879 do_moveto(v
[0].x
, v
[0].y
);
881 for (n
= 0; n
< num
- 1; n
++) {
882 do_curveto( v
[n
].x1
, v
[n
].y1
, v
[n
].x2
, v
[n
].y2
,
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.
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
);
904 /* find and save len of each segment, and accumulate total len */
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
]) ) );
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.
921 maxplace
= totlen
* (2.0/3.0);
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
) {
935 /* set the offset for this point for achieving tapering */
936 if (fromend
> maxplace
) {
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.
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
);
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.
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
970 } else if (n
== num
- 1) {
971 dx
= y
[num
-1] - y
[num
-2];
972 dy
= x
[num
-2] - x
[num
-1];
974 dx
= y
[n
+1] - y
[n
-1];
975 dy
= x
[n
-1] - x
[n
+1];
978 /* get hypotenuse of something */
979 temp
= off
[n
] / sqrt( (double) (SQUARED(dx
) + SQUARED(dy
)) );
981 /* get x and y offsets; may need to switch signs */
982 xoff
[n
] = fabs(temp
* dx
);
983 yoff
[n
] = fabs(temp
* dy
);
989 /* update cumulative and remaining length if not at end */
997 * Load coords into structures, and calculate control points, for one
998 * boundary of the curve.
1000 for (n
= 0; n
< num
; n
++) {
1001 v
[n
].x
= x
[n
] + xoff
[n
];
1002 v
[n
].y
= y
[n
] + yoff
[n
];
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.
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
);
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.
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
];
1030 * Draw a line across the curve's thickness at this end. Then
1031 * generate the curves that form this side's boundary.
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
);
1039 /* fill to form the full, solid curve */
1052 * Abstract: Calculate info for drawing cubic arcs through a curve's points.
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).
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 */
1081 int n
; /* loop through the points */
1082 float temp
, delx
, dely
; /* temp variables */
1083 float slope
, intercept
; /* for equation of a segment */
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
)) );
1093 ufatal("two curve points are equal");
1096 /* find the angle at each point other than the endpoints */
1097 for (n
= 1; n
< num
- 1; n
++) {
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.
1103 delx
= v
[n
+1].x
- v
[n
-1].x
;
1104 dely
= v
[n
+1].y
- v
[n
-1].y
;
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").
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
);
1114 /* if angle is 180, should be -1, but guard against roundoff */
1116 temp
= -1; /* should have been exactly -1 */
1118 /* if angle is 0, this is not allowed in our curve */
1120 ufatal("curve bends all the way back on itself");
1122 v
[n
].ang
= acos(temp
);
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
)
1135 if (v
[n
+1].x
>= v
[n
].x
)
1140 continue; /* go to next loop iteration */
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.
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
;
1153 if (v
[n
].x
> v
[n
-1].x
) {
1154 if (v
[n
+1].y
< temp
)
1159 if (v
[n
+1].y
< temp
)
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.
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
;
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).
1183 for (n
= 0; n
< num
; n
++)
1184 v
[n
].slopetan
= -v
[n
].bend
* tan( v
[n
].ang
/ 2 + PI
/ 2);
1187 * For each segment, calculate control points to define a Bezier curve
1188 * connecting the endpoints, according to the specifications.
1190 for (n
= 0; n
< num
- 1; n
++)
1195 * Name: findcontrol()
1197 * Abstract: Find Bezier control points for one segment of the curve.
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.
1210 struct CURVEINFO v
[];
1214 float costheta
, sintheta
; /* for rotating axes by theta */
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.
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 */
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
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.
1245 /* find the slope of the tangent lines at the first & second points */
1246 slope0
= v
[n
].slopetan
;
1247 slope3
= -v
[n
+1].slopetan
;
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
;
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
1260 /* ax and bx are always 0 */
1262 /* ay = a * CUBED(x3); this one is not needed */
1263 by
= b
* SQUARED(x3
);
1266 /* get control points 1 and 2 from Bezier coefficients & endpoints */
1270 y2
= y1
+ (by
+ cy
) / 3;
1273 * Rotate and translate the axes back to where they really were. Store
1274 * these real positions of the control points.
1276 costheta
= (v
[n
+1].x
- v
[n
].x
) / v
[n
].len
;
1277 sintheta
= (v
[n
+1].y
- v
[n
].y
) / v
[n
].len
;
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
;
1286 /* draw a V-shaped bend indicator by drawing two line segments */
1291 struct CRVLIST
*crvlist_p
;
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");
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
);
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 */
1311 pr_tabslur(crvlist_p
, ts_style
)
1313 struct CRVLIST
*crvlist_p
;
1317 if (crvlist_p
== (struct CRVLIST
*) 0
1318 || crvlist_p
->next
== (struct CRVLIST
*) 0) {
1319 pfatal("invalid tabslur crvlist");
1322 do_linetype(ts_style
);
1323 draw_line(crvlist_p
->x
, crvlist_p
->y
, crvlist_p
->next
->x
, crvlist_p
->next
->y
);
1327 /* print a small curve to indicate a 1/4 step bend on a tabnote */
1332 double x
, y
; /* where to start the curve. This is the bottom left end */
1335 float xlist
[4], ylist
[4]; /* coordinates of the curve */
1338 /* fill in the relative horizontal and vertical offsets. These
1339 * are hand picked to give a nice looking curve */
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
;
1349 /* now print the curve */
1350 pr_allcurve(xlist
, ylist
, 4, W_NORMAL
, NO
);
1354 /* Print 'atend' grids */
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 */
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
);
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
) {
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
;
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);
1399 /* print the grid itself */
1400 do_grid(gridx
- space
* (grid_p
->numstr
- 1) / 2.0,
1401 y
, space
, grid_p
, staff
);
1403 if (++column
>= Atend_info
.grids_per_row
&&
1404 g
< Atend_info
.grids_used
- 1) {
1405 /* move to next row */
1407 y
-= Atend_info
.vert_sep
;
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
);