| 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 | } |