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