]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-spanner.cc
e7e59d525948f9fc7186aa8f3eff8dbceded1dcc
[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 "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 Molecule
25 zigzag_molecule (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_grob_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_grob_property ("zigzag-width"), 1)*staff_space;
39   double l = robust_scm2double ( me->get_grob_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                       gh_bool2scm (true),
44                       gh_double2scm (w),
45                       gh_double2scm (h),
46                       gh_double2scm (thick),
47                       gh_double2scm (dx),
48                       gh_double2scm (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 Molecule (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_grob_property ("print-function", SCM_EOL);
88       
89     }
90   return SCM_EOL;
91 }
92
93
94 Molecule
95 Line_spanner::line_molecule (Grob *me, 
96                              Offset from,
97                              Offset to)
98 {
99   Offset dz = to -from ; 
100   SCM type = me->get_grob_property ("style");
101   if (gh_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_molecule (me, from, to)
110         : Line_interface::line (me, from, to);
111     }
112   else if (gh_symbol_p (type)
113            && type == ly_symbol2scm ("trill"))
114     {
115       SCM alist_chain = Font_interface::font_alist_chain (me);
116       SCM style_alist = scm_list_n (gh_cons (ly_symbol2scm ("font-family"),
117                                              ly_symbol2scm ("music")),
118                                     SCM_UNDEFINED);
119       
120       Font_metric *fm = select_font (me->get_paper (),
121                                                   gh_cons (style_alist,
122                                                            alist_chain));
123       Molecule m = fm->find_by_name ("scripts-trill-element");
124       Molecule 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 Molecule();
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 molecule_extent. At this point, the staves aren't
170   separated yet, so it doesn't work cross-staff.
171
172 */
173
174
175 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
176 SCM
177 Line_spanner::print (SCM smob) 
178 {
179   Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
180
181   Drul_array<Item*>  bound (me->get_bound (LEFT),
182                             me->get_bound (RIGHT));
183   
184   
185   Real gap = robust_scm2double (me->get_grob_property ("gap"), 0.0);
186
187   Offset ofxy (gap, 0); /*offset from start point to start of line*/
188   Offset dxy ;
189   Offset my_off;
190   Offset his_off;
191   
192
193   if (bound[RIGHT]->break_status_dir())
194     {
195       if (bound[LEFT]->break_status_dir ())
196         {
197           programming_error ("line-spanner with two broken ends. Farewell sweet world.");
198
199           me->suicide();
200           return SCM_EOL;
201         }
202
203       /*
204         This is hairy. For the normal case, we simply find common
205         parents, and draw a line between the bounds. When two note
206         heads are on different lines, there is no common parent
207         anymore. We have to find the piano-staff object.
208       */
209       
210       int k = broken_spanner_index (me);
211       Spanner *parent_sp = dynamic_cast<Spanner*> (me->original_);
212       Spanner *next_sp  = parent_sp->broken_intos_ [k+1];
213       Item *next_bound = next_sp->get_bound (RIGHT);
214
215       if (next_bound->break_status_dir ())
216         {
217           programming_error ("no note heads for the line spanner on next line?"
218                              " Confused.");
219           me->suicide();
220           return SCM_EOL;
221         }
222             
223       Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
224       commonx = me->common_refpoint (commonx, X_AXIS);
225       
226       Grob *next_common_y = line_spanner_common_parent (next_bound);
227       Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
228
229       Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
230       
231       Interval next_ext  = next_bound->extent (next_common_y, Y_AXIS);
232       Interval this_ext  = bound[LEFT]->extent (this_common_y, Y_AXIS);
233
234       Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
235       
236       Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
237                  this_ext.center ()  + yoff); 
238       Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
239                  next_ext.center () + yoff);
240       
241       Offset dz (p2 -p1);
242       Real len = dz.length ();
243
244       Offset dir  = dz *(1/ len);
245       dz = (dz.length () - 2*gap) *dir;
246       
247   
248       Molecule l (line_molecule (me, Offset(0, 0), dz));
249
250       l.translate (dir * gap +  p1
251                    - Offset (me->relative_coordinate (commonx, X_AXIS),
252                              me->relative_coordinate (all_common_y, Y_AXIS)));
253
254       return l.smobbed_copy (); 
255     }
256   else
257     {
258       Grob *common[] = { me, me };
259       for (int a = X_AXIS;  a < NO_AXES; a++)
260         {
261           common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
262           common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
263         }
264
265       // distance from center to start of line      
266       Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()*3)/4);
267
268       for (int a = X_AXIS; a < NO_AXES; a++)
269         {
270           Axis ax = (Axis)a;
271           dxy[ax] =
272             + bound[RIGHT]->extent (common[X_AXIS], ax).center ()
273             - bound[LEFT]->extent (common[X_AXIS], ax).center ();
274
275           my_off[ax] =me->relative_coordinate (common[a], ax);
276           his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
277           
278         }
279
280       ofxy = dxy * (off/dxy.length ());
281       dxy -= 2*ofxy;
282   
283       Molecule line = line_molecule (me, Offset (0,0),dxy);
284
285       line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length ()/2, X_AXIS); 
286       line.translate (ofxy - my_off + his_off);
287       return line.smobbed_copy ();
288     }
289 }
290
291
292 ADD_INTERFACE (Line_spanner, "line-spanner-interface",
293   "Generic line drawn between two objects, eg. for use with glissandi.\n"
294 "gap is measured in staff-spaces.\n"
295 "The property 'type is one of: line, dashed-line, trill, dotted-line or zigzag.\n"
296 "\n",
297   "gap zigzag-width zigzag-length thickness");
298
299