Docs: IR: fix documentation of 'style values
[lilypond.git] / lily / line-spanner.cc
blobb19151987f012dd9c046be13d1a54b3b73806bc3
1 /*
2 line-spanner.cc -- implement Line_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2009 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
9 #include "align-interface.hh"
10 #include "axis-group-interface.hh"
11 #include "font-interface.hh"
12 #include "grob-interface.hh"
13 #include "item.hh"
14 #include "lily-proto.hh"
15 #include "line-interface.hh"
16 #include "moment.hh"
17 #include "output-def.hh"
18 #include "pointer-group-interface.hh"
19 #include "spanner.hh"
20 #include "staff-symbol-referencer.hh"
21 #include "system.hh"
22 #include "text-interface.hh"
23 #include "warn.hh"
25 class Line_spanner
27 public:
28 DECLARE_SCHEME_CALLBACK (print, (SCM));
29 DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
30 DECLARE_SCHEME_CALLBACK (calc_left_bound_info, (SCM));
31 DECLARE_SCHEME_CALLBACK (calc_left_bound_info_and_text, (SCM));
32 DECLARE_SCHEME_CALLBACK (calc_right_bound_info, (SCM));
33 DECLARE_SCHEME_CALLBACK (calc_bound_info, (SCM, Direction));
34 DECLARE_GROB_INTERFACE ();
37 Spanner *parent_spanner (Grob *g)
39 if (Spanner::has_interface (g))
40 return dynamic_cast<Spanner*> (g);
41 return parent_spanner (g->get_parent (Y_AXIS));
44 SCM
45 Line_spanner::calc_bound_info (SCM smob, Direction dir)
47 Spanner *me = unsmob_spanner (smob);
49 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
50 commonx = me->common_refpoint (commonx, X_AXIS);
52 SCM bound_details = me->get_property ("bound-details");
54 SCM details = SCM_BOOL_F;
55 if (details == SCM_BOOL_F)
56 details = ly_assoc_get ((dir == LEFT)
57 ? ly_symbol2scm ("left")
58 : ly_symbol2scm ("right"), bound_details, SCM_BOOL_F);
60 if (me->get_bound (dir)->break_status_dir ())
62 SCM extra = ly_assoc_get ((dir == LEFT)
63 ? ly_symbol2scm ("left-broken")
64 : ly_symbol2scm ("right-broken"), bound_details, SCM_EOL);
66 for (SCM s = scm_reverse (extra); scm_is_pair (s); s = scm_cdr (s))
67 details = scm_cons (scm_car (s), details);
70 if (details == SCM_BOOL_F)
71 details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
73 SCM text = ly_assoc_get (ly_symbol2scm ("text"), details, SCM_BOOL_F);
74 if (Text_interface::is_markup (text))
76 Output_def *layout = me->layout ();
77 SCM properties = Font_interface::text_font_alist_chain (me);
78 details = scm_acons (ly_symbol2scm ("stencil"),
79 Text_interface::interpret_markup (layout->self_scm (),
80 properties, text),
81 details);
84 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
86 Direction attach = (Direction)
87 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
88 details, SCM_BOOL_F),
89 CENTER);
91 Item *bound_item = me->get_bound (dir);
92 Grob *bound_grob = bound_item;
93 if (to_boolean (ly_assoc_get (ly_symbol2scm ("end-on-note"), details, SCM_BOOL_F))
94 && bound_item->break_status_dir ())
96 extract_grob_set (me, "note-columns", columns);
97 if (columns.size ())
98 bound_grob = (dir == LEFT)
99 ? columns[0] : columns.back();
102 details = scm_acons (ly_symbol2scm ("X"),
103 scm_from_double (robust_relative_extent (bound_grob, commonx, X_AXIS)
104 .linear_combination (attach)),
105 details);
109 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
111 Real y = 0.0;
113 Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
114 0.0);
116 Grob *common_y = me->common_refpoint (me->get_bound (dir), Y_AXIS);
117 if (me->get_bound (dir)->break_status_dir ())
119 Spanner *next_sp = me->broken_neighbor (dir);
120 Item *next_bound = next_sp->get_bound (dir);
122 if (next_bound->break_status_dir ())
124 programming_error ("no note heads for the line spanner on neighbor line?"
125 " Confused.");
126 me->suicide ();
127 return SCM_EOL;
130 Spanner *next_bound_parent = parent_spanner (next_bound);
131 Interval next_ext = next_bound->extent (next_bound_parent, Y_AXIS);
133 /* We want to know what would be the
134 y-position of the next bound (relative to my y-parent) if it belonged
135 to the same system as this bound. We rely on the fact that
136 the y-parent of the next bound is a spanner (probably the
137 VerticalAxisGroup of a staff) that extends over the break.
139 Spanner *next_bound_parent_on_this_line =
140 next_bound_parent->broken_neighbor (other_dir (dir));
142 if (next_bound_parent_on_this_line)
144 Grob *common = me->common_refpoint (next_bound_parent_on_this_line, Y_AXIS);
145 Real bound_offset = next_bound_parent_on_this_line->relative_coordinate (common, Y_AXIS);
146 y = next_ext.center () + bound_offset - me->relative_coordinate (common, Y_AXIS);
148 else
150 /* We fall back to assuming that the distance between staves doesn't
151 change over line breaks. */
152 programming_error ("next-bound's parent doesn't extend to this line");
153 Grob *next_system = next_bound->get_system ();
154 Grob *this_system = me->get_system ();
155 y = next_ext.center () + next_bound_parent->relative_coordinate (next_system, Y_AXIS)
156 - me->relative_coordinate (this_system, Y_AXIS);
159 else
161 y = me->get_bound (dir)->extent (common_y, Y_AXIS).center ();
162 details = scm_acons (ly_symbol2scm ("common-Y"), common_y->self_scm (), details);
165 y += dir * extra_dy / 2;
166 details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
169 return details;
172 MAKE_SCHEME_CALLBACK (Line_spanner, calc_right_bound_info, 1);
174 Line_spanner::calc_right_bound_info (SCM smob)
176 return Line_spanner::calc_bound_info (smob, RIGHT);
179 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info, 1);
181 Line_spanner::calc_left_bound_info (SCM smob)
183 return Line_spanner::calc_bound_info (smob, LEFT);
186 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info_and_text, 1);
188 Line_spanner::calc_left_bound_info_and_text (SCM smob)
190 SCM alist = Line_spanner::calc_bound_info (smob, LEFT);
191 Spanner *me = unsmob_spanner (smob);
193 SCM text = me->get_property ("text");
194 if (Text_interface::is_markup (text)
195 && me->get_bound (LEFT)->break_status_dir () == CENTER
196 && ly_assoc_get (ly_symbol2scm ("stencil"), alist, SCM_BOOL_F) == SCM_BOOL_F)
198 Output_def *layout = me->layout ();
199 SCM properties = Font_interface::text_font_alist_chain (me);
200 alist = scm_acons (ly_symbol2scm ("stencil"),
201 Text_interface::interpret_markup (layout->self_scm (),
202 properties, text),
203 alist);
206 return alist;
209 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
211 Line_spanner::print (SCM smob)
213 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
215 Interval_t<Moment> moments = me->spanned_time ();
217 We remove the line at the start of the line. For piano voice
218 indicators, it makes no sense to have them at the start of the
219 line.
221 I'm not sure what the official rules for glissandi are, but
222 usually the 2nd note of the glissando is "exact", so when playing
223 from the start of the line, there is no need to glide.
225 From a typographical p.o.v. this makes sense, since the amount of
226 space left of a note at the start of a line is very small.
228 --hwn.
231 if (moments.length () == Moment (0,0))
232 return SCM_EOL;
234 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
235 me->get_property ("right-bound-info"));
238 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
239 commonx = me->common_refpoint (commonx, X_AXIS);
241 Drul_array<Offset> span_points;
243 Direction d = LEFT;
246 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
247 bounds[d], SCM_BOOL_F), 0.0),
248 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
249 bounds[d], SCM_BOOL_F), 0.0));
251 span_points[d] = z;
253 while (flip (&d) != LEFT);
255 Drul_array<Real> gaps (0, 0);
256 Drul_array<bool> arrows (0, 0);
257 Drul_array<Real> anchor_align (0, 0);
258 Drul_array<Stencil*> stencils (0,0);
259 Drul_array<Grob*> common_y (0, 0);
262 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
263 bounds[d], SCM_BOOL_F), 0.0);
264 arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
265 bounds[d], SCM_BOOL_F));
266 anchor_align[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("anchor-alignment"),
267 bounds[d], SCM_BOOL_F), LEFT);
268 stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
269 bounds[d], SCM_BOOL_F));
270 common_y[d] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
271 bounds[d], SCM_BOOL_F));
272 if (!common_y[d])
273 common_y[d] = me;
275 while (flip (&d) != LEFT);
277 Grob *my_common_y = common_y[LEFT]->common_refpoint (common_y[RIGHT], Y_AXIS);
279 span_points[d][Y_AXIS] += common_y[d]->relative_coordinate (my_common_y, Y_AXIS);
280 while (flip (&d) != LEFT);
282 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
283 Offset dz_dir = dz.direction ();
284 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
286 return SCM_EOL;
289 Stencil line;
292 span_points[d] += -d * gaps[d] * dz.direction ();
294 if (stencils[d])
296 Interval ext = stencils[d]->extent (X_AXIS);
297 Real anchor = ext.linear_combination (anchor_align[d]) - ext[LEFT];
298 span_points[d][X_AXIS] -= anchor;
300 Stencil s = stencils[d]->translated (span_points[d]);
301 SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
302 bounds[d], SCM_BOOL_F);
303 SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
304 bounds[d], SCM_BOOL_F);
306 if (scm_is_number (align))
307 s.align_to (Y_AXIS, scm_to_double (align));
310 todo: should use font-size.
312 if (is_number_pair (off))
313 s.translate (ly_scm2offset (off));
315 line.add_stencil (s);
318 while (flip (&d) != LEFT);
322 if (stencils[d])
323 span_points[d] += dz_dir *
324 (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
326 while (flip (&d) != LEFT);
328 Offset adjust = dz.direction() * Staff_symbol_referencer::staff_space (me);
330 Offset line_left = span_points[LEFT] + (arrows[LEFT] ? adjust*1.4 : Offset (0, 0));
331 Offset line_right = span_points[RIGHT] - (arrows[RIGHT] ? adjust*0.55 : Offset (0, 0));
332 if (line_right[X_AXIS] > line_left[X_AXIS])
334 line.add_stencil (Line_interface::line (me, line_left, line_right));
336 line.add_stencil (Line_interface::arrows (me,
337 span_points[LEFT],
338 span_points[RIGHT],
339 arrows[LEFT],
340 arrows[RIGHT]));
343 line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
344 -me->relative_coordinate (my_common_y, Y_AXIS)));
347 return line.smobbed_copy ();
350 ADD_INTERFACE (Line_spanner,
351 "Generic line drawn between two objects, e.g., for use with"
352 " glissandi.",
354 /* properties */
355 "bound-details "
356 "extra-dy "
357 "gap "
358 "left-bound-info "
359 "note-columns "
360 "right-bound-info "
361 "thickness "
362 "to-barline "