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"
20 #include "skyline-pair.hh"
23 /* return the right-pointing skyline of the left-items and the left-pointing
24 skyline of the right-items (with the skyline of the left-items in
27 Spacing_interface::skylines (Grob
*me
, Grob
*right_col
)
29 /* the logic here is a little convoluted.
30 A {Staff,Note}_spacing doesn't copy left-items when it clones,
31 so in order to find the separation items, we need to use the original
32 spacing grob. But once we find the separation items, we need to get back
36 Grob
*orig
= me
->original () ? me
->original () : me
;
37 Drul_array
<Direction
> break_dirs (dynamic_cast<Item
*> (me
)->break_status_dir (),
38 dynamic_cast<Item
*> (right_col
)->break_status_dir ());
39 Drul_array
<Skyline
> skylines
= Drul_array
<Skyline
> (Skyline (RIGHT
), Skyline (LEFT
));
40 Drul_array
<vector
<Grob
*> > items (ly_scm2link_array (orig
->get_object ("left-items")),
41 ly_scm2link_array (orig
->get_object ("right-items")));
43 Grob
*system
= me
->get_system ();
44 Grob
*left_col
= dynamic_cast<Item
*> (me
)->get_column ();
46 Drul_array
<Grob
*> columns (left_col
, right_col
);
51 for (vsize i
= 0; i
< items
[d
].size (); i
++)
53 Item
*g
= dynamic_cast<Item
*> (items
[d
][i
]);
55 if (Item
*piece
= g
->find_prebroken_piece (break_dirs
[d
]))
58 if (g
&& Separation_item::has_interface (g
) && g
->get_column () == columns
[d
])
60 SCM sky_scm
= g
->get_property ("horizontal-skylines");
61 Skyline_pair
*sky
= Skyline_pair::unsmob (sky_scm
);
63 extract_grob_set (g
, "elements", elts
);
64 Grob
*ycommon
= common_refpoint_of_array (elts
, g
, Y_AXIS
);
65 Real shift
= ycommon
->pure_relative_y_coordinate (system
, 0, INT_MAX
);
67 skylines
[d
].shift (-shift
);
70 skylines
[d
].merge ((*sky
)[-d
]);
72 programming_error ("separation item has no skyline");
74 if (d
== RIGHT
&& items
[LEFT
].size ())
75 skylines
[d
].merge (Separation_item::conditional_skyline (items
[d
][i
], items
[LEFT
][0]));
77 skylines
[d
].shift (shift
);
81 while (flip (&d
) != LEFT
);
87 Spacing_interface::minimum_distance (Grob
*me
, Grob
*right
)
89 Drul_array
<Skyline
> skylines
= Spacing_interface::skylines (me
, right
);
91 return max (0.0, skylines
[LEFT
].distance (skylines
[RIGHT
]));
95 Compute the column of the right-items. This is a big function,
96 since RIGHT-ITEMS may span more columns (eg. if a clef is inserted,
97 this will add a new column to RIGHT-ITEMS. Here we look at the
98 columns, and return the left-most. If there are multiple columns, we
101 If we end up pruning, we add a left-neighbor to every column that
102 gets pruned. This ensures that loose columns in cross-staff music
103 do indeed get marked as loose. The problem situation is when a voice
104 passes from staff 1 to staff 2 and a clef appears later on in staff 1.
105 Then the NoteSpacing attached to the last note in staff 1 has two
106 right-items: one pointing to the next note in staff 2 and one pointing
107 to the clef. We will prune the clef right-item here and, unless we add
108 a left-neighbor to the clef, it won't get marked as loose.
111 Spacing_interface::right_column (Grob
*me
)
116 Grob_array
*a
= unsmob_grob_array (me
->get_object ("right-items"));
118 int min_rank
= INT_MAX
;
120 for (vsize i
= 0; a
&& i
< a
->size (); i
++)
122 Item
*ri
= a
->item (i
);
123 Item
*col
= ri
->get_column ();
125 int rank
= Paper_column::get_rank (col
);
135 else if (rank
> min_rank
)
141 vector
<Grob
*> &right
= a
->array_reference ();
142 for (vsize i
= right
.size (); i
--;)
144 if (dynamic_cast<Item
*> (right
[i
])->get_column () != mincol
)
146 extract_grob_set (right
[i
], "left-neighbors", lns
);
148 Pointer_group_interface::add_grob (right
[i
],
149 ly_symbol2scm ("left-neighbors"),
150 dynamic_cast<Item
*> (me
)->get_column ());
152 right
.erase (right
.begin () + i
);
161 Spacing_interface::left_column (Grob
*me
)
166 return dynamic_cast<Item
*> (me
)->get_column ();
170 get_note_columns (vector
<Grob
*> const &elts
)
174 for (vsize i
= 0; i
< elts
.size (); i
++)
176 if (Note_column::has_interface (elts
[i
]))
177 ret
.push_back (dynamic_cast<Item
*> (elts
[i
]));
178 else if (Separation_item::has_interface (elts
[i
]))
180 extract_grob_set (elts
[i
], "elements", more_elts
);
181 vector
<Item
*> ncs
= get_note_columns (more_elts
);
183 ret
.insert (ret
.end (), ncs
.begin (), ncs
.end ());
191 Spacing_interface::right_note_columns (Grob
*me
)
193 extract_grob_set (me
, "right-items", elts
);
194 return get_note_columns (elts
);
198 Spacing_interface::left_note_columns (Grob
*me
)
200 extract_grob_set (me
, "left-items", elts
);
201 return get_note_columns (elts
);
205 Try to find the break-aligned symbol that belongs on the D-side
206 of ME, sticking out in direction -D. The x size is put in LAST_EXT
209 Spacing_interface::extremal_break_aligned_grob (Grob
*me
,
215 last_ext
->set_empty ();
218 extract_grob_set (me
, d
== LEFT
? "left-break-aligned" : "right-break-aligned", elts
);
220 for (vsize i
= elts
.size (); i
--;)
222 Item
*break_item
= dynamic_cast<Item
*> (elts
[i
]);
224 if (break_item
->break_status_dir () != break_dir
)
225 break_item
= break_item
->find_prebroken_piece (break_dir
);
227 if (!break_item
|| !scm_is_pair (break_item
->get_property ("space-alist")))
231 col
= dynamic_cast<Item
*> (elts
[0])->get_column ()->find_prebroken_piece (break_dir
);
233 Interval ext
= break_item
->extent (col
, X_AXIS
);
239 || (last_grob
&& d
* (ext
[-d
]- (*last_ext
)[-d
]) < 0))
242 last_grob
= break_item
;
250 ADD_INTERFACE (Spacing_interface
,
251 "This object calculates the desired and minimum distances"
252 " between two columns.",