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