]> git.donarmstrong.com Git - lilypond.git/blob - lily/cluster-engraver.cc
(warning): better robustness fix.
[lilypond.git] / lily / cluster-engraver.cc
1 /*
2   cluster-engraver.cc -- implement Cluster_engraver
3
4   (c) 2002 Juergen Reuter <reuter@ipd.uka.de>
5 */
6
7 #include "engraver.hh"
8 #include "item.hh"
9 #include "spanner.hh"
10 #include "note-head.hh"
11 #include "protected-scm.hh"
12 #include "warn.hh"
13
14 class Cluster_engraver : public Engraver
15 {
16
17 protected:
18 TRANSLATOR_DECLARATIONS(Cluster_engraver);
19   virtual void start_translation_timestep ();
20   virtual bool try_music (Music *);
21   virtual void process_music ();  
22   virtual void acknowledge_grob (Grob_info);
23   virtual void stop_translation_timestep ();
24
25 private:
26   Drul_array<Music*> reqs_drul_;
27   Pitch pitch_min_, pitch_max_;
28   Spanner *cluster_;
29   SCM columns_scm_;
30 };
31
32 void reset_min_max (Pitch *pitch_min, Pitch *pitch_max)
33 {
34   /*
35    * (pitch_min > pitch_max) means that pitches are not yet
36    * initialized
37    */
38   *pitch_min = Pitch (0, 0, +1);
39   *pitch_max = Pitch (0, 0, -1);
40 }
41
42 Cluster_engraver::Cluster_engraver ()
43 {
44   cluster_ = 0;
45   columns_scm_ = SCM_EOL;
46   reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0;
47 }
48
49 bool
50 Cluster_engraver::try_music (Music *m)
51 {
52   if (m->is_mus_type ("abort-event"))
53     {
54       reqs_drul_[START] = 0;
55       reqs_drul_[STOP] = 0;
56       if (cluster_)
57         {
58           cluster_->suicide ();
59           cluster_ = 0;
60           columns_scm_ = SCM_EOL;
61         }
62     }
63   else if (m->is_mus_type ("cluster-event"))
64     {
65       Direction d = to_dir (m->get_mus_property ("span-direction"));
66       reqs_drul_[d] = m;
67       return true;
68     }
69   return false;
70 }
71
72 void
73 Cluster_engraver::process_music ()
74 {
75   if (reqs_drul_[STOP])
76     {
77       if (!cluster_)
78         {
79           reqs_drul_[STOP]->origin ()->warning ("can't find start of cluster");
80         }
81       else
82         {
83           Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
84           cluster_->set_bound (RIGHT, bound);
85         }
86     }
87   if (reqs_drul_[START])
88     {
89       if (cluster_)
90         {
91           reqs_drul_[START]->origin ()->warning ("may not nest clusters");
92         }
93       else
94         {
95           SCM basicProperties = get_property ("Cluster");
96           cluster_ = new Spanner (basicProperties);
97           columns_scm_ = SCM_EOL;
98           Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
99           cluster_->set_bound (LEFT, bound);
100           announce_grob (cluster_, bound->self_scm ());
101         }
102       reqs_drul_[START] = 0;
103     }
104 }
105
106 void
107 Cluster_engraver::start_translation_timestep ()
108 {
109   reset_min_max (&pitch_min_, &pitch_max_);
110 }
111
112 void
113 Cluster_engraver::stop_translation_timestep ()
114 {
115   if (cluster_)
116     {
117       SCM column_scm = get_property ("currentMusicalColumn");
118       if (column_scm == SCM_EOL)
119         {
120           programming_error("failed retrieving current column");
121         }
122       else
123         {
124           if (Pitch::compare (pitch_min_, pitch_max_) <= 0)
125             {
126               Real y_bottom = 0.5 * pitch_min_.steps ();
127               SCM c0 = get_property ("centralCPosition");
128               if (gh_number_p (c0))
129                 y_bottom += 0.5 * gh_scm2int (c0);
130               else
131                 programming_error ("Cluster_engraver: failed evaluating c0");
132               Real y_top = y_bottom +
133                 0.5 * (pitch_max_.steps () - pitch_min_.steps ());
134               column_scm = Protected_scm (column_scm);
135               SCM segment = scm_list_n (column_scm,
136                                         gh_double2scm (y_bottom),
137                                         gh_double2scm (y_top),
138                                         SCM_UNDEFINED);
139               segment = scm_list_n (segment, SCM_UNDEFINED);
140               columns_scm_ = (columns_scm_ != SCM_EOL) ?
141                 gh_append2 (columns_scm_, segment) : segment;
142               cluster_->set_grob_property ("segments", columns_scm_); // Urrgh!
143             }
144           else
145             {
146               /* This timestep is caused by a different voice of the
147                  same staff and hence should be ignored. */
148             }
149         }
150
151       if (reqs_drul_[STOP])
152         {
153           reqs_drul_[STOP] = 0;
154           cluster_->set_grob_property ("segments", columns_scm_);
155           typeset_grob (cluster_);
156           cluster_ = 0;
157           columns_scm_ = SCM_EOL;
158         }
159     }
160 }
161
162 void
163 Cluster_engraver::acknowledge_grob (Grob_info info)
164 {
165   Item *item = dynamic_cast <Item *>(info.grob_);
166   if (item)
167     {
168       if (Note_head::has_interface (info.grob_))
169         {
170           Music *nr = info.music_cause ();
171           if (nr && nr->is_mus_type ("note-event"))
172             {
173               Pitch pitch = *unsmob_pitch (nr->get_mus_property ("pitch"));
174               if (Pitch::compare (pitch_min_, pitch_max_) > 0) // already init'd?
175                 {
176                   // not yet init'd; use current pitch to init min/max
177                   pitch_min_ = pitch;
178                   pitch_max_ = pitch;
179                 }
180               else if (Pitch::compare (pitch, pitch_max_) > 0) // new max?
181                 {
182                   pitch_max_ = pitch;
183                 }
184               else if (Pitch::compare (pitch, pitch_min_) < 0) // new min?
185                 {
186                   pitch_min_ = pitch;
187                 }
188             }
189         }
190     }
191 }
192
193 ENTER_DESCRIPTION(Cluster_engraver,
194 /* descr */     "engraves a cluster",
195 /* creats*/     "Cluster",
196 /* accepts */   "cluster-event",
197 /* acks  */     "note-head-interface",
198 /* reads */     "",
199 /* write */     "");