Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / lily / break-substitution.cc
blob2cd29707d611a1d60c704d0c9615eaa7a73af35c
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2001--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include <cstdio>
21 #include <cstdlib>
22 using namespace std;
24 #include "item.hh"
25 #include "system.hh"
26 #include "grob-array.hh"
28 static SCM break_criterion;
29 void
30 set_break_subsititution (SCM criterion)
32 break_criterion = criterion;
36 Perform the substitution for a single grob.
38 Grob *
39 substitute_grob (Grob *sc)
41 if (scm_is_integer (break_criterion))
43 Item *i = dynamic_cast<Item *> (sc);
44 Direction d = to_dir (break_criterion);
45 if (i && i->break_status_dir () != d)
47 Item *br = i->find_prebroken_piece (d);
48 return br;
51 else
53 System *line
54 = dynamic_cast<System *> (unsmob_grob (break_criterion));
55 if (sc->get_system () != line)
56 sc = sc->find_broken_piece (line);
58 /* now: !sc || (sc && sc->get_system () == line) */
59 if (!sc)
60 return 0;
62 /* now: sc && sc->get_system () == line */
63 if (!line)
64 return sc;
67 We don't return SCM_UNDEFINED for
68 suicided grobs, for two reasons
70 - it doesn't work (strange disappearing objects)
72 - it forces us to mark the parents of a grob, leading to
73 a huge recursion in the GC routine.
76 if (sc->common_refpoint (line, X_AXIS)
77 && sc->common_refpoint (line, Y_AXIS))
78 return sc;
79 return 0;
82 return sc;
86 Do break substitution in S, using CRITERION. Return new value.
87 CRITERION is either a SMOB pointer to the desired line, or a number
88 representing the break direction. Do not modify SRC.
90 It is rather tightly coded, since it takes a lot of time; it is
91 one of the top functions in the profile.
93 We don't pass break_criterion as a parameter, since it is
94 `constant', but takes up stack space.
96 It would be nice if we could do this in-place partially. We now
97 generate a lot of garbage.
99 SCM
100 do_break_substitution (SCM src)
102 again:
104 if (unsmob_grob (src))
106 Grob *new_ptr = substitute_grob (unsmob_grob (src));
107 return new_ptr ? new_ptr->self_scm () : SCM_UNDEFINED;
109 else if (scm_is_vector (src))
111 int len = scm_c_vector_length (src);
112 SCM nv = scm_c_make_vector (len, SCM_UNDEFINED);
113 for (int i = 0; i < len; i++)
115 SCM si = scm_from_int (i);
116 scm_vector_set_x (nv, si,
117 do_break_substitution (scm_vector_ref (src, si)));
120 else if (scm_is_pair (src))
123 UGH! breaks on circular lists.
125 SCM newcar = do_break_substitution (scm_car (src));
126 SCM oldcdr = scm_cdr (src);
128 if (newcar == SCM_UNDEFINED
129 && (scm_is_pair (oldcdr) || oldcdr == SCM_EOL))
132 This is tail-recursion, ie.
134 return do_break_substution (cdr);
136 We don't want to rely on the compiler to do this. Without
137 tail-recursion, this easily crashes with a stack overflow. */
138 src = oldcdr;
139 goto again;
142 return scm_cons (newcar, do_break_substitution (oldcdr));
144 else
145 return src;
147 return src;
151 Perform substitution on GROB_LIST using a constant amount of stack.
153 vector<Grob*> temporary_substition_array;
154 void
155 substitute_grob_array (Grob_array *grob_arr, Grob_array *new_arr)
157 vector<Grob*> &old_grobs (grob_arr->array_reference ());
158 vector<Grob*> *new_grobs (new_arr == grob_arr
159 ? & temporary_substition_array
160 : &new_arr->array_reference ());
162 new_grobs->resize (old_grobs.size ());
163 Grob **array = (Grob **) new_grobs->data ();
164 Grob **ptr = array;
165 for (vsize i = 0; i < old_grobs.size (); i++)
167 Grob *orig = old_grobs[i];
168 Grob *new_grob = substitute_grob (orig);
169 if (new_grob)
170 *ptr++ = new_grob;
173 new_grobs->resize (ptr - array);
174 if (new_arr == grob_arr)
175 new_arr->set_array (*new_grobs);
179 We don't do
181 forall b in broken-childs:
182 forall p in properties:
183 forall g in p (if grob-list):
184 g := substitute (g)
186 for spanners since this is O (SYSTEMCOUNT * GROBCOUNT), and SYSTEMCOUNT =
187 O (GROBCOUNT), we have a quadratic algorithm. --for a single spanner
189 This is problematic: with large (long) scores, the costs can be
190 significant; especially all-elements in System, can become huge. For
191 a typical 50 page score, it requires running through a 100k list 50
192 times.
194 Instead:
196 forall p in properties:
197 (if grob list)
199 put grob list in array,
201 reorder array so spanners are separate -- O (grobcount)
203 find first and last indexes of grobs on a specific system
205 for items this is O (itemcount)
207 for spanners this is O (sum-of spanner-system-ranges)
209 perform the substitution O (sum-of spanner-system-ranges)
212 The complexity is harder to determine, but should be subquadratic;
214 For the situation above, we run through the entire 100k list once,
215 and also (more or less) once through the item part of the 100k (say
216 98k elements) of the list.
219 These timings were measured without -O2.
221 lehre, before 28.98 seconds, after: 27.91 seconds, 3.5 %.
223 coriolan, before 2:30, after: 1:59. Increase of 20%.
225 moz-k498-p1, before 24.10, after: 19.790s, Increase of 18%
228 Slice
229 spanner_system_range (Spanner *sp)
231 Slice rv;
233 if (System *st = sp->get_system ())
234 rv = Slice (st->get_rank (), st->get_rank ());
235 else
237 if (sp->broken_intos_.size ())
238 rv = Slice (sp->broken_intos_[0]->get_system ()->get_rank (),
239 sp->broken_intos_.back ()->get_system ()->get_rank ());
241 return rv;
244 Slice
245 item_system_range (Item *it)
247 if (System *st = it->get_system ())
248 return Slice (st->get_rank (), st->get_rank ());
250 Slice sr;
251 Direction d = LEFT;
254 Item *bi = it->find_prebroken_piece (d);
255 if (bi && bi->get_system ())
256 sr.add_point (bi->get_system ()->get_rank ());
258 while (flip (&d) != LEFT);
260 return sr;
263 Slice
264 grob_system_range (Grob *g)
266 if (Spanner *s = dynamic_cast<Spanner *> (g))
267 return spanner_system_range (s);
268 else if (Item *it = dynamic_cast<Item *> (g))
269 return item_system_range (it);
270 else
271 return Slice ();
274 struct Substitution_entry
276 Grob *grob_;
278 /* Assumption: we have less than 32k paper columns. */
279 short left_;
280 short right_;
282 void set (Grob *g, Slice sr)
284 grob_ = g;
286 duh, don't support scores with more than 32000 systems.
288 if (sr.is_empty ())
291 overflow if we don't treat this specially.
293 left_ = 1;
294 right_ = -1;
296 else
298 left_ = (short) sr[LEFT];
299 right_ = (short) sr[RIGHT];
302 Substitution_entry ()
304 grob_ = 0;
305 left_ = right_ = -2;
308 int length () { return right_ - left_; }
309 static int
310 item_compare (void const *a, void const *b)
312 return ((Substitution_entry *)a)->left_
313 - ((Substitution_entry *)b)->left_;
316 static int
317 spanner_compare (void const *a, void const *b)
319 return ((Substitution_entry *)a)->length ()
320 - ((Substitution_entry *)b)->length ();
324 bool
325 Spanner::fast_substitute_grob_array (SCM sym,
326 Grob_array *grob_array)
328 int len = grob_array->size ();
330 if (grob_array->ordered ())
331 return false;
333 if (len < 15)
334 return false;
337 We store items on the left, spanners on the right in this vector.
339 FIXME: will not multithread.
341 static Substitution_entry *vec;
342 static int vec_room;
344 if (vec_room < len)
346 vec = (Substitution_entry *) realloc (vec, sizeof (Substitution_entry) * len);
347 vec_room = len;
350 Slice system_range = spanner_system_range (this);
352 int spanner_index = len;
353 int item_index = 0;
355 for (vsize i = 0; i < grob_array->size (); i++)
357 Grob *g = grob_array->grob (i);
359 Slice sr = grob_system_range (g);
360 sr.intersect (system_range);
362 int idx = 0;
363 if (dynamic_cast<Spanner *> (g))
364 idx = --spanner_index;
365 else if (dynamic_cast<Item *> (g))
366 idx = item_index++;
368 vec[idx].set (g, sr);
371 qsort (vec, item_index,
372 sizeof (Substitution_entry), &Substitution_entry::item_compare);
374 vector<Slice> item_indices;
375 vector<Slice> spanner_indices;
376 for (int i = 0; i <= system_range.length (); i++)
378 item_indices.push_back (Slice (len, 0));
379 spanner_indices.push_back (Slice (len, 0));
382 vector<Slice> *arrs[]
384 &item_indices, &spanner_indices
387 for (int i = 0; i < item_index;i++)
389 for (int j = vec[i].left_; j <= vec[i].right_; j++)
390 item_indices[j - system_range[LEFT]].add_point (i);
394 sorting vec[spanner_index.. len]
395 is a waste of time -- the staff-spanners screw up the
396 ordering, since they go across the entire score.
398 for (vsize i = spanner_indices.size (); i--;)
399 spanner_indices[i] = Slice (spanner_index, len - 1);
401 assert (item_index <= spanner_index);
403 assert ((broken_intos_.size () == (vsize)system_range.length () + 1)
404 || (broken_intos_.empty () && system_range.length () == 0));
405 for (vsize i = 0; i < broken_intos_.size (); i++)
407 Grob *sc = broken_intos_[i];
408 System *l = sc->get_system ();
409 set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
411 SCM newval = sc->internal_get_object (sym);
412 if (!unsmob_grob_array (newval))
414 newval = Grob_array::make_array ();
415 sc->set_object (sym, newval);
418 Grob_array *new_array = unsmob_grob_array (newval);
419 for (int k = 0; k < 2;k++)
420 for (int j = (*arrs[k])[i][LEFT]; j <= (*arrs[k])[i][RIGHT]; j++)
422 Grob *substituted = substitute_grob (vec[j].grob_);
423 if (substituted)
424 new_array->add (substituted);
427 #ifdef PARANOIA
428 printf ("%d (%d), sp %d (%d)\n",
429 item_indices [i].length (), item_index,
430 spanner_indices[i].length (), len -spanner_index);
433 SCM l1 = substitute_grob_list (grob_list);
434 assert (scm_ilength (l1) == scm_ilength (newval));
436 #endif
439 return true;
443 Although the substitution can be written as
445 property_alist = do_substitution (other_property_alist),
447 we have a special function here: we want to invoke a special
448 function for lists of grobs. These can be very long for large
449 orchestral scores (eg. 1M elements). do_break_substitution () can
450 recurse many levels, taking lots of stack space.
452 This becomes a problem if lily is linked against guile with
453 pthreads. pthreads impose small limits on the stack size.
456 substitute_object_alist (SCM alist, SCM dest)
458 SCM l = SCM_EOL;
459 SCM *tail = &l;
460 for (SCM s = alist; scm_is_pair (s); s = scm_cdr (s))
462 SCM sym = scm_caar (s);
463 SCM val = scm_cdar (s);
465 if (Grob_array *orig = unsmob_grob_array (val))
467 SCM handle = scm_assq (sym, dest);
468 SCM newval
469 = (scm_is_pair (handle))
470 ? scm_cdr (handle)
471 : Grob_array::make_array ();
473 Grob_array *new_arr = unsmob_grob_array (newval);
475 substitute_grob_array (orig, new_arr);
476 val = newval;
478 else
479 val = do_break_substitution (val);
481 if (val != SCM_UNDEFINED)
484 for ly:grob? properties, SCM_UNDEFINED could leak out
485 through ly:grob-property
487 *tail = scm_cons (scm_cons (sym, val), SCM_EOL);
488 tail = SCM_CDRLOC (*tail);
491 return l;
494 void
495 Spanner::substitute_one_mutable_property (SCM sym,
496 SCM val)
498 Spanner *s = this;
500 bool fast_done = false;
501 Grob_array *grob_array = unsmob_grob_array (val);
502 if (grob_array)
503 fast_done = s->fast_substitute_grob_array (sym, grob_array);
505 if (!fast_done)
506 for (vsize i = 0; i < s->broken_intos_.size (); i++)
508 Grob *sc = s->broken_intos_[i];
509 System *l = sc->get_system ();
510 set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
512 if (grob_array)
514 SCM newval = sc->internal_get_object (sym);
515 if (!unsmob_grob_array (newval))
517 newval = Grob_array::make_array ();
518 sc->set_object (sym, newval);
520 substitute_grob_array (grob_array, unsmob_grob_array (newval));
522 else
524 SCM newval = do_break_substitution (val);
525 sc->set_object (sym, newval);
530 void
531 Grob::substitute_object_links (SCM crit, SCM orig)
533 set_break_subsititution (crit);
534 object_alist_ = substitute_object_alist (orig, object_alist_);