]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-interface.cc
Merge commit 'ded1396' into jneeman
[lilypond.git] / lily / spacing-interface.cc
1 /*
2   spacing-interface.cc -- functionality that is shared between Note_spacing
3   and Staff_spacing
4
5   source file of the GNU LilyPond music typesetter
6
7   (c) 2007 Joe Neeman <joeneeman@gmail.com>
8 */
9
10 #include "spacing-interface.hh"
11
12 #include "grob.hh"
13 #include "grob-array.hh"
14 #include "item.hh"
15 #include "note-column.hh"
16 #include "pointer-group-interface.hh"
17 #include "paper-column.hh"
18 #include "separation-item.hh"
19 #include "skyline.hh"
20
21 /* return the right-pointing skyline of the left-items and the left-pointing
22    skyline of the right-items (with the skyline of the left-items in
23    ret[LEFT]) */
24 Drul_array<Skyline>
25 Spacing_interface::skylines (Grob *me, Grob *right_col)
26 {
27   /* the logic here is a little convoluted.
28      A {Staff,Note}_spacing doesn't copy left-items when it clones,
29      so in order to find the separation items, we need to use the original
30      spacing grob. But once we find the separation items, we need to get back
31      the broken piece.
32   */
33
34   Grob *orig = me->original () ? me->original () : me;
35   Drul_array<Direction> break_dirs (dynamic_cast<Item*> (me)->break_status_dir (),
36                                     dynamic_cast<Item*> (right_col)->break_status_dir ());
37   Drul_array<Skyline> skylines = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
38   Drul_array<vector<Grob*> > items (ly_scm2link_array (orig->get_object ("left-items")),
39                                     ly_scm2link_array (orig->get_object ("right-items")));
40
41   Direction d = LEFT;
42   do
43     {
44       skylines[d].set_minimum_height (0.0);
45
46       for (vsize i = 0; i < items[d].size (); i++)
47         {
48           Grob *g = items[d][i];
49           if (Item *it = dynamic_cast<Item*> (g))
50             if (Grob *piece = it->find_prebroken_piece (break_dirs[d]))
51               g = piece;
52
53           if (Separation_item::has_interface (g))
54             {
55               SCM sky_scm = g->get_property ("horizontal-skylines");
56               Skyline_pair *sky = Skyline_pair::unsmob (sky_scm);
57               if (sky)
58                 skylines[d].merge ((*sky)[-d]);
59               else
60                 programming_error ("separation item has no skyline");
61             
62               if (d == RIGHT && items[LEFT].size ())
63                 skylines[d].merge (Separation_item::conditional_skyline (items[d][i], items[LEFT][0]));
64             }
65         }
66     }
67   while (flip (&d) != LEFT);
68
69   return skylines;
70 }
71
72 Real
73 Spacing_interface::minimum_distance (Grob *me, Grob *right)
74 {
75   Drul_array<Skyline> skylines = Spacing_interface::skylines (me, right);
76
77   return max (0.0, skylines[LEFT].distance (skylines[RIGHT]));
78 }
79
80 /*
81   Compute the column of the right-items.  This is a big function,
82   since RIGHT-ITEMS may span more columns (eg. if a clef is inserted,
83   this will add a new column to RIGHT-ITEMS. Here we look at the
84   columns, and return the left-most. If there are multiple columns, we
85   prune RIGHT-ITEMS.
86 */
87 Item *
88 Spacing_interface::right_column (Grob *me)
89 {
90   if (!me->is_live ())
91     return 0;
92
93   Grob_array *a = unsmob_grob_array (me->get_object ("right-items"));
94   Item *mincol = 0;
95   int min_rank = INT_MAX;
96   bool prune = false;
97   for (vsize i = 0; a && i < a->size (); i++)
98     {
99       Item *ri = a->item (i);
100       Item *col = ri->get_column ();
101
102       int rank = Paper_column::get_rank (col);
103
104       if (rank < min_rank)
105         {
106           min_rank = rank;
107           if (mincol)
108             prune = true;
109
110           mincol = col;
111         }
112     }
113
114   if (prune && a)
115     {
116       vector<Grob*> &right = a->array_reference ();
117       for (vsize i = right.size (); i--;)
118         {
119           if (dynamic_cast<Item *> (right[i])->get_column () != mincol)
120             right.erase (right.begin () + i);
121         }
122     }
123
124   return mincol;
125 }
126
127 Item *
128 Spacing_interface::left_column (Grob *me)
129 {
130   if (!me->is_live ())
131     return 0;
132
133   return dynamic_cast<Item *> (me)->get_column ();
134 }
135
136 static vector<Item*>
137 get_note_columns (vector<Grob*> const &elts)
138 {
139   vector<Item*> ret;
140
141   for (vsize i = 0; i < elts.size (); i++)
142     {
143       if (Note_column::has_interface (elts[i]))
144         ret.push_back (dynamic_cast<Item*> (elts[i]));
145       else if (Separation_item::has_interface (elts[i]))
146         {
147           extract_grob_set (elts[i], "elements", more_elts);
148           vector<Item*> ncs = get_note_columns (more_elts);
149
150           ret.insert (ret.end (), ncs.begin (), ncs.end ());
151         }
152     }
153
154   return ret;
155 }
156
157 vector<Item*>
158 Spacing_interface::right_note_columns (Grob *me)
159 {
160   extract_grob_set (me, "right-items", elts);
161   return get_note_columns (elts);
162 }
163
164 vector<Item*>
165 Spacing_interface::left_note_columns (Grob *me)
166 {
167   extract_grob_set (me, "left-items", elts);
168   return get_note_columns (elts);
169 }
170
171 ADD_INTERFACE (Spacing_interface,
172                "This object calculates the desired and minimum distances between two columns.",
173
174                "left-items "
175                "right-items "
176                );