2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
10 #include "spacing-spanner.hh"
11 #include "paper-column.hh"
12 #include "dimensions.hh"
13 #include "paper-def.hh"
15 #include "paper-score.hh"
16 #include "line-of-score.hh"
20 Spacing_spanner::set_interface (Grob
*me
)
22 me
->set_extent_callback (SCM_EOL
, X_AXIS
);
23 me
->set_extent_callback (SCM_EOL
, Y_AXIS
) ;
36 column_compare (Grob
*const &t1
, Grob
*const &t2
)
38 return Moment::compare (Paper_column::when_mom (t1
),
39 Paper_column::when_mom (t2
));
44 run_length (Moment dt
, int i
, Array
<Moment
> const &moms
,
45 Link_array
<Note_run
> runs
)
53 Moment next
= moms
[i
] + dt
;
54 while (i
< moms
.size () && moms
[i
] < next
)
56 if (i
== moms
.size () || moms
[i
] != next
)
63 Moment dur
= idxes
.size ()
67 find_runs (Grob
*me
, Link_array
<Grob
> cols
)
69 Link_array
<Grob
> filter_cols
;
70 Array
<Moment
> col_moments
;
71 for (int i
= 0; i
< cols
.size (); i
++)
73 Moment w
= Paper_column::when_mom (cols
[i
]);
75 if (!w
.grace_part_
&& Paper_column::musical_b (cols
[i
]))
77 filter_cols
.push (cols
[i
]);
82 Moment end_mom
= col_moments
.top ();
83 for (int i
= 0; i
< col_moments
.size () ; i
++)
85 for (int j
= i
+1; j
< col_moments
.size (); j
++)
87 Moment dt
= Paper_column::col_momentsfilter_cols
95 The algorithm is partly taken from :
97 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
98 OSU-CISRC-10/87-TR35, Department of Computer and Information
99 Science, The Ohio State University, 1987.
107 Spacing_spanner::do_measure (Grob
*me
, Link_array
<Grob
> const & cols
)
110 Moment mean_shortest
;
113 space as if this duration is present.
115 Moment base_shortest_duration
= *unsmob_moment (me
->get_grob_property ("maximum-duration-for-spacing"));
116 shortest
.set_infinite (1);
119 for (int i
=0 ; i
< cols
.size (); i
++)
121 if (Paper_column::musical_b (cols
[i
]))
123 Moment
*when
= unsmob_moment (cols
[i
]->get_grob_property ("when"));
126 ignore grace notes for shortest notes.
128 if (when
&& when
->grace_part_
)
131 SCM st
= cols
[i
]->get_grob_property ("shortest-starter-duration");
132 Moment this_shortest
= *unsmob_moment (st
);
133 shortest
= shortest
<? this_shortest
;
134 if (!mean_shortest
.main_part_
.infty_b ())
137 mean_shortest
+= this_shortest
;
143 Array
<Spring
> springs
;
144 for (int i
= 0; i
< cols
.size () - 1; i
++)
146 Item
* l
= dynamic_cast<Item
*> (cols
[i
]);
147 Item
* r
= dynamic_cast<Item
*> (cols
[i
+1]);
148 Item
* lb
= dynamic_cast<Item
*> (l
->find_prebroken_piece (RIGHT
));
149 Item
* rb
= dynamic_cast<Item
*> (r
->find_prebroken_piece (LEFT
));
151 Item
* combinations
[4][2]={{l
,r
}, {lb
,r
}, {l
,rb
},{lb
,rb
}};
155 left refers to the space that is associated with items of the left column, so you have
157 LC <- left_space -><- right_space -> RC
161 typically, right_space is non-zero when there are
165 for (int j
=0; j
< 4; j
++)
167 Paper_column
* lc
= dynamic_cast<Paper_column
*> (combinations
[j
][0]);
168 Paper_column
*rc
= dynamic_cast<Paper_column
*> (combinations
[j
][1]);
173 s
.item_l_drul_
[LEFT
] = lc
;
174 s
.item_l_drul_
[RIGHT
] = rc
;
176 SCM hint
= lc
->get_grob_property ("extra-space");
177 SCM next_hint
= rc
->get_grob_property ("extra-space");
178 SCM stretch_hint
= lc
->get_grob_property ("stretch-distance");
179 SCM next_stretch_hint
= rc
->get_grob_property ("stretch-distance");
181 Real left_distance
= 0;
182 if (gh_pair_p (hint
))
184 left_distance
= gh_scm2double (gh_cdr (hint
));
186 // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score. FIXME
187 else if (!Paper_column::musical_b (lc
) && i
+1 < cols
.size ())
189 left_distance
= default_bar_spacing (me
,lc
,rc
,shortest
<? base_shortest_duration
);
191 else if (Paper_column::musical_b ( lc
))
193 left_distance
= note_spacing (me
,lc
, rc
, shortest
<? base_shortest_duration
);
196 programming_error ("uninitialised left_distance");
198 s
.distance_f_
= left_distance
;
201 Only do tight spaces *after* barlines (breakable columns),
204 We want the space before barline to be like the note
205 spacing in the measure.
207 SCM sfac
=lc
->get_grob_property ("space-factor");
208 if (gh_number_p (lc
->get_grob_property ("column-space-strength"))
209 && (Item::breakable_b (lc
) || lc
->original_l_
))
212 gh_scm2double (lc
->get_grob_property ("column-space-strength"));
214 else if (gh_number_p (sfac
))
215 left_distance
*= gh_scm2double (sfac
);
218 Real right_dist
= 0.0;
219 if (gh_pair_p (next_hint
))
221 right_dist
+= - gh_scm2double (gh_car (next_hint
));
225 Interval
ext (rc
->extent (rc
, X_AXIS
));
226 right_dist
= ext
.empty_b () ? 0.0 : - ext
[LEFT
];
230 don't want to create too much extra space for accidentals
232 if (Paper_column::musical_b (rc
))
234 if (to_boolean (rc
->get_grob_property ("contains-grace")))
235 right_dist
*= gh_scm2double (rc
->get_grob_property ("before-grace-spacing-factor")); // fixme.
237 right_dist
*= gh_scm2double (lc
->get_grob_property ("before-musical-spacing-factor"));
240 s
.distance_f_
= left_distance
+ right_dist
;
242 Real stretch_dist
= 0.;
243 if (gh_number_p (stretch_hint
))
244 stretch_dist
+= gh_scm2double (stretch_hint
);
246 stretch_dist
+= left_distance
;
248 if (gh_pair_p (next_stretch_hint
))
249 // see regtest spacing-tight
250 stretch_dist
+= - gh_scm2double (gh_car (next_stretch_hint
));
252 stretch_dist
+= right_dist
;
254 if (s
.distance_f_
<0)
256 programming_error ("Negative dist, setting to 1.0 PT");
259 if (stretch_dist
== 0.0)
262 \bar "". We give it 0 space, with high strength.
264 s
.strength_f_
= 20.0;
267 s
.strength_f_
/= stretch_dist
;
273 Spacing_spanner::stretch_to_regularity (me
, &springs
, cols
);
274 for (int i
=springs
.size (); i
--;)
275 springs
[i
].add_to_cols ();
279 Look at COLS, searching for columns that have 'regular-distance-to
280 set. A sequence of columns that have this property set should have
281 an equal distance (an equispaced run). Extract the projected
282 distance from SPRINGS, and scale SPRINGS for the equispaced run, to the
283 widest space necessary.
288 -- inefficient code; maybe it is easier to twiddle with the springs
289 after they've become grob properties (ie. have their
290 minimum-distances set)
292 -- does not adjust strength field of the springs very well: result
293 awkward spacing at the start of a line. (?)
295 -- will be confused when there are multiple equispaced runs in a measure.
297 -- dealing with springs for line breaks is a little tricky; in any
298 case, we will only space per measure.
300 -- we scale to actual distances, not to optical effects. Eg. if the
301 equispaced run contains optical corrections, then the scaling will
304 -- Regular_spacing_engraver doesn't mark the first column of the
305 next bar, making the space before a barline too short, in this case
309 x(8th) x(8th) <- equispaced run.
314 Spacing_spanner::stretch_to_regularity (Grob
*me
,
315 Array
<Spring
> * springs
,
316 Link_array
<Grob
> const & cols
)
319 Find the starting column of the run. REGULAR-DISTANCE-TO points
320 back to a previous column, so we look ahead to find a column
321 pointing back to the first one.
324 Grob
* first_regular_spaced_col
= 0;
325 for (int i
= 0 ; i
< cols
.size () && !first_regular_spaced_col
; i
++)
327 SCM rdt
= cols
[i
]->get_grob_property ("regular-distance-to");
328 if (cols
.find_l (unsmob_grob (rdt
)))
329 first_regular_spaced_col
= unsmob_grob (rdt
);
331 for (int i
= springs
->size (); i
-- ;)
332 springs
->elem (i
).set_to_cols ();
335 for (i
= 0; i
< springs
->size ()
336 && springs
->elem (i
).item_l_drul_
[RIGHT
] != first_regular_spaced_col
;
341 if (i
==springs
->size ())
346 Grob
*last_col
= first_regular_spaced_col
;
347 Grob
*last_regular_spaced_col
= first_regular_spaced_col
;
351 find the max distance for this run.
353 for (int j
= i
; j
< springs
->size (); j
++)
355 Spring
*s
= &(springs
->elem_ref (j
));
356 if (s
->item_l_drul_
[LEFT
] != last_col
)
359 dist
+= s
->distance_f_
;
361 last_col
= s
->item_l_drul_
[RIGHT
];
362 SCM rdt
= last_col
->get_grob_property ("regular-distance-to");
363 if (unsmob_grob (rdt
) == last_regular_spaced_col
)
365 maxdist
= maxdist
>? dist
;
367 last_regular_spaced_col
= last_col
;
376 last_col
= first_regular_spaced_col
;
377 last_regular_spaced_col
= first_regular_spaced_col
;
378 for (int j
= i
; j
< springs
->size (); j
++)
380 Spring
*s
= &springs
->elem_ref (j
);
381 if (s
->item_l_drul_
[LEFT
] != last_col
)
383 dist
+= s
->distance_f_
;
385 last_col
= s
->item_l_drul_
[RIGHT
];
386 SCM rdt
= last_col
->get_grob_property ("regular-distance-to");
387 if (unsmob_grob (rdt
) == last_regular_spaced_col
)
390 springs
->elem_ref (i
).distance_f_
*= maxdist
/ dist
;
391 springs
->elem_ref (i
).strength_f_
*= dist
/ maxdist
;
393 last_regular_spaced_col
= last_col
;
400 Do something if breakable column has no spacing hints set.
403 Spacing_spanner::default_bar_spacing (Grob
*me
, Grob
*lc
, Grob
*rc
,
406 Real symbol_distance
= lc
->extent (lc
,X_AXIS
)[RIGHT
] ;
407 Real durational_distance
= 0;
408 Moment delta_t
= Paper_column::when_mom (rc
) - Paper_column::when_mom (lc
);
411 ugh should use shortest_playing distance
413 if (delta_t
.to_bool ())
415 durational_distance
= get_duration_space (me
, delta_t
, shortest
);
418 return symbol_distance
>? durational_distance
;
423 Get the measure wide ant for arithmetic spacing.
426 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
427 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
428 The Ohio State University, 1987.
432 Spacing_spanner::get_duration_space (Grob
*me
, Moment d
, Moment shortest
)
434 Real log
= log_2 (shortest
.main_part_
);
435 Real k
= gh_scm2double (me
->get_grob_property ("arithmetic-basicspace"))
438 Rational compdur
= d
.main_part_
+ d
.grace_part_
/ Rational (3);
439 return (log_2 (compdur
) + k
) * gh_scm2double (me
->get_grob_property ("arithmetic-multiplier"));
444 Spacing_spanner::note_spacing (Grob
*me
, Grob
*lc
, Grob
*rc
,
447 Moment shortest_playing_len
= 0;
448 SCM s
= lc
->get_grob_property ("shortest-playing-duration");
450 // SCM s = lc->get_grob_property ("mean-playing-duration");
451 if (unsmob_moment (s
))
452 shortest_playing_len
= *unsmob_moment (s
);
454 if (! shortest_playing_len
.to_bool ())
456 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc
).str ());
457 shortest_playing_len
= 1;
460 if (! shortest
.to_bool ())
462 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc
).str ());
465 Moment delta_t
= Paper_column::when_mom (rc
) - Paper_column::when_mom (lc
);
466 Real dist
= get_duration_space (me
, shortest_playing_len
, shortest
);
470 ugh: 0.1 is an arbitrary distance.
472 dist
*= (double) (delta_t
.main_part_
/ shortest_playing_len
.main_part_
)
473 + 0.1 * (double) (delta_t
.grace_part_
/ shortest_playing_len
.main_part_
);
481 if (delta_t
> Moment (Rational (1,32)))
482 dist
+= stem_dir_correction (me
, lc
,rc
);
485 Moment
*lm
= unsmob_moment (lc
->get_grob_property ("when"));
486 Moment
*rm
= unsmob_moment (rc
->get_grob_property ("when"));
490 if (lm
->grace_part_
&& rm
->grace_part_
)
492 else if (!rm
->grace_part_
&& lm
->grace_part_
)
502 Correct for optical illusions. See [Wanske] p. 138. The combination
503 up-stem + down-stem should get extra space, the combination
504 down-stem + up-stem less.
506 This should be more advanced, since relative heights of the note
507 heads also influence required correction.
509 Also might not work correctly in case of multi voices or staff
512 TODO: lookup correction distances? More advanced correction?
513 Possibly turn this off?
515 TODO: have to check wether the stems are in the same staff.
517 This routine reads the DIR-LIST property of both its L and R arguments. */
519 Spacing_spanner::stem_dir_correction (Grob
*me
, Grob
*l
, Grob
*r
)
521 SCM dl
= l
->get_grob_property ("dir-list");
522 SCM dr
= r
->get_grob_property ("dir-list");
524 if (scm_ilength (dl
) != 1 || scm_ilength (dr
) != 1)
530 assert (gh_number_p (dl
) && gh_number_p (dr
));
531 int d1
= gh_scm2int (dl
);
532 int d2
= gh_scm2int (dr
);
538 Real correction
= 0.0;
539 Real ssc
= gh_scm2double (me
->get_grob_property ("stem-spacing-correction"));
541 if (d1
&& d2
&& d1
* d2
== -1)
543 correction
= d1
* ssc
;
546 programming_error ("Stem directions not set correctly for optical correction");
551 MAKE_SCHEME_CALLBACK (Spacing_spanner
, set_springs
,1);
553 Spacing_spanner::set_springs (SCM smob
)
555 Grob
*me
= unsmob_grob (smob
);
556 Link_array
<Grob
> all (me
->pscore_l_
->line_l_
->column_l_arr ()) ;
560 for (int i
= 1; i
< all
.size (); i
++)
563 if (Item::breakable_b (sc
))
565 Link_array
<Grob
> measure (all
.slice (j
, i
+1));
566 do_measure (me
, measure
);
572 farewell, cruel world
575 return SCM_UNSPECIFIED
;
581 maximum-duration-for-spacing
582 From: bf250@freenet.carleton.ca (John Sankey)
583 To: gnu-music-discuss@gnu.org
584 Subject: note spacing suggestion
585 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
587 Currently, Lily spaces notes by starting with a basic distance,
588 arithmetic_multiplier, which it applies to the minimum duration note
589 of the bar. Then she adds a logarithmic increment, scaled from
590 arithmetic_basicspace, for longer notes. (Then, columns are aligned
591 and justified.) Fundamentally, this matches visual spacing to musical
592 weight and works well.
594 A lot of the time in music, I see a section basically in melodic
595 notes that occasionally has a rapid ornamental run (scale). So, there
596 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
597 a return to long notes. Currently, Lily gives the same horizontal
598 space to the 1/32nd notes in their bar (even if set in small size as
599 is commonly done for cadenzii) as she gives to 1/4 notes in bars
600 where 1/4 note is the minimum duration. The resulting visual weight
601 does not match the musical weight over the page.
603 Looking at the music I am typesetting, I feel that Lily's spacing
604 could be significantly improved if, with no change in the basic
605 method used, arithmetic_multiplier could be applied referred to the
606 same duration throughout a piece. Of course, the current method
607 should be retained for those who have already set music in it, so I
608 suggest a property called something like arithmetic_base=16 to fix
609 1/16 duration as the reference for arithmetic_multiplier; the default
610 would be a dynamic base is it is now.
612 Does anyone else feel that this would be a useful improvement for
613 their music? (Of course, if arithmetic_multiplier became a regular
614 property, this could be used to achieve a similar result by