]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-engraver.cc
* lily/spacing-determine-loose-columns.cc: new file.
[lilypond.git] / lily / spacing-engraver.cc
1 /*
2   spacing-engraver.cc -- implement Spacing_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1999--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "paper-column.hh"
10 #include "engraver.hh"
11 #include "pqueue.hh"
12 #include "note-spacing.hh"
13 #include "staff-spacing.hh"
14 #include "pointer-group-interface.hh"
15 #include "spanner.hh"
16
17 #include "translator.icc"
18
19 struct Rhythmic_tuple
20 {
21   Grob_info info_;
22   Moment end_;
23
24   Rhythmic_tuple ()
25   {
26   }
27   Rhythmic_tuple (Grob_info i, Moment m)
28   {
29     info_ = i;
30     end_ = m;
31   }
32   static int time_compare (Rhythmic_tuple const &, Rhythmic_tuple const &);
33 };
34
35
36 /*
37   TODO: allow starting & stopping of spacing regions.
38  */
39 /*
40    Acknowledge rhythmic elements, for initializing spacing fields in
41    the columns.
42 */
43 class Spacing_engraver : public Engraver
44 {
45   PQueue<Rhythmic_tuple> playing_durations_;
46   Array<Rhythmic_tuple> now_durations_;
47   Array<Rhythmic_tuple> stopped_durations_;
48   Moment now_;
49   Spanner *spacing_;
50
51   TRANSLATOR_DECLARATIONS (Spacing_engraver);
52   
53 protected:
54   DECLARE_ACKNOWLEDGER (staff_spacing);
55   DECLARE_ACKNOWLEDGER (note_spacing);
56   DECLARE_ACKNOWLEDGER (rhythmic_head);
57   
58   void start_translation_timestep ();
59   void stop_translation_timestep ();
60   void process_music ();
61   virtual void finalize ();
62 };
63
64 inline int
65 compare (Rhythmic_tuple const &a, Rhythmic_tuple const &b)
66 {
67   return Rhythmic_tuple::time_compare (a, b);
68 }
69
70 int
71 Rhythmic_tuple::time_compare (Rhythmic_tuple const &h1,
72                               Rhythmic_tuple const &h2)
73 {
74   return (h1.end_ - h2.end_).main_part_.sign ();
75 }
76
77 Spacing_engraver::Spacing_engraver ()
78 {
79   spacing_ = 0;
80 }
81
82 void
83 Spacing_engraver::process_music ()
84 {
85   if (!spacing_)
86     {
87       spacing_ = make_spanner ("SpacingSpanner", SCM_EOL);
88       spacing_->set_bound (LEFT, unsmob_grob (get_property ("currentCommandColumn")));
89     }
90 }
91
92 void
93 Spacing_engraver::finalize ()
94 {
95   if (spacing_)
96     {
97       Grob *p = unsmob_grob (get_property ("currentCommandColumn"));
98
99       spacing_->set_bound (RIGHT, p);
100       spacing_ = 0;
101     }
102 }
103
104 void
105 Spacing_engraver::acknowledge_note_spacing (Grob_info i)
106 {
107   Pointer_group_interface::add_grob (spacing_, ly_symbol2scm ("wishes"), i.grob ());
108 }
109
110 void
111 Spacing_engraver::acknowledge_staff_spacing (Grob_info i)
112 {
113   Pointer_group_interface::add_grob (spacing_, ly_symbol2scm ("wishes"), i.grob ());
114 }
115   
116 void
117 Spacing_engraver::acknowledge_rhythmic_head (Grob_info i)
118 {
119   if (i.grob ()->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface"))
120       || i.grob ()->internal_has_interface (ly_symbol2scm ("multi-measure-interface")))
121     return;
122
123   /*
124     only pay attention to durations that are not grace notes.
125   */
126   if (!now_.grace_part_)
127     {
128       Music *r = i.music_cause ();
129       if (r && r->is_mus_type ("rhythmic-event"))
130         {
131           Moment len = r->get_length ();
132           Rhythmic_tuple t (i, now_mom () + len);
133           now_durations_.push (t);
134         }
135     }
136 }
137
138 void
139 Spacing_engraver::stop_translation_timestep ()
140 {
141   Paper_column *musical_column
142     = dynamic_cast<Paper_column *> (unsmob_grob (get_property ("currentMusicalColumn")));
143   
144   SCM proportional = get_property ("proportionalNotationDuration");
145   if (unsmob_moment (proportional))
146     {
147       musical_column->set_property ("shortest-playing-duration", proportional);
148       musical_column->set_property ("shortest-starter-duration", proportional);
149       return; 
150     }
151   
152   Moment shortest_playing;
153   shortest_playing.set_infinite (1);
154   for (int i = 0; i < playing_durations_.size (); i++)
155     {
156       Music *mus = playing_durations_[i].info_.music_cause ();
157       if (mus)
158         {
159           Moment m = mus->get_length ();
160           shortest_playing = min (shortest_playing, m);
161         }
162     }
163   Moment starter;
164   starter.set_infinite (1);
165
166   for (int i = 0; i < now_durations_.size (); i++)
167     {
168       Moment m = now_durations_[i].info_.music_cause ()->get_length ();
169       if (m.to_bool ())
170         {
171           starter = min (starter, m);
172           playing_durations_.insert (now_durations_[i]);
173         }
174     }
175   now_durations_.clear ();
176
177   shortest_playing = min (shortest_playing, starter);
178
179  
180   assert (starter.to_bool ());
181   SCM sh = shortest_playing.smobbed_copy ();
182   SCM st = starter.smobbed_copy ();
183
184   musical_column->set_property ("shortest-playing-duration", sh);
185   musical_column->set_property ("shortest-starter-duration", st);
186 }
187
188 void
189 Spacing_engraver::start_translation_timestep ()
190 {
191   now_ = now_mom ();
192   stopped_durations_.clear ();
193   while (playing_durations_.size () && playing_durations_.front ().end_ < now_)
194     playing_durations_.delmin ();
195   while (playing_durations_.size () && playing_durations_.front ().end_ == now_)
196     stopped_durations_.push (playing_durations_.get ());
197 }
198
199 ADD_ACKNOWLEDGER (Spacing_engraver, staff_spacing);
200 ADD_ACKNOWLEDGER (Spacing_engraver, note_spacing);
201 ADD_ACKNOWLEDGER (Spacing_engraver, rhythmic_head);
202   
203 ADD_TRANSLATOR (Spacing_engraver,
204                 "make a SpacingSpanner and do "
205                 "bookkeeping of shortest starting and playing notes  ",
206
207                 /* creats*/ "SpacingSpanner",
208                 /* accepts */ "",
209                 /* reads */ "currentMusicalColumn currentCommandColumn proportionalNotationDuration",
210                 /* write */ "");