9 static SCM break_criterion
;
11 set_break_subsititution (SCM criterion
)
13 break_criterion
= criterion
;
17 Perform the substitution for a single grob.
20 substitute_grob (Grob
*sc
)
22 if (SCM_INUMP (break_criterion
))
24 Item
* i
= dynamic_cast<Item
*> (sc
);
25 Direction d
= to_dir (break_criterion
);
26 if (i
&& i
->break_status_dir () != d
)
28 Item
*br
= i
->find_prebroken_piece (d
);
29 return (br
) ? br
->self_scm () : SCM_UNDEFINED
;
35 = dynamic_cast<System
*> (unsmob_grob (break_criterion
));
36 if (sc
->get_system () != line
)
38 sc
= sc
->find_broken_piece (line
);
42 /* now: !sc || (sc && sc->get_system () == line) */
46 /* now: sc && sc->get_system () == line */
48 return sc
->self_scm ();
51 We don't return SCM_UNDEFINED for
52 suicided grobs, for two reasons
54 - it doesn't work (strange disappearing objects)
56 - it forces us to mark the parents of a grob, leading to
57 a huge recursion in the GC routine.
60 if (sc
->common_refpoint (line
, X_AXIS
)
61 && sc
->common_refpoint (line
, Y_AXIS
))
63 return sc
->self_scm ();
68 return sc
->self_scm ();
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
))
93 return substitute_grob (unsmob_grob (src
));
94 else if (ly_c_vector_p (src
))
96 int len
= SCM_VECTOR_LENGTH (src
);
97 SCM nv
= scm_c_make_vector (len
, SCM_UNDEFINED
);
98 for (int i
= 0; i
< len
; i
++)
100 SCM si
= scm_int2num (i
);
101 scm_vector_set_x (nv
, si
,
102 do_break_substitution (scm_vector_ref (src
, si
)));
105 else if (ly_c_pair_p (src
))
108 UGH! breaks on circular lists.
110 SCM newcar
= do_break_substitution (ly_car (src
));
111 SCM oldcdr
= ly_cdr (src
);
113 if (newcar
== SCM_UNDEFINED
114 && (ly_c_pair_p (oldcdr
) || oldcdr
== SCM_EOL
))
117 This is tail-recursion, ie.
119 return do_break_substution (cdr);
121 We don't want to rely on the compiler to do this. Without
122 tail-recursion, this easily crashes with a stack overflow. */
127 return scm_cons (newcar
, do_break_substitution (oldcdr
));
137 Perform substitution on GROB_LIST using a constant amount of stack.
140 substitute_grob_list (SCM grob_list
)
145 for (SCM s
= grob_list
; ly_c_pair_p (s
); s
= ly_cdr (s
))
147 SCM n
= substitute_grob (unsmob_grob (ly_car (s
)));
149 if (n
!= SCM_UNDEFINED
)
151 *tail
= scm_cons (n
, SCM_EOL
);
152 tail
= SCM_CDRLOC (*tail
);
162 forall b in broken-childs:
163 forall p in properties:
164 forall g in p (if grob-list):
167 for spanners since this is O (SYSTEMCOUNT * GROBCOUNT), and SYSTEMCOUNT =
168 O (GROBCOUNT), we have a quadratic algorithm. --for a single spanner
170 This is problematic: with large (long) scores, the costs can be
171 significant; especially all-elements in System, can become huge. For
172 a typical 50 page score, it requires running through a 100k list 50
177 forall p in properties:
180 put grob list in array,
182 reorder array so spanners are separate -- O (grobcount)
184 find first and last indexes of grobs on a specific system
186 for items this is O (itemcount)
188 for spanners this is O (sum-of spanner-system-ranges)
190 perform the substitution O (sum-of spanner-system-ranges)
193 The complexity is harder to determine, but should be subquadratic;
195 For the situation above, we run through the entire 100k list once,
196 and also (more or less) once through the item part of the 100k (say
197 98k elements) of the list.
200 These timings were measured without -O2.
202 lehre, before 28.98 seconds, after: 27.91 seconds, 3.5 %.
204 coriolan, before 2:30, after: 1:59. Increase of 20%.
206 moz-k498-p1, before 24.10, after: 19.790s, Increase of 18%
213 spanner_system_range (Spanner
* sp
)
217 if (System
*st
= sp
->get_system ())
219 rv
= Slice (st
->rank_
, st
->rank_
);
223 if (sp
->broken_intos_
.size ())
224 rv
= Slice (sp
->broken_intos_
[0]->get_system ()->rank_
,
225 sp
->broken_intos_
.top ()->get_system ()->rank_
);
231 item_system_range (Item
* it
)
233 if (System
*st
= it
->get_system ())
234 return Slice (st
->rank_
, st
->rank_
);
240 Item
*bi
= it
->find_prebroken_piece (d
);
241 if (bi
&& bi
->get_system ())
242 sr
.add_point (bi
->get_system ()->rank_
);
244 while (flip (&d
)!=LEFT
);
250 grob_system_range (Grob
*g
)
252 if (Spanner
*s
= dynamic_cast<Spanner
*>(g
))
253 return spanner_system_range (s
);
254 else if (Item
* it
= dynamic_cast<Item
*> (g
))
255 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 ();
313 Spanner::fast_fubstitute_grob_list (SCM sym
,
316 int len
= scm_ilength (grob_list
);
319 Only do this complicated thing for large lists. This has the added
320 advantage that we won't screw up the ordering for elements in
321 alignments (which typically don't have more than 10 grobs.)
329 TODO : should not free it some time?
331 static Substitution_entry
* vec
;
336 vec
= (Substitution_entry
*) realloc (vec
, sizeof (Substitution_entry
) * len
);
340 Slice system_range
= spanner_system_range (this);
342 Array
<Slice
> it_indices
;
343 Array
<Slice
> sp_indices
;
344 for (int i
= 0; i
<= system_range
.length (); i
++)
346 it_indices
.push (Slice (len
, 0));
347 sp_indices
.push (Slice (len
, 0));
353 for (SCM s
= grob_list
; ly_c_pair_p (s
); s
= ly_cdr (s
))
355 Grob
* g
= unsmob_grob (ly_car (s
));
357 Slice sr
= grob_system_range (g
);
358 sr
.intersect (system_range
);
361 if (dynamic_cast<Spanner
*>(g
))
365 else if (dynamic_cast<Item
*> (g
))
370 vec
[idx
].set (g
, sr
);
373 qsort (vec
, it_index
,
374 sizeof (Substitution_entry
), &Substitution_entry::item_compare
);
376 Array
<Slice
> *arrs
[] = {
377 &it_indices
, &sp_indices
380 for (int i
= 0; i
< it_index
;i
++)
382 for (int j
= vec
[i
].left_
; j
<= vec
[i
].right_
; j
++)
384 it_indices
[j
- system_range
[LEFT
]].add_point (i
);
389 sorting vec[sp_index.. len]
390 is a waste of time -- the staff-spanners screw up the
391 ordering, since they go across the entire score.
393 for (int i
= sp_indices
.size (); i
--;)
394 sp_indices
[i
]= Slice (sp_index
, len
-1);
397 assert (it_index
<= sp_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
= SCM_EOL
;
407 SCM
* tail
= &newval
;
409 for (int k
= 0; k
< 2;k
++)
410 for (int j
= (*arrs
[k
])[i
][LEFT
]; j
<= (*arrs
[k
])[i
][RIGHT
]; j
++)
412 SCM subs
=substitute_grob (vec
[j
].grob_
);
413 if (subs
!= SCM_UNDEFINED
)
415 *tail
= scm_cons (subs
, SCM_EOL
);
417 tail
= SCM_CDRLOC (*tail
);
424 printf ("%d (%d), sp %d (%d)\n",
425 it_indices
[i
].length (), it_index
,
426 sp_indices
[i
].length () , len
-sp_index
);
429 SCM l1
=substitute_grob_list (grob_list
);
430 assert (scm_ilength (l1
) == scm_ilength (newval
));
437 if (sym
== ly_symbol2scm ("all-elements"))
438 sc
->mutable_property_alist_
439 = scm_assq_remove_x (sc
->mutable_property_alist_
,
440 ly_symbol2scm ("all-elements"));
442 sc
->mutable_property_alist_
= scm_acons (sym
, newval
,
443 sc
->mutable_property_alist_
);
451 Although the substitution can be written as
453 property_alist = do_substitution (other_property_alist),
455 we have a special function here: we want to invoke a special
456 function for lists of grobs. These can be very long for large
457 orchestral scores (eg. 1M elements). do_break_substitution () can
458 recurse many levels, taking lots of stack space.
460 This becomes a problem if lily is linked against guile with
461 pthreads. pthreads impose small limits on the stack size.
464 substitute_mutable_property_alist (SCM alist
)
466 SCM grob_list_p
= ly_scheme_function ("grob-list?");
470 for (SCM s
= alist
; ly_c_pair_p (s
); s
= ly_cdr (s
))
472 SCM sym
= ly_caar (s
);
473 SCM val
= ly_cdar (s
);
474 SCM type
= scm_object_property (sym
, ly_symbol2scm ("backend-type?"));
476 if (type
== grob_list_p
)
477 val
= substitute_grob_list (val
);
479 val
= do_break_substitution (val
);
481 *tail
= scm_cons (scm_cons (sym
, val
), SCM_EOL
);
482 tail
= SCM_CDRLOC (*tail
);
490 Spanner::substitute_one_mutable_property (SCM sym
,
493 SCM type
= scm_object_property (sym
, ly_symbol2scm ("backend-type?"));
496 bool fast_done
= false;
497 SCM grob_list_p
= ly_scheme_function ("grob-list?");
498 if (type
== grob_list_p
)
499 fast_done
= s
->fast_fubstitute_grob_list (sym
, val
);
502 for (int i
= 0; i
< s
->broken_intos_
.size (); i
++)
504 Grob
* sc
= s
->broken_intos_
[i
];
505 System
* l
= sc
->get_system ();
506 set_break_subsititution (l
? l
->self_scm () : SCM_UNDEFINED
);
508 SCM newval
= (type
== grob_list_p
)
509 ? substitute_grob_list (val
)
510 : do_break_substitution (val
);
513 For the substitution of a single property, we tack the result onto
514 mutable_property_alist_ ; mutable_property_alist_ is empty after
515 Grob::Grob (Grob const&), except that System has all-elements set,
516 as a side product of typeset_grob () on newly copied spanners.
518 Here we clear that list explicitly to free some memory and
519 counter some of the confusion I encountered while debugging
524 if (sym
== ly_symbol2scm ("all-elements"))
525 sc
->mutable_property_alist_
526 = scm_assq_remove_x (sc
->mutable_property_alist_
,
527 ly_symbol2scm ("all-elements"));
529 sc
->mutable_property_alist_
= scm_cons (scm_cons (sym
, newval
),
530 sc
->mutable_property_alist_
);