]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-line-spanner.cc
c6d8773f5cdaa7ef2f1494c124def397e5bd99d8
[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       if (me->get_bound (dir)->break_status_dir ())
187         {
188           /*
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.
193           */
194
195           Spanner *next_sp = me->broken_neighbor (dir);
196           Item *next_bound = next_sp->get_bound (dir);
197
198           if (next_bound->break_status_dir ())
199             {
200               programming_error ("no note heads for the line spanner on neighbor line?"
201                                  " Confused.");
202               me->suicide ();
203               return SCM_EOL;
204             }
205
206           Grob *next_common_y = line_spanner_common_parent (next_bound);
207           Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
208
209           y = next_ext.center ();
210         }
211       else
212         {
213           Grob *commony = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), Y_AXIS);
214           commony = me->common_refpoint (commony, Y_AXIS);
215         
216           details = scm_acons (ly_symbol2scm ("Y"),
217                                scm_from_double (me->get_bound (dir)->extent (commony, Y_AXIS).center()),
218                                details);
219         }
220     }
221
222   return details;
223 }
224
225 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_right_bound_info, 1);
226 SCM
227 New_line_spanner::calc_right_bound_info (SCM smob)
228 {
229   return New_line_spanner::calc_bound_info (smob, RIGHT);
230 }
231
232 MAKE_SCHEME_CALLBACK (New_line_spanner, calc_left_bound_info, 1);
233 SCM
234 New_line_spanner::calc_left_bound_info (SCM smob)
235 {
236   return New_line_spanner::calc_bound_info (smob, LEFT);
237 }
238
239 MAKE_SCHEME_CALLBACK (New_line_spanner, print, 1);
240 SCM
241 New_line_spanner::print (SCM smob)
242 {
243   Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
244
245   Interval_t<Moment> moments = me->spanned_time ();
246   if (moments.length () == Moment (0,0))
247     {
248       me->set_property ("transparent", SCM_BOOL_T);
249       return SCM_EOL;
250     }
251   
252   Drul_array<SCM> bounds (me->get_property ("left-bound-info"),
253                           me->get_property ("right-bound-info"));
254
255   
256   Grob *commonx = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
257   commonx = me->common_refpoint (commonx, X_AXIS);
258
259   Drul_array<Offset> span_points;
260
261   Direction d =  LEFT;
262   do
263     {
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));
269       
270       span_points[d] = z;
271     }
272   while (flip (&d) != LEFT);
273
274   Offset dz = (span_points[RIGHT] - span_points[LEFT]);
275   Drul_array<Real> gaps;
276   do
277      gaps[d] = robust_scm2double (ly_assoc_get (ly_symbol2scm ("padding"),
278                                                 bounds[d], SCM_BOOL_F), 0.0);
279   while (flip (&d) != LEFT);
280
281   if (gaps[LEFT] + gaps[RIGHT] > dz.length ())
282     {
283       me->set_property ("transparent", SCM_BOOL_T);
284       return SCM_EOL;
285     }
286
287   do
288     span_points[d] += -d * gaps[d] *  dz.direction ();
289   while (flip (&d) != LEFT);
290
291   Offset my_z (me->relative_coordinate (commonx, X_AXIS), 0);
292   
293   span_points[LEFT] -= my_z;
294   span_points[RIGHT] -= my_z;
295   
296   Stencil line = line_stencil (me,
297                                span_points[LEFT],
298                                span_points[RIGHT]);
299
300   
301   return line.smobbed_copy ();
302 }
303
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"
309                "\n",
310
311                "extra-dy "
312                "arrow "
313                "gap "
314                "thickness "
315                "zigzag-length "
316                "zigzag-width "
317                );
318