chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / locvar.c
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 }