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 "libc-extension.hh"
14 #include "group-interface.hh"
16 #include "paper-score.hh"
17 #include "paper-def.hh"
19 #include "molecule.hh"
20 #include "score-element.hh"
23 #include "line-of-score.hh"
25 #include "paper-column.hh"
26 #include "molecule.hh"
28 #include "paper-outputter.hh"
29 #include "dimension-cache.hh"
30 #include "side-position-interface.hh"
36 remove dynamic_cast<Spanner,Item> and put this code into respective
41 #define INFINITY_MSG "Infinity or NaN encountered"
43 Score_element::Score_element(SCM basicprops
)
45 set_extent_callback (molecule_extent
, X_AXIS
);
46 set_extent_callback (molecule_extent
, Y_AXIS
);
53 property_alist_
= basicprops
;
54 pointer_alist_
= SCM_EOL
;
57 set_elt_pointer ("dependencies", SCM_EOL
);
58 set_elt_property ("interfaces", SCM_EOL
);
62 Score_element::Score_element (Score_element
const&s
)
63 : dim_cache_ (s
.dim_cache_
)
66 original_l_
=(Score_element
*) &s
;
67 property_alist_
= s
.property_alist_
;
69 pointer_alist_
= SCM_EOL
;
71 status_i_
= s
.status_i_
;
72 lookup_l_
= s
.lookup_l_
;
73 pscore_l_
= s
.pscore_l_
;
78 Score_element::~Score_element()
80 // this goes awry when score-elements are copied...
83 Kijk goed naar hoe smobify en unsmobify werken. unsmobify_self
84 is te gebruiken wanneer C++ geheugen beheer weer overneemt van
90 Score_element::get_elt_pointer (const char *nm
) const
92 SCM sym
= ly_symbol2scm (nm
);
93 SCM s
= scm_assq(sym
, pointer_alist_
);
95 return (s
== SCM_BOOL_F
) ? SCM_UNDEFINED
: gh_cdr (s
);
98 // should also have one that takes SCM arg.
100 Score_element::get_elt_property (String nm
) const
102 SCM sym
= ly_symbol2scm (nm
.ch_C());
103 SCM s
= scm_assq(sym
, property_alist_
);
108 return SCM_UNDEFINED
;
112 Remove the value associated with KEY, and return it. The result is
113 that a next call will yield SCM_UNDEFINED (and not the underlying
117 Score_element::remove_elt_property (const char* key
)
119 SCM val
= get_elt_property (key
);
120 if (val
!= SCM_UNDEFINED
)
121 set_elt_property (key
, SCM_UNDEFINED
);
126 Score_element::set_elt_property (String k
, SCM val
)
128 SCM sym
= ly_symbol2scm (k
.ch_C ());
129 property_alist_
= gh_cons (gh_cons (sym
, val
), property_alist_
);
133 Score_element::set_elt_pointer (const char* k
, SCM v
)
135 SCM s
= ly_symbol2scm (k
);
136 pointer_alist_
= scm_assq_set_x (pointer_alist_
, s
, v
);
141 Score_element::molecule_extent (Score_element
const *s
, Axis a
)
143 Molecule m
= s
->get_molecule ();
148 Score_element::preset_extent (Score_element
const *s
, Axis a
)
150 SCM ext
= s
->get_elt_property ((a
== X_AXIS
)
156 Real l
= gh_scm2double (gh_car (ext
));
157 Real r
= gh_scm2double (gh_cdr (ext
));
158 l
*= s
->paper_l ()->get_var ("staffspace");
159 r
*= s
->paper_l ()->get_var ("staffspace");
160 return Interval (l
, r
);
169 Score_element::paper_l () const
171 return pscore_l_
? pscore_l_
->paper_l_
: 0;
175 Score_element::lookup_l () const
179 Score_element
* urg
= (Score_element
*)this;
180 SCM sz
= urg
->remove_elt_property ("fontsize");
181 int i
= (gh_number_p (sz
))
185 urg
->lookup_l_
= (Lookup
*)pscore_l_
->paper_l_
->lookup_l (i
);
191 Score_element::add_processing()
193 assert (status_i_
>=0);
202 Score_element::calculate_dependencies (int final
, int busy
, SCM funcname
)
204 assert (status_i_
>=0);
206 if (status_i_
>= final
)
209 if (status_i_
== busy
)
211 programming_error ("Element is busy, come back later");
217 for (SCM d
= get_elt_pointer ("dependencies"); gh_pair_p (d
); d
= gh_cdr (d
))
219 unsmob_element (gh_car (d
))
220 ->calculate_dependencies (final
, busy
, funcname
);
224 String s
= ly_symbol2string (funcname
);
225 SCM proc
= get_elt_property (s
.ch_C());
226 if (gh_procedure_p (proc
))
227 gh_call1 (proc
, this->self_scm_
);
234 Score_element::get_molecule () const
236 SCM proc
= get_elt_property ("molecule-callback");
237 if (gh_procedure_p (proc
))
238 return create_molecule (gh_apply (proc
, gh_list (this->self_scm_
, SCM_UNDEFINED
)));
250 Score_element::do_break_processing()
255 Score_element::do_space_processing ()
260 Score_element::do_add_processing()
265 MAKE_SCHEME_SCORE_ELEMENT_CALLBACK(Score_element
,brew_molecule
)
271 Score_element::brew_molecule (SCM smob
)
273 Score_element
* sc
= unsmob_element (smob
);
274 SCM glyph
= sc
->get_elt_property ("glyph");
275 if (gh_string_p (glyph
))
277 return sc
->lookup_l ()->afm_find (String (ly_scm2string (glyph
))).create_scheme ();
287 Score_element::line_l() const
293 Score_element::add_dependency (Score_element
*e
)
297 Pointer_group_interface
gi (this, "dependencies");
301 programming_error ("Null dependency added");
308 Do break substitution in S, using CRITERION. Return new value.
309 CRITERION is either a SMOB pointer to the desired line, or a number
310 representing the break direction. Do not modify SRC.
313 Score_element::handle_broken_smobs (SCM src
, SCM criterion
)
316 Score_element
*sc
= unsmob_element (src
);
319 if (gh_number_p (criterion
))
321 Item
* i
= dynamic_cast<Item
*> (sc
);
322 Direction d
= to_dir (criterion
);
323 if (i
&& i
->break_status_dir () != d
)
325 Item
*br
= i
->find_prebroken_piece (d
);
326 return (br
) ? br
->self_scm_
: SCM_UNDEFINED
;
332 = dynamic_cast<Line_of_score
*> (unsmob_element (criterion
));
333 if (sc
->line_l () != line
)
335 sc
= sc
->find_broken_piece (line
);
339 /* now: !sc || (sc && sc->line_l () == line) */
341 return SCM_UNDEFINED
;
343 /* now: sc && sc->line_l () == line */
345 || (sc
->common_refpoint (line
, X_AXIS
)
346 && sc
->common_refpoint (line
, Y_AXIS
)))
348 return sc
->self_scm_
;
350 return SCM_UNDEFINED
;
353 else if (gh_pair_p (src
))
355 SCM oldcar
=gh_car (src
);
357 UGH! breaks on circular lists.
359 SCM newcar
= handle_broken_smobs (oldcar
, criterion
);
360 SCM oldcdr
= gh_cdr (src
);
362 if (newcar
== SCM_UNDEFINED
363 && (gh_pair_p (oldcdr
) || oldcdr
== SCM_EOL
))
366 This is tail-recursion, ie.
368 return handle_broken_smobs (cdr, criterion);
370 We don't want to rely on the compiler to do this. Without
371 tail-recursion, this easily crashes with a stack overflow. */
376 SCM newcdr
= handle_broken_smobs (oldcdr
, criterion
);
377 return gh_cons (newcar
, newcdr
);
386 Score_element::handle_broken_dependencies()
388 Spanner
* s
= dynamic_cast<Spanner
*> (this);
389 if (original_l_
&& s
)
394 for (int i
= 0; i
< s
->broken_into_l_arr_
.size (); i
++)
396 Score_element
* sc
= s
->broken_into_l_arr_
[i
];
397 Line_of_score
* l
= sc
->line_l ();
399 handle_broken_smobs (pointer_alist_
,
400 l
? l
->self_scm_
: SCM_UNDEFINED
);
405 Line_of_score
*line
= line_l();
407 if (line
&& common_refpoint (line
, X_AXIS
) && common_refpoint (line
, Y_AXIS
))
410 = handle_broken_smobs (pointer_alist_
,
411 line
? line
->self_scm_
: SCM_UNDEFINED
);
413 else if (dynamic_cast <Line_of_score
*> (this))
415 pointer_alist_
= handle_broken_smobs (pointer_alist_
,
421 This element is `invalid'; it has been removed from all
422 dependencies, so let's junk the element itself.
424 do not do this for Line_of_score, since that would remove
425 references to the originals of score-elts, which get then GC'd
433 Note that we still want references to this element to be
434 rearranged, and not silently thrown away, so we keep pointers
435 like {broken_into_{drul,array}, original}
438 Score_element::suicide ()
440 property_alist_
= SCM_EOL
;
441 pointer_alist_
= SCM_EOL
;
442 set_extent_callback (0, Y_AXIS
);
443 set_extent_callback (0, X_AXIS
);
445 for (int a
= X_AXIS
; a
<= Y_AXIS
; a
++)
447 dim_cache_
[a
].off_callbacks_
.clear ();
452 Score_element::handle_prebroken_dependencies()
457 Score_element::find_broken_piece (Line_of_score
*) const
463 Score_element::translate_axis (Real y
, Axis a
)
465 if (isinf (y
) || isnan (y
))
466 programming_error (_(INFINITY_MSG
));
469 dim_cache_
[a
].offset_
+= y
;
474 Score_element::relative_coordinate (Score_element
const*refp
, Axis a
) const
480 We catch PARENT_L_ == nil case with this, but we crash if we did
481 not ask for the absolute coordinate (ie. REFP == nil.)
484 if (refp
== dim_cache_
[a
].parent_l_
)
485 return get_offset (a
);
487 return get_offset (a
) + dim_cache_
[a
].parent_l_
->relative_coordinate (refp
, a
);
491 Score_element::get_offset (Axis a
) const
493 Score_element
*me
= (Score_element
*) this;
494 while (dim_cache_
[a
].off_callbacks_
.size ())
496 Offset_callback c
= dim_cache_
[a
].off_callbacks_
[0];
497 me
->dim_cache_
[a
].off_callbacks_
.del (0);
498 Real r
= (*c
) (me
,a
);
499 if (isinf (r
) || isnan (r
))
502 programming_error (INFINITY_MSG
);
504 me
->dim_cache_
[a
].offset_
+=r
;
506 return dim_cache_
[a
].offset_
;
511 Score_element::point_dimension_callback (Score_element
const* , Axis
)
513 return Interval (0,0);
517 Score_element::empty_b (Axis a
)const
519 return !dim_cache_
[a
].extent_callback_l_
;
523 Score_element::extent (Axis a
) const
525 Dimension_cache
* d
= (Dimension_cache
*)&dim_cache_
[a
];
526 if (!d
->extent_callback_l_
)
528 d
->dim_
.set_empty ();
530 else if (!d
->valid_b_
)
532 d
->dim_
= (*d
->extent_callback_l_
) (this, a
);
536 Interval ext
= d
->dim_
;
541 SCM extra
= get_elt_property (a
== X_AXIS
548 Real s
= paper_l ()->get_var ("staffspace");
549 if (gh_pair_p (extra
))
551 ext
[BIGGER
] += s
* gh_scm2double (gh_cdr (extra
));
552 ext
[SMALLER
] += s
* gh_scm2double (gh_car (extra
));
555 extra
= get_elt_property (a
== X_AXIS
557 : "minimum-extent-Y");
558 if (gh_pair_p (extra
))
560 ext
.unite (Interval (s
* gh_scm2double (gh_car (extra
)),
561 s
* gh_scm2double (gh_cdr (extra
))));
569 Score_element::parent_l (Axis a
) const
571 return dim_cache_
[a
].parent_l_
;
575 Score_element::common_refpoint (Score_element
const* s
, Axis a
) const
578 I don't like the quadratic aspect of this code, but I see no other
579 way. The largest chain of parents might be 10 high or so, so
580 it shouldn't be a real issue. */
581 for (Score_element
const *c
= this; c
; c
= c
->dim_cache_
[a
].parent_l_
)
582 for (Score_element
const * d
= s
; d
; d
= d
->dim_cache_
[a
].parent_l_
)
584 return (Score_element
*)d
;
591 Score_element::common_refpoint (SCM elist
, Axis a
) const
593 Score_element
* common
= (Score_element
*) this;
594 for (; gh_pair_p (elist
); elist
= gh_cdr (elist
))
596 Score_element
* s
= unsmob_element (gh_car (elist
));
598 common
= common
->common_refpoint (s
, a
);
605 Score_element::name () const
607 return classname (this);
611 Score_element::add_offset_callback (Offset_callback cb
, Axis a
)
613 dim_cache_
[a
].off_callbacks_
.push (cb
);
617 Score_element::has_extent_callback_b (Extent_callback cb
, Axis a
)const
619 return cb
== dim_cache_
[a
].extent_callback_l_
;
623 Score_element::has_offset_callback_b (Offset_callback cb
, Axis a
)const
625 for (int i
= dim_cache_
[a
].off_callbacks_
.size (); i
--;)
627 if (dim_cache_
[a
].off_callbacks_
[i
] == cb
)
634 Score_element::set_extent_callback (Dim_cache_callback dc
, Axis a
)
636 dim_cache_
[a
].extent_callback_l_
= dc
;
641 Score_element::set_parent (Score_element
*g
, Axis a
)
643 dim_cache_
[a
].parent_l_
= g
;
647 Score_element::fixup_refpoint ()
649 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
652 Score_element
* parent
= parent_l (ax
);
657 if (parent
->line_l () != line_l () && line_l ())
659 Score_element
* newparent
= parent
->find_broken_piece (line_l ());
660 set_parent (newparent
, ax
);
663 if (Item
* i
= dynamic_cast<Item
*> (this))
665 Item
*parenti
= dynamic_cast<Item
*> (parent
);
669 Direction my_dir
= i
->break_status_dir () ;
670 if (my_dir
!= parenti
->break_status_dir())
672 Item
*newparent
= parenti
->find_prebroken_piece (my_dir
);
673 set_parent (newparent
, ax
);
682 /****************************************************
684 ****************************************************/
686 #include "ly-smobs.icc"
688 IMPLEMENT_UNSMOB(Score_element
, element
);
689 IMPLEMENT_SMOBS(Score_element
);
692 Score_element::mark_smob (SCM ses
)
694 Score_element
* s
= SMOB_TO_TYPE (Score_element
, ses
);
695 if (s
->self_scm_
!= ses
)
697 programming_error ("SMOB marking gone awry");
700 scm_gc_mark (s
->pointer_alist_
);
702 s
->do_derived_mark ();
703 if (s
->parent_l (Y_AXIS
))
704 scm_gc_mark (s
->parent_l (Y_AXIS
)->self_scm_
);
705 if (s
->parent_l (X_AXIS
))
706 scm_gc_mark (s
->parent_l (X_AXIS
)->self_scm_
);
708 return s
->property_alist_
;
712 Score_element::print_smob (SCM s
, SCM port
, scm_print_state
*)
714 Score_element
*sc
= (Score_element
*) gh_cdr (s
);
716 scm_puts ("#<Score_element ", port
);
717 scm_puts ((char *)sc
->name (), port
);
720 don't try to print properties, that is too much hassle.
722 scm_puts (" >", port
);
727 Score_element::do_derived_mark ()
732 Score_element::do_smobify_self ()
737 Score_element::equal_p (SCM a
, SCM b
)
739 return gh_cdr(a
) == gh_cdr(b
) ? SCM_BOOL_T
: SCM_BOOL_F
;
744 Score_element::ly_set_elt_property (SCM elt
, SCM sym
, SCM val
)
746 Score_element
* sc
= unsmob_element (elt
);
748 if (!gh_symbol_p (sym
))
750 error ("Not a symbol");
751 ly_display_scm (sym
);
752 return SCM_UNDEFINED
;
757 sc
->property_alist_
= scm_assq_set_x (sc
->property_alist_
, sym
, val
);
761 error ("Not a score element");
762 ly_display_scm (elt
);
765 return SCM_UNDEFINED
;
770 Score_element::ly_get_elt_property (SCM elt
, SCM sym
)
772 Score_element
* sc
= unsmob_element (elt
);
776 SCM s
= scm_assq(sym
, sc
->property_alist_
);
781 return SCM_UNDEFINED
;
785 error ("Not a score element");
786 ly_display_scm (elt
);
788 return SCM_UNDEFINED
;
793 Score_element::discretionary_processing()
799 spanner_get_bound (SCM slur
, SCM dir
)
801 return dynamic_cast<Spanner
*> (unsmob_element (slur
))->get_bound (to_dir (dir
))->self_scm_
;
805 score_element_get_pointer (SCM se
, SCM name
)
807 SCM s
= scm_assq (name
, unsmob_element (se
)->pointer_alist_
);
808 return (s
== SCM_BOOL_F
) ? SCM_UNDEFINED
: gh_cdr (s
);
812 score_element_get_property (SCM se
, SCM name
)
814 SCM s
= scm_assq (name
, unsmob_element (se
)->property_alist_
);
815 return (s
== SCM_BOOL_F
) ? SCM_UNDEFINED
: gh_cdr (s
);
822 scm_make_gsubr ("ly-get-elt-property", 2, 0, 0, (SCM(*)(...))Score_element::ly_get_elt_property
);
823 scm_make_gsubr ("ly-set-elt-property", 3, 0, 0, (SCM(*)(...))Score_element::ly_set_elt_property
);
824 scm_make_gsubr ("ly-get-elt-pointer", 2 , 0, 0,
825 (SCM(*)(...)) score_element_get_pointer
);
826 scm_make_gsubr ("ly-get-spanner-bound", 2 , 0, 0,
827 (SCM(*)(...)) spanner_get_bound
);
829 ADD_SCM_INIT_FUNC(scoreelt
, init_functions
);