chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / locvar.c
CommitLineData
69695f33
MW
1
2/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004 by Arkkra Enterprises */
3/* All rights reserved */
4
5/*
6There 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
16Type B variables, associated with bars, are extra strange. The y of a bar
17in not particularly useful. If the bar line happens to wind up at the end
18of a score, special consideration applies, because the right side of the
19bar is effectively at the beginning of the next score after the clefsig.
20So here are the rules:
21
221. If a bar does not occur at the end of a score, the location variable
23associated with it, if any, is handled normally.
24
252. If a bar does fall on the end of a score, if the x coordinate derived from
26a location variable associated with that bar in
27an INPCOORD, after offsetting, comes out to be either left of the x of
28the bar, or equal to the x of the bar, it will be handled normally.
29
303. If a bar falls on the end of a score, and
31if the x coordinate derived from an INPCOORD, after offsetting, comes out
32to be right of the x of the bar,
33the x coordinate will be recalculated using the pseudo
34bar at the beginning of the following score,
35and if the y coordinate of the same INPCOORD is also associated
36with the same bar coordinate, it will also be recalculated from the pseudo bar.
37
384. If rule 3 would normally apply, but there is no following score, rule 3
39will be ignored, and the coordinate will be used as is.
40
41A PRHEAD contains only a single location variable, so it should always
42be taken just as is.
43
44Lines and curves are more exciting, since they can have multiple
45coordinates, and thus may need to be split into 2 or more pieces
46across scores and/or pages. Lines are a degenerate case of curves,
47so if we can deal with curves, we've got it made.
48
49For drawing a specific curve, here are the rules:
50
511. If any coordinate is associated with a staff that is invisible, the entire
52curve will be ignored.
53
542. Type C variables are always used as is, never themselves causing splitting.
55Taking any adjacent pair of points in a curve, if either of them is of type C,
56the line segment between those 2 points will not be split.
57
583. If all variables of type A and B are on the same score of the same page,
59then the curve can be printed as is, with no splitting needed.
60
614. If the x and y components of a single INPCOORD are associated with
62different scores, this will be an error condition.
63
645. If the x and y components of a single INPCOORD are associated with
65different staffs, but the same score, the point will be treated as if
66it were associated with the staff associated with the y coordinate.
67
686. If 2 adjacent points of a curve are associated with different
69scores, the line segment must be split. The number of segments that will
70need to be generated will be equal to the number of FEEDs between
71the coordinates plus one.
72
737. Splitting will only be done to forward scores. In other words, if the
74coordinates of a curve would require splitting part of the curve onto a
75preceeding score, that will be an error. This is to keep things simpler,
76since I can't think of any times this restriction would cause a problem.
77
788. If a segment needs to be split, the first piece will extend in the
79x direction from the first point to 0.1 inch left of the right edge of the
80score associated with the first point.
81However, if the starting x is already at the right edge of the score, a line of length 0.1 inches will be drawn instead.
82The last piece of the split line segment will extend in
83the x direction from the pseudo bar of the clefsig.
84
858a. If there are additional scores
86between the one associated with the beginning point and the one associated
87with the endpoint, for each intervening score a line will be drawn with
88its x coordinates from the pseudo bar to the right margin.
89
909. To calculate the y coordinates of each piece of a split line segment,
91there are several cases. First the easy case, where the y coordinates of
92the beginning and ending point are both associated with the same staff.
93Conceptionally, the scores are lined up on a single line without score
94feeds. The slope of the line is then calculated. The y coordinates of
95the derived points are then calculated using this slope. Thus, for example,
96if the ending y coordinate would be A inches from of the beginning y coordinate
97in the x direction (if they were on the same score),
98and the line segment is split into 2 segments, with the first having
99a length in the x direction of B and the second having a length in the x
100direction of C, the y coordinate of the end of the first segment would be
101y[begin] + (A/(B+C)) * B, and the y coordinate of the beginning of the second
102piece would be y[end] - (A/(B+C)) * C.
103
10410. If the y coordinates of the 2 points are associated with different staffs.
105the slope is calculated based on the distance of the endpoints from their
106respective staffs. Then for each segment, the slope and endpoints are adjusted
107based on the ratio of the distance between the two staffs on the current score
108relative to the widest distance.
109
11011. For purposes of determining y coordinates, the y and n values of a bar
111are considered to be associated with the top visible score, and the s value
112is considered to be associated with the bottom visible score.
113Then 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
120static int Total_pages = 1; /* how many pages of output */
121
122/* if lines must be added for intervening scores, save info about them */
123struct 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};
128struct SEGINFO *Seginfo_p;
129
130static void gather_coord_info P((void));
131static 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));
134static void coord_staff P((struct STAFF *staff_p, int page,
135 int score, struct MAINLL *mll_feed_p));
136static void split_lines_and_curves P((void));
137static void chkline P((struct MAINLL *mll_p));
138static void coordcheck P((struct COORD_INFO *x_info_p,
139 struct COORD_INFO *y_info_p, char *fname, int lineno));
140static void add_segment P((struct SEGINFO *seginfo_p, double slope,
141 double y_offset, int staffno1, int linetype));
142static double find_effXlength P((double seg1xlen, double seg2xlen,
143 struct COORD_INFO *x1info_p, struct COORD_INFO *x2info_p,
144 int save_feed_info));
145static void svfeed P((struct MAINLL *mll_feed_p, double xlength));
146static int eff_staff P((struct COORD_INFO *yinfo_p));
147static double getYstaff P((struct MAINLL *mll_p, int staffno));
148static void chkcurve P((struct MAINLL *mll_p));
149static int bulgedir P((struct CURVE *curve_p, int index, char *inputfile,
150 int inputlineno));
151static int cmpcoords P((struct CURVE *curve_p, int p1, int p2));
152static int abs2rel P((int vtype));
153static 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));
156static int is_invis P((struct COORD_INFO *cinfo_p));
157static int is_builtin P((struct COORD_INFO *cinfo_p));
158static void move2correct_page P((void));
159static void move_it P((struct MAINLL *m_p, struct MAINLL *im_p, int page));
160static void move2pseudo P((void));
161static void do_pseudo P((struct INPCOORD *inpc_p, struct MAINLL *mll_p));
162static void fix_inpcoords P((struct MAINLL *mll_p));
163static void adj_coord P((struct INPCOORD *coord_p, struct MAINLL *mll_p,
164 struct INPCOORD *prev_coord_p));
165static 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
179void
180fix_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
202static void
203gather_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
304static void
305save_coord_info(coord_info_p, coordtype, page, score, staff, mll_feed_p, vis)
306
307struct COORD_INFO *coord_info_p;/* where to add -- assumed
308 * to be non-NULL */
309int coordtype; /* CT_BAR, CT_NOTE, etc */
310int page;
311int score;
312int staff;
313struct MAINLL *mll_feed_p; /* MAINLL containing FEED
314 * associated with score */
315int 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
341static void
342coord_staff(staff_p, page, score, mll_feed_p)
343
344struct STAFF *staff_p; /* get info from here */
345int page;
346int score;
347struct 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
393static void
394split_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
432static void
433chkline(mll_p)
434
435struct 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
718static void
719coordcheck(x_info_p, y_info_p, fname, lineno)
720
721struct COORD_INFO *x_info_p;
722struct COORD_INFO *y_info_p;
723char *fname;
724int 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
748static int
749eff_staff(yinfo_p)
750
751struct 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
777static double
778find_effXlength(seg1xlen, seg2xlen, x1info_p, x2info_p, save_feed_info)
779
780double seg1xlen; /* length of first part */
781double seg2xlen; /* length of last part */
782struct COORD_INFO *x1info_p; /* info about beginning point */
783struct COORD_INFO *x2info_p; /* info about last point */
784int 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
846static void
847svfeed(mll_feed_p, xlength)
848
849struct MAINLL *mll_feed_p;
850double 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
868static void
869add_segment(seginfo_p, slope, y_offset, staffno1, linetype)
870
871struct SEGINFO *seginfo_p;
872double slope;
873double y_offset; /* offset from staff of beginning point */
874int staffno1; /* staff associated with y of beginning */
875int 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
940static double
941getYstaff(mll_p, staffno)
942
943struct MAINLL *mll_p;
944int 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
962static void
963chkcurve(mll_p)
964
965struct 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
1405static int
1406bulgedir(curve_p, index, inputfile, inputlineno)
1407
1408struct CURVE *curve_p;
1409int index; /* check bulge dir between this point in array and
1410 * the next one */
1411char *inputfile; /* where curve was defined */
1412int 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
1470static int
1471cmpcoords(curve_p, p1, p2)
1472
1473struct CURVE *curve_p;
1474int p1;
1475int 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
1541static int
1542abs2rel(vtype)
1543
1544int 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
1564static void
1565add_crv_seg(seginfo_p, slope, y_offset, staffno1, curvetype, bulge, filename, lineno)
1566
1567struct SEGINFO *seginfo_p;
1568double slope;
1569double y_offset; /* offset from staff of beginning point */
1570int staffno1; /* staff associated with y of beginning */
1571int curvetype;
1572int bulge; /* 1 for bulge up, -1 for bulge down */
1573char *filename; /* where original curve was defined */
1574int 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
1655static int
1656is_invis(cinfo_p)
1657
1658struct 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
1676static int
1677is_builtin(cinfo_p)
1678
1679struct 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
1689static void
1690move2correct_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
1882static void
1883move_it(m_p, im_p, page)
1884
1885struct MAINLL *m_p; /* move this */
1886struct MAINLL *im_p; /* insert here */
1887int 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
1911static void
1912move2pseudo()
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
1954static void
1955do_pseudo(inpc_p, mll_p)
1956
1957struct INPCOORD *inpc_p;
1958struct 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
1992static void
1993fix_inpcoords(mll_p)
1994
1995struct 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
2021static void
2022adj_coord(coord_p, mll_p, prev_coord_p)
2023
2024struct INPCOORD *coord_p; /* what to potentially adjust */
2025struct MAINLL *mll_p; /* points to the line or curve containing coord_p */
2026struct 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
2184static void
2185calc_bulge(curve_p, fname, lineno, is_split, mll_p)
2186
2187struct CURVE *curve_p; /* curve defined using bulge */
2188char *fname;
2189int lineno;
2190int is_split; /* YES if goes across at least one FEED */
2191struct 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}