2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
25 #include "align-interface.hh"
26 #include "axis-group-interface.hh"
28 #include "international.hh"
33 #include "output-def.hh"
34 #include "pointer-group-interface.hh"
35 #include "program-option.hh"
37 #include "stream-event.hh"
41 #include "ly-smobs.icc"
46 return new Grob (*this);
49 Grob::Grob (SCM basicprops)
52 /* FIXME: default should be no callback. */
56 interfaces_ = SCM_EOL;
57 immutable_property_alist_ = basicprops;
58 mutable_property_alist_ = SCM_EOL;
59 object_alist_ = SCM_EOL;
61 /* We do smobify_self () as the first step. Since the object lives
62 on the heap, none of its SCM variables are protected from
63 GC. After smobify_self (), they are. */
66 SCM meta = get_property ("meta");
67 if (scm_is_pair (meta))
69 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
71 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
72 if (scm_is_pair (object_cbs))
74 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
75 set_object (scm_caar (s), scm_cdar (s));
79 if (get_property_data ("X-extent") == SCM_EOL)
80 set_property ("X-extent", Grob::stencil_width_proc);
81 if (get_property_data ("Y-extent") == SCM_EOL)
82 set_property ("Y-extent", Grob::stencil_height_proc);
83 if (get_property_data ("vertical-skylines") == SCM_EOL)
84 set_property ("vertical-skylines", Grob::simple_vertical_skylines_from_stencil_proc);
85 if (get_property_data ("horizontal-skylines") == SCM_EOL)
86 set_property ("horizontal-skylines", Grob::simple_horizontal_skylines_from_stencil_proc);
89 Grob::Grob (Grob const &s)
90 : dim_cache_ (s.dim_cache_)
92 original_ = (Grob *) & s;
95 immutable_property_alist_ = s.immutable_property_alist_;
96 mutable_property_alist_ = SCM_EOL;
97 interfaces_ = s.interfaces_;
98 object_alist_ = SCM_EOL;
104 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
111 /****************************************************************
113 ****************************************************************/
116 Grob::get_stencil () const
121 SCM stil = get_property ("stencil");
122 return unsmob_stencil (stil);
126 Grob::get_print_stencil () const
128 SCM stil = get_property ("stencil");
131 if (Stencil *m = unsmob_stencil (stil))
134 bool transparent = to_boolean (get_property ("transparent"));
137 retval = Stencil (m->extent_box (), SCM_EOL);
140 SCM expr = m->expr ();
141 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
144 retval = Stencil (m->extent_box (), expr);
147 SCM rot = get_property ("rotation");
148 if (scm_is_pair (rot))
150 Real angle = scm_to_double (scm_car (rot));
151 Real x = scm_to_double (scm_cadr (rot));
152 Real y = scm_to_double (scm_caddr (rot));
154 retval.rotate_degrees (angle, Offset (x, y));
157 /* color support... see interpret_stencil_expression () for more... */
158 SCM color = get_property ("color");
159 if (scm_is_pair (color))
161 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
165 retval = Stencil (retval.extent_box (), expr);
168 /* process whiteout */
169 /* a grob has to be visible, otherwise the whiteout property has no effect */
170 if (!transparent && to_boolean (get_property ("whiteout")))
172 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
173 /* to add a round-filled-box stencil to the stencil list */
175 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
176 retval.smobbed_copy ()));
179 SCM id = get_property ("id");
180 if (scm_is_string (id))
182 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
186 retval = Stencil (retval.extent_box (), expr);
194 /****************************************************************
196 ****************************************************************/
198 Grob::do_break_processing ()
203 Grob::discretionary_processing ()
208 Grob::get_system () const
213 /* This version of get_system is more reliable than this->get_system ()
214 before line-breaking has been done, at which point there is only
215 one system in the whole score and we can find it just by following
218 Grob::get_system (Grob *me)
220 Grob *p = me->get_parent (X_AXIS);
221 return p ? get_system (p) : dynamic_cast<System *>(me);
225 Grob::handle_broken_dependencies ()
227 Spanner *sp = dynamic_cast<Spanner *> (this);
228 if (original () && sp)
232 /* THIS, SP is the original spanner. We use a special function
233 because some Spanners have enormously long lists in their
234 properties, and a special function fixes FOO */
236 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
237 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
239 System *system = get_system ();
243 && common_refpoint (system, X_AXIS)
244 && common_refpoint (system, Y_AXIS))
245 substitute_object_links (system->self_scm (), object_alist_);
246 else if (dynamic_cast<System *> (this))
247 substitute_object_links (SCM_UNDEFINED, object_alist_);
249 /* THIS element is `invalid'; it has been removed from all
250 dependencies, so let's junk the element itself.
252 Do not do this for System, since that would remove references
253 to the originals of score-grobs, which get then GC'd (a bad
258 /* Note that we still want references to this element to be
259 rearranged, and not silently thrown away, so we keep pointers like
260 {broken_into_{drul, array}, original}
268 for (int a = X_AXIS; a < NO_AXES; a++)
269 dim_cache_[a].clear ();
271 mutable_property_alist_ = SCM_EOL;
272 object_alist_ = SCM_EOL;
273 immutable_property_alist_ = SCM_EOL;
274 interfaces_ = SCM_EOL;
278 Grob::handle_prebroken_dependencies ()
280 /* Don't do this in the derived method, since we want to keep access to
281 object_alist_ centralized. */
284 Item *it = dynamic_cast<Item *> (this);
285 substitute_object_links (scm_from_int (it->break_status_dir ()),
286 original ()->object_alist_);
291 Grob::find_broken_piece (System *) const
296 /****************************************************************
298 ****************************************************************/
301 Grob::translate_axis (Real y, Axis a)
303 if (isinf (y) || isnan (y))
305 programming_error ("Infinity or NaN encountered");
309 if (!dim_cache_[a].offset_)
310 dim_cache_[a].offset_ = new Real (y);
312 *dim_cache_[a].offset_ += y;
315 /* Find the offset relative to D. If D equals THIS, then it is 0.
316 Otherwise, it recursively defd as
318 OFFSET_ + PARENT_L_->relative_coordinate (D) */
320 Grob::relative_coordinate (Grob const *refp, Axis a) const
322 /* eaa - hmmm, should we do a programming_error() here? */
323 if ((this == NULL) || (refp == this))
326 /* We catch PARENT_L_ == nil case with this, but we crash if we did
327 not ask for the absolute coordinate (ie. REFP == nil.) */
328 Real off = get_offset (a);
329 if (refp == dim_cache_[a].parent_)
332 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
338 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
345 if (dim_cache_[Y_AXIS].offset_)
347 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
348 programming_error ("cyclic chain in pure-Y-offset callbacks");
350 off = *dim_cache_[Y_AXIS].offset_;
354 SCM proc = get_property_data ("Y-offset");
356 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
357 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
358 off = robust_scm2double (call_pure_function (proc,
359 scm_list_1 (self_scm ()),
362 del_property ("pure-Y-offset-in-progress");
363 delete dim_cache_[Y_AXIS].offset_;
364 dim_cache_[Y_AXIS].offset_ = 0;
367 /* we simulate positioning-done if we are the child of a VerticalAlignment,
368 but only if we don't have a cached offset. If we do have a cached offset,
369 it probably means that the Alignment was fixed and it has already been
372 if (Grob *p = get_parent (Y_AXIS))
375 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
376 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
378 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
383 /* Invoke callbacks to get offset relative to parent. */
385 Grob::get_offset (Axis a) const
387 if (dim_cache_[a].offset_)
388 return *dim_cache_[a].offset_;
390 Grob *me = (Grob *) this;
392 SCM sym = axis_offset_symbol (a);
393 me->dim_cache_[a].offset_ = new Real (0.0);
396 UGH: can't fold next 2 statements together. Apparently GCC thinks
397 dim_cache_[a].offset_ is unaliased.
399 Real off = robust_scm2double (internal_get_property (sym), 0.0);
400 if (me->dim_cache_[a].offset_)
402 *me->dim_cache_[a].offset_ += off;
403 me->del_property (sym);
404 return *me->dim_cache_[a].offset_;
411 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
413 if (pure && a != Y_AXIS)
414 programming_error ("tried to get pure X-offset");
415 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
416 : relative_coordinate (refp, a);
419 /****************************************************************
421 ****************************************************************/
424 Grob::flush_extent_cache (Axis axis)
426 if (dim_cache_[axis].extent_)
429 Ugh, this is not accurate; will flush property, causing
430 callback to be called if.
432 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
433 delete dim_cache_[axis].extent_;
434 dim_cache_[axis].extent_ = 0;
435 if (get_parent (axis))
436 get_parent (axis)->flush_extent_cache (axis);
441 Grob::extent (Grob *refp, Axis a) const
443 Real offset = relative_coordinate (refp, a);
445 if (dim_cache_[a].extent_)
447 real_ext = *dim_cache_[a].extent_;
452 Order is significant: ?-extent may trigger suicide.
456 ? ly_symbol2scm ("X-extent")
457 : ly_symbol2scm ("Y-extent");
459 SCM ext = internal_get_property (ext_sym);
460 if (is_number_pair (ext))
461 real_ext.unite (ly_scm2interval (ext));
465 ? ly_symbol2scm ("minimum-X-extent")
466 : ly_symbol2scm ("minimum-Y-extent");
467 SCM min_ext = internal_get_property (min_ext_sym);
468 if (is_number_pair (min_ext))
469 real_ext.unite (ly_scm2interval (min_ext));
471 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
474 // We never want nan, so we avoid shifting infinite values.
475 for (LEFT_and_RIGHT (d))
476 if (!isinf (real_ext[d]))
477 real_ext[d] += offset;
483 Grob::pure_height (Grob *refp, int start, int end)
485 SCM iv_scm = get_pure_property ("Y-extent", start, end);
486 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
487 Real offset = pure_relative_y_coordinate (refp, start, end);
489 SCM min_ext = get_property ("minimum-Y-extent");
491 /* we don't add minimum-Y-extent if the extent is empty. This solves
492 a problem with Hara-kiri spanners. They would request_suicide and
493 return empty extents, but we would force them here to be large. */
494 if (!iv.is_empty () && is_number_pair (min_ext))
495 iv.unite (ly_scm2interval (min_ext));
498 iv.translate (offset);
503 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
505 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
509 Grob::spanned_rank_interval () const
511 return Interval_t<int> (-1, 0);
515 Grob::pure_is_visible (int /* start */, int /* end */) const
520 /* Sort grobs according to their starting column. */
522 Grob::less (Grob *g1, Grob *g2)
524 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
527 /****************************************************************
529 ****************************************************************/
531 /* Find the group-element which has both #this# and #s# */
533 Grob::common_refpoint (Grob const *s, Axis a) const
536 /* Catching the trivial cases is likely costlier than just running
537 through: one can't avoid going to the respective chain ends
538 anyway. We might save the second run through when the chain ends
539 differ, but keeping track of the ends makes the loop more costly.
546 for (c = this; c; ++balance)
547 c = c->dim_cache_[a].parent_;
549 for (d = s; d; --balance)
550 d = d->dim_cache_[a].parent_;
552 /* Cut down ancestry to same size */
554 for (c = this; balance > 0; --balance)
555 c = c->dim_cache_[a].parent_;
557 for (d = s; balance < 0; ++balance)
558 d = d->dim_cache_[a].parent_;
560 /* Now find point where our lineages converge */
563 c = c->dim_cache_[a].parent_;
564 d = d->dim_cache_[a].parent_;
571 Grob::set_parent (Grob *g, Axis a)
573 dim_cache_[a].parent_ = g;
577 Grob::get_parent (Axis a) const
579 return dim_cache_[a].parent_;
583 Grob::fixup_refpoint ()
585 for (int a = X_AXIS; a < NO_AXES; a++)
588 Grob *parent = get_parent (ax);
593 if (parent->get_system () != get_system () && get_system ())
595 Grob *newparent = parent->find_broken_piece (get_system ());
596 set_parent (newparent, ax);
599 if (Item *i = dynamic_cast<Item *> (this))
601 Item *parenti = dynamic_cast<Item *> (parent);
605 Direction my_dir = i->break_status_dir ();
606 if (my_dir != parenti->break_status_dir ())
608 Item *newparent = parenti->find_prebroken_piece (my_dir);
609 set_parent (newparent, ax);
616 /****************************************************************
618 ****************************************************************/
621 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
625 if (Align_interface::has_interface (g))
626 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
627 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
632 Grob::get_root_vertical_alignment (Grob *g)
634 return get_maybe_root_vertical_alignment (g, 0);
638 Grob::get_vertical_axis_group (Grob *g)
642 if (!g->get_parent (Y_AXIS))
644 if (Axis_group_interface::has_interface (g)
645 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
647 return get_vertical_axis_group (g->get_parent (Y_AXIS));
652 Grob::get_vertical_axis_group_index (Grob *g)
654 Grob *val = get_root_vertical_alignment (g);
657 Grob *vax = get_vertical_axis_group (g);
658 extract_grob_set (val, "elements", elts);
659 for (vsize i = 0; i < elts.size (); i++)
662 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
667 Grob::vertical_less (Grob *g1, Grob *g2)
669 return internal_vertical_less (g1, g2, false);
673 Grob::pure_vertical_less (Grob *g1, Grob *g2)
675 return internal_vertical_less (g1, g2, true);
679 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
681 Grob *vag = get_root_vertical_alignment (g1);
684 g1->programming_error ("grob does not belong to a VerticalAlignment?");
688 Grob *ag1 = get_vertical_axis_group (g1);
689 Grob *ag2 = get_vertical_axis_group (g2);
691 extract_grob_set (vag, "elements", elts);
693 if (ag1 == ag2 && !pure)
695 Grob *common = g1->common_refpoint (g2, Y_AXIS);
696 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
699 for (vsize i = 0; i < elts.size (); i++)
707 g1->programming_error ("could not place this grob in its axis group");
711 /****************************************************************
713 ****************************************************************/
715 Grob::programming_error (string s) const
717 SCM cause = self_scm ();
718 while (Grob *g = unsmob_grob (cause))
719 cause = g->get_property ("cause");
721 /* ES TODO: cause can't be Music*/
722 if (Music *m = unsmob_music (cause))
723 m->origin ()->programming_error (s);
724 else if (Stream_event *ev = unsmob_stream_event (cause))
725 ev->origin ()->programming_error (s);
727 ::programming_error (s);
731 Grob::warning (string s) const
733 SCM cause = self_scm ();
734 while (Grob *g = unsmob_grob (cause))
735 cause = g->get_property ("cause");
737 /* ES TODO: cause can't be Music*/
738 if (Music *m = unsmob_music (cause))
739 m->origin ()->warning (s);
740 else if (Stream_event *ev = unsmob_stream_event (cause))
741 ev->origin ()->warning (s);
749 SCM meta = get_property ("meta");
750 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
751 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
752 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
756 "A grob represents a piece of music notation.\n"
758 "All grobs have an X and Y@tie{}position on the page. These"
759 " X and Y@tie{}positions are stored in a relative format, thus"
760 " they can easily be combined by stacking them, hanging one"
761 " grob to the side of another, or coupling them into grouping"
764 "Each grob has a reference point (a.k.a.@: parent): The"
765 " position of a grob is stored relative to that reference"
766 " point. For example, the X@tie{}reference point of a staccato"
767 " dot usually is the note head that it applies to. When the"
768 " note head is moved, the staccato dot moves along"
771 "A grob is often associated with a symbol, but some grobs do"
772 " not print any symbols. They take care of grouping objects."
773 " For example, there is a separate grob that stacks staves"
774 " vertically. The @ref{NoteCollision} object is also an"
775 " abstract grob: It only moves around chords, but doesn't print"
778 "Grobs have properties (Scheme variables) that can be read and"
779 " set. Two types of them exist: immutable and mutable."
780 " Immutable variables define the default style and behavior."
781 " They are shared between many objects. They can be changed"
782 " using @code{\\override} and @code{\\revert}. Mutable"
783 " properties are variables that are specific to one grob."
784 " Typically, lists of other objects, or results from"
785 " computations are stored in mutable properties. In"
786 " particular, every call to @code{ly:grob-set-property!}"
787 " (or its C++ equivalent) sets a mutable property.\n"
789 "The properties @code{after-line-breaking} and"
790 " @code{before-line-breaking} are dummies that are not"
791 " user-serviceable.",
798 "after-line-breaking "
800 "axis-group-parent-X "
801 "axis-group-parent-Y "
802 "before-line-breaking "
812 "horizontal-skylines "
818 "outside-staff-horizontal-padding "
819 "outside-staff-padding "
820 "outside-staff-priority "
821 "pure-Y-offset-in-progress "
823 "skyline-horizontal-padding "
832 /****************************************************************
834 ****************************************************************/
837 grob_stencil_extent (Grob *me, Axis a)
839 Stencil *m = me->get_stencil ();
843 return ly_interval2scm (e);
846 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
848 Grob::stencil_height (SCM smob)
850 Grob *me = unsmob_grob (smob);
851 return grob_stencil_extent (me, Y_AXIS);
854 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
856 Grob::y_parent_positioning (SCM smob)
858 Grob *me = unsmob_grob (smob);
859 Grob *par = me->get_parent (Y_AXIS);
861 (void) par->get_property ("positioning-done");
863 return scm_from_double (0.0);
866 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
868 Grob::x_parent_positioning (SCM smob)
870 Grob *me = unsmob_grob (smob);
872 Grob *par = me->get_parent (X_AXIS);
874 (void) par->get_property ("positioning-done");
876 return scm_from_double (0.0);
879 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
881 Grob::stencil_width (SCM smob)
883 Grob *me = unsmob_grob (smob);
884 return grob_stencil_extent (me, X_AXIS);
888 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
890 for (; scm_is_pair (elist); elist = scm_cdr (elist))
891 if (Grob *s = unsmob_grob (scm_car (elist)))
894 common = common->common_refpoint (s, a);
903 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
905 for (vsize i = 0; i < arr.size (); i++)
907 common = common->common_refpoint (arr[i], a);
915 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
917 set<Grob *>::iterator it;
919 for (it = arr.begin (); it != arr.end (); it++)
921 common = common->common_refpoint (*it, a);
929 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
931 Interval ext = me->extent (refpoint, a);
933 ext.add_point (me->relative_coordinate (refpoint, a));
938 // Checks whether there is a vertical alignment in the chain of
939 // parents between this and commony.
941 Grob::check_cross_staff (Grob *commony)
943 if (Align_interface::has_interface (commony))
946 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
947 if (Align_interface::has_interface (g))