2 figured-bass-engraver.cc -- implement Figured_bass_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2005--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
10 #include "engraver.hh"
16 #include "axis-group-interface.hh"
17 #include "align-interface.hh"
18 #include "pointer-group-interface.hh"
19 #include "text-interface.hh"
20 #include "grob-array.hh"
23 #include "translator.icc"
28 Spanner *continuation_line_;
34 Music *current_music_;
35 bool force_no_continuation_;
40 force_no_continuation_ = false;
41 continuation_line_ = 0;
43 alteration_ = SCM_EOL;
47 bool is_continuation () const
51 && !force_no_continuation_
52 && ly_is_equal (number_,
53 current_music_->get_property ("figure"))
54 && ly_is_equal (alteration_,
55 current_music_->get_property ("alteration"));
59 struct Figured_bass_engraver : public Engraver
61 TRANSLATOR_DECLARATIONS(Figured_bass_engraver);
62 void clear_spanners();
66 void center_continuations (Link_array<Spanner> const &consecutive_lines);
67 void center_repeated_continuations ();
69 std::vector<Figure_group> groups_;
71 Link_array<Music> new_musics_;
73 bool new_music_found_;
78 virtual bool try_music (Music *);
79 virtual void derived_mark () const;
81 void start_translation_timestep ();
82 void stop_translation_timestep ();
83 void process_music ();
87 Figured_bass_engraver::derived_mark () const
89 for (vsize i = 0; i < groups_.size (); i++)
91 scm_gc_mark (groups_[i].number_);
92 scm_gc_mark (groups_[i].alteration_);
97 Figured_bass_engraver::stop_translation_timestep ()
100 || now_mom ().main_part_ < stop_moment_.main_part_
101 || now_mom ().grace_part_ < Rational (0))
105 for (vsize i = 0; !found && i < groups_.size (); i++)
106 found = found || groups_[i].current_music_;
112 Figured_bass_engraver::Figured_bass_engraver ()
115 continuation_ = false;
117 new_music_found_ = false;
121 Figured_bass_engraver::start_translation_timestep ()
123 if (now_mom ().main_part_ < stop_moment_.main_part_
124 || now_mom ().grace_part_ < Rational (0))
128 new_musics_.clear ();
129 for (vsize i = 0; i < groups_.size (); i++)
130 groups_[i].current_music_ = 0;
131 continuation_ = false;
135 Figured_bass_engraver::try_music (Music *m)
137 new_music_found_ = true;
138 if (m->is_mus_type ("rest-event"))
145 stop_moment_ = now_mom () + m->get_length ();
147 SCM fig = m->get_property ("figure");
148 for (vsize i = 0; i < groups_.size (); i++)
150 if (!groups_[i].current_music_
151 && ly_is_equal (groups_[i].number_, fig))
153 groups_[i].current_music_ = m;
154 groups_[i].force_no_continuation_
155 = to_boolean (m->get_property ("no-continuation"));
156 continuation_ = true;
161 new_musics_.push_back (m);
168 Figured_bass_engraver::center_continuations (Link_array<Spanner> const &consecutive_lines)
170 if (consecutive_lines.size () == 2)
172 Link_array<Grob> left_figs;
173 for (vsize j = consecutive_lines.size(); j--;)
174 left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
176 SCM ga = Grob_array::make_array ();
177 unsmob_grob_array (ga)->set_array (left_figs);
179 for (vsize j = consecutive_lines.size(); j--;)
180 consecutive_lines[j]->set_object ("figures",
181 unsmob_grob_array (ga)->smobbed_copy ());
186 Figured_bass_engraver::center_repeated_continuations ()
188 Link_array<Spanner> consecutive_lines;
189 for (vsize i = 0; i <= groups_.size(); i++)
191 if (i < groups_.size ()
192 && groups_[i].continuation_line_
193 && (consecutive_lines.empty ()
194 || (consecutive_lines[0]->get_bound(LEFT)->get_column ()
195 == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
196 && consecutive_lines[0]->get_bound(RIGHT)->get_column ()
197 == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
198 consecutive_lines.push_back (groups_[i].continuation_line_);
201 center_continuations (consecutive_lines);
202 consecutive_lines.clear ();
208 Figured_bass_engraver::clear_spanners ()
214 if (to_boolean (get_property ("figuredBassCenterContinuations")))
215 center_repeated_continuations();
221 Figured_bass_engraver::add_brackets ()
223 Link_array<Grob> encompass;
225 for (vsize i = 0; i < groups_.size (); i ++)
227 if (!groups_[i].current_music_)
230 if (to_boolean (groups_[i].current_music_->get_property ("bracket-start")))
233 if (inside && groups_[i].figure_item_)
234 encompass.push_back (groups_[i].figure_item_);
236 if (to_boolean (groups_[i].current_music_->get_property ("bracket-stop")))
240 Item * brack = make_item ("BassFigureBracket", groups_[i].current_music_->self_scm ());
241 for (vsize j = 0; j < encompass.size (); j++)
243 Pointer_group_interface::add_grob (brack,
244 ly_symbol2scm ("elements"),
253 Figured_bass_engraver::process_music ()
262 && new_musics_.empty ())
268 if (!new_music_found_)
271 new_music_found_ = false;
274 Don't need to sync alignments, if we're not using extenders.
276 bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
279 if (to_boolean (get_property ("figuredBassCenterContinuations")))
280 center_repeated_continuations ();
282 for (vsize i = 0; i < groups_.size (); i++)
284 groups_[i].group_ = 0;
285 groups_[i].continuation_line_ = 0;
293 for (vsize i = 0; i < new_musics_.size (); i++)
295 while (k < groups_.size ()
296 && groups_[k].current_music_)
299 if (k >= groups_.size ())
302 groups_.push_back (group);
305 groups_[k].current_music_ = new_musics_[i];
306 groups_[k].figure_item_ = 0;
310 for (vsize i = 0; i < groups_.size (); i++)
312 if (!groups_[i].is_continuation ())
314 groups_[i].number_ = SCM_BOOL_F;
315 groups_[i].alteration_ = SCM_BOOL_F;
321 std::vector<int> junk_continuations;
322 for (vsize i = 0; i < groups_.size(); i++)
324 Figure_group &group = groups_[i];
326 if (group.is_continuation ())
328 if (!group.continuation_line_)
330 Spanner * line = make_spanner ("BassFigureContinuation", SCM_EOL);
331 Item * item = group.figure_item_;
332 group.continuation_line_ = line;
333 line->set_bound (LEFT, item);
336 Don't add as child. This will cache the wrong
337 (pre-break) stencil when callbacks are triggered.
339 line->set_parent (group.group_, Y_AXIS);
340 Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
342 group.figure_item_ = 0;
345 else if (group.continuation_line_)
346 junk_continuations.push_back (i);
352 Link_array<Spanner> consecutive;
353 if (to_boolean (get_property ("figuredBassCenterContinuations")))
355 for (vsize i = 0; i <= junk_continuations.size (); i++)
357 if (i < junk_continuations.size()
358 && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
359 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
362 center_continuations (consecutive);
363 consecutive.clear ();
364 if (i < junk_continuations.size ())
365 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
369 for (vsize i = 0; i < junk_continuations.size (); i++)
370 groups_[junk_continuations[i]].continuation_line_ = 0;
378 Figured_bass_engraver::create_grobs ()
380 Grob *muscol = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
383 alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
384 alignment_->set_bound (LEFT, muscol);
386 alignment_->set_bound (RIGHT, muscol);
388 SCM proc = get_property ("figuredBassFormatter");
389 for (vsize i = 0; i < groups_.size(); i++)
391 Figure_group &group = groups_[i];
393 if (group.current_music_)
396 = make_item ("BassFigure",
397 group.current_music_->self_scm ());
400 SCM fig = group.current_music_->get_property ("figure");
403 group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
404 group.group_->set_bound (LEFT, muscol);
405 Align_interface::add_element (alignment_,
409 if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F)
411 item->set_property ("transparent", SCM_BOOL_T);
412 item->set_property ("implicit", SCM_BOOL_T);
416 group.alteration_ = group.current_music_->get_property ("alteration");
418 SCM text = group.current_music_->get_property ("text");
419 if (!Text_interface::is_markup (text)
420 && ly_is_procedure (proc))
422 text = scm_call_3 (proc, fig, group.current_music_->self_scm (),
423 context ()->self_scm ());
426 item->set_property ("text", text);
428 Axis_group_interface::add_element (group.group_, item);
429 group.figure_item_ = item;
432 if (group.continuation_line_)
435 UGH should connect to the bass staff, and get the note heads.
437 group.figure_item_->set_property ("transparent", SCM_BOOL_T);
438 group.continuation_line_->set_bound (RIGHT, group.figure_item_);
442 if (groups_[i].group_)
443 groups_[i].group_->set_bound (RIGHT, muscol);
448 ADD_TRANSLATOR (Figured_bass_engraver,
451 "Make figured bass numbers.",
454 "BassFigureAlignment "
456 "BassFigureContinuation "
460 "bass-figure-event rest-event",
463 "figuredBassAlterationDirection "
464 "figuredBassCenterContinuations "
465 "figuredBassFormatter "
466 "implicitBassFigures "
467 "useBassFigureExtenders "