]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-engraver.cc
* lily/context.cc (where_defined): also assign value in
[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 struct Rhythmic_tuple
18 {
19   Grob_info info_;
20   Moment end_;
21
22   Rhythmic_tuple ()
23   {
24   }
25   Rhythmic_tuple (Grob_info i, Moment m)
26   {
27     info_ = i;
28     end_ = m;
29   }
30   static int time_compare (Rhythmic_tuple const &, Rhythmic_tuple const &);
31 };
32
33 /**
34    Acknowledge rhythmic elements, for initializing spacing fields in
35    the columns.
36
37    should be the  last one of the toplevel context
38 */
39 class Spacing_engraver : public Engraver
40 {
41   PQueue<Rhythmic_tuple> playing_durations_;
42   Array<Rhythmic_tuple> now_durations_;
43   Array<Rhythmic_tuple> stopped_durations_;
44   Moment now_;
45   Spanner *spacing_;
46
47   TRANSLATOR_DECLARATIONS (Spacing_engraver);
48 protected:
49   DECLARE_ACKNOWLEDGER(staff_spacing);
50   DECLARE_ACKNOWLEDGER(note_spacing);
51   DECLARE_ACKNOWLEDGER(rhythmic_head);
52   PRECOMPUTED_VIRTUAL void start_translation_timestep ();
53   PRECOMPUTED_VIRTUAL void stop_translation_timestep ();
54   PRECOMPUTED_VIRTUAL void process_music ();
55   virtual void finalize ();
56 };
57
58 inline int
59 compare (Rhythmic_tuple const &a, Rhythmic_tuple const &b)
60 {
61   return Rhythmic_tuple::time_compare (a, b);
62 }
63
64 int
65 Rhythmic_tuple::time_compare (Rhythmic_tuple const &h1,
66                               Rhythmic_tuple const &h2)
67 {
68   return (h1.end_ - h2.end_).main_part_.sign ();
69 }
70
71 Spacing_engraver::Spacing_engraver ()
72 {
73   spacing_ = 0;
74 }
75
76 void
77 Spacing_engraver::process_music ()
78 {
79   if (!spacing_)
80     {
81       spacing_ = make_spanner ("SpacingSpanner", SCM_EOL);
82       spacing_->set_bound (LEFT, unsmob_grob (get_property ("currentCommandColumn")));
83     }
84 }
85
86 void
87 Spacing_engraver::finalize ()
88 {
89   if (spacing_)
90     {
91       Grob *p = unsmob_grob (get_property ("currentCommandColumn"));
92
93       spacing_->set_bound (RIGHT, p);
94       spacing_ = 0;
95     }
96 }
97
98 void
99 Spacing_engraver::acknowledge_note_spacing (Grob_info i)
100 {
101   Pointer_group_interface::add_grob (spacing_, ly_symbol2scm ("wishes"), i.grob ());
102 }
103
104 void
105 Spacing_engraver::acknowledge_staff_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_rhythmic_head (Grob_info i)
112 {
113   if (i.grob ()->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface"))
114       || i.grob ()->internal_has_interface (ly_symbol2scm ("multi-measure-interface")))
115     return;
116
117   /*
118     only pay attention to durations that are not grace notes.
119   */
120   if (!now_.grace_part_)
121     {
122       Music *r = i.music_cause ();
123       if (r && r->is_mus_type ("rhythmic-event"))
124         {
125           Moment len = r->get_length ();
126           Rhythmic_tuple t (i, now_mom () + len);
127           now_durations_.push (t);
128         }
129     }
130 }
131
132 void
133 Spacing_engraver::stop_translation_timestep ()
134 {
135   Moment shortest_playing;
136   shortest_playing.set_infinite (1);
137   for (int i = 0; i < playing_durations_.size (); i++)
138     {
139       Music *mus = playing_durations_[i].info_.music_cause ();
140       if (mus)
141         {
142           Moment m = mus->get_length ();
143           shortest_playing = min (shortest_playing, m);
144         }
145     }
146   Moment starter;
147   starter.set_infinite (1);
148
149   for (int i = 0; i < now_durations_.size (); i++)
150     {
151       Moment m = now_durations_[i].info_.music_cause ()->get_length ();
152       if (m.to_bool ())
153         {
154           starter = min (starter, m);
155           playing_durations_.insert (now_durations_[i]);
156         }
157     }
158   now_durations_.clear ();
159
160   shortest_playing = min (shortest_playing, starter);
161
162   Paper_column *sc
163     = dynamic_cast<Paper_column *> (unsmob_grob (get_property ("currentMusicalColumn")));
164
165   assert (starter.to_bool ());
166   SCM sh = shortest_playing.smobbed_copy ();
167   SCM st = starter.smobbed_copy ();
168
169   sc->set_property ("shortest-playing-duration", sh);
170   sc->set_property ("shortest-starter-duration", st);
171 }
172
173 void
174 Spacing_engraver::start_translation_timestep ()
175 {
176   now_ = now_mom ();
177   stopped_durations_.clear ();
178   while (playing_durations_.size () && playing_durations_.front ().end_ < now_)
179     playing_durations_.delmin ();
180   while (playing_durations_.size () && playing_durations_.front ().end_ == now_)
181     stopped_durations_.push (playing_durations_.get ());
182 }
183
184 #include "translator.icc"
185
186 ADD_ACKNOWLEDGER(Spacing_engraver,staff_spacing);
187 ADD_ACKNOWLEDGER(Spacing_engraver,note_spacing);
188 ADD_ACKNOWLEDGER(Spacing_engraver,rhythmic_head);
189   
190 ADD_TRANSLATOR (Spacing_engraver,
191                 /* descr */ "make a SpacingSpanner and do bookkeeping of shortest starting and playing notes  ",
192                 /* creats*/ "SpacingSpanner",
193                 /* accepts */ "",
194                 /* reads */ "",
195                 /* write */ "");