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>
9 #include "align-interface.hh"
10 #include "axis-group-interface.hh"
11 #include "font-interface.hh"
12 #include "grob-interface.hh"
14 #include "lily-proto.hh"
15 #include "line-interface.hh"
16 #include "output-def.hh"
17 #include "pointer-group-interface.hh"
19 #include "staff-symbol-referencer.hh"
21 #include "text-interface.hh"
27 DECLARE_SCHEME_CALLBACK (print
, (SCM
));
28 DECLARE_SCHEME_CALLBACK (calc_left_bound_info
, (SCM
));
29 DECLARE_SCHEME_CALLBACK (calc_left_bound_info_and_text
, (SCM
));
30 DECLARE_SCHEME_CALLBACK (calc_right_bound_info
, (SCM
));
31 DECLARE_SCHEME_CALLBACK (calc_bound_info
, (SCM
, Direction
));
32 DECLARE_GROB_INTERFACE ();
35 Spanner
*parent_spanner (Grob
*g
)
37 if (Spanner::has_interface (g
))
38 return dynamic_cast<Spanner
*> (g
);
39 return parent_spanner (g
->get_parent (Y_AXIS
));
43 Line_spanner::calc_bound_info (SCM smob
, Direction dir
)
45 Spanner
*me
= unsmob_spanner (smob
);
47 Grob
*commonx
= me
->get_bound (LEFT
)->common_refpoint (me
->get_bound (RIGHT
), X_AXIS
);
48 commonx
= me
->common_refpoint (commonx
, X_AXIS
);
50 SCM bound_details
= me
->get_property ("bound-details");
52 SCM details
= SCM_BOOL_F
;
53 if (details
== SCM_BOOL_F
)
54 details
= ly_assoc_get ((dir
== LEFT
)
55 ? ly_symbol2scm ("left")
56 : ly_symbol2scm ("right"), bound_details
, SCM_BOOL_F
);
58 if (me
->get_bound (dir
)->break_status_dir ())
60 SCM extra
= ly_assoc_get ((dir
== LEFT
)
61 ? ly_symbol2scm ("left-broken")
62 : ly_symbol2scm ("right-broken"), bound_details
, SCM_EOL
);
64 for (SCM s
= scm_reverse (extra
); scm_is_pair (s
); s
= scm_cdr (s
))
65 details
= scm_cons (scm_car (s
), details
);
68 if (details
== SCM_BOOL_F
)
69 details
= ly_assoc_get (ly_symbol2scm ("default"), bound_details
, SCM_EOL
);
71 SCM text
= ly_assoc_get (ly_symbol2scm ("text"), details
, SCM_BOOL_F
);
72 if (Text_interface::is_markup (text
))
74 Output_def
*layout
= me
->layout ();
75 SCM properties
= Font_interface::text_font_alist_chain (me
);
76 details
= scm_acons (ly_symbol2scm ("stencil"),
77 Text_interface::interpret_markup (layout
->self_scm (),
82 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details
, SCM_BOOL_F
)))
84 Direction attach
= (Direction
)
85 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
89 Item
*bound_item
= me
->get_bound (dir
);
90 Grob
*bound_grob
= bound_item
;
91 if (to_boolean (ly_assoc_get (ly_symbol2scm ("end-on-note"), details
, SCM_BOOL_F
))
92 && bound_item
->break_status_dir ())
94 extract_grob_set (me
, "note-columns", columns
);
96 bound_grob
= (dir
== LEFT
)
97 ? columns
[0] : columns
.back();
100 details
= scm_acons (ly_symbol2scm ("X"),
101 scm_from_double (robust_relative_extent (bound_grob
, commonx
, X_AXIS
)
102 .linear_combination (attach
)),
107 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details
, SCM_BOOL_F
)))
111 Real extra_dy
= robust_scm2double (me
->get_property ("extra-dy"),
114 Grob
*common_y
= me
->common_refpoint (me
->get_bound (dir
), Y_AXIS
);
115 if (me
->get_bound (dir
)->break_status_dir ())
117 Spanner
*next_sp
= me
->broken_neighbor (dir
);
118 Item
*next_bound
= next_sp
->get_bound (dir
);
120 if (next_bound
->break_status_dir ())
122 programming_error ("no note heads for the line spanner on neighbor line?"
128 Spanner
*next_bound_parent
= parent_spanner (next_bound
);
129 Interval next_ext
= next_bound
->extent (next_bound_parent
, Y_AXIS
);
131 /* We want to know what would be the
132 y-position of the next bound (relative to my y-parent) if it belonged
133 to the same system as this bound. We rely on the fact that
134 the y-parent of the next bound is a spanner (probably the
135 VerticalAxisGroup of a staff) that extends over the break.
137 Spanner
*next_bound_parent_on_this_line
=
138 next_bound_parent
->broken_neighbor (other_dir (dir
));
140 if (next_bound_parent_on_this_line
)
142 Grob
*common
= me
->common_refpoint (next_bound_parent_on_this_line
, Y_AXIS
);
143 Real bound_offset
= next_bound_parent_on_this_line
->relative_coordinate (common
, Y_AXIS
);
144 y
= next_ext
.center () + bound_offset
- me
->relative_coordinate (common
, Y_AXIS
);
148 /* We fall back to assuming that the distance between staves doesn't
149 change over line breaks. */
150 programming_error ("next-bound's parent doesn't extend to this line");
151 Grob
*next_system
= next_bound
->get_system ();
152 Grob
*this_system
= me
->get_system ();
153 y
= next_ext
.center () + next_bound_parent
->relative_coordinate (next_system
, Y_AXIS
)
154 - me
->relative_coordinate (this_system
, Y_AXIS
);
159 y
= me
->get_bound (dir
)->extent (common_y
, Y_AXIS
).center ();
160 details
= scm_acons (ly_symbol2scm ("common-Y"), common_y
->self_scm (), details
);
163 y
+= dir
* extra_dy
/ 2;
164 details
= scm_acons (ly_symbol2scm ("Y"), scm_from_double (y
), details
);
170 MAKE_SCHEME_CALLBACK (Line_spanner
, calc_right_bound_info
, 1);
172 Line_spanner::calc_right_bound_info (SCM smob
)
174 return Line_spanner::calc_bound_info (smob
, RIGHT
);
177 MAKE_SCHEME_CALLBACK (Line_spanner
, calc_left_bound_info
, 1);
179 Line_spanner::calc_left_bound_info (SCM smob
)
181 return Line_spanner::calc_bound_info (smob
, LEFT
);
184 MAKE_SCHEME_CALLBACK (Line_spanner
, calc_left_bound_info_and_text
, 1);
186 Line_spanner::calc_left_bound_info_and_text (SCM smob
)
188 SCM alist
= Line_spanner::calc_bound_info (smob
, LEFT
);
189 Spanner
*me
= unsmob_spanner (smob
);
191 SCM text
= me
->get_property ("text");
192 if (Text_interface::is_markup (text
)
193 && me
->get_bound (LEFT
)->break_status_dir () == CENTER
194 && ly_assoc_get (ly_symbol2scm ("stencil"), alist
, SCM_BOOL_F
) == SCM_BOOL_F
)
196 Output_def
*layout
= me
->layout ();
197 SCM properties
= Font_interface::text_font_alist_chain (me
);
198 alist
= scm_acons (ly_symbol2scm ("stencil"),
199 Text_interface::interpret_markup (layout
->self_scm (),
207 MAKE_SCHEME_CALLBACK (Line_spanner
, print
, 1);
209 Line_spanner::print (SCM smob
)
211 Spanner
*me
= dynamic_cast<Spanner
*> (unsmob_grob (smob
));
213 Drul_array
<SCM
> bounds (me
->get_property ("left-bound-info"),
214 me
->get_property ("right-bound-info"));
216 Grob
*commonx
= me
->get_bound (LEFT
)->common_refpoint (me
->get_bound (RIGHT
), X_AXIS
);
217 commonx
= me
->common_refpoint (commonx
, X_AXIS
);
219 Drul_array
<Offset
> span_points
;
224 Offset
z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
225 bounds
[d
], SCM_BOOL_F
), 0.0),
226 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
227 bounds
[d
], SCM_BOOL_F
), 0.0));
231 while (flip (&d
) != LEFT
);
233 Drul_array
<Real
> gaps (0, 0);
234 Drul_array
<bool> arrows (0, 0);
235 Drul_array
<Real
> anchor_align (0, 0);
236 Drul_array
<Stencil
*> stencils (0,0);
237 Drul_array
<Grob
*> common_y (0, 0);
240 gaps
[d
] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
241 bounds
[d
], SCM_BOOL_F
), 0.0);
242 arrows
[d
] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
243 bounds
[d
], SCM_BOOL_F
));
244 anchor_align
[d
] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("anchor-alignment"),
245 bounds
[d
], SCM_BOOL_F
), LEFT
);
246 stencils
[d
] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
247 bounds
[d
], SCM_BOOL_F
));
248 common_y
[d
] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
249 bounds
[d
], SCM_BOOL_F
));
253 while (flip (&d
) != LEFT
);
255 Grob
*my_common_y
= common_y
[LEFT
]->common_refpoint (common_y
[RIGHT
], Y_AXIS
);
257 span_points
[d
][Y_AXIS
] += common_y
[d
]->relative_coordinate (my_common_y
, Y_AXIS
);
258 while (flip (&d
) != LEFT
);
260 Offset dz
= (span_points
[RIGHT
] - span_points
[LEFT
]);
261 Offset dz_dir
= dz
.direction ();
262 if (gaps
[LEFT
] + gaps
[RIGHT
] > dz
.length ())
270 span_points
[d
] += -d
* gaps
[d
] * dz
.direction ();
274 Interval ext
= stencils
[d
]->extent (X_AXIS
);
275 Real anchor
= ext
.linear_combination (anchor_align
[d
]) - ext
[LEFT
];
276 span_points
[d
][X_AXIS
] -= anchor
;
278 Stencil s
= stencils
[d
]->translated (span_points
[d
]);
279 SCM align
= ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
280 bounds
[d
], SCM_BOOL_F
);
281 SCM off
= ly_assoc_get (ly_symbol2scm ("stencil-offset"),
282 bounds
[d
], SCM_BOOL_F
);
284 if (scm_is_number (align
))
285 s
.align_to (Y_AXIS
, scm_to_double (align
));
288 todo: should use font-size.
290 if (is_number_pair (off
))
291 s
.translate (ly_scm2offset (off
));
293 line
.add_stencil (s
);
296 while (flip (&d
) != LEFT
);
301 span_points
[d
] += dz_dir
*
302 (stencils
[d
]->extent (X_AXIS
)[-d
] / dz_dir
[X_AXIS
]);
304 while (flip (&d
) != LEFT
);
306 Offset adjust
= dz
.direction() * Staff_symbol_referencer::staff_space (me
);
308 Offset line_left
= span_points
[LEFT
] + (arrows
[LEFT
] ? adjust
*1.4 : Offset (0, 0));
309 Offset line_right
= span_points
[RIGHT
] - (arrows
[RIGHT
] ? adjust
*0.55 : Offset (0, 0));
310 if (line_right
[X_AXIS
] > line_left
[X_AXIS
])
312 line
.add_stencil (Line_interface::line (me
, line_left
, line_right
));
314 line
.add_stencil (Line_interface::arrows (me
,
321 line
.translate (Offset (-me
->relative_coordinate (commonx
, X_AXIS
),
322 -me
->relative_coordinate (my_common_y
, Y_AXIS
)));
325 return line
.smobbed_copy ();
328 ADD_INTERFACE (Line_spanner
,
329 "Generic line drawn between two objects, e.g., for use with"