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 "molecule.hh"
25 #include "molecule.hh"
30 #include "ly-smobs.icc"
35 remove dynamic_cast<Spanner,Item> and put this code into respective
39 //#define HASHING_FOR_MUTABLE_PROPS
41 #define INFINITY_MSG "Infinity or NaN encountered"
43 Grob::Grob (SCM basicprops)
46 fixme: default should be no callback.
52 immutable_property_alist_ = basicprops;
53 mutable_property_alist_ = SCM_EOL;
56 We do smobify_self() as the first step. Since the object lives on
57 the heap, none of its SCM variables are protected from GC. After
58 smobify_self(), they are.
63 #ifdef HASHING_FOR_MUTABLE_PROPS
64 mutable_property_alist_ = scm_c_make_hash_table (HASH_SIZE);
67 SCM meta = get_grob_property ("meta");
70 SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
73 Switch off interface checks for the moment.
75 bool itc = internal_type_checking_global_b;
76 internal_type_checking_global_b = false;
77 internal_set_grob_property (ly_symbol2scm ("interfaces"), gh_cdr(ifs));
78 internal_type_checking_global_b = itc;
84 destill this into a function, so we can re-init the immutable
85 properties with a new BASICPROPS value after creation. Convenient
86 eg. when using \override with StaffSymbol. */
88 char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
89 char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
91 for (int a = X_AXIS; a <= Y_AXIS; a++)
93 SCM l = get_grob_property (onames[a]);
95 if (scm_ilength (l) >=0)
97 dim_cache_[a].offset_callbacks_ = l;
98 dim_cache_[a].offsets_left_ = scm_ilength (l);
102 programming_error ("[XY]-offset-callbacks must be a list");
105 SCM cb = get_grob_property (enames[a]);
108 Should change default to be empty?
111 && !gh_procedure_p (cb) && !gh_pair_p (cb)
112 && gh_procedure_p (get_grob_property ("molecule-callback"))
114 cb = molecule_extent_proc;
116 dim_cache_[a].dimension_ = cb;
121 Grob::Grob (Grob const&s)
122 : dim_cache_ (s.dim_cache_)
124 original_ = (Grob*) &s;
125 immutable_property_alist_ = s.immutable_property_alist_;
127 mutable_property_alist_ = SCM_EOL;
130 No properties are copied. That is the job of handle_broken_dependencies.
138 #ifdef HASHING_FOR_MUTABLE_PROPS
139 mutable_property_alist_ = scm_c_make_hash_table (HASH_SIZE);
146 do nothing scm-ish and no unprotecting here.
154 MAKE_SCHEME_CALLBACK (Grob,molecule_extent,2);
156 Grob::molecule_extent (SCM element_smob, SCM scm_axis)
158 Grob *s = unsmob_grob (element_smob);
159 Axis a = (Axis) gh_scm2int (scm_axis);
161 Molecule *m = s->get_molecule ();
165 return ly_interval2scm (e);
168 MAKE_SCHEME_CALLBACK (Grob,preset_extent,2);
170 Grob::preset_extent (SCM element_smob, SCM scm_axis)
172 Grob *s = unsmob_grob (element_smob);
173 Axis a = (Axis) gh_scm2int (scm_axis);
175 SCM ext = s->get_grob_property ((a == X_AXIS)
181 Real l = gh_scm2double (ly_car (ext));
182 Real r = gh_scm2double (ly_cdr (ext));
183 return ly_interval2scm (Interval (l, r));
186 return ly_interval2scm (Interval ());
192 Grob::get_paper () const
194 return pscore_ ? pscore_->paper_ : 0;
198 Grob::calculate_dependencies (int final, int busy, SCM funcname)
200 if (status_ >= final)
205 programming_error ("Element is busy, come back later");
211 for (SCM d = get_grob_property ("dependencies"); gh_pair_p (d);
214 unsmob_grob (ly_car (d))
215 ->calculate_dependencies (final, busy, funcname);
219 SCM proc = internal_get_grob_property (funcname);
220 if (gh_procedure_p (proc))
221 gh_call1 (proc, this->self_scm ());
227 Grob::get_molecule () const
235 SCM mol = get_grob_property ("molecule");
236 if (unsmob_molecule (mol))
237 return unsmob_molecule (mol);
239 mol = get_uncached_molecule ();
241 Grob *me = (Grob*)this;
242 me->set_grob_property ("molecule", mol);
244 return unsmob_molecule (mol);
248 Grob::get_uncached_molecule ()const
250 SCM proc = get_grob_property ("molecule-callback");
253 if (gh_procedure_p (proc))
254 mol = gh_apply (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
257 Molecule *m = unsmob_molecule (mol);
259 if (unsmob_molecule (mol))
261 SCM origin = ly_symbol2scm ("no-origin");
263 if (store_locations_global_b){
264 SCM cause = get_grob_property ("cause");
265 if (Music*m = unsmob_music (cause))
267 SCM music_origin = m->get_mus_property ("origin");
268 if (unsmob_input (music_origin))
269 origin = music_origin;
275 mol = Molecule (m->extent_box (),
276 scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
279 m = unsmob_molecule (mol);
283 transparent retains dimensions of element.
285 if (m && to_boolean (get_grob_property ("transparent")))
286 mol = Molecule (m->extent_box (), SCM_EOL).smobbed_copy ();
297 Grob::do_break_processing ()
302 Grob::get_system () const
308 Grob::add_dependency (Grob*e)
312 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
315 programming_error ("Null dependency added");
320 Grob::handle_broken_dependencies ()
322 Spanner * s= dynamic_cast<Spanner*> (this);
328 for (int i = 0; i< s->broken_intos_ .size (); i++)
330 Grob * sc = s->broken_intos_[i];
331 System * l = sc->get_system ();
333 sc->substitute_mutable_properties (l ? l->self_scm () : SCM_UNDEFINED,
334 mutable_property_alist_);
339 System *system = get_system ();
342 && system && common_refpoint (system, X_AXIS) && common_refpoint (system, Y_AXIS))
344 substitute_mutable_properties (system ? system->self_scm () : SCM_UNDEFINED,
345 mutable_property_alist_);
347 else if (dynamic_cast <System*> (this))
349 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
354 This element is `invalid'; it has been removed from all
355 dependencies, so let's junk the element itself.
357 do not do this for System, since that would remove references
358 to the originals of score-grobs, which get then GC'd (a bad
367 Note that we still want references to this element to be
368 rearranged, and not silently thrown away, so we keep pointers
369 like {broken_into_{drul,array}, original}
374 mutable_property_alist_ = SCM_EOL;
375 immutable_property_alist_ = SCM_EOL;
377 set_extent (SCM_EOL, Y_AXIS);
378 set_extent (SCM_EOL, X_AXIS);
380 for (int a= X_AXIS; a <= Y_AXIS; a++)
382 dim_cache_[a].offset_callbacks_ = SCM_EOL;
383 dim_cache_[a].offsets_left_ = 0;
388 Grob::handle_prebroken_dependencies ()
391 Don't do this in the derived method, since we want to keep access to
392 mutable_property_alist_ centralized.
396 Item * it = dynamic_cast<Item*> (this);
397 substitute_mutable_properties (gh_int2scm (it->break_status_dir ()),
398 original_->mutable_property_alist_);
403 Grob::find_broken_piece (System*) const
409 translate in one direction
412 Grob::translate_axis (Real y, Axis a)
414 if (isinf (y) || isnan (y))
415 programming_error (_ (INFINITY_MSG));
418 dim_cache_[a].offset_ += y;
424 Find the offset relative to D. If D equals THIS, then it is 0.
425 Otherwise, it recursively defd as
427 OFFSET_ + PARENT_L_->relative_coordinate (D)
430 Grob::relative_coordinate (Grob const*refp, Axis a) const
436 We catch PARENT_L_ == nil case with this, but we crash if we did
437 not ask for the absolute coordinate (ie. REFP == nil.)
440 if (refp == dim_cache_[a].parent_)
441 return get_offset (a);
443 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
449 Invoke callbacks to get offset relative to parent.
452 Grob::get_offset (Axis a) const
454 Grob *me = (Grob*) this;
455 while (dim_cache_[a].offsets_left_)
457 int l = --me->dim_cache_[a].offsets_left_;
458 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, gh_int2scm (l));
459 SCM retval = gh_call2 (cb, self_scm (), gh_int2scm (a));
461 Real r = gh_scm2double (retval);
462 if (isinf (r) || isnan (r))
464 programming_error (INFINITY_MSG);
467 me->dim_cache_[a].offset_ +=r;
469 return dim_cache_[a].offset_;
473 MAKE_SCHEME_CALLBACK (Grob,point_dimension_callback,2);
475 Grob::point_dimension_callback (SCM , SCM)
477 return ly_interval2scm (Interval (0,0));
481 Grob::empty_b (Axis a)const
483 return ! (gh_pair_p (dim_cache_[a].dimension_) ||
484 gh_procedure_p (dim_cache_[a].dimension_));
488 Grob::extent (Grob * refp, Axis a) const
490 Real x = relative_coordinate (refp, a);
493 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
495 if (gh_pair_p (d->dimension_))
497 else if (gh_procedure_p (d->dimension_))
500 FIXME: add doco on types, and should typecheck maybe?
502 d->dimension_= gh_call2 (d->dimension_, self_scm (), gh_int2scm (a));
507 if (!gh_pair_p (d->dimension_))
510 ext = ly_scm2interval (d->dimension_);
512 SCM extra = get_grob_property (a == X_AXIS
519 if (gh_pair_p (extra))
521 ext[BIGGER] += gh_scm2double (ly_cdr (extra));
522 ext[SMALLER] += gh_scm2double (ly_car (extra));
525 extra = get_grob_property (a == X_AXIS
527 : "minimum-extent-Y");
528 if (gh_pair_p (extra))
530 ext.unite (Interval (gh_scm2double (ly_car (extra)),
531 gh_scm2double (ly_cdr (extra))));
540 Find the group-element which has both #this# and #s#
543 Grob::common_refpoint (Grob const* s, Axis a) const
546 I don't like the quadratic aspect of this code, but I see no other
547 way. The largest chain of parents might be 10 high or so, so
548 it shouldn't be a real issue. */
549 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
550 for (Grob const * d = s; d; d = d->dim_cache_[a].parent_)
559 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
561 for (; gh_pair_p (elist); elist = ly_cdr (elist))
563 Grob * s = unsmob_grob (ly_car (elist));
567 common = common->common_refpoint (s, a);
578 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
580 for (int i = arr.size() ; i--; )
587 common = common->common_refpoint (s, a);
598 SCM meta = get_grob_property ("meta");
599 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
600 nm = (gh_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
601 return gh_symbol_p (nm) ? ly_symbol2string (nm) : classname (this);
605 Grob::add_offset_callback (SCM cb, Axis a)
607 if (!has_offset_callback_b (cb, a))
609 dim_cache_[a].offset_callbacks_ = gh_cons (cb, dim_cache_[a].offset_callbacks_);
610 dim_cache_[a].offsets_left_ ++;
615 Grob::has_extent_callback_b (SCM cb, Axis a)const
617 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
622 Grob::has_offset_callback_b (SCM cb, Axis a)const
624 return scm_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
628 Grob::set_extent (SCM dc, Axis a)
630 dim_cache_[a].dimension_ =dc;
634 Grob::set_parent (Grob *g, Axis a)
636 dim_cache_[a].parent_ = g;
639 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
641 Grob::fixup_refpoint (SCM smob)
643 Grob *me = unsmob_grob (smob);
644 for (int a = X_AXIS; a < NO_AXES; a ++)
647 Grob * parent = me->get_parent (ax);
652 if (parent->get_system () != me->get_system () && me->get_system ())
654 Grob * newparent = parent->find_broken_piece (me->get_system ());
655 me->set_parent (newparent, ax);
658 if (Item * i = dynamic_cast<Item*> (me))
660 Item *parenti = dynamic_cast<Item*> (parent);
664 Direction my_dir = i->break_status_dir () ;
665 if (my_dir!= parenti->break_status_dir ())
667 Item *newparent = parenti->find_prebroken_piece (my_dir);
668 me->set_parent (newparent, ax);
677 Grob::warning (String s)const
679 SCM cause = self_scm();
680 while (cause != SCM_EOL && !unsmob_music (cause))
682 Grob * g = unsmob_grob (cause);
683 cause = g->get_grob_property ("cause");
686 if (Music *m = unsmob_music (cause))
688 m->origin()->warning (s);
695 Grob::programming_error (String s)const
697 s = "Programming error: " + s;
702 /****************************************************
704 ****************************************************/
708 IMPLEMENT_SMOBS (Grob);
709 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
712 Grob::mark_smob (SCM ses)
714 Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
715 scm_gc_mark (s->immutable_property_alist_);
717 for (int a =0 ; a < 2; a++)
719 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
720 scm_gc_mark (s->dim_cache_[a].dimension_);
723 don't mark the parents. The pointers in the mutable property
724 list form two tree like structures (one for X relations, one
725 for Y relations). Marking these can be done in limited stack
726 space. If we add the parents, we will jump between X and Y in
727 an erratic manner, leading to much more recursion depth (and
728 core dumps if we link to pthreads.)
733 scm_gc_mark (s->original_->self_scm ());
735 s->do_derived_mark ();
736 return s->mutable_property_alist_;
740 Grob::print_smob (SCM s, SCM port, scm_print_state *)
742 Grob *sc = (Grob *) ly_cdr (s);
744 scm_puts ("#<Grob ", port);
745 scm_puts ((char *)sc->name ().to_str0 (), port);
748 don't try to print properties, that is too much hassle.
750 scm_puts (" >", port);
755 Grob::do_derived_mark ()
763 Grob::discretionary_processing ()
768 Grob::internal_has_interface (SCM k)
770 SCM ifs = get_grob_property ("interfaces");
772 return scm_memq (k, ifs) != SCM_BOOL_F;
776 /** Return Array of Grobs in SCM list L */
780 Link_array<Grob> arr;
782 for (SCM s = l; gh_pair_p (s); s = gh_cdr (s))
785 arr.push (unsmob_grob (e));
792 /** Return SCM list of Grob array A */
794 ly_grobs2scm (Link_array<Grob> a)
797 for (int i = a.size (); i; i--)
798 s = gh_cons (a[i-1]->self_scm (), s);
804 IMPLEMENT_TYPE_P (Grob, "ly-grob?");
806 ADD_INTERFACE (Grob, "grob-interface",
807 "In music notation, lots of symbols are related in some way. You can
808 think of music notation as a graph where nodes are formed by the
809 symbols, and the arcs by their relations. A grob is a node in that
810 graph. The directed edges in the graph are formed by references to
811 other grobs (i.e. pointers). This big graph of grobs specifies the
812 notation problem. The solution of this problem is a description of the
813 printout in closed form, i.e. a list of values. These values are
816 All grobs have an X and Y-position on the page. These X and Y positions
817 are stored in a relative format, so they can easily be combined by
818 stacking them, hanging one grob to the side of another, and coupling
819 them into a grouping-grob.
821 Each grob has a reference point (a.k.a. parent): the position of a grob
822 is stored relative to that reference point. For example the X-reference
823 point of a staccato dot usually is the note head that it applies
824 to. When the note head is moved, the staccato dot moves along
827 A grob is often associated with a symbol, but some grobs do not print
828 any symbols. They take care of grouping objects. For example, there is a
829 separate grob that stacks staves vertically. The @ref{NoteCollision}
830 is also an abstract grob: it only moves around chords, but doesn't print
833 "X-offset-callbacks Y-offset-callbacks X-extent-callback molecule cause
834 Y-extent-callback molecule-callback extra-offset spacing-procedure
835 staff-symbol interfaces dependencies extent-X extent-Y extra-extent-X
836 causes meta layer before-line-breaking-callback
837 after-line-breaking-callback extra-extent-Y minimum-extent-X
838 minimum-extent-Y transparent");