]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-interface.cc
Issue 4997/1: Add Preinit class for early initialization
[lilypond.git] / lily / line-interface.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2004--2015 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   Offset dir = (end - begin).direction ();
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] * dir + 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.angle_degrees (), Offset (LEFT, CENTER));
74   line.translate (from);
75
76   return line;
77 }
78
79 Stencil
80 Line_interface::make_zigzag_line (Grob *me,
81                                   Offset from,
82                                   Offset to)
83 {
84   Offset dz = to - from;
85
86   Real thick = Staff_symbol_referencer::line_thickness (me);
87   thick *= robust_scm2double (me->get_property ("thickness"), 1.0); // todo: staff sym referencer?
88
89   Real staff_space = Staff_symbol_referencer::staff_space (me);
90
91   Real w = robust_scm2double (me->get_property ("zigzag-width"), 1) * staff_space;
92   int count = (int) ceil (dz.length () / w);
93   w = dz.length () / count;
94
95   Real l = robust_scm2double (me->get_property ("zigzag-length"), 1) * w;
96   Real h = l > w / 2 ? sqrt (l * l - w * w / 4) : 0;
97
98   Offset rotation_factor = dz.direction ();
99
100   Offset points[3];
101   points[0] = Offset (0, -h / 2);
102   points[1] = Offset (w / 2, h / 2);
103   points[2] = Offset (w, -h / 2);
104   for (int i = 0; i < 3; i++)
105     points[i] = complex_multiply (points[i], rotation_factor);
106
107   Stencil squiggle (Line_interface::make_line (thick, points[0], points[1]));
108   squiggle.add_stencil (Line_interface::make_line (thick, points[1], points[2]));
109
110   Stencil total;
111   for (int i = 0; i < count; i++)
112     {
113       Stencil moved_squiggle (squiggle);
114       moved_squiggle.translate (from + Offset (i * w, 0) * rotation_factor);
115       total.add_stencil (moved_squiggle);
116     }
117
118   return total;
119 }
120
121 Stencil
122 Line_interface::make_dashed_line (Real thick, Offset from, Offset to,
123                                   Real dash_period, Real dash_fraction)
124 {
125   dash_fraction = min (max (dash_fraction, 0.0), 1.0);
126   Real on = dash_fraction * dash_period; 
127   Real off = max (0.0, dash_period - on - thick);
128
129   SCM at = scm_list_n (ly_symbol2scm ("dashed-line"),
130                        scm_from_double (thick),
131                        scm_from_double (on),
132                        scm_from_double (off),
133                        scm_from_double (to[X_AXIS] - from[X_AXIS]),
134                        scm_from_double (to[Y_AXIS] - from[Y_AXIS]),
135                        scm_from_double (0.0),
136                        SCM_UNDEFINED);
137
138   Box box;
139   box.add_point (Offset (0, 0));
140   box.add_point (to - from);
141
142   box[X_AXIS].widen (thick / 2);
143   box[Y_AXIS].widen (thick / 2);
144
145   Stencil m = Stencil (box, at);
146   m.translate (from);
147   return m;
148 }
149
150 Stencil
151 Line_interface::make_line (Real th, Offset from, Offset to)
152 {
153   SCM at = scm_list_n (ly_symbol2scm ("draw-line"),
154                        scm_from_double (th),
155                        scm_from_double (from[X_AXIS]),
156                        scm_from_double (from[Y_AXIS]),
157                        scm_from_double (to[X_AXIS]),
158                        scm_from_double (to[Y_AXIS]),
159                        SCM_UNDEFINED);
160
161   Box box;
162   box.add_point (from);
163   box.add_point (to);
164
165   box[X_AXIS].widen (th / 2);
166   box[Y_AXIS].widen (th / 2);
167
168   return Stencil (box, at);
169 }
170
171 Stencil
172 Line_interface::arrows (Grob *me, Offset from, Offset to,
173                         bool from_arrow,
174                         bool to_arrow)
175 {
176   Stencil a;
177   if (from_arrow || to_arrow)
178     {
179       Real thick = Staff_symbol_referencer::line_thickness (me)
180                    * robust_scm2double (me->get_property ("thickness"), 1);
181       Real ss = Staff_symbol_referencer::staff_space (me);
182
183       Real len = robust_scm2double (me->get_property ("arrow-length"), 1.3 * ss);
184       Real wid = robust_scm2double (me->get_property ("arrow-width"), 0.5 * ss);
185
186       if (to_arrow)
187         a.add_stencil (make_arrow (from, to, thick, len, wid));
188
189       if (from_arrow)
190         a.add_stencil (make_arrow (to, from, thick, len, wid));
191     }
192
193   return a;
194 }
195
196 Stencil
197 Line_interface::line (Grob *me, Offset from, Offset to)
198 {
199   Real thick = Staff_symbol_referencer::line_thickness (me)
200                * robust_scm2double (me->get_property ("thickness"), 1);
201
202   SCM type = me->get_property ("style");
203   if (scm_is_eq (type, ly_symbol2scm ("zigzag")))
204     return make_zigzag_line (me, from, to);
205   else if (scm_is_eq (type, ly_symbol2scm ("trill")))
206     return make_trill_line (me, from, to);
207   else if (scm_is_eq (type, ly_symbol2scm ("none")))
208     return Stencil ();
209
210   Stencil stencil;
211
212   if (scm_is_eq (type, ly_symbol2scm ("dashed-line"))
213       || scm_is_eq (type, ly_symbol2scm ("dotted-line")))
214     {
215
216       Real fraction
217         = scm_is_eq (type, ly_symbol2scm ("dotted-line"))
218           ? 0.0
219           : robust_scm2double (me->get_property ("dash-fraction"), 0.4);
220
221       fraction = min (max (fraction, 0.0), 1.0);
222       Real period = Staff_symbol_referencer::staff_space (me)
223                     * robust_scm2double (me->get_property ("dash-period"), 1.0);
224
225       if (period <= 0)
226         return Stencil ();
227
228       Real len = (to - from).length ();
229
230       int n = (int) rint ((len - period * fraction) / period);
231       n = max (0, n);
232       if (n > 0)
233         {
234           /*
235             TODO: figure out something intelligent for really short
236             sections.
237            */
238           period = ((to - from).length () - period * fraction) / n;
239         }
240       stencil = make_dashed_line (thick, from, to, period, fraction);
241     }
242   else
243     stencil = make_line (thick, from, to);
244
245   return stencil;
246 }
247
248 ADD_INTERFACE (Line_interface,
249                "Generic line objects.  Any object using lines supports this."
250                "  The property @code{style} can be @code{line},"
251                " @code{dashed-line}, @code{trill}, @code{dotted-line},"
252                " @code{zigzag} or @code{none} (a transparent line).\n"
253                "\n"
254                "For @code{dashed-line}, the length of the dashes is tuned"
255                " with @code{dash-fraction}.  If the latter is set to@tie{}0, a"
256                " dotted line is produced.",
257
258                /* properties */
259                "arrow-length "
260                "arrow-width "
261                "dash-fraction "
262                "dash-period "
263                "style "
264                "thickness "
265                "zigzag-length "
266                "zigzag-width "
267               );