]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-spanner.cc
* scm/define-grob-properties.scm: remove 'type
[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_chain = 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                                                   scm_list_n (style_chain,
157                                                            alist_chain,
158                                                            SCM_UNDEFINED));
159       Molecule m = fm->find_by_name ("scripts-trill-element");
160       do
161         mol.add_at_edge (X_AXIS, RIGHT, m, 0,0);
162       while (m.extent (X_AXIS).length ()
163              && mol.extent (X_AXIS).length ()
164              + m.extent (X_AXIS).length () < dx);
165
166       /*
167         FIXME: should center element on x/y
168        */
169       mol.translate_axis (m.extent (X_AXIS).length () / 2, X_AXIS);
170       mol.translate_axis (-(mol.extent (Y_AXIS)[DOWN]
171                             + mol.extent (Y_AXIS).length ())/2, Y_AXIS); 
172     }
173   return mol;
174 }
175
176 /*
177   Find a common Y parent, which --if found-- should be the
178   fixed-distance alignment.
179  */
180 Grob *
181 line_spanner_common_parent (Grob *me)
182 {
183   Grob * common = find_fixed_alignment_parent (me);
184   if (!common)
185     {
186       common = Staff_symbol_referencer::get_staff_symbol (me);
187       if (common)
188         common = common->get_parent (Y_AXIS);
189       else
190         common = me->get_parent (Y_AXIS);
191     }
192
193   return common;
194 }
195
196 /*
197   Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
198
199  (If not, you risk that this is called from the staff-alignment
200   routine, via molecule_extent. At this point, the staves aren't
201   separated yet, so it doesn't work cross-staff.
202
203 */
204
205
206 MAKE_SCHEME_CALLBACK (Line_spanner, brew_molecule, 1);
207 SCM
208 Line_spanner::brew_molecule (SCM smob) 
209 {
210   Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
211
212   Drul_array<Item*>  bound (me->get_bound (LEFT),
213                             me->get_bound (RIGHT));
214   
215   
216   Real gap = gh_scm2double (me->get_grob_property ("gap"));
217
218   Offset ofxy (gap, 0); /*offset from start point to start of line*/
219   Offset dxy ;
220   Offset my_off;
221   Offset his_off;
222   
223   Real thick = me->get_paper ()->get_var ("linethickness");  
224
225   SCM s = me->get_grob_property ("thickness");
226   if (gh_number_p (s))
227     thick *= gh_scm2double (s);
228
229   if (bound[RIGHT]->break_status_dir())
230     {
231       if (bound[LEFT]->break_status_dir ())
232         {
233           programming_error ("line-spanner with two broken ends. Farewell sweet world.");
234
235           me->suicide();
236           return SCM_EOL;
237         }
238
239       /*
240         This is hairy. For the normal case, we simply find common
241         parents, and draw a line between the bounds. When two note
242         heads are on different lines, there is no common parent
243         anymore. We have to find the piano-staff object.
244       */
245       
246       int k = broken_spanner_index (me);
247       Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
248       Spanner *next_sp  = parent_sp->broken_intos_ [k+1];
249       Item *next_bound = next_sp->get_bound (RIGHT);
250
251       if (next_bound->break_status_dir ())
252         {
253           programming_error ("no note heads for the line spanner on next line?"
254                              " Confused.");
255           me->suicide();
256           return SCM_EOL;
257         }
258             
259       Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
260       commonx = me->common_refpoint (commonx, X_AXIS);
261       
262       Grob *next_common_y = line_spanner_common_parent (next_bound);
263       Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
264
265       Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
266       
267       Interval next_ext  = next_bound->extent (next_common_y, Y_AXIS);
268       Interval this_ext  = bound[LEFT]->extent (this_common_y, Y_AXIS);
269
270       Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
271       
272       Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
273                  this_ext.center ()  + yoff); 
274       Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
275                  next_ext.center () + yoff);
276       
277       Offset dz (p2 -p1);
278       Real len = dz.length ();
279
280       Offset dir  = dz *(1/ len);
281       dz = (dz.length () - 2*gap) *dir;
282       
283   
284       Molecule l (line_molecule (me, thick, dz[X_AXIS],
285                                  dz[Y_AXIS]));
286
287       l.translate (dir * gap +  p1
288                    - Offset (me->relative_coordinate (commonx, X_AXIS),
289                              me->relative_coordinate (all_common_y, Y_AXIS)));
290
291       return l.smobbed_copy (); 
292     }
293   else
294     {
295       Grob *common[] = { me, me };
296       for (int a = X_AXIS;  a < NO_AXES; a++)
297         {
298           common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
299           common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
300         }
301
302       // distance from center to start of line      
303       Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
304
305       for (int a = X_AXIS; a < NO_AXES; a++)
306         {
307           Axis ax = (Axis)a;
308           dxy[ax] =
309             + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
310             - bound[LEFT]->extent (common[X_AXIS], ax).center ();
311
312           my_off[ax] =me->relative_coordinate (common[a], ax);
313           his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
314           
315         }
316
317       ofxy = dxy * (off/dxy.length ());
318       dxy -= 2*ofxy;
319   
320       Molecule line = line_molecule (me, thick, dxy[X_AXIS], dxy[Y_AXIS]);
321       line.translate_axis (bound[LEFT]->extent (bound[LEFT],
322                                                 X_AXIS).length ()/2, X_AXIS); 
323       line.translate (ofxy - my_off + his_off);
324       return line.smobbed_copy ();
325     }
326 }
327
328
329 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
330   "Generic line drawn between two objects, eg. for use with glissandi.\n"
331 "gap is measured in staff-spaces.\n"
332 "The property 'type is one of: line, dashed-line, trill, dotted-line or zigzag.\n"
333 "\n",
334   "gap dash-period dash-length zigzag-width zigzag-length thickness style");
335
336