Commit | Line | Data |
---|---|---|
69695f33 MW |
1 | |
2 | /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2005 by Arkkra Enterprises */ | |
3 | /* All rights reserved */ | |
4 | ||
5 | /* functions to determine the curves that make up phrase marks, | |
6 | * ties and slurs. */ | |
7 | ||
8 | #include "defines.h" | |
9 | #include "structs.h" | |
10 | #include "globals.h" | |
11 | ||
12 | /* distance to the top/bottom of a V-shape (indicating a bend) relative to | |
13 | * the line connecting the endpoints of the V */ | |
14 | #define V_HEIGHT (2.7 * Stepsize) | |
15 | ||
16 | /* How much bulge to allow in curves. There are three slightly different | |
17 | * approaches that are tried, each succeeding approach allows more bulge | |
18 | * in a more despearate attempt to get a reasonable curve. */ | |
19 | #define MINBULGE (1.2) | |
20 | #define MAXBULGE (2.1) | |
21 | #define MIN2BULGE (1.4) | |
22 | #define MAX2BULGE (4.1) | |
23 | #define MIN3BULGE (1.8) | |
24 | #define MAX3BULGE (5.6) | |
25 | ||
26 | /* We'd normally want curves to begin and end (in the x direction) exactly in | |
27 | * the middle of their note. But if one curve ends and another begins on | |
28 | * the same note, the curve endpoints would collide, which could look bad. | |
29 | * So we always offset the endpoints by a tiny amount (ends end a little | |
30 | * west of center, and beginnings begin a little east) so they don't touch. | |
31 | * This is the amount they are shifted from center. | |
32 | */ | |
33 | #define XOFFSET4CURVE (0.75 * Stdpad) | |
34 | ||
35 | /* Curves must be at least this far away from notes */ | |
36 | #define CLEARANCE (3.0 * Stdpad) | |
37 | ||
38 | /* try_bulge() is called lots of times in a row with mostly the same values, | |
39 | * and it needs lots of values, so it is convenient to put them in a struct, | |
40 | * and just pass a pointer to it */ | |
41 | struct TRYBULGE { | |
42 | struct MAINLL *mll_p; /* STUFF hangs off here */ | |
43 | struct GRPSYL *begin_gs_p; /* group at left end of curve */ | |
44 | struct GRPSYL *end_gs_p; /* group at right end of curve */ | |
45 | int place; /* PL_* */ | |
46 | struct CRVLIST *curvelist_p; /* points to beginning of curve */ | |
47 | struct CRVLIST *endlist_p; /* points to end of curve */ | |
48 | double xlen; /* distance to midpoint */ | |
49 | double ylen; /* how much bulge to add */ | |
50 | double sintheta; /* for rotation from horizontal */ | |
51 | double costheta; | |
52 | double minbulge; /* first bulge factor to try */ | |
53 | double maxbulge; /* last bulge to try before giving up */ | |
54 | int leftcount; /* value is returned here. Count of | |
55 | * how many "stick-outs" are near the | |
56 | * left end of the curve */ | |
57 | int rightcount; /* similar for right end */ | |
58 | }; | |
59 | ||
60 | static int nowhere_slide P((struct STUFF *stuff_p)); | |
61 | static void do_nowhere P((struct STUFF *stuff_p, double x1, double y1, | |
62 | double x2, double y2)); | |
63 | static void curve_points P((struct MAINLL *mll_p, struct STUFF *stuff_p, | |
64 | int is_phrase)); | |
65 | static double inner_adj P((struct GRPSYL *gs_p, struct NOTE *note_p, | |
66 | double y_adj, int place)); | |
67 | static double stick_out P((struct TRYBULGE *info_p)); | |
68 | static double try_bulge P((struct TRYBULGE *info_p)); | |
69 | static double tieslurx P((struct GRPSYL *gs_p, struct NOTE *note_p, int place)); | |
70 | static struct MAINLL *next_staff P((int staff, struct MAINLL *mll_p)); | |
71 | static void redo_steep P((struct CRVLIST *first_p, struct CRVLIST *last_p, | |
72 | int place)); | |
73 | static void final_touches P((struct MAINLL *mll_p, struct GRPSYL *begin_gs_p, | |
74 | struct GRPSYL *end_gs_p, struct CRVLIST *crvlist_p, int place)); | |
75 | static double eff_tupext P((struct GRPSYL * gs_p, struct STAFF *staff_p, int side)); | |
76 | static int bulge_direction P((struct MAINLL *mll_p, struct GRPSYL *gs1_p, | |
77 | int note_index, int curveno)); | |
78 | \f | |
79 | ||
80 | /* figure out what points are needed for a phrase mark */ | |
81 | /* attach a linked list of x,y coordinates that show where to draw the curve. | |
82 | * The curve will be out of the way of any groups within the phrase. */ | |
83 | ||
84 | void | |
85 | phrase_points(mll_p, stuff_p) | |
86 | ||
87 | struct MAINLL *mll_p; /* MAINLL that stuff_p hangs off of */ | |
88 | struct STUFF *stuff_p; /* info about the phrase mark */ | |
89 | ||
90 | { | |
91 | curve_points(mll_p, stuff_p, YES); | |
92 | } | |
93 | \f | |
94 | ||
95 | /* figure out what points are needed for a tie or slur mark */ | |
96 | ||
97 | void | |
98 | tieslur_points(mll_p, stuff_p) | |
99 | ||
100 | struct MAINLL *mll_p; /* MAINLL that stuff_p hangs off of */ | |
101 | struct STUFF *stuff_p; /* info about the phrase mark */ | |
102 | ||
103 | { | |
104 | /* if slide to/from nowhere in particular, do that */ | |
105 | if (nowhere_slide(stuff_p) == YES) { | |
106 | return; | |
107 | } | |
108 | ||
109 | curve_points(mll_p, stuff_p, NO); | |
110 | } | |
111 | \f | |
112 | ||
113 | /* determine the 3 points that define a V_shaped bend indicator on the tabnote | |
114 | * staff associated with a tab staff, and put them in the stuff crvlist */ | |
115 | ||
116 | void | |
117 | bend_points(mll_p, stuff_p) | |
118 | ||
119 | struct MAINLL *mll_p; | |
120 | struct STUFF *stuff_p; | |
121 | ||
122 | { | |
123 | struct CRVLIST *first_point_p, *last_point_p; /* the beginning | |
124 | * and end points of the curve */ | |
125 | struct CRVLIST *mid_point_p; /* middle of the V-shape */ | |
126 | struct CRVLIST *one2discard_p; /* a point to discard */ | |
127 | double midx, midy; /* midpoint between the ends */ | |
128 | double v_height; /* V_HEIGHT, or less than | |
129 | * that for narrow V's */ | |
130 | double xlen; /* to help find v_height */ | |
131 | double slope; /* of perpendicular line from | |
132 | * (midx, midy) to the point | |
133 | * of the V, v_height away. */ | |
134 | ||
135 | ||
136 | /* first figure everything out as if it were a normal slur */ | |
137 | curve_points(mll_p, stuff_p, NO); | |
138 | ||
139 | /* Now make into V-shaped curve. | |
140 | * First throw away the inner points that we had found. | |
141 | * It's a bit unfortunate to do all that work, then throw it | |
142 | * away, but the curve_point() function that finds all the points | |
143 | * also does lots of other good things that we want, so rather than | |
144 | * make it more complicated than it already is by having it know | |
145 | * about bends, we just save the things it did that help us here. | |
146 | */ | |
147 | first_point_p = stuff_p->crvlist_p; | |
148 | for (last_point_p = first_point_p->next; | |
149 | last_point_p->next != (struct CRVLIST *) 0; ) { | |
150 | one2discard_p = last_point_p; | |
151 | last_point_p = last_point_p->next; | |
152 | FREE(one2discard_p); | |
153 | } | |
154 | ||
155 | /* get a midpoint struct and stitch it into the list */ | |
156 | MALLOC(CRVLIST, mid_point_p, 1); | |
157 | first_point_p->next = mid_point_p; | |
158 | last_point_p->prev = mid_point_p; | |
159 | mid_point_p->prev = first_point_p; | |
160 | mid_point_p->next = last_point_p; | |
161 | ||
162 | /* find the midpoint of the line between the endpoints */ | |
163 | midx = (last_point_p->x + first_point_p->x) / 2.0; | |
164 | midy = (last_point_p->y + first_point_p->y) / 2.0; | |
165 | ||
166 | /* get height. Use V_HEIGHT, except adjust for narrow V's */ | |
167 | xlen = fabs(last_point_p->x - first_point_p->x); | |
168 | if (xlen < 2.0 * V_HEIGHT) { | |
169 | v_height = 0.35 * V_HEIGHT; | |
170 | } | |
171 | else if (xlen < 3.5 * V_HEIGHT) { | |
172 | v_height = 0.65 * V_HEIGHT; | |
173 | } | |
174 | else { | |
175 | v_height = V_HEIGHT; | |
176 | } | |
177 | ||
178 | /* if the y's of the endpoints are equal or nearly so, finding the | |
179 | * midpoint of the V is easy: the x is midx, and the y is midy offset | |
180 | * by v_height in the appropriate direction */ | |
181 | if (fabs(last_point_p->y - first_point_p->y) < 0.001) { | |
182 | mid_point_p->x = midx; | |
183 | mid_point_p->y = midy + v_height * | |
184 | (stuff_p->place == PL_ABOVE ? 1.0 : -1.0); | |
185 | return; | |
186 | } | |
187 | ||
188 | /* find the slope of the perpendicular */ | |
189 | slope = (first_point_p->x - last_point_p->x) / | |
190 | (last_point_p->y - first_point_p->y); | |
191 | ||
192 | /* we want the length of the perpendicular line from (midx, midy) | |
193 | * to the point at the top (or bottom) of the V to be v_height. | |
194 | * Using that line as the hypotenuse of a triangle, we know that | |
195 | * we can find the x and y relative to (midx, midy) by using | |
196 | * Pythagorean x^2 + y^2 = v_height^2. Furthermore, we calculated | |
197 | * the slope of the line earlier, and knowing that slope = y/x, | |
198 | * we now solve 2 equations in 2 unknowns: | |
199 | * x^2 + y^2 = v_height^2 | |
200 | * slope = y / x | |
201 | * Rearranging the first equation and substituing (slope * x) for y: | |
202 | * x^2 + (slope * x)^2 = v_height^2 | |
203 | * solve for x: | |
204 | * (1 + slope^2) * x^2 = v_height^2 | |
205 | * x = sqrt( v_height^2 / (1 + slope^2)) | |
206 | * Then having found x, solve the second equation for y. | |
207 | * y = x * slope | |
208 | * Adjust for being relative to (midx, midy) and for bend direction | |
209 | * and slope direction, and we are done. | |
210 | */ | |
211 | mid_point_p->x = (sqrt((v_height * v_height) / (1.0 + (slope * slope)))) | |
212 | * (first_point_p->y > last_point_p->y ? 1.0 : -1.0) | |
213 | * (stuff_p->place == PL_ABOVE ? 1.0 : -1.0); | |
214 | mid_point_p->y = (slope * mid_point_p->x) + midy; | |
215 | mid_point_p->x += midx; | |
216 | } | |
217 | \f | |
218 | ||
219 | /* determine the 2 points that define a line indicating a slide for a | |
220 | * tab or tabnote staff, and put the points in the stuff crvlist */ | |
221 | ||
222 | void | |
223 | tabslur_points(mll_p, stuff_p) | |
224 | ||
225 | struct MAINLL *mll_p; | |
226 | struct STUFF *stuff_p; | |
227 | ||
228 | { | |
229 | struct CRVLIST *curvelist_p; | |
230 | struct GRPSYL *beggrp_p; | |
231 | struct GRPSYL *endgrp_p; | |
232 | struct NOTE *begnote_p; | |
233 | struct NOTE *endnote_p; | |
234 | float slant; /* 0, 1 or -1 to show slant direction */ | |
235 | int acc1, acc2; /* effective accidentals on the 2 groups, | |
236 | * -2 to +2 */ | |
237 | int n, st; /* index through notelist and slurtolist */ | |
238 | ||
239 | ||
240 | /* if slide to/from nowhere in particular, do that */ | |
241 | if (nowhere_slide(stuff_p) == YES) { | |
242 | return; | |
243 | } | |
244 | ||
245 | /* find the end note */ | |
246 | if (stuff_p->carryin == YES) { | |
247 | /* on carryin, beggrp_p is really the ending group, | |
248 | * and the previous group is the real beggrp_p */ | |
249 | endgrp_p = stuff_p->beggrp_p; | |
250 | endnote_p = stuff_p->begnote_p; | |
251 | beggrp_p = prevgrpsyl(stuff_p->beggrp_p, &mll_p); | |
252 | ||
253 | /* go through all the notes in the previous group, | |
254 | * to find the one that has a slide to the note being | |
255 | * carried into. If there is more than one, use the first | |
256 | * one we find. */ | |
257 | for (n = 0; n < beggrp_p->nnotes; n++) { | |
258 | for (st = 0; st < beggrp_p->notelist[n].nslurto; st++) { | |
259 | if (endnote_p->letter == | |
260 | beggrp_p->notelist[n].slurtolist[st].letter | |
261 | && (is_tab_staff(endgrp_p->staffno) == YES | |
262 | || endnote_p->octave == | |
263 | beggrp_p->notelist[n].slurtolist[st].octave)) { | |
264 | /* found the one sliding to us */ | |
265 | break; | |
266 | } | |
267 | } | |
268 | if (st < beggrp_p->notelist[n].nslurto) { | |
269 | /* found it, so need to jump out */ | |
270 | break; | |
271 | } | |
272 | } | |
273 | if (n == beggrp_p->nnotes) { | |
274 | pfatal("can't find note being slid from"); | |
275 | } | |
276 | begnote_p = &(beggrp_p->notelist[n]); | |
277 | } | |
278 | else { | |
279 | beggrp_p = stuff_p->beggrp_p; | |
280 | begnote_p = stuff_p->begnote_p; | |
281 | if ((endgrp_p = nextgrpsyl(stuff_p->beggrp_p, &mll_p)) | |
282 | == (struct GRPSYL *) 0) { | |
283 | pfatal("failed to find next group in tabslur_points"); | |
284 | } | |
285 | endnote_p = find_matching_note (endgrp_p, | |
286 | stuff_p->begnote_p->slurtolist | |
287 | [stuff_p->curveno].letter, | |
288 | stuff_p->begnote_p->slurtolist | |
289 | [stuff_p->curveno].octave, "slide"); | |
290 | ||
291 | if (endnote_p == (struct NOTE *) 0) { | |
292 | pfatal("failed to find endnote in tabslur_points"); | |
293 | } | |
294 | } | |
295 | ||
296 | if (is_tab_staff(mll_p->u.staff_p->staffno) == YES) { | |
297 | /* figure out whether to slant up or down based on whether | |
298 | * first or second fret is higher */ | |
299 | if (begnote_p->FRETNO > endnote_p->FRETNO) { | |
300 | slant = 1; | |
301 | } | |
302 | else { | |
303 | slant = -1; | |
304 | } | |
305 | } | |
306 | else { | |
307 | /* on non-tab staff, usually the line goes to the midpoint of | |
308 | * the note head, so no need to adjust, so set slant to 0 */ | |
309 | slant = 0; | |
310 | ||
311 | /* there are two exceptions: first, if both notes have the same | |
312 | * letter/octave, but different accidentals, then we have to | |
313 | * determine the slant based on the accidental. */ | |
314 | if (begnote_p->letter == endnote_p->letter | |
315 | && begnote_p->octave == endnote_p->octave) { | |
316 | ||
317 | /* if the accidental on the begin note is higher than | |
318 | * the accidental on the end note, then it slants | |
319 | * down from left to right, and vice versa. Get the | |
320 | * effective accidental on each group, | |
321 | * accounting for key signature, accidentals earlier | |
322 | * in the measure, etc. */ | |
323 | acc1 = eff_acc(beggrp_p, begnote_p, mll_p); | |
324 | acc2 = eff_acc(endgrp_p, endnote_p, mll_p); | |
325 | ||
326 | /* error if the slide is between identical notes */ | |
327 | if (acc1 == acc2) { | |
328 | l_ufatal(endgrp_p->inputfile, | |
329 | endgrp_p->inputlineno, | |
330 | "can't slide to the same note"); | |
331 | } | |
332 | else if (acc1 > acc2) { | |
333 | slant = 1; | |
334 | } | |
335 | else { | |
336 | slant = -1; | |
337 | } | |
338 | } | |
339 | ||
340 | /* second exception: if the slide is carried in, then it needs | |
341 | * to be slanted, so figure out which way */ | |
342 | if (stuff_p->carryin == YES) { | |
343 | #ifdef __STDC__ | |
344 | switch(notecomp( (const void *) begnote_p, | |
345 | (const void *) endnote_p)) { | |
346 | #else | |
347 | switch(notecomp( (char *) begnote_p, (char *) endnote_p)) { | |
348 | #endif | |
349 | case 1: | |
350 | slant = 0.5; | |
351 | break; | |
352 | case -1: | |
353 | slant = -0.5; | |
354 | break; | |
355 | default: | |
356 | /* same note, so have to use accidental as | |
357 | * the deciding factor */ | |
358 | acc1 = eff_acc(beggrp_p, begnote_p, mll_p); | |
359 | acc2 = eff_acc(endgrp_p, endnote_p, mll_p); | |
360 | ||
361 | /* error if the slide is | |
362 | * between identical notes */ | |
363 | if (acc1 == acc2) { | |
364 | l_ufatal(endgrp_p->inputfile, | |
365 | endgrp_p->inputlineno, | |
366 | "can't slide to the same note"); | |
367 | } | |
368 | else if (acc1 > acc2) { | |
369 | slant = 0.5; | |
370 | } | |
371 | else { | |
372 | slant = -0.5; | |
373 | } | |
374 | break; | |
375 | } | |
376 | } | |
377 | } | |
378 | ||
379 | /* find beginning point of line */ | |
380 | MALLOC(CRVLIST, curvelist_p, 1); | |
381 | curvelist_p->prev = (struct CRVLIST *) 0; | |
382 | if (stuff_p->carryin == YES) { | |
383 | /* start a bit west of the end note */ | |
384 | curvelist_p->x = stuff_p->beggrp_p->c[AX] + | |
385 | notehorz(stuff_p->beggrp_p, stuff_p->begnote_p, RW) | |
386 | - 3.0 * Stepsize; | |
387 | curvelist_p->y = endnote_p->c[RY] + (slant * Stepsize); | |
388 | } | |
389 | else { | |
390 | /* start just beyond east of begin note */ | |
391 | curvelist_p->x = begnote_p->c[AE] + Stdpad; | |
392 | curvelist_p->y = stuff_p->begnote_p->c[RY] + (slant * Stepsize); | |
393 | } | |
394 | ||
395 | /* end point of line */ | |
396 | MALLOC(CRVLIST, curvelist_p->next, 1); | |
397 | curvelist_p->next->prev = curvelist_p; | |
398 | curvelist_p->next->next = (struct CRVLIST *) 0; | |
399 | if (stuff_p->carryout == YES) { | |
400 | /* extend to near end of score */ | |
401 | curvelist_p->next->x = PGWIDTH - eff_rightmargin(mll_p) - Stepsize; | |
402 | } | |
403 | else { | |
404 | /* go to just before west of end note */ | |
405 | curvelist_p->next->x = endgrp_p->c[AX] + | |
406 | notehorz(endgrp_p, endnote_p, RW) - Stdpad; | |
407 | } | |
408 | curvelist_p->next->y = endnote_p->c[RY] - (slant * Stepsize); | |
409 | ||
410 | /* attach to stuff */ | |
411 | stuff_p->crvlist_p = curvelist_p; | |
412 | ||
413 | /* place doesn't really make sense, so set arbitrarily */ | |
414 | stuff_p->place = PL_ABOVE; | |
415 | } | |
416 | \f | |
417 | ||
418 | /* if the slide for given tabslur stuff is to/from nowhere in particular, | |
419 | * then handle that here and return YES. Otherwise return NO. */ | |
420 | ||
421 | static int | |
422 | nowhere_slide(stuff_p) | |
423 | ||
424 | struct STUFF *stuff_p; | |
425 | ||
426 | { | |
427 | double boundary; /* east or west boundary of note, with | |
428 | * the slide included */ | |
429 | double adjust = 0.0; /* to move the slanted line slightly when | |
430 | * there is a note on the other side of | |
431 | * the stem that is in the way. */ | |
432 | struct GRPSYL *gs_p; | |
433 | struct NOTE *note_p; | |
434 | int n; | |
435 | float slidexlen; /* SLIDEXLEN * Staffscale */ | |
436 | ||
437 | ||
438 | if (stuff_p->curveno < 0) { | |
439 | return(NO); | |
440 | } | |
441 | ||
442 | if (stuff_p->begnote_p->nslurto == 0) { | |
443 | return(NO); | |
444 | } | |
445 | ||
446 | /* find which note it is in the chord, so check later for possible | |
447 | * collisions between the slide and a neighboring note */ | |
448 | gs_p = stuff_p->beggrp_p; | |
449 | note_p = stuff_p->begnote_p; | |
450 | for (n = 0; n < gs_p->nnotes; n++) { | |
451 | if ( &(gs_p->notelist[n]) == note_p) { | |
452 | break; | |
453 | } | |
454 | } | |
455 | if (n == gs_p->nnotes) { | |
456 | pfatal("couldn't find note in chord for slide"); | |
457 | } | |
458 | ||
459 | slidexlen = SLIDEXLEN * Staffscale; | |
460 | ||
461 | /* for each type, find the outer boundary of the note with the | |
462 | * nowhere slide included and draw a line from there towards the | |
463 | * note, slanted the appropriate direction */ | |
464 | switch (stuff_p->begnote_p->slurtolist[stuff_p->curveno].octave) { | |
465 | ||
466 | case IN_UPWARD: | |
467 | boundary = stuff_p->beggrp_p->c[AX] + | |
468 | notehorz(stuff_p->beggrp_p, stuff_p->begnote_p, RW); | |
469 | /* If there is a note one stepsize below this note, and it's | |
470 | * to the left of the stem while the target note is on the | |
471 | * right side, move the slide up a | |
472 | * tiny bit so it doesn't get swallowed up in that other note | |
473 | * and/or a slide coming into it. | |
474 | * If we're sliding into the middle of a cluster with | |
475 | * wrong-side notes both above and below the target note, it | |
476 | * will still get somewhat swallowed, but that's unlikely to | |
477 | * happen very often, and if it does, this is still about the | |
478 | * best we can manage in that case. */ | |
479 | n++; | |
480 | if (n < gs_p->nnotes && gs_p->notelist[n].stepsup | |
481 | == note_p->stepsup - 1 && | |
482 | gs_p->notelist[n].c[AX] < note_p->c[AX]) { | |
483 | adjust = Stdpad; | |
484 | } | |
485 | do_nowhere(stuff_p, | |
486 | boundary, stuff_p->begnote_p->c[RY] - Stepsize + adjust, | |
487 | boundary + slidexlen, stuff_p->begnote_p->c[RY] + adjust); | |
488 | return(YES); | |
489 | ||
490 | case IN_DOWNWARD: | |
491 | boundary = stuff_p->beggrp_p->c[AX] + | |
492 | notehorz(stuff_p->beggrp_p, stuff_p->begnote_p, RW); | |
493 | /* if there is a note just above that we might | |
494 | * collide with, adjust to dodge it. */ | |
495 | n--; | |
496 | if (n >= 0 && gs_p->notelist[n].stepsup | |
497 | == note_p->stepsup + 1 && | |
498 | gs_p->notelist[n].c[AX] < note_p->c[AX]) { | |
499 | adjust = Stdpad; | |
500 | } | |
501 | do_nowhere(stuff_p, | |
502 | boundary, stuff_p->begnote_p->c[RY] + Stepsize - adjust, | |
503 | boundary + slidexlen, stuff_p->begnote_p->c[RY] - adjust); | |
504 | return(YES); | |
505 | ||
506 | case OUT_UPWARD: | |
507 | boundary = stuff_p->beggrp_p->c[AX] + | |
508 | notehorz(stuff_p->beggrp_p, stuff_p->begnote_p, RE); | |
509 | /* If note just above this one that we might collide with, | |
510 | * dodge it */ | |
511 | n--; | |
512 | if (n >= 0 && gs_p->notelist[n].stepsup | |
513 | == note_p->stepsup + 1 && | |
514 | gs_p->notelist[n].c[AX] > note_p->c[AX]) { | |
515 | adjust = Stdpad; | |
516 | } | |
517 | do_nowhere(stuff_p, | |
518 | boundary - slidexlen, stuff_p->begnote_p->c[RY] - adjust, | |
519 | boundary, stuff_p->begnote_p->c[RY] + Stepsize - adjust); | |
520 | return(YES); | |
521 | ||
522 | case OUT_DOWNWARD: | |
523 | boundary = stuff_p->beggrp_p->c[AX] + | |
524 | notehorz(stuff_p->beggrp_p, stuff_p->begnote_p, RE); | |
525 | /* If note below we might collide with, dodge it */ | |
526 | n++; | |
527 | if (n < gs_p->nnotes && gs_p->notelist[n].stepsup | |
528 | == note_p->stepsup - 1 && | |
529 | gs_p->notelist[n].c[AX] > note_p->c[AX]) { | |
530 | adjust = Stdpad; | |
531 | } | |
532 | do_nowhere(stuff_p, | |
533 | boundary - slidexlen, stuff_p->begnote_p->c[RY] + adjust, | |
534 | boundary, stuff_p->begnote_p->c[RY] - Stepsize + adjust); | |
535 | return(YES); | |
536 | ||
537 | default: | |
538 | return(NO); | |
539 | } | |
540 | } | |
541 | \f | |
542 | ||
543 | /* make a CRVLIST with the 2 given points and put it in the given stuff */ | |
544 | ||
545 | static void | |
546 | do_nowhere(stuff_p, x1, y1, x2, y2) | |
547 | ||
548 | struct STUFF *stuff_p; | |
549 | double x1, y1, x2, y2; | |
550 | ||
551 | { | |
552 | MALLOC(CRVLIST, stuff_p->crvlist_p, 1); | |
553 | stuff_p->crvlist_p->x = x1; | |
554 | stuff_p->crvlist_p->y = y1; | |
555 | MALLOC(CRVLIST, stuff_p->crvlist_p->next, 1); | |
556 | stuff_p->crvlist_p->next->x = x2; | |
557 | stuff_p->crvlist_p->next->y = y2; | |
558 | ||
559 | stuff_p->crvlist_p->prev = stuff_p->crvlist_p->next->next | |
560 | = (struct CRVLIST *) 0; | |
561 | stuff_p->crvlist_p->next->prev = stuff_p->crvlist_p; | |
562 | ||
563 | /* place is not really relevant, but put something in it */ | |
564 | stuff_p->place = PL_ABOVE; | |
565 | } | |
566 | \f | |
567 | ||
568 | /* Figure out what points are needed for a curve, either a phrase mark | |
569 | * or a tie/slur, or a bend. | |
570 | * First it figures out where the endpoints should be, | |
571 | * then finds a curve that will be beyond all the groups that it covers. | |
572 | */ | |
573 | ||
574 | static void | |
575 | curve_points(mll_p, stuff_p, is_phrase) | |
576 | ||
577 | struct MAINLL *mll_p; /* MAINLL that stuff_p hangs off of */ | |
578 | struct STUFF *stuff_p; /* info about the phrase mark or tie/slur */ | |
579 | int is_phrase; /* YES if phrase, NO if tie or slur */ | |
580 | ||
581 | { | |
582 | struct GRPSYL *begin_gs_p; /* curve starts on this group */ | |
583 | struct GRPSYL *end_gs_p; /* curve ends on this group */ | |
584 | struct NOTE *begnote_p; /* first note for tie/slur */ | |
585 | struct NOTE *endnote_p = 0; /* last note of tie/slur */ | |
586 | int place; /* bend PL_ABOVE or PL_BELOW */ | |
587 | int side; /* RN or RS */ | |
588 | int side_adj; /* AN or AS. This field is used to | |
589 | * adjust for nested phrase marks */ | |
590 | double bulgeval; /* bulge factor of curve */ | |
591 | int try; /* count of tries to get a good curve */ | |
592 | int found_good; /* YES if found a good-looking curve */ | |
593 | struct TRYBULGE tb; /* Info for try_bulge() */ | |
594 | struct TRYBULGE *try_p; /* = &tb */ | |
595 | float sign; /* based on if curve is up or down */ | |
596 | struct CRVLIST *curvelist_p; /* beginning of curve */ | |
597 | struct CRVLIST *endlist_p; /* last point of curve */ | |
598 | struct CRVLIST *new_p; /* point to add to list of points */ | |
599 | struct MAINLL *bar_mll_p = 0; /* to find bar or pseudo bar */ | |
600 | float length; /* length of curve */ | |
601 | float ylen; /* length of segment in y direction before | |
602 | * rotation */ | |
603 | float y_adj = 0.0, y2_adj = 0.0;/* if moved because was an end note */ | |
604 | char *name; /* "phrase" or "tie/slur" */ | |
605 | float sintheta, costheta;/* for rotating */ | |
606 | ||
607 | ||
608 | debug(32, "curve_points lineno %d", stuff_p->inputlineno ); | |
609 | ||
610 | /* get short names to groups and notes we'll use a lot */ | |
611 | begin_gs_p = stuff_p->beggrp_p; | |
612 | end_gs_p = stuff_p->endgrp_p; | |
613 | begnote_p = stuff_p->begnote_p; | |
614 | ||
615 | /* figure out what string ("phrase" or "tie/slur") to use for error | |
616 | * messages and make sure begin group is not null */ | |
617 | if (is_phrase == YES) { | |
618 | name = "phrase"; | |
619 | if ( (begin_gs_p == (struct GRPSYL *) 0) | |
620 | || (end_gs_p == (struct GRPSYL *) 0) ) { | |
621 | pfatal("no group associated with phrase"); | |
622 | } | |
623 | } | |
624 | else { | |
625 | int indx; | |
626 | ||
627 | name = "tie/slur"; | |
628 | if (begin_gs_p == (struct GRPSYL *) 0) { | |
629 | pfatal("no group associated with tie/slur"); | |
630 | } | |
631 | /* figure out which direction to bend the tie/slur */ | |
632 | if (stuff_p->carryin == YES) { | |
633 | struct MAINLL *m_p; | |
634 | struct GRPSYL *g_p; | |
635 | struct STUFF *st_p; | |
636 | ||
637 | /* Need to base bend direction on the | |
638 | * group/note/curve that was the carryout, | |
639 | * otherwise the carryin and carryout could have | |
640 | * different bend directions. | |
641 | * We also need the costuff_p to get | |
642 | * any user override of bend direction. | |
643 | * | |
644 | * Find the MAINLL pointing to the STAFF that | |
645 | * should contain the costuff. Use prevgrpsyl, | |
646 | * since it knows how to deal with endings, | |
647 | * but we're really interested in the MAINLL | |
648 | * pointing to the GRPSYL | |
649 | * rather than the GRPSYL itself. | |
650 | */ | |
651 | ||
652 | /* First make sure we have the first group | |
653 | * in the measure. */ | |
654 | for (g_p = begin_gs_p; g_p->prev != 0; g_p = g_p->prev) { | |
655 | ; | |
656 | } | |
657 | ||
658 | /* Now find the MAINLL pointing to the prev meas */ | |
659 | m_p = mll_p; | |
660 | (void) prevgrpsyl(g_p, &m_p); | |
661 | if (m_p == 0 || m_p->str != S_STAFF) { | |
662 | pfatal("failed to find costaff_p's mainll"); | |
663 | } | |
664 | ||
665 | /* Locate the costuff. We could just use | |
666 | * stuff_p->costuff_p, but by searching for it here, | |
667 | * we double check that we really found the right | |
668 | * MAINLL, and can pfatal if not. */ | |
669 | for (st_p = m_p->u.staff_p->stuff_p; st_p != 0; | |
670 | st_p = st_p->next) { | |
671 | if (st_p == stuff_p->costuff_p) { | |
672 | break; | |
673 | } | |
674 | } | |
675 | if (st_p == 0) { | |
676 | pfatal("failed to find costaff_p from mainll"); | |
677 | } | |
678 | ||
679 | indx = st_p->begnote_p - &(st_p->beggrp_p->notelist[0]); | |
680 | stuff_p->place = (bulge_direction(m_p, st_p->beggrp_p, | |
681 | indx, st_p->curveno) == UP | |
682 | ? PL_ABOVE : PL_BELOW); | |
683 | } | |
684 | else { | |
685 | indx = begnote_p - &(begin_gs_p->notelist[0]); | |
686 | stuff_p->place = (bulge_direction(mll_p, begin_gs_p, | |
687 | indx, stuff_p->curveno) == UP | |
688 | ? PL_ABOVE : PL_BELOW); | |
689 | } | |
690 | } | |
691 | ||
692 | place = stuff_p->place; | |
693 | ||
694 | /* determine whether to use north or south of groups, and what sign to | |
695 | * use to get the bends in the correct direction */ | |
696 | if (place == PL_ABOVE) { | |
697 | side = RN; | |
698 | side_adj = AN; | |
699 | sign = 1.0; | |
700 | } | |
701 | else { | |
702 | side = RS; | |
703 | side_adj = AS; | |
704 | sign = -1.0; | |
705 | } | |
706 | ||
707 | /* set up the beginning coord */ | |
708 | MALLOC(CRVLIST, curvelist_p, 1); | |
709 | curvelist_p->prev = (struct CRVLIST *) 0; | |
710 | if (is_phrase == YES) { | |
711 | /* Start slightly to east of center, so that if another | |
712 | * curves ends on this group, they won't quite touch */ | |
713 | curvelist_p->x = begin_gs_p->c[AX] + XOFFSET4CURVE; | |
714 | if (begin_gs_p->grpcont != GC_SPACE) { | |
715 | curvelist_p->y = begin_gs_p->c[side] | |
716 | + eff_tupext(begin_gs_p, mll_p->u.staff_p, place) | |
717 | + (sign * 2.0 * Stdpad); | |
718 | /* If there is something in [side_adj] there | |
719 | * was another phrase on this group. But if that phrase | |
720 | * ended on this group, it can be ignored. */ | |
721 | if (begin_gs_p->c[side_adj] != 0.0 && | |
722 | (begin_gs_p->phraseside & EAST_SIDE)) { | |
723 | curvelist_p->y += begin_gs_p->c[side_adj]; | |
724 | } | |
725 | } | |
726 | else { | |
727 | /* bizarre case. first group is a space. | |
728 | * Use 1 step from top or bottom of staff for y coord */ | |
729 | curvelist_p->y = sign * (1 + halfstaffhi(begin_gs_p->staffno)); | |
730 | } | |
731 | } | |
732 | ||
733 | else { /* is tie or slur */ | |
734 | ||
735 | curvelist_p->y = begnote_p->c[RY]; | |
736 | y_adj = 0.0; | |
737 | ||
738 | /* if on the "end" note of a group, | |
739 | * the curve can probably be moved | |
740 | * to the x of the note instead of the edge of the group. | |
741 | * We assume it can if the curve bends away | |
742 | * from the stem and there are no "with" | |
743 | * list items on the group. If there is a with list, move | |
744 | * a little bit, but not enough to hit with items */ | |
745 | if (begin_gs_p->stemdir == UP && place == PL_BELOW | |
746 | && begnote_p == &(begin_gs_p->notelist | |
747 | [begin_gs_p->nnotes - 1])) { | |
748 | if (begin_gs_p->nwith == 0 || begin_gs_p->normwith == NO) { | |
749 | curvelist_p->x = begnote_p->c[AX] | |
750 | + (2.0 * Stdpad); | |
751 | y_adj = (Stepsize * (begnote_p->notesize | |
752 | == GS_NORMAL ? 1.7 : 1.2)); | |
753 | curvelist_p->y -= y_adj; | |
754 | } | |
755 | else { | |
756 | curvelist_p->x = begnote_p->c[AE]; | |
757 | y_adj = (Stepsize * (begnote_p->notesize | |
758 | == GS_NORMAL ? 1.2 : 0.9)); | |
759 | curvelist_p->y -= y_adj; | |
760 | } | |
761 | } | |
762 | else if (begin_gs_p->stemdir == DOWN && place == PL_ABOVE | |
763 | && begnote_p == &(begin_gs_p->notelist[0])) { | |
764 | if (begin_gs_p->nwith == 0 | |
765 | || begin_gs_p->normwith == NO) { | |
766 | curvelist_p->x = begnote_p->c[AX] | |
767 | + (2.0 * Stdpad); | |
768 | y_adj = (Stepsize * (begnote_p->notesize | |
769 | == GS_NORMAL ? 1.7 : 1.2)); | |
770 | curvelist_p->y += y_adj; | |
771 | } | |
772 | else { | |
773 | curvelist_p->x = begnote_p->c[AE]; | |
774 | y_adj = (Stepsize * (begnote_p->notesize | |
775 | == GS_NORMAL ? 1.2 : 0.9)); | |
776 | curvelist_p->y += y_adj; | |
777 | } | |
778 | } | |
779 | ||
780 | /* whole notes and longer don't really have a stem, so top | |
781 | * note of "stem up" can be moved. Stemless grace notes also | |
782 | * don't have a stem, so the same logic applies. */ | |
783 | else if ( (begin_gs_p->basictime < 2 | |
784 | || (begin_gs_p->grpvalue == GV_ZERO | |
785 | && begin_gs_p->basictime < 8)) | |
786 | && begin_gs_p->stemdir == UP | |
787 | && place == PL_ABOVE && | |
788 | begnote_p == &(begin_gs_p->notelist[0])) { | |
789 | if (begin_gs_p->nwith == 0 | |
790 | || begin_gs_p->normwith == YES) { | |
791 | curvelist_p->x = begnote_p->c[AX] + Stdpad; | |
792 | y_adj = (Stepsize * (begnote_p->notesize | |
793 | == GS_NORMAL ? 1.7 : 1.2)); | |
794 | curvelist_p->y += y_adj; | |
795 | } | |
796 | else { | |
797 | curvelist_p->x = begnote_p->c[AE]; | |
798 | y_adj = (Stepsize * (begnote_p->notesize | |
799 | == GS_NORMAL ? 1.2 : 0.9)); | |
800 | curvelist_p->y += y_adj; | |
801 | } | |
802 | } | |
803 | ||
804 | /* can also be moved if bottom note of stem-down group */ | |
805 | else if (begin_gs_p->stemdir == DOWN && place == PL_BELOW | |
806 | && begnote_p == &(begin_gs_p->notelist | |
807 | [begin_gs_p->nnotes - 1]) && | |
808 | stuff_p->carryin == NO) { | |
809 | if (begin_gs_p->basictime < 2 && begin_gs_p->nwith > 0 | |
810 | && begin_gs_p->normwith == NO) { | |
811 | curvelist_p->x = begin_gs_p->c[AE]; | |
812 | y_adj = (Stepsize * (begnote_p->notesize | |
813 | == GS_NORMAL ? 1.2 : 0.9)); | |
814 | curvelist_p->y -= y_adj; | |
815 | } | |
816 | else { | |
817 | curvelist_p->x = begin_gs_p->c[AX]; | |
818 | y_adj = (Stepsize * (begnote_p->notesize | |
819 | == GS_NORMAL ? 1.7 : 1.2)); | |
820 | curvelist_p->y -= y_adj; | |
821 | } | |
822 | } | |
823 | else { | |
824 | curvelist_p->x = begin_gs_p->c[AX] + | |
825 | notehorz(begin_gs_p, begnote_p, RE) + | |
826 | Stdpad; | |
827 | } | |
828 | ||
829 | /* If two notes are a stepsize apart and the curve from the | |
830 | * west note is bending towards the east note, | |
831 | * then the x should be moved east a little. | |
832 | * First case: this isn't the top note, but the note just | |
833 | * above is 1 stepsize away and on the east side, and the | |
834 | * curve is going up and it's not a carryin. */ | |
835 | if (begnote_p != &(begin_gs_p->notelist[0]) && | |
836 | begnote_p->stepsup == | |
837 | begnote_p[-1].stepsup - 1 && | |
838 | begnote_p->c[RX] < begnote_p[-1].c[RX] && | |
839 | place == PL_ABOVE && | |
840 | stuff_p->carryin == NO) { | |
841 | curvelist_p->x += 1.5 * Stepsize; | |
842 | } | |
843 | /* Second case: not bottom note, note just | |
844 | * below is one step away and on the east side, the curve | |
845 | * is going down, and it's not a carryin. */ | |
846 | else if (begnote_p != &(begin_gs_p->notelist[begin_gs_p->nnotes-1]) && | |
847 | begnote_p->stepsup == | |
848 | begnote_p[1].stepsup + 1 && | |
849 | begnote_p->c[RX] < begnote_p[1].c[RX] && | |
850 | place == PL_BELOW && | |
851 | stuff_p->carryin == NO) { | |
852 | curvelist_p->x += 1.5 * Stepsize; | |
853 | } | |
854 | ||
855 | } | |
856 | ||
857 | /* if carried over from previous score, start a bit farther left */ | |
858 | if (stuff_p->carryin == YES) { | |
859 | ||
860 | /* find the pseudo bar and set x to that */ | |
861 | for (bar_mll_p = mll_p->prev; | |
862 | bar_mll_p != (struct MAINLL *) 0; | |
863 | bar_mll_p = bar_mll_p->prev) { | |
864 | if (bar_mll_p->str == S_CLEFSIG) { | |
865 | if (bar_mll_p->u.clefsig_p->bar_p | |
866 | == (struct BAR *) 0) { | |
867 | /* carryin to an ending */ | |
868 | continue; | |
869 | } | |
870 | curvelist_p->x = | |
871 | bar_mll_p->u.clefsig_p->bar_p->c[AE] | |
872 | - (TIESLURPAD * Staffscale); | |
873 | ||
874 | /* Long notes (wholes, etc) generally get | |
875 | * more space on their left than short notes, | |
876 | * so a curve carried in to a long note | |
877 | * may look overly long, especially if other | |
878 | * scores on the same page have carryins | |
879 | * to short notes. So limit carryin curve | |
880 | * length to 5 stepsizes. | |
881 | */ | |
882 | if (begin_gs_p->c[AW] - curvelist_p->x > 5.0 * Stepsize) { | |
883 | curvelist_p->x = begin_gs_p->c[AW] | |
884 | - 5.0 * Stepsize; | |
885 | } | |
886 | break; | |
887 | } | |
888 | else if (bar_mll_p->str == S_BAR) { | |
889 | /* carryin to an ending */ | |
890 | curvelist_p->x = begin_gs_p->c[AW]; | |
891 | break; | |
892 | } | |
893 | } | |
894 | ||
895 | if (bar_mll_p == (struct MAINLL *) 0) { | |
896 | pfatal("missing CELFSIG when carrying over %s mark", | |
897 | name); | |
898 | } | |
899 | } | |
900 | ||
901 | /* set up ending coord */ | |
902 | MALLOC(CRVLIST, endlist_p, 1); | |
903 | if (is_phrase == YES) { | |
904 | /* End slightly to west of group center, so that another | |
905 | * curve can start on this group (if needed) with | |
906 | * touching this curve. */ | |
907 | endlist_p->x = end_gs_p->c[AX] - XOFFSET4CURVE; | |
908 | if (end_gs_p->grpcont != GC_SPACE) { | |
909 | endlist_p->y = end_gs_p->c[side] | |
910 | + eff_tupext(end_gs_p, mll_p->u.staff_p, place) | |
911 | + (sign * 2.0 * Stdpad); | |
912 | /* Add in space for any relevant nested phrases */ | |
913 | if (end_gs_p->c[side_adj] != 0.0 && | |
914 | (end_gs_p->phraseside & WEST_SIDE)) { | |
915 | endlist_p->y += end_gs_p->c[side_adj]; | |
916 | } | |
917 | } | |
918 | else { | |
919 | /* bizarre case. last group is a space. Use 1 stepsize | |
920 | * from top or bottom of staff for y coord */ | |
921 | endlist_p->y = sign * (1 + halfstaffhi(begin_gs_p->staffno)); | |
922 | } | |
923 | } | |
924 | else { | |
925 | if (stuff_p->carryin == YES) { | |
926 | /* in case of carryin, the "begin" group is actually | |
927 | * the ending group, so set the end group, and | |
928 | * adjust the beginning y */ | |
929 | endlist_p->x = begin_gs_p->c[AW]; | |
930 | ||
931 | /* adjust things carried into endings to account for | |
932 | * the padding that was added */ | |
933 | if (bar_mll_p->str == S_BAR) { | |
934 | endlist_p->x += TIESLURPAD * Staffscale; | |
935 | } | |
936 | ||
937 | endlist_p->y = curvelist_p->y; | |
938 | end_gs_p = begin_gs_p; | |
939 | ||
940 | /* if end note, adjust */ | |
941 | if (place == PL_ABOVE && begnote_p | |
942 | == &(begin_gs_p->notelist[0])) { | |
943 | endlist_p->x = begnote_p->c[AX]; | |
944 | if ((begin_gs_p->basictime > 1) && | |
945 | (begin_gs_p->stemdir == UP)) { | |
946 | endlist_p->y += Stepsize; | |
947 | curvelist_p->y += Stepsize; | |
948 | } | |
949 | } | |
950 | else if (place == PL_BELOW && begnote_p == | |
951 | &(begin_gs_p->notelist | |
952 | [begin_gs_p->nnotes - 1]) | |
953 | && (begin_gs_p->stemdir == UP | |
954 | || begin_gs_p->basictime < 2)) { | |
955 | endlist_p->x = begnote_p->c[AX]; | |
956 | if ((begin_gs_p->basictime < 2) && | |
957 | (begin_gs_p->stemdir == DOWN)) { | |
958 | endlist_p->y -= Stepsize; | |
959 | curvelist_p->y -= Stepsize; | |
960 | } | |
961 | } | |
962 | } | |
963 | else { | |
964 | /* not carryin */ | |
965 | end_gs_p = find_next_group (mll_p, begin_gs_p, | |
966 | (stuff_p->curveno == -1 ? "tie" : "slur")); | |
967 | if (stuff_p->curveno == -1) { | |
968 | /* this is a tie */ | |
969 | endnote_p = find_matching_note (end_gs_p, | |
970 | begnote_p->letter, | |
971 | begnote_p->octave, "tie"); | |
972 | } | |
973 | else { | |
974 | if (IS_NOWHERE(begnote_p->slurtolist | |
975 | [stuff_p->curveno].octave)) { | |
976 | pfatal("curve_points called on slide to nowhere"); | |
977 | } | |
978 | ||
979 | endnote_p = find_matching_note (end_gs_p, | |
980 | begnote_p->slurtolist | |
981 | [stuff_p->curveno].letter, | |
982 | begnote_p->slurtolist | |
983 | [stuff_p->curveno].octave, | |
984 | "slur/slide"); | |
985 | } | |
986 | ||
987 | endlist_p->y = endnote_p->c[RY]; | |
988 | ||
989 | y2_adj = 0.0; | |
990 | ||
991 | /* move if below curve and bottom note with stem up */ | |
992 | if (end_gs_p->stemdir == UP && place == PL_BELOW | |
993 | && endnote_p == &(end_gs_p->notelist | |
994 | [end_gs_p->nnotes - 1])) { | |
995 | if (end_gs_p->nwith == 0 | |
996 | || end_gs_p->normwith == NO) { | |
997 | endlist_p->x = endnote_p->c[AX] | |
998 | - (2.0 * Stdpad); | |
999 | y2_adj = (Stepsize * | |
1000 | (endnote_p->notesize | |
1001 | == GS_NORMAL ? 1.7 : 1.2)); | |
1002 | endlist_p->y -= y2_adj; | |
1003 | } | |
1004 | else { | |
1005 | endlist_p->x = endnote_p->c[AW]; | |
1006 | y2_adj = (Stepsize * | |
1007 | (endnote_p->notesize | |
1008 | == GS_NORMAL ? 1.2 : 0.9)); | |
1009 | endlist_p->y -= y2_adj; | |
1010 | } | |
1011 | } | |
1012 | ||
1013 | /* move if above and top note with stem down */ | |
1014 | else if (end_gs_p->stemdir == DOWN && place == PL_ABOVE | |
1015 | && endnote_p == &(end_gs_p->notelist[0])) { | |
1016 | if (end_gs_p->nwith == 0 | |
1017 | || end_gs_p->normwith == NO) { | |
1018 | endlist_p->x = endnote_p->c[AX] | |
1019 | - (2.0 * Stdpad); | |
1020 | y2_adj = (Stepsize * | |
1021 | (endnote_p->notesize | |
1022 | == GS_NORMAL ? 1.7 : 1.2)); | |
1023 | endlist_p->y += y2_adj; | |
1024 | } | |
1025 | else { | |
1026 | endlist_p->x = endnote_p->c[AW]; | |
1027 | y2_adj = (Stepsize * | |
1028 | (endnote_p->notesize | |
1029 | == GS_NORMAL ? 1.2 : 0.9)); | |
1030 | endlist_p->y += y2_adj; | |
1031 | } | |
1032 | } | |
1033 | ||
1034 | /* whole and longer don't have stem, so end note where | |
1035 | * a stem would be (if there were one) can be moved */ | |
1036 | else if (end_gs_p->basictime < 2 && | |
1037 | end_gs_p->stemdir == DOWN | |
1038 | && place == PL_BELOW | |
1039 | && endnote_p == &(end_gs_p->notelist | |
1040 | [end_gs_p->nnotes - 1])) { | |
1041 | if (end_gs_p->nwith == 0 | |
1042 | || end_gs_p->normwith == YES) { | |
1043 | endlist_p->x = endnote_p->c[AX] | |
1044 | - Stdpad; | |
1045 | y2_adj = (Stepsize * | |
1046 | (endnote_p->notesize | |
1047 | == GS_NORMAL ? 1.7 : 1.2)); | |
1048 | endlist_p->y -= y2_adj; | |
1049 | } | |
1050 | else { | |
1051 | endlist_p->x = endnote_p->c[AW]; | |
1052 | y2_adj = (Stepsize * | |
1053 | (endnote_p->notesize | |
1054 | == GS_NORMAL ? 1.2 : 0.9)); | |
1055 | endlist_p->y -= y2_adj; | |
1056 | } | |
1057 | } | |
1058 | ||
1059 | /* move if above and top note of stem up */ | |
1060 | else if (end_gs_p->stemdir == UP && place == PL_ABOVE | |
1061 | && endnote_p == | |
1062 | &(end_gs_p->notelist[0]) ) { | |
1063 | endlist_p->x = end_gs_p->c[AX]; | |
1064 | y2_adj = (Stepsize * (endnote_p->notesize | |
1065 | == GS_NORMAL ? 1.7 : 1.2)); | |
1066 | endlist_p->y += y2_adj; | |
1067 | ||
1068 | /* if tied from note is also the top of its | |
1069 | * group, level the tie/slur */ | |
1070 | if (begin_gs_p->stemdir == UP && | |
1071 | begnote_p == | |
1072 | &(begin_gs_p->notelist[0]) && | |
1073 | begin_gs_p->basictime > 1 ) { | |
1074 | curvelist_p->y += (Stepsize * | |
1075 | (begnote_p->notesize | |
1076 | == GS_NORMAL ? 1.7 : 1.2)); | |
1077 | } | |
1078 | } | |
1079 | else if (begin_gs_p->grpvalue == GV_ZERO) { | |
1080 | /* grace note to main note, can't use the west | |
1081 | * of the end group because that would include | |
1082 | * the grace note. */ | |
1083 | endlist_p->x = endnote_p->c[AX] + | |
1084 | notehorz(end_gs_p, endnote_p, RW); | |
1085 | } | |
1086 | else { | |
1087 | endlist_p->x = tieslurx(end_gs_p, endnote_p, | |
1088 | stuff_p->place) - (2.0 * Stdpad); | |
1089 | } | |
1090 | ||
1091 | /* if note tied from is bottom of group with stem down, | |
1092 | * level the tie/slur */ | |
1093 | if (end_gs_p->stemdir == DOWN && place == PL_BELOW | |
1094 | && endnote_p == &(end_gs_p->notelist | |
1095 | [end_gs_p->nnotes - 1]) && | |
1096 | begin_gs_p->stemdir == DOWN && | |
1097 | begnote_p == &(begin_gs_p->notelist | |
1098 | [begin_gs_p->nnotes - 1]) && | |
1099 | end_gs_p->basictime > 1 ) { | |
1100 | endlist_p->y -= (Stepsize * | |
1101 | (begnote_p->notesize | |
1102 | == GS_NORMAL ? 1.7 : 1.2)); | |
1103 | } | |
1104 | ||
1105 | /* if beginning of curve was adjusted and this is | |
1106 | * an inner note, but there is room on the relevant | |
1107 | * side, and this is a tie, then adjust this end's y | |
1108 | * to level the curve */ | |
1109 | else if (y_adj != 0.0 && stuff_p->curveno == -1) { | |
1110 | endlist_p->y += inner_adj(end_gs_p, endnote_p, | |
1111 | y_adj, place); | |
1112 | } | |
1113 | ||
1114 | /* level beginning if the note in the previous | |
1115 | * chord was the same note but wasn't the top, | |
1116 | * but the next note is more than a stepsize | |
1117 | * away. */ | |
1118 | if (y2_adj != 0.0 && stuff_p->curveno == -1) { | |
1119 | curvelist_p->y += inner_adj(begin_gs_p, | |
1120 | begnote_p, y2_adj, place); | |
1121 | } | |
1122 | } | |
1123 | } | |
1124 | ||
1125 | /* one final adjustment. If the stem of first group is up and stem | |
1126 | * of second group is down, and the notes being tied/slurred are both | |
1127 | * the tops notes if the place is above or both bottom notes if the | |
1128 | * place is below, then move the y coord on the side that wasn't | |
1129 | * already moved, to level the curve. Do only if the note is shorter | |
1130 | * than a whole note, because longer notes were already moved because | |
1131 | * they had no stem. */ | |
1132 | if (is_phrase == NO && begin_gs_p->stemdir == UP | |
1133 | && end_gs_p != (struct GRPSYL *) 0 | |
1134 | && end_gs_p->stemdir == DOWN) { | |
1135 | if (place == PL_ABOVE && begnote_p == | |
1136 | &(begin_gs_p->notelist[0]) | |
1137 | && endnote_p == &(end_gs_p->notelist[0]) | |
1138 | && begin_gs_p->basictime > 1) { | |
1139 | curvelist_p->y += (Stepsize * (begnote_p->notesize | |
1140 | == GS_NORMAL ? 1.7 : 1.2)); | |
1141 | } | |
1142 | else if (place == PL_BELOW && begnote_p == | |
1143 | &(begin_gs_p->notelist[begin_gs_p->nnotes - 1]) | |
1144 | && endnote_p == | |
1145 | &(end_gs_p->notelist[end_gs_p->nnotes - 1]) | |
1146 | && end_gs_p->basictime > 1) { | |
1147 | endlist_p->y -= (Stepsize * (endnote_p->notesize | |
1148 | == GS_NORMAL ? 1.7 : 1.2)); | |
1149 | } | |
1150 | } | |
1151 | ||
1152 | endlist_p->next = (struct CRVLIST *) 0; | |
1153 | /* no need to set other links now because we will be added other nodes | |
1154 | * in between in a moment anyway */ | |
1155 | ||
1156 | /* if carrying over, extend x to margin */ | |
1157 | if (stuff_p->carryout) { | |
1158 | endlist_p->x = PGWIDTH - eff_rightmargin(mll_p); | |
1159 | } | |
1160 | ||
1161 | /* find length of curve by Pythagorean */ | |
1162 | length = sqrt(SQUARED(endlist_p->x - curvelist_p->x) | |
1163 | + SQUARED(endlist_p->y - curvelist_p->y)); | |
1164 | ||
1165 | /* Find y length for creating bulge in the curve. | |
1166 | * Make bigger bend if longer curve, but not too big or too small. | |
1167 | */ | |
1168 | ylen = length / 16; | |
1169 | if (ylen > 2.2 * Stepsize) { | |
1170 | ylen = 2.2 * Stepsize; | |
1171 | } | |
1172 | else if (ylen < (Stepsize * 0.75)) { | |
1173 | ylen = Stepsize * 0.75; | |
1174 | } | |
1175 | ylen = ylen * sign; | |
1176 | ||
1177 | /* we figure out curve as if endpoints were on the x axis, then adjust | |
1178 | * with the proper sin and cos factors to get them where they really | |
1179 | * belong */ | |
1180 | sintheta = (endlist_p->y - curvelist_p->y) / length; | |
1181 | costheta = (endlist_p->x - curvelist_p->x) / length; | |
1182 | ||
1183 | /* set up node for another point on curve */ | |
1184 | MALLOC(CRVLIST, new_p, 1); | |
1185 | new_p->prev = curvelist_p; | |
1186 | new_p->next = endlist_p; | |
1187 | curvelist_p->next = new_p; | |
1188 | endlist_p->prev = new_p; | |
1189 | ||
1190 | if (stuff_p->carryout == YES) { | |
1191 | if (is_phrase == YES) { | |
1192 | endlist_p->y += ylen / 2.0; | |
1193 | } | |
1194 | else { | |
1195 | end_gs_p = begin_gs_p; | |
1196 | } | |
1197 | } | |
1198 | ||
1199 | /* First try a single point in the middle. Try bigger bulge | |
1200 | * value if some groups stick out, up to a maximum. */ | |
1201 | tb.mll_p = mll_p; | |
1202 | tb.begin_gs_p = begin_gs_p; | |
1203 | tb.end_gs_p = end_gs_p; | |
1204 | tb.place = place; | |
1205 | tb.curvelist_p = curvelist_p; | |
1206 | tb.endlist_p = endlist_p; | |
1207 | tb.xlen = length / 2.0; | |
1208 | tb.ylen = ylen; | |
1209 | tb.sintheta = sintheta; | |
1210 | tb.costheta = costheta; | |
1211 | tb.minbulge = MINBULGE; | |
1212 | tb.maxbulge = MAXBULGE; | |
1213 | try_p = &tb; | |
1214 | if ((bulgeval = try_bulge(try_p)) < MAXBULGE) { | |
1215 | /* This curve works. Go with it */ | |
1216 | if (bulgeval == MINBULGE) { | |
1217 | /* The very first try worked with nothing in the way, | |
1218 | * so may be safe to try to | |
1219 | * beautify any really steep curves. | |
1220 | * So try to redo and see if still okay. | |
1221 | * If not, put back the original. | |
1222 | */ | |
1223 | double save_x, save_y; | |
1224 | save_x = curvelist_p->next->x; | |
1225 | save_y = curvelist_p->next->y; | |
1226 | redo_steep(curvelist_p, endlist_p, place); | |
1227 | if (stick_out(try_p) > 0.0) { | |
1228 | curvelist_p->next->x = save_x; | |
1229 | curvelist_p->next->y = save_y; | |
1230 | } | |
1231 | } | |
1232 | /* adjust group boundaries to include the curve */ | |
1233 | final_touches(mll_p, begin_gs_p, end_gs_p, curvelist_p, place); | |
1234 | ||
1235 | /* attach the curve to the stuff */ | |
1236 | stuff_p->crvlist_p = curvelist_p; | |
1237 | return; | |
1238 | } | |
1239 | ||
1240 | /* Using a single inner point didn't give a good curve. | |
1241 | * So we'll try two inner points. Add another point to the curve. */ | |
1242 | MALLOC(CRVLIST, new_p, 1); | |
1243 | new_p->prev = endlist_p->prev; | |
1244 | new_p->next = endlist_p; | |
1245 | new_p->prev->next = new_p; | |
1246 | endlist_p->prev = new_p; | |
1247 | ||
1248 | /* We now have three segments, each 1/3 of total length */ | |
1249 | tb.xlen = length / 3.0; | |
1250 | ||
1251 | /* We're a little more desperate, so allow more bulge */ | |
1252 | tb.minbulge = MIN2BULGE; | |
1253 | tb.maxbulge = MAX2BULGE; | |
1254 | ||
1255 | if ((bulgeval = try_bulge(try_p)) < MAXBULGE) { | |
1256 | /* This curve works. Go with it */ | |
1257 | final_touches(mll_p, begin_gs_p, end_gs_p, curvelist_p, place); | |
1258 | ||
1259 | /* attach the curve to the stuff */ | |
1260 | stuff_p->crvlist_p = curvelist_p; | |
1261 | return; | |
1262 | } | |
1263 | ||
1264 | /* Really getting desperate now, so allow even more bulge */ | |
1265 | tb.minbulge = MIN3BULGE; | |
1266 | tb.maxbulge = MAX3BULGE; | |
1267 | ||
1268 | /* Just adjusting bulge didn't work, | |
1269 | * so we try repeatedly moving the ends slightly | |
1270 | * and trying again until something works. | |
1271 | * Worst case should be something like an above curve encompassing c0 | |
1272 | * to b9 back to c0, with a stem up on the b9. That would be about 80 | |
1273 | * stepsizes. But if an end is a cross-staff stem group completely | |
1274 | * on the other staff, and if that other staff is ridiculously | |
1275 | * far away because of very tall STUFF, even 100 iterations | |
1276 | * of moving by a Stepsize sometimes isn't enough. | |
1277 | * So we'll try 200 times before giving up with a pfatal. | |
1278 | */ | |
1279 | found_good = NO; | |
1280 | for (try = 0; try < 200; try++) { | |
1281 | double mvbegin, mvend, mvboth; | |
1282 | int leftcount, rightcount; | |
1283 | ||
1284 | /* Try moving each end individually and both together, | |
1285 | * then try to go with whatever gave the best results | |
1286 | * with the least movement. | |
1287 | */ | |
1288 | ||
1289 | /* try with just begin point moved */ | |
1290 | curvelist_p->y += Stepsize * sign; | |
1291 | mvbegin = try_bulge(try_p); | |
1292 | ||
1293 | /* try with both endpoints moved */ | |
1294 | endlist_p->y += Stepsize * sign; | |
1295 | mvboth = try_bulge(try_p); | |
1296 | leftcount = tb.leftcount; | |
1297 | rightcount = tb.rightcount; | |
1298 | ||
1299 | /* try with just end point moved */ | |
1300 | curvelist_p->y -= Stepsize * sign; | |
1301 | mvend = try_bulge(try_p); | |
1302 | ||
1303 | /* See which of the three attempts seemed best */ | |
1304 | if ( (mvend < mvbegin && mvend < mvboth) | |
1305 | || (try < 5 && leftcount == 0 && rightcount > 0) ) { | |
1306 | /* moving just the end was best */ | |
1307 | if (mvend < MAX3BULGE) { | |
1308 | found_good = YES; | |
1309 | break; | |
1310 | } | |
1311 | } | |
1312 | else if ( (mvbegin < mvend && mvbegin < mvboth) | |
1313 | || (try < 5 && leftcount > 0 && rightcount == 0) ) { | |
1314 | /* moving just the beginning was best */ | |
1315 | curvelist_p->y += Stepsize * sign; | |
1316 | endlist_p->y -= Stepsize * sign; | |
1317 | if (mvbegin < MAX3BULGE) { | |
1318 | found_good = YES; | |
1319 | break; | |
1320 | } | |
1321 | } | |
1322 | else { | |
1323 | /* move both ends */ | |
1324 | curvelist_p->y += Stepsize * sign; | |
1325 | if (mvboth < MAX3BULGE) { | |
1326 | found_good = YES; | |
1327 | break; | |
1328 | } | |
1329 | } | |
1330 | } | |
1331 | ||
1332 | if (found_good == YES) { | |
1333 | /* Call try_bulge again to set the inner points (the one | |
1334 | * we chose might not be the last one we tried. */ | |
1335 | (void) try_bulge(try_p); | |
1336 | final_touches(mll_p, begin_gs_p, end_gs_p, curvelist_p, place); | |
1337 | ||
1338 | /* attach the curve to the stuff */ | |
1339 | stuff_p->crvlist_p = curvelist_p; | |
1340 | return; | |
1341 | } | |
1342 | ||
1343 | pfatal("unable to find a usable curve"); | |
1344 | } | |
1345 | \f | |
1346 | ||
1347 | /* | |
1348 | * Returns the smallest bulge factor that worked, or a value >= maxbulge if | |
1349 | * nothing worked. The more the return value exceeds maxbulge, the worse | |
1350 | * the amount of "stick out." The curvelist_p should point to a curve with | |
1351 | * 3 or 4 points. | |
1352 | */ | |
1353 | ||
1354 | static double | |
1355 | try_bulge(info_p) | |
1356 | ||
1357 | struct TRYBULGE *info_p; /* points to all the info this func needs */ | |
1358 | ||
1359 | { | |
1360 | struct CRVLIST *mid_p; /* interior point of curve */ | |
1361 | struct CRVLIST *mid2_p; /* second inner point, if any */ | |
1362 | double bulge_factor; /* how much to bulge */ | |
1363 | double amount = 0.0; /* amount of stick out */ | |
1364 | ||
1365 | ||
1366 | /* Get pointer to the midpoint(s) */ | |
1367 | mid_p = info_p->curvelist_p->next; | |
1368 | if (mid_p->next != info_p->endlist_p) { | |
1369 | mid2_p = mid_p->next; | |
1370 | } | |
1371 | else { | |
1372 | mid2_p = 0; | |
1373 | } | |
1374 | ||
1375 | /* Keep trying bigger bulge until we find one that clears all the | |
1376 | * groups or until the specified maximum is reached. */ | |
1377 | for (bulge_factor = info_p->minbulge; bulge_factor < info_p->maxbulge; | |
1378 | bulge_factor += 0.25) { | |
1379 | ||
1380 | /* find (x,y) values for midpoint(s) taking the rotation | |
1381 | * from horizontal into account. */ | |
1382 | mid_p->x = info_p->curvelist_p->x | |
1383 | + (info_p->xlen * info_p->costheta) | |
1384 | - (bulge_factor * info_p->ylen * info_p->sintheta); | |
1385 | mid_p->y = info_p->curvelist_p->y | |
1386 | + (bulge_factor * info_p->ylen * info_p->costheta) | |
1387 | + (info_p->xlen * info_p->sintheta); | |
1388 | ||
1389 | if (mid2_p != 0) { | |
1390 | mid2_p->x = info_p->curvelist_p->x | |
1391 | + (2.0 * info_p->xlen * info_p->costheta) | |
1392 | - (bulge_factor * info_p->ylen * info_p->sintheta); | |
1393 | mid2_p->y = info_p->curvelist_p->y | |
1394 | + (bulge_factor * info_p->ylen * info_p->costheta) | |
1395 | + (2.0 * info_p->xlen * info_p->sintheta); | |
1396 | } | |
1397 | ||
1398 | if ((amount = stick_out(info_p)) <= 0.0) { | |
1399 | /* This curve works. Go with it */ | |
1400 | break; | |
1401 | } | |
1402 | } | |
1403 | ||
1404 | /* Even max allowed bulge value was not enough. Returning the max bulge | |
1405 | * allowed tells caller we failed, and adding on how much | |
1406 | * "stick-out" gives an indication of how badly we failed. | |
1407 | */ | |
1408 | return(bulge_factor + amount); | |
1409 | } | |
1410 | \f | |
1411 | ||
1412 | /* adjust the endpoint of an inner note if the opposite end was adjusted, | |
1413 | * and there is room to adjust this end. */ | |
1414 | ||
1415 | static double | |
1416 | inner_adj(gs_p, note_p, y_adj, place) | |
1417 | ||
1418 | struct GRPSYL *gs_p; /* not is in this group */ | |
1419 | struct NOTE *note_p; /* this is the note being tied to */ | |
1420 | double y_adj; /* how much other end of tie was adjusted */ | |
1421 | int place; /* PL_ABOVE or PL_BELOW */ | |
1422 | ||
1423 | { | |
1424 | int i; | |
1425 | ||
1426 | ||
1427 | if (gs_p->nnotes <= 2) { | |
1428 | /* can't possibly be an inner note, so no adjust */ | |
1429 | return(0.0); | |
1430 | } | |
1431 | ||
1432 | /* find index of note */ | |
1433 | for (i = 0; i < gs_p->nnotes; i++) { | |
1434 | if (note_p == &(gs_p->notelist[i])) { | |
1435 | break; | |
1436 | } | |
1437 | } | |
1438 | ||
1439 | if (i == gs_p->nnotes) { | |
1440 | pfatal("couldn't find note in chord"); | |
1441 | } | |
1442 | ||
1443 | if (i == 0 || i == gs_p->nnotes - 1) { | |
1444 | /* not an inner note. no adjust */ | |
1445 | return(0.0); | |
1446 | } | |
1447 | ||
1448 | /* check if next note in chord is within STEPSIZE away. If not, | |
1449 | * we can adjust this end */ | |
1450 | if (place == PL_ABOVE && gs_p->notelist[i-1].stepsup | |
1451 | > gs_p->notelist[i].stepsup + 1) { | |
1452 | return(y_adj); | |
1453 | } | |
1454 | else if (place == PL_BELOW && gs_p->notelist[i+1].stepsup | |
1455 | < gs_p->notelist[i].stepsup - 1) { | |
1456 | /* y_adj will always come in as a positive number and will be | |
1457 | * added on return, so return negative value for below curves */ | |
1458 | return(-y_adj); | |
1459 | } | |
1460 | return(0.0); | |
1461 | } | |
1462 | \f | |
1463 | ||
1464 | /* Returns the sum of the "stick out" of groups in the given curve. | |
1465 | * If all groups are inside, this will be 0.0 | |
1466 | * Also counts number of "stickouts" that are near each end, | |
1467 | * in case that might be useful in deciding which endpoint to move. | |
1468 | * These counts are stored in the leftcount and rightcount fields of | |
1469 | * the passed-in struct. | |
1470 | */ | |
1471 | ||
1472 | static double | |
1473 | stick_out(info_p) | |
1474 | ||
1475 | struct TRYBULGE *info_p; | |
1476 | ||
1477 | { | |
1478 | struct GRPSYL *gs_p; /* to walk through list */ | |
1479 | struct GRPSYL *begin_gs_p, *end_gs_p; | |
1480 | double yleft, yright; /* y value of point on the line that is | |
1481 | * at the x position of the left and right | |
1482 | * sides of the current GRPSYL, */ | |
1483 | double yg; /* y of group accounting for other phrases */ | |
1484 | struct MAINLL *mll_p; /* the curve's STUFF hangs off of here */ | |
1485 | int place; /* PL_* */ | |
1486 | struct CRVLIST *curvelist_p; /* beginning of curve to check */ | |
1487 | struct CRVLIST *endlist_p; /* end of curve to check */ | |
1488 | int staff; | |
1489 | int voice; | |
1490 | double stickout; /* return value */ | |
1491 | double len; /* length that is deemed "near the end" of the | |
1492 | * curve, for setting left/right counts */ | |
1493 | ||
1494 | ||
1495 | begin_gs_p = info_p->begin_gs_p; | |
1496 | end_gs_p = info_p->end_gs_p; | |
1497 | if (begin_gs_p == 0 || end_gs_p == 0) { | |
1498 | pfatal("got null pointer when checking phrase marks"); | |
1499 | } | |
1500 | ||
1501 | info_p->leftcount = 0; | |
1502 | info_p->rightcount = 0; | |
1503 | ||
1504 | /* If starting phrase on last note of score or ending one on first | |
1505 | * note of a score, begin and end will be the same. We know that | |
1506 | * note has already been accounted for, so nothing to do. */ | |
1507 | if (begin_gs_p == end_gs_p) { | |
1508 | return(0.0); | |
1509 | } | |
1510 | ||
1511 | staff = begin_gs_p->staffno; | |
1512 | voice = begin_gs_p->vno; | |
1513 | curvelist_p = info_p->curvelist_p; | |
1514 | endlist_p = info_p->endlist_p; | |
1515 | mll_p = info_p->mll_p; | |
1516 | place = info_p->place; | |
1517 | stickout = 0.0; | |
1518 | /* We will be counting up how many groups stick out near each end, | |
1519 | * to potentially help decide which endpoint to move to avoid | |
1520 | * collisions. For that purpose, we'll define "near the end" | |
1521 | * as being within 1/4 of the total x distance from an endpoint. | |
1522 | */ | |
1523 | len = (endlist_p->x - curvelist_p->x) / 4.0; | |
1524 | ||
1525 | /* Go through each group between the beginning and end. We've | |
1526 | * already set the curve endings to clear the group boundaries */ | |
1527 | for (gs_p = begin_gs_p->next; gs_p != end_gs_p; gs_p = gs_p->next) { | |
1528 | ||
1529 | /* If hit end of measure go to next measure */ | |
1530 | if (gs_p == (struct GRPSYL *) 0) { | |
1531 | mll_p = next_staff(staff, mll_p->next); | |
1532 | if (info_p->mll_p == (struct MAINLL *) 0) { | |
1533 | pfatal("fell off end of list while doing phrase marks"); | |
1534 | } | |
1535 | gs_p = mll_p->u.staff_p->groups_p[voice - 1]; | |
1536 | } | |
1537 | ||
1538 | if (gs_p == end_gs_p) { | |
1539 | break; | |
1540 | } | |
1541 | ||
1542 | /* Find out where the y of the curve is at this group. | |
1543 | * We actually check two points, one each slightly | |
1544 | * to the east and west of the group's x. | |
1545 | * It would even more accurate to figure out the actual | |
1546 | * width of whatever is at the end (a notehead, a stem, | |
1547 | * a rest, stem with flag, etc) but just taking 1.5 Stepsizes | |
1548 | * on either side generally gives adequate results and | |
1549 | * is a lot simpler. | |
1550 | */ | |
1551 | yleft = curve_y_at_x(curvelist_p, gs_p->c[AX] - 1.5 * Stepsize); | |
1552 | yright = curve_y_at_x(curvelist_p, gs_p->c[AX] + 1.5 * Stepsize); | |
1553 | ||
1554 | /* See if this group is within the curve */ | |
1555 | if (info_p->place == PL_ABOVE) { | |
1556 | /* Consider the group (RN) plus any relevant | |
1557 | * nested phrase marks (their space is stored in AN). | |
1558 | * It is relevant unless it's for the begin group | |
1559 | * and that group's east is not relevent, or it's the | |
1560 | * end group and that group's west is not relevant */ | |
1561 | yg = gs_p->c[RN] + CLEARANCE | |
1562 | + eff_tupext(gs_p, mll_p->u.staff_p, place); | |
1563 | if ( (gs_p != begin_gs_p || | |
1564 | ((gs_p->phraseside & EAST_SIDE) == 0)) | |
1565 | && (gs_p != end_gs_p || | |
1566 | ((gs_p->phraseside & WEST_SIDE) == 0)) ) { | |
1567 | yg += gs_p->c[AN]; | |
1568 | } | |
1569 | if (yleft > yg && yright > yg) { | |
1570 | /* Good. It's inside */ | |
1571 | continue; | |
1572 | } | |
1573 | else { | |
1574 | /* Bad. It stuck over */ | |
1575 | stickout += yg - MIN(yleft, yright); | |
1576 | /* If near either end, make a note of that */ | |
1577 | if (gs_p->c[AX] - curvelist_p->x < len) { | |
1578 | info_p->leftcount += 1; | |
1579 | } | |
1580 | else if (endlist_p->x - gs_p->c[AX] < len) { | |
1581 | info_p->rightcount +=1; | |
1582 | } | |
1583 | } | |
1584 | } | |
1585 | else { | |
1586 | /* Do the same for curve going down */ | |
1587 | yg = gs_p->c[RS] - CLEARANCE | |
1588 | + eff_tupext(gs_p, mll_p->u.staff_p, place); | |
1589 | if ( (gs_p != begin_gs_p || | |
1590 | ((gs_p->phraseside & EAST_SIDE) == 0)) | |
1591 | && (gs_p != end_gs_p || | |
1592 | ((gs_p->phraseside & WEST_SIDE) == 0)) ) { | |
1593 | yg += gs_p->c[AS]; | |
1594 | } | |
1595 | if (yleft < yg && yright < yg) { | |
1596 | continue; | |
1597 | } | |
1598 | else { | |
1599 | stickout += MAX(yleft, yright) - yg; | |
1600 | if (gs_p->c[AX] - curvelist_p->x < len) { | |
1601 | info_p->leftcount++; | |
1602 | } | |
1603 | else if (endlist_p->x - gs_p->c[AX] < len) { | |
1604 | info_p->rightcount++; | |
1605 | } | |
1606 | } | |
1607 | } | |
1608 | } | |
1609 | return(stickout); | |
1610 | } | |
1611 | \f | |
1612 | ||
1613 | /* find the x of the end of a tie/slur. Usually we could just used the west of | |
1614 | * the group, but if there are lots of accidentals on notes that are far | |
1615 | * away from the note in question, the end of the tie can come out rather | |
1616 | * far away from its note. So try to see if we can move it closer, by | |
1617 | * checking to see if there are any accidentals on notes nearby. This | |
1618 | * function is not foolproof, sometimes leaving space when the tie/slur | |
1619 | * could actually get threaded through a tiny opening, and sometimes | |
1620 | * overwriting the edge of an accidental somewhat, but tries to do a better | |
1621 | * job than the original single line of code for figuring this out had done. */ | |
1622 | ||
1623 | static double | |
1624 | tieslurx(gs_p, note_p, place) | |
1625 | ||
1626 | struct GRPSYL *gs_p; /* check notes in this group */ | |
1627 | struct NOTE *note_p; /* check for accidentals near this note */ | |
1628 | int place; /* PL_ABOVE or PL_BELOW to tell which side to look on */ | |
1629 | ||
1630 | { | |
1631 | int n; /* index through notelist */ | |
1632 | int acc; /* accidental */ | |
1633 | ||
1634 | ||
1635 | /* if "wrong" side of a stem up group, better use group boundary */ | |
1636 | if (note_p->c[AX] > gs_p->c[AX] && gs_p->stemdir == UP) { | |
1637 | return(gs_p->c[AW]); | |
1638 | } | |
1639 | ||
1640 | /* if there is another note nearby, | |
1641 | * and that note has an accidental, better use | |
1642 | * the west of the group to be safe, otherwise | |
1643 | * use the west of the note. */ | |
1644 | for (n = 0; n < gs_p->nnotes; n++) { | |
1645 | ||
1646 | acc = gs_p->notelist[n].accidental; | |
1647 | switch (gs_p->notelist[n].stepsup - note_p->stepsup) { | |
1648 | case 1: | |
1649 | case 2: | |
1650 | /* close enough that sharp, flat, and dblflat may | |
1651 | * interfere, if coming in from above */ | |
1652 | if (place == PL_ABOVE && (acc == '#' || acc == '&' | |
1653 | || acc == 'B')) { | |
1654 | return(gs_p->c[AW]); | |
1655 | } | |
1656 | break; | |
1657 | case 3: | |
1658 | /* close enough that sharp may interfere, if coming | |
1659 | * in from above */ | |
1660 | if (place == PL_ABOVE && acc == '#') { | |
1661 | return(gs_p->c[AW]); | |
1662 | } | |
1663 | break; | |
1664 | case 0: | |
1665 | /* the note itself */ | |
1666 | break; | |
1667 | case -1: | |
1668 | /* sharp, flat, and dblflat may interfere from | |
1669 | * either direction */ | |
1670 | if (acc == '#' || acc == '&' || acc == 'B') { | |
1671 | return(gs_p->c[AW]); | |
1672 | } | |
1673 | break; | |
1674 | ||
1675 | case -2: | |
1676 | case -3: | |
1677 | case -4: | |
1678 | /* close enough that things may interfere */ | |
1679 | if (place == PL_BELOW && (acc == '#' || acc == '&' | |
1680 | || acc == 'B')) { | |
1681 | return(gs_p->c[AW]); | |
1682 | } | |
1683 | break; | |
1684 | ||
1685 | default: | |
1686 | /* this note is too far away to matter */ | |
1687 | break; | |
1688 | } | |
1689 | } | |
1690 | ||
1691 | /* it seems there are no accidentals in the way, so use the note | |
1692 | * boundary, rather than group boundary */ | |
1693 | return(gs_p->c[AX] + notehorz(gs_p, note_p, AW)); | |
1694 | } | |
1695 | \f | |
1696 | ||
1697 | /* given a main list struct, search forward from there for the STAFF matching | |
1698 | * the given staff. If fall off end of main list, return NULL */ | |
1699 | ||
1700 | static struct MAINLL * | |
1701 | next_staff(staff, mll_p) | |
1702 | ||
1703 | int staff; /* find this staff number */ | |
1704 | struct MAINLL *mll_p; /* where to start */ | |
1705 | ||
1706 | { | |
1707 | /* walk through main list looking for desired staff */ | |
1708 | for ( ; mll_p != (struct MAINLL *) 0; mll_p = mll_p->next) { | |
1709 | if (mll_p->str == S_STAFF) { | |
1710 | if (mll_p->u.staff_p->staffno == staff) { | |
1711 | return(mll_p); | |
1712 | } | |
1713 | } | |
1714 | } | |
1715 | ||
1716 | /* didn't find it */ | |
1717 | return( (struct MAINLL *) 0); | |
1718 | } | |
1719 | \f | |
1720 | /* | |
1721 | * Name: redo_steep() | |
1722 | * | |
1723 | * Abstract: Redo curves that are very steep. | |
1724 | * | |
1725 | * Returns: void | |
1726 | * | |
1727 | * Description: If the curve is "too steep", it redoes it | |
1728 | * so that it's horizontal at the outer end, rather than already | |
1729 | * sloping in towards the inner end. | |
1730 | * Caller needs to verify the redone curve doesn't collide | |
1731 | * with any groups. Assumes the curve contains 3 points. | |
1732 | */ | |
1733 | ||
1734 | static void | |
1735 | redo_steep (first_p, last_p, place) | |
1736 | ||
1737 | struct CRVLIST *first_p; /* left endpoint of curve */ | |
1738 | struct CRVLIST *last_p; /* right endpoint of curve */ | |
1739 | int place; /* above or below */ | |
1740 | ||
1741 | { | |
1742 | struct CRVLIST *mid_p; /* new midpoint of curve */ | |
1743 | float delx; /* distance from the end to test */ | |
1744 | float a, b; /* some distances, see below */ | |
1745 | float midoff; /* vert offset of midpoint */ | |
1746 | ||
1747 | ||
1748 | /* | |
1749 | * We need to test whether either end of the curve is sloping in. So | |
1750 | * we really should find the derivative at the endpoints. But we can | |
1751 | * approximate it close enough by finding the y value at a point "near" | |
1752 | * the end and comparing it to the end's y value. "delx" tells how | |
1753 | * near. We'd like to set it to a millionth of an inch, but due to | |
1754 | * apparent roundoff errors in curve_y_at_x(), we make it bigger than | |
1755 | * that: 1/4 the curve length, but never more than 2 stepsizes. | |
1756 | */ | |
1757 | if (last_p->x - first_p->x > 8 * Stepsize) { | |
1758 | delx = 2 * Stepsize; | |
1759 | } else { | |
1760 | delx = (last_p->x - first_p->x) / 4.0; | |
1761 | } | |
1762 | if (place == PL_ABOVE) { | |
1763 | /* if both near points are higher than end points, return */ | |
1764 | if (curve_y_at_x(first_p, first_p->x + delx) >= first_p->y && | |
1765 | curve_y_at_x(first_p, last_p->x - delx) >= last_p->y) { | |
1766 | return; | |
1767 | } | |
1768 | } else { | |
1769 | /* if both near points are lower than end points, return */ | |
1770 | if (curve_y_at_x(first_p, first_p->x + delx) <= first_p->y && | |
1771 | curve_y_at_x(first_p, last_p->x - delx) <= last_p->y) { | |
1772 | return; | |
1773 | } | |
1774 | } | |
1775 | ||
1776 | /* | |
1777 | * The curve is steep. First, we choose a new point, | |
1778 | * horizontally in the middle. We are | |
1779 | * going to choose its vertical position so that the outer end of the | |
1780 | * curve starts out horizontal. | |
1781 | * | |
1782 | * Imagine the case of PL_BELOW where the left end is the outer (lower) | |
1783 | * end. (The other 3 cases are symmetrical to this, and we can use the | |
1784 | * analogous result.) Set the axes so that the left end is at the | |
1785 | * origin, and the right end is at (2*a, b). The new point will be at | |
1786 | * (a, y), and we have to find y. We know that y will be between 0 and | |
1787 | * b/2. (Draw a picture.) | |
1788 | * | |
1789 | * Draw segments from (0, 0) to (a, y), and (a, y) to (2*a, b). Then | |
1790 | * draw a line through (a, y) such that it forms the same angle theta | |
1791 | * with each of these segments. The way calccurve() works, it will | |
1792 | * form two cubic arcs (in rotated coordinate systems) through the | |
1793 | * three points, such that the slope of each arc at each point forms | |
1794 | * the same angle theta with the segment next to it. The last line we | |
1795 | * drew hits the X axis at a point which, with (0, 0) and (a, y) forms | |
1796 | * an isoceles triangle, where the angles at (0, 0) and (a, y) are | |
1797 | * both theta (because we're saying the arc at (0, 0) is horizontal). | |
1798 | * So the other angle is 180 degrees minus 2*theta. That means the | |
1799 | * other angle the line forms with the X axis is 2*theta. And that | |
1800 | * means the angle between the horizontal line through (a, y) and the | |
1801 | * second segment (a, y) to (2*a, b) is 3*theta. | |
1802 | * | |
1803 | * Looking at triangle (0, 0) to (a, 0) to (a, y), we see that | |
1804 | * tan(theta) = y/a | |
1805 | * Looking at triangle (a, y) to (2*a, y) to (2*a, b), we see that | |
1806 | * tan(3*theta) = (b-y)/a | |
1807 | * There is a trig identity | |
1808 | * 3*tan(theta) - (tan(theta))^3 | |
1809 | * tan(3*theta) = ------------------------------ | |
1810 | * 1 - 3*(tan(theta))^2 | |
1811 | * Plug into this our values for tan(theta) and tan(3*theta), and you | |
1812 | * end up with | |
1813 | * 4 y^3 - 3 b y^2 - 4 a^2 y + a^2 b = 0 | |
1814 | * To solve this cubic, we could do a whole routine for solving cubics, | |
1815 | * but it's easier to approximate as follows. | |
1816 | * | |
1817 | # Define | |
1818 | * F(x) = 4 x^3 - 3 b x^2 - 4 a^2 x + a^2 b | |
1819 | * a and b are positive. So at x = 0, F(x) > 0. At x=b/2, F(x) < 0. | |
1820 | * Thus, as we expect, F(x) = 0 somewhere in between. For the | |
1821 | * following algorithm to work, we need to know that F(x) is strictly | |
1822 | * decreasing (the slope is always negative). The slope is | |
1823 | * F'(x) = 12 x^2 - 6 b x - 4 a^2 | |
1824 | * (the derivative). It is a parabola opening upward and going through | |
1825 | * (0, -4a^2) and (b/2, -4a^2). So it is always negative in this | |
1826 | * interval. | |
1827 | * | |
1828 | * The algorithm starts with lo = 0 and hi = b/2. It draws a straight | |
1829 | * line between (lo, F(lo)) and (hi, F(hi)). The point where this | |
1830 | * crosses the X axis we call "mid". Based on whether F(mid) is | |
1831 | * positive or negative, we reset lo or hi to mid, and repeat the | |
1832 | * process until F(mid) is within b/1000 of the axis. Then we will use | |
1833 | * mid as our y value in the picture. | |
1834 | */ | |
1835 | a = ABSDIFF(first_p->x, last_p->x) / 2.0; | |
1836 | b = ABSDIFF(first_p->y, last_p->y); | |
1837 | ||
1838 | midoff= solvecubic(4.0, -3.0*b, -4.0*a*a, a*a*b, 0.0, b/2.0, POINT/2.0); | |
1839 | ||
1840 | mid_p = first_p->next; | |
1841 | mid_p->x = first_p->x + a; /* horizontally halfway between */ | |
1842 | ||
1843 | /* handle the 4 cases, using the "mid" value for y in the diagram */ | |
1844 | if (place == PL_ABOVE) { | |
1845 | if (first_p->y < last_p->y) { | |
1846 | mid_p->y = last_p->y - midoff; | |
1847 | } else { | |
1848 | mid_p->y = first_p->y - midoff; | |
1849 | } | |
1850 | } else { | |
1851 | if (first_p->y < last_p->y) { | |
1852 | mid_p->y = first_p->y + midoff; | |
1853 | } else { | |
1854 | mid_p->y = last_p->y + midoff; | |
1855 | } | |
1856 | } | |
1857 | } | |
1858 | \f | |
1859 | ||
1860 | /* do final refinements of curve. | |
1861 | * Remove any really tiny line segments. | |
1862 | * Then reset the group north or south boundaries to reflect | |
1863 | * the inclusion of the phrase mark. | |
1864 | */ | |
1865 | ||
1866 | static void | |
1867 | final_touches(mll_p, begin_gs_p, end_gs_p, curvelist_p, place) | |
1868 | ||
1869 | struct MAINLL *mll_p; /* points to first group in curve */ | |
1870 | struct GRPSYL *begin_gs_p; /* first group in curve */ | |
1871 | struct GRPSYL *end_gs_p; /* last group in curve */ | |
1872 | struct CRVLIST *curvelist_p; /* the curve */ | |
1873 | int place; /* PL_ABOVE or PL_BELOW */ | |
1874 | ||
1875 | { | |
1876 | int voice; | |
1877 | int staff; | |
1878 | int index; /* in coord array: RN or RS */ | |
1879 | int adj_index; /* in coord array: AN or AS. Used to store how much | |
1880 | * to adjust for this phrase, in case there are | |
1881 | * nested phrases. */ | |
1882 | float y_c; /* y of curve */ | |
1883 | float x1, y1; /* lengths of segments in each dimension */ | |
1884 | float length; /* of line segment */ | |
1885 | struct CRVLIST *crvlist_p; | |
1886 | struct CRVLIST *extra_p; /* pointer to point to be freed */ | |
1887 | struct GRPSYL *gs_p; /* index through groups */ | |
1888 | ||
1889 | ||
1890 | if ( (mll_p == (struct MAINLL *) 0) | |
1891 | || (begin_gs_p == (struct GRPSYL *) 0) | |
1892 | || (end_gs_p == (struct GRPSYL *) 0) | |
1893 | || (curvelist_p == (struct CRVLIST *) 0) ) { | |
1894 | pfatal("null pointer in final_touches()"); | |
1895 | } | |
1896 | ||
1897 | /* If there are really tiny line segments in a curve, the code for | |
1898 | * tapering the curve has problems because if the width of the curve | |
1899 | * is more than the length of the line and the angles work out just | |
1900 | * wrong, various warts, sometimes huge ones, appear on the curves. | |
1901 | * So go through the curve and if there are any really tiny lines, | |
1902 | * throw away one of the points and make the remaining point the | |
1903 | * average of what it was and what the discarded one was. | |
1904 | * With the new way of calculating curves, this is probably now | |
1905 | * unnecessary, but it seems safer to leave it in, just in case. | |
1906 | */ | |
1907 | for (crvlist_p = curvelist_p; crvlist_p->next != (struct CRVLIST *) 0; | |
1908 | crvlist_p = crvlist_p->next) { | |
1909 | x1 = crvlist_p->next->x - crvlist_p->x; | |
1910 | y1 = crvlist_p->next->y - crvlist_p->y; | |
1911 | length = sqrt(SQUARED(x1) + SQUARED(y1)); | |
1912 | if (length < 0.01) { | |
1913 | /* replace with average */ | |
1914 | crvlist_p->x = (crvlist_p->x + crvlist_p->next->x) | |
1915 | / 2.0; | |
1916 | crvlist_p->y = (crvlist_p->y + crvlist_p->next->y) | |
1917 | / 2.0; | |
1918 | /* take the extra out of the list */ | |
1919 | extra_p = crvlist_p->next; | |
1920 | if (crvlist_p->next->next != (struct CRVLIST *) 0) { | |
1921 | crvlist_p->next->next->prev = crvlist_p; | |
1922 | } | |
1923 | crvlist_p->next = crvlist_p->next->next; | |
1924 | if (crvlist_p->next == (struct CRVLIST *) 0) { | |
1925 | /* avoid trying to take ->next of null ptr */ | |
1926 | break; | |
1927 | } | |
1928 | FREE(extra_p); | |
1929 | } | |
1930 | } | |
1931 | ||
1932 | /* adjust north or south of each group within the curve to account for | |
1933 | * the space needed for the curve */ | |
1934 | voice = begin_gs_p->vno; | |
1935 | staff = begin_gs_p->staffno; | |
1936 | if (place == PL_ABOVE) { | |
1937 | index = RN; | |
1938 | adj_index = AN; | |
1939 | } | |
1940 | else { | |
1941 | index = RS; | |
1942 | adj_index = AS; | |
1943 | } | |
1944 | ||
1945 | for (gs_p = begin_gs_p; ; gs_p = gs_p->next) { | |
1946 | ||
1947 | /* if hit end of measure go to next measure, skipping over | |
1948 | * any empty measure (which could happen if vscheme changed | |
1949 | * from 2 to 1 and back in the middle of the phrase) */ | |
1950 | while (gs_p == (struct GRPSYL *) 0) { | |
1951 | mll_p = next_staff(staff, mll_p->next); | |
1952 | if (mll_p == (struct MAINLL *) 0) { | |
1953 | pfatal("fell off end of list while doing phrase marks"); | |
1954 | } | |
1955 | gs_p = mll_p->u.staff_p->groups_p[voice - 1]; | |
1956 | } | |
1957 | ||
1958 | /* find where the curve y is at the x of the group, and | |
1959 | * adjust the north or south of the group appropriately, | |
1960 | * to be used later by any nesting phrase marks */ | |
1961 | y_c = curve_y_at_x(curvelist_p, gs_p->c[AX]); | |
1962 | ||
1963 | /* check for an inner tie. They don't affect the boundary */ | |
1964 | if ( ((index == RN) && (y_c < gs_p->c[index])) || | |
1965 | ((index == RS) && (y_c > gs_p->c[index]))) { | |
1966 | gs_p->c[adj_index] = 0.0; | |
1967 | } | |
1968 | else { | |
1969 | if (place == PL_ABOVE) { | |
1970 | gs_p->c[adj_index] = (y_c - gs_p->c[index] | |
1971 | + Stepsize); | |
1972 | } | |
1973 | else { | |
1974 | gs_p->c[adj_index] = - (gs_p->c[index] - y_c | |
1975 | + Stepsize); | |
1976 | } | |
1977 | } | |
1978 | ||
1979 | if (gs_p == end_gs_p) { | |
1980 | /* On the last group on the phrase, this phrase | |
1981 | * only affects the west side--another phrase can | |
1982 | * start on this same group with considering this one */ | |
1983 | gs_p->phraseside |= WEST_SIDE; | |
1984 | /* We are done with this curve */ | |
1985 | break; | |
1986 | } | |
1987 | else if (gs_p == begin_gs_p){ | |
1988 | /* Only affects east side of first group */ | |
1989 | gs_p->phraseside |= EAST_SIDE; | |
1990 | } | |
1991 | else { | |
1992 | /* not of the end, so both side are relevant */ | |
1993 | gs_p->phraseside |= (EAST_SIDE | WEST_SIDE); | |
1994 | } | |
1995 | } | |
1996 | } | |
1997 | \f | |
1998 | ||
1999 | /* determine effective tuplet extension value. Normally, the tupext tells | |
2000 | * us how much room to leave to allow for the tuplet bracket. However, | |
2001 | * if the tuplet doesn't get a bracket, this can cause us to leave extra | |
2002 | * space. Unfortunately, we do still need to leave room for the tuplet | |
2003 | * number even if the bracket isn't there, and it's hard to know exactly | |
2004 | * where the number is going to be. So we do the best we can: if the | |
2005 | * group is the start or end of a tuplet and the tuplet does not get a | |
2006 | * bracket, we ignore the tupext on that group. If it's in the middle | |
2007 | * of a tuplet, we leave the tupext as is, because we can't tell for sure | |
2008 | * whether we'll need it to get out of the way of the tuplet number or not. | |
2009 | * But if this isn't a tuplet, or its bracket is on the opposite side | |
2010 | * as where we're trying to put a curve, then it doesn't count as all. | |
2011 | */ | |
2012 | ||
2013 | static double | |
2014 | eff_tupext(gs_p, staff_p, side) | |
2015 | ||
2016 | struct GRPSYL *gs_p; | |
2017 | struct STAFF *staff_p; | |
2018 | int side; /* where the curve will be */ | |
2019 | ||
2020 | { | |
2021 | /* if not a tuplet, return tupextend as is */ | |
2022 | if (gs_p->tuploc == NOITEM) { | |
2023 | return(gs_p->tupextend); | |
2024 | } | |
2025 | ||
2026 | /* if curve is on opposite side as tuplet bracket, ignore tupextend */ | |
2027 | if (side != tupdir(gs_p, staff_p)) { | |
2028 | return(0.0); | |
2029 | } | |
2030 | /* if group passed in is first group in a tuplet, | |
2031 | * but the tuplet gets no bracket, ignore the tupextend */ | |
2032 | if (gs_p->tuploc == STARTITEM) { | |
2033 | if (tupgetsbrack(gs_p) == NO) { | |
2034 | return(0.0); | |
2035 | } | |
2036 | } | |
2037 | ||
2038 | /* if group passed in is last group in a tuplet, | |
2039 | * but the tuplet gets no bracket, ignore the tupextend */ | |
2040 | if (gs_p->tuploc == ENDITEM) { | |
2041 | /* first have to back up to find first group of tuplet, | |
2042 | * because that's what tupgetsbrack() wants */ | |
2043 | do { | |
2044 | gs_p = gs_p->prev; | |
2045 | if (gs_p == (struct GRPSYL *) 0) { | |
2046 | pfatal("can't find tuplet start in eff_tupext"); | |
2047 | } | |
2048 | } while (gs_p->tuploc != STARTITEM); | |
2049 | ||
2050 | if (tupgetsbrack(gs_p) == NO) { | |
2051 | return(0.0); | |
2052 | } | |
2053 | } | |
2054 | ||
2055 | /* if no special case applies, just return tupextend as is */ | |
2056 | return(gs_p->tupextend); | |
2057 | } | |
2058 | \f | |
2059 | ||
2060 | /* determine correct bend direction for curve, return UP or DOWN */ | |
2061 | ||
2062 | static int | |
2063 | bulge_direction(mll_p, gs1_p, note_index, curveno) | |
2064 | ||
2065 | struct MAINLL *mll_p; /* main list struct pointing to gs1_p */ | |
2066 | struct GRPSYL *gs1_p; /* curve will be from note in gs1_p to note in gs2_p */ | |
2067 | int note_index; /* which note in first group to tie */ | |
2068 | int curveno; /* index into slurto, or -1 for a tie */ | |
2069 | ||
2070 | { | |
2071 | struct GRPSYL *gs_p; | |
2072 | RATIONAL vtime1, vtime2; /* beginning and ending time of group */ | |
2073 | int othervoice; /* array index of other voice */ | |
2074 | ||
2075 | ||
2076 | /* If user explicitly set a bend direction, use that */ | |
2077 | if (curveno == -1 && gs1_p->notelist[note_index].tiedir != UNKNOWN) { | |
2078 | return(gs1_p->notelist[note_index].tiedir); | |
2079 | } | |
2080 | else if (curveno >= 0 && gs1_p->notelist[note_index] | |
2081 | .slurtolist[curveno].slurdir != UNKNOWN) { | |
2082 | return(gs1_p->notelist[note_index].slurtolist[curveno].slurdir); | |
2083 | } | |
2084 | ||
2085 | /* If there are 2 voices on the staff, bend is toward the stem */ | |
2086 | /* However, if the other voice is all spaces, pretend there is only | |
2087 | * one voice */ | |
2088 | if ( (mll_p->u.staff_p->groups_p[0] != (struct GRPSYL *) 0) | |
2089 | && (mll_p->u.staff_p->groups_p[1] | |
2090 | != (struct GRPSYL *) 0) ) { | |
2091 | ||
2092 | /* there are 2 voices */ | |
2093 | ||
2094 | /* calculate begin and end time of tied group */ | |
2095 | vtime1 = Zero; | |
2096 | for (gs_p = mll_p->u.staff_p->groups_p[gs1_p->vno - 1]; | |
2097 | gs_p != gs1_p; gs_p = gs_p->next) { | |
2098 | vtime1 = radd(vtime1, gs_p->fulltime); | |
2099 | } | |
2100 | ||
2101 | /* ending time is vtime1 plus the length of group 1. If group | |
2102 | * 1 is a grace note, use a very short time */ | |
2103 | if (EQ(gs1_p->fulltime, Zero)) { | |
2104 | RATIONAL tiny; | |
2105 | ||
2106 | tiny.n = 1; | |
2107 | tiny.d = 4 * MAXBASICTIME; | |
2108 | vtime2 = radd(vtime1, tiny); | |
2109 | } | |
2110 | else { | |
2111 | vtime2 = radd(vtime1, gs1_p->fulltime); | |
2112 | } | |
2113 | ||
2114 | /* get array index of other voice to check it */ | |
2115 | othervoice = (gs1_p->vno == 1 ? 1 : 0); | |
2116 | ||
2117 | if (hasspace(mll_p->u.staff_p->groups_p[othervoice], | |
2118 | vtime1, vtime2) == NO) { | |
2119 | /* there IS another voice, so stem goes opposite */ | |
2120 | return(gs1_p->stemdir); | |
2121 | } | |
2122 | } | |
2123 | ||
2124 | /* if only one voice (either because there is actually only one | |
2125 | * or because there is effectively only one since the other is space) | |
2126 | * and there is only one note in group, then bend is opposite stem */ | |
2127 | if (gs1_p->nnotes < 2) { | |
2128 | /* quarter note grace groups are a special case, since they | |
2129 | * don't have a stem (they are for showing prebends). So we | |
2130 | * put the bend opposite the stem of the following group. */ | |
2131 | if (gs1_p->grpvalue == GV_ZERO && gs1_p->basictime == 4 && | |
2132 | gs1_p->next != (struct GRPSYL *) 0) { | |
2133 | return(gs1_p->next->stemdir == UP ? DOWN : UP); | |
2134 | } | |
2135 | return(gs1_p->stemdir == UP ? DOWN : UP); | |
2136 | } | |
2137 | ||
2138 | /* if one voice on staff with more than one note in the group, all | |
2139 | * bend opposite the stem except the top if the stem is up or the | |
2140 | * bottom if the stem is down */ | |
2141 | if ( ((gs1_p->stemdir == DOWN) && (note_index == gs1_p->nnotes - 1)) | |
2142 | || ((gs1_p->stemdir == UP) && (note_index == 0)) ) { | |
2143 | return(gs1_p->stemdir); | |
2144 | } | |
2145 | else { | |
2146 | return(gs1_p->stemdir == UP ? DOWN : UP); | |
2147 | } | |
2148 | } |