]> git.donarmstrong.com Git - lilypond.git/blob - lily/stencil.cc
* lily/stencil-scheme.cc (LY_DEFINE): ly:stencil-in-color
[lilypond.git] / lily / stencil.cc
1 /*
2   stencil.cc -- implement Stencil
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "stencil.hh"
10
11 #include <math.h>
12
13 #include "dimensions.hh"
14 #include "font-metric.hh"
15 #include "input-smob.hh"
16 #include "libc-extension.hh"    // isinf
17 #include "string-convert.hh"
18 #include "warn.hh"
19
20 #include "ly-smobs.icc"
21
22 Stencil::Stencil ()
23 {
24   expr_ = SCM_EOL;
25   set_empty (true);
26 }
27
28 Stencil::Stencil (Box b, SCM func)
29 {
30   expr_ = func;
31   dim_ = b;
32 }
33
34 int
35 Stencil::print_smob (SCM, SCM port, scm_print_state *)
36 {
37   scm_puts ("#<Stencil ", port);
38   scm_puts (" >", port);
39   return 1;
40 }
41
42 SCM
43 Stencil::mark_smob (SCM smob)
44 {
45   Stencil *s = (Stencil *) SCM_CELL_WORD_1 (smob);
46   return s->expr_;
47 }
48
49 IMPLEMENT_SIMPLE_SMOBS (Stencil);
50 IMPLEMENT_TYPE_P (Stencil, "ly:stencil?");
51 IMPLEMENT_DEFAULT_EQUAL_P (Stencil);
52
53 Interval
54 Stencil::extent (Axis a) const
55 {
56   return dim_[a];
57 }
58
59 /* Hmm... maybe this is not such a good idea ; stuff can be empty,
60    while expr_ == '()  */
61 bool
62 Stencil::is_empty () const
63 {
64   return expr_ == SCM_EOL;
65 }
66
67 SCM
68 Stencil::expr () const
69 {
70   return expr_;
71 }
72
73 Box
74 Stencil::extent_box () const
75 {
76   return dim_;
77 }
78 Offset
79 Stencil::origin () const
80 {
81   return origin_;
82 }
83
84 void
85 Stencil::translate (Offset o)
86 {
87   Axis a = X_AXIS;
88   while (a < NO_AXES)
89     {
90       if (isinf (o[a])
91           || isnan (o[a])
92
93           // ugh, hardcoded. 
94           || fabs (o[a]) > 1e6) 
95         {
96           programming_error (String_convert::form_string ("Improbable offset for stencil: %f staff space", o[a])
97                              + "\n"
98                              + "Setting to zero.");
99           o[a] = 0.0;
100         }
101       incr (a);
102     }
103
104   expr_ = scm_list_n (ly_symbol2scm ("translate-stencil"),
105                       ly_offset2scm (o),
106                       expr_, SCM_UNDEFINED);
107   if (!is_empty ())
108     dim_.translate (o);
109   origin_ += o;
110 }
111
112 void
113 Stencil::translate_axis (Real x, Axis a)
114 {
115   Offset o (0, 0);
116   o[a] = x;
117   translate (o);
118 }
119
120 void
121 Stencil::add_stencil (Stencil const &s)
122 {
123   expr_ = scm_list_3 (ly_symbol2scm ("combine-stencil"), s.expr_, expr_);
124   dim_.unite (s.dim_);
125 }
126
127 void
128 Stencil::set_empty (bool e)
129 {
130   if (e)
131     {
132       dim_[X_AXIS].set_empty ();
133       dim_[Y_AXIS].set_empty ();
134     }
135   else
136     {
137       dim_[X_AXIS] = Interval (0, 0);
138       dim_[Y_AXIS] = Interval (0, 0);
139     }
140 }
141
142 void
143 Stencil::align_to (Axis a, Real x)
144 {
145   if (is_empty ())
146     return;
147
148   Interval i (extent (a));
149   translate_axis (-i.linear_combination (x), a);
150 }
151
152 /* FIXME: unintuitive naming, you would expect *this to be moved.
153    Kept (keeping?) API for compat with add_at_edge ().
154
155    What is PADDING, what is MINIMUM, exactly?  */
156 Stencil
157 Stencil::moved_to_edge (Axis a, Direction d, Stencil const &s,
158                         Real padding, Real minimum) const
159 {
160   Interval my_extent = dim_[a];
161   Interval i (s.extent (a));
162   Real his_extent;
163   if (i.is_empty ())
164     {
165       programming_error ("Stencil::moved_to_edge: adding empty stencil.");
166       his_extent = 0.0;
167       //      SCM_ASSERT_TYPE (0, s.expr (), SCM_ARG1, __FUNCTION__, "non-empty stencil");
168     }
169   else
170     his_extent = i[-d];
171
172   Real offset = (my_extent.is_empty () ? 0.0 : my_extent[d] - his_extent)
173     + d * padding;
174
175   Stencil toadd (s);
176   toadd.translate_axis (offset, a);
177
178   if (minimum > 0 && d * (-origin ()[a] + toadd.origin ()[a]) < minimum)
179     toadd.translate_axis (-toadd.origin ()[a]
180                           + origin ()[a] + d * minimum, a);
181
182   return toadd;
183 }
184
185 /*  See scheme Function.  */
186 void
187 Stencil::add_at_edge (Axis a, Direction d, Stencil const &s, Real padding,
188                       Real minimum)
189 {
190   add_stencil (moved_to_edge (a, d, s, padding, minimum));
191 }
192
193
194
195 Stencil
196 Stencil::in_color (Real r, Real g, Real b) const
197 {
198   Stencil new_stencil (extent_box (),
199                        scm_list_3 (ly_symbol2scm ("color"),
200                                    scm_list_3 (scm_from_double (r),
201                                                scm_from_double (g),
202                                                scm_from_double (b)),
203                                    expr ()));
204   return new_stencil;
205 }
206
207 /****************************************************************/
208
209 void
210 interpret_stencil_expression (SCM expr,
211                               void (*func) (void *, SCM),
212                               void *func_arg,
213                               Offset o)
214 {
215   while (1)
216     {
217       if (!scm_is_pair (expr))
218         return;
219
220       SCM head = scm_car (expr);
221
222       if (head == ly_symbol2scm ("translate-stencil"))
223         {
224           o += ly_scm2offset (scm_cadr (expr));
225           expr = scm_caddr (expr);
226         }
227       else if (head == ly_symbol2scm ("combine-stencil"))
228         {
229           for (SCM x = scm_cdr (expr); scm_is_pair (x); x = scm_cdr (x))
230             interpret_stencil_expression (scm_car (x), func, func_arg, o);
231           return;
232         }
233       else if (head == ly_symbol2scm ("grob-cause"))
234         {
235           SCM grob = scm_cadr (expr);
236
237           (*func) (func_arg, scm_list_3 (head,
238                                          ly_quote_scm (ly_offset2scm (o)), grob));
239           interpret_stencil_expression (scm_caddr (expr), func, func_arg, o);
240           (*func) (func_arg, scm_list_1 (ly_symbol2scm ("no-origin")));
241           return;
242         }
243       else if (head == ly_symbol2scm ("color"))
244         {
245           SCM color = scm_cadr (expr);
246           SCM r = scm_car (color);
247           SCM g = scm_cadr (color);
248           SCM b = scm_caddr (color);
249
250           (*func) (func_arg, scm_list_4 (ly_symbol2scm ("setcolor"), r, g, b));
251           interpret_stencil_expression (scm_caddr (expr), func, func_arg, o);
252           (*func) (func_arg, scm_list_1 (ly_symbol2scm ("resetcolor")));
253
254           return;
255         }
256       else
257         {
258           (*func) (func_arg,
259                    scm_list_4 (ly_symbol2scm ("placebox"),
260                                scm_make_real (o[X_AXIS]),
261                                scm_make_real (o[Y_AXIS]),
262                                expr));
263           return;
264         }
265     }
266 }
267
268 struct Font_list
269 {
270   SCM fonts_;
271 };
272
273 static void
274 find_font_function (void *fs, SCM x)
275 {
276   Font_list *me = (Font_list *) fs;
277
278   if (scm_car (x) == ly_symbol2scm ("placebox"))
279     {
280       SCM args = scm_cdr (x);
281       SCM what = scm_caddr (args);
282
283       if (scm_is_pair (what))
284         {
285           SCM head = scm_car (what);
286           if (ly_symbol2scm ("text") == head)
287             me->fonts_ = scm_cons (scm_cadr (what), me->fonts_);
288           else if (head == ly_symbol2scm ("char"))
289             me->fonts_ = scm_cons (scm_cadr (what), me->fonts_);
290         }
291     }
292 }
293
294 SCM
295 find_expression_fonts (SCM expr)
296 {
297   Font_list fl;
298
299   fl.fonts_ = SCM_EOL;
300
301   interpret_stencil_expression (expr, &find_font_function,
302                                 (void *) & fl, Offset (0, 0));
303
304   return fl.fonts_;
305 }