2 break-substitution.cc -- implement grob break substitution.
4 source file of the GNU LilyPond music typesetter
6 (c) 2001--2005 Han-Wen Nienhuys <hanwen@xs4all.nl>
12 #include "grob-array.hh"
16 static SCM break_criterion;
18 set_break_subsititution (SCM criterion)
20 break_criterion = criterion;
24 Perform the substitution for a single grob.
27 substitute_grob (Grob *sc)
29 if (scm_is_integer (break_criterion))
31 Item *i = dynamic_cast<Item *> (sc);
32 Direction d = to_dir (break_criterion);
33 if (i && i->break_status_dir () != d)
35 Item *br = i->find_prebroken_piece (d);
42 = dynamic_cast<System *> (unsmob_grob (break_criterion));
43 if (sc->get_system () != line)
44 sc = sc->find_broken_piece (line);
46 /* now: !sc || (sc && sc->get_system () == line) */
50 /* now: sc && sc->get_system () == line */
55 We don't return SCM_UNDEFINED for
56 suicided grobs, for two reasons
58 - it doesn't work (strange disappearing objects)
60 - it forces us to mark the parents of a grob, leading to
61 a huge recursion in the GC routine.
64 if (sc->common_refpoint (line, X_AXIS)
65 && sc->common_refpoint (line, Y_AXIS))
74 Do break substitution in S, using CRITERION. Return new value.
75 CRITERION is either a SMOB pointer to the desired line, or a number
76 representing the break direction. Do not modify SRC.
78 It is rather tightly coded, since it takes a lot of time; it is
79 one of the top functions in the profile.
81 We don't pass break_criterion as a parameter, since it is
82 `constant', but takes up stack space.
84 It would be nice if we could do this in-place partially. We now
85 generate a lot of garbage.
88 do_break_substitution (SCM src)
92 if (unsmob_grob (src))
94 Grob *new_ptr = substitute_grob (unsmob_grob (src));
95 return new_ptr ? new_ptr->self_scm () : SCM_UNDEFINED;
97 else if (scm_is_vector (src))
99 int len = scm_c_vector_length (src);
100 SCM nv = scm_c_make_vector (len, SCM_UNDEFINED);
101 for (int i = 0; i < len; i++)
103 SCM si = scm_from_int (i);
104 scm_vector_set_x (nv, si,
105 do_break_substitution (scm_vector_ref (src, si)));
108 else if (scm_is_pair (src))
111 UGH! breaks on circular lists.
113 SCM newcar = do_break_substitution (scm_car (src));
114 SCM oldcdr = scm_cdr (src);
116 if (newcar == SCM_UNDEFINED
117 && (scm_is_pair (oldcdr) || oldcdr == SCM_EOL))
120 This is tail-recursion, ie.
122 return do_break_substution (cdr);
124 We don't want to rely on the compiler to do this. Without
125 tail-recursion, this easily crashes with a stack overflow. */
130 return scm_cons (newcar, do_break_substitution (oldcdr));
139 Perform substitution on GROB_LIST using a constant amount of stack.
141 Link_array<Grob> temporary_substition_array;
143 substitute_grob_array (Grob_array *grob_arr, Grob_array *new_arr)
145 Link_array<Grob> &old_grobs (grob_arr->array_reference ());
146 Link_array<Grob> *new_grobs (new_arr == grob_arr
147 ? & temporary_substition_array
148 : &new_arr->array_reference ());
150 new_grobs->set_size (old_grobs.size ());
151 Grob **array = (Grob **) new_grobs->accesses ();
153 for (int i = 0; i < old_grobs.size (); i++)
155 Grob *orig = old_grobs[i];
156 Grob *new_grob = substitute_grob (orig);
161 new_grobs->set_size (ptr - array);
162 if (new_arr == grob_arr)
163 new_arr->set_array (*new_grobs);
169 forall b in broken-childs:
170 forall p in properties:
171 forall g in p (if grob-list):
174 for spanners since this is O (SYSTEMCOUNT * GROBCOUNT), and SYSTEMCOUNT =
175 O (GROBCOUNT), we have a quadratic algorithm. --for a single spanner
177 This is problematic: with large (long) scores, the costs can be
178 significant; especially all-elements in System, can become huge. For
179 a typical 50 page score, it requires running through a 100k list 50
184 forall p in properties:
187 put grob list in array,
189 reorder array so spanners are separate -- O (grobcount)
191 find first and last indexes of grobs on a specific system
193 for items this is O (itemcount)
195 for spanners this is O (sum-of spanner-system-ranges)
197 perform the substitution O (sum-of spanner-system-ranges)
200 The complexity is harder to determine, but should be subquadratic;
202 For the situation above, we run through the entire 100k list once,
203 and also (more or less) once through the item part of the 100k (say
204 98k elements) of the list.
207 These timings were measured without -O2.
209 lehre, before 28.98 seconds, after: 27.91 seconds, 3.5 %.
211 coriolan, before 2:30, after: 1:59. Increase of 20%.
213 moz-k498-p1, before 24.10, after: 19.790s, Increase of 18%
217 spanner_system_range (Spanner *sp)
221 if (System *st = sp->get_system ())
222 rv = Slice (st->get_rank (), st->get_rank ());
225 if (sp->broken_intos_.size ())
226 rv = Slice (sp->broken_intos_[0]->get_system ()->get_rank (),
227 sp->broken_intos_.top ()->get_system ()->get_rank ());
233 item_system_range (Item *it)
235 if (System *st = it->get_system ())
236 return Slice (st->get_rank (), st->get_rank ());
242 Item *bi = it->find_prebroken_piece (d);
243 if (bi && bi->get_system ())
244 sr.add_point (bi->get_system ()->get_rank ());
246 while (flip (&d) != LEFT);
252 grob_system_range (Grob *g)
254 if (Spanner *s = dynamic_cast<Spanner *> (g))
255 return spanner_system_range (s);
256 else if (Item *it = dynamic_cast<Item *> (g))
257 return item_system_range (it);
262 struct Substitution_entry
268 void set (Grob *g, Slice sr)
272 duh, don't support scores with more than 32000 systems.
277 overflow if we don't treat this specially.
288 Substitution_entry ()
294 int length () { return right_ - left_; }
296 item_compare (void const *a, void const *b)
298 return ((Substitution_entry *)a)->left_
299 - ((Substitution_entry *)b)->left_;
303 spanner_compare (void const *a, void const *b)
305 return ((Substitution_entry *)a)->length ()
306 - ((Substitution_entry *)b)->length ();
311 Spanner::fast_substitute_grob_array (SCM sym,
312 Grob_array *grob_array)
314 int len = grob_array->size ();
316 if (grob_array->ordered ())
323 We store items on the left, spanners on the right in this vector.
325 static Substitution_entry *vec;
330 vec = (Substitution_entry *) realloc (vec, sizeof (Substitution_entry) * len);
334 Slice system_range = spanner_system_range (this);
336 int spanner_index = len;
339 for (int i = 0; i < grob_array->size (); i++)
341 Grob *g = grob_array->grob (i);
343 Slice sr = grob_system_range (g);
344 sr.intersect (system_range);
347 if (dynamic_cast<Spanner *> (g))
348 idx = --spanner_index;
349 else if (dynamic_cast<Item *> (g))
352 vec[idx].set (g, sr);
355 qsort (vec, item_index,
356 sizeof (Substitution_entry), &Substitution_entry::item_compare);
358 Array<Slice> item_indices;
359 Array<Slice> spanner_indices;
360 for (int i = 0; i <= system_range.length (); i++)
362 item_indices.push (Slice (len, 0));
363 spanner_indices.push (Slice (len, 0));
368 &item_indices, &spanner_indices
371 for (int i = 0; i < item_index;i++)
373 for (int j = vec[i].left_; j <= vec[i].right_; j++)
374 item_indices[j - system_range[LEFT]].add_point (i);
378 sorting vec[spanner_index.. len]
379 is a waste of time -- the staff-spanners screw up the
380 ordering, since they go across the entire score.
382 for (int i = spanner_indices.size (); i--;)
383 spanner_indices[i] = Slice (spanner_index, len - 1);
385 assert (item_index <= spanner_index);
387 assert ((broken_intos_.size () == system_range.length () + 1)
388 || (broken_intos_.is_empty () && system_range.length () == 0));
389 for (int i = 0; i < broken_intos_.size (); i++)
391 Grob *sc = broken_intos_[i];
392 System *l = sc->get_system ();
393 set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
395 SCM newval = sc->internal_get_object (sym);
396 if (!unsmob_grob_array (newval))
398 newval = Grob_array::make_array ();
399 sc->internal_set_object (sym, newval);
402 Grob_array *new_array = unsmob_grob_array (newval);
403 for (int k = 0; k < 2;k++)
404 for (int j = (*arrs[k])[i][LEFT]; j <= (*arrs[k])[i][RIGHT]; j++)
406 Grob *substituted = substitute_grob (vec[j].grob_);
408 new_array->add (substituted);
412 printf ("%d (%d), sp %d (%d)\n",
413 item_indices [i].length (), item_index,
414 spanner_indices[i].length (), len -spanner_index);
417 SCM l1 = substitute_grob_list (grob_list);
418 assert (scm_ilength (l1) == scm_ilength (newval));
427 Although the substitution can be written as
429 property_alist = do_substitution (other_property_alist),
431 we have a special function here: we want to invoke a special
432 function for lists of grobs. These can be very long for large
433 orchestral scores (eg. 1M elements). do_break_substitution () can
434 recurse many levels, taking lots of stack space.
436 This becomes a problem if lily is linked against guile with
437 pthreads. pthreads impose small limits on the stack size.
440 substitute_object_alist (SCM alist, SCM dest)
444 for (SCM s = alist; scm_is_pair (s); s = scm_cdr (s))
446 SCM sym = scm_caar (s);
447 SCM val = scm_cdar (s);
449 if (Grob_array *orig = unsmob_grob_array (val))
451 SCM handle = scm_assq (sym, dest);
453 = (scm_is_pair (handle))
455 : Grob_array::make_array ();
457 Grob_array *new_arr = unsmob_grob_array (newval);
459 substitute_grob_array (orig, new_arr);
463 val = do_break_substitution (val);
465 if (val != SCM_UNDEFINED)
468 for ly:grob? properties, SCM_UNDEFINED could leak out
469 through ly:grob-property
471 *tail = scm_cons (scm_cons (sym, val), SCM_EOL);
472 tail = SCM_CDRLOC (*tail);
479 Spanner::substitute_one_mutable_property (SCM sym,
484 bool fast_done = false;
485 Grob_array *grob_array = unsmob_grob_array (val);
487 fast_done = s->fast_substitute_grob_array (sym, grob_array);
490 for (int i = 0; i < s->broken_intos_.size (); i++)
492 Grob *sc = s->broken_intos_[i];
493 System *l = sc->get_system ();
494 set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
498 SCM newval = sc->internal_get_object (sym);
499 if (!unsmob_grob_array (newval))
501 newval = Grob_array::make_array ();
502 sc->internal_set_object (sym, newval);
504 substitute_grob_array (grob_array, unsmob_grob_array (newval));
508 SCM newval = do_break_substitution (val);
509 sc->internal_set_object (sym, newval);