]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-basic.cc
* lily/spacing-basic.cc (standard_breakable_column_spacing): also
[lilypond.git] / lily / spacing-basic.cc
1 /*
2   spacing-basic.cc -- implement Spacing_spanner, simplistic spacing routines
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2005--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "spacing-spanner.hh"
10 #include "moment.hh"
11 #include "paper-column.hh"
12 #include "misc.hh"
13 #include "warn.hh"
14
15 /*
16   LilyPond spaces by taking a simple-minded spacing algorithm, and
17   adding subtle adjustments to that. This file does the simple-minded
18   spacing routines.
19 */
20
21 /*
22   Get the measure wide ant for arithmetic spacing.
23 */
24 Real
25 Spacing_options::get_duration_space (Moment d,
26                                      bool *expand_only) const
27 {
28   Real k = shortest_duration_space_;
29
30   if (d < global_shortest_)
31     {
32       /*
33         We don't space really short notes using the log of the
34         duration, since it would disproportionally stretches the long
35         notes in a piece. In stead, we use geometric spacing with constant 0.5
36         (i.e. linear.)
37
38         This should probably be tunable, to use other base numbers.
39
40         In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note =
41         3.6 mm (total).  head-width = 2.4, so we 1.2mm for 16th, 1.5
42         mm for 8th. (white space), suggesting that we use
43
44         (1.2 / 1.5)^{-log2(duration ratio)}
45
46
47       */
48       Rational ratio = d.main_part_ / global_shortest_;
49
50       return ((k - 1) + double (ratio)) * increment_;
51     }
52   else
53     {
54       /*
55         John S. Gourlay. ``Spacing a Line of Music, '' Technical
56         Report OSU-CISRC-10/87-TR35, Department of Computer and
57         Information Science, The Ohio State University, 1987.
58       */
59       Real log = log_2 (global_shortest_);
60       k -= log;
61       Rational compdur = d.main_part_ + d.grace_part_ / Rational (3);
62       *expand_only = false;
63
64       return (log_2 (compdur) + k) * increment_;
65     }
66 }
67
68 /*
69   The one-size-fits all spacing. It doesn't take into account
70   different spacing wishes from one to the next column.
71 */
72 void
73 Spacing_spanner::standard_breakable_column_spacing (Grob *me, Item *l, Item *r,
74                                                     Real *fixed, Real *space,
75                                                     Spacing_options const *options)
76 {
77   *fixed = 0.0;
78   Direction d = LEFT;
79   Drul_array<Item *> cols (l, r);
80
81   do
82     {
83       if (!Paper_column::is_musical (cols[d]))
84         {
85           /*
86             Tied accidentals over barlines cause problems, so lets see
87             what happens if we do this for non musical columns only.
88           */
89           Interval lext = cols[d]->extent (cols [d], X_AXIS);
90           if (!lext.is_empty ())
91             *fixed += -d * lext[-d];
92         }
93     }
94   while (flip (&d) != LEFT);
95
96   if ((l->is_breakable (l) || l->original ())
97       && (r->is_breakable (r) || r->original ()))
98     {
99       Moment *dt = unsmob_moment (l->get_property ("measure-length"));
100       Moment mlen (1);
101       if (dt)
102         mlen = *dt;
103
104       Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1);
105
106       *space = *fixed + incr * double (mlen.main_part_ / options->global_shortest_) * 0.8;
107     }
108   else
109     {
110       Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
111
112       if (dt == Moment (0, 0))
113         {
114           /*
115             In this case, Staff_spacing should handle the job,
116             using dt when it is 0 is silly.
117           */
118           *space = *fixed + 0.5;
119         }
120       else
121         {
122           bool dummy;
123           *space = *fixed + options->get_duration_space (dt, &dummy);
124         }
125     }
126 }
127
128 Real
129 Spacing_spanner::note_spacing (Grob *me, Grob *lc, Grob *rc,
130                                Spacing_options const *options,
131                                bool *expand_only)
132 {
133   Moment shortest_playing_len = 0;
134   SCM s = lc->get_property ("shortest-playing-duration");
135
136   if (unsmob_moment (s))
137     shortest_playing_len = *unsmob_moment (s);
138
139   if (! shortest_playing_len.to_bool ())
140     {
141       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).to_string ());
142       shortest_playing_len = 1;
143     }
144
145   Moment lwhen = Paper_column::when_mom (lc);
146   Moment rwhen = Paper_column::when_mom (rc);
147
148   Moment delta_t = rwhen - lwhen;
149   if (!Paper_column::is_musical (rc))
150     {
151       /*
152         when toying with mmrests, it is possible to have musical
153         column on the left and non-musical on the right, spanning
154         several measures.
155       */
156
157       Moment *dt = unsmob_moment (rc->get_property ("measure-length"));
158       if (dt)
159         {
160           delta_t = min (delta_t, *dt);
161
162           /*
163             The following is an extra safety measure, such that
164             the length of a mmrest event doesn't cause havoc.
165           */
166           shortest_playing_len = min (shortest_playing_len, *dt);
167         }
168     }
169
170   Real dist = 0.0;
171   if (delta_t.main_part_ && !lwhen.grace_part_)
172     {
173       dist = options->get_duration_space (shortest_playing_len,
174                                           expand_only);
175       dist *= double (delta_t.main_part_ / shortest_playing_len.main_part_);
176     }
177   else if (delta_t.grace_part_)
178     {
179       /*
180         Crude hack for spacing graces: we take the shortest space
181         available (namely the space for the global shortest note), and
182         multiply that by grace-space-factor
183       */
184       dist = options->get_duration_space (options->global_shortest_, expand_only);
185
186       Real grace_fact
187         = robust_scm2double (me->get_property ("grace-space-factor"), 1);
188
189       dist *= grace_fact;
190     }
191
192   return dist;
193 }
194
195 /****************************************************************/
196
197 void
198 Spacing_options::init_from_grob (Grob *me)
199 {
200   increment_ = robust_scm2double (me->get_property ("spacing-increment"), 1);
201
202   packed_ = to_boolean (me->get_property ("packed-spacing"));
203   stretch_uniformly_ = to_boolean (me->get_property ("uniform-stretching"));
204   float_nonmusical_columns_
205     = to_boolean (me->get_property ("strict-note-spacing"));
206   shortest_duration_space_ = robust_scm2double (me->get_property ("shortest-duration-space"), 1);
207 }
208
209 void
210 Spacing_options::init ()
211 {
212   increment_ = 1.2;
213   packed_ = false;
214   stretch_uniformly_ = false;
215   float_nonmusical_columns_ = false;
216   shortest_duration_space_ = 2.0;
217
218   global_shortest_ = Rational (1, 8);
219 }