]> git.donarmstrong.com Git - lilypond.git/blob - lily/porrectus-engraver.cc
release: 1.5.10
[lilypond.git] / lily / porrectus-engraver.cc
1 /*
2   porrectus-engraver.cc -- implement Porrectus_engraver
3
4   Copyright (C) 2001 Juergen Reuter
5
6   written for the GNU LilyPond music typesetter
7 */
8
9 /*
10  * FIXME: Currently, when creating a porrectus item, it takes the
11  * moment of the second note.  Actually, it should take the moment of
12  * the first note.
13  *
14  * TODO: Introduce "\~" as alternative syntax for "\porrectus"?
15  *
16  * TODO: Hufnagel support.
17  *
18  * TODO: Fine-tuning of porrectus shape.  In particular, the mensural
19  * non-solid shape could either be slightly bigger in height, or the
20  * extrem points could be slightly vertically shifted apart.
21  *
22  * TODO: For white mensural (i.e. #'style=#'mensural, #'solid=##f)
23  * porrectus grobs, it is possible to automatically determine all
24  * porrectus specific properties (add-stem, stem-direction) solely
25  * from the duration of the contributing notes and time-signature.
26  * Introduce a boolean grob property called auto-config, so that, if
27  * turned on, lily automatically sets the remaining properties
28  * properly.
29  *
30  * TODO: The following issues are not (and should not be) handled by
31  * this engraver: (1) accidentals placement, (2) avoiding line
32  * breaking inbetween porrectus, (3) spacing.  For example, currently
33  * only the accidental for the second note (cp. the above FIXME) is
34  * printed.  These issues should be resolved by some sort of ligature
35  * context that encloses use of this engraver, using syntax like:
36  * \ligature { e \porrectus c }.
37  *
38  * TODO: Do not allow a series of adjacent porrectus requests, as in:
39  * e \porrectus d \porrectus c.
40  */
41
42 #include "staff-symbol-referencer.hh"
43 #include "porrectus.hh"
44 #include "musical-request.hh"
45 #include "command-request.hh"
46 #include "rhythmic-head.hh"
47 #include "item.hh"
48 #include "engraver.hh"
49 #include "pqueue.hh"
50
51 // TODO: PHead_melodic_tuple is duplicated code from tie-engraver.cc.
52 // Maybe put this into public class?
53 struct PHead_melodic_tuple {
54   Melodic_req *req_l_;
55   Grob *head_l_;
56   Moment end_;
57   PHead_melodic_tuple ();
58   PHead_melodic_tuple (Grob*, Melodic_req*, Moment);
59   static int pitch_compare (PHead_melodic_tuple const &,
60                             PHead_melodic_tuple const &);
61   static int time_compare (PHead_melodic_tuple const &,
62                            PHead_melodic_tuple const &);  
63 };
64
65 inline int compare (PHead_melodic_tuple const &a, PHead_melodic_tuple const &b)
66 {
67   return PHead_melodic_tuple::time_compare (a,b);
68 }
69
70 class Porrectus_engraver : public Engraver {
71 public:
72   Porrectus_engraver ();
73   VIRTUAL_COPY_CONS (Translator);
74   
75 protected:
76   virtual bool try_music (Music *req_l);
77   virtual void create_grobs ();
78   virtual void stop_translation_timestep ();
79   virtual void start_translation_timestep ();
80   virtual void acknowledge_grob (Grob_info);
81
82 private:
83   PQueue<PHead_melodic_tuple> past_notes_pq_;
84   Porrectus_req *porrectus_req_l_;
85   Array<PHead_melodic_tuple> left_heads_;
86   Array<PHead_melodic_tuple> right_heads_;
87   Link_array<Grob> porrectus_p_arr_;
88 };
89
90 Porrectus_engraver::Porrectus_engraver ()
91 {
92   porrectus_req_l_ = 0;
93 }
94
95 bool
96 Porrectus_engraver::try_music (Music *m)
97 {
98   if (Porrectus_req *req_l_ = dynamic_cast <Porrectus_req *> (m))
99     {
100       porrectus_req_l_ = req_l_;
101       return true;
102     }
103   else
104     return false;
105 }
106
107 void
108 Porrectus_engraver::acknowledge_grob (Grob_info info_l_)
109 {
110   if (Rhythmic_head::has_interface (info_l_.elem_l_))
111     {
112       Note_req *note_req_l_ = dynamic_cast <Note_req *> (info_l_.req_l_);
113       if (!note_req_l_)
114         return;
115       left_heads_.push (PHead_melodic_tuple (info_l_.elem_l_, note_req_l_,
116                                              now_mom () +
117                                              note_req_l_->length_mom ()));
118     }
119 }
120
121 void
122 Porrectus_engraver::create_grobs ()
123 {
124   if (porrectus_req_l_)
125     {
126       left_heads_.sort (PHead_melodic_tuple::pitch_compare);
127       right_heads_.sort (PHead_melodic_tuple::pitch_compare);
128
129       SCM head_list = SCM_EOL;
130       
131       int i = left_heads_.size () - 1;
132       int j = right_heads_.size () - 1;
133
134       while ((i >= 0) && (j >= 0))
135         {
136           head_list =
137             gh_cons (gh_cons (right_heads_[j].head_l_->self_scm (),
138                               left_heads_[i].head_l_->self_scm ()),
139                      head_list);
140
141           past_notes_pq_. insert (left_heads_[i]);
142           left_heads_.del (i);
143           right_heads_.del (j);
144           i--;
145           j--;
146         }
147
148       for (SCM s = head_list; gh_pair_p (s); s = gh_cdr (s))
149         {
150           SCM caar = gh_caar (s);
151           SCM cdar = gh_cdar (s);
152
153           Item *left_head = dynamic_cast<Item*> (unsmob_grob (caar));
154           Item *right_head = dynamic_cast<Item*> (unsmob_grob (cdar));
155           left_head->set_grob_property("transparent", gh_bool2scm(true));
156           right_head->set_grob_property("transparent", gh_bool2scm(true));
157
158           Grob *porrectus_p_ = new Item (get_property ("Porrectus"));
159           Porrectus::set_left_head(porrectus_p_, caar);
160           Porrectus::set_right_head(porrectus_p_, cdar);
161           porrectus_p_arr_.push (porrectus_p_);
162           announce_grob (porrectus_p_, 0);
163         }
164     }
165 }
166
167 void
168 Porrectus_engraver::stop_translation_timestep ()
169 {
170   for (int i = 0; i < left_heads_.size (); i++)
171     {
172       past_notes_pq_.insert (left_heads_[i]);
173     }
174   left_heads_.clear ();
175
176   for (int i = 0; i < porrectus_p_arr_.size (); i++)
177     {
178       typeset_grob (porrectus_p_arr_[i]);
179     }
180   porrectus_p_arr_.clear ();
181 }
182
183 void
184 Porrectus_engraver::start_translation_timestep ()
185 {
186   porrectus_req_l_ = 0;
187   Moment now = now_mom ();
188   while (past_notes_pq_.size () && past_notes_pq_.front ().end_ < now)
189     past_notes_pq_.delmin ();
190
191   right_heads_.clear ();
192   while (past_notes_pq_.size () &&
193          (past_notes_pq_.front ().end_ == now))
194     right_heads_.push (past_notes_pq_.get ());
195 }
196
197 ADD_THIS_TRANSLATOR (Porrectus_engraver);
198
199 // TODO: PHead_melodic_tuple is duplicated code from tie-engraver.cc.
200 // Maybe put this into public class?
201
202 PHead_melodic_tuple::PHead_melodic_tuple ()
203 {
204   head_l_ = 0;
205   req_l_ = 0;
206   end_ = 0;
207 }
208
209 PHead_melodic_tuple::PHead_melodic_tuple (Grob *h, Melodic_req*m, Moment mom)
210 {
211   head_l_ = h;
212   req_l_ = m;
213   end_ = mom;
214 }
215
216 /*
217   signed compare, should use pitch<? 
218  */
219 int
220 PHead_melodic_tuple::pitch_compare (PHead_melodic_tuple const&h1,
221                                     PHead_melodic_tuple const &h2)
222 {
223   SCM p1 = h1.req_l_->get_mus_property ("pitch");
224   SCM p2 = h2.req_l_->get_mus_property ("pitch");
225   
226   int result = Pitch::compare (*unsmob_pitch (p1),
227                                *unsmob_pitch (p2));
228   return result;
229 }
230
231 int
232 PHead_melodic_tuple::time_compare (PHead_melodic_tuple const&h1,
233                                    PHead_melodic_tuple const &h2)
234 {
235   int result = Moment::compare(h1.end_,  h2.end_);
236   return result;
237 }