]> git.donarmstrong.com Git - lilypond.git/blob - lily/break-substitution.cc
(mark_smob): don't mark parents, explain why.
[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->line_l () != line)
34         {
35           sc = sc->find_broken_piece (line);
36
37         }
38           
39       /* now: !sc || (sc && sc->line_l () == line) */
40       if (!sc)
41         return SCM_UNDEFINED;
42
43       /* now: sc && sc->line_l () == 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
94 SCM
95 do_break_substitution (SCM src)
96 {
97  again:
98  
99   if (unsmob_grob (src))
100     {
101       return substitute_grob (unsmob_grob (src));
102     }
103   else if (ly_pair_p (src)) 
104     {
105       /*
106         UGH! breaks on circular lists.
107       */
108       SCM newcar = do_break_substitution (ly_car (src));
109       SCM oldcdr = ly_cdr (src);
110       
111       if (newcar == SCM_UNDEFINED
112           && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
113         {
114           /*
115             This is tail-recursion, ie. 
116             
117             return do_break_substution (cdr);
118
119             We don't want to rely on the compiler to do this.  Without
120             tail-recursion, this easily crashes with a stack overflow.  */
121           src =  oldcdr;
122           goto again;
123         }
124
125       return scm_cons (newcar, do_break_substitution (oldcdr));
126     }
127   else
128     return src;
129
130   return src;
131 }
132
133
134 /*
135   Perform substitution on GROB_LIST using a constant amount of stack.
136  */
137 SCM
138 substitute_grob_list (SCM grob_list)
139 {
140   SCM l = SCM_EOL;
141   SCM * tail = &l;
142
143   for (SCM s = grob_list; gh_pair_p (s); s =  gh_cdr (s))
144     {
145       SCM n= substitute_grob (unsmob_grob (gh_car (s)));
146
147       if (n != SCM_UNDEFINED)
148         {
149           *tail = gh_cons (n, SCM_EOL);
150           tail = SCM_CDRLOC(*tail);
151         }
152     }
153
154   return l;
155 }
156
157
158 SCM grob_list_p; 
159
160 /*
161   Although the substitution can be written as
162
163   mutable_property_alist_ = do_substitution (mutable_property_alist_),
164
165   we have a special function here: we want to invoke a special
166   function for lists of grobs. These can be very long for large
167   orchestral scores (eg. 1M elements). do_break_substitution() can
168   recurse many levels, taking lots of stack space.
169
170   This becomes a problem if lily is linked against guile with
171   pthreads. pthreads impose small limits on the stack size.
172  */
173 SCM
174 substitute_mutable_properties (SCM alist)
175 {
176   if (!grob_list_p)
177     grob_list_p = scm_c_eval_string ("grob-list?");
178
179   SCM l = SCM_EOL;
180   SCM *tail = &l;
181   for (SCM s = alist; gh_pair_p (s); s = gh_cdr (s))
182     {
183       SCM sym = gh_caar(s);
184       SCM val = gh_cdar(s);
185       SCM type = scm_object_property (sym, ly_symbol2scm ("backend-type?"));
186
187       if (type == grob_list_p)
188         val = substitute_grob_list (val);
189       else
190         val = do_break_substitution (val);
191
192       *tail = gh_cons (gh_cons (sym, val), SCM_EOL);
193       tail = SCM_CDRLOC (*tail);
194     }
195
196   return l;
197 }