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