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