2 score-elem.cc -- implement Score_element
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
13 #include "input-smob.hh"
14 #include "libc-extension.hh"
15 #include "group-interface.hh"
17 #include "paper-score.hh"
18 #include "paper-def.hh"
20 #include "molecule.hh"
21 #include "score-element.hh"
24 #include "line-of-score.hh"
26 #include "paper-column.hh"
27 #include "molecule.hh"
29 #include "paper-outputter.hh"
30 #include "dimension-cache.hh"
31 #include "side-position-interface.hh"
37 remove dynamic_cast<Spanner,Item> and put this code into respective
42 #define INFINITY_MSG "Infinity or NaN encountered"
44 Score_element::Score_element(SCM basicprops
)
46 set_extent_callback (molecule_extent
, X_AXIS
);
47 set_extent_callback (molecule_extent
, Y_AXIS
);
54 immutable_property_alist_
= basicprops
;
55 mutable_property_alist_
= SCM_EOL
;
58 set_elt_property ("dependencies", SCM_EOL
);
60 if (get_elt_property ("interfaces") == SCM_UNDEFINED
)
61 set_elt_property ("interfaces", SCM_EOL
);
65 Score_element::Score_element (Score_element
const&s
)
66 : dim_cache_ (s
.dim_cache_
)
69 original_l_
=(Score_element
*) &s
;
70 immutable_property_alist_
= s
.immutable_property_alist_
;
71 mutable_property_alist_
= SCM_EOL
;
73 status_i_
= s
.status_i_
;
74 lookup_l_
= s
.lookup_l_
;
75 pscore_l_
= s
.pscore_l_
;
80 Score_element::~Score_element()
83 do nothing scm-ish and no unprotecting here.
89 Score_element::get_elt_property (const char *nm
) const
91 SCM sym
= ly_symbol2scm (nm
);
92 return get_elt_property (sym
);
96 Score_element::get_elt_property (SCM sym
) const
98 SCM s
= scm_sloppy_assq(sym
, mutable_property_alist_
);
102 s
= scm_sloppy_assq (sym
, immutable_property_alist_
);
103 return (s
== SCM_BOOL_F
) ? SCM_EOL
: gh_cdr (s
);
107 Remove the value associated with KEY, and return it. The result is
108 that a next call will yield SCM_UNDEFINED (and not the underlying
112 Score_element::remove_elt_property (const char* key
)
114 SCM val
= get_elt_property (key
);
116 set_elt_property (key
, SCM_EOL
);
121 Score_element::set_elt_property (const char* k
, SCM v
)
123 SCM s
= ly_symbol2scm (k
);
124 set_elt_property (s
, v
);
128 Puts the k, v in the immutable_property_alist_, which is convenient for
129 storing variables that are needed during the breaking process. (eg.
130 Line_of_score::rank : int )
133 Score_element::set_immutable_elt_property (const char*k
, SCM v
)
135 SCM s
= ly_symbol2scm (k
);
136 set_immutable_elt_property (s
, v
);
140 Score_element::set_immutable_elt_property (SCM s
, SCM v
)
142 immutable_property_alist_
= gh_cons (gh_cons (s
,v
), mutable_property_alist_
);
143 mutable_property_alist_
= scm_assq_remove_x (mutable_property_alist_
, s
);
146 Score_element::set_elt_property (SCM s
, SCM v
)
148 mutable_property_alist_
= scm_assq_set_x (mutable_property_alist_
, s
, v
);
153 Score_element::molecule_extent (Score_element
*s
, Axis a
)
155 Molecule m
= s
->get_molecule ();
160 Score_element::preset_extent (Score_element
*s
, Axis a
)
162 SCM ext
= s
->get_elt_property ((a
== X_AXIS
)
168 Real l
= gh_scm2double (gh_car (ext
));
169 Real r
= gh_scm2double (gh_cdr (ext
));
170 l
*= s
->paper_l ()->get_var ("staffspace");
171 r
*= s
->paper_l ()->get_var ("staffspace");
172 return Interval (l
, r
);
181 Score_element::paper_l () const
183 return pscore_l_
? pscore_l_
->paper_l_
: 0;
187 Score_element::lookup_l () const
191 Score_element
* urg
= (Score_element
*)this;
192 SCM sz
= urg
->remove_elt_property ("fontsize");
193 int i
= (gh_number_p (sz
))
197 urg
->lookup_l_
= (Lookup
*)pscore_l_
->paper_l_
->lookup_l (i
);
203 Score_element::add_processing()
205 assert (status_i_
>=0);
214 Score_element::calculate_dependencies (int final
, int busy
, SCM funcname
)
216 assert (status_i_
>=0);
218 if (status_i_
>= final
)
221 if (status_i_
== busy
)
223 programming_error ("Element is busy, come back later");
229 for (SCM d
= get_elt_property ("dependencies"); gh_pair_p (d
); d
= gh_cdr (d
))
231 unsmob_element (gh_car (d
))
232 ->calculate_dependencies (final
, busy
, funcname
);
236 String s
= ly_symbol2string (funcname
);
237 SCM proc
= get_elt_property (s
.ch_C());
238 if (gh_procedure_p (proc
))
239 gh_call1 (proc
, this->self_scm_
);
246 Score_element::get_molecule () const
248 SCM proc
= get_elt_property ("molecule-callback");
251 if (gh_procedure_p (proc
))
252 mol
= gh_apply (proc
, gh_list (this->self_scm_
, SCM_UNDEFINED
));
255 SCM origin
=get_elt_property ("origin");
256 if (!unsmob_input (origin
))
257 origin
=ly_symbol2scm ("no-origin");
262 mol
= gh_cons (gh_list (origin
, gh_car (mol
), SCM_UNDEFINED
), gh_cdr (mol
));
266 return create_molecule (mol
);
276 Score_element::do_break_processing()
282 Score_element::do_add_processing()
287 MAKE_SCHEME_CALLBACK(Score_element
,brew_molecule
)
293 Score_element::brew_molecule (SCM smob
)
295 Score_element
* sc
= unsmob_element (smob
);
296 SCM glyph
= sc
->get_elt_property ("glyph");
297 if (gh_string_p (glyph
))
299 return sc
->lookup_l ()->afm_find (String (ly_scm2string (glyph
))).create_scheme ();
309 Score_element::line_l() const
315 Score_element::add_dependency (Score_element
*e
)
319 Pointer_group_interface
gi (this, "dependencies");
323 programming_error ("Null dependency added");
330 Do break substitution in S, using CRITERION. Return new value.
331 CRITERION is either a SMOB pointer to the desired line, or a number
332 representing the break direction. Do not modify SRC.
335 Score_element::handle_broken_smobs (SCM src
, SCM criterion
)
338 Score_element
*sc
= unsmob_element (src
);
341 if (gh_number_p (criterion
))
343 Item
* i
= dynamic_cast<Item
*> (sc
);
344 Direction d
= to_dir (criterion
);
345 if (i
&& i
->break_status_dir () != d
)
347 Item
*br
= i
->find_prebroken_piece (d
);
348 return (br
) ? br
->self_scm_
: SCM_UNDEFINED
;
354 = dynamic_cast<Line_of_score
*> (unsmob_element (criterion
));
355 if (sc
->line_l () != line
)
357 sc
= sc
->find_broken_piece (line
);
361 /* now: !sc || (sc && sc->line_l () == line) */
363 return SCM_UNDEFINED
;
365 /* now: sc && sc->line_l () == line */
367 || (sc
->common_refpoint (line
, X_AXIS
)
368 && sc
->common_refpoint (line
, Y_AXIS
)))
370 return sc
->self_scm_
;
372 return SCM_UNDEFINED
;
375 else if (gh_pair_p (src
))
377 SCM oldcar
=gh_car (src
);
379 UGH! breaks on circular lists.
381 SCM newcar
= handle_broken_smobs (oldcar
, criterion
);
382 SCM oldcdr
= gh_cdr (src
);
384 if (newcar
== SCM_UNDEFINED
385 && (gh_pair_p (oldcdr
) || oldcdr
== SCM_EOL
))
388 This is tail-recursion, ie.
390 return handle_broken_smobs (cdr, criterion);
392 We don't want to rely on the compiler to do this. Without
393 tail-recursion, this easily crashes with a stack overflow. */
398 SCM newcdr
= handle_broken_smobs (oldcdr
, criterion
);
399 return gh_cons (newcar
, newcdr
);
408 Score_element::handle_broken_dependencies()
410 Spanner
* s
= dynamic_cast<Spanner
*> (this);
411 if (original_l_
&& s
)
416 for (int i
= 0; i
< s
->broken_into_l_arr_
.size (); i
++)
418 Score_element
* sc
= s
->broken_into_l_arr_
[i
];
419 Line_of_score
* l
= sc
->line_l ();
420 sc
->mutable_property_alist_
=
421 handle_broken_smobs (mutable_property_alist_
,
422 l
? l
->self_scm_
: SCM_UNDEFINED
);
427 Line_of_score
*line
= line_l();
429 if (line
&& common_refpoint (line
, X_AXIS
) && common_refpoint (line
, Y_AXIS
))
431 mutable_property_alist_
432 = handle_broken_smobs (mutable_property_alist_
,
433 line
? line
->self_scm_
: SCM_UNDEFINED
);
435 else if (dynamic_cast <Line_of_score
*> (this))
437 mutable_property_alist_
= handle_broken_smobs (mutable_property_alist_
,
443 This element is `invalid'; it has been removed from all
444 dependencies, so let's junk the element itself.
446 do not do this for Line_of_score, since that would remove
447 references to the originals of score-elts, which get then GC'd
455 Note that we still want references to this element to be
456 rearranged, and not silently thrown away, so we keep pointers
457 like {broken_into_{drul,array}, original}
460 Score_element::suicide ()
462 mutable_property_alist_
= SCM_EOL
;
463 immutable_property_alist_
= SCM_EOL
;
464 set_extent_callback (0, Y_AXIS
);
465 set_extent_callback (0, X_AXIS
);
467 for (int a
= X_AXIS
; a
<= Y_AXIS
; a
++)
469 dim_cache_
[a
].off_callbacks_
.clear ();
474 Score_element::handle_prebroken_dependencies()
479 Score_element::find_broken_piece (Line_of_score
*) const
485 Score_element::translate_axis (Real y
, Axis a
)
487 if (isinf (y
) || isnan (y
))
488 programming_error (_(INFINITY_MSG
));
491 dim_cache_
[a
].offset_
+= y
;
496 Score_element::relative_coordinate (Score_element
const*refp
, Axis a
) const
502 We catch PARENT_L_ == nil case with this, but we crash if we did
503 not ask for the absolute coordinate (ie. REFP == nil.)
506 if (refp
== dim_cache_
[a
].parent_l_
)
507 return get_offset (a
);
509 return get_offset (a
) + dim_cache_
[a
].parent_l_
->relative_coordinate (refp
, a
);
513 Score_element::get_offset (Axis a
) const
515 Score_element
*me
= (Score_element
*) this;
516 while (dim_cache_
[a
].off_callbacks_
.size ())
518 Offset_callback c
= dim_cache_
[a
].off_callbacks_
[0];
519 me
->dim_cache_
[a
].off_callbacks_
.del (0);
520 Real r
= (*c
) (me
,a
);
521 if (isinf (r
) || isnan (r
))
524 programming_error (INFINITY_MSG
);
526 me
->dim_cache_
[a
].offset_
+=r
;
528 return dim_cache_
[a
].offset_
;
533 Score_element::point_dimension_callback (Score_element
* , Axis
)
535 return Interval (0,0);
539 Score_element::empty_b (Axis a
)const
541 return !dim_cache_
[a
].extent_callback_l_
;
545 Score_element::extent (Axis a
) const
547 Dimension_cache
* d
= (Dimension_cache
*)&dim_cache_
[a
];
548 if (!d
->extent_callback_l_
)
550 d
->dim_
.set_empty ();
552 else if (!d
->valid_b_
)
554 d
->dim_
= (*d
->extent_callback_l_
) ((Score_element
*)this, a
);
558 Interval ext
= d
->dim_
;
563 SCM extra
= get_elt_property (a
== X_AXIS
570 Real s
= paper_l ()->get_var ("staffspace");
571 if (gh_pair_p (extra
))
573 ext
[BIGGER
] += s
* gh_scm2double (gh_cdr (extra
));
574 ext
[SMALLER
] += s
* gh_scm2double (gh_car (extra
));
577 extra
= get_elt_property (a
== X_AXIS
579 : "minimum-extent-Y");
580 if (gh_pair_p (extra
))
582 ext
.unite (Interval (s
* gh_scm2double (gh_car (extra
)),
583 s
* gh_scm2double (gh_cdr (extra
))));
591 Score_element::parent_l (Axis a
) const
593 return dim_cache_
[a
].parent_l_
;
597 Score_element::common_refpoint (Score_element
const* s
, Axis a
) const
600 I don't like the quadratic aspect of this code, but I see no other
601 way. The largest chain of parents might be 10 high or so, so
602 it shouldn't be a real issue. */
603 for (Score_element
const *c
= this; c
; c
= c
->dim_cache_
[a
].parent_l_
)
604 for (Score_element
const * d
= s
; d
; d
= d
->dim_cache_
[a
].parent_l_
)
606 return (Score_element
*)d
;
613 Score_element::common_refpoint (SCM elist
, Axis a
) const
615 Score_element
* common
= (Score_element
*) this;
616 for (; gh_pair_p (elist
); elist
= gh_cdr (elist
))
618 Score_element
* s
= unsmob_element (gh_car (elist
));
620 common
= common
->common_refpoint (s
, a
);
627 Score_element::name () const
629 return classname (this);
633 Score_element::add_offset_callback (Offset_callback cb
, Axis a
)
635 dim_cache_
[a
].off_callbacks_
.push (cb
);
639 Score_element::has_extent_callback_b (Extent_callback cb
, Axis a
)const
641 return cb
== dim_cache_
[a
].extent_callback_l_
;
645 Score_element::has_offset_callback_b (Offset_callback cb
, Axis a
)const
647 for (int i
= dim_cache_
[a
].off_callbacks_
.size (); i
--;)
649 if (dim_cache_
[a
].off_callbacks_
[i
] == cb
)
656 Score_element::set_extent_callback (Dim_cache_callback dc
, Axis a
)
658 dim_cache_
[a
].extent_callback_l_
= dc
;
663 Score_element::set_parent (Score_element
*g
, Axis a
)
665 dim_cache_
[a
].parent_l_
= g
;
668 MAKE_SCHEME_CALLBACK(Score_element
,fixup_refpoint
);
670 Score_element::fixup_refpoint (SCM smob
)
672 Score_element
*me
= unsmob_element (smob
);
673 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
676 Score_element
* parent
= me
->parent_l (ax
);
681 if (parent
->line_l () != me
->line_l () && me
->line_l ())
683 Score_element
* newparent
= parent
->find_broken_piece (me
->line_l ());
684 me
->set_parent (newparent
, ax
);
687 if (Item
* i
= dynamic_cast<Item
*> (me
))
689 Item
*parenti
= dynamic_cast<Item
*> (parent
);
693 Direction my_dir
= i
->break_status_dir () ;
694 if (my_dir
!= parenti
->break_status_dir())
696 Item
*newparent
= parenti
->find_prebroken_piece (my_dir
);
697 me
->set_parent (newparent
, ax
);
707 /****************************************************
709 ****************************************************/
711 #include "ly-smobs.icc"
713 IMPLEMENT_UNSMOB(Score_element
, element
);
714 IMPLEMENT_SMOBS(Score_element
);
717 Score_element::mark_smob (SCM ses
)
719 Score_element
* s
= SMOB_TO_TYPE (Score_element
, ses
);
720 if (s
->self_scm_
!= ses
)
722 programming_error ("SMOB marking gone awry");
725 scm_gc_mark (s
->immutable_property_alist_
);
726 scm_gc_mark (s
->mutable_property_alist_
);
728 if (s
->parent_l (Y_AXIS
))
729 scm_gc_mark (s
->parent_l (Y_AXIS
)->self_scm_
);
730 if (s
->parent_l (X_AXIS
))
731 scm_gc_mark (s
->parent_l (X_AXIS
)->self_scm_
);
734 scm_gc_mark (s
->original_l_
->self_scm_
);
735 return s
->do_derived_mark ();
739 Score_element::print_smob (SCM s
, SCM port
, scm_print_state
*)
741 Score_element
*sc
= (Score_element
*) gh_cdr (s
);
743 scm_puts ("#<Score_element ", port
);
744 scm_puts ((char *)sc
->name (), port
);
747 don't try to print properties, that is too much hassle.
749 scm_puts (" >", port
);
754 Score_element::do_derived_mark ()
760 Score_element::do_smobify_self ()
765 Score_element::equal_p (SCM a
, SCM b
)
767 return gh_cdr(a
) == gh_cdr(b
) ? SCM_BOOL_T
: SCM_BOOL_F
;
772 ly_set_elt_property (SCM elt
, SCM sym
, SCM val
)
774 Score_element
* sc
= unsmob_element (elt
);
776 if (!gh_symbol_p (sym
))
778 error ("Not a symbol");
779 ly_display_scm (sym
);
780 return SCM_UNSPECIFIED
;
785 sc
->set_elt_property (sym
, val
);
789 error ("Not a score element");
790 ly_display_scm (elt
);
793 return SCM_UNSPECIFIED
;
798 ly_get_elt_property (SCM elt
, SCM sym
)
800 Score_element
* sc
= unsmob_element (elt
);
804 return sc
->get_elt_property (sym
);
808 error ("Not a score element");
809 ly_display_scm (elt
);
811 return SCM_UNSPECIFIED
;
816 Score_element::discretionary_processing()
823 spanner_get_bound (SCM slur
, SCM dir
)
825 return dynamic_cast<Spanner
*> (unsmob_element (slur
))->get_bound (to_dir (dir
))->self_scm_
;
830 static SCM interfaces_sym
;
835 interfaces_sym
= scm_permanent_object (ly_symbol2scm ("interfaces"));
837 scm_make_gsubr ("ly-get-elt-property", 2, 0, 0, (SCM(*)(...))ly_get_elt_property
);
838 scm_make_gsubr ("ly-set-elt-property", 3, 0, 0, (SCM(*)(...))ly_set_elt_property
);
839 scm_make_gsubr ("ly-get-spanner-bound", 2 , 0, 0, (SCM(*)(...)) spanner_get_bound
);
843 Score_element::has_interface (SCM k
)
845 if (mutable_property_alist_
== SCM_EOL
)
848 SCM ifs
= get_elt_property (interfaces_sym
);
850 return scm_memq (k
, ifs
) != SCM_BOOL_F
;
854 Score_element::set_interface (SCM k
)
856 if (has_interface (k
))
860 set_elt_property (interfaces_sym
,
861 gh_cons (k
, get_elt_property (interfaces_sym
)));
866 ADD_SCM_INIT_FUNC(scoreelt
, init_functions
);