2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
14 #include "input-smob.hh"
16 #include "group-interface.hh"
18 #include "paper-score.hh"
19 #include "paper-def.hh"
20 #include "molecule.hh"
24 #include "line-of-score.hh"
26 #include "paper-column.hh"
27 #include "molecule.hh"
29 #include "paper-outputter.hh"
33 #include "ly-smobs.icc"
38 remove dynamic_cast<Spanner,Item> and put this code into respective
43 #define INFINITY_MSG "Infinity or NaN encountered"
45 Grob::Grob (SCM basicprops)
48 fixme: default should be no callback.
54 immutable_property_alist_ = basicprops;
55 mutable_property_alist_ = SCM_EOL;
60 SCM meta = get_grob_property ("meta");
63 SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
66 do it directly to bypass interface checks.
68 mutable_property_alist_ = gh_cons (gh_cons (ly_symbol2scm ("interfaces"),
70 mutable_property_alist_);
76 destill this into a function, so we can re-init the immutable
77 properties with a new BASICPROPS value after creation. Convenient
78 eg. when using \override with StaffSymbol. */
80 char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
81 char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
83 for (int a = X_AXIS; a <= Y_AXIS; a++)
85 SCM l = get_grob_property (onames[a]);
87 if (scm_ilength (l) >=0)
89 dim_cache_[a].offset_callbacks_ = l;
90 dim_cache_[a].offsets_left_ = scm_ilength (l);
94 programming_error ("[XY]-offset-callbacks must be a list");
97 SCM cb = get_grob_property (enames[a]);
100 Should change default to be empty?
103 && !gh_procedure_p (cb) && !gh_pair_p (cb)
104 && gh_procedure_p (get_grob_property ("molecule-callback"))
106 cb = molecule_extent_proc;
108 dim_cache_[a].dimension_ = cb;
113 Grob::Grob (Grob const&s)
114 : dim_cache_ (s.dim_cache_)
116 original_l_ = (Grob*) &s;
117 immutable_property_alist_ = s.immutable_property_alist_;
118 mutable_property_alist_ = SCM_EOL;
120 status_c_ = s.status_c_;
121 pscore_l_ = s.pscore_l_;
129 do nothing scm-ish and no unprotecting here.
135 extern void check_interfaces_for_property (Grob const *me, SCM sym);
138 Grob::internal_set_grob_property (SCM s, SCM v)
141 if (internal_type_checking_global_b)
143 assert (type_check_assignment (s, v, ly_symbol2scm ("backend-type?")));
144 check_interfaces_for_property(this, s);
149 mutable_property_alist_ = scm_assq_set_x (mutable_property_alist_, s, v);
154 Grob::internal_get_grob_property (SCM sym) const
156 SCM s = scm_sloppy_assq (sym, mutable_property_alist_);
160 s = scm_sloppy_assq (sym, immutable_property_alist_);
163 if (internal_type_checking_global_b && gh_pair_p (s))
165 assert (type_check_assignment (sym, gh_cdr (s), ly_symbol2scm ("backend-type?")));
166 check_interfaces_for_property(this, sym);
170 return (s == SCM_BOOL_F) ? SCM_EOL : ly_cdr (s);
174 Remove the value associated with KEY, and return it. The result is
175 that a next call will yield SCM_EOL (and not the underlying
179 Grob::remove_grob_property (const char* key)
181 SCM val = get_grob_property (key);
183 set_grob_property (key, SCM_EOL);
189 MAKE_SCHEME_CALLBACK (Grob,molecule_extent,2);
191 Grob::molecule_extent (SCM element_smob, SCM scm_axis)
193 Grob *s = unsmob_grob (element_smob);
194 Axis a = (Axis) gh_scm2int (scm_axis);
196 Molecule *m = s->get_molecule ();
200 return ly_interval2scm (e);
203 MAKE_SCHEME_CALLBACK (Grob,preset_extent,2);
206 Grob::preset_extent (SCM element_smob, SCM scm_axis)
208 Grob *s = unsmob_grob (element_smob);
209 Axis a = (Axis) gh_scm2int (scm_axis);
211 SCM ext = s->get_grob_property ((a == X_AXIS)
217 Real l = gh_scm2double (ly_car (ext));
218 Real r = gh_scm2double (ly_cdr (ext));
219 return ly_interval2scm (Interval (l, r));
222 return ly_interval2scm (Interval ());
228 Grob::paper_l () const
230 return pscore_l_ ? pscore_l_->paper_l_ : 0;
234 Grob::calculate_dependencies (int final, int busy, SCM funcname)
236 if (status_c_ >= final)
239 if (status_c_== busy)
241 programming_error ("Element is busy, come back later");
247 for (SCM d = get_grob_property ("dependencies"); gh_pair_p (d);
250 unsmob_grob (ly_car (d))
251 ->calculate_dependencies (final, busy, funcname);
255 SCM proc = internal_get_grob_property (funcname);
256 if (gh_procedure_p (proc))
257 gh_call1 (proc, this->self_scm ());
263 Grob::get_molecule () const
265 if (immutable_property_alist_ == SCM_EOL)
271 SCM mol = get_grob_property ("molecule");
272 if (unsmob_molecule (mol))
273 return unsmob_molecule (mol);
275 mol = get_uncached_molecule ();
277 Grob *me = (Grob*)this;
278 me->set_grob_property ("molecule", mol);
280 return unsmob_molecule (mol);
283 Grob::get_uncached_molecule ()const
285 SCM proc = get_grob_property ("molecule-callback");
288 if (gh_procedure_p (proc))
289 mol = gh_apply (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
292 Molecule *m = unsmob_molecule (mol);
294 if (unsmob_molecule (mol))
296 SCM origin = ly_symbol2scm ("no-origin");
298 if (store_locations_global_b){
299 SCM cause = get_grob_property ("cause");
300 if (Music*m = unsmob_music (cause))
302 SCM music_origin = m->get_mus_property ("origin");
303 if (unsmob_input (music_origin))
304 origin = music_origin;
310 mol = Molecule (m->extent_box (),
311 scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
314 m = unsmob_molecule (mol);
318 transparent retains dimensions of element.
320 if (m && to_boolean (get_grob_property ("transparent")))
321 mol = Molecule (m->extent_box (), SCM_EOL).smobbed_copy ();
332 Grob::do_break_processing ()
342 Grob::line_l () const
348 Grob::add_dependency (Grob*e)
352 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
355 programming_error ("Null dependency added");
362 Do break substitution in S, using CRITERION. Return new value.
363 CRITERION is either a SMOB pointer to the desired line, or a number
364 representing the break direction. Do not modify SRC.
366 It is rather tightly coded, since it takes a lot of time; it is
367 one of the top functions in the profile.
371 Grob::handle_broken_grobs (SCM src, SCM criterion)
374 Grob *sc = unsmob_grob (src);
377 if (SCM_INUMP (criterion))
379 Item * i = dynamic_cast<Item*> (sc);
380 Direction d = to_dir (criterion);
381 if (i && i->break_status_dir () != d)
383 Item *br = i->find_prebroken_piece (d);
384 return (br) ? br->self_scm () : SCM_UNDEFINED;
390 = dynamic_cast<System*> (unsmob_grob (criterion));
391 if (sc->line_l () != line)
393 sc = sc->find_broken_piece (line);
397 /* now: !sc || (sc && sc->line_l () == line) */
399 return SCM_UNDEFINED;
401 /* now: sc && sc->line_l () == line */
403 return sc->self_scm();
405 This was introduced in 1.3.49 as a measure to prevent
406 programming errors. It looks expensive (?).
410 benchmark , document when (what kind of programming
411 errors) this happens.
413 if (sc->common_refpoint (line, X_AXIS)
414 && sc->common_refpoint (line, Y_AXIS))
416 return sc->self_scm ();
418 return SCM_UNDEFINED;
421 else if (ly_pair_p (src)) // SCM_CONSP (src)) // huh?
423 SCM oldcar =ly_car (src);
425 UGH! breaks on circular lists.
427 SCM newcar = handle_broken_grobs (oldcar, criterion);
428 SCM oldcdr = ly_cdr (src);
430 if (newcar == SCM_UNDEFINED
431 && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
434 This is tail-recursion, ie.
436 return handle_broken_grobs (cdr, criterion);
438 We don't want to rely on the compiler to do this. Without
439 tail-recursion, this easily crashes with a stack overflow. */
444 SCM newcdr = handle_broken_grobs (oldcdr, criterion);
445 return scm_cons (newcar, newcdr);
454 Grob::handle_broken_dependencies ()
456 Spanner * s= dynamic_cast<Spanner*> (this);
457 if (original_l_ && s)
462 for (int i = 0; i< s->broken_into_l_arr_ .size (); i++)
464 Grob * sc = s->broken_into_l_arr_[i];
465 System * l = sc->line_l ();
466 sc->mutable_property_alist_ =
467 handle_broken_grobs (mutable_property_alist_,
468 l ? l->self_scm () : SCM_UNDEFINED);
473 System *line = line_l ();
475 if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
477 mutable_property_alist_
478 = handle_broken_grobs (mutable_property_alist_,
479 line ? line->self_scm () : SCM_UNDEFINED);
481 else if (dynamic_cast <System*> (this))
483 mutable_property_alist_ = handle_broken_grobs (mutable_property_alist_,
489 This element is `invalid'; it has been removed from all
490 dependencies, so let's junk the element itself.
492 do not do this for System, since that would remove
493 references to the originals of score-elts, which get then GC'd
501 Note that we still want references to this element to be
502 rearranged, and not silently thrown away, so we keep pointers
503 like {broken_into_{drul,array}, original}
508 mutable_property_alist_ = SCM_EOL;
509 immutable_property_alist_ = SCM_EOL;
511 set_extent_callback (SCM_EOL, Y_AXIS);
512 set_extent_callback (SCM_EOL, X_AXIS);
514 for (int a= X_AXIS; a <= Y_AXIS; a++)
516 dim_cache_[a].offset_callbacks_ = SCM_EOL;
517 dim_cache_[a].offsets_left_ = 0;
522 Grob::handle_prebroken_dependencies ()
527 Grob::find_broken_piece (System*) const
533 Grob::translate_axis (Real y, Axis a)
535 if (isinf (y) || isnan (y))
536 programming_error (_ (INFINITY_MSG));
539 dim_cache_[a].offset_ += y;
544 Grob::relative_coordinate (Grob const*refp, Axis a) const
550 We catch PARENT_L_ == nil case with this, but we crash if we did
551 not ask for the absolute coordinate (ie. REFP == nil.)
554 if (refp == dim_cache_[a].parent_l_)
555 return get_offset (a);
557 return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
561 Grob::get_offset (Axis a) const
563 Grob *me = (Grob*) this;
564 while (dim_cache_[a].offsets_left_)
566 int l = --me->dim_cache_[a].offsets_left_;
567 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, gh_int2scm (l));
568 SCM retval = gh_call2 (cb, self_scm (), gh_int2scm (a));
570 Real r = gh_scm2double (retval);
571 if (isinf (r) || isnan (r))
573 programming_error (INFINITY_MSG);
576 me->dim_cache_[a].offset_ +=r;
578 return dim_cache_[a].offset_;
582 MAKE_SCHEME_CALLBACK (Grob,point_dimension_callback,2);
584 Grob::point_dimension_callback (SCM , SCM)
586 return ly_interval2scm (Interval (0,0));
590 Grob::empty_b (Axis a)const
592 return ! (gh_pair_p (dim_cache_[a].dimension_) ||
593 gh_procedure_p (dim_cache_[a].dimension_));
597 Grob::extent (Grob * refp, Axis a) const
599 Real x = relative_coordinate (refp, a);
602 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
604 if (gh_pair_p (d->dimension_))
606 else if (gh_procedure_p (d->dimension_))
609 FIXME: add doco on types, and should typecheck maybe?
611 d->dimension_= gh_call2 (d->dimension_, self_scm (), gh_int2scm (a));
616 if (!gh_pair_p (d->dimension_))
619 ext = ly_scm2interval (d->dimension_);
621 SCM extra = get_grob_property (a == X_AXIS
628 if (gh_pair_p (extra))
630 ext[BIGGER] += gh_scm2double (ly_cdr (extra));
631 ext[SMALLER] += gh_scm2double (ly_car (extra));
634 extra = get_grob_property (a == X_AXIS
636 : "minimum-extent-Y");
637 if (gh_pair_p (extra))
639 ext.unite (Interval (gh_scm2double (ly_car (extra)),
640 gh_scm2double (ly_cdr (extra))));
649 Grob::common_refpoint (Grob const* s, Axis a) const
652 I don't like the quadratic aspect of this code, but I see no other
653 way. The largest chain of parents might be 10 high or so, so
654 it shouldn't be a real issue. */
655 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_l_)
656 for (Grob const * d = s; d; d = d->dim_cache_[a].parent_l_)
665 Grob::common_refpoint (SCM elist, Axis a) const
667 Grob * common = (Grob*) this;
668 for (; gh_pair_p (elist); elist = ly_cdr (elist))
670 Grob * s = unsmob_grob (ly_car (elist));
672 common = common->common_refpoint (s, a);
681 SCM meta = get_grob_property ("meta");
682 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
683 nm = (gh_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
684 return gh_symbol_p (nm) ? ly_symbol2string (nm) : classname (this);
688 Grob::add_offset_callback (SCM cb, Axis a)
690 if (!has_offset_callback_b (cb, a))
692 dim_cache_[a].offset_callbacks_ = gh_cons (cb, dim_cache_[a].offset_callbacks_);
693 dim_cache_[a].offsets_left_ ++;
698 Grob::has_extent_callback_b (SCM cb, Axis a)const
700 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
705 Grob::has_extent_callback_b (Axis a) const
707 return gh_procedure_p (dim_cache_[a].dimension_);
711 Grob::has_offset_callback_b (SCM cb, Axis a)const
713 return scm_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
717 Grob::set_extent_callback (SCM dc, Axis a)
719 dim_cache_[a].dimension_ =dc;
723 Grob::set_parent (Grob *g, Axis a)
725 dim_cache_[a].parent_l_ = g;
728 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
730 Grob::fixup_refpoint (SCM smob)
732 Grob *me = unsmob_grob (smob);
733 for (int a = X_AXIS; a < NO_AXES; a ++)
736 Grob * parent = me->get_parent (ax);
741 if (parent->line_l () != me->line_l () && me->line_l ())
743 Grob * newparent = parent->find_broken_piece (me->line_l ());
744 me->set_parent (newparent, ax);
747 if (Item * i = dynamic_cast<Item*> (me))
749 Item *parenti = dynamic_cast<Item*> (parent);
753 Direction my_dir = i->break_status_dir () ;
754 if (my_dir!= parenti->break_status_dir ())
756 Item *newparent = parenti->find_prebroken_piece (my_dir);
757 me->set_parent (newparent, ax);
766 Grob::warning (String s)
768 SCM cause = self_scm();
769 while (cause != SCM_EOL && !unsmob_music (cause))
771 Grob * g = unsmob_grob (cause);
772 cause = g->get_grob_property ("cause");
775 if (Music *m = unsmob_music (cause))
777 m->origin()->warning (s);
785 /****************************************************
787 ****************************************************/
791 IMPLEMENT_SMOBS (Grob);
792 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
795 Grob::mark_smob (SCM ses)
797 Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
798 scm_gc_mark (s->immutable_property_alist_);
799 scm_gc_mark (s->mutable_property_alist_);
801 for (int a =0 ; a < 2; a++)
803 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
804 scm_gc_mark (s->dim_cache_[a].dimension_);
805 Grob *p = s->get_parent (Y_AXIS);
807 scm_gc_mark (p->self_scm ());
811 scm_gc_mark (s->original_l_->self_scm ());
813 return s->do_derived_mark ();
817 Grob::print_smob (SCM s, SCM port, scm_print_state *)
819 Grob *sc = (Grob *) ly_cdr (s);
821 scm_puts ("#<Grob ", port);
822 scm_puts ((char *)sc->name ().ch_C (), port);
825 don't try to print properties, that is too much hassle.
827 scm_puts (" >", port);
832 Grob::do_derived_mark ()
839 ly_set_grob_property (SCM elt, SCM sym, SCM val)
841 Grob * sc = unsmob_grob (elt);
842 SCM_ASSERT_TYPE(sc, elt, SCM_ARG1, __FUNCTION__, "grob");
843 SCM_ASSERT_TYPE(gh_symbol_p(sym), sym, SCM_ARG2, __FUNCTION__, "symbol");
845 if (!type_check_assignment (sym, val, ly_symbol2scm ("backend-type?")))
846 error ("typecheck failed");
848 sc->internal_set_grob_property (sym, val);
849 return SCM_UNSPECIFIED;
854 ly_get_grob_property (SCM elt, SCM sym)
856 Grob * sc = unsmob_grob (elt);
857 SCM_ASSERT_TYPE(sc, elt, SCM_ARG1, __FUNCTION__, "grob");
858 SCM_ASSERT_TYPE(gh_symbol_p(sym), sym, SCM_ARG2, __FUNCTION__, "symbol");
860 return sc->internal_get_grob_property (sym);
865 Grob::discretionary_processing ()
872 spanner_get_bound (SCM slur, SCM dir)
874 Spanner * sl = dynamic_cast<Spanner*> (unsmob_grob (slur));
875 SCM_ASSERT_TYPE(sl, slur, SCM_ARG1, __FUNCTION__, "spanner grob");
876 SCM_ASSERT_TYPE(ly_dir_p (dir), slur, SCM_ARG2, __FUNCTION__, "dir");
877 return sl->get_bound (to_dir (dir))->self_scm ();
881 ly_get_paper_var (SCM grob, SCM sym)
883 Grob * sc = unsmob_grob (grob);
884 SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
885 SCM_ASSERT_TYPE(gh_symbol_p(sym), sym, SCM_ARG2, __FUNCTION__, "symbol");
887 return sc->paper_l() ->get_scmvar_scm (sym);
893 ly_get_extent (SCM grob, SCM refp, SCM axis)
895 Grob * sc = unsmob_grob (grob);
896 Grob * ref = unsmob_grob (refp);
897 SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
898 SCM_ASSERT_TYPE(ref, refp, SCM_ARG2, __FUNCTION__, "grob");
900 SCM_ASSERT_TYPE(ly_axis_p(axis), axis, SCM_ARG3, __FUNCTION__, "axis");
902 return ly_interval2scm ( sc->extent (ref, Axis (gh_scm2int (axis))));
906 ly_get_parent (SCM grob, SCM axis)
908 Grob * sc = unsmob_grob (grob);
909 SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
910 SCM_ASSERT_TYPE(ly_axis_p(axis), axis, SCM_ARG2, __FUNCTION__, "axis");
912 return sc->get_parent (Axis (gh_scm2int (axis)))->self_scm();
919 scm_c_define_gsubr ("ly-get-grob-property", 2, 0, 0,
920 (Scheme_function_unknown)ly_get_grob_property);
921 scm_c_define_gsubr ("ly-set-grob-property", 3, 0, 0,
922 (Scheme_function_unknown)ly_set_grob_property);
923 scm_c_define_gsubr ("ly-get-spanner-bound", 2 , 0, 0,
924 (Scheme_function_unknown) spanner_get_bound);
925 scm_c_define_gsubr ("ly-get-paper-variable", 2, 0, 0,
926 (Scheme_function_unknown) ly_get_paper_var);
927 scm_c_define_gsubr ("ly-get-extent", 3, 0, 0,
928 (Scheme_function_unknown) ly_get_extent);
929 scm_c_define_gsubr ("ly-get-parent", 2, 0, 0,
930 (Scheme_function_unknown) ly_get_parent);
934 Grob::has_interface (SCM k)
936 SCM ifs = get_grob_property ("interfaces");
938 return scm_memq (k, ifs) != SCM_BOOL_F;
942 ADD_SCM_INIT_FUNC (scoreelt, init_functions);
943 IMPLEMENT_TYPE_P (Grob, "ly-grob?");
945 ADD_INTERFACE (Grob, "grob-interface",
946 "All grobs support this",
947 "X-offset-callbacks Y-offset-callbacks X-extent-callback molecule cause
948 Y-extent-callback molecule-callback extra-offset
949 staff-symbol interfaces dependencies extra-extent-X causes meta
950 layer before-line-breaking-callback after-line-breaking-callback extra-extent-Y minimum-extent-X minimum-extent-Y transparent");