8 static SCM break_criterion;
10 set_break_subsititution (SCM criterion)
12 break_criterion = criterion;
16 Perform the substitution for a single grob.
19 substitute_grob (Grob *sc)
21 if (SCM_INUMP (break_criterion))
23 Item * i = dynamic_cast<Item*> (sc);
24 Direction d = to_dir (break_criterion);
25 if (i && i->break_status_dir () != d)
27 Item *br = i->find_prebroken_piece (d);
28 return (br) ? br->self_scm () : SCM_UNDEFINED;
34 = dynamic_cast<System*> (unsmob_grob (break_criterion));
35 if (sc->get_system () != line)
37 sc = sc->find_broken_piece (line);
41 /* now: !sc || (sc && sc->get_system () == line) */
45 /* now: sc && sc->get_system () == line */
47 return sc->self_scm();
50 We don't return SCM_UNDEFINED for
51 suicided grobs, for two reasons
53 - it doesn't work (strange disappearing objects)
55 - it forces us to mark the parents of a grob, leading to
56 a huge recursion in the GC routine.
60 This was introduced in 1.3.49 as a measure to prevent
61 programming errors. It looks rather expensive (?).
65 benchmark , document when (what kind of programming
68 if (sc->common_refpoint (line, X_AXIS)
69 && sc->common_refpoint (line, Y_AXIS))
71 return sc->self_scm ();
76 return sc->self_scm();
82 Do break substitution in S, using CRITERION. Return new value.
83 CRITERION is either a SMOB pointer to the desired line, or a number
84 representing the break direction. Do not modify SRC.
86 It is rather tightly coded, since it takes a lot of time; it is
87 one of the top functions in the profile.
89 We don't pass break_criterion as a parameter, since it is
90 `constant', but takes up stack space.
92 It would be nice if we could do this in-place partially. We now
93 generate a lot of garbage.
96 do_break_substitution (SCM src)
100 if (unsmob_grob (src))
102 return substitute_grob (unsmob_grob (src));
104 else if (gh_vector_p (src))
106 int l = SCM_VECTOR_LENGTH (src);
107 SCM nv = scm_c_make_vector (l, SCM_UNDEFINED);
109 for (int i =0 ; i< l ; i++)
111 SCM si = scm_int2num (i);
112 scm_vector_set_x (nv, si, do_break_substitution (scm_vector_ref (src, si)));
115 else if (ly_pair_p (src))
118 UGH! breaks on circular lists.
120 SCM newcar = do_break_substitution (ly_car (src));
121 SCM oldcdr = ly_cdr (src);
123 if (newcar == SCM_UNDEFINED
124 && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
127 This is tail-recursion, ie.
129 return do_break_substution (cdr);
131 We don't want to rely on the compiler to do this. Without
132 tail-recursion, this easily crashes with a stack overflow. */
137 return scm_cons (newcar, do_break_substitution (oldcdr));
147 Perform substitution on GROB_LIST using a constant amount of stack.
150 substitute_grob_list (SCM grob_list)
155 for (SCM s = grob_list; gh_pair_p (s); s = gh_cdr (s))
157 SCM n= substitute_grob (unsmob_grob (gh_car (s)));
159 if (n != SCM_UNDEFINED)
161 *tail = gh_cons (n, SCM_EOL);
162 tail = SCM_CDRLOC(*tail);
172 forall b in broken-childs:
173 forall p in properties:
174 forall g in p (if grob-list):
177 for spanners since this is O(SYSTEMCOUNT * GROBCOUNT), and SYSTEMCOUNT =
178 O(GROBCOUNT), we have a quadratic algorithm. --for a single spanner
180 This is problematic: with large (long) scores, the costs can be
181 significant; especially all-elements in System, can become huge. For
182 a typical 50 page score, it requires running through a 100k list 50
187 forall p in properties:
190 put grob list in array,
192 reorder array so spanners are separate -- O(grobcount)
194 find first and last indexes of grobs on a specific system
196 for items this is O(itemcount)
198 for spanners this is O(sum-of spanner-system-ranges)
200 perform the substitution O(sum-of spanner-system-ranges)
203 The complexity is harder to determine, but should be subquadratic;
205 For the situation above, we run through the entire 100k list once,
206 and also (more or less) once through the item part of the 100k (say
207 98k elements) of the list.
210 These timings were measured without -O2.
212 lehre, before 28.98 seconds, after: 27.91 seconds, 3.5 %.
214 coriolan, before 2:30, after: 1:59. Increase of 20%.
216 moz-k498-p1, before 24.10, after: 19.790s, Increase of 18%
223 spanner_system_range (Spanner* sp)
227 if (System*st = sp->get_system())
229 rv = Slice (st->rank_, st->rank_);
233 if (sp->broken_intos_.size())
234 rv = Slice (sp->broken_intos_[0]->get_system()->rank_,
235 sp->broken_intos_.top()->get_system()->rank_);
241 item_system_range (Item* it)
243 if (System*st= it->get_system())
244 return Slice (st->rank_, st->rank_);
250 Item *bi = it->find_prebroken_piece (d);
251 if (bi && bi->get_system())
252 sr.add_point (bi->get_system()->rank_);
254 while (flip(&d)!=LEFT);
260 grob_system_range (Grob *g)
262 if (Spanner*s = dynamic_cast<Spanner*>(g))
263 return spanner_system_range (s);
264 else if (Item* it = dynamic_cast<Item*> (g))
265 return item_system_range (it);
272 struct Substitution_entry
278 void set (Grob*g, Slice sr)
282 duh, don't support scores with more than 32000 systems.
287 overflow if we don't treat this specially.
304 int length () { return right_ - left_ ; }
306 item_compare (void const * a , void const * b)
308 return ((Substitution_entry*)a)->left_ -
309 ((Substitution_entry*)b)->left_;
313 spanner_compare (void const * a , void const * b)
315 return ((Substitution_entry*)a)->length() -
316 ((Substitution_entry*)b)->length ();
323 Spanner::fast_fubstitute_grob_list (SCM sym,
326 int len = scm_ilength (grob_list);
329 Only do this complicated thing for large lists. This has the added
330 advantage that we won't screw up the ordering for elements in
331 alignments (which typically don't have more than 100 grobs.)
339 TODO : should not reallocate every time?
341 static Substitution_entry * vec;
346 vec = (Substitution_entry*) scm_realloc (vec, sizeof (Substitution_entry) * len);
350 Slice system_range = spanner_system_range (this);
352 Array<Slice> it_indices;
353 Array<Slice> sp_indices;
354 for (int i = 0; i <= system_range.length (); i++)
356 it_indices.push (Slice (len, 0));
357 sp_indices.push (Slice (len, 0));
363 for (SCM s = grob_list; gh_pair_p (s); s = gh_cdr (s))
365 Grob * g = unsmob_grob (gh_car(s));
367 Slice sr = grob_system_range (g);
368 sr.intersect (system_range);
371 if (dynamic_cast<Spanner*>(g))
375 else if (dynamic_cast<Item*> (g))
380 vec[idx].set (g, sr);
383 qsort (vec, it_index,
384 sizeof (Substitution_entry), &Substitution_entry::item_compare);
386 Array<Slice> *arrs[] = {
387 &it_indices, &sp_indices
390 for (int i = 0; i < it_index ;i++)
392 for (int j = vec[i].left_; j <= vec[i].right_; j++)
394 it_indices[j - system_range[LEFT]].add_point (i);
399 qsort (vec + sp_index, len - sp_index,
400 sizeof (Substitution_entry), &Substitution_entry::spanner_compare);
402 This is a waste of time -- the staff-spanners screw up the
403 ordering, since they go across the entire score.
405 for (int i = sp_index; i < len ;i++)
408 for (int j = vec[i].left_; j <= vec[i].right_; j++)
410 sp_indices[j - system_range[LEFT]].add_point (i);
414 for (int i = sp_indices.size(); i--;)
415 sp_indices[i]= Slice (sp_index, len-1);
419 assert (it_index <= sp_index);
421 assert (broken_intos_.size () == system_range.length () + 1);
422 for (int i = 0; i < broken_intos_.size(); i++)
424 Grob * sc = broken_intos_[i];
425 System * l = sc->get_system ();
426 set_break_subsititution (l ? l->self_scm(): SCM_UNDEFINED);
428 SCM newval = SCM_EOL;
429 SCM * tail = &newval;
431 for (int k = 0; k < 2;k++)
432 for (int j = (*arrs[k])[i][LEFT]; j <= (*arrs[k])[i][RIGHT]; j++)
434 SCM subs =substitute_grob (vec[j].grob_);
435 if (subs!= SCM_UNDEFINED)
437 *tail = scm_cons (subs, SCM_EOL);
439 tail = SCM_CDRLOC(*tail);
446 printf ("%d (%d), sp %d (%d)\n",
447 it_indices [i].length (), it_index,
448 sp_indices[i].length() , len -sp_index);
451 SCM l1 =substitute_grob_list (grob_list);
452 assert (scm_ilength (l1) == scm_ilength (newval));
456 sc->mutable_property_alist_ = scm_acons (sym, newval,
457 sc->mutable_property_alist_);
467 Although the substitution can be written as
469 property_alist = do_substitution (other_property_alist),
471 we have a special function here: we want to invoke a special
472 function for lists of grobs. These can be very long for large
473 orchestral scores (eg. 1M elements). do_break_substitution() can
474 recurse many levels, taking lots of stack space.
476 This becomes a problem if lily is linked against guile with
477 pthreads. pthreads impose small limits on the stack size.
480 substitute_mutable_property_alist (SCM alist)
483 grob_list_p = scm_c_eval_string ("grob-list?");
487 for (SCM s = alist; gh_pair_p (s); s = gh_cdr (s))
489 SCM sym = gh_caar(s);
490 SCM val = gh_cdar(s);
491 SCM type = scm_object_property (sym, ly_symbol2scm ("backend-type?"));
493 if (type == grob_list_p)
494 val = substitute_grob_list (val);
496 val = do_break_substitution (val);
498 *tail = gh_cons (gh_cons (sym, val), SCM_EOL);
499 tail = SCM_CDRLOC (*tail);
507 Spanner::substitute_one_mutable_property (SCM sym,
510 SCM type = scm_object_property (sym, ly_symbol2scm ("backend-type?"));
513 bool fast_done = false;
514 if (type == grob_list_p)
515 fast_done = s->fast_fubstitute_grob_list (sym, val);
518 for (int i = 0; i < s->broken_intos_ .size (); i++)
520 Grob * sc = s->broken_intos_[i];
521 System * l = sc->get_system ();
522 set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
524 SCM newval = (type == grob_list_p)
525 ? substitute_grob_list (val)
526 : do_break_substitution(val);
528 sc->mutable_property_alist_ = scm_cons (scm_cons (sym, newval),
529 sc->mutable_property_alist_);