2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2015 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"
39 #include "unpure-pure-container.hh"
47 return new Grob (*this);
50 Grob::Grob (SCM basicprops)
53 /* 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 (scm_is_null (get_property_data ("X-extent")))
80 set_property ("X-extent", Grob::stencil_width_proc);
81 if (scm_is_null (get_property_data ("Y-extent")))
82 set_property ("Y-extent",
83 Unpure_pure_container::make_smob (Grob::stencil_height_proc,
84 Grob::pure_stencil_height_proc));
85 if (scm_is_null (get_property_data ("vertical-skylines")))
86 set_property ("vertical-skylines",
87 Unpure_pure_container::make_smob (Grob::simple_vertical_skylines_from_extents_proc,
88 Grob::pure_simple_vertical_skylines_from_extents_proc));
89 if (scm_is_null (get_property_data ("horizontal-skylines")))
90 set_property ("horizontal-skylines",
91 Unpure_pure_container::make_smob (Grob::simple_horizontal_skylines_from_extents_proc,
92 Grob::pure_simple_horizontal_skylines_from_extents_proc));
95 Grob::Grob (Grob const &s)
98 original_ = (Grob *) & s;
100 immutable_property_alist_ = s.immutable_property_alist_;
101 mutable_property_alist_ = SCM_EOL;
103 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
104 dim_cache_ [a] = s.dim_cache_ [a];
106 interfaces_ = s.interfaces_;
107 object_alist_ = SCM_EOL;
113 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
120 /****************************************************************
122 ****************************************************************/
125 Grob::get_stencil () const
130 SCM stil = get_property ("stencil");
131 return Stencil::unsmob (stil);
135 Grob::get_print_stencil () const
137 SCM stil = get_property ("stencil");
140 if (Stencil *m = Stencil::unsmob (stil))
143 bool transparent = to_boolean (get_property ("transparent"));
146 retval = Stencil (m->extent_box (), SCM_EOL);
149 SCM expr = m->expr ();
150 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
153 retval = Stencil (m->extent_box (), expr);
156 SCM rot = get_property ("rotation");
157 if (scm_is_pair (rot))
159 Real angle = scm_to_double (scm_car (rot));
160 Real x = scm_to_double (scm_cadr (rot));
161 Real y = scm_to_double (scm_caddr (rot));
163 retval.rotate_degrees (angle, Offset (x, y));
166 /* color support... see interpret_stencil_expression () for more... */
167 SCM color = get_property ("color");
168 if (scm_is_pair (color))
170 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
174 retval = Stencil (retval.extent_box (), expr);
177 /* process whiteout */
178 /* a grob has to be visible, otherwise the whiteout property has no effect */
179 if (!transparent && to_boolean (get_property ("whiteout")))
181 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
182 /* to add a round-filled-box stencil to the stencil list */
184 = *Stencil::unsmob (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
185 retval.smobbed_copy ()));
188 SCM id = get_property ("id");
189 if (scm_is_string (id))
191 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
195 retval = Stencil (retval.extent_box (), expr);
203 /****************************************************************
205 ****************************************************************/
207 Grob::do_break_processing ()
212 Grob::discretionary_processing ()
217 Grob::get_system () const
222 /* This version of get_system is more reliable than this->get_system ()
223 before line-breaking has been done, at which point there is only
224 one system in the whole score and we can find it just by following
227 Grob::get_system (Grob *me)
229 Grob *p = me->get_parent (X_AXIS);
230 return p ? get_system (p) : dynamic_cast<System *>(me);
234 Grob::handle_broken_dependencies ()
236 Spanner *sp = dynamic_cast<Spanner *> (this);
237 if (original () && sp)
241 /* THIS, SP is the original spanner. We use a special function
242 because some Spanners have enormously long lists in their
243 properties, and a special function fixes FOO */
245 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
246 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
248 System *system = get_system ();
252 && common_refpoint (system, X_AXIS)
253 && common_refpoint (system, Y_AXIS))
254 substitute_object_links (system->self_scm (), object_alist_);
255 else if (dynamic_cast<System *> (this))
256 substitute_object_links (SCM_UNDEFINED, object_alist_);
258 /* THIS element is `invalid'; it has been removed from all
259 dependencies, so let's junk the element itself.
261 Do not do this for System, since that would remove references
262 to the originals of score-grobs, which get then GC'd (a bad
267 /* Note that we still want references to this element to be
268 rearranged, and not silently thrown away, so we keep pointers like
269 {broken_into_{drul, array}, original}
277 for (int a = X_AXIS; a < NO_AXES; a++)
278 dim_cache_[a].clear ();
280 mutable_property_alist_ = SCM_EOL;
281 object_alist_ = SCM_EOL;
282 immutable_property_alist_ = SCM_EOL;
283 interfaces_ = SCM_EOL;
287 Grob::handle_prebroken_dependencies ()
289 /* Don't do this in the derived method, since we want to keep access to
290 object_alist_ centralized. */
293 Item *it = dynamic_cast<Item *> (this);
294 substitute_object_links (scm_from_int (it->break_status_dir ()),
295 original ()->object_alist_);
300 Grob::find_broken_piece (System *) const
305 /****************************************************************
307 ****************************************************************/
310 Grob::translate_axis (Real y, Axis a)
312 if (isinf (y) || isnan (y))
314 programming_error ("Infinity or NaN encountered");
318 if (!dim_cache_[a].offset_)
319 dim_cache_[a].offset_ = new Real (y);
321 *dim_cache_[a].offset_ += y;
324 /* Find the offset relative to D. If D equals THIS, then it is 0.
325 Otherwise, it recursively defd as
327 OFFSET_ + PARENT_L_->relative_coordinate (D) */
329 Grob::relative_coordinate (Grob const *refp, Axis a) const
331 /* eaa - hmmm, should we do a programming_error() here? */
332 if ((this == NULL) || (refp == this))
335 /* We catch PARENT_L_ == nil case with this, but we crash if we did
336 not ask for the absolute coordinate (ie. REFP == nil.) */
337 Real off = get_offset (a);
338 if (refp == dim_cache_[a].parent_)
341 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
347 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
354 if (dim_cache_[Y_AXIS].offset_)
356 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
357 programming_error ("cyclic chain in pure-Y-offset callbacks");
359 off = *dim_cache_[Y_AXIS].offset_;
363 SCM proc = get_property_data ("Y-offset");
365 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
366 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
367 off = robust_scm2double (call_pure_function (proc,
368 scm_list_1 (self_scm ()),
371 del_property ("pure-Y-offset-in-progress");
372 delete dim_cache_[Y_AXIS].offset_;
373 dim_cache_[Y_AXIS].offset_ = 0;
376 /* we simulate positioning-done if we are the child of a VerticalAlignment,
377 but only if we don't have a cached offset. If we do have a cached offset,
378 it probably means that the Alignment was fixed and it has already been
381 if (Grob *p = get_parent (Y_AXIS))
384 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
385 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
387 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
392 /* Invoke callbacks to get offset relative to parent. */
394 Grob::get_offset (Axis a) const
396 if (dim_cache_[a].offset_)
397 return *dim_cache_[a].offset_;
399 Grob *me = (Grob *) this;
401 SCM sym = axis_offset_symbol (a);
402 me->dim_cache_[a].offset_ = new Real (0.0);
405 UGH: can't fold next 2 statements together. Apparently GCC thinks
406 dim_cache_[a].offset_ is unaliased.
408 Real off = robust_scm2double (get_property (sym), 0.0);
409 if (me->dim_cache_[a].offset_)
411 *me->dim_cache_[a].offset_ += off;
412 me->del_property (sym);
413 return *me->dim_cache_[a].offset_;
420 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
422 if (pure && a != Y_AXIS)
423 programming_error ("tried to get pure X-offset");
424 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
425 : relative_coordinate (refp, a);
428 /****************************************************************
430 ****************************************************************/
433 Grob::flush_extent_cache (Axis axis)
435 if (dim_cache_[axis].extent_)
438 Ugh, this is not accurate; will flush property, causing
439 callback to be called if.
441 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
442 delete dim_cache_[axis].extent_;
443 dim_cache_[axis].extent_ = 0;
444 if (get_parent (axis))
445 get_parent (axis)->flush_extent_cache (axis);
450 Grob::extent (Grob *refp, Axis a) const
452 Real offset = relative_coordinate (refp, a);
454 if (dim_cache_[a].extent_)
456 real_ext = *dim_cache_[a].extent_;
461 Order is significant: ?-extent may trigger suicide.
463 SCM ext = (a == X_AXIS)
464 ? get_property ("X-extent")
465 : get_property ("Y-extent");
466 if (is_number_pair (ext))
467 real_ext.unite (ly_scm2interval (ext));
469 SCM min_ext = (a == X_AXIS)
470 ? get_property ("minimum-X-extent")
471 : get_property ("minimum-Y-extent");
472 if (is_number_pair (min_ext))
473 real_ext.unite (ly_scm2interval (min_ext));
475 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
478 // We never want nan, so we avoid shifting infinite values.
480 real_ext.translate(offset);
482 this->warning(_f ("ignored infinite %s-offset",
483 a == X_AXIS ? "X" : "Y"));
489 Grob::pure_height (Grob *refp, int start, int end)
491 SCM iv_scm = get_pure_property ("Y-extent", start, end);
492 Interval iv = robust_scm2interval (iv_scm, Interval ());
493 Real offset = pure_relative_y_coordinate (refp, start, end);
495 SCM min_ext = get_property ("minimum-Y-extent");
497 /* we don't add minimum-Y-extent if the extent is empty. This solves
498 a problem with Hara-kiri spanners. They would request_suicide and
499 return empty extents, but we would force them here to be large. */
500 if (!iv.is_empty () && is_number_pair (min_ext))
501 iv.unite (ly_scm2interval (min_ext));
504 iv.translate (offset);
509 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
511 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
515 Grob::spanned_rank_interval () const
517 return Interval_t<int> (-1, 0);
521 Grob::pure_is_visible (int /* start */, int /* end */) const
526 /* Sort grobs according to their starting column. */
528 Grob::less (Grob *g1, Grob *g2)
530 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
533 /****************************************************************
535 ****************************************************************/
537 /* Find the group-element which has both #this# and #s# */
539 Grob::common_refpoint (Grob const *s, Axis a) const
542 /* Catching the trivial cases is likely costlier than just running
543 through: one can't avoid going to the respective chain ends
544 anyway. We might save the second run through when the chain ends
545 differ, but keeping track of the ends makes the loop more costly.
552 for (c = this; c; ++balance)
553 c = c->dim_cache_[a].parent_;
555 for (d = s; d; --balance)
556 d = d->dim_cache_[a].parent_;
558 /* Cut down ancestry to same size */
560 for (c = this; balance > 0; --balance)
561 c = c->dim_cache_[a].parent_;
563 for (d = s; balance < 0; ++balance)
564 d = d->dim_cache_[a].parent_;
566 /* Now find point where our lineages converge */
569 c = c->dim_cache_[a].parent_;
570 d = d->dim_cache_[a].parent_;
577 Grob::set_parent (Grob *g, Axis a)
579 dim_cache_[a].parent_ = g;
583 Grob::get_parent (Axis a) const
585 return dim_cache_[a].parent_;
589 Grob::fixup_refpoint ()
591 for (int a = X_AXIS; a < NO_AXES; a++)
594 Grob *parent = get_parent (ax);
599 if (parent->get_system () != get_system () && get_system ())
601 Grob *newparent = parent->find_broken_piece (get_system ());
602 set_parent (newparent, ax);
605 if (Item *i = dynamic_cast<Item *> (this))
607 Item *parenti = dynamic_cast<Item *> (parent);
611 Direction my_dir = i->break_status_dir ();
612 if (my_dir != parenti->break_status_dir ())
614 Item *newparent = parenti->find_prebroken_piece (my_dir);
615 set_parent (newparent, ax);
622 /****************************************************************
624 ****************************************************************/
627 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
631 if (Align_interface::has_interface (g))
632 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
633 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
638 Grob::get_root_vertical_alignment (Grob *g)
640 return get_maybe_root_vertical_alignment (g, 0);
644 Grob::get_vertical_axis_group (Grob *g)
648 if (!g->get_parent (Y_AXIS))
650 if (Axis_group_interface::has_interface (g)
651 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
653 return get_vertical_axis_group (g->get_parent (Y_AXIS));
658 Grob::get_vertical_axis_group_index (Grob *g)
660 Grob *val = get_root_vertical_alignment (g);
663 Grob *vax = get_vertical_axis_group (g);
664 extract_grob_set (val, "elements", elts);
665 for (vsize i = 0; i < elts.size (); i++)
668 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
673 Grob::vertical_less (Grob *g1, Grob *g2)
675 return internal_vertical_less (g1, g2, false);
679 Grob::pure_vertical_less (Grob *g1, Grob *g2)
681 return internal_vertical_less (g1, g2, true);
685 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
687 Grob *vag = get_root_vertical_alignment (g1);
690 g1->programming_error ("grob does not belong to a VerticalAlignment?");
694 Grob *ag1 = get_vertical_axis_group (g1);
695 Grob *ag2 = get_vertical_axis_group (g2);
697 extract_grob_set (vag, "elements", elts);
699 if (ag1 == ag2 && !pure)
701 Grob *common = g1->common_refpoint (g2, Y_AXIS);
702 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
705 for (vsize i = 0; i < elts.size (); i++)
713 g1->programming_error ("could not place this grob in its axis group");
717 /****************************************************************
719 ****************************************************************/
721 Grob::programming_error (const string &s) const
723 SCM cause = self_scm ();
724 while (Grob *g = Grob::unsmob (cause))
725 cause = g->get_property ("cause");
727 /* ES TODO: cause can't be Music*/
728 if (Music *m = Music::unsmob (cause))
729 m->origin ()->programming_error (s);
730 else if (Stream_event *ev = Stream_event::unsmob (cause))
731 ev->origin ()->programming_error (s);
733 ::programming_error (s);
737 Grob::warning (const string &s) const
739 SCM cause = self_scm ();
740 while (Grob *g = Grob::unsmob (cause))
741 cause = g->get_property ("cause");
743 /* ES TODO: cause can't be Music*/
744 if (Music *m = Music::unsmob (cause))
745 m->origin ()->warning (s);
746 else if (Stream_event *ev = Stream_event::unsmob (cause))
747 ev->origin ()->warning (s);
755 SCM meta = get_property ("meta");
756 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
757 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
758 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
762 "A grob represents a piece of music notation.\n"
764 "All grobs have an X and Y@tie{}position on the page. These"
765 " X and Y@tie{}positions are stored in a relative format, thus"
766 " they can easily be combined by stacking them, hanging one"
767 " grob to the side of another, or coupling them into grouping"
770 "Each grob has a reference point (a.k.a.@: parent): The"
771 " position of a grob is stored relative to that reference"
772 " point. For example, the X@tie{}reference point of a staccato"
773 " dot usually is the note head that it applies to. When the"
774 " note head is moved, the staccato dot moves along"
777 "A grob is often associated with a symbol, but some grobs do"
778 " not print any symbols. They take care of grouping objects."
779 " For example, there is a separate grob that stacks staves"
780 " vertically. The @ref{NoteCollision} object is also an"
781 " abstract grob: It only moves around chords, but doesn't print"
784 "Grobs have properties (Scheme variables) that can be read and"
785 " set. Two types of them exist: immutable and mutable."
786 " Immutable variables define the default style and behavior."
787 " They are shared between many objects. They can be changed"
788 " using @code{\\override} and @code{\\revert}. Mutable"
789 " properties are variables that are specific to one grob."
790 " Typically, lists of other objects, or results from"
791 " computations are stored in mutable properties. In"
792 " particular, every call to @code{ly:grob-set-property!}"
793 " (or its C++ equivalent) sets a mutable property.\n"
795 "The properties @code{after-line-breaking} and"
796 " @code{before-line-breaking} are dummies that are not"
797 " user-serviceable.",
804 "after-line-breaking "
806 "axis-group-parent-X "
807 "axis-group-parent-Y "
808 "before-line-breaking "
816 "horizontal-skylines "
822 "parenthesis-friends "
823 "pure-Y-offset-in-progress "
825 "skyline-horizontal-padding "
834 /****************************************************************
836 ****************************************************************/
839 grob_stencil_extent (Grob *me, Axis a)
841 Stencil *m = me->get_stencil ();
845 return ly_interval2scm (e);
848 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
850 Grob::stencil_height (SCM smob)
852 Grob *me = Grob::unsmob (smob);
853 return grob_stencil_extent (me, Y_AXIS);
856 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
858 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
860 Grob *me = Grob::unsmob (smob);
861 if (Stencil::is_smob (me->get_property_data ("stencil")))
862 return grob_stencil_extent (me, Y_AXIS);
864 return ly_interval2scm (Interval ());
868 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
870 Grob::y_parent_positioning (SCM smob)
872 Grob *me = Grob::unsmob (smob);
873 Grob *par = me->get_parent (Y_AXIS);
875 (void) par->get_property ("positioning-done");
877 return scm_from_double (0.0);
880 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
882 Grob::x_parent_positioning (SCM smob)
884 Grob *me = Grob::unsmob (smob);
886 Grob *par = me->get_parent (X_AXIS);
888 (void) par->get_property ("positioning-done");
890 return scm_from_double (0.0);
893 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
895 Grob::stencil_width (SCM smob)
897 Grob *me = Grob::unsmob (smob);
898 return grob_stencil_extent (me, X_AXIS);
902 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
904 for (; scm_is_pair (elist); elist = scm_cdr (elist))
905 if (Grob *s = Grob::unsmob (scm_car (elist)))
908 common = common->common_refpoint (s, a);
917 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
919 for (vsize i = 0; i < arr.size (); i++)
921 common = common->common_refpoint (arr[i], a);
929 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
931 set<Grob *>::iterator it;
933 for (it = arr.begin (); it != arr.end (); it++)
935 common = common->common_refpoint (*it, a);
943 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
945 Interval ext = me->extent (refpoint, a);
947 ext.add_point (me->relative_coordinate (refpoint, a));
952 // Checks whether there is a vertical alignment in the chain of
953 // parents between this and commony.
955 Grob::check_cross_staff (Grob *commony)
957 if (Align_interface::has_interface (commony))
960 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
961 if (Align_interface::has_interface (g))
969 indirect_less (Grob **a, Grob **b)
971 // Use original order as tie breaker. That gives us a stable sort
972 // at the lower price tag of an unstable one, and we want a stable
973 // sort in order to reliably retain the first instance of a grob
975 return *a < *b || (*a == *b && a < b);
980 indirect_eq (Grob **a, Grob **b)
987 direct_less (Grob **a, Grob **b)
992 // uniquify uniquifies on the memory addresses of the Grobs, but then
993 // uses the original order. This makes results independent from the
994 // memory allocation of Grobs.
997 uniquify (vector <Grob *> & grobs)
999 vector <Grob **> vec (grobs.size ());
1000 for (vsize i = 0; i < grobs.size (); i++)
1002 vector_sort (vec, indirect_less);
1003 vec.erase (unique (vec.begin (), vec.end (), indirect_eq), vec.end ());
1004 vector_sort (vec, direct_less);
1006 // Since the output is a sorted copy of the input with some elements
1007 // removed, we can fill in the vector in-place if we do it starting
1009 for (vsize i = 0; i < vec.size (); i++)
1011 grobs.erase (grobs.begin () + vec.size (), grobs.end ());