]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-spacing.cc
4b0d784b18e3930335865c9c5ae310d42c41f039
[lilypond.git] / lily / note-spacing.cc
1 /*   
2   note-spacing.cc -- implement Note_spacing
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9
10 #include "paper-column.hh"
11 #include "item.hh"
12 #include "moment.hh"
13 #include "note-spacing.hh"
14 #include "grob.hh"
15 #include "note-column.hh"
16 #include "warn.hh"
17 #include "stem.hh"
18
19 bool
20 Note_spacing::has_interface (Grob* g)
21 {
22   return g && g->has_interface (ly_symbol2scm ("note-spacing-interface"));
23 }
24
25 Real
26 Note_spacing::get_spacing (Grob *me)
27 {
28   Drul_array<SCM> props(me->get_grob_property ("left-items"),
29                         me->get_grob_property ("right-items"));
30   Direction d = LEFT;
31   Drul_array<Interval> extents;
32   do
33     {
34       for (SCM  s = props[d]; gh_pair_p (s); s = gh_cdr (s))
35         {
36           Item * it= dynamic_cast<Item*> (unsmob_grob (gh_car(s)));
37           extents[d].unite (it->extent (it->column_l (), X_AXIS));
38
39           if (d == RIGHT)
40             {
41               Grob * accs = Note_column::accidentals (it);
42               if (!accs)
43                 accs = Note_column::accidentals (it->get_parent (X_AXIS));
44               
45               if (accs)
46                 extents[d].unite (accs->extent (it->column_l (), X_AXIS));
47             }
48         }
49
50       if (extents[d].empty_b ())
51         extents[d] = Interval (0,0);
52     }
53   while (flip (&d) != LEFT);
54
55   /*
56     
57     What's sticking out at the left of the right side has less
58     influence.
59
60   */
61   Real dx= extents[LEFT][RIGHT] - 0.5 * extents[RIGHT][LEFT];
62   return dx;
63 }
64
65 Item *
66 Note_spacing::left_column (Grob *me)
67 {
68   if (me->immutable_property_alist_ == SCM_EOL)
69     return 0;
70   
71   return dynamic_cast<Item*> (me)->column_l ();
72 }
73
74 /*
75   Compute the column of the right-items.  This is a big function,
76 since RIGHT-ITEMS may span more columns (eg. if a clef if inserted,
77 this will add a new columns to RIGHT-ITEMS. Here we look at the
78 columns, and return the left-most. If there are multiple columns, we
79 prune RIGHT-ITEMS.
80    
81  */
82 Item *
83 Note_spacing::right_column (Grob*me)
84 {
85   /*
86     ugh. should have generic is_live() method?
87    */
88   if (me->immutable_property_alist_ == SCM_EOL)
89     return 0;
90   
91   SCM right = me->get_grob_property ("right-items");
92   Item *mincol = 0;
93   int min_rank = INT_MAX;
94   bool prune = false;
95   for (SCM s = right ; gh_pair_p (s) ; s = gh_cdr (s))
96     {
97       Item * ri = unsmob_item (gh_car (s));
98
99       Item * col = ri->column_l ();
100       int rank = Paper_column::rank_i (col);
101
102       if (rank < min_rank)
103         {
104           min_rank = rank;
105           if (mincol)
106             prune = true;
107
108           mincol = col;
109         }
110     }
111   
112   if (prune)
113     {
114       // I'm a lazy bum. We could do this in-place.
115       SCM newright  = SCM_EOL;
116       for (SCM s = right ; gh_pair_p (s) ; s =gh_cdr (s))
117         {
118           if (unsmob_item (gh_car (s))->column_l () == mincol)
119             newright = gh_cons (gh_car (s), newright);
120         }
121
122       me->set_grob_property ("right-items", newright);
123     }
124   
125   if (!mincol)
126     {
127       /*
128       int r = Paper_column::rank_i (dynamic_cast<Item*>(me)->column_l ());
129       programming_error (_f("Spacing wish column %d has no right item.", r));
130       */
131
132       return 0;
133     }
134
135   return mincol;
136 }
137
138 /**
139    Correct for optical illusions. See [Wanske] p. 138. The combination
140    up-stem + down-stem should get extra space, the combination
141    down-stem + up-stem less.
142
143    TODO: have to check wether the stems are in the same staff.
144
145    TODO: also correct for bar lines in RIGHT-ITEMS.  Should check if
146    the barline is the leftmost object of the break alignment.
147
148 */
149 Real
150 Note_spacing::stem_dir_correction (Grob*me) 
151 {
152   Drul_array<Direction> stem_dirs(CENTER,CENTER);
153   Drul_array<Interval> posns;
154   Drul_array<SCM> props(me->get_grob_property ("left-items"),
155                         me->get_grob_property ("right-items"));
156
157   stem_dirs[LEFT] = stem_dirs[RIGHT] = CENTER;
158   Interval intersect;
159   bool correct = true;
160   Direction d = LEFT;
161   do
162     {
163       for (SCM  s = props[d]; gh_pair_p (s); s = gh_cdr (s))
164         {
165           Item * it= dynamic_cast<Item*> (unsmob_grob (gh_car(s)));
166
167           Grob *stem = Note_column::stem_l (it);
168
169           if (!stem || Stem::invisible_b (stem))
170             {
171               correct = false;
172               goto exit_loop ;
173             }
174
175           Direction sd = Stem::get_direction (stem);
176           if (stem_dirs[d] && stem_dirs[d] != sd)
177             {
178               correct = false;
179               goto exit_loop;
180             }
181           stem_dirs[d] = sd;
182
183           Real chord_start = Stem::head_positions (stem)[sd];
184           Real stem_end = Stem::stem_end_position (stem);
185           
186           posns[d] = Interval(chord_start<?stem_end, chord_start>? stem_end);
187         }
188     }
189   while (flip (&d) != LEFT);
190   
191   intersect = posns[LEFT];  
192   intersect.intersect(posns[RIGHT]);
193   correct = correct && !intersect.empty_b ();
194   correct = correct && (stem_dirs[LEFT] *stem_dirs[RIGHT] == -1);
195   
196  exit_loop:
197   if(!correct)
198     return 0.0;
199
200   /*
201     Ugh. 7 is hardcoded.
202    */
203   Real correction = abs (intersect.length ());
204   correction = (correction/7) <? 1.0;
205   correction *= stem_dirs[LEFT] ;
206   correction *= gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
207
208   return correction;
209 }
210