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
) ;
28 The algorithm is partly taken from :
30 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
31 OSU-CISRC-10/87-TR35, Department of Computer and Information
32 Science, The Ohio State University, 1987.
40 Spacing_spanner::do_measure (Grob
*me
, Link_array
<Grob
> cols
)
46 space as if this duration is present.
48 Moment base_shortest_duration
= *unsmob_moment (me
->get_grob_property ("maximum-duration-for-spacing"));
49 shortest
.set_infinite (1);
52 for (int i
=0 ; i
< cols
.size (); i
++)
54 if (dynamic_cast<Paper_column
*> (cols
[i
])->musical_b ())
56 SCM st
= cols
[i
]->get_grob_property ("shortest-starter-duration");
57 Moment this_shortest
= *unsmob_moment (st
);
58 shortest
= shortest
<? this_shortest
;
59 if (!mean_shortest
.infty_b ())
62 mean_shortest
+= this_shortest
;
69 for (int i
= 0; i
< cols
.size () - 1; i
++)
71 Item
* l
= dynamic_cast<Item
*> (cols
[i
]);
72 Item
* r
= dynamic_cast<Item
*> (cols
[i
+1]);
73 Item
* lb
= dynamic_cast<Item
*> (l
->find_prebroken_piece (RIGHT
));
74 Item
* rb
= dynamic_cast<Item
*> (r
->find_prebroken_piece (LEFT
));
76 Item
* combinations
[4][2]={{l
,r
}, {lb
,r
}, {l
,rb
},{lb
,rb
}};
80 left refers to the space that is associated with items of the left column, so you have
82 LC <- left_space -><- right_space -> RC
86 typically, right_space is non-zero when there are
90 for (int j
=0; j
< 4; j
++)
92 Paper_column
* lc
= dynamic_cast<Paper_column
*> (combinations
[j
][0]);
93 Paper_column
*rc
= dynamic_cast<Paper_column
*> (combinations
[j
][1]);
98 s
.item_l_drul_
[LEFT
] = lc
;
99 s
.item_l_drul_
[RIGHT
] = rc
;
101 SCM hint
= lc
->get_grob_property ("extra-space");
102 SCM next_hint
= rc
->get_grob_property ("extra-space");
103 SCM stretch_hint
= lc
->get_grob_property ("stretch-distance");
104 SCM next_stretch_hint
= rc
->get_grob_property ("stretch-distance");
106 Real left_distance
= 0;
107 if (gh_pair_p (hint
))
109 left_distance
= gh_scm2double (gh_cdr (hint
));
111 // 2nd condition should be (i+1 < col_count ()), ie. not the last column in score. FIXME
112 else if (!lc
->musical_b () && i
+1 < cols
.size ())
114 left_distance
= default_bar_spacing (me
,lc
,rc
,shortest
<? base_shortest_duration
);
116 else if (lc
->musical_b ())
118 left_distance
= note_spacing (me
,lc
, rc
, shortest
<? base_shortest_duration
);
121 programming_error ("uninitialised left_distance");
123 s
.distance_f_
= left_distance
;
126 Only do tight spaces *after* barlines (breakable columns),
129 We want the space before barline to be like the note
130 spacing in the measure.
132 SCM sfac
=lc
->get_grob_property ("space-factor");
133 if (gh_number_p (lc
->get_grob_property ("column-space-strength"))
134 && (Item::breakable_b (lc
) || lc
->original_l_
))
137 gh_scm2double (lc
->get_grob_property ("column-space-strength"));
139 else if (gh_number_p (sfac
))
140 left_distance
*= gh_scm2double (sfac
);
143 Real right_dist
= 0.0;
144 if (gh_pair_p (next_hint
))
146 right_dist
+= - gh_scm2double (gh_car (next_hint
));
150 Interval
ext (rc
->extent (rc
, X_AXIS
));
151 right_dist
= ext
.empty_b () ? 0.0 : - ext
[LEFT
];
155 don't want to create too much extra space for accidentals
157 if (rc
->musical_b ())
159 if (to_boolean (rc
->get_grob_property ("contains-grace")))
160 right_dist
*= gh_scm2double (rc
->get_grob_property ("before-grace-spacing-factor")); // fixme.
162 right_dist
*= gh_scm2double (lc
->get_grob_property ("before-musical-spacing-factor"));
165 s
.distance_f_
= left_distance
+ right_dist
;
167 Real stretch_dist
= 0.;
168 if (gh_number_p (stretch_hint
))
169 stretch_dist
+= gh_scm2double (stretch_hint
);
171 stretch_dist
+= left_distance
;
173 if (gh_pair_p (next_stretch_hint
))
174 // see regtest spacing-tight
175 stretch_dist
+= - gh_scm2double (gh_car (next_stretch_hint
));
177 stretch_dist
+= right_dist
;
179 if (s
.distance_f_
<0)
181 programming_error ("Negative dist, setting to 1.0 PT");
184 if (stretch_dist
== 0.0)
187 \bar "". We give it 0 space, with high strength.
189 s
.strength_f_
= 20.0;
192 s
.strength_f_
/= stretch_dist
;
201 Do something if breakable column has no spacing hints set.
204 Spacing_spanner::default_bar_spacing (Grob
*me
, Grob
*lc
, Grob
*rc
,
207 Real symbol_distance
= lc
->extent (lc
,X_AXIS
)[RIGHT
] ;
208 Real durational_distance
= 0;
209 Moment delta_t
= Paper_column::when_mom (rc
) - Paper_column::when_mom (lc
);
212 ugh should use shortest_playing distance
216 durational_distance
= get_duration_space (me
, delta_t
, shortest
);
219 return symbol_distance
>? durational_distance
;
224 Get the measure wide ant for arithmetic spacing.
227 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
228 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
229 The Ohio State University, 1987.
233 Spacing_spanner::get_duration_space (Grob
*me
, Moment d
, Moment shortest
)
235 Real log
= log_2 (shortest
);
236 Real k
= gh_scm2double (me
->get_grob_property ("arithmetic-basicspace"))
239 return (log_2 (d
) + k
) * gh_scm2double (me
->get_grob_property ("arithmetic-multiplier"));
244 Spacing_spanner::note_spacing (Grob
*me
, Grob
*lc
, Grob
*rc
,
247 Moment shortest_playing_len
= 0;
248 SCM s
= lc
->get_grob_property ("shortest-playing-duration");
250 // SCM s = lc->get_grob_property ("mean-playing-duration");
251 if (unsmob_moment (s
))
252 shortest_playing_len
= *unsmob_moment (s
);
254 if (! shortest_playing_len
)
256 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc
).str ());
257 shortest_playing_len
= 1;
262 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc
).str ());
265 Moment delta_t
= Paper_column::when_mom (rc
) - Paper_column::when_mom (lc
);
266 Real dist
= get_duration_space (me
, shortest_playing_len
, shortest
);
267 dist
*= (double) (delta_t
/ shortest_playing_len
);
273 if (delta_t
> Moment (1,32))
274 dist
+= stem_dir_correction (me
, lc
,rc
);
280 Correct for optical illusions. See [Wanske] p. 138. The combination
281 up-stem + down-stem should get extra space, the combination
282 down-stem + up-stem less.
284 This should be more advanced, since relative heights of the note
285 heads also influence required correction.
287 Also might not work correctly in case of multi voices or staff
290 TODO: lookup correction distances? More advanced correction?
291 Possibly turn this off?
293 TODO: have to check wether the stems are in the same staff.
295 This routine reads the DIR-LIST property of both its L and R arguments. */
297 Spacing_spanner::stem_dir_correction (Grob
*me
, Grob
*l
, Grob
*r
)
299 SCM dl
= l
->get_grob_property ("dir-list");
300 SCM dr
= r
->get_grob_property ("dir-list");
302 if (scm_ilength (dl
) != 1 || scm_ilength (dr
) != 1)
308 assert (gh_number_p (dl
) && gh_number_p (dr
));
309 int d1
= gh_scm2int (dl
);
310 int d2
= gh_scm2int (dr
);
316 Real correction
= 0.0;
317 Real ssc
= gh_scm2double (me
->get_grob_property ("stem-spacing-correction"));
319 if (d1
&& d2
&& d1
* d2
== -1)
321 correction
= d1
* ssc
;
324 programming_error ("Stem directions not set correctly for optical correction");
329 MAKE_SCHEME_CALLBACK (Spacing_spanner
, set_springs
,1);
331 Spacing_spanner::set_springs (SCM smob
)
333 Grob
*me
= unsmob_grob (smob
);
334 Link_array
<Grob
> all (me
->pscore_l_
->line_l_
->column_l_arr ()) ;
338 for (int i
= 1; i
< all
.size (); i
++)
341 if (Item::breakable_b (sc
))
343 Link_array
<Grob
> measure (all
.slice (j
, i
+1));
344 do_measure (me
, measure
);
350 farewell, cruel world
353 return SCM_UNSPECIFIED
;
359 maximum-duration-for-spacing
360 From: bf250@freenet.carleton.ca (John Sankey)
361 To: gnu-music-discuss@gnu.org
362 Subject: note spacing suggestion
363 Date: Mon, 10 Jul 2000 11:28:03 -0400 (EDT)
365 Currently, Lily spaces notes by starting with a basic distance,
366 arithmetic_multiplier, which it applies to the minimum duration note
367 of the bar. Then she adds a logarithmic increment, scaled from
368 arithmetic_basicspace, for longer notes. (Then, columns are aligned
369 and justified.) Fundamentally, this matches visual spacing to musical
370 weight and works well.
372 A lot of the time in music, I see a section basically in melodic
373 notes that occasionally has a rapid ornamental run (scale). So, there
374 will be a section in 1/4 notes, then a brief passage in 1/32nds, then
375 a return to long notes. Currently, Lily gives the same horizontal
376 space to the 1/32nd notes in their bar (even if set in small size as
377 is commonly done for cadenzii) as she gives to 1/4 notes in bars
378 where 1/4 note is the minimum duration. The resulting visual weight
379 does not match the musical weight over the page.
381 Looking at the music I am typesetting, I feel that Lily's spacing
382 could be significantly improved if, with no change in the basic
383 method used, arithmetic_multiplier could be applied referred to the
384 same duration throughout a piece. Of course, the current method
385 should be retained for those who have already set music in it, so I
386 suggest a property called something like arithmetic_base=16 to fix
387 1/16 duration as the reference for arithmetic_multiplier; the default
388 would be a dynamic base is it is now.
390 Does anyone else feel that this would be a useful improvement for
391 their music? (Of course, if arithmetic_multiplier became a regular
392 property, this could be used to achieve a similar result by