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