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