]> git.donarmstrong.com Git - lilypond.git/blob - lily/stencil.cc
* lily/spanner.cc:
[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]) || isnan (o[a]))
91         {
92           programming_error (String_convert::form_string ("Improbable offset for stencil: %f staff space", o[a])
93                              + "\n"
94                              + "Setting to zero.");
95           o[a] = 0.0;
96         }
97       incr (a);
98     }
99
100   expr_ = scm_list_n (ly_symbol2scm ("translate-stencil"),
101                       ly_offset2scm (o),
102                       expr_, SCM_UNDEFINED);
103   if (!is_empty ())
104     dim_.translate (o);
105   origin_ += o;
106 }
107
108 void
109 Stencil::translate_axis (Real x, Axis a)
110 {
111   Offset o (0, 0);
112   o[a] = x;
113   translate (o);
114 }
115
116 void
117 Stencil::add_stencil (Stencil const &s)
118 {
119   expr_ = scm_list_3 (ly_symbol2scm ("combine-stencil"), s.expr_, expr_);
120   dim_.unite (s.dim_);
121 }
122
123 void
124 Stencil::set_empty (bool e)
125 {
126   if (e)
127     {
128       dim_[X_AXIS].set_empty ();
129       dim_[Y_AXIS].set_empty ();
130     }
131   else
132     {
133       dim_[X_AXIS] = Interval (0, 0);
134       dim_[Y_AXIS] = Interval (0, 0);
135     }
136 }
137
138 void
139 Stencil::align_to (Axis a, Real x)
140 {
141   if (is_empty ())
142     return;
143
144   Interval i (extent (a));
145   translate_axis (-i.linear_combination (x), a);
146 }
147
148 /* FIXME: unintuitive naming, you would expect *this to be moved.
149    Kept (keeping?) API for compat with add_at_edge ().
150
151    What is PADDING, what is MINIMUM, exactly?  */
152 Stencil
153 Stencil::moved_to_edge (Axis a, Direction d, Stencil const &s,
154                         Real padding, Real minimum) const
155 {
156   Interval my_extent = dim_[a];
157   Interval i (s.extent (a));
158   Real his_extent;
159   if (i.is_empty ())
160     {
161       programming_error ("Stencil::moved_to_edge: adding empty stencil.");
162       his_extent = 0.0;
163       //      SCM_ASSERT_TYPE (0, s.expr (), SCM_ARG1, __FUNCTION__, "non-empty stencil");
164     }
165   else
166     his_extent = i[-d];
167
168   Real offset = (my_extent.is_empty () ? 0.0 : my_extent[d] - his_extent)
169     + d * padding;
170
171   Stencil toadd (s);
172   toadd.translate_axis (offset, a);
173
174   if (minimum > 0 && d * (-origin ()[a] + toadd.origin ()[a]) < minimum)
175     toadd.translate_axis (-toadd.origin ()[a]
176                           + origin ()[a] + d * minimum, a);
177
178   return toadd;
179 }
180
181 /*  See scheme Function.  */
182 void
183 Stencil::add_at_edge (Axis a, Direction d, Stencil const &s, Real padding,
184                       Real minimum)
185 {
186   add_stencil (moved_to_edge (a, d, s, padding, minimum));
187 }
188
189 /****************************************************************/
190
191 void
192 interpret_stencil_expression (SCM expr,
193                               void (*func) (void *, SCM),
194                               void *func_arg,
195                               Offset o)
196 {
197   while (1)
198     {
199       if (!scm_is_pair (expr))
200         return;
201
202       SCM head = scm_car (expr);
203
204       if (head == ly_symbol2scm ("translate-stencil"))
205         {
206           o += ly_scm2offset (scm_cadr (expr));
207           expr = scm_caddr (expr);
208         }
209       else if (head == ly_symbol2scm ("combine-stencil"))
210         {
211           for (SCM x = scm_cdr (expr); scm_is_pair (x); x = scm_cdr (x))
212             interpret_stencil_expression (scm_car (x), func, func_arg, o);
213           return;
214         }
215       else if (head == ly_symbol2scm ("grob-cause"))
216         {
217           SCM grob = scm_cadr (expr);
218
219           (*func) (func_arg, scm_list_3 (head,
220                                          ly_quote_scm (ly_offset2scm (o)), grob));
221           interpret_stencil_expression (scm_caddr (expr), func, func_arg, o);
222           (*func) (func_arg, scm_list_1 (ly_symbol2scm ("no-origin")));
223           return;
224         }
225       else if (head == ly_symbol2scm ("color"))
226         {
227           SCM color = scm_cadr (expr);
228           SCM r = scm_car (color);
229           SCM g = scm_cadr (color);
230           SCM b = scm_caddr (color);
231
232           (*func) (func_arg, scm_list_4 (ly_symbol2scm ("setcolor"), r, g, b));
233           interpret_stencil_expression (scm_caddr (expr), func, func_arg, o);
234           (*func) (func_arg, scm_list_1 (ly_symbol2scm ("resetcolor")));
235
236           return;
237         }
238       else
239         {
240           (*func) (func_arg,
241                    scm_list_4 (ly_symbol2scm ("placebox"),
242                                scm_make_real (o[X_AXIS]),
243                                scm_make_real (o[Y_AXIS]),
244                                expr));
245           return;
246         }
247     }
248 }
249
250 struct Font_list
251 {
252   SCM fonts_;
253 };
254
255 static void
256 find_font_function (void *fs, SCM x)
257 {
258   Font_list *me = (Font_list *) fs;
259
260   if (scm_car (x) == ly_symbol2scm ("placebox"))
261     {
262       SCM args = scm_cdr (x);
263       SCM what = scm_caddr (args);
264
265       if (scm_is_pair (what))
266         {
267           SCM head = scm_car (what);
268           if (ly_symbol2scm ("text") == head)
269             me->fonts_ = scm_cons (scm_cadr (what), me->fonts_);
270           else if (head == ly_symbol2scm ("char"))
271             me->fonts_ = scm_cons (scm_cadr (what), me->fonts_);
272         }
273     }
274 }
275
276 SCM
277 find_expression_fonts (SCM expr)
278 {
279   Font_list fl;
280
281   fl.fonts_ = SCM_EOL;
282
283   interpret_stencil_expression (expr, &find_font_function,
284                                 (void *) & fl, Offset (0, 0));
285
286   return fl.fonts_;
287 }
288