2 line-spanner.cc -- implement New_line_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2007 Jan Nieuwenhuizen <janneke@gnu.org>
10 #include "output-def.hh"
11 #include "paper-column.hh"
12 #include "staff-symbol-referencer.hh"
13 #include "font-interface.hh"
15 #include "align-interface.hh"
17 #include "line-interface.hh"
20 #include "lily-proto.hh"
21 #include "grob-interface.hh"
23 class New_line_spanner
26 DECLARE_SCHEME_CALLBACK (print, (SCM));
27 DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
29 DECLARE_SCHEME_CALLBACK (calc_left_bound_info, (SCM));
30 DECLARE_SCHEME_CALLBACK (calc_right_bound_info, (SCM));
31 DECLARE_SCHEME_CALLBACK (calc_bound_info, (SCM, Direction));
32 DECLARE_GROB_INTERFACE();
34 static Stencil line_stencil (Grob *me, Offset f, Offset t);
40 line_spanner_common_parent (Grob *me)
42 Grob *common = find_fixed_alignment_parent (me);
45 common = Staff_symbol_referencer::get_staff_symbol (me);
47 common = common->get_parent (Y_AXIS);
49 common = me->get_parent (Y_AXIS);
55 MAKE_SCHEME_CALLBACK (New_line_spanner, after_line_breaking, 1);
57 New_line_spanner::after_line_breaking (SCM g)
59 Grob *me = unsmob_grob (g);
60 Spanner *sp = dynamic_cast<Spanner *> (me);
63 We remove the line at the start of the line. For piano voice
64 indicators, it makes no sense to have them at the start of the
67 I'm not sure what the official rules for glissandi are, but
68 usually the 2nd note of the glissando is "exact", so when playing
69 from the start of the line, there is no need to glide.
71 From a typographical p.o.v. this makes sense, since the amount of
72 space left of a note at the start of a line is very small.
77 if (sp->get_bound (LEFT)->break_status_dir ()
78 && !sp->get_bound (RIGHT)->break_status_dir ())
81 Can't do suicide, since this mucks up finding the trend.
83 me->set_property ("transparent", SCM_BOOL_T);
89 New_line_spanner::line_stencil (Grob *me,
94 SCM type = me->get_property ("style");
98 if (scm_is_symbol (type)
99 && (type == ly_symbol2scm ("line")
100 || type == ly_symbol2scm ("dashed-line")
101 || type == ly_symbol2scm ("dotted-line")
102 || type == ly_symbol2scm ("zigzag")
103 || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
105 line = Line_interface::line (me, from, to);
107 else if (scm_is_symbol (type)
108 && type == ly_symbol2scm ("trill"))
110 SCM alist_chain = Font_interface::text_font_alist_chain (me);
111 SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
112 ly_symbol2scm ("fetaMusic")),
115 Font_metric *fm = select_font (me->layout (),
116 scm_cons (style_alist,
118 Stencil m = fm->find_by_name ("scripts.trill_element");
122 mol.add_at_edge (X_AXIS, RIGHT, m, 0);
123 while (m.extent (X_AXIS).length ()
124 && mol.extent (X_AXIS).length ()
125 + m.extent (X_AXIS).length () < dz[X_AXIS])
129 FIXME: should center element on x/y
131 mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
132 mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
133 + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
135 mol.translate (from);
139 if (to_boolean (me->get_property ("arrow")))
140 line.add_stencil (Line_interface::arrows (me, from, to, false, true));
147 New_line_spanner::calc_bound_info (SCM smob, Direction dir)
149 Spanner *me = unsmob_spanner (smob);
151 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
152 commonx = me->common_refpoint (commonx, X_AXIS);
154 SCM bound_details = me->get_property ("bound-details");
158 (me->get_bound (dir)->break_status_dir ())
159 ? (dir == LEFT ? ly_symbol2scm ("left-broken")
160 : ly_symbol2scm ("right-broken"))
161 : (dir == LEFT ? ly_symbol2scm ("left")
162 : ly_symbol2scm ("right"));
164 SCM details = ly_assoc_get (sym, bound_details, SCM_BOOL_F);
165 if (details == SCM_BOOL_F)
166 details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
168 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
170 Direction attach = (Direction)
171 robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
172 details, SCM_BOOL_F),
175 details = scm_acons (ly_symbol2scm ("X"),
176 scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
177 .linear_combination (attach)),
182 if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
186 if (me->get_bound (dir)->break_status_dir ())
189 This is hairy. For the normal case, we simply find common
190 parents, and draw a line between the bounds. When two note
191 heads are on different systems, there is no common parent
192 anymore. We have to find the piano-staff object.
195 Spanner *next_sp = me->broken_neighbor (dir);
196 Item *next_bound = next_sp->get_bound (dir);
198 if (next_bound->break_status_dir ())
200 programming_error ("no note heads for the line spanner on neighbor line?"
206 Grob *next_common_y = line_spanner_common_parent (next_bound);
207 Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
209 y = next_ext.center ();
213 Grob *commony = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), Y_AXIS);
214 commony = me->common_refpoint (commony, Y_AXIS);
216 details = scm_acons (ly_symbol2scm ("Y"),
217 scm_from_double (me->get_bound (dir)->extent (commony, Y_AXIS).center()),
225 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_right_bound_info, 1);
227 New_line_spanner::calc_right_bound_info (SCM smob)
229 return New_line_spanner::calc_bound_info (smob, RIGHT);
232 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_left_bound_info, 1);
234 New_line_spanner::calc_left_bound_info (SCM smob)
236 return New_line_spanner::calc_bound_info (smob, LEFT);
239 MAKE_SCHEME_CALLBACK (New_line_spanner, print, 1);
241 New_line_spanner::print (SCM smob)
243 Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
245 Interval_t<Moment> moments = me->spanned_time ();
246 if (moments.length () == Moment (0,0))
248 me->set_property ("transparent", SCM_BOOL_T);
252 Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
253 me->get_property ("right-bound-info"));
256 Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
257 commonx = me->common_refpoint (commonx, X_AXIS);
259 Drul_array<Offset> span_points;
264 Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
265 bounds[d], SCM_BOOL_F), 0.0)
266 + commonx->relative_coordinate (commonx, X_AXIS),
267 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
268 bounds[d], SCM_BOOL_F), 0.0));
272 while (flip (&d) != LEFT);
274 Offset dz = (span_points[RIGHT] - span_points[LEFT]);
275 Drul_array<Real> gaps;
277 gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
278 bounds[d], SCM_BOOL_F), 0.0);
279 while (flip (&d) != LEFT);
281 if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
283 me->set_property ("transparent", SCM_BOOL_T);
288 span_points[d] += -d * gaps[d] * dz.direction ();
289 while (flip (&d) != LEFT);
291 Offset my_z (me->relative_coordinate (commonx, X_AXIS), 0);
293 span_points[LEFT] -= my_z;
294 span_points[RIGHT] -= my_z;
296 Stencil line = line_stencil (me,
301 return line.smobbed_copy ();
304 ADD_INTERFACE (New_line_spanner,
305 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
306 "The property @code{style} can be @code{line}, "
307 "@code{dashed-line}, @code{trill}, \n"
308 "@code{dotted-line} or @code{zigzag}.\n"