]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-line-spanner.cc
extra-dy support for new line-spanner.
[lilypond.git] / lily / new-line-spanner.cc
1 /*
2   line-spanner.cc -- implement New_line_spanner
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2000--2007 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "spanner.hh"
10 #include "output-def.hh"
11 #include "paper-column.hh"
12 #include "staff-symbol-referencer.hh"
13 #include "font-interface.hh"
14 #include "warn.hh"
15 #include "align-interface.hh"
16 #include "lookup.hh"
17 #include "line-interface.hh"
18 #include "moment.hh"
19
20 #include "lily-proto.hh"
21 #include "grob-interface.hh"
22
23 class New_line_spanner
24 {
25 public:
26   DECLARE_SCHEME_CALLBACK (print, (SCM));
27   DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
28
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();
33
34   static Stencil line_stencil (Grob *me, Offset f, Offset t);
35 };
36
37
38
39 static Grob *
40 line_spanner_common_parent (Grob *me)
41 {
42   Grob *common = find_fixed_alignment_parent (me);
43   if (!common)
44     {
45       common = Staff_symbol_referencer::get_staff_symbol (me);
46       if (common)
47         common = common->get_parent (Y_AXIS);
48       else
49         common = me->get_parent (Y_AXIS);
50     }
51
52   return common;
53 }
54
55 MAKE_SCHEME_CALLBACK (New_line_spanner, after_line_breaking, 1);
56 SCM
57 New_line_spanner::after_line_breaking (SCM g)
58 {
59   Grob *me = unsmob_grob (g);
60   Spanner *sp = dynamic_cast<Spanner *> (me);
61
62   /*
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
65     line.
66
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.
70
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.
73
74     --hwn.
75
76   */
77   if (sp->get_bound (LEFT)->break_status_dir ()
78       && !sp->get_bound (RIGHT)->break_status_dir ())
79     {
80       /*
81         Can't do suicide, since this mucks up finding the trend.
82       */
83       me->set_property ("transparent", SCM_BOOL_T);
84     }
85   return SCM_EOL;
86 }
87
88 Stencil
89 New_line_spanner::line_stencil (Grob *me,
90                             Offset from,
91                             Offset to)
92 {
93   Offset dz = to -from;
94   SCM type = me->get_property ("style");
95
96   Stencil line;
97
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)))
104     {
105       line = Line_interface::line (me, from, to);
106     }
107   else if (scm_is_symbol (type)
108            && type == ly_symbol2scm ("trill"))
109     {
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")),
113                                     SCM_UNDEFINED);
114
115       Font_metric *fm = select_font (me->layout (),
116                                      scm_cons (style_alist,
117                                                alist_chain));
118       Stencil m = fm->find_by_name ("scripts.trill_element");
119       Stencil mol;
120
121       do
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])
126         ;
127
128       /*
129         FIXME: should center element on x/y
130       */
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);
134
135       mol.translate (from);
136       line = mol;
137     }
138
139   if (to_boolean (me->get_property ("arrow")))
140     line.add_stencil (Line_interface::arrows (me, from, to, false, true));
141
142   return line;
143 }
144
145
146 SCM
147 New_line_spanner::calc_bound_info (SCM smob, Direction dir)
148 {
149   Spanner *me = unsmob_spanner (smob);
150
151   Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
152   commonx = me->common_refpoint (commonx, X_AXIS);
153
154   SCM bound_details = me->get_property ("bound-details");
155
156   
157   SCM sym = 
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"));
163
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);
167
168   if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
169     {
170       Direction attach = (Direction)
171         robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
172                                                      details, SCM_BOOL_F),
173                         CENTER);
174       
175       details = scm_acons (ly_symbol2scm ("X"),
176                            scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
177                                             .linear_combination (attach)),
178                            details);
179     }
180   
181
182   if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
183     {
184       Real y = 0.0;
185
186       Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
187                                          0.0);
188          
189       if (me->get_bound (dir)->break_status_dir ())
190         {
191           /*
192             This is hairy. For the normal case, we simply find common
193             parents, and draw a line between the bounds. When two note
194             heads are on different systems, there is no common parent
195             anymore. We have to find the piano-staff object.
196           */
197
198           Spanner *next_sp = me->broken_neighbor (dir);
199           Item *next_bound = next_sp->get_bound (dir);
200
201           if (next_bound->break_status_dir ())
202             {
203               programming_error ("no note heads for the line spanner on neighbor line?"
204                                  " Confused.");
205               me->suicide ();
206               return SCM_EOL;
207             }
208
209           Grob *next_common_y = line_spanner_common_parent (next_bound);
210           Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
211
212           y = next_ext.center ();
213         }
214       else
215         {
216           Grob *commony = me->get_bound (LEFT);
217           commony = me->common_refpoint (commony, Y_AXIS);
218           y = me->get_bound (dir)->extent (commony, Y_AXIS).center();
219         }
220
221       y += dir * extra_dy / 2; 
222       details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
223     }
224
225   return details;
226 }
227
228 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_right_bound_info, 1);
229 SCM
230 New_line_spanner::calc_right_bound_info (SCM smob)
231 {
232   return New_line_spanner::calc_bound_info (smob, RIGHT);
233 }
234
235 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_left_bound_info, 1);
236 SCM
237 New_line_spanner::calc_left_bound_info (SCM smob)
238 {
239   return New_line_spanner::calc_bound_info (smob, LEFT);
240 }
241
242 MAKE_SCHEME_CALLBACK (New_line_spanner, print, 1);
243 SCM
244 New_line_spanner::print (SCM smob)
245 {
246   Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
247
248   Interval_t<Moment> moments = me->spanned_time ();
249   if (moments.length () == Moment (0,0))
250     {
251       me->set_property ("transparent", SCM_BOOL_T);
252       return SCM_EOL;
253     }
254   
255   Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
256                           me->get_property ("right-bound-info"));
257
258   
259   Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
260   commonx = me->common_refpoint (commonx, X_AXIS);
261
262   Drul_array<Offset> span_points;
263
264   Direction d =  LEFT;
265   do
266     {
267       Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
268                                                  bounds[d], SCM_BOOL_F), 0.0)
269                 + commonx->relative_coordinate (commonx, X_AXIS),
270                 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
271                                                  bounds[d], SCM_BOOL_F), 0.0));
272       
273       span_points[d] = z;
274     }
275   while (flip (&d) != LEFT);
276
277   Offset dz = (span_points[RIGHT] - span_points[LEFT]);
278   Drul_array<Real> gaps;
279   do
280      gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
281                                                 bounds[d], SCM_BOOL_F), 0.0);
282   while (flip (&d) != LEFT);
283
284   if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
285     {
286       me->set_property ("transparent", SCM_BOOL_T);
287       return SCM_EOL;
288     }
289
290   do
291     span_points[d] += -d * gaps[d] *  dz.direction ();
292   while (flip (&d) != LEFT);
293
294   Offset my_z (me->relative_coordinate (commonx, X_AXIS), 0);
295   
296   span_points[LEFT] -= my_z;
297   span_points[RIGHT] -= my_z;
298   
299   Stencil line = line_stencil (me,
300                                span_points[LEFT],
301                                span_points[RIGHT]);
302
303   
304   return line.smobbed_copy ();
305 }
306
307 ADD_INTERFACE (New_line_spanner,
308                "Generic line drawn between two objects, e.g. for use with glissandi.\n"
309                "The property @code{style} can be @code{line}, "
310                "@code{dashed-line}, @code{trill}, \n"
311                "@code{dotted-line} or @code{zigzag}.\n"
312                "\n",
313
314                "extra-dy "
315                "arrow "
316                "gap "
317                "thickness "
318                "bound-details " 
319                "left-bound-info " 
320                "right-bound-info " 
321                );
322