]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-spanner.cc
Merge with master
[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--2007 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "line-spanner.hh"
10
11
12 #include "spanner.hh"
13 #include "output-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 #include "lookup.hh"
20 #include "line-interface.hh"
21
22 MAKE_SCHEME_CALLBACK (Line_spanner, after_line_breaking, 1);
23 SCM
24 Line_spanner::after_line_breaking (SCM g)
25 {
26   Grob *me = unsmob_grob (g);
27   Spanner *sp = dynamic_cast<Spanner *> (me);
28
29   /*
30     We remove the line at the start of the line.  For piano voice
31     indicators, it makes no sense to have them at the start of the
32     line.
33
34     I'm not sure what the official rules for glissandi are, but
35     usually the 2nd note of the glissando is "exact", so when playing
36     from the start of the line, there is no need to glide.
37
38     From a typographical p.o.v. this makes sense, since the amount of
39     space left of a note at the start of a line is very small.
40
41     --hwn.
42
43   */
44   if (sp->get_bound (LEFT)->break_status_dir ()
45       && !sp->get_bound (RIGHT)->break_status_dir ())
46     {
47       /*
48         Can't do suicide, since this mucks up finding the trend.
49       */
50       me->set_property ("transparent", SCM_BOOL_T);
51     }
52   return SCM_EOL;
53 }
54
55 Stencil
56 Line_spanner::line_stencil (Grob *me,
57                             Offset from,
58                             Offset to)
59 {
60   Stencil line = Line_interface::line (me, from, to);
61
62   if (to_boolean (me->get_property ("arrow")))
63     line.add_stencil (Line_interface::arrows (me, from, to, false, true));
64
65   return line;
66 }
67
68 /*
69   Find a common Y parent, which --if found-- should be the
70   fixed-distance alignment.
71 */
72 Grob *
73 line_spanner_common_parent (Grob *me)
74 {
75   Grob *common = find_fixed_alignment_parent (me);
76   if (!common)
77     {
78       common = Staff_symbol_referencer::get_staff_symbol (me);
79       if (common)
80         common = common->get_parent (Y_AXIS);
81       else
82         common = me->get_parent (Y_AXIS);
83     }
84
85   return common;
86 }
87
88 /*
89   Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
90
91   (If not, you risk that this is called from the staff-alignment
92   routine, via stencil_extent. At this point, the staves aren't
93   separated yet, so it doesn't work cross-staff.
94
95   (huh? crossable staves have fixed distance? --hwn)
96 */
97
98 MAKE_SCHEME_CALLBACK (Line_spanner, print, 1);
99 SCM
100 Line_spanner::print (SCM smob)
101 {
102   Spanner *me = dynamic_cast<Spanner *> (unsmob_grob (smob));
103
104   Drul_array<Item *> bound (me->get_bound (LEFT),
105                             me->get_bound (RIGHT));
106
107   Real gap = robust_scm2double (me->get_property ("gap"), 0.0);
108
109   Offset ofxy (gap, 0); /* offset from start point to start of line */
110   Offset dxy;
111   Offset my_off;
112   Offset his_off;
113
114   Real extra_dy = robust_scm2double (me->get_property ("extra-dy"),
115                                      0.0);
116   
117   if (bound[RIGHT]->break_status_dir ())
118     {
119       if (bound[LEFT]->break_status_dir ())
120         {
121           programming_error ("line-spanner with two broken ends. Farewell sweet world.");
122
123           me->suicide ();
124           return SCM_EOL;
125         }
126
127       /*
128         This is hairy. For the normal case, we simply find common
129         parents, and draw a line between the bounds. When two note
130         heads are on different systems, there is no common parent
131         anymore. We have to find the piano-staff object.
132       */
133
134       Spanner *next_sp = me->broken_neighbor (RIGHT);
135       Item *next_bound = next_sp->get_bound (RIGHT);
136
137       if (next_bound->break_status_dir ())
138         {
139           programming_error ("no note heads for the line spanner on next line?"
140                              " Confused.");
141           me->suicide ();
142           return SCM_EOL;
143         }
144
145       Grob *commonx = bound[LEFT]->common_refpoint (bound[RIGHT], X_AXIS);
146       commonx = me->common_refpoint (commonx, X_AXIS);
147
148       Grob *next_common_y = line_spanner_common_parent (next_bound);
149       Grob *this_common_y = line_spanner_common_parent (bound[LEFT]);
150
151       Grob *all_common_y = me->common_refpoint (this_common_y, Y_AXIS);
152
153       Interval next_ext = next_bound->extent (next_common_y, Y_AXIS);
154       Interval this_ext = bound[LEFT]->extent (this_common_y, Y_AXIS);
155
156       Real yoff = this_common_y->relative_coordinate (all_common_y, Y_AXIS);
157
158       Offset p1 (bound[LEFT]->extent (commonx, X_AXIS)[RIGHT],
159                  this_ext.center () + yoff - extra_dy / 2);
160       Offset p2 (bound[RIGHT]->extent (commonx, X_AXIS)[LEFT],
161                  next_ext.center () + yoff + extra_dy / 2);
162
163       Offset dz (p2 - p1);
164       Real len = dz.length ();
165
166       Offset dir = dz * (1 / len);
167       dz = (dz.length () - 2 * gap) * dir;
168
169       Stencil l (line_stencil (me, Offset (0, 0), dz));
170
171       l.translate (dir * gap + p1
172                    - Offset (me->relative_coordinate (commonx, X_AXIS),
173                              me->relative_coordinate (all_common_y, Y_AXIS)));
174
175       return l.smobbed_copy ();
176     }
177   else
178     {
179       Grob *common[] = { me, me };
180       for (int a = X_AXIS; a < NO_AXES; a++)
181         {
182           common[a] = me->common_refpoint (bound[RIGHT], Axis (a));
183           common[a] = common[a]->common_refpoint (bound[LEFT], Axis (a));
184         }
185
186       // distance from center to start of line      
187       Real off = gap + ((bound[LEFT]->extent (bound[LEFT], X_AXIS).length () * 3) / 4);
188
189       for (int a = X_AXIS; a < NO_AXES; a++)
190         {
191           Axis ax = (Axis)a;
192           dxy[ax]
193             = + robust_relative_extent (bound[RIGHT], common[X_AXIS], ax).center ()
194             - robust_relative_extent (bound[LEFT], common[X_AXIS], ax).center ();
195
196           my_off[ax] = me->relative_coordinate (common[a], ax);
197           his_off[ax] = bound[LEFT]->relative_coordinate (common[a], ax);
198         }
199
200       ofxy = dxy * (off / dxy.length ()) ;
201       dxy -= 2*ofxy;
202
203       dxy[Y_AXIS] += extra_dy;
204       
205       Stencil line = line_stencil (me, Offset (0, 0), dxy);
206
207       line.translate_axis (bound[LEFT]->extent (bound[LEFT], X_AXIS).length () / 2, X_AXIS);
208       line.translate (ofxy - my_off + his_off + Offset (0, -extra_dy/2));
209       return line.smobbed_copy ();
210     }
211 }
212
213 ADD_INTERFACE (Line_spanner,
214                "Generic line drawn between two objects, e.g. for use with glissandi.\n"
215                "The property @code{style} can be @code{line}, "
216                "@code{dashed-line}, @code{trill}, \n"
217                "@code{dotted-line} or @code{zigzag}.\n"
218                "\n",
219
220                "extra-dy "
221                "arrow "
222                "gap "
223                "thickness "
224                );
225