Commit | Line | Data |
---|---|---|
69695f33 MW |
1 | |
2 | /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004 by Arkkra Enterprises */ | |
3 | /* All rights reserved */ | |
4 | ||
5 | /* | |
6 | There are 3 classes of location variables: | |
7 | ||
8 | A. Those associated with a specific staff of a specific score. | |
9 | This includes those for GRPSYLs and NOTES. | |
10 | ||
11 | B. Those associated with a specific score. These are for bars. | |
12 | ||
13 | C. Those associated with the current page. These are the builtin | |
14 | variables such as _win and _page, or absolute coordinates. | |
15 | ||
16 | Type B variables, associated with bars, are extra strange. The y of a bar | |
17 | in not particularly useful. If the bar line happens to wind up at the end | |
18 | of a score, special consideration applies, because the right side of the | |
19 | bar is effectively at the beginning of the next score after the clefsig. | |
20 | So here are the rules: | |
21 | ||
22 | 1. If a bar does not occur at the end of a score, the location variable | |
23 | associated with it, if any, is handled normally. | |
24 | ||
25 | 2. If a bar does fall on the end of a score, if the x coordinate derived from | |
26 | a location variable associated with that bar in | |
27 | an INPCOORD, after offsetting, comes out to be either left of the x of | |
28 | the bar, or equal to the x of the bar, it will be handled normally. | |
29 | ||
30 | 3. If a bar falls on the end of a score, and | |
31 | if the x coordinate derived from an INPCOORD, after offsetting, comes out | |
32 | to be right of the x of the bar, | |
33 | the x coordinate will be recalculated using the pseudo | |
34 | bar at the beginning of the following score, | |
35 | and if the y coordinate of the same INPCOORD is also associated | |
36 | with the same bar coordinate, it will also be recalculated from the pseudo bar. | |
37 | ||
38 | 4. If rule 3 would normally apply, but there is no following score, rule 3 | |
39 | will be ignored, and the coordinate will be used as is. | |
40 | ||
41 | A PRHEAD contains only a single location variable, so it should always | |
42 | be taken just as is. | |
43 | ||
44 | Lines and curves are more exciting, since they can have multiple | |
45 | coordinates, and thus may need to be split into 2 or more pieces | |
46 | across scores and/or pages. Lines are a degenerate case of curves, | |
47 | so if we can deal with curves, we've got it made. | |
48 | ||
49 | For drawing a specific curve, here are the rules: | |
50 | ||
51 | 1. If any coordinate is associated with a staff that is invisible, the entire | |
52 | curve will be ignored. | |
53 | ||
54 | 2. Type C variables are always used as is, never themselves causing splitting. | |
55 | Taking any adjacent pair of points in a curve, if either of them is of type C, | |
56 | the line segment between those 2 points will not be split. | |
57 | ||
58 | 3. If all variables of type A and B are on the same score of the same page, | |
59 | then the curve can be printed as is, with no splitting needed. | |
60 | ||
61 | 4. If the x and y components of a single INPCOORD are associated with | |
62 | different scores, this will be an error condition. | |
63 | ||
64 | 5. If the x and y components of a single INPCOORD are associated with | |
65 | different staffs, but the same score, the point will be treated as if | |
66 | it were associated with the staff associated with the y coordinate. | |
67 | ||
68 | 6. If 2 adjacent points of a curve are associated with different | |
69 | scores, the line segment must be split. The number of segments that will | |
70 | need to be generated will be equal to the number of FEEDs between | |
71 | the coordinates plus one. | |
72 | ||
73 | 7. Splitting will only be done to forward scores. In other words, if the | |
74 | coordinates of a curve would require splitting part of the curve onto a | |
75 | preceeding score, that will be an error. This is to keep things simpler, | |
76 | since I can't think of any times this restriction would cause a problem. | |
77 | ||
78 | 8. If a segment needs to be split, the first piece will extend in the | |
79 | x direction from the first point to 0.1 inch left of the right edge of the | |
80 | score associated with the first point. | |
81 | However, if the starting x is already at the right edge of the score, a line of length 0.1 inches will be drawn instead. | |
82 | The last piece of the split line segment will extend in | |
83 | the x direction from the pseudo bar of the clefsig. | |
84 | ||
85 | 8a. If there are additional scores | |
86 | between the one associated with the beginning point and the one associated | |
87 | with the endpoint, for each intervening score a line will be drawn with | |
88 | its x coordinates from the pseudo bar to the right margin. | |
89 | ||
90 | 9. To calculate the y coordinates of each piece of a split line segment, | |
91 | there are several cases. First the easy case, where the y coordinates of | |
92 | the beginning and ending point are both associated with the same staff. | |
93 | Conceptionally, the scores are lined up on a single line without score | |
94 | feeds. The slope of the line is then calculated. The y coordinates of | |
95 | the derived points are then calculated using this slope. Thus, for example, | |
96 | if the ending y coordinate would be A inches from of the beginning y coordinate | |
97 | in the x direction (if they were on the same score), | |
98 | and the line segment is split into 2 segments, with the first having | |
99 | a length in the x direction of B and the second having a length in the x | |
100 | direction of C, the y coordinate of the end of the first segment would be | |
101 | y[begin] + (A/(B+C)) * B, and the y coordinate of the beginning of the second | |
102 | piece would be y[end] - (A/(B+C)) * C. | |
103 | ||
104 | 10. If the y coordinates of the 2 points are associated with different staffs. | |
105 | the slope is calculated based on the distance of the endpoints from their | |
106 | respective staffs. Then for each segment, the slope and endpoints are adjusted | |
107 | based on the ratio of the distance between the two staffs on the current score | |
108 | relative to the widest distance. | |
109 | ||
110 | 11. For purposes of determining y coordinates, the y and n values of a bar | |
111 | are considered to be associated with the top visible score, and the s value | |
112 | is considered to be associated with the bottom visible score. | |
113 | Then rules 9 and 10 above are applied as for with type A coordinates. | |
114 | */ | |
115 | ||
116 | #include "defines.h" | |
117 | #include "structs.h" | |
118 | #include "globals.h" | |
119 | ||
120 | static int Total_pages = 1; /* how many pages of output */ | |
121 | ||
122 | /* if lines must be added for intervening scores, save info about them */ | |
123 | struct SEGINFO { | |
124 | struct MAINLL *mll_p; /* FEED where line segment must go */ | |
125 | double xlength; /* x length prior to current score */ | |
126 | struct SEGINFO *next; /* linked list */ | |
127 | }; | |
128 | struct SEGINFO *Seginfo_p; | |
129 | ||
130 | static void gather_coord_info P((void)); | |
131 | static void save_coord_info P((struct COORD_INFO *coord_info_p, | |
132 | int coordtype, int page, int score, int staff, | |
133 | struct MAINLL *mll_feed_p, int vis)); | |
134 | static void coord_staff P((struct STAFF *staff_p, int page, | |
135 | int score, struct MAINLL *mll_feed_p)); | |
136 | static void split_lines_and_curves P((void)); | |
137 | static void chkline P((struct MAINLL *mll_p)); | |
138 | static void coordcheck P((struct COORD_INFO *x_info_p, | |
139 | struct COORD_INFO *y_info_p, char *fname, int lineno)); | |
140 | static void add_segment P((struct SEGINFO *seginfo_p, double slope, | |
141 | double y_offset, int staffno1, int linetype)); | |
142 | static double find_effXlength P((double seg1xlen, double seg2xlen, | |
143 | struct COORD_INFO *x1info_p, struct COORD_INFO *x2info_p, | |
144 | int save_feed_info)); | |
145 | static void svfeed P((struct MAINLL *mll_feed_p, double xlength)); | |
146 | static int eff_staff P((struct COORD_INFO *yinfo_p)); | |
147 | static double getYstaff P((struct MAINLL *mll_p, int staffno)); | |
148 | static void chkcurve P((struct MAINLL *mll_p)); | |
149 | static int bulgedir P((struct CURVE *curve_p, int index, char *inputfile, | |
150 | int inputlineno)); | |
151 | static int cmpcoords P((struct CURVE *curve_p, int p1, int p2)); | |
152 | static int abs2rel P((int vtype)); | |
153 | static void add_crv_seg P((struct SEGINFO *seginfo_p, double slope, | |
154 | double y_offset, int staffno1, int curvetype, int bulge, | |
155 | char *filename, int lineno)); | |
156 | static int is_invis P((struct COORD_INFO *cinfo_p)); | |
157 | static int is_builtin P((struct COORD_INFO *cinfo_p)); | |
158 | static void move2correct_page P((void)); | |
159 | static void move_it P((struct MAINLL *m_p, struct MAINLL *im_p, int page)); | |
160 | static void move2pseudo P((void)); | |
161 | static void do_pseudo P((struct INPCOORD *inpc_p, struct MAINLL *mll_p)); | |
162 | static void fix_inpcoords P((struct MAINLL *mll_p)); | |
163 | static void adj_coord P((struct INPCOORD *coord_p, struct MAINLL *mll_p, | |
164 | struct INPCOORD *prev_coord_p)); | |
165 | static void calc_bulge P((struct CURVE *curve_p, char *fname, int lineno, | |
166 | int is_split, struct MAINLL *mll_p)); | |
167 | \f | |
168 | ||
169 | /* during parse phase, a table of coordinates associated with location | |
170 | * variables was built. After all the positioning has been done, we need | |
171 | * to go through the main list and stuff off of it checking each coordinate. | |
172 | * If the coordinate is pointed to by something else, we'll need to | |
173 | * save some info about it. Then we have to go through the main list | |
174 | * again and for each line and curve, see whether it needs to be split | |
175 | * into pieces. If so, add LINE or CURVE structs at appropriate places. | |
176 | */ | |
177 | ||
178 | ||
179 | void | |
180 | fix_locvars() | |
181 | ||
182 | { | |
183 | /* first get info about all coordinates with loc variables */ | |
184 | gather_coord_info(); | |
185 | ||
186 | /* move things to pseudo-bar if necessary */ | |
187 | move2pseudo(); | |
188 | ||
189 | /* split any lines and curves that need to be split */ | |
190 | split_lines_and_curves(); | |
191 | ||
192 | /* move anything that is on the wrong page */ | |
193 | move2correct_page(); | |
194 | } | |
195 | \f | |
196 | ||
197 | /* go through everything looking for coordinates. For each one found, if | |
198 | * there is a location tag pointing at it, save info about what the coord | |
199 | * is associated with (bar, note, or group), what page, score and | |
200 | * staff it's on, etc. */ | |
201 | ||
202 | static void | |
203 | gather_coord_info() | |
204 | ||
205 | { | |
206 | struct MAINLL *mll_p; /* to walk through list */ | |
207 | short page = 1; /* which page we're on */ | |
208 | short score = 0; /* which staff on current page */ | |
209 | struct MAINLL *mll_feed_p; /* FEED info for current score */ | |
210 | struct COORD_INFO *coord_info_p; | |
211 | struct COORD_INFO *last_bar_coord_info_p; /* info about the | |
212 | * most recent bar line, in case we | |
213 | * need to attach information about | |
214 | * the pseudo bar at the beginning | |
215 | * of the following score */ | |
216 | ||
217 | ||
218 | debug(32, "gather_coord_info"); | |
219 | ||
220 | initstructs(); | |
221 | last_bar_coord_info_p = (struct COORD_INFO *) 0; | |
222 | /* We know that because of how the main list is set up, we will never | |
223 | * actually access mll_feed_p without setting it first, but compilers | |
224 | * aren't smart enough to know that, and some picky compilers warn | |
225 | * that mll_feed_p could be used without being set, so shut them up. | |
226 | */ | |
227 | mll_feed_p = (struct MAINLL *) 0; | |
228 | ||
229 | for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0; | |
230 | mll_p = mll_p->next) { | |
231 | switch (mll_p->str) { | |
232 | ||
233 | case S_FEED: | |
234 | /* keep track of which page and score we're on */ | |
235 | if (mll_p->u.feed_p->pagefeed == YES) { | |
236 | page++; | |
237 | score = 1; | |
238 | Total_pages++; | |
239 | } | |
240 | else { | |
241 | score++; | |
242 | } | |
243 | if (IS_CLEFSIG_FEED(mll_p)) { | |
244 | mll_feed_p = mll_p; | |
245 | } | |
246 | break; | |
247 | ||
248 | case S_BAR: | |
249 | /* if bar is pointed to, save info about it */ | |
250 | if ((coord_info_p = find_coord(mll_p->u.bar_p->c)) | |
251 | != (struct COORD_INFO *) 0) { | |
252 | ||
253 | save_coord_info(coord_info_p, CT_BAR, | |
254 | page, score, 0, | |
255 | mll_feed_p, YES); | |
256 | last_bar_coord_info_p = coord_info_p; | |
257 | } | |
258 | ||
259 | else { | |
260 | /* no reference to this bar, so no need to | |
261 | * attach pseudo bar info */ | |
262 | last_bar_coord_info_p = (struct COORD_INFO *) 0; | |
263 | } | |
264 | break; | |
265 | ||
266 | case S_CLEFSIG: | |
267 | if (mll_p->u.clefsig_p->bar_p != (struct BAR *) 0) { | |
268 | if (last_bar_coord_info_p != (struct COORD_INFO *) 0) { | |
269 | /* point bar at end of previous score | |
270 | * to the pseudo bar on this score */ | |
271 | last_bar_coord_info_p->pseudo_bar_p | |
272 | = mll_p->u.clefsig_p->bar_p; | |
273 | } | |
274 | ||
275 | /* always save info, because a split curve may | |
276 | * need to refer to it */ | |
277 | add_coord(mll_p->u.clefsig_p->bar_p->c, CT_BAR); | |
278 | coord_info_p = find_coord(mll_p->u.clefsig_p->bar_p->c); | |
279 | save_coord_info(coord_info_p, CT_BAR, | |
280 | page, score, 0, mll_feed_p, YES); | |
281 | } | |
282 | break; | |
283 | ||
284 | case S_STAFF: | |
285 | /* will have to get info for both GRPSYLs and NOTES. */ | |
286 | coord_staff(mll_p->u.staff_p, page, score, mll_feed_p); | |
287 | break; | |
288 | ||
289 | case S_SSV: | |
290 | /* keep track of VISIBLE status */ | |
291 | asgnssv(mll_p->u.ssv_p); | |
292 | break; | |
293 | ||
294 | default: | |
295 | /* nothing else is of interest at this point */ | |
296 | break; | |
297 | } | |
298 | } | |
299 | } | |
300 | \f | |
301 | ||
302 | /* fill in the COORD_INFO table with information about a coordinate. */ | |
303 | ||
304 | static void | |
305 | save_coord_info(coord_info_p, coordtype, page, score, staff, mll_feed_p, vis) | |
306 | ||
307 | struct COORD_INFO *coord_info_p;/* where to add -- assumed | |
308 | * to be non-NULL */ | |
309 | int coordtype; /* CT_BAR, CT_NOTE, etc */ | |
310 | int page; | |
311 | int score; | |
312 | int staff; | |
313 | struct MAINLL *mll_feed_p; /* MAINLL containing FEED | |
314 | * associated with score */ | |
315 | int vis; /* YES if visible */ | |
316 | ||
317 | { | |
318 | if (coord_info_p == (struct COORD_INFO *) 0) { | |
319 | pfatal("invalid coordinate information"); | |
320 | } | |
321 | ||
322 | /* make sure this phase matches parse phase */ | |
323 | if ((coord_info_p->flags & coordtype) == 0) { | |
324 | pfatal("coordinate type mismatch"); | |
325 | } | |
326 | ||
327 | /* save relevant info */ | |
328 | coord_info_p->page = (short) page; | |
329 | coord_info_p->scorenum = (short) score; | |
330 | coord_info_p->staffno = (short) staff; | |
331 | coord_info_p->mll_feed_p = mll_feed_p; | |
332 | if (vis == NO) { | |
333 | coord_info_p->flags |= CT_INVISIBLE; | |
334 | } | |
335 | } | |
336 | \f | |
337 | ||
338 | /* given a STAFF struct, save relevant info about all the GRPSYL | |
339 | * and NOTE coordinates */ | |
340 | ||
341 | static void | |
342 | coord_staff(staff_p, page, score, mll_feed_p) | |
343 | ||
344 | struct STAFF *staff_p; /* get info from here */ | |
345 | int page; | |
346 | int score; | |
347 | struct MAINLL *mll_feed_p; /* FEED associated with this score */ | |
348 | ||
349 | { | |
350 | struct GRPSYL *gs_p; | |
351 | struct COORD_INFO *coord_info_p; | |
352 | int vis; /* YES if staff is visible */ | |
353 | register int n; /* to walk through NOTE lists */ | |
354 | register int v; /* walk through voices/verses */ | |
355 | ||
356 | ||
357 | /* do for each voice */ | |
358 | for (v = 0; v < MAXVOICES; v++) { | |
359 | ||
360 | vis = vvpath(staff_p->staffno, v + 1, VISIBLE)->visible; | |
361 | /* for each GRPSYL in the list */ | |
362 | for (gs_p = staff_p->groups_p[v]; gs_p != (struct GRPSYL *) 0; | |
363 | gs_p = gs_p->next) { | |
364 | ||
365 | /* check its coordinate */ | |
366 | if ((coord_info_p = find_coord(gs_p->c)) | |
367 | != (struct COORD_INFO *) 0) { | |
368 | save_coord_info(coord_info_p, CT_GRPSYL, | |
369 | page, score, gs_p->staffno, | |
370 | mll_feed_p, vis); | |
371 | } | |
372 | ||
373 | /* if has notes, check each note coordinate */ | |
374 | for (n = 0; n < gs_p->nnotes; n++) { | |
375 | ||
376 | if ((coord_info_p = find_coord(gs_p->notelist[n].c)) | |
377 | != (struct COORD_INFO *) 0) { | |
378 | ||
379 | save_coord_info(coord_info_p, CT_NOTE, | |
380 | page, score, | |
381 | gs_p->staffno, | |
382 | mll_feed_p, vis); | |
383 | } | |
384 | } | |
385 | } | |
386 | } | |
387 | } | |
388 | \f | |
389 | ||
390 | /* go down main list. For any lines and curves, see if they need to be | |
391 | * split */ | |
392 | ||
393 | static void | |
394 | split_lines_and_curves() | |
395 | ||
396 | { | |
397 | struct MAINLL *mll_p; /* walk through main list */ | |
398 | ||
399 | ||
400 | debug(16, "split_lines_and_curves"); | |
401 | ||
402 | initstructs(); | |
403 | for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0; | |
404 | mll_p = mll_p->next) { | |
405 | ||
406 | switch(mll_p->str) { | |
407 | ||
408 | case S_LINE: | |
409 | fix_inpcoords(mll_p); | |
410 | chkline(mll_p); | |
411 | break; | |
412 | ||
413 | case S_CURVE: | |
414 | fix_inpcoords(mll_p); | |
415 | chkcurve(mll_p); | |
416 | break; | |
417 | ||
418 | case S_SSV: | |
419 | asgnssv(mll_p->u.ssv_p); | |
420 | break; | |
421 | ||
422 | default: | |
423 | /* ignore everything else */ | |
424 | break; | |
425 | } | |
426 | } | |
427 | } | |
428 | \f | |
429 | ||
430 | /* check whether a LINE needs to be split. If so, split it */ | |
431 | ||
432 | static void | |
433 | chkline(mll_p) | |
434 | ||
435 | struct MAINLL *mll_p; /* points to LINE */ | |
436 | ||
437 | { | |
438 | struct COORD_INFO *x1info_p, *y1info_p; /* info about coordinates | |
439 | * referenced for the beginning of the line */ | |
440 | struct COORD_INFO *x2info_p, *y2info_p; /* same for end of line */ | |
441 | struct LINE *line_p; /* the line being processed */ | |
442 | struct MAINLL *new_mll_p; /* new main list struct to add | |
443 | * if line has to be split */ | |
444 | struct LINE *end_line_p; /* new LINE struct to hang off | |
445 | * of new_mll_p if the line | |
446 | * has to be split */ | |
447 | double offset; | |
448 | struct MAINLL *mll_clefsig_p; /* clefsig before a continued | |
449 | * line segment */ | |
450 | struct MAINLL *m_p; /* for finding BAR */ | |
451 | char *fname; /* file name for messages */ | |
452 | int lineno; /* line # for messages */ | |
453 | double seg1xlen, seg2xlen; /* lengths of split segments */ | |
454 | double effective_x_len; /* effective horizontal | |
455 | * distance of line, adding | |
456 | * the split segments */ | |
457 | double slope; /* of effective line */ | |
458 | int p1staff, p2staff; /* effective staff associated | |
459 | * with y coord of line ends */ | |
460 | struct SEGINFO *seg_p; /* walk through segment list */ | |
461 | struct SEGINFO *to_free_p; /* which is to be freed */ | |
462 | ||
463 | ||
464 | /* if we added this line internally, it's already split, so no | |
465 | * more to check on it */ | |
466 | if (mll_p->inputlineno <= 0) { | |
467 | return; | |
468 | } | |
469 | ||
470 | Seginfo_p = (struct SEGINFO *) 0; | |
471 | ||
472 | /* get relevant info about each referenced coordinate */ | |
473 | line_p = mll_p->u.line_p; | |
474 | x1info_p = find_coord(line_p->start.hor_p); | |
475 | y1info_p = find_coord(line_p->start.vert_p); | |
476 | x2info_p = find_coord(line_p->end.hor_p); | |
477 | y2info_p = find_coord(line_p->end.vert_p); | |
478 | ||
479 | if (x1info_p == (struct COORD_INFO *) 0 | |
480 | || y1info_p == (struct COORD_INFO *) 0 | |
481 | || x2info_p == (struct COORD_INFO *) 0 | |
482 | || y2info_p == (struct COORD_INFO *) 0) { | |
483 | /* must be an absolute coordinate */ | |
484 | return; | |
485 | } | |
486 | ||
487 | fname = mll_p->inputfile; | |
488 | lineno = mll_p->inputlineno; | |
489 | ||
490 | /* rule 1: if any invisible, ignore */ | |
491 | if ( is_invis(x1info_p) || is_invis(y1info_p) || is_invis(x2info_p) | |
492 | || is_invis(y2info_p) ) { | |
493 | /* not to be printed, so remove from main list */ | |
494 | unlinkMAINLL(mll_p); | |
495 | /* don't free the space, since this way the function that | |
496 | * called us can still do mll_p->next to get to the next | |
497 | * item in list. The space will never get reclaimed, but | |
498 | * this case will be hit so rarely anyway, who cares | |
499 | * about a few dead bytes? */ | |
500 | return; | |
501 | } | |
502 | ||
503 | /* rule 2: | |
504 | * if there are any references to a builtin variable (like _cur) | |
505 | * then there will be no split */ | |
506 | if ( is_builtin(x1info_p) || is_builtin(y1info_p) | |
507 | || is_builtin(x2info_p) || is_builtin(y2info_p) ) { | |
508 | return; | |
509 | } | |
510 | ||
511 | /* rule 3: | |
512 | * if all references are on same page and score, no split needed */ | |
513 | if ( (x1info_p->scorenum == y1info_p->scorenum) | |
514 | && (x1info_p->scorenum == x2info_p->scorenum) | |
515 | && (x1info_p->scorenum == y2info_p->scorenum) | |
516 | && (x1info_p->page == y1info_p->page) | |
517 | && (x1info_p->page == x2info_p->page) | |
518 | && (x1info_p->page == y2info_p->page) ) { | |
519 | return; | |
520 | } | |
521 | ||
522 | /* rule 4: | |
523 | * If x and y of a single INPCOORD are associated with different | |
524 | * scores, we give up. (coordcheck ufatals if x and y are on | |
525 | * different scores.) | |
526 | */ | |
527 | coordcheck(x1info_p, y1info_p, fname, lineno); | |
528 | coordcheck(x2info_p, y2info_p, fname, lineno); | |
529 | ||
530 | /* rule 5: | |
531 | * if x and y are associated with different staffs, | |
532 | * make effective staff that of the y coordinate. */ | |
533 | /* figure out which staff the beginning is associated with */ | |
534 | p1staff = eff_staff(y1info_p); | |
535 | ||
536 | /* figure out which staff end of line is associated with */ | |
537 | p2staff = eff_staff(y2info_p); | |
538 | ||
539 | /* rule 6: | |
540 | * Arrrgh! The line will have to be split. No specific code to do | |
541 | * for this rule...the mere fact that we are here indicates rule 6 | |
542 | * has been satisfied */ | |
543 | ||
544 | /* rule 7: | |
545 | * Make sure x2 is not behind x1. */ | |
546 | if (x2info_p->page < x1info_p->page || | |
547 | (x2info_p->page == x1info_p->page && | |
548 | x2info_p->scorenum < x1info_p->scorenum)) { | |
549 | l_ufatal(fname, lineno, | |
550 | "can't draw line backwards to previous score"); | |
551 | } | |
552 | ||
553 | /* So... there will have to be at least 1 more LINE struct | |
554 | * (more if the end is more than 1 score away) */ | |
555 | new_mll_p = newMAINLLstruct(S_LINE, -1); | |
556 | new_mll_p->inputfile = mll_p->inputfile; | |
557 | end_line_p = new_mll_p->u.line_p; | |
558 | end_line_p->linetype = (short) line_p->linetype; | |
559 | ||
560 | /* the new LINE will have its end equal to what the original LINE had */ | |
561 | end_line_p->end = line_p->end; | |
562 | ||
563 | /* Start out with end of first segment the same as its | |
564 | * start. Later, we'll add appropriate x and y offsets. */ | |
565 | line_p->end = line_p->start; | |
566 | ||
567 | /* start out with last segment's beginning the same as its end. | |
568 | * In a bit, we'll adjust the x and y appropriately. */ | |
569 | end_line_p->start = end_line_p->end; | |
570 | ||
571 | /* rule 8: | |
572 | * finding the x's of the new pieces isn't too bad... */ | |
573 | ||
574 | /* the end x of the first segment is just like the beginning x, | |
575 | * but offset to the east far enough to | |
576 | * reach the end of the score. */ | |
577 | seg1xlen = PGWIDTH - eff_rightmargin(mll_p) - inpc_x( &(line_p->start), | |
578 | fname, lineno ); | |
579 | ||
580 | /* handle bizarre case of beginning being too far right to deal | |
581 | * with properly */ | |
582 | if (seg1xlen < 0.1) { | |
583 | seg1xlen = 0.1; | |
584 | } | |
585 | /* convert inches to stepsizes, which is how offset are stored */ | |
586 | line_p->end.hsteps += seg1xlen / STEPSIZE; | |
587 | ||
588 | /* the begin x of the last segment is at the pseudo-bar */ | |
589 | /* The relevant clefsig should be immediately after the FEED | |
590 | * associated with y2 */ | |
591 | mll_clefsig_p = y2info_p->mll_feed_p->next; | |
592 | if (mll_clefsig_p->str != S_CLEFSIG) { | |
593 | pfatal("missing clefsig info after newscore"); | |
594 | } | |
595 | ||
596 | /* fill in x of beginning of final segment based on the pseudo-bar */ | |
597 | end_line_p->start.hor_p = mll_clefsig_p->u.clefsig_p->bar_p->c; | |
598 | end_line_p->start.htype = AX; | |
599 | end_line_p->start.hsteps = 0.0; | |
600 | end_line_p->start.counts = 0.0; | |
601 | ||
602 | /* effective distance in x direction will be the sum of the lengths of | |
603 | * the first and last line segments and any intervening. We already | |
604 | * know the length of the first segment and and now | |
605 | * determine the lengths of the last segment. */ | |
606 | seg1xlen = inpc_x( &(line_p->end), fname, lineno) | |
607 | - inpc_x( &(line_p->start), fname, lineno); | |
608 | seg2xlen = inpc_x( &(end_line_p->end), fname, lineno) | |
609 | - inpc_x( &(end_line_p->start), fname, lineno); | |
610 | ||
611 | /* rule 8a */ | |
612 | /* check for intervening scores and find the effective length in | |
613 | * the X direction. */ | |
614 | effective_x_len = find_effXlength(seg1xlen, seg2xlen, x1info_p, | |
615 | x2info_p, YES); | |
616 | ||
617 | /* now find y values */ | |
618 | ||
619 | /* figure out the first segment y relative to the effective staff */ | |
620 | for (m_p = x1info_p->mll_feed_p; m_p != (struct MAINLL *) 0; | |
621 | m_p = m_p->next) { | |
622 | if (m_p->str == S_STAFF && | |
623 | m_p->u.staff_p->staffno == p1staff) { | |
624 | break; | |
625 | } | |
626 | } | |
627 | offset = inpc_y( &(line_p->start), fname, lineno) | |
628 | - m_p->u.staff_p->c[AY]; | |
629 | ||
630 | /* rule 9: | |
631 | * First we tackle the easy (relatively speaking) case of both | |
632 | * coordinates being associated with the same staff. */ | |
633 | if (p1staff == p2staff) { | |
634 | ||
635 | /* calculate y values based on slope */ | |
636 | slope = ((end_line_p->end.vert_p[RY] | |
637 | + end_line_p->end.vsteps * STEPSIZE) - | |
638 | (line_p->start.vert_p[RY] | |
639 | + line_p->start.vsteps * STEPSIZE)) | |
640 | / effective_x_len; | |
641 | ||
642 | /* use the slope to the end y of the first segment and | |
643 | * begin y of the last segment, converted to stepsizes */ | |
644 | line_p->end.vsteps += (slope * seg1xlen) / STEPSIZE; | |
645 | end_line_p->start.vsteps -= (slope * seg2xlen) / STEPSIZE; | |
646 | ||
647 | /* if need more than 2 line segments | |
648 | * do the rest of them */ | |
649 | for (seg_p = Seginfo_p; seg_p != (struct SEGINFO *) 0; ) { | |
650 | ||
651 | add_segment(seg_p, slope, offset, p1staff, | |
652 | line_p->linetype); | |
653 | ||
654 | /* move on the next segment in list, if any. First | |
655 | * remember current one so we can free it, then move | |
656 | * to next, then free the one we just finished with */ | |
657 | to_free_p = seg_p; | |
658 | seg_p = seg_p->next; | |
659 | FREE(to_free_p); | |
660 | } | |
661 | } | |
662 | ||
663 | else { | |
664 | /* ends are associated with different staffs */ | |
665 | double y1, y2; | |
666 | ||
667 | /* find two slopes, one for the beginning line segment, one | |
668 | * for the end. For each, base the slope on the distance | |
669 | * between the two effective staffs, adjusted by the | |
670 | * appropriate offset from those staffs. */ | |
671 | y1 = end_line_p->end.vert_p[RY] | |
672 | + end_line_p->end.vsteps * STEPSIZE; | |
673 | y2 = line_p->start.vert_p[RY] | |
674 | + line_p->start.vsteps * STEPSIZE; | |
675 | slope = ( (getYstaff(y1info_p->mll_feed_p, p2staff) + y1) - | |
676 | (getYstaff(y1info_p->mll_feed_p, p1staff) + y2) ) | |
677 | / effective_x_len; | |
678 | line_p->end.vsteps += (slope * seg1xlen) / STEPSIZE; | |
679 | ||
680 | slope = ( (getYstaff(y2info_p->mll_feed_p, p2staff) + y1) - | |
681 | (getYstaff(y2info_p->mll_feed_p, p1staff) + y2) ) | |
682 | / effective_x_len; | |
683 | end_line_p->start.vsteps -= (slope * seg2xlen) / STEPSIZE; | |
684 | ||
685 | /* if need more than 2 line segments | |
686 | * do the rest of them */ | |
687 | for (seg_p = Seginfo_p; seg_p != (struct SEGINFO *) 0; ) { | |
688 | ||
689 | slope = ( (getYstaff(seg_p->mll_p, p2staff) + y1) - | |
690 | (getYstaff(seg_p->mll_p, p1staff) + y2) ) | |
691 | / effective_x_len; | |
692 | ||
693 | add_segment(seg_p, slope, offset, p1staff, | |
694 | line_p->linetype); | |
695 | ||
696 | /* move on the next segment in list, if any. First | |
697 | * remember current one so we can free it, then move | |
698 | * to next, then free the one we just finished with */ | |
699 | to_free_p = seg_p; | |
700 | seg_p = seg_p->next; | |
701 | FREE(to_free_p); | |
702 | } | |
703 | } | |
704 | ||
705 | /* link end_line_p into proper place in main list */ | |
706 | /* this will be right before the first BAR after the FEED associated | |
707 | * with y2 */ | |
708 | for (m_p = mll_clefsig_p->next; m_p->str != S_BAR; m_p = m_p->next) { | |
709 | ; | |
710 | } | |
711 | insertMAINLL(new_mll_p, m_p->prev); | |
712 | } | |
713 | \f | |
714 | ||
715 | /* check if location variables associated with an x and y point to at least | |
716 | * the same score on the same page. If not, give up */ | |
717 | ||
718 | static void | |
719 | coordcheck(x_info_p, y_info_p, fname, lineno) | |
720 | ||
721 | struct COORD_INFO *x_info_p; | |
722 | struct COORD_INFO *y_info_p; | |
723 | char *fname; | |
724 | int lineno; | |
725 | ||
726 | { | |
727 | if (x_info_p == (struct COORD_INFO *) 0 || | |
728 | y_info_p == (struct COORD_INFO *) 0) { | |
729 | pfatal("coordinate not in table\n"); | |
730 | } | |
731 | ||
732 | if ( (x_info_p->flags & CT_BUILTIN) || (y_info_p->flags & CT_BUILTIN)) { | |
733 | /* if any reference to builtin tag, leave as is */ | |
734 | return; | |
735 | } | |
736 | ||
737 | if ( (x_info_p->scorenum != y_info_p->scorenum) | |
738 | || (x_info_p->page != y_info_p->page) ) { | |
739 | l_ufatal(fname, lineno, | |
740 | "x and y cannot be associated with different scores"); | |
741 | } | |
742 | } | |
743 | \f | |
744 | ||
745 | /* given info about a coord, return its effective staff. This is the staff | |
746 | * associated with the info if any, otherwise the top visible staff */ | |
747 | ||
748 | static int | |
749 | eff_staff(yinfo_p) | |
750 | ||
751 | struct COORD_INFO *yinfo_p; | |
752 | ||
753 | { | |
754 | int staff; | |
755 | ||
756 | ||
757 | if (yinfo_p->staffno != 0) { | |
758 | staff = yinfo_p->staffno; | |
759 | } | |
760 | else { | |
761 | /* use top visible staff as effective staff */ | |
762 | for (staff = 1; staff <= Score.staffs; staff++) { | |
763 | if (svpath(staff, VISIBLE)->visible == YES) { | |
764 | break; | |
765 | } | |
766 | } | |
767 | } | |
768 | return(staff); | |
769 | } | |
770 | \f | |
771 | ||
772 | /* find the total effective length of a line or curve, accounting for all | |
773 | * intervening scores. For each intermediate score, if the save_feed_info | |
774 | * flag is set, save away information for use in adding | |
775 | * a line or curve for that score */ | |
776 | ||
777 | static double | |
778 | find_effXlength(seg1xlen, seg2xlen, x1info_p, x2info_p, save_feed_info) | |
779 | ||
780 | double seg1xlen; /* length of first part */ | |
781 | double seg2xlen; /* length of last part */ | |
782 | struct COORD_INFO *x1info_p; /* info about beginning point */ | |
783 | struct COORD_INFO *x2info_p; /* info about last point */ | |
784 | int save_feed_info; /* if YES, do svfeed() call, otherwise not */ | |
785 | ||
786 | { | |
787 | double effective_x_len; | |
788 | struct MAINLL *m_p; /* to search main list */ | |
789 | ||
790 | ||
791 | /* start out with length of first segment */ | |
792 | effective_x_len = seg1xlen; | |
793 | ||
794 | /* check if there might be one or more intervening scores. If the | |
795 | * end point is on the next page, there might be. If both are on | |
796 | * the same page, with the first having a scorenum greater than | |
797 | * the first one plus one, then there is an intervening score | |
798 | * for sure. */ | |
799 | if (x2info_p->page > x1info_p->page || | |
800 | (x2info_p->page == x1info_p->page && | |
801 | x2info_p->scorenum > x1info_p->scorenum + 1)) { | |
802 | /* search forward in main list. Every time we find | |
803 | * a matching newscore that isn't the one associated with | |
804 | * the last segment, save info to be able to | |
805 | * add an intervening line. Also add the length of that line | |
806 | * to the effective x length. */ | |
807 | for (m_p = x1info_p->mll_feed_p->next; | |
808 | m_p != (struct MAINLL *) 0; m_p = m_p->next) { | |
809 | if (IS_CLEFSIG_FEED(m_p)) { | |
810 | if (m_p == x2info_p->mll_feed_p) { | |
811 | /* hurray! We found the score with | |
812 | * the last line segment. No more to | |
813 | * add */ | |
814 | break; | |
815 | } | |
816 | else { | |
817 | /* need to add another line segment */ | |
818 | if (m_p->next != (struct MAINLL *) 0 && | |
819 | m_p->next->str == S_CLEFSIG && | |
820 | m_p->next->u.clefsig_p->bar_p | |
821 | != (struct BAR *) 0) { | |
822 | if (save_feed_info == YES) { | |
823 | svfeed(m_p, effective_x_len); | |
824 | } | |
825 | effective_x_len += PGWIDTH | |
826 | - eff_rightmargin(m_p) - | |
827 | m_p->next->u.clefsig_p->bar_p->c[AX]; | |
828 | } | |
829 | else { | |
830 | pfatal("error in main list while splitting lines"); | |
831 | } | |
832 | } | |
833 | } | |
834 | } | |
835 | } | |
836 | ||
837 | /* add in length of final segment */ | |
838 | effective_x_len += seg2xlen; | |
839 | ||
840 | return(effective_x_len); | |
841 | } | |
842 | \f | |
843 | ||
844 | /* allocate SEGINFO and fill it in */ | |
845 | ||
846 | static void | |
847 | svfeed(mll_feed_p, xlength) | |
848 | ||
849 | struct MAINLL *mll_feed_p; | |
850 | double xlength; | |
851 | ||
852 | { | |
853 | struct SEGINFO *new_p; | |
854 | ||
855 | ||
856 | MALLOC(SEGINFO, new_p, 1); | |
857 | new_p->mll_p = mll_feed_p; | |
858 | new_p->xlength = xlength; | |
859 | ||
860 | /* link onto list */ | |
861 | new_p->next = Seginfo_p; | |
862 | Seginfo_p = new_p; | |
863 | } | |
864 | \f | |
865 | ||
866 | /* add LINE for intervening scores */ | |
867 | ||
868 | static void | |
869 | add_segment(seginfo_p, slope, y_offset, staffno1, linetype) | |
870 | ||
871 | struct SEGINFO *seginfo_p; | |
872 | double slope; | |
873 | double y_offset; /* offset from staff of beginning point */ | |
874 | int staffno1; /* staff associated with y of beginning */ | |
875 | int linetype; | |
876 | ||
877 | { | |
878 | struct MAINLL *m_p; /* index through main list */ | |
879 | struct MAINLL *new_mll_p; /* points to new LINE */ | |
880 | struct LINE *new_line_p; /* LINE connected to new_mll_p */ | |
881 | double xleng; /* distance to end in x direction */ | |
882 | ||
883 | ||
884 | /* create a new LINE */ | |
885 | new_mll_p = newMAINLLstruct(S_LINE, -1); | |
886 | new_line_p = new_mll_p->u.line_p; | |
887 | new_line_p->linetype = linetype; | |
888 | ||
889 | /* x coords of the line are at the pseudobar and the rightmargin. We | |
890 | * get to the right margin by adding the correct number of stepsizes | |
891 | * from the pseudobar */ | |
892 | new_line_p->start.hor_p = seginfo_p->mll_p->next->u.clefsig_p->bar_p->c; | |
893 | new_line_p->start.htype = AX; | |
894 | new_line_p->start.hsteps = 0.0; | |
895 | new_line_p->start.counts = 0.0; | |
896 | new_line_p->end.hor_p = new_line_p->start.hor_p; | |
897 | new_line_p->end.htype = AX; | |
898 | xleng = PGWIDTH - eff_rightmargin(seginfo_p->mll_p) | |
899 | - new_line_p->start.hor_p[AX]; | |
900 | new_line_p->end.hsteps = xleng / STEPSIZE; | |
901 | new_line_p->end.counts = 0.0; | |
902 | ||
903 | /* find staff coord info */ | |
904 | for (m_p = seginfo_p->mll_p; m_p != (struct MAINLL *) 0; | |
905 | m_p = m_p->next) { | |
906 | if (m_p->str == S_STAFF && m_p->u.staff_p->staffno == staffno1) { | |
907 | break; | |
908 | } | |
909 | } | |
910 | /* y coords are determined from the slope */ | |
911 | new_line_p->start.vert_p = m_p->u.staff_p->c; | |
912 | new_line_p->start.vtype = AY; | |
913 | new_line_p->start.vsteps = (y_offset + (slope * seginfo_p->xlength)) | |
914 | / STEPSIZE; | |
915 | new_line_p->end.vert_p = m_p->u.staff_p->c; | |
916 | new_line_p->end.vtype = AY; | |
917 | new_line_p->end.vsteps = (y_offset + | |
918 | (slope * (xleng + seginfo_p->xlength))) / STEPSIZE; | |
919 | ||
920 | /* link into proper place in main list */ | |
921 | /* this will be right before the first BAR after the FEED */ | |
922 | for (m_p = seginfo_p->mll_p->next; m_p != (struct MAINLL *) 0; | |
923 | m_p = m_p->next) { | |
924 | if (m_p->str == S_BAR) { | |
925 | break; | |
926 | } | |
927 | } | |
928 | ||
929 | if (m_p == (struct MAINLL *) 0) { | |
930 | pfatal("couldn't find bar while adding line segment"); | |
931 | } | |
932 | ||
933 | insertMAINLL(new_mll_p, m_p->prev); | |
934 | } | |
935 | \f | |
936 | ||
937 | /* given a MAINLL and a staff number, return the absolute Y of the staff | |
938 | * searching forward from the MAINLL */ | |
939 | ||
940 | static double | |
941 | getYstaff(mll_p, staffno) | |
942 | ||
943 | struct MAINLL *mll_p; | |
944 | int staffno; | |
945 | ||
946 | { | |
947 | for ( ; mll_p != (struct MAINLL *) 0; mll_p = mll_p->next) { | |
948 | if (mll_p->str == S_STAFF) { | |
949 | if (mll_p->u.staff_p->staffno == staffno) { | |
950 | return(mll_p->u.staff_p->c[AY]); | |
951 | } | |
952 | } | |
953 | } | |
954 | pfatal("couldn't find Y of staff"); | |
955 | /*NOTREACHED*/ | |
956 | return(0.0); | |
957 | } | |
958 | \f | |
959 | ||
960 | /* check whether a CURVE needs to be split. If so, split it */ | |
961 | ||
962 | static void | |
963 | chkcurve(mll_p) | |
964 | ||
965 | struct MAINLL *mll_p; /* points to CURVE */ | |
966 | ||
967 | { | |
968 | struct CURVE *curve_p; | |
969 | struct COORD_INFO *x1info_p, *y1info_p, *x2info_p, *y2info_p; | |
970 | int bulge; /* 1 for UP or -1 for DOWN */ | |
971 | register int n; | |
972 | int j; | |
973 | int curscore, curpage; /* current score and page */ | |
974 | int is_split = NO; | |
975 | int p1staff, p2staff; /* staff associate with each endpoint */ | |
976 | struct MAINLL *new_mll_p; /* place for 2nd part of split curve */ | |
977 | struct MAINLL *m_p; /* to find place in list to insert */ | |
978 | struct CURVE *new_crv_p; /* points for second part */ | |
979 | int ncoord1, ncoord2; /* number of coords in each piece */ | |
980 | int add1, add2; /* 1 if need to add another point to the | |
981 | * first of second piece of the curve, | |
982 | * 0 if not */ | |
983 | float offset; | |
984 | struct MAINLL *mll_clefsig_p; /* clefsig for score where part of | |
985 | * a curve goes */ | |
986 | double seg1xlen, seg2xlen; /* length of begin and end parts */ | |
987 | double effective_x_len; /* total length in X direction */ | |
988 | double slope; /* of line */ | |
989 | int index1, index2; /* into coordlist array */ | |
990 | char *fname; | |
991 | int lineno; | |
992 | struct SEGINFO *seg_p, *to_free_p; /* to deal with curves for | |
993 | * intermediate scores */ | |
994 | double addedx; /* x of endpoint that we added */ | |
995 | double userx; /* x of nearest user-defined point */ | |
996 | double y1, y2; | |
997 | ||
998 | ||
999 | curve_p = mll_p->u.curve_p; | |
1000 | fname = mll_p->inputfile; | |
1001 | lineno = mll_p->inputlineno; | |
1002 | curscore = curpage = -1; | |
1003 | ||
1004 | Seginfo_p = (struct SEGINFO *) 0; | |
1005 | ||
1006 | for (n = 0; n < curve_p->ncoord; n++) { | |
1007 | ||
1008 | x1info_p = find_coord( curve_p->coordlist[n].hor_p); | |
1009 | y1info_p = find_coord( curve_p->coordlist[n].vert_p); | |
1010 | ||
1011 | if (x1info_p == (struct COORD_INFO *) 0 || | |
1012 | y1info_p == (struct COORD_INFO *) 0) { | |
1013 | /* must be an absolute coordinate */ | |
1014 | continue; | |
1015 | } | |
1016 | ||
1017 | /* rule 1: if any coordinate on the list | |
1018 | * is associated with something | |
1019 | * invisible, ignore the whole curve. */ | |
1020 | if ( is_invis(x1info_p) || is_invis(y1info_p) ) { | |
1021 | ||
1022 | /* as with lines, unlink so we don't print, but | |
1023 | * don't free, so that calling function can still | |
1024 | * follow the ->next pointer */ | |
1025 | unlinkMAINLL(mll_p); | |
1026 | return; | |
1027 | } | |
1028 | ||
1029 | /* rule 4. Check that x and y are on same score */ | |
1030 | coordcheck(x1info_p, y1info_p, fname, lineno); | |
1031 | ||
1032 | /* rule 3 checking. See if all on same score/page */ | |
1033 | if (curpage == -1) { | |
1034 | curscore = x1info_p->scorenum; | |
1035 | curpage = x1info_p->page; | |
1036 | } | |
1037 | else { | |
1038 | if (curscore != x1info_p->scorenum || | |
1039 | curpage != x1info_p->page) { | |
1040 | is_split = YES; | |
1041 | } | |
1042 | } | |
1043 | } | |
1044 | ||
1045 | /* If this curve was specified using bulge parameters, go calculate | |
1046 | * the intermediate points */ | |
1047 | if (curve_p->nbulge > 0) { | |
1048 | calc_bulge(curve_p, fname, lineno, is_split, mll_p); | |
1049 | /* some INPCOORDs might well point off the page, so fix those */ | |
1050 | fix_inpcoords(mll_p); | |
1051 | } | |
1052 | ||
1053 | /* finish rule 3 checking. If all were on same score, we are done */ | |
1054 | if (is_split == NO) { | |
1055 | return; | |
1056 | } | |
1057 | ||
1058 | /* go through curve points a pair at a time */ | |
1059 | for (n = 0; n < curve_p->ncoord - 1; n++) { | |
1060 | x1info_p = find_coord( curve_p->coordlist[n].hor_p); | |
1061 | y1info_p = find_coord( curve_p->coordlist[n].vert_p); | |
1062 | x2info_p = find_coord( curve_p->coordlist[n + 1].hor_p); | |
1063 | y2info_p = find_coord( curve_p->coordlist[n + 1].vert_p); | |
1064 | ||
1065 | if (x1info_p == (struct COORD_INFO *) 0 | |
1066 | || y1info_p == (struct COORD_INFO *) 0 | |
1067 | || x2info_p == (struct COORD_INFO *) 0 | |
1068 | || y2info_p == (struct COORD_INFO *) 0) { | |
1069 | /* absolute coordinate */ | |
1070 | continue; | |
1071 | } | |
1072 | ||
1073 | /* rule 2. If any builtin variable used, | |
1074 | * no split of this segment */ | |
1075 | if ( is_builtin(x1info_p) || is_builtin(y1info_p) || | |
1076 | is_builtin(x2info_p) || is_builtin(y2info_p) ) { | |
1077 | continue; | |
1078 | } | |
1079 | ||
1080 | /* rule 6. If both ends of segment are on same page/score | |
1081 | * no split of this segment */ | |
1082 | if ( (x1info_p->scorenum == y1info_p->scorenum) | |
1083 | && (x1info_p->scorenum == x2info_p->scorenum) | |
1084 | && (x1info_p->scorenum == y2info_p->scorenum) | |
1085 | && (x1info_p->page == y1info_p->page) | |
1086 | && (x1info_p->page == x2info_p->page) | |
1087 | && (x1info_p->page == y2info_p->page) ) { | |
1088 | continue; | |
1089 | } | |
1090 | ||
1091 | /* rule 7. Only split to forward score */ | |
1092 | if (x2info_p->page < x1info_p->page || | |
1093 | (x2info_p->page == x1info_p->page && | |
1094 | x2info_p->scorenum < x1info_p->scorenum)) { | |
1095 | l_ufatal(mll_p->inputfile, mll_p->inputlineno, | |
1096 | "can't draw curve backwards to previous score"); | |
1097 | } | |
1098 | ||
1099 | /* if we're here, segment must be split */ | |
1100 | ||
1101 | /* figure out if curve generally bulges up or down */ | |
1102 | bulge = bulgedir(curve_p, n, mll_p->inputfile, | |
1103 | mll_p->inputlineno); | |
1104 | ||
1105 | /* get effective staffs */ | |
1106 | p1staff = eff_staff(y1info_p); | |
1107 | p2staff = eff_staff(y2info_p); | |
1108 | ||
1109 | /* set up first part of split curve. It will have as many | |
1110 | * coords as we have so far, unless that is only 2, in which | |
1111 | * case we have to add another, because a curve must have at | |
1112 | * least three points */ | |
1113 | if (n == 0) { | |
1114 | ncoord1 = 3; | |
1115 | add1 = 1; | |
1116 | } | |
1117 | else { | |
1118 | ncoord1 = n + 2; | |
1119 | add1 = 0; | |
1120 | } | |
1121 | /* similarly, the second portion has as many points as are | |
1122 | * left, or a minimum of 3 */ | |
1123 | if (curve_p->ncoord - n == 2) { | |
1124 | ncoord2 = 3; | |
1125 | add2 = 1; | |
1126 | } | |
1127 | else { | |
1128 | ncoord2 = curve_p->ncoord - n; | |
1129 | add2 = 0; | |
1130 | } | |
1131 | ||
1132 | /* Split off the second part into a separate curve */ | |
1133 | new_mll_p = newMAINLLstruct(S_CURVE, mll_p->inputlineno); | |
1134 | new_mll_p->inputfile = mll_p->inputfile; | |
1135 | new_crv_p = new_mll_p->u.curve_p; | |
1136 | new_crv_p->curvetype = curve_p->curvetype; | |
1137 | new_crv_p->ncoord = (short) ncoord2; | |
1138 | MALLOC (INPCOORD, new_crv_p->coordlist, ncoord2); | |
1139 | ||
1140 | /* copy second part into second curve. Copy backwards from | |
1141 | * the end, but don't fill in the first point of it, because | |
1142 | * we still need to calculate that */ | |
1143 | for (ncoord2--, j = curve_p->ncoord - 1; ncoord2 > 0 + add2; | |
1144 | ncoord2--, j--) { | |
1145 | new_crv_p->coordlist[ncoord2] = curve_p->coordlist[j]; | |
1146 | } | |
1147 | ||
1148 | /* realloc space for first part of curve, with just the | |
1149 | * points needed */ | |
1150 | REALLOC(INPCOORD, curve_p->coordlist, ncoord1); | |
1151 | curve_p->ncoord = (short) ncoord1; | |
1152 | ||
1153 | /* find new endpoints for the split ends */ | |
1154 | ||
1155 | /* the end x of the first segment is just like the beginning x, | |
1156 | * but offset to the east far enough to | |
1157 | * reach the end of the score. */ | |
1158 | curve_p->coordlist[ncoord1 - 1] = curve_p->coordlist[0]; | |
1159 | offset = PGWIDTH - eff_rightmargin(mll_p) | |
1160 | - inpc_x( &(curve_p->coordlist[0]), | |
1161 | fname, lineno ); | |
1162 | ||
1163 | /* handle bizarre case of beginning being too far right to deal | |
1164 | * with properly */ | |
1165 | if (offset < 0.1) { | |
1166 | offset = 0.1; | |
1167 | } | |
1168 | /* convert inches to stepsizes, | |
1169 | * which is how offset are stored */ | |
1170 | curve_p->coordlist[ncoord1 - 1].hsteps | |
1171 | += offset / STEPSIZE; | |
1172 | ||
1173 | /* the begin x of the last segment is at the pseudo-bar */ | |
1174 | /* The relevant clefsig should be immediately after the FEED | |
1175 | * associated with y2 */ | |
1176 | mll_clefsig_p = y2info_p->mll_feed_p->next; | |
1177 | if (mll_clefsig_p->str != S_CLEFSIG) { | |
1178 | pfatal("missing clefsig info after newscore"); | |
1179 | } | |
1180 | ||
1181 | ||
1182 | /* fill in x of beginning of final part based | |
1183 | * on the pseudo-bar */ | |
1184 | new_crv_p->coordlist[0].hor_p | |
1185 | = mll_clefsig_p->u.clefsig_p->bar_p->c; | |
1186 | new_crv_p->coordlist[0].htype = AX; | |
1187 | new_crv_p->coordlist[0].hsteps = 0.0; | |
1188 | new_crv_p->coordlist[0].counts = 0.0; | |
1189 | ||
1190 | /* If the first user defined point on the subsequent score | |
1191 | * is extremely close to the pseudo-bar where we want to | |
1192 | * start this segment, or worse yet, is west of it | |
1193 | * (because they specified a negative offset that makes the | |
1194 | * curve bend back into the preceeding measure), | |
1195 | * we move the beginning point that we added, | |
1196 | * to make it 0.1 inch west of the user's point. | |
1197 | * This is not the same remedial action as we take later for | |
1198 | * the somewhat similar case at the end of the preceeding score. | |
1199 | * The argument for this lack of symmetry is that when | |
1200 | * carrying out from the end of a score, we don't want to | |
1201 | * spill out into the margin--it's better to end the curve | |
1202 | * a tiny bit too early. On the other hand, at the beginning | |
1203 | * of a score, there is probably some room in the | |
1204 | * clef/key/time area to allow starting somewhat earlier | |
1205 | * than the pseudo-bar and still look okay. | |
1206 | * Also, if the user did do some crazy curve that would | |
1207 | * bend back into the preceeding measure, it's just too | |
1208 | * hard to try to do anything about that on the preceeding | |
1209 | * score, but we can make it bend back prior to the | |
1210 | * pseudobar, which can sort of honor what they asked for. | |
1211 | */ | |
1212 | addedx = inpc_x( &(new_crv_p->coordlist[0]), fname, lineno ); | |
1213 | userx = inpc_x( &(new_crv_p->coordlist[1+add2]), fname, lineno ); | |
1214 | if (userx - addedx < 0.1) { | |
1215 | new_crv_p->coordlist[0].hsteps = | |
1216 | (-0.1 + (userx - addedx)) / STEPSIZE; | |
1217 | } | |
1218 | ||
1219 | /* use the last user defined point (the one immediately | |
1220 | * before the one or two points we just added to the | |
1221 | * first part) as a reference point */ | |
1222 | index1 = (add1 ? 0 : curve_p->ncoord - 2); | |
1223 | /* similarly, use first user-defined point of last part */ | |
1224 | index2 = (add2 ? 2 : 1); | |
1225 | ||
1226 | /* find y values for split ends */ | |
1227 | ||
1228 | /* copy vertical info from nearest point, will adjust vsteps | |
1229 | * later as needed based on slope */ | |
1230 | curve_p->coordlist[ncoord1 - 1].vert_p = | |
1231 | curve_p->coordlist[index1].vert_p; | |
1232 | curve_p->coordlist[ncoord1 - 1].vtype = | |
1233 | curve_p->coordlist[index1].vtype; | |
1234 | curve_p->coordlist[ncoord1 - 1].vsteps = | |
1235 | curve_p->coordlist[index1].vsteps; | |
1236 | new_crv_p->coordlist[0].vert_p = | |
1237 | new_crv_p->coordlist[index2].vert_p; | |
1238 | new_crv_p->coordlist[0].vtype = | |
1239 | new_crv_p->coordlist[index2].vtype; | |
1240 | new_crv_p->coordlist[0].vsteps = | |
1241 | new_crv_p->coordlist[index2].vsteps; | |
1242 | ||
1243 | /* first need to find effective length in X direction for | |
1244 | * determining slope */ | |
1245 | seg1xlen = offset; | |
1246 | seg2xlen = inpc_x( &(new_crv_p->coordlist[index2]), fname, lineno) | |
1247 | - inpc_x( &(new_crv_p->coordlist[0]), fname, lineno); | |
1248 | effective_x_len = find_effXlength(seg1xlen, seg2xlen, x1info_p, | |
1249 | x2info_p, YES); | |
1250 | ||
1251 | /* figure out the first part's y relative | |
1252 | * to the effective staff */ | |
1253 | for (m_p = x1info_p->mll_feed_p; m_p != (struct MAINLL *) 0; | |
1254 | m_p = m_p->next) { | |
1255 | if (m_p->str == S_STAFF && | |
1256 | m_p->u.staff_p->staffno == p1staff) { | |
1257 | break; | |
1258 | } | |
1259 | } | |
1260 | offset = inpc_y( &(curve_p->coordlist[index1]), | |
1261 | fname, lineno) - m_p->u.staff_p->c[AY]; | |
1262 | ||
1263 | /* find two slopes, one for the beginning line segment, | |
1264 | * one for the end. For each, base the slope | |
1265 | * on the distance between the two effective staffs, | |
1266 | * adjusted by the appropriate offset | |
1267 | * from those staffs. */ | |
1268 | y1 = new_crv_p->coordlist[index2].vert_p[RY] + | |
1269 | new_crv_p->coordlist[index2].vsteps * STEPSIZE; | |
1270 | y2 = curve_p->coordlist[index1].vert_p[RY] | |
1271 | + curve_p->coordlist[index1].vsteps * STEPSIZE; | |
1272 | slope = ((getYstaff(y1info_p->mll_feed_p, p2staff) + y1) | |
1273 | - (getYstaff(y1info_p->mll_feed_p, p1staff) + y2)) | |
1274 | / effective_x_len; | |
1275 | curve_p->coordlist[ncoord1 - 1].vsteps | |
1276 | += (slope * seg1xlen) / STEPSIZE; | |
1277 | ||
1278 | slope = ((getYstaff(y2info_p->mll_feed_p, p2staff) + y1) - | |
1279 | (getYstaff(y2info_p->mll_feed_p, p1staff) + y2)) | |
1280 | / effective_x_len; | |
1281 | new_crv_p->coordlist[0].vsteps | |
1282 | -= (slope * seg2xlen) / STEPSIZE; | |
1283 | ||
1284 | /* if need more than 2 curve segments | |
1285 | * do the rest of them */ | |
1286 | for (seg_p = Seginfo_p; seg_p != (struct SEGINFO *) 0; ) { | |
1287 | ||
1288 | slope = ((getYstaff(seg_p->mll_p, p2staff) + y1) | |
1289 | - (getYstaff(seg_p->mll_p, p1staff) + y2)) | |
1290 | / effective_x_len; | |
1291 | ||
1292 | add_crv_seg(seg_p, slope, offset, p1staff, | |
1293 | curve_p->curvetype, bulge, fname, lineno); | |
1294 | ||
1295 | /* move on the next segment in list, if any. | |
1296 | * First remember current one so we can free it, | |
1297 | * then move to next, then free the one | |
1298 | * we just finished with */ | |
1299 | to_free_p = seg_p; | |
1300 | seg_p = seg_p->next; | |
1301 | FREE(to_free_p); | |
1302 | } | |
1303 | ||
1304 | /* If there was a user-defined point extremely close to | |
1305 | * where we did the split (which is moderately likely, | |
1306 | * since they may well specific a point at a bar line), | |
1307 | * or even worse, if the one we added somehow came out | |
1308 | * to the left of the user-defined point, | |
1309 | * the curve could end up looking very strange since it | |
1310 | * contains a very tiny segment. So in that case we discard | |
1311 | * the extra point we added as the end of the split place, | |
1312 | * and just use the user-defined point. | |
1313 | */ | |
1314 | addedx = inpc_x( &(curve_p->coordlist[ncoord1 - 1]), fname, lineno); | |
1315 | userx = inpc_x( &(curve_p->coordlist[ncoord1 - 2]), fname, lineno); | |
1316 | if (add1 == 0 && (fabs(addedx - userx) < 0.1 || userx > addedx)) { | |
1317 | if (ncoord1 == 3) { | |
1318 | /* If discarding a point would get us down | |
1319 | * to only two points, we'll discard the | |
1320 | * user's point, by marking that we need to | |
1321 | * fill in an extra point in the middle | |
1322 | * (at subscript [1]). The ending point we added | |
1323 | * at [2] is so close that no one should | |
1324 | * notice. */ | |
1325 | add1 = 1; | |
1326 | } | |
1327 | else { | |
1328 | /* We already had more than 3 points, | |
1329 | * so we'll just ignore the extra one we | |
1330 | * added. The previous user-defined point | |
1331 | * is close enough to where it should end. | |
1332 | * It isn't worth the trouble to reclaim | |
1333 | * the extra array element; just let it leak. | |
1334 | */ | |
1335 | ncoord1--; | |
1336 | curve_p->ncoord = ncoord1; | |
1337 | } | |
1338 | } | |
1339 | ||
1340 | /* if first part of curve ended up with only a single segment, | |
1341 | * need to add another point in the middle to make the | |
1342 | * required minimum of 3 points for a curve. So copy the | |
1343 | * first point, adjust the x to be halfway between the first | |
1344 | * and last point, and adjust the y to be halfway between the | |
1345 | * the y's of the endpoint, offset by a little bit to get | |
1346 | * a bend in the curve. */ | |
1347 | if (add1 == 1) { | |
1348 | curve_p->coordlist[1] = curve_p->coordlist[0]; | |
1349 | curve_p->coordlist[1].hsteps = | |
1350 | (curve_p->coordlist[0].hsteps | |
1351 | + curve_p->coordlist[2].hsteps) / 2.0; | |
1352 | curve_p->coordlist[1].counts = | |
1353 | (curve_p->coordlist[0].counts | |
1354 | + curve_p->coordlist[2].counts) / 2.0; | |
1355 | /* the square root is to make the amount of bulge | |
1356 | * proportional to the x length, 1 stepsize for a | |
1357 | * piece 1 inch long, less for shorter pieces, | |
1358 | * more for longer pieces */ | |
1359 | curve_p->coordlist[1].vsteps = | |
1360 | (curve_p->coordlist[0].vsteps | |
1361 | + curve_p->coordlist[2].vsteps) / 2.0 | |
1362 | + (bulge * sqrt(seg1xlen * Score.scale_factor)); | |
1363 | } | |
1364 | ||
1365 | /* similarly for the ending part of curve */ | |
1366 | if (add2 == 1) { | |
1367 | new_crv_p->coordlist[1] = new_crv_p->coordlist[0]; | |
1368 | new_crv_p->coordlist[1].hsteps = | |
1369 | (seg2xlen / 2.0) / STEPSIZE; | |
1370 | new_crv_p->coordlist[1].counts = 0.0; | |
1371 | /* the square root is to make the amount of bulge | |
1372 | * proportional to the x length, 1 stepsize for a | |
1373 | * piece 1 inch long, less for shorter pieces, | |
1374 | * more for longer pieces */ | |
1375 | new_crv_p->coordlist[1].vsteps = | |
1376 | (new_crv_p->coordlist[0].vsteps | |
1377 | + new_crv_p->coordlist[2].vsteps) / 2.0 | |
1378 | + (bulge * sqrt(seg2xlen * Score.scale_factor)); | |
1379 | } | |
1380 | ||
1381 | ||
1382 | /* link new_mll_p into proper place in main list */ | |
1383 | /* this will be right before the first BAR after | |
1384 | * the FEED associated with y2 */ | |
1385 | for (m_p = mll_clefsig_p->next; m_p->str != S_BAR; | |
1386 | m_p = m_p->next) { | |
1387 | ; | |
1388 | } | |
1389 | insertMAINLL(new_mll_p, m_p->prev); | |
1390 | ||
1391 | /* If the rest of the curve requires further splitting, | |
1392 | * we do that now, then break out of this loop */ | |
1393 | chkcurve(new_mll_p); | |
1394 | break; | |
1395 | } | |
1396 | } | |
1397 | \f | |
1398 | ||
1399 | /* try to determine whether a user-defined curve generally bulged upward | |
1400 | * or downward and return 1 for up or -1 for down as appropriate. | |
1401 | * If intermediate points | |
1402 | * seem to be mainly higher than the endpoints it is probably up, if they | |
1403 | * tend to be below the endpoints, it is probably down. */ | |
1404 | ||
1405 | static int | |
1406 | bulgedir(curve_p, index, inputfile, inputlineno) | |
1407 | ||
1408 | struct CURVE *curve_p; | |
1409 | int index; /* check bulge dir between this point in array and | |
1410 | * the next one */ | |
1411 | char *inputfile; /* where curve was defined */ | |
1412 | int inputlineno; | |
1413 | ||
1414 | { | |
1415 | int retval = 0; | |
1416 | ||
1417 | ||
1418 | if (index == 0 || index == curve_p->ncoord - 2) { | |
1419 | /* if checking an end of the curve, we use the two end | |
1420 | * segments to guess the direction */ | |
1421 | retval += cmpcoords(curve_p, 0, 1); | |
1422 | retval += cmpcoords(curve_p, curve_p->ncoord - 1, | |
1423 | curve_p->ncoord - 2); | |
1424 | /* if more than 3 points in curve, we can use the adjacent | |
1425 | * segment on the one side where there is an adjacent segment | |
1426 | * as another reference point */ | |
1427 | if (curve_p->ncoord > 3) { | |
1428 | if (index == 0) { | |
1429 | retval += cmpcoords(curve_p, 1, 2); | |
1430 | } | |
1431 | else { | |
1432 | retval += cmpcoords(curve_p, | |
1433 | curve_p->ncoord - 2, | |
1434 | curve_p->ncoord - 3); | |
1435 | } | |
1436 | } | |
1437 | } | |
1438 | else { | |
1439 | /* for a segment in the middle, use the segments on | |
1440 | * either side for reference */ | |
1441 | retval += cmpcoords(curve_p, index - 1, index); | |
1442 | retval += cmpcoords(curve_p, index + 2, index + 1); | |
1443 | /* if that was inconclusive, try using the endpoints */ | |
1444 | if (retval == 0) { | |
1445 | retval += cmpcoords(curve_p, 0, 1); | |
1446 | retval += cmpcoords(curve_p, curve_p->ncoord - 1, | |
1447 | curve_p->ncoord - 2); | |
1448 | } | |
1449 | } | |
1450 | ||
1451 | if (retval == 0) { | |
1452 | /**** eventually try more drastic measures to try to deduce | |
1453 | *** the direction??? It's debatable about whether this should | |
1454 | * be a ufatal or pfatal. The program should be smart enough | |
1455 | * to figure out the direction, but probably can't be that | |
1456 | * smart for just any arbitrary curve shape | |
1457 | * the user tries to throw at it, and | |
1458 | * user can probably always manage to get what they want by | |
1459 | * specifying enough points, so make ufatal. */ | |
1460 | l_ufatal(inputfile, inputlineno, | |
1461 | "can't determine curve bend direction; try specifying more points"); | |
1462 | } | |
1463 | return (retval > 0 ? 1 : -1); | |
1464 | } | |
1465 | \f | |
1466 | ||
1467 | /* return 1 if point p1 appears to be below point p2. Return -1 if p1 appears | |
1468 | * to be above point p2. Return 0 if can't tell */ | |
1469 | ||
1470 | static int | |
1471 | cmpcoords(curve_p, p1, p2) | |
1472 | ||
1473 | struct CURVE *curve_p; | |
1474 | int p1; | |
1475 | int p2; | |
1476 | ||
1477 | { | |
1478 | struct COORD_INFO *y1info_p, *y2info_p; | |
1479 | int staff1, staff2; | |
1480 | double y1, y2; | |
1481 | ||
1482 | ||
1483 | /* check the two points */ | |
1484 | y1info_p = find_coord(curve_p->coordlist[p1].vert_p); | |
1485 | y2info_p = find_coord(curve_p->coordlist[p2].vert_p); | |
1486 | ||
1487 | if ((y1info_p == (struct COORD_INFO *) 0) | |
1488 | || (y2info_p == (struct COORD_INFO *) 0)) { | |
1489 | pfatal("couldn't find coord info in cmpcoords"); | |
1490 | } | |
1491 | ||
1492 | /* if on same score, can compare the absolute Y values */ | |
1493 | if (y1info_p->mll_feed_p == y2info_p->mll_feed_p) { | |
1494 | y1 = inpc_y( &(curve_p->coordlist[p1]), (char *) 0, -1); | |
1495 | y2 = inpc_y( &(curve_p->coordlist[p2]), (char *) 0, -1); | |
1496 | if (y1 < y2) { | |
1497 | return(1); | |
1498 | } | |
1499 | else if (y2 < y1) { | |
1500 | return(-1); | |
1501 | } | |
1502 | } | |
1503 | else { | |
1504 | /* weren't on same score. See if associated with same staff. | |
1505 | * If so, we can compare the relative Y values. If associated | |
1506 | * with different staffs, if second point is with lower staff | |
1507 | * it probably bulges downward. */ | |
1508 | staff1 = eff_staff(y1info_p); | |
1509 | staff2 = eff_staff(y2info_p); | |
1510 | if (staff1 == staff2) { | |
1511 | y1 = curve_p->coordlist[p1].vert_p | |
1512 | [abs2rel(curve_p->coordlist[p1].vtype)] | |
1513 | + (curve_p->coordlist[p1].vsteps * STEPSIZE | |
1514 | * svpath(staff1, STAFFSCALE)->staffscale); | |
1515 | y2 = curve_p->coordlist[p2].vert_p | |
1516 | [abs2rel(curve_p->coordlist[p2].vtype)] | |
1517 | + (curve_p->coordlist[p2].vsteps * STEPSIZE | |
1518 | * svpath(staff2, STAFFSCALE)->staffscale); | |
1519 | if (y1 < y2) { | |
1520 | return(1); | |
1521 | } | |
1522 | else if (y2 < y1) { | |
1523 | return(-1); | |
1524 | } | |
1525 | } | |
1526 | else if (staff1 < staff2) { | |
1527 | /* first point higher, bends down */ | |
1528 | return(-1); | |
1529 | } | |
1530 | else { | |
1531 | return(1); | |
1532 | } | |
1533 | } | |
1534 | return(0); | |
1535 | } | |
1536 | \f | |
1537 | ||
1538 | /* given a coord type of AY, AN, or AS, return its relative coord type, | |
1539 | * that is RY, RN, or RS respectively */ | |
1540 | ||
1541 | static int | |
1542 | abs2rel(vtype) | |
1543 | ||
1544 | int vtype; /* AY, AN, or AS */ | |
1545 | ||
1546 | { | |
1547 | switch(vtype) { | |
1548 | case AY: | |
1549 | return(RY); | |
1550 | case AN: | |
1551 | return(RN); | |
1552 | case AS: | |
1553 | return(RS); | |
1554 | default: | |
1555 | pfatal("illegal coordinate type in abs2rel"); | |
1556 | } | |
1557 | /*NOTREACHED*/ | |
1558 | return(0); | |
1559 | } | |
1560 | \f | |
1561 | ||
1562 | /* add CURVE for intervening scores */ | |
1563 | ||
1564 | static void | |
1565 | add_crv_seg(seginfo_p, slope, y_offset, staffno1, curvetype, bulge, filename, lineno) | |
1566 | ||
1567 | struct SEGINFO *seginfo_p; | |
1568 | double slope; | |
1569 | double y_offset; /* offset from staff of beginning point */ | |
1570 | int staffno1; /* staff associated with y of beginning */ | |
1571 | int curvetype; | |
1572 | int bulge; /* 1 for bulge up, -1 for bulge down */ | |
1573 | char *filename; /* where original curve was defined */ | |
1574 | int lineno; /* where original curve was defined */ | |
1575 | ||
1576 | { | |
1577 | struct MAINLL *m_p; /* index through main list */ | |
1578 | struct MAINLL *new_mll_p; /* points to new LINE */ | |
1579 | struct CURVE *new_crv_p; /* CURVE connected to new_mll_p */ | |
1580 | double xleng; /* distance to end in x direction */ | |
1581 | ||
1582 | ||
1583 | /* create a new CURVE */ | |
1584 | new_mll_p = newMAINLLstruct(S_CURVE, lineno); | |
1585 | new_mll_p->inputfile = filename; | |
1586 | new_crv_p = new_mll_p->u.curve_p; | |
1587 | new_crv_p->curvetype = (short) curvetype; | |
1588 | new_crv_p->ncoord = 3; | |
1589 | MALLOC (INPCOORD, new_crv_p->coordlist, 3); | |
1590 | ||
1591 | /* x coords of the curve ends are at the pseudobar and the rightmargin. | |
1592 | * The middle point, appropriately enough, is in the middle. We | |
1593 | * get to the right margin by adding the correct number of stepsizes | |
1594 | * from the pseudobar */ | |
1595 | new_crv_p->coordlist[0].hor_p = seginfo_p->mll_p->next->u.clefsig_p->bar_p->c; | |
1596 | new_crv_p->coordlist[0].htype = AX; | |
1597 | new_crv_p->coordlist[0].hsteps = 0.0; | |
1598 | new_crv_p->coordlist[0].counts = 0.0; | |
1599 | new_crv_p->coordlist[2].hor_p = new_crv_p->coordlist[0].hor_p; | |
1600 | new_crv_p->coordlist[2].htype = AX; | |
1601 | xleng = PGWIDTH - eff_rightmargin(seginfo_p->mll_p) | |
1602 | - new_crv_p->coordlist[0].hor_p[AX]; | |
1603 | new_crv_p->coordlist[2].hsteps = xleng / STEPSIZE; | |
1604 | new_crv_p->coordlist[2].counts = 0.0; | |
1605 | new_crv_p->coordlist[1].hor_p = new_crv_p->coordlist[0].hor_p; | |
1606 | new_crv_p->coordlist[1].htype = AX; | |
1607 | new_crv_p->coordlist[1].hsteps = (xleng / 2.0) / STEPSIZE; | |
1608 | new_crv_p->coordlist[1].counts = 0.0; | |
1609 | ||
1610 | /* find staff coord info */ | |
1611 | for (m_p = seginfo_p->mll_p; m_p != (struct MAINLL *) 0; | |
1612 | m_p = m_p->next) { | |
1613 | if (m_p->str == S_STAFF && m_p->u.staff_p->staffno == staffno1) { | |
1614 | break; | |
1615 | } | |
1616 | } | |
1617 | /* y coords are determined from the slope. Offset the endpoint by 1 | |
1618 | * STEPSIZE and the middle point by 1 STEPSIZE in the opposite | |
1619 | * direction to get a little bulge */ | |
1620 | new_crv_p->coordlist[0].vert_p = m_p->u.staff_p->c; | |
1621 | new_crv_p->coordlist[0].vtype = AY; | |
1622 | new_crv_p->coordlist[0].vsteps = (y_offset - (bulge * STEPSIZE) | |
1623 | + (slope * seginfo_p->xlength)) / STEPSIZE; | |
1624 | new_crv_p->coordlist[2].vert_p = m_p->u.staff_p->c; | |
1625 | new_crv_p->coordlist[2].vtype = AY; | |
1626 | new_crv_p->coordlist[2].vsteps = (y_offset - (bulge * STEPSIZE) + | |
1627 | (slope * (xleng + seginfo_p->xlength))) / STEPSIZE; | |
1628 | ||
1629 | /* add middle point, bulging by 2 stepsizes */ | |
1630 | new_crv_p->coordlist[1].vert_p = m_p->u.staff_p->c; | |
1631 | new_crv_p->coordlist[1].vtype = AY; | |
1632 | new_crv_p->coordlist[1].vsteps = (y_offset + (bulge * STEPSIZE) + | |
1633 | (slope * (seginfo_p->xlength + (xleng / 2.0)))) | |
1634 | / STEPSIZE; | |
1635 | ||
1636 | /* link into proper place in main list */ | |
1637 | /* this will be right before the first BAR after the FEED */ | |
1638 | for (m_p = seginfo_p->mll_p->next; m_p != (struct MAINLL *) 0; | |
1639 | m_p = m_p->next) { | |
1640 | if (m_p->str == S_BAR) { | |
1641 | break; | |
1642 | } | |
1643 | } | |
1644 | ||
1645 | if (m_p == (struct MAINLL *) 0) { | |
1646 | pfatal("couldn't find bar when adding curve segment"); | |
1647 | } | |
1648 | ||
1649 | insertMAINLL(new_mll_p, m_p->prev); | |
1650 | } | |
1651 | \f | |
1652 | ||
1653 | /* return YES if given coordinate is invisible */ | |
1654 | ||
1655 | static int | |
1656 | is_invis(cinfo_p) | |
1657 | ||
1658 | struct COORD_INFO *cinfo_p; | |
1659 | ||
1660 | { | |
1661 | /* It is invisible if explictly marked as such, or if | |
1662 | * it is not a builtin, but never had its page set differently | |
1663 | * than the initial default of zero. */ | |
1664 | if ((cinfo_p->flags & CT_INVISIBLE) || | |
1665 | (cinfo_p->page == 0 && is_builtin(cinfo_p) == NO)) { | |
1666 | return(YES); | |
1667 | } | |
1668 | else { | |
1669 | return(NO); | |
1670 | } | |
1671 | } | |
1672 | \f | |
1673 | ||
1674 | /* return YES if given coordinate is a builtin location variable */ | |
1675 | ||
1676 | static int | |
1677 | is_builtin(cinfo_p) | |
1678 | ||
1679 | struct COORD_INFO *cinfo_p; | |
1680 | ||
1681 | { | |
1682 | return((cinfo_p->flags & CT_BUILTIN) ? YES : NO); | |
1683 | } | |
1684 | \f | |
1685 | ||
1686 | /* go through list and see if any variables were defined on one page and | |
1687 | * used on another. If so, move them. */ | |
1688 | ||
1689 | static void | |
1690 | move2correct_page() | |
1691 | ||
1692 | { | |
1693 | int page = 1; /* current page */ | |
1694 | struct MAINLL *m_p; /* index through main list */ | |
1695 | struct MAINLL **insertp_p; /* where, on each page, to | |
1696 | * insert items moved from | |
1697 | * other pages */ | |
1698 | struct COORD_INFO *info_p, *info1_p; /* to see what page the item | |
1699 | * is supposed to be on */ | |
1700 | struct PRINTDATA *pr_p; /* index through print list */ | |
1701 | struct PRINTDATA **pr_del_p_p; /* where to delete item from | |
1702 | * list when moving */ | |
1703 | struct MAINLL *next_p; /* which to check next */ | |
1704 | int xabs, yabs; /* YES if x or y is absolute or | |
1705 | * builtin-relative coord */ | |
1706 | ||
1707 | ||
1708 | /* allocate array for saving where to insert things to be moved. | |
1709 | * There is no page 0, so leave extra element for that */ | |
1710 | CALLOC(MAINLL *, insertp_p, Total_pages + 1); | |
1711 | ||
1712 | ||
1713 | for (m_p = Mainllhc_p; m_p != (struct MAINLL *) 0; ) { | |
1714 | ||
1715 | /* save what will be the next to check, in case the current | |
1716 | * one gets moved */ | |
1717 | next_p = m_p->next; | |
1718 | ||
1719 | switch(m_p->str) { | |
1720 | ||
1721 | case S_BAR: | |
1722 | if (insertp_p[page] == (struct MAINLL *) 0) { | |
1723 | /* find first bar on that page and save the | |
1724 | * main list struct right before that. That | |
1725 | * is where we will move anything that has to | |
1726 | * be moved to this page */ | |
1727 | insertp_p[page] = m_p->prev; | |
1728 | } | |
1729 | break; | |
1730 | ||
1731 | case S_FEED: | |
1732 | if (m_p->u.feed_p->pagefeed == YES) { | |
1733 | page++; | |
1734 | } | |
1735 | break; | |
1736 | ||
1737 | case S_LINE: | |
1738 | /* only check user defined lines */ | |
1739 | if (m_p->inputlineno != -1) { | |
1740 | info_p = find_coord(m_p->u.line_p->start.hor_p); | |
1741 | if (info_p != (struct COORD_INFO *) 0 && | |
1742 | info_p->page != page && | |
1743 | info_p->flags != CT_BUILTIN) { | |
1744 | move_it(m_p, insertp_p[info_p->page], | |
1745 | info_p->page); | |
1746 | } | |
1747 | } | |
1748 | break; | |
1749 | ||
1750 | case S_CURVE: | |
1751 | /* only check user defined curves */ | |
1752 | if (m_p->inputlineno != -1) { | |
1753 | info_p = find_coord(m_p->u.curve_p-> | |
1754 | coordlist[0].hor_p); | |
1755 | if (info_p != (struct COORD_INFO *) 0 && | |
1756 | info_p->page != page && | |
1757 | info_p->flags != CT_BUILTIN) { | |
1758 | move_it(m_p, insertp_p[info_p->page], | |
1759 | info_p->page); | |
1760 | } | |
1761 | } | |
1762 | break; | |
1763 | ||
1764 | case S_PRHEAD: | |
1765 | for (pr_p = m_p->u.prhead_p->printdata_p, pr_del_p_p = | |
1766 | &(m_p->u.prhead_p->printdata_p); | |
1767 | pr_p != (struct PRINTDATA *) 0; | |
1768 | pr_del_p_p = &(pr_p->next)) { | |
1769 | ||
1770 | /* find out about x and y portions */ | |
1771 | info_p = find_coord(pr_p->location.hor_p); | |
1772 | info1_p = find_coord(pr_p->location.vert_p); | |
1773 | ||
1774 | /* figure out if x and y are absolute or | |
1775 | * associated with builtins */ | |
1776 | xabs = yabs = NO; | |
1777 | if (info_p == (struct COORD_INFO *) 0) { | |
1778 | xabs = YES; | |
1779 | } | |
1780 | else if (info_p->flags & CT_BUILTIN) { | |
1781 | xabs = YES; | |
1782 | } | |
1783 | if (info1_p == (struct COORD_INFO *) 0) { | |
1784 | yabs = YES; | |
1785 | } | |
1786 | else if (info1_p->flags & CT_BUILTIN) { | |
1787 | yabs = YES; | |
1788 | } | |
1789 | /* if both x and y are absolute coordinates, | |
1790 | * don't move it */ | |
1791 | if ((xabs == YES) && (yabs == YES)) { | |
1792 | pr_p = pr_p->next; | |
1793 | continue; | |
1794 | } | |
1795 | ||
1796 | /* if both x and y are not absolute, make sure | |
1797 | * they are associated with same staff */ | |
1798 | if ((xabs == NO) && (yabs == NO)) { | |
1799 | coordcheck(info_p, info1_p, | |
1800 | pr_p->inputfile, | |
1801 | pr_p->inputlineno); | |
1802 | } | |
1803 | ||
1804 | /* normally we'll check for moving based on x. | |
1805 | * (most of the time x and y will be on the same | |
1806 | * page, so we can use either.) However, if | |
1807 | * x happens to be the one that is absolute, | |
1808 | * use y instead */ | |
1809 | if ((xabs == YES) && (yabs == NO)) { | |
1810 | info_p = info1_p; | |
1811 | } | |
1812 | ||
1813 | if (info_p->page != page ) { | |
1814 | struct MAINLL *new_mll_p; | |
1815 | struct PRINTDATA *save_p; | |
1816 | ||
1817 | /* moving a PRINTDATA is harder than | |
1818 | * moving a line or curve, because, | |
1819 | * there could be a list, so we have | |
1820 | * surgically remove this one from the | |
1821 | * list and graft onto a new PRHEAD */ | |
1822 | new_mll_p = newMAINLLstruct(S_PRHEAD, | |
1823 | m_p->u.prhead_p->printdata_p-> | |
1824 | inputlineno); | |
1825 | new_mll_p->inputfile = m_p->u.prhead_p-> printdata_p->inputfile; | |
1826 | new_mll_p->u.prhead_p->printdata_p | |
1827 | = pr_p; | |
1828 | ||
1829 | /* save link for continuing for loop */ | |
1830 | save_p = pr_p->next; | |
1831 | ||
1832 | /* patch up linked list */ | |
1833 | *pr_del_p_p = pr_p->next; | |
1834 | pr_p->next = (struct PRINTDATA *) 0; | |
1835 | ||
1836 | /* If there is a page to move it to, | |
1837 | * move it there. If page is zero, | |
1838 | * it must be associated with something | |
1839 | * invisible, so discard it. */ | |
1840 | if (info_p->page != 0) { | |
1841 | /* move to correct page */ | |
1842 | if (insertp_p[info_p->page] == | |
1843 | 0) { | |
1844 | l_ufatal(pr_p->inputfile, | |
1845 | pr_p->inputlineno, | |
1846 | "forward reference to location tag"); | |
1847 | } | |
1848 | insertMAINLL(new_mll_p, | |
1849 | insertp_p[info_p->page]); | |
1850 | } | |
1851 | else { | |
1852 | FREE(new_mll_p); | |
1853 | } | |
1854 | ||
1855 | /* prepare for next time through loop */ | |
1856 | pr_p = save_p; | |
1857 | } | |
1858 | else { | |
1859 | pr_p = pr_p->next; | |
1860 | } | |
1861 | } | |
1862 | ||
1863 | /* if all moved, can discard */ | |
1864 | if (m_p->u.prhead_p->printdata_p | |
1865 | == (struct PRINTDATA *) 0) { | |
1866 | unlinkMAINLL(m_p); | |
1867 | } | |
1868 | break; | |
1869 | ||
1870 | default: | |
1871 | break; | |
1872 | } | |
1873 | ||
1874 | m_p = next_p; | |
1875 | } | |
1876 | FREE(insertp_p); | |
1877 | } | |
1878 | \f | |
1879 | ||
1880 | /* move given MAINLL to specified place */ | |
1881 | ||
1882 | static void | |
1883 | move_it(m_p, im_p, page) | |
1884 | ||
1885 | struct MAINLL *m_p; /* move this */ | |
1886 | struct MAINLL *im_p; /* insert here */ | |
1887 | int page; /* if page 0, move to oblivion */ | |
1888 | ||
1889 | { | |
1890 | unlinkMAINLL(m_p); | |
1891 | if (page == 0) { | |
1892 | /* must be invisible, so discard it */ | |
1893 | FREE(m_p); | |
1894 | return; | |
1895 | } | |
1896 | if (im_p == (struct MAINLL *) 0) { | |
1897 | l_ufatal(m_p->inputfile, m_p->inputlineno, | |
1898 | "forward reference to location tag"); | |
1899 | } | |
1900 | insertMAINLL(m_p, im_p); | |
1901 | } | |
1902 | \f | |
1903 | ||
1904 | /* Go through main list. | |
1905 | * If there are any INPCOORDs that have a hor_p of a bar, and have a positive | |
1906 | * x offset, and that bar is at the end of a score and thus has a pseudo-bar | |
1907 | * on the following score, move the hor_p to point to the pseudo-bar instead. | |
1908 | * If the y of the same INPCOORD also pointed to the same bar before, move it | |
1909 | * as well, otherwise leave it as is. */ | |
1910 | ||
1911 | static void | |
1912 | move2pseudo() | |
1913 | ||
1914 | { | |
1915 | struct MAINLL *m_p; /* walk through main list */ | |
1916 | struct PRINTDATA *pr_p; /* walk through list of print commands */ | |
1917 | int n; /* index through curve coordinates */ | |
1918 | ||
1919 | ||
1920 | /* go through main list */ | |
1921 | for (m_p = Mainllhc_p; m_p != (struct MAINLL *) 0; m_p = m_p->next) { | |
1922 | ||
1923 | if (m_p->str == S_LINE) { | |
1924 | /* handle start and end points of line */ | |
1925 | do_pseudo( &(m_p->u.line_p->start), m_p ); | |
1926 | do_pseudo( &(m_p->u.line_p->end), m_p ); | |
1927 | } | |
1928 | ||
1929 | else if (m_p->str == S_CURVE) { | |
1930 | /* do each point of curve */ | |
1931 | for (n = m_p->u.curve_p->ncoord - 1; n >= 0; n--) { | |
1932 | do_pseudo( &(m_p->u.curve_p->coordlist[n]), m_p); | |
1933 | } | |
1934 | } | |
1935 | ||
1936 | else if (m_p->str == S_PRHEAD) { | |
1937 | /* do each print command */ | |
1938 | for (pr_p = m_p->u.prhead_p->printdata_p; | |
1939 | pr_p != (struct PRINTDATA *) 0; | |
1940 | pr_p = pr_p->next) { | |
1941 | do_pseudo( &(pr_p->location), m_p ); | |
1942 | } | |
1943 | } | |
1944 | } | |
1945 | } | |
1946 | \f | |
1947 | ||
1948 | /* given an INPCOORD, if it has a hor_p of a bar, and has a positive | |
1949 | * x offset, and that bar is at the end of a score and thus has a pseudo-bar | |
1950 | * on the following score, move the hor_p to point to the pseudo-bar instead. | |
1951 | * If the y of the same INPCOORD also pointed to the same bar before, move it | |
1952 | * as well, otherwise leave it as is. */ | |
1953 | ||
1954 | static void | |
1955 | do_pseudo(inpc_p, mll_p) | |
1956 | ||
1957 | struct INPCOORD *inpc_p; | |
1958 | struct MAINLL *mll_p; | |
1959 | ||
1960 | { | |
1961 | struct COORD_INFO *info_p; | |
1962 | ||
1963 | ||
1964 | if ((info_p = find_coord(inpc_p->hor_p)) == (struct COORD_INFO *) 0) { | |
1965 | /* probably an absolute coordinate */ | |
1966 | return; | |
1967 | } | |
1968 | ||
1969 | /* if x is associated with a bar... */ | |
1970 | if (info_p->flags & CT_BAR) { | |
1971 | /* and that bar has an associated pseudo bar... */ | |
1972 | if (info_p->pseudo_bar_p != (struct BAR *) 0) { | |
1973 | /* and the x value after adding offsets is into | |
1974 | * the right margin area... */ | |
1975 | if (inpc_x(inpc_p, (char *) 0, -1) | |
1976 | > PGWIDTH - eff_rightmargin(mll_p)) { | |
1977 | /* if y of INPCOORD was also associated with | |
1978 | * the same bar, move it to pseudo-bar */ | |
1979 | if (inpc_p->hor_p == inpc_p->vert_p) { | |
1980 | inpc_p->vert_p = info_p->pseudo_bar_p->c; | |
1981 | } | |
1982 | /* move x to pseudo-bar */ | |
1983 | inpc_p->hor_p = info_p->pseudo_bar_p->c; | |
1984 | } | |
1985 | } | |
1986 | } | |
1987 | } | |
1988 | \f | |
1989 | ||
1990 | /* Given a line or curve, fix any INPCOORD that end up off the margin */ | |
1991 | ||
1992 | static void | |
1993 | fix_inpcoords(mll_p) | |
1994 | ||
1995 | struct MAINLL *mll_p; | |
1996 | ||
1997 | { | |
1998 | int n; /* index through curve points */ | |
1999 | ||
2000 | ||
2001 | if (mll_p->str == S_CURVE) { | |
2002 | for (n = 0; n < mll_p->u.curve_p->ncoord; n++) { | |
2003 | adj_coord( & (mll_p->u.curve_p->coordlist[n]), mll_p, | |
2004 | ((n > 0) ? &(mll_p->u.curve_p->coordlist[n-1]) | |
2005 | : (struct INPCOORD *) 0) ); | |
2006 | } | |
2007 | } | |
2008 | else if (mll_p->str == S_LINE) { | |
2009 | adj_coord( &(mll_p->u.line_p->start), mll_p, | |
2010 | (struct INPCOORD *) 0); | |
2011 | adj_coord( &(mll_p->u.line_p->end), mll_p, | |
2012 | &(mll_p->u.line_p->start) ); | |
2013 | } | |
2014 | } | |
2015 | \f | |
2016 | ||
2017 | /* If x of INPCOORD ends up off the page, change the INPCOORD | |
2018 | * to be on the following score, using that score's pseudo-bar | |
2019 | * as the reference. */ | |
2020 | ||
2021 | static void | |
2022 | adj_coord(coord_p, mll_p, prev_coord_p) | |
2023 | ||
2024 | struct INPCOORD *coord_p; /* what to potentially adjust */ | |
2025 | struct MAINLL *mll_p; /* points to the line or curve containing coord_p */ | |
2026 | struct INPCOORD *prev_coord_p; /* previous coord if any, else NULL */ | |
2027 | ||
2028 | { | |
2029 | struct MAINLL *m_p; /* for finding thing in main list */ | |
2030 | float x, y; | |
2031 | float prev_x, prev_y; /* location of prev_coord_p */ | |
2032 | struct INPCOORD temp_coord; /* reference if prev_coord_p is NULL */ | |
2033 | float right_margin_x; /* PGWIDTH - eff_rightmargin */ | |
2034 | float staff_y = 0.0; | |
2035 | struct COORD_INFO *xinfo_p, *yinfo_p; /* for finding which staff, | |
2036 | * clefsig, etc is associated with | |
2037 | * the point */ | |
2038 | struct BAR *bar_p; /* pseudo-bar */ | |
2039 | int staffno; | |
2040 | ||
2041 | ||
2042 | /* don't bother with invisible points. */ | |
2043 | if (prev_coord_p != (struct INPCOORD *) 0) { | |
2044 | xinfo_p = find_coord(prev_coord_p->hor_p); | |
2045 | yinfo_p = find_coord(prev_coord_p->vert_p); | |
2046 | if (xinfo_p == (struct COORD_INFO *) 0 | |
2047 | || yinfo_p == (struct COORD_INFO *) 0) { | |
2048 | return; | |
2049 | } | |
2050 | ||
2051 | if (is_invis(xinfo_p) == YES || is_invis(yinfo_p) == YES) { | |
2052 | /* things with invisible points are ignored */ | |
2053 | return; | |
2054 | } | |
2055 | } | |
2056 | ||
2057 | xinfo_p = find_coord(coord_p->hor_p); | |
2058 | yinfo_p = find_coord(coord_p->vert_p); | |
2059 | if (xinfo_p == (struct COORD_INFO *) 0 | |
2060 | || yinfo_p == (struct COORD_INFO *) 0) { | |
2061 | return; | |
2062 | } | |
2063 | ||
2064 | if (is_invis(xinfo_p) == YES || is_invis(yinfo_p) == YES) { | |
2065 | return; | |
2066 | } | |
2067 | ||
2068 | x = inpc_x(coord_p, (char *) 0, -1); | |
2069 | y = inpc_y(coord_p, mll_p->inputfile, mll_p->inputlineno); | |
2070 | prev_x = prev_y = 0.0; /* avoid bogus "used before set" warning */ | |
2071 | ||
2072 | /* Check for points being too close together. If user specifies the | |
2073 | * same point for both endpoints of a line, or something like that, | |
2074 | * PostScript might get asked to divide by zero. */ | |
2075 | if (prev_coord_p != (struct INPCOORD *) 0) { | |
2076 | prev_x = inpc_x(prev_coord_p, mll_p->inputfile, mll_p->inputlineno); | |
2077 | prev_y = inpc_y(prev_coord_p, mll_p->inputfile, mll_p->inputlineno); | |
2078 | ||
2079 | if ( (fabs(x - prev_x) < .0001) && (fabs(y - prev_y) < .0001)) { | |
2080 | l_ufatal(mll_p->inputfile, mll_p->inputlineno, | |
2081 | "points too close together"); | |
2082 | } | |
2083 | } | |
2084 | ||
2085 | /* Find the x value and see if it is off the right of the page. | |
2086 | * Pretend we don't know the file/lineno, because that way if it is | |
2087 | * off the page, no error message will be printed, which is what we | |
2088 | * want, since we hope to be able to patch things up so it isn't | |
2089 | * off the page anymore. */ | |
2090 | if (x < PGWIDTH) { | |
2091 | /* this one is okay as is */ | |
2092 | return; | |
2093 | } | |
2094 | ||
2095 | /* Get the staff associated with the y */ | |
2096 | staffno = eff_staff(yinfo_p); | |
2097 | ||
2098 | /* Find the pseudo bar of the next score and the y of the staff */ | |
2099 | for (m_p = xinfo_p->mll_feed_p->next; m_p != (struct MAINLL *) 0; | |
2100 | m_p = m_p->next) { | |
2101 | ||
2102 | if (m_p->str == S_STAFF && m_p->u.staff_p->staffno == staffno) { | |
2103 | staff_y = m_p->u.staff_p->c[AY]; | |
2104 | } | |
2105 | ||
2106 | if (IS_CLEFSIG_FEED(m_p)) { | |
2107 | /* pseudo-bar will be in CLEFSIG right after this */ | |
2108 | break; | |
2109 | } | |
2110 | } | |
2111 | if (m_p == (struct MAINLL *) 0) { | |
2112 | /* no future score. Give up trying to fix this one */ | |
2113 | return; | |
2114 | } | |
2115 | ||
2116 | /* Use the pseudo-bar as reference */ | |
2117 | bar_p = m_p->next->u.clefsig_p->bar_p; | |
2118 | ||
2119 | /* If there was a previous point, we will use that as a reference | |
2120 | * point, otherwise make a temporary point that is the same | |
2121 | * as the current point but with no x or y offset. */ | |
2122 | if (prev_coord_p == (struct INPCOORD *) 0) { | |
2123 | temp_coord = *coord_p; | |
2124 | temp_coord.hsteps = 0.0; | |
2125 | temp_coord.counts = 0.0; | |
2126 | temp_coord.vsteps = 0.0; | |
2127 | prev_coord_p = &temp_coord; | |
2128 | prev_x = inpc_x(prev_coord_p, mll_p->inputfile, mll_p->inputlineno); | |
2129 | prev_y = inpc_y(prev_coord_p, mll_p->inputfile, mll_p->inputlineno); | |
2130 | } | |
2131 | ||
2132 | /* Use the pseudo-bar for y */ | |
2133 | coord_p->vert_p = bar_p->c; | |
2134 | coord_p->vtype = AY; | |
2135 | ||
2136 | /* Pretend to draw a line from reference point to the current point, | |
2137 | * and calculate, using similar triangles, what the y would be | |
2138 | * where that line would hit the right margin. */ | |
2139 | right_margin_x = PGWIDTH - eff_rightmargin(m_p); | |
2140 | y = prev_y + ((y - prev_y) * (right_margin_x - prev_x)) / (x - prev_x); | |
2141 | /* Now adjust to be relative to the staff's y */ | |
2142 | y = y - staff_y; | |
2143 | ||
2144 | /* on the following score, where we are moving the INPCOORD, | |
2145 | * find the y of the appropriate staff */ | |
2146 | for (m_p = m_p->next; m_p != (struct MAINLL *) 0; m_p = m_p->next) { | |
2147 | if (m_p->str == S_STAFF && m_p->u.staff_p->staffno == staffno) { | |
2148 | /* The new y for the INPCOORD is the staff's y offset | |
2149 | * from the score's y, plus the relative offset | |
2150 | * found above. */ | |
2151 | y += m_p->u.staff_p->c[AY] - bar_p->c[AY]; | |
2152 | break; | |
2153 | } | |
2154 | if (m_p->str == S_BAR) { | |
2155 | l_ufatal(mll_p->inputfile, mll_p->inputlineno, | |
2156 | "curve is associated with staff %d, which does not exists", staffno); | |
2157 | } | |
2158 | } | |
2159 | ||
2160 | coord_p->vsteps = y / (STEPSIZE * svpath(staffno, STAFFSCALE)->staffscale); | |
2161 | ||
2162 | /* change the INPCOORD x to point to the pseudo-bar's coord array */ | |
2163 | coord_p->hor_p = bar_p->c; | |
2164 | coord_p->htype = AX; | |
2165 | coord_p->counts = 0.0; | |
2166 | /* x offset will be the excess that sticks beyond the right edge | |
2167 | * of the score */ | |
2168 | coord_p->hsteps = (x - right_margin_x) | |
2169 | / (STEPSIZE * svpath(staffno, STAFFSCALE)->staffscale); | |
2170 | ||
2171 | /* If the original was really, really far off the page, even the | |
2172 | * moved version may still be off the page, so try again. Eventually | |
2173 | * either we should get within the current score or run off the end | |
2174 | * of the song and have to give up. */ | |
2175 | adj_coord(coord_p, mll_p, prev_coord_p); | |
2176 | } | |
2177 | \f | |
2178 | ||
2179 | /* For manual curves that use "bulge" values, figure out the intermediate | |
2180 | * points for curves, and put them in the coordlist, getting rid of the | |
2181 | * bulgelist. | |
2182 | */ | |
2183 | ||
2184 | static void | |
2185 | calc_bulge(curve_p, fname, lineno, is_split, mll_p) | |
2186 | ||
2187 | struct CURVE *curve_p; /* curve defined using bulge */ | |
2188 | char *fname; | |
2189 | int lineno; | |
2190 | int is_split; /* YES if goes across at least one FEED */ | |
2191 | struct MAINLL *mll_p; /* for finding effective margin */ | |
2192 | ||
2193 | { | |
2194 | double x1, y1; /* start point location */ | |
2195 | double x2, y2; /* end point location */ | |
2196 | double xlen, ylen; /* distances between endpoints */ | |
2197 | double seg1xlen, seg2xlen; /* lengths of parts of curve on | |
2198 | * first and last score when split */ | |
2199 | double sintheta, costheta; /* for rotation */ | |
2200 | double length; /* between endpoints */ | |
2201 | double segX, segY; /* distance to intermediate point */ | |
2202 | int p1staff, p2staff; /* staff associated with each point */ | |
2203 | struct INPCOORD *coordlist_p; /* the new calculated curve */ | |
2204 | int n; /* index through bulge points */ | |
2205 | int nbulge; /* how many bulge points specified */ | |
2206 | double staffscale; | |
2207 | struct COORD_INFO *x1info_p, *y1info_p; /* to get staff info, etc */ | |
2208 | struct COORD_INFO *x2info_p, *y2info_p; | |
2209 | ||
2210 | ||
2211 | nbulge = curve_p->nbulge; | |
2212 | ||
2213 | /* The calculated curve will have the 2 endpoints plus nbulge | |
2214 | * intermediate points */ | |
2215 | MALLOC (INPCOORD, coordlist_p, 2 + nbulge); | |
2216 | ||
2217 | /* The endpoints just get copied. All the inner points will | |
2218 | * be calculated relative to the first, so for now we copy all of | |
2219 | * the INPCOORD data from the first point into them, then later | |
2220 | * we will overwrite the offset values appropriately. */ | |
2221 | for (n = 0; n < nbulge + 1; n++) { | |
2222 | coordlist_p[n] = curve_p->coordlist[0]; | |
2223 | } | |
2224 | coordlist_p[nbulge + 1] = curve_p->coordlist[1]; | |
2225 | ||
2226 | /* Find relevant information about the endpoints */ | |
2227 | x1 = inpc_x( &(curve_p->coordlist[0]), fname, lineno); | |
2228 | y1 = inpc_y( &(curve_p->coordlist[0]), fname, lineno); | |
2229 | x2 = inpc_x( &(curve_p->coordlist[1]), fname, lineno); | |
2230 | y2 = inpc_y( &(curve_p->coordlist[1]), fname, lineno); | |
2231 | ||
2232 | x1info_p = find_coord( curve_p->coordlist[0].hor_p); | |
2233 | y1info_p = find_coord( curve_p->coordlist[0].vert_p); | |
2234 | x2info_p = find_coord( curve_p->coordlist[1].hor_p); | |
2235 | y2info_p = find_coord( curve_p->coordlist[1].vert_p); | |
2236 | ||
2237 | /* If all coordinates associated with staffs having the same | |
2238 | * staffscale value, then use that, otherwise use score value | |
2239 | */ | |
2240 | if ((staffscale = svpath(x1info_p->staffno, STAFFSCALE)->staffscale) != | |
2241 | svpath(y1info_p->staffno, STAFFSCALE)->staffscale | |
2242 | || staffscale != | |
2243 | svpath(x2info_p->staffno, STAFFSCALE)->staffscale | |
2244 | || staffscale != | |
2245 | svpath(y2info_p->staffno, STAFFSCALE)->staffscale) { | |
2246 | staffscale = Score.staffscale; | |
2247 | } | |
2248 | ||
2249 | /* Find the length of the line segment | |
2250 | * that would go straight between the two endpoints. To do this, | |
2251 | * we get the x and y distances to use with Pythagorean theorem */ | |
2252 | if (is_split == NO) { | |
2253 | /* if all on same score, easier to find a and y */ | |
2254 | xlen = (x2 - x1); | |
2255 | ylen = (y2 - y1); | |
2256 | } | |
2257 | else { | |
2258 | /* Split curves take more work. First find x length | |
2259 | * on the score containing the first part of the curve */ | |
2260 | seg1xlen = PGWIDTH - eff_rightmargin(mll_p) - | |
2261 | inpc_x( &(curve_p->coordlist[0]), fname, lineno ); | |
2262 | ||
2263 | /* Find the x length of the score containing the last past. | |
2264 | * To do this, have to find the pseudo-bar inside the | |
2265 | * appropriate CLEFSIG, which should be on the main list | |
2266 | * immediately following the FEED | |
2267 | * of the score containing the ending x coordinate. */ | |
2268 | seg2xlen = inpc_x( &(curve_p->coordlist[1]), fname, lineno) - | |
2269 | x2info_p->mll_feed_p->next->u.clefsig_p->bar_p->c[AX]; | |
2270 | ||
2271 | /* Finally, add in the x lengths of any intervening scores */ | |
2272 | xlen = find_effXlength(seg1xlen, seg2xlen, | |
2273 | x1info_p, x2info_p, NO); | |
2274 | ||
2275 | /* Now we need the distance in the y direction. First, the | |
2276 | * easy case, when both endpoints are associated with the | |
2277 | * same staff */ | |
2278 | p1staff = eff_staff(y1info_p); | |
2279 | p2staff = eff_staff(y2info_p); | |
2280 | if (p1staff == p2staff) { | |
2281 | /* y length is the relative Y of the INPCOORD | |
2282 | * of the final point, offset by its vsteps, minus | |
2283 | * the similar value of the beginning point. */ | |
2284 | ylen = (curve_p->coordlist[1].vert_p | |
2285 | [abs2rel(curve_p->coordlist[1].vtype)] | |
2286 | + curve_p->coordlist[1].vsteps | |
2287 | * STEPSIZE * staffscale) | |
2288 | - (curve_p->coordlist[0].vert_p | |
2289 | [abs2rel(curve_p->coordlist[0].vtype)] | |
2290 | + curve_p->coordlist[0].vsteps | |
2291 | * STEPSIZE * staffscale); | |
2292 | } | |
2293 | else { | |
2294 | /* The endpoints are associated with different staffs. | |
2295 | * The y distance between these two staffs may vary | |
2296 | * from score to score, so to get things really | |
2297 | * accurate, we'd have to adjust the y proportionally | |
2298 | * on each intervening score, which may require adding | |
2299 | * lots of intermediate points and lots of complicated | |
2300 | * calculations which may or may not look much better | |
2301 | * than doing something more simple. So just do | |
2302 | * something fairly simple: Find the distance between | |
2303 | * each endpoint's y and its staff's y, and subtract | |
2304 | * those two distances to get an approximate ylen. | |
2305 | * As long as the distance between the two staffs is | |
2306 | * somewhat similar on both scores, | |
2307 | * which is likely to be the case, | |
2308 | * the results should be pretty good. */ | |
2309 | ylen = (y1info_p->mll_feed_p->next->u.clefsig_p->bar_p->c[AY] - y1) - | |
2310 | (y2info_p->mll_feed_p->next->u.clefsig_p->bar_p->c[AY] - y2); | |
2311 | } | |
2312 | } | |
2313 | ||
2314 | /* Find distance between the endpoints */ | |
2315 | length = sqrt( SQUARED(xlen) + SQUARED(ylen) ); | |
2316 | ||
2317 | /* Guard again divide by zero */ | |
2318 | if (length < 0.0001) { | |
2319 | l_ufatal(fname, lineno, "curve endpoints too close together"); | |
2320 | } | |
2321 | ||
2322 | /* We find the intermediate points as if the line were horizontal, | |
2323 | * then rotate it, so need the sine and cosine for rotation. | |
2324 | */ | |
2325 | sintheta = ylen / length; | |
2326 | costheta = xlen / length; | |
2327 | ||
2328 | /* Calculate the position of each inner point. */ | |
2329 | for (n = 1; n <= nbulge; n++) { | |
2330 | /* horizontal offset is based on a fraction of the length | |
2331 | * between endpoints: 1/2 if there is one bulge value, | |
2332 | * 1/3 and 2/3 if there are two values, etc, so use | |
2333 | * n/(nbulge + 1) to get the unrotated x lengths. | |
2334 | * Use the bulge value (which is already in stepsizes) | |
2335 | * for the unrotated y length. | |
2336 | * Then to do the rotation, use | |
2337 | * x' = x costheta - y sintheta | |
2338 | * y' = y costheta + x sintheta | |
2339 | */ | |
2340 | segX = (length * ((double) n / (double)(nbulge + 1))) | |
2341 | / (STEPSIZE * staffscale); | |
2342 | segY = curve_p->bulgelist[n-1]; | |
2343 | coordlist_p[n].hsteps += (segX * costheta) - (segY * sintheta); | |
2344 | coordlist_p[n].vsteps += (segY * costheta) + (segX * sintheta); | |
2345 | } | |
2346 | ||
2347 | /* free the old coord list, which just had the endpoints */ | |
2348 | FREE(curve_p->coordlist); | |
2349 | ||
2350 | /* replace the old coordlist with the newly calculated one */ | |
2351 | curve_p->coordlist = coordlist_p; | |
2352 | curve_p->ncoord = 2 + nbulge; | |
2353 | ||
2354 | /* don't need bulgelist anymore, since it has been converted to | |
2355 | * regular curve. */ | |
2356 | FREE(curve_p->bulgelist); | |
2357 | curve_p->bulgelist = 0; | |
2358 | curve_p->nbulge = 0; | |
2359 | } |