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>
13 #include "grob-array.hh"
17 static SCM break_criterion;
19 set_break_subsititution (SCM criterion)
21 break_criterion = criterion;
25 Perform the substitution for a single grob.
28 substitute_grob (Grob *sc)
30 if (scm_is_integer (break_criterion))
32 Item *i = dynamic_cast<Item *> (sc);
33 Direction d = to_dir (break_criterion);
34 if (i && i->break_status_dir () != d)
36 Item *br = i->find_prebroken_piece (d);
37 return (br) ? br->self_scm () : SCM_UNDEFINED;
43 = dynamic_cast<System *> (unsmob_grob (break_criterion));
44 if (sc->get_system () != line)
46 sc = sc->find_broken_piece (line);
49 /* now: !sc || (sc && sc->get_system () == line) */
53 /* now: sc && sc->get_system () == line */
55 return sc->self_scm ();
58 We don't return SCM_UNDEFINED for
59 suicided grobs, for two reasons
61 - it doesn't work (strange disappearing objects)
63 - it forces us to mark the parents of a grob, leading to
64 a huge recursion in the GC routine.
67 if (sc->common_refpoint (line, X_AXIS)
68 && sc->common_refpoint (line, Y_AXIS))
70 return sc->self_scm ();
75 return sc->self_scm ();
79 Do break substitution in S, using CRITERION. Return new value.
80 CRITERION is either a SMOB pointer to the desired line, or a number
81 representing the break direction. Do not modify SRC.
83 It is rather tightly coded, since it takes a lot of time; it is
84 one of the top functions in the profile.
86 We don't pass break_criterion as a parameter, since it is
87 `constant', but takes up stack space.
89 It would be nice if we could do this in-place partially. We now
90 generate a lot of garbage.
93 do_break_substitution (SCM src)
97 if (unsmob_grob (src))
98 return substitute_grob (unsmob_grob (src));
99 else if (scm_is_vector (src))
101 int len = scm_c_vector_length (src);
102 SCM nv = scm_c_make_vector (len, SCM_UNDEFINED);
103 for (int i = 0; i < len; i++)
105 SCM si = scm_int2num (i);
106 scm_vector_set_x (nv, si,
107 do_break_substitution (scm_vector_ref (src, si)));
110 else if (scm_is_pair (src))
113 UGH! breaks on circular lists.
115 SCM newcar = do_break_substitution (scm_car (src));
116 SCM oldcdr = scm_cdr (src);
118 if (newcar == SCM_UNDEFINED
119 && (scm_is_pair (oldcdr) || oldcdr == SCM_EOL))
122 This is tail-recursion, ie.
124 return do_break_substution (cdr);
126 We don't want to rely on the compiler to do this. Without
127 tail-recursion, this easily crashes with a stack overflow. */
132 return scm_cons (newcar, do_break_substitution (oldcdr));
141 Perform substitution on GROB_LIST using a constant amount of stack.
144 substitute_grob_array (Grob_array *grob_arr, Grob_array * new_arr)
146 Link_array<Grob> &old_grobs (grob_arr->array_reference ());
147 Link_array<Grob> *new_grobs (new_arr == grob_arr
148 ? new Link_array<Grob>
149 : &new_arr->array_reference ());
151 for (int i = 0; i < old_grobs.size (); i++)
153 Grob * orig = old_grobs[i];
154 SCM new_grob = substitute_grob (orig);
155 if (new_grob != SCM_UNDEFINED)
157 new_grobs->push (unsmob_grob (new_grob));
161 if (new_arr == grob_arr)
163 new_arr->set_array (*new_grobs);
170 forall b in broken-childs:
171 forall p in properties:
172 forall g in p (if grob-list):
175 for spanners since this is O (SYSTEMCOUNT * GROBCOUNT), and SYSTEMCOUNT =
176 O (GROBCOUNT), we have a quadratic algorithm. --for a single spanner
178 This is problematic: with large (long) scores, the costs can be
179 significant; especially all-elements in System, can become huge. For
180 a typical 50 page score, it requires running through a 100k list 50
185 forall p in properties:
188 put grob list in array,
190 reorder array so spanners are separate -- O (grobcount)
192 find first and last indexes of grobs on a specific system
194 for items this is O (itemcount)
196 for spanners this is O (sum-of spanner-system-ranges)
198 perform the substitution O (sum-of spanner-system-ranges)
201 The complexity is harder to determine, but should be subquadratic;
203 For the situation above, we run through the entire 100k list once,
204 and also (more or less) once through the item part of the 100k (say
205 98k elements) of the list.
208 These timings were measured without -O2.
210 lehre, before 28.98 seconds, after: 27.91 seconds, 3.5 %.
212 coriolan, before 2:30, after: 1:59. Increase of 20%.
214 moz-k498-p1, before 24.10, after: 19.790s, Increase of 18%
218 spanner_system_range (Spanner *sp)
222 if (System *st = sp->get_system ())
224 rv = Slice (st->get_rank (), st->get_rank ());
228 if (sp->broken_intos_.size ())
229 rv = Slice (sp->broken_intos_[0]->get_system ()->get_rank (),
230 sp->broken_intos_.top ()->get_system ()->get_rank());
236 item_system_range (Item *it)
238 if (System *st = it->get_system ())
239 return Slice (st->get_rank (), st->get_rank ());
245 Item *bi = it->find_prebroken_piece (d);
246 if (bi && bi->get_system ())
247 sr.add_point (bi->get_system ()->get_rank ());
249 while (flip (&d) != LEFT);
255 grob_system_range (Grob *g)
257 if (Spanner *s = dynamic_cast<Spanner *> (g))
258 return spanner_system_range (s);
259 else if (Item *it = dynamic_cast<Item *> (g))
260 return item_system_range (it);
265 struct Substitution_entry
271 void set (Grob *g, Slice sr)
275 duh, don't support scores with more than 32000 systems.
280 overflow if we don't treat this specially.
291 Substitution_entry ()
297 int length () { return right_ - left_; }
299 item_compare (void const *a, void const *b)
301 return ((Substitution_entry *)a)->left_
302 - ((Substitution_entry *)b)->left_;
306 spanner_compare (void const *a, void const *b)
308 return ((Substitution_entry *)a)->length ()
309 - ((Substitution_entry *)b)->length ();
314 Spanner::fast_substitute_grob_array (SCM sym,
315 Grob_array *grob_array)
317 int len = grob_array->size();
320 Only do this complicated thing for large sets. This has the added
321 advantage that we won't screw up the ordering for elements in
322 alignments (which typically don't have more than 10 grobs.)
329 We store items on the left, spanners on the right in this vector.
331 static Substitution_entry *vec;
336 vec = (Substitution_entry *) realloc (vec, sizeof (Substitution_entry) * len);
340 Slice system_range = spanner_system_range (this);
342 int spanner_index = len;
345 for (int i = 0 ; i < grob_array->size (); i++)
347 Grob *g = grob_array->grob (i);
349 Slice sr = grob_system_range (g);
350 sr.intersect (system_range);
353 if (dynamic_cast<Spanner *> (g))
355 idx = --spanner_index;
357 else if (dynamic_cast<Item *> (g))
362 vec[idx].set (g, sr);
365 qsort (vec, item_index,
366 sizeof (Substitution_entry), &Substitution_entry::item_compare);
368 Array<Slice> item_indices;
369 Array<Slice> spanner_indices;
370 for (int i = 0; i <= system_range.length (); i++)
372 item_indices.push (Slice (len, 0));
373 spanner_indices.push (Slice (len, 0));
378 &item_indices, &spanner_indices
381 for (int i = 0; i < item_index;i++)
383 for (int j = vec[i].left_; j <= vec[i].right_; j++)
385 item_indices[j - system_range[LEFT]].add_point (i);
390 sorting vec[spanner_index.. len]
391 is a waste of time -- the staff-spanners screw up the
392 ordering, since they go across the entire score.
394 for (int i = spanner_indices.size (); i--;)
395 spanner_indices[i] = Slice (spanner_index, len - 1);
397 assert (item_index <= spanner_index);
399 assert (broken_intos_.size () == system_range.length () + 1);
400 for (int i = 0; i < broken_intos_.size (); i++)
402 Grob *sc = broken_intos_[i];
403 System *l = sc->get_system ();
404 set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
406 SCM newval = sc->internal_get_object (sym);
407 if (!unsmob_grob_array (newval))
409 newval = Grob_array::make_array ();
410 sc->internal_set_object (sym, newval);
413 Grob_array *new_array = unsmob_grob_array (newval);
414 for (int k = 0; k < 2;k++)
415 for (int j = (*arrs[k])[i][LEFT]; j <= (*arrs[k])[i][RIGHT]; j++)
417 SCM subs = substitute_grob (vec[j].grob_);
418 if (subs != SCM_UNDEFINED)
420 new_array->add (unsmob_grob (subs));
425 printf ("%d (%d), sp %d (%d)\n",
426 item_indices [i].length (), item_index,
427 spanner_indices[i].length (), len -spanner_index);
430 SCM l1 = substitute_grob_list (grob_list);
431 assert (scm_ilength (l1) == scm_ilength (newval));
440 Although the substitution can be written as
442 property_alist = do_substitution (other_property_alist),
444 we have a special function here: we want to invoke a special
445 function for lists of grobs. These can be very long for large
446 orchestral scores (eg. 1M elements). do_break_substitution () can
447 recurse many levels, taking lots of stack space.
449 This becomes a problem if lily is linked against guile with
450 pthreads. pthreads impose small limits on the stack size.
453 substitute_object_alist (SCM alist, SCM dest)
457 for (SCM s = alist; scm_is_pair (s); s = scm_cdr (s))
459 SCM sym = scm_caar (s);
460 SCM val = scm_cdar (s);
462 if (Grob_array * orig = unsmob_grob_array (val))
464 SCM handle = scm_assq (sym, dest);
466 (scm_is_pair (handle))
468 : Grob_array::make_array ();
470 Grob_array *new_arr = unsmob_grob_array (newval);
472 substitute_grob_array (orig, new_arr);
476 val = do_break_substitution (val);
478 if (val != SCM_UNDEFINED)
481 for ly:grob? properties, SCM_UNDEFINED could leak out
482 through ly:grob-property
484 *tail = scm_cons (scm_cons (sym, val), SCM_EOL);
485 tail = SCM_CDRLOC (*tail);
492 Spanner::substitute_one_mutable_property (SCM sym,
497 bool fast_done = false;
498 Grob_array * grob_array = unsmob_grob_array (val);
500 fast_done = s->fast_substitute_grob_array (sym, grob_array);
503 for (int i = 0; i < s->broken_intos_.size (); i++)
505 Grob *sc = s->broken_intos_[i];
506 System *l = sc->get_system ();
507 set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
511 SCM newval = sc->internal_get_object (sym);
512 if (!unsmob_grob_array (newval))
514 newval = Grob_array::make_array ();
515 sc->internal_set_object (sym, newval);
517 substitute_grob_array (grob_array, unsmob_grob_array (newval));
521 SCM newval = do_break_substitution (val);
522 sc->internal_set_object (sym, newval);