]> git.donarmstrong.com Git - lilypond.git/blob - lily/porrectus-engraver.cc
4cc35e910c55216ec673c75eb9cb9271baa7e073
[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   TRANSLATOR_DECLARATIONS(Porrectus_engraver);
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_.grob_l_))
111     {
112       Note_req *note_req_l_ = dynamic_cast <Note_req *> (info_l_.req_l_);
113       if (!note_req_l_)
114         return;
115       right_heads_.push (PHead_melodic_tuple (info_l_.grob_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       int i = left_heads_.size () - 1;
129       int j = right_heads_.size () - 1;
130
131       while ((i >= 0) && (j >= 0))
132         {
133           Item *left_head = dynamic_cast<Item*> (left_heads_[i].head_l_);
134           Item *right_head = dynamic_cast<Item*> (right_heads_[j].head_l_);
135           left_head->set_grob_property("transparent", gh_bool2scm(true));
136           right_head->set_grob_property("transparent", gh_bool2scm(true));
137
138           Grob *porrectus_p_ = new Item (get_property ("Porrectus"));
139           Porrectus::set_left_head(porrectus_p_, left_head);
140           Porrectus::set_right_head(porrectus_p_, right_head);
141           porrectus_p_arr_.push (porrectus_p_);
142           announce_grob (porrectus_p_, porrectus_req_l_);
143
144           past_notes_pq_. insert (right_heads_[i]);
145           left_heads_.del (i);
146           right_heads_.del (j);
147           i--;
148           j--;
149         }
150     }
151 }
152
153 void
154 Porrectus_engraver::stop_translation_timestep ()
155 {
156   for (int i = 0; i < right_heads_.size (); i++)
157     {
158       past_notes_pq_.insert (right_heads_[i]);
159     }
160   right_heads_.clear ();
161
162   for (int i = 0; i < porrectus_p_arr_.size (); i++)
163     {
164       typeset_grob (porrectus_p_arr_[i]);
165     }
166   porrectus_p_arr_.clear ();
167 }
168
169 void
170 Porrectus_engraver::start_translation_timestep ()
171 {
172   porrectus_req_l_ = 0;
173   Moment now = now_mom ();
174   while (past_notes_pq_.size () && past_notes_pq_.front ().end_ < now)
175     past_notes_pq_.delmin ();
176
177   left_heads_.clear ();
178   while (past_notes_pq_.size () &&
179          (past_notes_pq_.front ().end_ == now))
180     left_heads_.push (past_notes_pq_.get ());
181 }
182
183
184
185 // TODO: PHead_melodic_tuple is duplicated code from tie-engraver.cc.
186 // Maybe put this into public class?
187
188 PHead_melodic_tuple::PHead_melodic_tuple ()
189 {
190   head_l_ = 0;
191   req_l_ = 0;
192   end_ = 0;
193 }
194
195 PHead_melodic_tuple::PHead_melodic_tuple (Grob *h, Melodic_req*m, Moment mom)
196 {
197   head_l_ = h;
198   req_l_ = m;
199   end_ = mom;
200 }
201
202 /*
203   signed compare, should use pitch<? 
204  */
205 int
206 PHead_melodic_tuple::pitch_compare (PHead_melodic_tuple const&h1,
207                                     PHead_melodic_tuple const &h2)
208 {
209   SCM p1 = h1.req_l_->get_mus_property ("pitch");
210   SCM p2 = h2.req_l_->get_mus_property ("pitch");
211   
212   int result = Pitch::compare (*unsmob_pitch (p1),
213                                *unsmob_pitch (p2));
214   return result;
215 }
216
217 int
218 PHead_melodic_tuple::time_compare (PHead_melodic_tuple const&h1,
219                                    PHead_melodic_tuple const &h2)
220 {
221   int result = Moment::compare(h1.end_,  h2.end_);
222   return result;
223 }
224 ENTER_DESCRIPTION(Porrectus_engraver,
225 /* descr */       "Join adjacent notes to a porrectus ligature.",
226 /* creats*/       "Porrectus",
227 /* acks  */       "rhythmic-head-interface",
228 /* reads */       "",
229 /* write */       "");