]> git.donarmstrong.com Git - lilypond.git/blob - lily/stencil-scheme.cc
Issue 4550 (1/2) Avoid "using namespace std;" in included files
[lilypond.git] / lily / stencil-scheme.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--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 "font-metric.hh"
21 #include "libc-extension.hh"
22 #include "lookup.hh"
23 #include "stencil.hh"
24
25 using std::vector;
26
27 /*
28   TODO: naming add/combine.
29 */
30
31 LY_DEFINE (ly_stencil_translate_axis, "ly:stencil-translate-axis",
32            3, 0, 0, (SCM stil, SCM amount, SCM axis),
33            "Return a copy of @var{stil} but translated by @var{amount}"
34            " in @var{axis} direction.")
35 {
36   Stencil *s = unsmob<Stencil> (stil);
37   LY_ASSERT_SMOB (Stencil, stil, 1);
38   LY_ASSERT_TYPE (scm_is_number, amount, 2);
39
40   LY_ASSERT_TYPE (is_axis, axis, 3);
41
42   Real real_amount = scm_to_double (amount);
43
44   SCM new_s = s->smobbed_copy ();
45   scm_remember_upto_here_1 (stil);
46
47   Stencil *q = unsmob<Stencil> (new_s);
48   q->translate_axis (real_amount, Axis (scm_to_int (axis)));
49   return new_s;
50 }
51
52 LY_DEFINE (ly_stencil_translate, "ly:stencil-translate",
53            2, 0, 0, (SCM stil, SCM offset),
54            "Return a @var{stil}, but translated by @var{offset}"
55            " (a pair of numbers).")
56 {
57   Stencil *s = unsmob<Stencil> (stil);
58   LY_ASSERT_SMOB (Stencil, stil, 1);
59   LY_ASSERT_TYPE (is_number_pair, offset, 2);
60   Offset o = ly_scm2offset (offset);
61
62   SCM new_s = s->smobbed_copy ();
63   scm_remember_upto_here_1 (stil);
64
65   Stencil *q = unsmob<Stencil> (new_s);
66   q->translate (o);
67   return new_s;
68 }
69
70 LY_DEFINE (ly_stencil_expr, "ly:stencil-expr",
71            1, 0, 0, (SCM stil),
72            "Return the expression of @var{stil}.")
73 {
74   Stencil *s = unsmob<Stencil> (stil);
75   LY_ASSERT_SMOB (Stencil, stil, 1);
76   return s->expr ();
77 }
78
79 LY_DEFINE (ly_stencil_extent, "ly:stencil-extent",
80            2, 0, 0, (SCM stil, SCM axis),
81            "Return a pair of numbers signifying the extent of @var{stil} in"
82            " @var{axis} direction (@code{0} or @code{1} for x and"
83            " y@tie{}axis, respectively).")
84 {
85   Stencil *s = unsmob<Stencil> (stil);
86   LY_ASSERT_SMOB (Stencil, stil, 1);
87   LY_ASSERT_TYPE (is_axis, axis, 2);
88
89   return ly_interval2scm (s->extent (Axis (scm_to_int (axis))));
90 }
91
92 LY_DEFINE (ly_stencil_empty_p, "ly:stencil-empty?",
93            1, 1, 0, (SCM stil, SCM axis),
94            "Return whether @var{stil} is empty.  If an optional"
95            " @var{axis} is supplied, the emptiness check is"
96            " restricted to that axis.")
97 {
98   Stencil *s = unsmob<Stencil> (stil);
99   LY_ASSERT_SMOB (Stencil, stil, 1);
100   if (SCM_UNBNDP (axis))
101     return scm_from_bool (s->is_empty ());
102   LY_ASSERT_TYPE (is_axis, axis, 2);
103   return scm_from_bool (s->is_empty (Axis (scm_to_int (axis))));
104 }
105
106 LY_DEFINE (ly_stencil_combine_at_edge, "ly:stencil-combine-at-edge",
107            4, 1, 0, (SCM first, SCM axis, SCM direction,
108                      SCM second,
109                      SCM padding),
110            "Construct a stencil by putting @var{second} next to @var{first}."
111            "  @var{axis} can be 0 (x-axis) or@tie{}1 (y-axis)."
112            "  @var{direction} can be -1 (left or down) or@tie{}1 (right or"
113            " up).  The stencils are juxtaposed with @var{padding} as extra"
114            " space.  @var{first} and @var{second} may also be @code{'()} or"
115            " @code{#f}.")
116 {
117   Stencil *s1 = unsmob<Stencil> (first);
118   Stencil *s2 = unsmob<Stencil> (second);
119   Stencil result;
120
121   SCM_ASSERT_TYPE (s1 || scm_is_false (first) || scm_is_null (first),
122                    first, SCM_ARG1, __FUNCTION__, "Stencil, #f or ()");
123   SCM_ASSERT_TYPE (s2 || scm_is_false (second) || scm_is_null (second),
124                    second, SCM_ARG4, __FUNCTION__, "Stencil, #f or ()");
125   LY_ASSERT_TYPE (is_axis, axis, 2);
126   LY_ASSERT_TYPE (is_direction, direction, 3);
127
128   Real p = 0.0;
129   if (!SCM_UNBNDP (padding))
130     {
131       LY_ASSERT_TYPE (scm_is_number, padding, 5);
132       p = scm_to_double (padding);
133     }
134
135   if (s1)
136     result = *s1;
137
138   if (s2)
139     result.add_at_edge (Axis (scm_to_int (axis)),
140                         Direction (scm_to_int (direction)), *s2, p);
141
142   scm_remember_upto_here_2 (first, second);
143
144   return result.smobbed_copy ();
145 }
146
147 LY_DEFINE (ly_stencil_stack, "ly:stencil-stack",
148            4, 2, 0, (SCM first, SCM axis, SCM direction,
149                      SCM second,
150                      SCM padding,
151                      SCM mindist),
152            "Construct a stencil by stacking @var{second} next to @var{first}."
153            "  @var{axis} can be 0 (x-axis) or@tie{}1 (y-axis)."
154            "  @var{direction} can be -1 (left or down) or@tie{}1 (right or"
155            " up).  The stencils are juxtaposed with @var{padding} as extra"
156            " space.  @var{first} and @var{second} may also be @code{'()} or"
157            " @code{#f}.  As opposed to @code{ly:stencil-combine-at-edge},"
158            " metrics are suited for successively accumulating lines of"
159            " stencils.  Also, @var{second} stencil is drawn last.\n\n"
160            "If @var{mindist} is specified, reference points are placed"
161            " apart at least by this distance.  If either of the stencils"
162            " is spacing, @var{padding} and @var{mindist} do not apply.")
163 {
164   Stencil *s1 = unsmob<Stencil> (first);
165   Stencil *s2 = unsmob<Stencil> (second);
166   Stencil result;
167
168   SCM_ASSERT_TYPE (s1 || scm_is_false (first) || scm_is_null (first),
169                    first, SCM_ARG1, __FUNCTION__, "Stencil, #f or ()");
170   SCM_ASSERT_TYPE (s2 || scm_is_false (second) || scm_is_null (second),
171                    second, SCM_ARG4, __FUNCTION__, "Stencil, #f or ()");
172   LY_ASSERT_TYPE (is_axis, axis, 2);
173   LY_ASSERT_TYPE (is_direction, direction, 3);
174
175   Real p = 0.0;
176   if (!SCM_UNBNDP (padding))
177     {
178       LY_ASSERT_TYPE (scm_is_number, padding, 5);
179       p = scm_to_double (padding);
180     }
181   Real d = -infinity_f;
182   if (!SCM_UNBNDP (mindist))
183     {
184       LY_ASSERT_TYPE (scm_is_number, mindist, 6);
185       d = scm_to_double (mindist);
186     }
187
188   if (s1)
189     result = *s1;
190
191   if (s2)
192     result.stack (Axis (scm_to_int (axis)),
193                   Direction (scm_to_int (direction)), *s2, p, d);
194
195   scm_remember_upto_here_2 (first, second);
196
197   return result.smobbed_copy ();
198 }
199
200 LY_DEFINE (ly_stencil_add, "ly:stencil-add",
201            0, 0, 1, (SCM args),
202            "Combine stencils.  Takes any number of arguments.")
203 {
204 #define FUNC_NAME __FUNCTION__
205   SCM_VALIDATE_REST_ARGUMENT (args);
206
207   SCM expr = SCM_EOL;
208   SCM cs = ly_symbol2scm ("combine-stencil");
209
210   Box extent;
211   extent.set_empty ();
212
213   while (!SCM_NULLP (args))
214     {
215       Stencil *s = unsmob<Stencil> (scm_car (args));
216       if (!s)
217         SCM_ASSERT_TYPE (s, scm_car (args), SCM_ARGn, __FUNCTION__, "Stencil");
218
219       extent.unite (s->extent_box ());
220       if (scm_is_pair (s->expr ()) && scm_is_eq (cs, s->expr ()))
221         {
222           expr = scm_reverse_x (scm_list_copy (scm_cdr (s->expr ())),
223                                 expr);
224         }
225       else
226         expr = scm_cons (s->expr (), expr);
227
228       args = scm_cdr (args);
229     }
230
231   expr = scm_cons (cs, scm_reverse_x (expr, SCM_EOL));
232   return Stencil (extent, expr).smobbed_copy ();
233 }
234
235 LY_DEFINE (ly_make_stencil, "ly:make-stencil",
236            1, 2, 0, (SCM expr, SCM xext, SCM yext),
237            "Stencils are device independent output expressions."
238            "  They carry two pieces of information:\n"
239            "\n"
240            "@enumerate\n"
241            "@item\n"
242            "A specification of how to print this object."
243            "  This specification is processed by the output backends,"
244            " for example @file{scm/output-ps.scm}.\n"
245            "\n"
246            "@item\n"
247            "The vertical and horizontal extents of the object, given as"
248            " pairs.  If an extent is unspecified (or if you use"
249            " @code{empty-interval} as its value), it is taken to be empty.\n"
250            "@end enumerate\n")
251 {
252   SCM_ASSERT_TYPE (!scm_is_pair (expr)
253                    || is_stencil_head (scm_car (expr)),
254                    expr, SCM_ARG1, __FUNCTION__, "registered stencil expression");
255
256   Interval x;
257   if (!SCM_UNBNDP (xext))
258     {
259       LY_ASSERT_TYPE (is_number_pair, xext, 2);
260       x = ly_scm2interval (xext);
261     }
262
263   Interval y;
264   if (!SCM_UNBNDP (yext))
265     {
266       LY_ASSERT_TYPE (is_number_pair, yext, 3);
267       y = ly_scm2interval (yext);
268     }
269
270   Box b (x, y);
271   Stencil s (b, expr);
272   return s.smobbed_copy ();
273 }
274
275 LY_DEFINE (ly_stencil_aligned_to, "ly:stencil-aligned-to",
276            3, 0, 0, (SCM stil, SCM axis, SCM dir),
277            "Align @var{stil} using its own extents.  @var{dir} is a number."
278            "  @w{@code{-1}} and @code{1} are left and right, respectively."
279            "  Other values are interpolated (so @code{0} means the center).")
280 {
281   LY_ASSERT_SMOB (Stencil, stil, 1);
282   LY_ASSERT_TYPE (is_axis, axis, 2);
283   LY_ASSERT_TYPE (scm_is_number, dir, 3);
284
285   Stencil target = *unsmob<Stencil> (stil);
286
287   target.align_to ((Axis)scm_to_int (axis),
288                    scm_to_double (dir));
289   return target.smobbed_copy ();
290 }
291
292 LY_DEFINE (ly_stencil_fonts, "ly:stencil-fonts",
293            1, 0, 0, (SCM s),
294            "Analyze @var{s}, and return a list of fonts used"
295            " in@tie{}@var{s}.")
296 {
297   LY_ASSERT_SMOB (Stencil, s, 1);
298   Stencil *stil = unsmob<Stencil> (s);
299   return find_expression_fonts (stil->expr ());
300 }
301
302 LY_DEFINE (ly_stencil_in_color, "ly:stencil-in-color",
303            4, 0, 0, (SCM stc, SCM r, SCM g, SCM b),
304            "Put @var{stc} in a different color.")
305 {
306   LY_ASSERT_SMOB (Stencil, stc, 1);
307   Stencil *stil = unsmob<Stencil> (stc);
308   return Stencil (stil->extent_box (),
309                   scm_list_3 (ly_symbol2scm ("color"),
310                               scm_list_3 (r, g, b),
311                               stil->expr ())).smobbed_copy ();
312 }
313
314 struct Stencil_interpret_arguments
315 {
316   SCM func;
317   SCM arg1;
318 };
319
320 SCM stencil_interpret_in_scm (void *p, SCM expr)
321 {
322   Stencil_interpret_arguments *ap = (Stencil_interpret_arguments *) p;
323   return scm_call_2 (ap->func, ap->arg1, expr);
324 }
325
326 LY_DEFINE (ly_interpret_stencil_expression, "ly:interpret-stencil-expression",
327            4, 0, 0, (SCM expr, SCM func, SCM arg1, SCM offset),
328            "Parse @var{expr}, feed bits to @var{func} with first arg"
329            " @var{arg1} having offset @var{offset}.")
330 {
331   LY_ASSERT_TYPE (ly_is_procedure, func, 2);
332
333   Stencil_interpret_arguments a;
334   a.func = func;
335   a.arg1 = arg1;
336   Offset o = ly_scm2offset (offset);
337
338   interpret_stencil_expression (expr, stencil_interpret_in_scm, (void *) & a, o);
339
340   return SCM_UNSPECIFIED;
341 }
342
343 LY_DEFINE (ly_bracket, "ly:bracket",
344            4, 0, 0,
345            (SCM a, SCM iv, SCM t, SCM p),
346            "Make a bracket in direction@tie{}@var{a}.  The extent of the"
347            " bracket is given by @var{iv}.  The wings protrude by an amount"
348            " of@tie{}@var{p}, which may be negative.  The thickness is given"
349            " by@tie{}@var{t}.")
350 {
351   LY_ASSERT_TYPE (is_axis, a, 1);
352   LY_ASSERT_TYPE (is_number_pair, iv, 2);
353   LY_ASSERT_TYPE (scm_is_number, t, 3);
354   LY_ASSERT_TYPE (scm_is_number, p, 4);
355
356   return Lookup::bracket ((Axis)scm_to_int (a), ly_scm2interval (iv),
357                           scm_to_double (t),
358                           scm_to_double (p),
359                           0.95 * scm_to_double (t)).smobbed_copy ();
360 }
361
362 LY_DEFINE (ly_stencil_rotate, "ly:stencil-rotate",
363            4, 0, 0, (SCM stil, SCM angle, SCM x, SCM y),
364            "Return a stencil @var{stil} rotated @var{angle} degrees around"
365            " the relative offset (@var{x}, @var{y}).  E.g., an offset of"
366            " (-1, 1) will rotate the stencil around the left upper corner.")
367 {
368   Stencil *s = unsmob<Stencil> (stil);
369   LY_ASSERT_SMOB (Stencil, stil, 1);
370   LY_ASSERT_TYPE (scm_is_number, angle, 2);
371   LY_ASSERT_TYPE (scm_is_number, x, 3);
372   LY_ASSERT_TYPE (scm_is_number, y, 4);
373   Real a = scm_to_double (angle);
374   Real x_off = scm_to_double (x);
375   Real y_off = scm_to_double (y);
376
377   SCM new_s = s->smobbed_copy ();
378   Stencil *q = unsmob<Stencil> (new_s);
379   q->rotate_degrees (a, Offset (x_off, y_off));
380   return new_s;
381 }
382
383 LY_DEFINE (ly_stencil_rotate_absolute, "ly:stencil-rotate-absolute",
384            4, 0, 0, (SCM stil, SCM angle, SCM x, SCM y),
385            "Return a stencil @var{stil} rotated @var{angle} degrees around"
386            " point (@var{x}, @var{y}), given in absolute coordinates.")
387 {
388   Stencil *s = unsmob<Stencil> (stil);
389   LY_ASSERT_SMOB (Stencil, stil, 1);
390   LY_ASSERT_TYPE (scm_is_number, angle, 2);
391   LY_ASSERT_TYPE (scm_is_number, x, 3);
392   LY_ASSERT_TYPE (scm_is_number, y, 4);
393   Real a = scm_to_double (angle);
394   Real x_off = scm_to_double (x);
395   Real y_off = scm_to_double (y);
396
397   SCM new_s = s->smobbed_copy ();
398   Stencil *q = unsmob<Stencil> (new_s);
399   q->rotate_degrees_absolute (a, Offset (x_off, y_off));
400   return new_s;
401 }
402
403 LY_DEFINE (ly_round_filled_box, "ly:round-filled-box",
404            3, 0, 0,
405            (SCM xext, SCM yext, SCM blot),
406            "Make a @code{Stencil} object that prints a black box of"
407            " dimensions @var{xext}, @var{yext} and roundness @var{blot}.")
408 {
409   LY_ASSERT_TYPE (is_number_pair, xext, 1);
410   LY_ASSERT_TYPE (is_number_pair, yext, 2);
411   LY_ASSERT_TYPE (scm_is_number, blot, 3);
412
413   return Lookup::round_filled_box (Box (ly_scm2interval (xext), ly_scm2interval (yext)),
414                                    scm_to_double (blot)).smobbed_copy ();
415 }
416
417 LY_DEFINE (ly_round_filled_polygon, "ly:round-filled-polygon",
418            2, 0, 0,
419            (SCM points, SCM blot),
420            "Make a @code{Stencil} object that prints a black polygon with"
421            " corners at the points defined by @var{points} (list of coordinate"
422            " pairs) and roundness @var{blot}.")
423 {
424   SCM_ASSERT_TYPE (scm_ilength (points) > 0, points, SCM_ARG1, __FUNCTION__, "list of coordinate pairs");
425   LY_ASSERT_TYPE (scm_is_number, blot, 2);
426   vector<Offset> pts;
427   for (SCM p = points; scm_is_pair (p); p = scm_cdr (p))
428     {
429       SCM scm_pt = scm_car (p);
430       if (scm_is_pair (scm_pt))
431         {
432           pts.push_back (ly_scm2offset (scm_pt));
433         }
434       else
435         {
436           // TODO: Print out warning
437         }
438     }
439   return Lookup::round_filled_polygon (pts, scm_to_double (blot)).smobbed_copy ();
440 }
441
442 LY_DEFINE (ly_register_stencil_expression, "ly:register-stencil-expression",
443            1, 0, 0,
444            (SCM symbol),
445            "Add @var{symbol} as head of a stencil expression.")
446 {
447   LY_ASSERT_TYPE (ly_is_symbol, symbol, 1);
448   register_stencil_head (symbol);
449   return SCM_UNSPECIFIED;
450 }
451
452 LY_DEFINE (ly_all_stencil_expressions, "ly:all-stencil-expressions",
453            0, 0, 0,
454            (),
455            "Return all symbols recognized as stencil expressions.")
456 {
457   return all_stencil_heads ();
458 }
459
460 LY_DEFINE (ly_stencil_scale, "ly:stencil-scale",
461            3, 0, 0, (SCM stil, SCM x, SCM y),
462            "Scale stencil @var{stil} using the horizontal and vertical"
463            " scaling factors @var{x} and @var{y}.  Negative values will"
464            " flip or mirror @var{stil} without changing its origin;"
465            " this may result in collisions unless it is repositioned.")
466 {
467   Stencil *s = unsmob<Stencil> (stil);
468   LY_ASSERT_SMOB (Stencil, stil, 1);
469   LY_ASSERT_TYPE (scm_is_number, x, 2);
470   LY_ASSERT_TYPE (scm_is_number, y, 3);
471
472   SCM new_s = s->smobbed_copy ();
473   Stencil *q = unsmob<Stencil> (new_s);
474
475   q->scale (scm_to_double (x), scm_to_double (y));
476   return new_s;
477 }