2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2010 Jan Nieuwenhuizen <janneke@gnu.org>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "align-interface.hh"
21 #include "axis-group-interface.hh"
22 #include "font-interface.hh"
23 #include "grob-interface.hh"
25 #include "lily-proto.hh"
26 #include "line-interface.hh"
27 #include "output-def.hh"
28 #include "pointer-group-interface.hh"
30 #include "staff-symbol-referencer.hh"
32 #include "text-interface.hh"
38 DECLARE_SCHEME_CALLBACK (print
, (SCM
));
39 DECLARE_SCHEME_CALLBACK (calc_left_bound_info
, (SCM
));
40 DECLARE_SCHEME_CALLBACK (calc_left_bound_info_and_text
, (SCM
));
41 DECLARE_SCHEME_CALLBACK (calc_right_bound_info
, (SCM
));
42 DECLARE_SCHEME_CALLBACK (calc_bound_info
, (SCM
, Direction
));
43 DECLARE_GROB_INTERFACE ();
46 Spanner
*parent_spanner (Grob
*g
)
48 if (Spanner::has_interface (g
))
49 return dynamic_cast<Spanner
*> (g
);
50 return parent_spanner (g
->get_parent (Y_AXIS
));
54 Line_spanner::calc_bound_info (SCM smob
, Direction dir
)
56 Spanner
*me
= unsmob_spanner (smob
);
58 Grob
*commonx
= me
->get_bound (LEFT
)->common_refpoint (me
->get_bound (RIGHT
), X_AXIS
);
59 commonx
= me
->common_refpoint (commonx
, X_AXIS
);
61 SCM bound_details
= me
->get_property ("bound-details");
63 SCM details
= SCM_BOOL_F
;
64 if (details
== SCM_BOOL_F
)
65 details
= ly_assoc_get ((dir
== LEFT
)
66 ? ly_symbol2scm ("left")
67 : ly_symbol2scm ("right"), bound_details
, SCM_BOOL_F
);
69 if (me
->get_bound (dir
)->break_status_dir ())
71 SCM extra
= ly_assoc_get ((dir
== LEFT
)
72 ? ly_symbol2scm ("left-broken")
73 : ly_symbol2scm ("right-broken"), bound_details
, SCM_EOL
);
75 for (SCM s
= scm_reverse (extra
); scm_is_pair (s
); s
= scm_cdr (s
))
76 details
= scm_cons (scm_car (s
), details
);
79 if (details
== SCM_BOOL_F
)
80 details
= ly_assoc_get (ly_symbol2scm ("default"), bound_details
, SCM_EOL
);
82 SCM text
= ly_assoc_get (ly_symbol2scm ("text"), details
, SCM_BOOL_F
);
83 if (Text_interface::is_markup (text
))
85 Output_def
*layout
= me
->layout ();
86 SCM properties
= Font_interface::text_font_alist_chain (me
);
87 details
= scm_acons (ly_symbol2scm ("stencil"),
88 Text_interface::interpret_markup (layout
->self_scm (),
93 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details
, SCM_BOOL_F
)))
95 Direction attach
= (Direction
)
96 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
100 Item
*bound_item
= me
->get_bound (dir
);
101 Grob
*bound_grob
= bound_item
;
102 if (to_boolean (ly_assoc_get (ly_symbol2scm ("end-on-note"), details
, SCM_BOOL_F
))
103 && bound_item
->break_status_dir ())
105 extract_grob_set (me
, "note-columns", columns
);
107 bound_grob
= (dir
== LEFT
)
108 ? columns
[0] : columns
.back();
111 details
= scm_acons (ly_symbol2scm ("X"),
112 scm_from_double (robust_relative_extent (bound_grob
, commonx
, X_AXIS
)
113 .linear_combination (attach
)),
118 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details
, SCM_BOOL_F
)))
122 Real extra_dy
= robust_scm2double (me
->get_property ("extra-dy"),
125 Grob
*common_y
= me
->common_refpoint (me
->get_bound (dir
), Y_AXIS
);
126 if (me
->get_bound (dir
)->break_status_dir ())
128 Spanner
*next_sp
= me
->broken_neighbor (dir
);
129 Item
*next_bound
= next_sp
->get_bound (dir
);
131 if (next_bound
->break_status_dir ())
133 programming_error ("no note heads for the line spanner on neighbor line?"
139 Spanner
*next_bound_parent
= parent_spanner (next_bound
);
140 Interval next_ext
= next_bound
->extent (next_bound_parent
, Y_AXIS
);
143 We want to know what would be the y-position of the next
144 bound (relative to my y-parent) if it belonged to the
145 same system as this bound. We rely on the fact that the
146 y-parent of the next bound is a spanner (probably the
147 VerticalAxisGroup of a staff) that extends over the break.
149 Spanner
*next_bound_parent_on_this_line
=
150 next_bound_parent
->broken_neighbor (other_dir (dir
));
152 if (next_bound_parent_on_this_line
)
154 Grob
*common
= me
->common_refpoint (next_bound_parent_on_this_line
, Y_AXIS
);
155 Real bound_offset
= next_bound_parent_on_this_line
->relative_coordinate (common
, Y_AXIS
);
156 y
= next_ext
.center () + bound_offset
- me
->relative_coordinate (common
, Y_AXIS
);
161 We fall back to assuming that the distance between
162 staves doesn't change over line breaks.
164 programming_error ("next-bound's parent doesn't extend to this line");
165 Grob
*next_system
= next_bound
->get_system ();
166 Grob
*this_system
= me
->get_system ();
167 y
= next_ext
.center () + next_bound_parent
->relative_coordinate (next_system
, Y_AXIS
)
168 - me
->relative_coordinate (this_system
, Y_AXIS
);
173 y
= me
->get_bound (dir
)->extent (common_y
, Y_AXIS
).center ();
174 details
= scm_acons (ly_symbol2scm ("common-Y"), common_y
->self_scm (), details
);
177 y
+= dir
* extra_dy
/ 2;
178 details
= scm_acons (ly_symbol2scm ("Y"), scm_from_double (y
), details
);
184 MAKE_SCHEME_CALLBACK (Line_spanner
, calc_right_bound_info
, 1);
186 Line_spanner::calc_right_bound_info (SCM smob
)
188 return Line_spanner::calc_bound_info (smob
, RIGHT
);
191 MAKE_SCHEME_CALLBACK (Line_spanner
, calc_left_bound_info
, 1);
193 Line_spanner::calc_left_bound_info (SCM smob
)
195 return Line_spanner::calc_bound_info (smob
, LEFT
);
198 MAKE_SCHEME_CALLBACK (Line_spanner
, calc_left_bound_info_and_text
, 1);
200 Line_spanner::calc_left_bound_info_and_text (SCM smob
)
202 SCM alist
= Line_spanner::calc_bound_info (smob
, LEFT
);
203 Spanner
*me
= unsmob_spanner (smob
);
205 SCM text
= me
->get_property ("text");
206 if (Text_interface::is_markup (text
)
207 && me
->get_bound (LEFT
)->break_status_dir () == CENTER
208 && ly_assoc_get (ly_symbol2scm ("stencil"), alist
, SCM_BOOL_F
) == SCM_BOOL_F
)
210 Output_def
*layout
= me
->layout ();
211 SCM properties
= Font_interface::text_font_alist_chain (me
);
212 alist
= scm_acons (ly_symbol2scm ("stencil"),
213 Text_interface::interpret_markup (layout
->self_scm (),
221 MAKE_SCHEME_CALLBACK (Line_spanner
, print
, 1);
223 Line_spanner::print (SCM smob
)
225 Spanner
*me
= dynamic_cast<Spanner
*> (unsmob_grob (smob
));
227 Drul_array
<SCM
> bounds (me
->get_property ("left-bound-info"),
228 me
->get_property ("right-bound-info"));
230 Grob
*commonx
= me
->get_bound (LEFT
)->common_refpoint (me
->get_bound (RIGHT
), X_AXIS
);
231 commonx
= me
->common_refpoint (commonx
, X_AXIS
);
233 Drul_array
<Offset
> span_points
;
238 Offset
z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
239 bounds
[d
], SCM_BOOL_F
), 0.0),
240 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
241 bounds
[d
], SCM_BOOL_F
), 0.0));
245 while (flip (&d
) != LEFT
);
247 Drul_array
<Real
> gaps (0, 0);
248 Drul_array
<bool> arrows (0, 0);
249 Drul_array
<Real
> anchor_align (0, 0);
250 Drul_array
<Stencil
*> stencils (0,0);
251 Drul_array
<Grob
*> common_y (0, 0);
253 // For scaling of 'padding and 'stencil-offset
255 = pow (2, robust_scm2double (me
->get_property ("font-size"), 0.0) / 6);
259 gaps
[d
] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
260 bounds
[d
], SCM_BOOL_F
), 0.0);
261 arrows
[d
] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
262 bounds
[d
], SCM_BOOL_F
));
263 anchor_align
[d
] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("anchor-alignment"),
264 bounds
[d
], SCM_BOOL_F
), LEFT
);
265 stencils
[d
] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
266 bounds
[d
], SCM_BOOL_F
));
267 common_y
[d
] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
268 bounds
[d
], SCM_BOOL_F
));
272 while (flip (&d
) != LEFT
);
274 Grob
*my_common_y
= common_y
[LEFT
]->common_refpoint (common_y
[RIGHT
], Y_AXIS
);
276 span_points
[d
][Y_AXIS
] += common_y
[d
]->relative_coordinate (my_common_y
, Y_AXIS
);
277 while (flip (&d
) != LEFT
);
279 Offset dz
= (span_points
[RIGHT
] - span_points
[LEFT
]);
280 Offset dz_dir
= dz
.direction ();
281 if (gaps
[LEFT
] + gaps
[RIGHT
] > dz
.length ())
289 span_points
[d
] += -d
* gaps
[d
] * magstep
* dz
.direction ();
293 Interval ext
= stencils
[d
]->extent (X_AXIS
);
294 Real anchor
= ext
.linear_combination (anchor_align
[d
]) - ext
[LEFT
];
295 span_points
[d
][X_AXIS
] -= anchor
;
297 Stencil s
= stencils
[d
]->translated (span_points
[d
]);
298 SCM align
= ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
299 bounds
[d
], SCM_BOOL_F
);
300 SCM off
= ly_assoc_get (ly_symbol2scm ("stencil-offset"),
301 bounds
[d
], SCM_BOOL_F
);
303 if (scm_is_number (align
))
304 s
.align_to (Y_AXIS
, scm_to_double (align
));
306 if (is_number_pair (off
))
307 s
.translate (ly_scm2offset (off
) * magstep
);
309 line
.add_stencil (s
);
312 while (flip (&d
) != LEFT
);
317 span_points
[d
] += dz_dir
*
318 (stencils
[d
]->extent (X_AXIS
)[-d
] / dz_dir
[X_AXIS
]);
320 while (flip (&d
) != LEFT
);
322 Offset adjust
= dz
.direction() * Staff_symbol_referencer::staff_space (me
);
324 Offset line_left
= span_points
[LEFT
] + (arrows
[LEFT
] ? adjust
*1.4 : Offset (0, 0));
325 Offset line_right
= span_points
[RIGHT
] - (arrows
[RIGHT
] ? adjust
*0.55 : Offset (0, 0));
326 if (line_right
[X_AXIS
] > line_left
[X_AXIS
])
328 line
.add_stencil (Line_interface::line (me
, line_left
, line_right
));
330 line
.add_stencil (Line_interface::arrows (me
,
337 line
.translate (Offset (-me
->relative_coordinate (commonx
, X_AXIS
),
338 -me
->relative_coordinate (my_common_y
, Y_AXIS
)));
341 return line
.smobbed_copy ();
344 ADD_INTERFACE (Line_spanner
,
345 "Generic line drawn between two objects, e.g., for use with"