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;
127 immutable_property_alist_ = s.immutable_property_alist_;
129 mutable_property_alist_ = SCM_EOL;
132 No properties are copied. That is the job of handle_broken_dependencies.
140 #ifdef HASHING_FOR_MUTABLE_PROPS
141 mutable_property_alist_ = scm_c_make_hash_table (HASH_SIZE);
148 do nothing scm-ish and no unprotecting here.
156 MAKE_SCHEME_CALLBACK (Grob,molecule_extent,2);
158 Grob::molecule_extent (SCM element_smob, SCM scm_axis)
160 Grob *s = unsmob_grob (element_smob);
161 Axis a = (Axis) gh_scm2int (scm_axis);
163 Molecule *m = s->get_molecule ();
167 return ly_interval2scm (e);
170 MAKE_SCHEME_CALLBACK (Grob,preset_extent,2);
172 Grob::preset_extent (SCM element_smob, SCM scm_axis)
174 Grob *s = unsmob_grob (element_smob);
175 Axis a = (Axis) gh_scm2int (scm_axis);
177 SCM ext = s->get_grob_property ((a == X_AXIS)
183 Real l = gh_scm2double (ly_car (ext));
184 Real r = gh_scm2double (ly_cdr (ext));
185 return ly_interval2scm (Interval (l, r));
188 return ly_interval2scm (Interval ());
194 Grob::get_paper () const
196 return pscore_ ? pscore_->paper_ : 0;
200 Grob::calculate_dependencies (int final, int busy, SCM funcname)
202 if (status_ >= final)
207 programming_error ("Element is busy, come back later");
213 for (SCM d = get_grob_property ("dependencies"); gh_pair_p (d);
216 unsmob_grob (ly_car (d))
217 ->calculate_dependencies (final, busy, funcname);
221 SCM proc = internal_get_grob_property (funcname);
222 if (gh_procedure_p (proc))
223 gh_call1 (proc, this->self_scm ());
229 Grob::get_molecule () const
237 SCM mol = get_grob_property ("molecule");
238 if (unsmob_molecule (mol))
239 return unsmob_molecule (mol);
241 mol = get_uncached_molecule ();
243 Grob *me = (Grob*)this;
244 me->set_grob_property ("molecule", mol);
246 return unsmob_molecule (mol);
250 Grob::get_uncached_molecule ()const
252 SCM proc = get_grob_property ("molecule-callback");
255 if (gh_procedure_p (proc))
256 mol = gh_apply (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
259 Molecule *m = unsmob_molecule (mol);
261 if (unsmob_molecule (mol))
263 SCM origin = ly_symbol2scm ("no-origin");
265 if (store_locations_global_b)
267 SCM cause = get_grob_property ("cause");
268 if (Music*m = unsmob_music (cause))
270 SCM music_origin = m->get_mus_property ("origin");
271 if (unsmob_input (music_origin))
272 origin = music_origin;
278 mol = Molecule (m->extent_box (),
279 scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
282 m = unsmob_molecule (mol);
286 transparent retains dimensions of element.
288 if (m && to_boolean (get_grob_property ("transparent")))
289 mol = Molecule (m->extent_box (), SCM_EOL).smobbed_copy ();
300 Grob::do_break_processing ()
305 Grob::get_system () const
311 Grob::add_dependency (Grob*e)
315 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
318 programming_error ("Null dependency added");
323 Grob::handle_broken_dependencies ()
325 Spanner * sp = dynamic_cast<Spanner*> (this);
331 for (SCM s = mutable_property_alist_; gh_pair_p(s);
334 sp->substitute_one_mutable_property (gh_caar (s),
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 () const
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");