]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-spanner.cc
Merge branch 'master' of ssh+git://hanwen@git.sv.gnu.org/srv/git/lilypond
[lilypond.git] / lily / line-spanner.cc
1 /*
2   line-spanner.cc -- implement 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 "item.hh"
12 #include "staff-symbol-referencer.hh"
13 #include "font-interface.hh"
14 #include "warn.hh"
15 #include "align-interface.hh"
16 #include "line-interface.hh"
17 #include "moment.hh"
18
19 #include "lily-proto.hh"
20 #include "grob-interface.hh"
21 #include "text-interface.hh"
22
23 class Line_spanner
24 {
25 public:
26   DECLARE_SCHEME_CALLBACK (print, (SCM));
27   DECLARE_SCHEME_CALLBACK (after_line_breaking, (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 ();
33 };
34
35
36 static Grob *
37 line_spanner_common_parent (Grob *me)
38 {
39   /* FIXME: what is the right thing to do here, now that PianoStaves don't
40      have fixed spacing? */
41   Grob *common = 0; //find_fixed_alignment_parent (me);
42   if (!common)
43     {
44       common = Staff_symbol_referencer::get_staff_symbol (me);
45       if (common)
46         common = common->get_parent (Y_AXIS);
47       else
48         common = me->get_parent (Y_AXIS);
49     }
50
51   return common;
52 }
53
54 SCM
55 Line_spanner::calc_bound_info (SCM smob, Direction dir)
56 {
57   Spanner *me = unsmob_spanner (smob);
58
59   Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
60   commonx = me->common_refpoint (commonx, X_AXIS);
61
62   SCM bound_details = me->get_property ("bound-details");
63
64   SCM details = SCM_BOOL_F;
65   if (details == SCM_BOOL_F)
66     details = ly_assoc_get ((dir == LEFT)
67                             ? ly_symbol2scm ("left")
68                             : ly_symbol2scm ("right"), bound_details, SCM_BOOL_F);
69
70   if (me->get_bound (dir)->break_status_dir ())
71     {
72       SCM extra = ly_assoc_get ((dir == LEFT)
73                                 ? ly_symbol2scm ("left-broken")
74                                 : ly_symbol2scm ("right-broken"), bound_details, SCM_EOL);
75
76       for (SCM s = extra; scm_is_pair (s); s = scm_cdr (s))
77         details = scm_cons (scm_car (s), details);
78     }
79   
80   if (details == SCM_BOOL_F)
81     details = ly_assoc_get (ly_symbol2scm ("default"), bound_details, SCM_EOL);
82
83   SCM text = ly_assoc_get (ly_symbol2scm ("text"), details, SCM_BOOL_F);
84   if (Text_interface::is_markup (text))
85     {
86       Output_def *layout = me->layout ();
87       SCM properties = Font_interface::text_font_alist_chain (me);
88       details = scm_acons (ly_symbol2scm ("stencil"),
89                            Text_interface::interpret_markup (layout->self_scm (),
90                                                              properties, text),
91                            details);
92     }
93   
94   if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("X"), details, SCM_BOOL_F)))
95     {
96       Direction attach = (Direction)
97         robust_scm2int (ly_assoc_get (ly_symbol2scm ("attach-dir"),
98                                                      details, SCM_BOOL_F),
99                         CENTER);
100
101       details = scm_acons (ly_symbol2scm ("X"),
102                            scm_from_double (me->get_bound (dir)->extent (commonx, X_AXIS)
103                                             .linear_combination (attach)),
104                            details);
105     }
106   
107
108   if (!scm_is_number (ly_assoc_get (ly_symbol2scm ("Y"), details, SCM_BOOL_F)))
109     {
110       Real y = 0.0;
111
112       Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
113                                          0.0);
114          
115       if (me->get_bound (dir)->break_status_dir ())
116         {
117           /*
118             This is hairy. For the normal case, we simply find common
119             parents, and draw a line between the bounds. When two note
120             heads are on different systems, there is no common parent
121             anymore. We have to find the piano-staff object.
122           */
123
124           Spanner *next_sp = me->broken_neighbor (dir);
125           Item *next_bound = next_sp->get_bound (dir);
126
127           if (next_bound->break_status_dir ())
128             {
129               programming_error ("no note heads for the line spanner on neighbor line?"
130                                  " Confused.");
131               me->suicide ();
132               return SCM_EOL;
133             }
134
135           Grob *next_common_y = line_spanner_common_parent (next_bound);
136           Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
137
138           y = next_ext.center ();
139         }
140       else
141         {
142           Grob *commony = me->common_refpoint (me->get_bound (dir), Y_AXIS);
143           y = me->get_bound (dir)->extent (commony, Y_AXIS).center ();
144           details = scm_acons (ly_symbol2scm ("common-Y"), commony->self_scm (), details);
145         }
146
147       y += dir * extra_dy / 2; 
148       details = scm_acons (ly_symbol2scm ("Y"), scm_from_double (y), details);
149     }
150
151   return details;
152 }
153
154 MAKE_SCHEME_CALLBACK (Line_spanner, calc_right_bound_info, 1);
155 SCM
156 Line_spanner::calc_right_bound_info (SCM smob)
157 {
158   return Line_spanner::calc_bound_info (smob, RIGHT);
159 }
160
161 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info, 1);
162 SCM
163 Line_spanner::calc_left_bound_info (SCM smob)
164 {
165   return Line_spanner::calc_bound_info (smob, LEFT);
166 }
167
168 MAKE_SCHEME_CALLBACK (Line_spanner, calc_left_bound_info_and_text, 1);
169 SCM
170 Line_spanner::calc_left_bound_info_and_text (SCM smob)
171 {
172   SCM alist = Line_spanner::calc_bound_info (smob, LEFT);
173   Spanner *me = unsmob_spanner (smob);
174
175   SCM text = me->get_property ("text");
176   if (Text_interface::is_markup (text)
177       && me->get_bound (LEFT)->break_status_dir () == CENTER
178       && ly_assoc_get (ly_symbol2scm ("stencil"), alist, SCM_BOOL_F) == SCM_BOOL_F)
179     {
180       Output_def *layout = me->layout ();
181       SCM properties = Font_interface::text_font_alist_chain (me);
182       alist = scm_acons (ly_symbol2scm ("stencil"),
183                          Text_interface::interpret_markup (layout->self_scm (),
184                                                            properties, text),
185                          alist);
186     }
187   
188   return alist;
189 }
190
191 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
192 SCM
193 Line_spanner::print (SCM smob)
194 {
195   Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
196
197   Interval_t<Moment> moments = me->spanned_time ();
198   /*
199     We remove the line at the start of the line.  For piano voice
200     indicators, it makes no sense to have them at the start of the
201     line.
202
203     I'm not sure what the official rules for glissandi are, but
204     usually the 2nd note of the glissando is "exact", so when playing
205     from the start of the line, there is no need to glide.
206
207     From a typographical p.o.v. this makes sense, since the amount of
208     space left of a note at the start of a line is very small.
209
210     --hwn.
211
212   */
213   if (moments.length () == Moment (0,0))
214     return SCM_EOL;
215   
216   Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
217                           me->get_property ("right-bound-info"));
218
219   
220   Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
221   commonx = me->common_refpoint (commonx, X_AXIS);
222
223   Drul_array<Offset> span_points;
224
225   Direction d =  LEFT;
226   do
227     {
228       Offset z (robust_scm2double (ly_assoc_get (ly_symbol2scm ("X"),
229                                                  bounds[d], SCM_BOOL_F), 0.0)
230                 + commonx->relative_coordinate (commonx, X_AXIS),
231                 robust_scm2double (ly_assoc_get (ly_symbol2scm ("Y"),
232                                                  bounds[d], SCM_BOOL_F), 0.0));
233       
234       span_points[d] = z;
235     }
236   while (flip (&d) != LEFT);
237
238   Drul_array<Real> gaps (0, 0);
239   Drul_array<bool> arrows (0, 0);
240   Drul_array<Stencil*> stencils (0,0);
241   Drul_array<Grob*> common_y (0, 0);
242   do
243     {
244       gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
245                                                  bounds[d], SCM_BOOL_F), 0.0);
246       arrows[d] = to_boolean (ly_assoc_get (ly_symbol2scm ("arrow"),
247                                             bounds[d], SCM_BOOL_F));
248       stencils[d] = unsmob_stencil (ly_assoc_get (ly_symbol2scm ("stencil"),
249                                                   bounds[d], SCM_BOOL_F));
250       common_y[d] = unsmob_grob (ly_assoc_get (ly_symbol2scm ("common-Y"),
251                                                bounds[d], SCM_BOOL_F));
252       if (!common_y[d])
253         common_y[d] = me; 
254     }
255   while (flip (&d) != LEFT);
256
257   Grob *my_common_y = common_y[LEFT]->common_refpoint (common_y[RIGHT], Y_AXIS);
258   do
259     span_points[d][Y_AXIS] += common_y[d]->relative_coordinate (my_common_y, Y_AXIS);
260   while (flip (&d) != LEFT);
261
262   Offset dz = (span_points[RIGHT] - span_points[LEFT]);
263   Offset dz_dir = dz.direction ();
264   if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
265     {
266       return SCM_EOL;
267     }
268
269   Stencil line;
270   do
271     {
272       if (stencils[d])
273         {
274          Stencil s = stencils[d]->translated (span_points[d]);
275          SCM align = ly_assoc_get (ly_symbol2scm ("stencil-align-dir-y"),
276                                    bounds[d], SCM_BOOL_F);
277          SCM off = ly_assoc_get (ly_symbol2scm ("stencil-offset"),
278                                    bounds[d], SCM_BOOL_F);
279
280          if (scm_is_number (align)) 
281            s.align_to (Y_AXIS, scm_to_double (align));
282
283          /*
284            todo: should use font-size.
285           */
286          if (is_number_pair (off))
287            s.translate (ly_scm2offset (off));
288          
289          line.add_stencil (s);
290         }
291     }
292   while (flip (&d) != LEFT);
293
294   do
295     {
296       if (stencils[d])
297         span_points[d] += dz_dir *
298           (stencils[d]->extent (X_AXIS)[-d] / dz_dir[X_AXIS]);
299       
300       span_points[d] += -d * gaps[d] *  dz.direction ();
301     }
302   while (flip (&d) != LEFT);
303
304   line.add_stencil (Line_interface::line (me, 
305                                           span_points[LEFT],
306                                           span_points[RIGHT]));
307
308   line.add_stencil (Line_interface::arrows (me,
309                                             span_points[LEFT],
310                                             span_points[RIGHT],
311                                             arrows[LEFT],
312                                             arrows[RIGHT]));
313
314   line.translate (Offset (-me->relative_coordinate (commonx, X_AXIS),
315                           -me->relative_coordinate (my_common_y, Y_AXIS)));
316                           
317     
318   return line.smobbed_copy ();
319 }
320
321 ADD_INTERFACE (Line_spanner,
322                "Generic line drawn between two objects, e.g. for use with glissandi.\n"
323                "The property @code{style} can be @code{line}, "
324                "@code{dashed-line}, @code{trill}, \n"
325                "@code{dotted-line} or @code{zigzag}.\n"
326                "\n",
327
328                "extra-dy "
329                "gap "
330                "thickness "
331                "bound-details " 
332                "left-bound-info " 
333                "right-bound-info " 
334                );
335