]> git.donarmstrong.com Git - lilypond.git/blob - lily/break-substitution.cc
* lily, lily/include: Rename line to system. Junk _l suffix.
[lilypond.git] / lily / break-substitution.cc
1 #include  "grob.hh"
2 #include "item.hh"
3 #include  "spanner.hh"
4 #include  "system.hh"
5
6 static SCM break_criterion; 
7 void
8 set_break_subsititution (SCM criterion)
9 {
10   break_criterion = criterion;
11 }
12
13 /*
14   Perform the substitution for a single grob.   
15  */
16 SCM
17 substitute_grob (Grob *sc)
18 {
19   if (SCM_INUMP (break_criterion))
20     {
21       Item * i = dynamic_cast<Item*> (sc);
22       Direction d = to_dir (break_criterion);
23       if (i && i->break_status_dir () != d)
24         {
25           Item *br = i->find_prebroken_piece (d);
26           return (br) ? br->self_scm () : SCM_UNDEFINED;
27         }
28     }
29   else
30     {
31       System * line
32         = dynamic_cast<System*> (unsmob_grob (break_criterion));
33       if (sc->get_system () != line)
34         {
35           sc = sc->find_broken_piece (line);
36
37         }
38           
39       /* now: !sc || (sc && sc->get_system () == line) */
40       if (!sc)
41         return SCM_UNDEFINED;
42
43       /* now: sc && sc->get_system () == line */
44       if (!line)
45         return sc->self_scm();
46
47       /*
48         We don't return SCM_UNDEFINED for
49         suicided grobs, for two reasons
50
51         - it doesn't work (strange disappearing objects)
52
53         - it forces us to mark the parents of a grob, leading to
54         a huge recursion in the GC routine.
55        */
56
57       /*
58         This was introduced in 1.3.49 as a measure to prevent
59         programming errors. It looks rather expensive (?).
60
61         TODO:
62                 
63         benchmark , document when (what kind of programming
64         errors) this happens.
65       */
66       if (sc->common_refpoint (line, X_AXIS)
67           && sc->common_refpoint (line, Y_AXIS))
68         {
69           return sc->self_scm ();
70         }
71       return SCM_UNDEFINED;
72     }
73
74   return sc->self_scm();
75 }
76
77
78
79 /*
80   Do break substitution in S, using CRITERION. Return new value.
81   CRITERION is either a SMOB pointer to the desired line, or a number
82   representing the break direction. Do not modify SRC.
83
84   It is rather tightly coded, since it takes a lot of time; it is
85   one of the top functions in the profile.
86
87   We don't pass break_criterion as a parameter, since it is
88   `constant', but takes up stack space.
89
90   It would be nice if we could do this in-place partially.  We now
91   generate a lot of garbage.
92 */
93 SCM
94 do_break_substitution (SCM src)
95 {
96  again:
97  
98   if (unsmob_grob (src))
99     {
100       return substitute_grob (unsmob_grob (src));
101     }
102   else if (gh_vector_p (src))
103     {
104       int  l = SCM_VECTOR_LENGTH (src);
105       SCM nv = scm_c_make_vector (l, SCM_UNDEFINED);
106
107       for (int i  =0 ; i< l ; i++)
108         {
109           SCM si = gh_int2scm (i);
110           scm_vector_set_x (nv, si, do_break_substitution (scm_vector_ref (src, si))); 
111         }
112     }
113   else if (ly_pair_p (src)) 
114     {
115       /*
116         UGH! breaks on circular lists.
117       */
118       SCM newcar = do_break_substitution (ly_car (src));
119       SCM oldcdr = ly_cdr (src);
120       
121       if (newcar == SCM_UNDEFINED
122           && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
123         {
124           /*
125             This is tail-recursion, ie. 
126             
127             return do_break_substution (cdr);
128
129             We don't want to rely on the compiler to do this.  Without
130             tail-recursion, this easily crashes with a stack overflow.  */
131           src =  oldcdr;
132           goto again;
133         }
134
135       return scm_cons (newcar, do_break_substitution (oldcdr));
136     }
137   else
138     return src;
139
140   return src;
141 }
142
143
144 /*
145   Perform substitution on GROB_LIST using a constant amount of stack.
146  */
147 SCM
148 substitute_grob_list (SCM grob_list)
149 {
150   SCM l = SCM_EOL;
151   SCM * tail = &l;
152
153   for (SCM s = grob_list; gh_pair_p (s); s =  gh_cdr (s))
154     {
155       SCM n= substitute_grob (unsmob_grob (gh_car (s)));
156
157       if (n != SCM_UNDEFINED)
158         {
159           *tail = gh_cons (n, SCM_EOL);
160           tail = SCM_CDRLOC(*tail);
161         }
162     }
163
164   return l;
165 }
166
167
168 SCM grob_list_p; 
169
170 /*
171   Although the substitution can be written as
172
173   property_alist = do_substitution (other_property_alist),
174
175   we have a special function here: we want to invoke a special
176   function for lists of grobs. These can be very long for large
177   orchestral scores (eg. 1M elements). do_break_substitution() can
178   recurse many levels, taking lots of stack space.
179
180   This becomes a problem if lily is linked against guile with
181   pthreads. pthreads impose small limits on the stack size.
182  */
183 SCM
184 substitute_mutable_property_alist (SCM alist)
185 {
186   if (!grob_list_p)
187     grob_list_p = scm_c_eval_string ("grob-list?");
188
189   SCM l = SCM_EOL;
190   SCM *tail = &l;
191   for (SCM s = alist; gh_pair_p (s); s = gh_cdr (s))
192     {
193       SCM sym = gh_caar(s);
194       SCM val = gh_cdar(s);
195       SCM type = scm_object_property (sym, ly_symbol2scm ("backend-type?"));
196
197       if (type == grob_list_p)
198         val = substitute_grob_list (val);
199       else
200         val = do_break_substitution (val);
201
202       *tail = gh_cons (gh_cons (sym, val), SCM_EOL);
203       tail = SCM_CDRLOC (*tail);
204     }
205
206   return l;
207 }
208
209