2 spacing-interface.cc -- functionality that is shared between Note_spacing
5 source file of the GNU LilyPond music typesetter
7 (c) 2007 Joe Neeman <joeneeman@gmail.com>
10 #include "spacing-interface.hh"
13 #include "grob-array.hh"
15 #include "note-column.hh"
16 #include "pointer-group-interface.hh"
17 #include "paper-column.hh"
18 #include "separation-item.hh"
22 /* return the right-pointing skyline of the left-items and the left-pointing
23 skyline of the right-items (with the skyline of the left-items in
26 Spacing_interface::skylines (Grob *me, Grob *right_col)
28 /* the logic here is a little convoluted.
29 A {Staff,Note}_spacing doesn't copy left-items when it clones,
30 so in order to find the separation items, we need to use the original
31 spacing grob. But once we find the separation items, we need to get back
35 Grob *orig = me->original () ? me->original () : me;
36 Drul_array<Direction> break_dirs (dynamic_cast<Item*> (me)->break_status_dir (),
37 dynamic_cast<Item*> (right_col)->break_status_dir ());
38 Drul_array<Skyline> skylines = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
39 Drul_array<vector<Grob*> > items (ly_scm2link_array (orig->get_object ("left-items")),
40 ly_scm2link_array (orig->get_object ("right-items")));
42 Grob *system = me->get_system ();
43 Grob *left_col = dynamic_cast<Item*> (me)->get_column ();
45 Drul_array<Grob*> columns (left_col, right_col);
50 for (vsize i = 0; i < items[d].size (); i++)
52 Item *g = dynamic_cast<Item*> (items[d][i]);
54 if (Item *piece = g->find_prebroken_piece (break_dirs[d]))
57 if (g && Separation_item::has_interface (g) && g->get_column () == columns[d])
59 SCM sky_scm = g->get_property ("horizontal-skylines");
60 Skyline_pair *sky = Skyline_pair::unsmob (sky_scm);
62 extract_grob_set (g, "elements", elts);
63 Grob *ycommon = common_refpoint_of_array (elts, g, Y_AXIS);
64 Real shift = ycommon->pure_relative_y_coordinate (system, 0, INT_MAX);
66 skylines[d].shift (-shift);
69 skylines[d].merge ((*sky)[-d]);
71 programming_error ("separation item has no skyline");
73 if (d == RIGHT && items[LEFT].size ())
74 skylines[d].merge (Separation_item::conditional_skyline (items[d][i], items[LEFT][0]));
76 skylines[d].shift (shift);
80 while (flip (&d) != LEFT);
86 Spacing_interface::minimum_distance (Grob *me, Grob *right)
88 Drul_array<Skyline> skylines = Spacing_interface::skylines (me, right);
90 return max (0.0, skylines[LEFT].distance (skylines[RIGHT]));
94 Compute the column of the right-items. This is a big function,
95 since RIGHT-ITEMS may span more columns (eg. if a clef is inserted,
96 this will add a new column to RIGHT-ITEMS. Here we look at the
97 columns, and return the left-most. If there are multiple columns, we
100 If we end up pruning, we add a left-neighbor to every column that
101 gets pruned. This ensures that loose columns in cross-staff music
102 do indeed get marked as loose. The problem situation is when a voice
103 passes from staff 1 to staff 2 and a clef appears later on in staff 1.
104 Then the NoteSpacing attached to the last note in staff 1 has two
105 right-items: one pointing to the next note in staff 2 and one pointing
106 to the clef. We will prune the clef right-item here and, unless we add
107 a left-neighbor to the clef, it won't get marked as loose.
110 Spacing_interface::right_column (Grob *me)
115 Grob_array *a = unsmob_grob_array (me->get_object ("right-items"));
117 int min_rank = INT_MAX;
119 for (vsize i = 0; a && i < a->size (); i++)
121 Item *ri = a->item (i);
122 Item *col = ri->get_column ();
124 int rank = Paper_column::get_rank (col);
134 else if (rank > min_rank)
140 vector<Grob*> &right = a->array_reference ();
141 for (vsize i = right.size (); i--;)
143 if (dynamic_cast<Item *> (right[i])->get_column () != mincol)
145 extract_grob_set (right[i], "left-neighbors", lns);
147 Pointer_group_interface::add_grob (right[i],
148 ly_symbol2scm ("left-neighbors"),
149 dynamic_cast<Item*> (me)->get_column ());
151 right.erase (right.begin () + i);
160 Spacing_interface::left_column (Grob *me)
165 return dynamic_cast<Item *> (me)->get_column ();
169 get_note_columns (vector<Grob*> const &elts)
173 for (vsize i = 0; i < elts.size (); i++)
175 if (Note_column::has_interface (elts[i]))
176 ret.push_back (dynamic_cast<Item*> (elts[i]));
177 else if (Separation_item::has_interface (elts[i]))
179 extract_grob_set (elts[i], "elements", more_elts);
180 vector<Item*> ncs = get_note_columns (more_elts);
182 ret.insert (ret.end (), ncs.begin (), ncs.end ());
190 Spacing_interface::right_note_columns (Grob *me)
192 extract_grob_set (me, "right-items", elts);
193 return get_note_columns (elts);
197 Spacing_interface::left_note_columns (Grob *me)
199 extract_grob_set (me, "left-items", elts);
200 return get_note_columns (elts);
204 Try to find the break-aligned symbol that belongs on the D-side
205 of ME, sticking out in direction -D. The x size is put in LAST_EXT
208 Spacing_interface::extremal_break_aligned_grob (Grob *me,
214 last_ext->set_empty ();
217 extract_grob_set (me, d == LEFT ? "left-break-aligned" : "right-break-aligned", elts);
219 for (vsize i = elts.size (); i--;)
221 Item *break_item = dynamic_cast<Item*> (elts[i]);
223 if (break_item->break_status_dir () != break_dir)
224 break_item = break_item->find_prebroken_piece (break_dir);
226 if (!break_item || !scm_is_pair (break_item->get_property ("space-alist")))
230 col = dynamic_cast<Item*> (elts[0])->get_column ()->find_prebroken_piece (break_dir);
232 Interval ext = break_item->extent (col, X_AXIS);
238 || (last_grob && d * (ext[-d]- (*last_ext)[-d]) < 0))
241 last_grob = break_item;
249 ADD_INTERFACE (Spacing_interface,
250 "This object calculates the desired and minimum distances"
251 " between two columns.",