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