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)
265 SCM cause = get_grob_property ("cause");
266 if (Music*m = unsmob_music (cause))
268 SCM music_origin = m->get_mus_property ("origin");
269 if (unsmob_input (music_origin))
270 origin = music_origin;
276 mol = Molecule (m->extent_box (),
277 scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
280 m = unsmob_molecule (mol);
284 transparent retains dimensions of element.
286 if (m && to_boolean (get_grob_property ("transparent")))
287 mol = Molecule (m->extent_box (), SCM_EOL).smobbed_copy ();
298 Grob::do_break_processing ()
303 Grob::get_system () const
309 Grob::add_dependency (Grob*e)
313 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
316 programming_error ("Null dependency added");
321 Grob::handle_broken_dependencies ()
323 Spanner * sp = dynamic_cast<Spanner*> (this);
329 for (SCM s = mutable_property_alist_; gh_pair_p(s);
332 sp->substitute_one_mutable_property (gh_caar (s),
338 System *system = get_system ();
341 && system && common_refpoint (system, X_AXIS) && common_refpoint (system, Y_AXIS))
343 substitute_mutable_properties (system ? system->self_scm () : SCM_UNDEFINED,
344 mutable_property_alist_);
346 else if (dynamic_cast <System*> (this))
348 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
353 This element is `invalid'; it has been removed from all
354 dependencies, so let's junk the element itself.
356 do not do this for System, since that would remove references
357 to the originals of score-grobs, which get then GC'd (a bad
366 Note that we still want references to this element to be
367 rearranged, and not silently thrown away, so we keep pointers
368 like {broken_into_{drul,array}, original}
373 mutable_property_alist_ = SCM_EOL;
374 immutable_property_alist_ = SCM_EOL;
376 set_extent (SCM_EOL, Y_AXIS);
377 set_extent (SCM_EOL, X_AXIS);
379 for (int a= X_AXIS; a <= Y_AXIS; a++)
381 dim_cache_[a].offset_callbacks_ = SCM_EOL;
382 dim_cache_[a].offsets_left_ = 0;
387 Grob::handle_prebroken_dependencies ()
390 Don't do this in the derived method, since we want to keep access to
391 mutable_property_alist_ centralized.
395 Item * it = dynamic_cast<Item*> (this);
396 substitute_mutable_properties (gh_int2scm (it->break_status_dir ()),
397 original_->mutable_property_alist_);
402 Grob::find_broken_piece (System*) const
408 translate in one direction
411 Grob::translate_axis (Real y, Axis a)
413 if (isinf (y) || isnan (y))
414 programming_error (_ (INFINITY_MSG));
417 dim_cache_[a].offset_ += y;
423 Find the offset relative to D. If D equals THIS, then it is 0.
424 Otherwise, it recursively defd as
426 OFFSET_ + PARENT_L_->relative_coordinate (D)
429 Grob::relative_coordinate (Grob const*refp, Axis a) const
435 We catch PARENT_L_ == nil case with this, but we crash if we did
436 not ask for the absolute coordinate (ie. REFP == nil.)
439 if (refp == dim_cache_[a].parent_)
440 return get_offset (a);
442 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
448 Invoke callbacks to get offset relative to parent.
451 Grob::get_offset (Axis a) const
453 Grob *me = (Grob*) this;
454 while (dim_cache_[a].offsets_left_)
456 int l = --me->dim_cache_[a].offsets_left_;
457 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, gh_int2scm (l));
458 SCM retval = gh_call2 (cb, self_scm (), gh_int2scm (a));
460 Real r = gh_scm2double (retval);
461 if (isinf (r) || isnan (r))
463 programming_error (INFINITY_MSG);
466 me->dim_cache_[a].offset_ +=r;
468 return dim_cache_[a].offset_;
472 MAKE_SCHEME_CALLBACK (Grob,point_dimension_callback,2);
474 Grob::point_dimension_callback (SCM , SCM)
476 return ly_interval2scm (Interval (0,0));
480 Grob::empty_b (Axis a)const
482 return ! (gh_pair_p (dim_cache_[a].dimension_) ||
483 gh_procedure_p (dim_cache_[a].dimension_));
487 Grob::extent (Grob * refp, Axis a) const
489 Real x = relative_coordinate (refp, a);
492 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
494 if (gh_pair_p (d->dimension_))
496 else if (gh_procedure_p (d->dimension_))
499 FIXME: add doco on types, and should typecheck maybe?
501 d->dimension_= gh_call2 (d->dimension_, self_scm (), gh_int2scm (a));
506 if (!gh_pair_p (d->dimension_))
509 ext = ly_scm2interval (d->dimension_);
511 SCM extra = get_grob_property (a == X_AXIS
518 if (gh_pair_p (extra))
520 ext[BIGGER] += gh_scm2double (ly_cdr (extra));
521 ext[SMALLER] += gh_scm2double (ly_car (extra));
524 extra = get_grob_property (a == X_AXIS
526 : "minimum-Y-extent");
527 if (gh_pair_p (extra))
529 ext.unite (Interval (gh_scm2double (ly_car (extra)),
530 gh_scm2double (ly_cdr (extra))));
539 Find the group-element which has both #this# and #s#
542 Grob::common_refpoint (Grob const* s, Axis a) const
545 I don't like the quadratic aspect of this code, but I see no other
546 way. The largest chain of parents might be 10 high or so, so
547 it shouldn't be a real issue. */
548 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
549 for (Grob const * d = s; d; d = d->dim_cache_[a].parent_)
558 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
560 for (; gh_pair_p (elist); elist = ly_cdr (elist))
562 Grob * s = unsmob_grob (ly_car (elist));
566 common = common->common_refpoint (s, a);
577 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
579 for (int i = arr.size() ; i--; )
586 common = common->common_refpoint (s, a);
597 SCM meta = get_grob_property ("meta");
598 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
599 nm = (gh_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
600 return gh_symbol_p (nm) ? ly_symbol2string (nm) : classname (this);
604 Grob::add_offset_callback (SCM cb, Axis a)
606 if (!has_offset_callback_b (cb, a))
608 dim_cache_[a].offset_callbacks_ = gh_cons (cb, dim_cache_[a].offset_callbacks_);
609 dim_cache_[a].offsets_left_ ++;
614 Grob::has_extent_callback_b (SCM cb, Axis a)const
616 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
621 Grob::has_offset_callback_b (SCM cb, Axis a)const
623 return scm_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
627 Grob::set_extent (SCM dc, Axis a)
629 dim_cache_[a].dimension_ =dc;
633 Grob::set_parent (Grob *g, Axis a)
635 dim_cache_[a].parent_ = g;
638 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
640 Grob::fixup_refpoint (SCM smob)
642 Grob *me = unsmob_grob (smob);
643 for (int a = X_AXIS; a < NO_AXES; a ++)
646 Grob * parent = me->get_parent (ax);
651 if (parent->get_system () != me->get_system () && me->get_system ())
653 Grob * newparent = parent->find_broken_piece (me->get_system ());
654 me->set_parent (newparent, ax);
657 if (Item * i = dynamic_cast<Item*> (me))
659 Item *parenti = dynamic_cast<Item*> (parent);
663 Direction my_dir = i->break_status_dir () ;
664 if (my_dir!= parenti->break_status_dir ())
666 Item *newparent = parenti->find_prebroken_piece (my_dir);
667 me->set_parent (newparent, ax);
676 Grob::warning (String s)const
678 SCM cause = self_scm();
679 while (cause != SCM_EOL && !unsmob_music (cause))
681 Grob * g = unsmob_grob (cause);
682 cause = g->get_grob_property ("cause");
685 if (Music *m = unsmob_music (cause))
687 m->origin()->warning (s);
694 Grob::programming_error (String s)const
696 s = "Programming error: " + s;
701 /****************************************************
703 ****************************************************/
707 IMPLEMENT_SMOBS (Grob);
708 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
711 Grob::mark_smob (SCM ses)
713 Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
714 scm_gc_mark (s->immutable_property_alist_);
716 for (int a =0 ; a < 2; a++)
718 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
719 scm_gc_mark (s->dim_cache_[a].dimension_);
722 don't mark the parents. The pointers in the mutable property
723 list form two tree like structures (one for X relations, one
724 for Y relations). Marking these can be done in limited stack
725 space. If we add the parents, we will jump between X and Y in
726 an erratic manner, leading to much more recursion depth (and
727 core dumps if we link to pthreads.)
732 scm_gc_mark (s->original_->self_scm ());
734 s->do_derived_mark ();
735 return s->mutable_property_alist_;
739 Grob::print_smob (SCM s, SCM port, scm_print_state *)
741 Grob *sc = (Grob *) ly_cdr (s);
743 scm_puts ("#<Grob ", port);
744 scm_puts ((char *)sc->name ().to_str0 (), port);
747 don't try to print properties, that is too much hassle.
749 scm_puts (" >", port);
754 Grob::do_derived_mark ()
762 Grob::discretionary_processing ()
767 Grob::internal_has_interface (SCM k)
769 SCM ifs = get_grob_property ("interfaces");
771 return scm_memq (k, ifs) != SCM_BOOL_F;
775 /** Return Array of Grobs in SCM list L */
779 Link_array<Grob> arr;
781 for (SCM s = l; gh_pair_p (s); s = gh_cdr (s))
784 arr.push (unsmob_grob (e));
791 /** Return SCM list of Grob array A */
793 ly_grobs2scm (Link_array<Grob> a)
796 for (int i = a.size (); i; i--)
797 s = gh_cons (a[i-1]->self_scm (), s);
803 IMPLEMENT_TYPE_P (Grob, "ly-grob?");
805 ADD_INTERFACE (Grob, "grob-interface",
806 "In music notation, lots of symbols are related in some way. You can
807 think of music notation as a graph where nodes are formed by the
808 symbols, and the arcs by their relations. A grob is a node in that
809 graph. The directed edges in the graph are formed by references to
810 other grobs (i.e. pointers). This big graph of grobs specifies the
811 notation problem. The solution of this problem is a description of the
812 printout in closed form, i.e. a list of values. These values are
815 All grobs have an X and Y-position on the page. These X and Y positions
816 are stored in a relative format, so they can easily be combined by
817 stacking them, hanging one grob to the side of another, and coupling
818 them into a grouping-grob.
820 Each grob has a reference point (a.k.a. parent): the position of a grob
821 is stored relative to that reference point. For example the X-reference
822 point of a staccato dot usually is the note head that it applies
823 to. When the note head is moved, the staccato dot moves along
826 A grob is often associated with a symbol, but some grobs do not print
827 any symbols. They take care of grouping objects. For example, there is a
828 separate grob that stacks staves vertically. The @ref{NoteCollision}
829 is also an abstract grob: it only moves around chords, but doesn't print
832 "X-offset-callbacks Y-offset-callbacks X-extent-callback molecule cause
833 Y-extent-callback molecule-callback extra-offset spacing-procedure
834 staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent
835 causes meta layer before-line-breaking-callback
836 after-line-breaking-callback extra-Y-extent minimum-X-extent
837 minimum-Y-extent transparent");