2 line-spanner.cc -- implement Line_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2004 Jan Nieuwenhuizen <janneke@gnu.org>
14 #include "line-spanner.hh"
15 #include "output-def.hh"
16 #include "paper-column.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "font-interface.hh"
20 #include "align-interface.hh"
22 #include "line-interface.hh"
25 zigzag_stencil (Grob
*me
,
33 Real thick
= Staff_symbol_referencer::line_thickness (me
);
34 thick
*= robust_scm2double (me
->get_property ("thickness"), 1.0); // todo: staff sym referencer?
36 Real staff_space
= Staff_symbol_referencer::staff_space (me
);
38 double w
= robust_scm2double (me
->get_property ("zigzag-width"), 1)*staff_space
;
39 double l
= robust_scm2double ( me
->get_property ("zigzag-length"), 1)* w
;
40 double h
= l
>w
/2 ? sqrt (l
*l
-w
*w
/4) : 0;
42 SCM list
= scm_list_n (ly_symbol2scm ("zigzag-line"),
46 scm_make_real (thick
),
51 b
.add_point (Offset (0,0));
53 b
[X_AXIS
].widen (thick
/2);
54 b
[Y_AXIS
].widen (thick
/2);
56 return Stencil (b
, list
);
59 MAKE_SCHEME_CALLBACK (Line_spanner
, after_line_breaking
, 1);
61 Line_spanner::after_line_breaking (SCM g
)
63 Grob
*me
= unsmob_grob (g
);
64 Spanner
*sp
= dynamic_cast<Spanner
*> (me
);
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
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.
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.
81 if (sp
->get_bound (LEFT
)->break_status_dir ()
82 && !sp
->get_bound (RIGHT
)->break_status_dir ())
85 Can't do suicide, since this mucks up finding the trend.
87 me
->set_property ("print-function", SCM_EOL
);
95 Line_spanner::line_stencil (Grob
*me
,
99 Offset dz
= to
-from
;
100 SCM type
= me
->get_property ("style");
101 if (ly_c_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)))
108 return (type
== ly_symbol2scm ("zigzag"))
109 ? zigzag_stencil (me
, from
, to
)
110 : Line_interface::line (me
, from
, to
);
112 else if (ly_c_symbol_p (type
)
113 && type
== ly_symbol2scm ("trill"))
115 SCM alist_chain
= Font_interface::text_font_alist_chain (me
);
116 SCM style_alist
= scm_list_n (scm_cons (ly_symbol2scm ("font-encoding"),
117 ly_symbol2scm ("fetaMusic")),
120 Font_metric
*fm
= select_font (me
->get_paper (),
121 scm_cons (style_alist
,
123 Stencil m
= fm
->find_by_name ("scripts-trill-element");
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
]);
133 FIXME: should center element on x/y
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
);
139 mol
.translate (from
);
146 Find a common Y parent, which --if found-- should be the
147 fixed-distance alignment.
150 line_spanner_common_parent (Grob
*me
)
152 Grob
* common
= find_fixed_alignment_parent (me
);
155 common
= Staff_symbol_referencer::get_staff_symbol (me
);
157 common
= common
->get_parent (Y_AXIS
);
159 common
= me
->get_parent (Y_AXIS
);
166 Warning: this thing is a cross-staff object, so it should have empty Y-dimensions.
168 (If not, you risk that this is called from the staff-alignment
169 routine, via stencil_extent. At this point, the staves aren't
170 separated yet, so it doesn't work cross-staff.
175 MAKE_SCHEME_CALLBACK (Line_spanner
, print
, 1);
177 Line_spanner::print (SCM smob
)
179 Spanner
*me
= dynamic_cast<Spanner
*> (unsmob_grob (smob
));
181 Drul_array
<Item
*> bound (me
->get_bound (LEFT
),
182 me
->get_bound (RIGHT
));
185 Real gap
= robust_scm2double (me
->get_property ("gap"), 0.0);
187 Offset
ofxy (gap
, 0); /*offset from start point to start of line*/
193 if (bound
[RIGHT
]->break_status_dir ())
195 if (bound
[LEFT
]->break_status_dir ())
197 programming_error ("line-spanner with two broken ends. Farewell sweet world.");
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.
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
);
215 if (next_bound
->break_status_dir ())
217 programming_error ("no note heads for the line spanner on next line?"
223 Grob
*commonx
= bound
[LEFT
]->common_refpoint (bound
[RIGHT
], X_AXIS
);
224 commonx
= me
->common_refpoint (commonx
, X_AXIS
);
226 Grob
*next_common_y
= line_spanner_common_parent (next_bound
);
227 Grob
*this_common_y
= line_spanner_common_parent (bound
[LEFT
]);
229 Grob
*all_common_y
= me
->common_refpoint (this_common_y
, Y_AXIS
);
231 Interval next_ext
= next_bound
->extent (next_common_y
, Y_AXIS
);
232 Interval this_ext
= bound
[LEFT
]->extent (this_common_y
, Y_AXIS
);
234 Real yoff
= this_common_y
->relative_coordinate (all_common_y
, Y_AXIS
);
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
);
242 Real len
= dz
.length ();
244 Offset dir
= dz
*(1/ len
);
245 dz
= (dz
.length () - 2*gap
) *dir
;
248 Stencil
l (line_stencil (me
, Offset (0, 0), dz
));
250 l
.translate (dir
* gap
+ p1
251 - Offset (me
->relative_coordinate (commonx
, X_AXIS
),
252 me
->relative_coordinate (all_common_y
, Y_AXIS
)));
254 return l
.smobbed_copy ();
258 Grob
*common
[] = { me
, me
};
259 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
261 common
[a
] = me
->common_refpoint (bound
[RIGHT
], Axis (a
));
262 common
[a
] = common
[a
]->common_refpoint (bound
[LEFT
], Axis (a
));
265 // distance from center to start of line
266 Real off
= gap
+ ((bound
[LEFT
]->extent (bound
[LEFT
], X_AXIS
).length ()*3)/4);
268 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
272 + bound
[RIGHT
]->extent (common
[X_AXIS
], ax
).center ()
273 - bound
[LEFT
]->extent (common
[X_AXIS
], ax
).center ();
275 my_off
[ax
] =me
->relative_coordinate (common
[a
], ax
);
276 his_off
[ax
] = bound
[LEFT
]->relative_coordinate (common
[a
], ax
);
280 ofxy
= dxy
* (off
/dxy
.length ());
283 Stencil line
= line_stencil (me
, Offset (0,0),dxy
);
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 ();
292 ADD_INTERFACE (Line_spanner
, "line-spanner-interface",
293 "Generic line drawn between two objects, e.g. for use with glissandi.\n"
294 "The property @code{style} can be @code{line}, "
295 "@code{dashed-line}, @code{trill}, \n"
296 "@code{dotted-line} or @code{zigzag}.\n"
299 "gap zigzag-width zigzag-length thickness");