2002->2003
[lilypond.git] / lily / grob.cc
blobb1fd927a149202c6a63b71330879f0b3510e1739
1 /*
2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
10 #include <string.h>
11 #include <math.h>
13 #include "main.hh"
14 #include "input-smob.hh"
15 #include "warn.hh"
16 #include "group-interface.hh"
17 #include "misc.hh"
18 #include "paper-score.hh"
19 #include "molecule.hh"
20 #include "grob.hh"
21 #include "warn.hh"
22 #include "spanner.hh"
23 #include "system.hh"
24 #include "item.hh"
25 #include "molecule.hh"
26 #include "misc.hh"
27 #include "music.hh"
28 #include "item.hh"
30 #include "ly-smobs.icc"
33 TODO:
35 remove dynamic_cast<Spanner,Item> and put this code into respective
36 subclass.
39 //#define HASHING_FOR_MUTABLE_PROPS
40 #define HASH_SIZE 3
41 #define INFINITY_MSG "Infinity or NaN encountered"
43 Grob::Grob (SCM basicprops)
46 fixme: default should be no callback.
48 self_scm_ = SCM_EOL;
49 pscore_=0;
50 status_ = 0;
51 original_ = 0;
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.
60 smobify_self ();
63 #ifdef HASHING_FOR_MUTABLE_PROPS
64 mutable_property_alist_ = scm_c_make_hash_table (HASH_SIZE);
65 #endif
67 SCM meta = get_grob_property ("meta");
68 if (gh_pair_p (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;
82 TODO:
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);
100 else
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?
110 if (cb != SCM_BOOL_F
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 self_scm_ = SCM_EOL;
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.
135 status_ = s.status_;
136 pscore_ = s.pscore_;
138 smobify_self ();
140 #ifdef HASHING_FOR_MUTABLE_PROPS
141 mutable_property_alist_ = scm_c_make_hash_table (HASH_SIZE);
142 #endif
145 Grob::~Grob ()
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 ();
164 Interval e ;
165 if (m)
166 e = m->extent (a);
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)
178 ? "X-extent"
179 : "Y-extent");
181 if (gh_pair_p (ext))
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 ());
193 Paper_def*
194 Grob::get_paper () const
196 return pscore_ ? pscore_->paper_ : 0;
199 void
200 Grob::calculate_dependencies (int final, int busy, SCM funcname)
202 if (status_ >= final)
203 return;
205 if (status_== busy)
207 programming_error ("Element is busy, come back later");
208 return;
211 status_= busy;
213 for (SCM d = get_grob_property ("dependencies"); gh_pair_p (d);
214 d = ly_cdr (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 ());
225 status_= final;
228 Molecule *
229 Grob::get_molecule () const
231 if (!live())
233 return 0;
237 SCM mol = get_grob_property ("molecule");
238 if (unsmob_molecule (mol))
239 return unsmob_molecule (mol);
241 mol = get_uncached_molecule ();
243 if (live ())
245 Grob *me = (Grob*)this;
246 me->set_grob_property ("molecule", mol);
249 return unsmob_molecule (mol);
253 Grob::get_uncached_molecule ()const
255 SCM proc = get_grob_property ("molecule-callback");
257 SCM mol = SCM_EOL;
258 if (gh_procedure_p (proc))
259 mol = gh_apply (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
261 Molecule *m = unsmob_molecule (mol);
263 if (unsmob_molecule (mol))
265 SCM origin = ly_symbol2scm ("no-origin");
267 if (store_locations_global_b)
269 SCM cause = get_grob_property ("cause");
270 if (Music*m = unsmob_music (cause))
272 SCM music_origin = m->get_mus_property ("origin");
273 if (unsmob_input (music_origin))
274 origin = music_origin;
278 // ugr.
280 mol = Molecule (m->extent_box (),
281 scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
282 ). smobbed_copy ();
284 m = unsmob_molecule (mol);
288 transparent retains dimensions of element.
290 if (m && to_boolean (get_grob_property ("transparent")))
291 mol = Molecule (m->extent_box (), SCM_EOL).smobbed_copy ();
293 return mol;
298 VIRTUAL STUBS
301 void
302 Grob::do_break_processing ()
306 System *
307 Grob::get_system () const
309 return 0;
312 void
313 Grob::add_dependency (Grob*e)
315 if (e)
317 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
319 else
320 programming_error ("Null dependency added");
324 void
325 Grob::handle_broken_dependencies ()
327 Spanner * sp = dynamic_cast<Spanner*> (this);
328 if (original_ && sp)
329 return;
331 if (sp)
333 for (SCM s = mutable_property_alist_; gh_pair_p(s);
334 s = gh_cdr(s))
336 sp->substitute_one_mutable_property (gh_caar (s),
337 gh_cdar (s));
342 System *system = get_system ();
344 if (live ()
345 && system && common_refpoint (system, X_AXIS) && common_refpoint (system, Y_AXIS))
347 substitute_mutable_properties (system ? system->self_scm () : SCM_UNDEFINED,
348 mutable_property_alist_);
350 else if (dynamic_cast <System*> (this))
352 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
354 else
357 This element is `invalid'; it has been removed from all
358 dependencies, so let's junk the element itself.
360 do not do this for System, since that would remove references
361 to the originals of score-grobs, which get then GC'd (a bad
362 thing.)
365 suicide ();
370 Note that we still want references to this element to be
371 rearranged, and not silently thrown away, so we keep pointers
372 like {broken_into_{drul,array}, original}
374 void
375 Grob::suicide ()
377 if (!live ())
378 return;
380 #if 0 // see below.
381 String nm = name();
382 #endif
384 mutable_property_alist_ = SCM_EOL;
385 immutable_property_alist_ = SCM_EOL;
387 set_extent (SCM_EOL, Y_AXIS);
388 set_extent (SCM_EOL, X_AXIS);
390 for (int a= X_AXIS; a <= Y_AXIS; a++)
392 dim_cache_[a].offset_callbacks_ = SCM_EOL;
393 dim_cache_[a].offsets_left_ = 0;
396 #if 0
398 This can make debugging a little easier: we can still know what
399 the object used to be. However, since all its links have been
400 broken, it's usually more convenient to set a conditional
401 breakpoint in GDB before the property lists are wiped.
403 mutable_property_alist_ = scm_acons (ly_symbol2scm ("name"),
404 scm_makfrom0str (nm.to_str0()),
405 mutable_property_alist_
407 #endif
410 void
411 Grob::handle_prebroken_dependencies ()
414 Don't do this in the derived method, since we want to keep access to
415 mutable_property_alist_ centralized.
417 if (original_)
419 Item * it = dynamic_cast<Item*> (this);
420 substitute_mutable_properties (gh_int2scm (it->break_status_dir ()),
421 original_->mutable_property_alist_);
425 Grob*
426 Grob::find_broken_piece (System*) const
428 return 0;
432 translate in one direction
434 void
435 Grob::translate_axis (Real y, Axis a)
437 if (isinf (y) || isnan (y))
438 programming_error (_ (INFINITY_MSG));
439 else
441 dim_cache_[a].offset_ += y;
447 Find the offset relative to D. If D equals THIS, then it is 0.
448 Otherwise, it recursively defd as
450 OFFSET_ + PARENT_L_->relative_coordinate (D)
452 Real
453 Grob::relative_coordinate (Grob const*refp, Axis a) const
455 if (refp == this)
456 return 0.0;
459 We catch PARENT_L_ == nil case with this, but we crash if we did
460 not ask for the absolute coordinate (ie. REFP == nil.)
463 if (refp == dim_cache_[a].parent_)
464 return get_offset (a);
465 else
466 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
472 Invoke callbacks to get offset relative to parent.
474 Real
475 Grob::get_offset (Axis a) const
477 Grob *me = (Grob*) this;
478 while (dim_cache_[a].offsets_left_)
480 int l = --me->dim_cache_[a].offsets_left_;
481 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, gh_int2scm (l));
482 SCM retval = gh_call2 (cb, self_scm (), gh_int2scm (a));
484 Real r = gh_scm2double (retval);
485 if (isinf (r) || isnan (r))
487 programming_error (INFINITY_MSG);
488 r = 0.0;
490 me->dim_cache_[a].offset_ +=r;
492 return dim_cache_[a].offset_;
496 MAKE_SCHEME_CALLBACK (Grob,point_dimension_callback,2);
498 Grob::point_dimension_callback (SCM , SCM)
500 return ly_interval2scm (Interval (0,0));
503 bool
504 Grob::empty_b (Axis a)const
506 return ! (gh_pair_p (dim_cache_[a].dimension_) ||
507 gh_procedure_p (dim_cache_[a].dimension_));
510 Interval
511 Grob::extent (Grob * refp, Axis a) const
513 Real x = relative_coordinate (refp, a);
516 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
517 Interval ext ;
518 if (gh_pair_p (d->dimension_))
520 else if (gh_procedure_p (d->dimension_))
523 FIXME: add doco on types, and should typecheck maybe?
525 d->dimension_= gh_call2 (d->dimension_, self_scm (), gh_int2scm (a));
527 else
528 return ext;
530 if (!gh_pair_p (d->dimension_))
531 return ext;
533 ext = ly_scm2interval (d->dimension_);
535 SCM extra = get_grob_property (a == X_AXIS
536 ? "extra-X-extent"
537 : "extra-Y-extent");
540 signs ?
542 if (gh_pair_p (extra))
544 ext[BIGGER] += gh_scm2double (ly_cdr (extra));
545 ext[SMALLER] += gh_scm2double (ly_car (extra));
548 extra = get_grob_property (a == X_AXIS
549 ? "minimum-X-extent"
550 : "minimum-Y-extent");
551 if (gh_pair_p (extra))
553 ext.unite (Interval (gh_scm2double (ly_car (extra)),
554 gh_scm2double (ly_cdr (extra))));
557 ext.translate (x);
559 return ext;
563 Find the group-element which has both #this# and #s#
565 Grob *
566 Grob::common_refpoint (Grob const* s, Axis a) const
569 I don't like the quadratic aspect of this code, but I see no other
570 way. The largest chain of parents might be 10 high or so, so
571 it shouldn't be a real issue. */
572 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
573 for (Grob const * d = s; d; d = d->dim_cache_[a].parent_)
574 if (d == c)
575 return (Grob*)d;
577 return 0;
581 Grob *
582 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
584 for (; gh_pair_p (elist); elist = ly_cdr (elist))
586 Grob * s = unsmob_grob (ly_car (elist));
587 if (!s)
588 continue;
589 if (common)
590 common = common->common_refpoint (s, a);
591 else
592 common = s;
595 return common;
600 Grob *
601 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
603 for (int i = arr.size() ; i--; )
605 Grob * s = arr[i];
606 if (!s)
607 continue;
609 if (common)
610 common = common->common_refpoint (s, a);
611 else
612 common = s;
615 return common;
618 String
619 Grob::name () const
621 SCM meta = get_grob_property ("meta");
622 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
623 nm = (gh_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
624 return gh_symbol_p (nm) ? ly_symbol2string (nm) : classname (this);
627 void
628 Grob::add_offset_callback (SCM cb, Axis a)
630 if (!has_offset_callback_b (cb, a))
632 dim_cache_[a].offset_callbacks_ = gh_cons (cb, dim_cache_[a].offset_callbacks_);
633 dim_cache_[a].offsets_left_ ++;
637 bool
638 Grob::has_extent_callback_b (SCM cb, Axis a)const
640 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
644 bool
645 Grob::has_offset_callback_b (SCM cb, Axis a)const
647 return scm_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
650 void
651 Grob::set_extent (SCM dc, Axis a)
653 dim_cache_[a].dimension_ =dc;
656 void
657 Grob::set_parent (Grob *g, Axis a)
659 dim_cache_[a].parent_ = g;
662 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
664 Grob::fixup_refpoint (SCM smob)
666 Grob *me = unsmob_grob (smob);
667 for (int a = X_AXIS; a < NO_AXES; a ++)
669 Axis ax = (Axis)a;
670 Grob * parent = me->get_parent (ax);
672 if (!parent)
673 continue;
675 if (parent->get_system () != me->get_system () && me->get_system ())
677 Grob * newparent = parent->find_broken_piece (me->get_system ());
678 me->set_parent (newparent, ax);
681 if (Item * i = dynamic_cast<Item*> (me))
683 Item *parenti = dynamic_cast<Item*> (parent);
685 if (parenti && i)
687 Direction my_dir = i->break_status_dir () ;
688 if (my_dir!= parenti->break_status_dir ())
690 Item *newparent = parenti->find_prebroken_piece (my_dir);
691 me->set_parent (newparent, ax);
696 return smob;
699 void
700 Grob::warning (String s)const
702 SCM cause = self_scm();
703 while (Grob * g = unsmob_grob (cause))
705 cause = g->get_grob_property ("cause");
708 if (Music *m = unsmob_music (cause))
710 m->origin()->warning (s);
712 else
713 ::warning (s);
716 void
717 Grob::programming_error (String s)const
719 s = "Programming error: " + s;
720 warning (s);
724 /****************************************************
725 SMOB funcs
726 ****************************************************/
730 IMPLEMENT_SMOBS (Grob);
731 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
734 Grob::mark_smob (SCM ses)
736 Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
737 scm_gc_mark (s->immutable_property_alist_);
739 for (int a =0 ; a < 2; a++)
741 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
742 scm_gc_mark (s->dim_cache_[a].dimension_);
745 don't mark the parents. The pointers in the mutable property
746 list form two tree like structures (one for X relations, one
747 for Y relations). Marking these can be done in limited stack
748 space. If we add the parents, we will jump between X and Y in
749 an erratic manner, leading to much more recursion depth (and
750 core dumps if we link to pthreads.)
754 if (s->original_)
755 scm_gc_mark (s->original_->self_scm ());
757 s->do_derived_mark ();
758 return s->mutable_property_alist_;
762 Grob::print_smob (SCM s, SCM port, scm_print_state *)
764 Grob *sc = (Grob *) ly_cdr (s);
766 scm_puts ("#<Grob ", port);
767 scm_puts ((char *)sc->name ().to_str0 (), port);
770 don't try to print properties, that is too much hassle.
772 scm_puts (" >", port);
773 return 1;
777 Grob::do_derived_mark () const
779 return SCM_EOL;
784 void
785 Grob::discretionary_processing ()
789 bool
790 Grob::internal_has_interface (SCM k)
792 SCM ifs = get_grob_property ("interfaces");
794 return scm_memq (k, ifs) != SCM_BOOL_F;
798 /** Return Array of Grobs in SCM list L */
799 Link_array<Grob>
800 ly_scm2grobs (SCM l)
802 Link_array<Grob> arr;
804 for (SCM s = l; gh_pair_p (s); s = gh_cdr (s))
806 SCM e = gh_car (s);
807 arr.push (unsmob_grob (e));
810 arr.reverse ();
811 return arr;
814 /** Return SCM list of Grob array A */
816 ly_grobs2scm (Link_array<Grob> a)
818 SCM s = SCM_EOL;
819 for (int i = a.size (); i; i--)
820 s = gh_cons (a[i-1]->self_scm (), s);
822 return s;
826 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
828 ADD_INTERFACE (Grob, "grob-interface",
829 "In music notation, lots of symbols are related in some way. You can\n"
830 "think of music notation as a graph where nodes are formed by the\n"
831 "symbols, and the arcs by their relations. A grob is a node in that\n"
832 "graph. The directed edges in the graph are formed by references to\n"
833 "other grobs (i.e. pointers). This big graph of grobs specifies the\n"
834 "notation problem. The solution of this problem is a description of the\n"
835 "printout in closed form, i.e. a list of values. These values are\n"
836 "Molecules.\n"
837 "\n"
838 "All grobs have an X and Y-position on the page. These X and Y positions\n"
839 "are stored in a relative format, so they can easily be combined by\n"
840 "stacking them, hanging one grob to the side of another, and coupling\n"
841 "them into a grouping-grob.\n"
842 "\n"
843 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
844 "is stored relative to that reference point. For example the X-reference\n"
845 "point of a staccato dot usually is the note head that it applies\n"
846 "to. When the note head is moved, the staccato dot moves along\n"
847 "automatically.\n"
848 "\n"
849 "A grob is often associated with a symbol, but some grobs do not print\n"
850 "any symbols. They take care of grouping objects. For example, there is a\n"
851 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
852 "is also an abstract grob: it only moves around chords, but doesn't print\n"
853 "anything.\n"
855 "X-offset-callbacks Y-offset-callbacks X-extent-callback molecule cause "
856 "Y-extent-callback molecule-callback extra-offset spacing-procedure "
857 "staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
858 "causes meta layer before-line-breaking-callback "
859 "after-line-breaking-callback extra-Y-extent minimum-X-extent "
860 "minimum-Y-extent transparent");