]> git.donarmstrong.com Git - lilypond.git/blob - lily/stencil.cc
*** empty log message ***
[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 void
196 interpret_stencil_expression (SCM expr,
197                               void (*func) (void *, SCM),
198                               void *func_arg,
199                               Offset o)
200 {
201   while (1)
202     {
203       if (!scm_is_pair (expr))
204         return;
205
206       SCM head = scm_car (expr);
207
208       if (head == ly_symbol2scm ("translate-stencil"))
209         {
210           o += ly_scm2offset (scm_cadr (expr));
211           expr = scm_caddr (expr);
212         }
213       else if (head == ly_symbol2scm ("combine-stencil"))
214         {
215           for (SCM x = scm_cdr (expr); scm_is_pair (x); x = scm_cdr (x))
216             interpret_stencil_expression (scm_car (x), func, func_arg, o);
217           return;
218         }
219       else if (head == ly_symbol2scm ("grob-cause"))
220         {
221           SCM grob = scm_cadr (expr);
222
223           (*func) (func_arg, scm_list_3 (head,
224                                          ly_quote_scm (ly_offset2scm (o)), grob));
225           interpret_stencil_expression (scm_caddr (expr), func, func_arg, o);
226           (*func) (func_arg, scm_list_1 (ly_symbol2scm ("no-origin")));
227           return;
228         }
229       else if (head == ly_symbol2scm ("color"))
230         {
231           SCM color = scm_cadr (expr);
232           SCM r = scm_car (color);
233           SCM g = scm_cadr (color);
234           SCM b = scm_caddr (color);
235
236           (*func) (func_arg, scm_list_4 (ly_symbol2scm ("setcolor"), r, g, b));
237           interpret_stencil_expression (scm_caddr (expr), func, func_arg, o);
238           (*func) (func_arg, scm_list_1 (ly_symbol2scm ("resetcolor")));
239
240           return;
241         }
242       else
243         {
244           (*func) (func_arg,
245                    scm_list_4 (ly_symbol2scm ("placebox"),
246                                scm_make_real (o[X_AXIS]),
247                                scm_make_real (o[Y_AXIS]),
248                                expr));
249           return;
250         }
251     }
252 }
253
254 struct Font_list
255 {
256   SCM fonts_;
257 };
258
259 static void
260 find_font_function (void *fs, SCM x)
261 {
262   Font_list *me = (Font_list *) fs;
263
264   if (scm_car (x) == ly_symbol2scm ("placebox"))
265     {
266       SCM args = scm_cdr (x);
267       SCM what = scm_caddr (args);
268
269       if (scm_is_pair (what))
270         {
271           SCM head = scm_car (what);
272           if (ly_symbol2scm ("text") == head)
273             me->fonts_ = scm_cons (scm_cadr (what), me->fonts_);
274           else if (head == ly_symbol2scm ("char"))
275             me->fonts_ = scm_cons (scm_cadr (what), me->fonts_);
276         }
277     }
278 }
279
280 SCM
281 find_expression_fonts (SCM expr)
282 {
283   Font_list fl;
284
285   fl.fonts_ = SCM_EOL;
286
287   interpret_stencil_expression (expr, &find_font_function,
288                                 (void *) & fl, Offset (0, 0));
289
290   return fl.fonts_;
291 }
292