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