]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-interface.cc
Merge branch 'master' into lilypond/translation
[lilypond.git] / lily / line-interface.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2004--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "line-interface.hh"
21
22 #include "font-interface.hh"
23 #include "grob.hh"
24 #include "lookup.hh"
25 #include "output-def.hh"
26 #include "staff-symbol-referencer.hh"
27
28 Stencil
29 Line_interface::make_arrow (Offset begin, Offset end,
30                             Real thick,
31                             Real length, Real width)
32 {
33   Real angle = (end - begin).arg ();
34   vector<Offset> points;
35
36   points.push_back (Offset (0, 0));
37   points.push_back (Offset (-length, width));
38   points.push_back (Offset (-length, -width));
39
40   for (vsize i = 0; i < points.size (); i++)
41     points[i] = points[i] * complex_exp (Offset (0, angle)) + end;
42
43   return Lookup::round_filled_polygon (points, thick);
44 }
45
46 Stencil
47 Line_interface::make_trill_line (Grob *me,
48                                  Offset from,
49                                  Offset to)
50 {
51   Offset dz = (to - from);
52
53   Font_metric *fm = Font_interface::get_default_font (me);
54
55   Stencil elt = fm->find_by_name ("scripts.trill_element");
56   elt.align_to (Y_AXIS, CENTER);
57   Real elt_len = elt.extent (X_AXIS).length ();
58   if (elt_len <= 0.0)
59     {
60       programming_error ("can't find scripts.trill_element");
61       return Stencil ();
62     }
63       
64   Stencil line;
65   Real len = 0.0;
66   do
67     {
68       line.add_at_edge (X_AXIS, RIGHT, elt, 0);
69       len = line.extent (X_AXIS).length ();
70     }
71   while (len + elt_len < dz.length ());
72
73   line.rotate (dz.arg (), Offset (LEFT, CENTER));
74   line.translate (from);
75
76   return line; 
77 }
78
79
80 Stencil
81 Line_interface::make_zigzag_line (Grob *me,
82                                   Offset from,
83                                   Offset to)
84 {
85   Offset dz = to - from;
86
87   Real thick = Staff_symbol_referencer::line_thickness (me);
88   thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer? 
89
90   Real staff_space = Staff_symbol_referencer::staff_space (me);
91
92   Real w = robust_scm2double (me->get_property ("zigzag-width"), 1) * staff_space;
93   int count = (int) ceil (dz.length () / w);
94   w = dz.length () / count;
95
96   Real l = robust_scm2double (me->get_property ("zigzag-length"), 1) * w;
97   Real h = l > w / 2 ? sqrt (l * l - w * w / 4) : 0;
98
99   Offset rotation_factor = complex_exp (Offset (0, dz.arg ()));
100
101   Offset points[3];
102   points[0] = Offset (0, -h / 2);
103   points[1] = Offset (w / 2, h / 2);
104   points[2] = Offset (w, -h / 2);
105   for (int i = 0; i < 3; i++)
106     points[i] = complex_multiply (points[i], rotation_factor);
107
108   Stencil squiggle (Line_interface::make_line (thick, points[0], points[1]));
109   squiggle.add_stencil (Line_interface::make_line (thick, points[1], points[2]));
110
111   Stencil total;
112   for (int i = 0; i < count; i++)
113     {
114       Stencil moved_squiggle (squiggle);
115       moved_squiggle.translate (from + Offset (i * w, 0) * rotation_factor);
116       total.add_stencil (moved_squiggle);
117     }
118
119   return total;
120 }
121
122
123 Stencil
124 Line_interface::make_dashed_line (Real thick, Offset from, Offset to,
125                                   Real dash_period, Real dash_fraction)
126 {
127   dash_fraction = min (max (dash_fraction, 0.0), 1.0);
128   Real on = dash_fraction * dash_period + thick;
129   Real off = max (0.0, dash_period - on);
130
131   SCM at = scm_list_n (ly_symbol2scm ("dashed-line"),
132                        scm_from_double (thick),
133                        scm_from_double (on),
134                        scm_from_double (off),
135                        scm_from_double (to[X_AXIS] - from[X_AXIS]),
136                        scm_from_double (to[Y_AXIS] - from[Y_AXIS]),
137                        scm_from_double (0.0),
138                        SCM_UNDEFINED);
139
140   Box box;
141   box.add_point (Offset (0, 0));
142   box.add_point (to - from);
143
144   box[X_AXIS].widen (thick / 2);
145   box[Y_AXIS].widen (thick / 2);
146
147   Stencil m = Stencil (box, at);
148   m.translate (from);
149   return m;
150 }
151
152 Stencil
153 Line_interface::make_line (Real th, Offset from, Offset to)
154 {
155   SCM at = scm_list_n (ly_symbol2scm ("draw-line"),
156                        scm_from_double (th),
157                        scm_from_double (from[X_AXIS]),
158                        scm_from_double (from[Y_AXIS]),
159                        scm_from_double (to[X_AXIS]),
160                        scm_from_double (to[Y_AXIS]),
161                        SCM_UNDEFINED);
162
163   Box box;
164   box.add_point (from);
165   box.add_point (to);
166
167   box[X_AXIS].widen (th / 2);
168   box[Y_AXIS].widen (th / 2);
169
170   return Stencil (box, at);
171 }
172
173 Stencil
174 Line_interface::arrows (Grob *me, Offset from, Offset to,
175                         bool from_arrow,
176                         bool to_arrow)
177 {
178   Stencil a;
179   if (from_arrow || to_arrow)
180     {
181       Real thick = Staff_symbol_referencer::line_thickness (me)
182         * robust_scm2double (me->get_property ("thickness"), 1);
183       Real ss = Staff_symbol_referencer::staff_space (me);
184
185       Real len = robust_scm2double (me->get_property ("arrow-length"), 1.3 * ss);
186       Real wid = robust_scm2double (me->get_property ("arrow-width"), 0.5 * ss);
187
188       if (to_arrow)
189         a.add_stencil (make_arrow (from, to, thick, len, wid));
190
191       if (from_arrow)
192         a.add_stencil (make_arrow (to, from, thick, len, wid));
193     }
194
195   return a;
196 }
197
198 Stencil
199 Line_interface::line (Grob *me, Offset from, Offset to)
200 {
201   Real thick = Staff_symbol_referencer::line_thickness (me)
202     * robust_scm2double (me->get_property ("thickness"), 1);
203
204   SCM type = me->get_property ("style");
205   if (type == ly_symbol2scm ("zigzag"))
206     return make_zigzag_line (me, from, to);
207   else if (type == ly_symbol2scm ("trill"))
208     return make_trill_line (me, from, to);
209   else if (type == ly_symbol2scm ("none"))
210     return Stencil ();
211   
212   Stencil stencil;
213
214   if (type == ly_symbol2scm ("dashed-line") || type == ly_symbol2scm ("dotted-line"))
215     {
216
217       Real fraction
218         = type == ly_symbol2scm ("dotted-line")
219         ? 0.0
220         : robust_scm2double (me->get_property ("dash-fraction"), 0.4);
221
222       fraction = min (max (fraction, 0.0), 1.0);
223       Real period = Staff_symbol_referencer::staff_space (me)
224         * robust_scm2double (me->get_property ("dash-period"), 1.0);
225
226       if (period <= 0)
227         return Stencil ();
228
229       Real len = (to - from).length ();
230       
231       int n = (int) rint ((len - period * fraction) / period);
232       n = max (0, n);
233       if (n > 0)
234         {
235           /*
236             TODO: figure out something intelligent for really short
237             sections.
238            */
239           period = ((to - from).length () - period * fraction) / n;
240         }
241       stencil = make_dashed_line (thick, from, to, period, fraction);
242     }
243   else
244     stencil = make_line (thick, from, to);
245
246   return stencil;
247 }
248
249 ADD_INTERFACE (Line_interface,
250                "Generic line objects.  Any object using lines supports this."
251                "  The property @code{style} can be @code{line},"
252                " @code{dashed-line}, @code{trill}, @code{dotted-line},"
253                " @code{zigzag} or @code{none} (a transparent line).\n"
254                "\n"
255                "For @code{dashed-line}, the length of the dashes is tuned"
256                " with @code{dash-fraction}.  If the latter is set to@tie{}0, a"
257                " dotted line is produced.",
258
259                /* properties */
260                "arrow-length "
261                "arrow-width "
262                "dash-fraction "
263                "dash-period "
264                "style "
265                "thickness "
266                "zigzag-length "
267                "zigzag-width "
268                );