Commit | Line | Data |
---|---|---|
69695f33 MW |
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 | } |