]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-spanner.cc
move zigzag to line-interface.
[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 "line-spanner.hh"
10
11
12 #include "spanner.hh"
13 #include "output-def.hh"
14 #include "paper-column.hh"
15 #include "staff-symbol-referencer.hh"
16 #include "font-interface.hh"
17 #include "warn.hh"
18 #include "align-interface.hh"
19 #include "lookup.hh"
20 #include "line-interface.hh"
21
22 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
23 SCM
24 Line_spanner::after_line_breaking (SCM g)
25 {
26   Grob *me = unsmob_grob (g);
27   Spanner *sp = dynamic_cast<Spanner *> (me);
28
29   /*
30     We remove the line at the start of the line.  For piano voice
31     indicators, it makes no sense to have them at the start of the
32     line.
33
34     I'm not sure what the official rules for glissandi are, but
35     usually the 2nd note of the glissando is "exact", so when playing
36     from the start of the line, there is no need to glide.
37
38     From a typographical p.o.v. this makes sense, since the amount of
39     space left of a note at the start of a line is very small.
40
41     --hwn.
42
43   */
44   if (sp->get_bound (LEFT)->break_status_dir ()
45       && !sp->get_bound (RIGHT)->break_status_dir ())
46     {
47       /*
48         Can't do suicide, since this mucks up finding the trend.
49       */
50       me->set_property ("transparent", SCM_BOOL_T);
51     }
52   return SCM_EOL;
53 }
54
55 Stencil
56 Line_spanner::line_stencil (Grob *me,
57                             Offset from,
58                             Offset to)
59 {
60   Offset dz = to -from;
61   SCM type = me->get_property ("style");
62
63   Stencil line;
64
65   if (scm_is_symbol (type)
66       && (type == ly_symbol2scm ("line")
67           || type == ly_symbol2scm ("dashed-line")
68           || type == ly_symbol2scm ("dotted-line")
69           || type == ly_symbol2scm ("zigzag")
70           || (type == ly_symbol2scm ("trill") && dz[Y_AXIS] != 0)))
71     {
72       line = Line_interface::line (me, from, to);
73     }
74   else if (scm_is_symbol (type)
75            && type == ly_symbol2scm ("trill"))
76     {
77       SCM alist_chain = Font_interface::text_font_alist_chain (me);
78       SCM style_alist = scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
79                                               ly_symbol2scm ("fetaMusic")),
80                                     SCM_UNDEFINED);
81
82       Font_metric *fm = select_font (me->layout (),
83                                      scm_cons (style_alist,
84                                                alist_chain));
85       Stencil m = fm->find_by_name ("scripts.trill_element");
86       Stencil mol;
87
88       do
89         mol.add_at_edge (X_AXIS, RIGHT, m, 0);
90       while (m.extent (X_AXIS).length ()
91              && mol.extent (X_AXIS).length ()
92              + m.extent (X_AXIS).length () < dz[X_AXIS])
93         ;
94
95       /*
96         FIXME: should center element on x/y
97       */
98       mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
99       mol.translate_axis (- (mol.extent (Y_AXIS)[DOWN]
100                              + mol.extent (Y_AXIS).length ()) / 2, Y_AXIS);
101
102       mol.translate (from);
103       line = mol;
104     }
105
106   if (to_boolean (me->get_property ("arrow")))
107     line.add_stencil (Line_interface::arrows (me, from, to, false, true));
108
109   return line;
110 }
111
112 /*
113   Find a common Y parent, which --if found-- should be the
114   fixed-distance alignment.
115 */
116 Grob *
117 line_spanner_common_parent (Grob *me)
118 {
119   Grob *common = find_fixed_alignment_parent (me);
120   if (!common)
121     {
122       common = Staff_symbol_referencer::get_staff_symbol (me);
123       if (common)
124         common = common->get_parent (Y_AXIS);
125       else
126         common = me->get_parent (Y_AXIS);
127     }
128
129   return common;
130 }
131
132 /*
133   Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
134
135   (If not, you risk that this is called from the staff-alignment
136   routine, via stencil_extent. At this point, the staves aren't
137   separated yet, so it doesn't work cross-staff.
138
139   (huh? crossable staves have fixed distance? --hwn)
140 */
141
142 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
143 SCM
144 Line_spanner::print (SCM smob)
145 {
146   Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
147
148   Drul_array<Item *> bound (me->get_bound (LEFT),
149                             me->get_bound (RIGHT));
150
151   Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
152
153   Offset ofxy (gap, 0); /* offset from start point to start of line */
154   Offset dxy;
155   Offset my_off;
156   Offset his_off;
157
158   Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
159                                      0.0);
160   
161   if (bound[RIGHT]->break_status_dir ())
162     {
163       if (bound[LEFT]->break_status_dir ())
164         {
165           programming_error ("line-spanner with two broken ends. Farewell sweet world.");
166
167           me->suicide ();
168           return SCM_EOL;
169         }
170
171       /*
172         This is hairy. For the normal case, we simply find common
173         parents, and draw a line between the bounds. When two note
174         heads are on different systems, there is no common parent
175         anymore. We have to find the piano-staff object.
176       */
177
178       Spanner *next_sp = me->broken_neighbor (RIGHT);
179       Item *next_bound = next_sp->get_bound (RIGHT);
180
181       if (next_bound->break_status_dir ())
182         {
183           programming_error ("no note heads for the line spanner on next line?"
184                              " Confused.");
185           me->suicide ();
186           return SCM_EOL;
187         }
188
189       Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
190       commonx = me->common_refpoint (commonx, X_AXIS);
191
192       Grob *next_common_y = line_spanner_common_parent (next_bound);
193       Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
194
195       Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
196
197       Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
198       Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
199
200       Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
201
202       Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
203                  this_ext.center () + yoff - extra_dy / 2);
204       Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
205                  next_ext.center () + yoff + extra_dy / 2);
206
207       Offset dz (p2 - p1);
208       Real len = dz.length ();
209
210       Offset dir = dz * (1 / len);
211       dz = (dz.length () - 2 * gap) * dir;
212
213       Stencil l (line_stencil (me, Offset (0, 0), dz));
214
215       l.translate (dir * gap + p1
216                    - Offset (me->relative_coordinate (commonx, X_AXIS),
217                              me->relative_coordinate (all_common_y, Y_AXIS)));
218
219       return l.smobbed_copy ();
220     }
221   else
222     {
223       Grob *common[] = { me, me };
224       for (int a = X_AXIS; a < NO_AXES; a++)
225         {
226           common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
227           common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
228         }
229
230       // distance from center to start of line      
231       Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
232
233       for (int a = X_AXIS; a < NO_AXES; a++)
234         {
235           Axis ax = (Axis)a;
236           dxy[ax]
237             = + robust_relative_extent (bound[RIGHT], common[X_AXIS], ax).center ()
238             - robust_relative_extent (bound[LEFT], common[X_AXIS], ax).center ();
239
240           my_off[ax] = me->relative_coordinate (common[a], ax);
241           his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
242         }
243
244       ofxy = dxy * (off / dxy.length ()) ;
245       dxy -= 2*ofxy;
246
247       dxy[Y_AXIS] += extra_dy;
248       
249       Stencil line = line_stencil (me, Offset (0, 0), dxy);
250
251       line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
252       line.translate (ofxy - my_off + his_off + Offset (0, -extra_dy/2));
253       return line.smobbed_copy ();
254     }
255 }
256
257 ADD_INTERFACE (Line_spanner,
258                "Generic line drawn between two objects, e.g. for use with glissandi.\n"
259                "The property @code{style} can be @code{line}, "
260                "@code{dashed-line}, @code{trill}, \n"
261                "@code{dotted-line} or @code{zigzag}.\n"
262                "\n",
263
264                "extra-dy "
265                "arrow "
266                "gap "
267                "thickness "
268                );
269