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 * s= dynamic_cast<Spanner*> (this);
329 for (int i = 0; i< s->broken_intos_ .size (); i++)
331 Grob * sc = s->broken_intos_[i];
332 System * l = sc->get_system ();
334 sc->substitute_mutable_properties (l ? l->self_scm () : SCM_UNDEFINED,
335 mutable_property_alist_);
340 System *system = get_system ();
343 && system && common_refpoint (system, X_AXIS) && common_refpoint (system, Y_AXIS))
345 substitute_mutable_properties (system ? system->self_scm () : SCM_UNDEFINED,
346 mutable_property_alist_);
348 else if (dynamic_cast <System*> (this))
350 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
355 This element is `invalid'; it has been removed from all
356 dependencies, so let's junk the element itself.
358 do not do this for System, since that would remove references
359 to the originals of score-grobs, which get then GC'd (a bad
368 Note that we still want references to this element to be
369 rearranged, and not silently thrown away, so we keep pointers
370 like {broken_into_{drul,array}, original}
375 mutable_property_alist_ = SCM_EOL;
376 immutable_property_alist_ = SCM_EOL;
378 set_extent (SCM_EOL, Y_AXIS);
379 set_extent (SCM_EOL, X_AXIS);
381 for (int a= X_AXIS; a <= Y_AXIS; a++)
383 dim_cache_[a].offset_callbacks_ = SCM_EOL;
384 dim_cache_[a].offsets_left_ = 0;
389 Grob::handle_prebroken_dependencies ()
392 Don't do this in the derived method, since we want to keep access to
393 mutable_property_alist_ centralized.
397 Item * it = dynamic_cast<Item*> (this);
398 substitute_mutable_properties (gh_int2scm (it->break_status_dir ()),
399 original_->mutable_property_alist_);
404 Grob::find_broken_piece (System*) const
410 translate in one direction
413 Grob::translate_axis (Real y, Axis a)
415 if (isinf (y) || isnan (y))
416 programming_error (_ (INFINITY_MSG));
419 dim_cache_[a].offset_ += y;
425 Find the offset relative to D. If D equals THIS, then it is 0.
426 Otherwise, it recursively defd as
428 OFFSET_ + PARENT_L_->relative_coordinate (D)
431 Grob::relative_coordinate (Grob const*refp, Axis a) const
437 We catch PARENT_L_ == nil case with this, but we crash if we did
438 not ask for the absolute coordinate (ie. REFP == nil.)
441 if (refp == dim_cache_[a].parent_)
442 return get_offset (a);
444 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
450 Invoke callbacks to get offset relative to parent.
453 Grob::get_offset (Axis a) const
455 Grob *me = (Grob*) this;
456 while (dim_cache_[a].offsets_left_)
458 int l = --me->dim_cache_[a].offsets_left_;
459 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, gh_int2scm (l));
460 SCM retval = gh_call2 (cb, self_scm (), gh_int2scm (a));
462 Real r = gh_scm2double (retval);
463 if (isinf (r) || isnan (r))
465 programming_error (INFINITY_MSG);
468 me->dim_cache_[a].offset_ +=r;
470 return dim_cache_[a].offset_;
474 MAKE_SCHEME_CALLBACK (Grob,point_dimension_callback,2);
476 Grob::point_dimension_callback (SCM , SCM)
478 return ly_interval2scm (Interval (0,0));
482 Grob::empty_b (Axis a)const
484 return ! (gh_pair_p (dim_cache_[a].dimension_) ||
485 gh_procedure_p (dim_cache_[a].dimension_));
489 Grob::extent (Grob * refp, Axis a) const
491 Real x = relative_coordinate (refp, a);
494 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
496 if (gh_pair_p (d->dimension_))
498 else if (gh_procedure_p (d->dimension_))
501 FIXME: add doco on types, and should typecheck maybe?
503 d->dimension_= gh_call2 (d->dimension_, self_scm (), gh_int2scm (a));
508 if (!gh_pair_p (d->dimension_))
511 ext = ly_scm2interval (d->dimension_);
513 SCM extra = get_grob_property (a == X_AXIS
520 if (gh_pair_p (extra))
522 ext[BIGGER] += gh_scm2double (ly_cdr (extra));
523 ext[SMALLER] += gh_scm2double (ly_car (extra));
526 extra = get_grob_property (a == X_AXIS
528 : "minimum-Y-extent");
529 if (gh_pair_p (extra))
531 ext.unite (Interval (gh_scm2double (ly_car (extra)),
532 gh_scm2double (ly_cdr (extra))));
541 Find the group-element which has both #this# and #s#
544 Grob::common_refpoint (Grob const* s, Axis a) const
547 I don't like the quadratic aspect of this code, but I see no other
548 way. The largest chain of parents might be 10 high or so, so
549 it shouldn't be a real issue. */
550 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
551 for (Grob const * d = s; d; d = d->dim_cache_[a].parent_)
560 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
562 for (; gh_pair_p (elist); elist = ly_cdr (elist))
564 Grob * s = unsmob_grob (ly_car (elist));
568 common = common->common_refpoint (s, a);
579 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
581 for (int i = arr.size() ; i--; )
588 common = common->common_refpoint (s, a);
599 SCM meta = get_grob_property ("meta");
600 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
601 nm = (gh_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
602 return gh_symbol_p (nm) ? ly_symbol2string (nm) : classname (this);
606 Grob::add_offset_callback (SCM cb, Axis a)
608 if (!has_offset_callback_b (cb, a))
610 dim_cache_[a].offset_callbacks_ = gh_cons (cb, dim_cache_[a].offset_callbacks_);
611 dim_cache_[a].offsets_left_ ++;
616 Grob::has_extent_callback_b (SCM cb, Axis a)const
618 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
623 Grob::has_offset_callback_b (SCM cb, Axis a)const
625 return scm_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
629 Grob::set_extent (SCM dc, Axis a)
631 dim_cache_[a].dimension_ =dc;
635 Grob::set_parent (Grob *g, Axis a)
637 dim_cache_[a].parent_ = g;
640 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
642 Grob::fixup_refpoint (SCM smob)
644 Grob *me = unsmob_grob (smob);
645 for (int a = X_AXIS; a < NO_AXES; a ++)
648 Grob * parent = me->get_parent (ax);
653 if (parent->get_system () != me->get_system () && me->get_system ())
655 Grob * newparent = parent->find_broken_piece (me->get_system ());
656 me->set_parent (newparent, ax);
659 if (Item * i = dynamic_cast<Item*> (me))
661 Item *parenti = dynamic_cast<Item*> (parent);
665 Direction my_dir = i->break_status_dir () ;
666 if (my_dir!= parenti->break_status_dir ())
668 Item *newparent = parenti->find_prebroken_piece (my_dir);
669 me->set_parent (newparent, ax);
678 Grob::warning (String s)const
680 SCM cause = self_scm();
681 while (cause != SCM_EOL && !unsmob_music (cause))
683 Grob * g = unsmob_grob (cause);
684 cause = g->get_grob_property ("cause");
687 if (Music *m = unsmob_music (cause))
689 m->origin()->warning (s);
696 Grob::programming_error (String s)const
698 s = "Programming error: " + s;
703 /****************************************************
705 ****************************************************/
709 IMPLEMENT_SMOBS (Grob);
710 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
713 Grob::mark_smob (SCM ses)
715 Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
716 scm_gc_mark (s->immutable_property_alist_);
718 for (int a =0 ; a < 2; a++)
720 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
721 scm_gc_mark (s->dim_cache_[a].dimension_);
724 don't mark the parents. The pointers in the mutable property
725 list form two tree like structures (one for X relations, one
726 for Y relations). Marking these can be done in limited stack
727 space. If we add the parents, we will jump between X and Y in
728 an erratic manner, leading to much more recursion depth (and
729 core dumps if we link to pthreads.)
734 scm_gc_mark (s->original_->self_scm ());
736 s->do_derived_mark ();
737 return s->mutable_property_alist_;
741 Grob::print_smob (SCM s, SCM port, scm_print_state *)
743 Grob *sc = (Grob *) ly_cdr (s);
745 scm_puts ("#<Grob ", port);
746 scm_puts ((char *)sc->name ().to_str0 (), port);
749 don't try to print properties, that is too much hassle.
751 scm_puts (" >", port);
756 Grob::do_derived_mark ()
764 Grob::discretionary_processing ()
769 Grob::internal_has_interface (SCM k)
771 SCM ifs = get_grob_property ("interfaces");
773 return scm_memq (k, ifs) != SCM_BOOL_F;
777 /** Return Array of Grobs in SCM list L */
781 Link_array<Grob> arr;
783 for (SCM s = l; gh_pair_p (s); s = gh_cdr (s))
786 arr.push (unsmob_grob (e));
793 /** Return SCM list of Grob array A */
795 ly_grobs2scm (Link_array<Grob> a)
798 for (int i = a.size (); i; i--)
799 s = gh_cons (a[i-1]->self_scm (), s);
805 IMPLEMENT_TYPE_P (Grob, "ly-grob?");
807 ADD_INTERFACE (Grob, "grob-interface",
808 "In music notation, lots of symbols are related in some way. You can
809 think of music notation as a graph where nodes are formed by the
810 symbols, and the arcs by their relations. A grob is a node in that
811 graph. The directed edges in the graph are formed by references to
812 other grobs (i.e. pointers). This big graph of grobs specifies the
813 notation problem. The solution of this problem is a description of the
814 printout in closed form, i.e. a list of values. These values are
817 All grobs have an X and Y-position on the page. These X and Y positions
818 are stored in a relative format, so they can easily be combined by
819 stacking them, hanging one grob to the side of another, and coupling
820 them into a grouping-grob.
822 Each grob has a reference point (a.k.a. parent): the position of a grob
823 is stored relative to that reference point. For example the X-reference
824 point of a staccato dot usually is the note head that it applies
825 to. When the note head is moved, the staccato dot moves along
828 A grob is often associated with a symbol, but some grobs do not print
829 any symbols. They take care of grouping objects. For example, there is a
830 separate grob that stacks staves vertically. The @ref{NoteCollision}
831 is also an abstract grob: it only moves around chords, but doesn't print
834 "X-offset-callbacks Y-offset-callbacks X-extent-callback molecule cause
835 Y-extent-callback molecule-callback extra-offset spacing-procedure
836 staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent
837 causes meta layer before-line-breaking-callback
838 after-line-breaking-callback extra-Y-extent minimum-X-extent
839 minimum-Y-extent transparent");