2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2014 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"
46 return new Grob (*this);
49 Grob::Grob (SCM basicprops)
52 /* FIXME: default should be no callback. */
55 interfaces_ = SCM_EOL;
56 immutable_property_alist_ = basicprops;
57 mutable_property_alist_ = SCM_EOL;
58 object_alist_ = SCM_EOL;
60 /* We do smobify_self () as the first step. Since the object lives
61 on the heap, none of its SCM variables are protected from
62 GC. After smobify_self (), they are. */
65 SCM meta = get_property ("meta");
66 if (scm_is_pair (meta))
68 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
70 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
71 if (scm_is_pair (object_cbs))
73 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
74 set_object (scm_caar (s), scm_cdar (s));
78 if (get_property_data ("X-extent") == SCM_EOL)
79 set_property ("X-extent", Grob::stencil_width_proc);
80 if (get_property_data ("Y-extent") == SCM_EOL)
81 set_property ("Y-extent",
82 ly_make_unpure_pure_container (Grob::stencil_height_proc,
83 Grob::pure_stencil_height_proc));
84 if (get_property_data ("vertical-skylines") == SCM_EOL)
85 set_property ("vertical-skylines",
86 ly_make_unpure_pure_container (Grob::simple_vertical_skylines_from_extents_proc,
87 Grob::pure_simple_vertical_skylines_from_extents_proc));
88 if (get_property_data ("horizontal-skylines") == SCM_EOL)
89 set_property ("horizontal-skylines",
90 ly_make_unpure_pure_container (Grob::simple_horizontal_skylines_from_extents_proc,
91 Grob::pure_simple_horizontal_skylines_from_extents_proc));
94 Grob::Grob (Grob const &s)
96 original_ = (Grob *) & s;
98 immutable_property_alist_ = s.immutable_property_alist_;
99 mutable_property_alist_ = SCM_EOL;
101 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
102 dim_cache_ [a] = s.dim_cache_ [a];
104 interfaces_ = s.interfaces_;
105 object_alist_ = SCM_EOL;
111 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
118 /****************************************************************
120 ****************************************************************/
123 Grob::get_stencil () const
128 SCM stil = get_property ("stencil");
129 return Stencil::unsmob (stil);
133 Grob::get_print_stencil () const
135 SCM stil = get_property ("stencil");
138 if (Stencil *m = Stencil::unsmob (stil))
141 bool transparent = to_boolean (get_property ("transparent"));
144 retval = Stencil (m->extent_box (), SCM_EOL);
147 SCM expr = m->expr ();
148 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
151 retval = Stencil (m->extent_box (), expr);
154 SCM rot = get_property ("rotation");
155 if (scm_is_pair (rot))
157 Real angle = scm_to_double (scm_car (rot));
158 Real x = scm_to_double (scm_cadr (rot));
159 Real y = scm_to_double (scm_caddr (rot));
161 retval.rotate_degrees (angle, Offset (x, y));
164 /* color support... see interpret_stencil_expression () for more... */
165 SCM color = get_property ("color");
166 if (scm_is_pair (color))
168 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
172 retval = Stencil (retval.extent_box (), expr);
175 /* process whiteout */
176 /* a grob has to be visible, otherwise the whiteout property has no effect */
177 if (!transparent && to_boolean (get_property ("whiteout")))
179 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
180 /* to add a round-filled-box stencil to the stencil list */
182 = *Stencil::unsmob (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
183 retval.smobbed_copy ()));
186 SCM id = get_property ("id");
187 if (scm_is_string (id))
189 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
193 retval = Stencil (retval.extent_box (), expr);
201 /****************************************************************
203 ****************************************************************/
205 Grob::do_break_processing ()
210 Grob::discretionary_processing ()
215 Grob::get_system () const
220 /* This version of get_system is more reliable than this->get_system ()
221 before line-breaking has been done, at which point there is only
222 one system in the whole score and we can find it just by following
225 Grob::get_system (Grob *me)
227 Grob *p = me->get_parent (X_AXIS);
228 return p ? get_system (p) : dynamic_cast<System *>(me);
232 Grob::handle_broken_dependencies ()
234 Spanner *sp = dynamic_cast<Spanner *> (this);
235 if (original () && sp)
239 /* THIS, SP is the original spanner. We use a special function
240 because some Spanners have enormously long lists in their
241 properties, and a special function fixes FOO */
243 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
244 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
246 System *system = get_system ();
250 && common_refpoint (system, X_AXIS)
251 && common_refpoint (system, Y_AXIS))
252 substitute_object_links (system->self_scm (), object_alist_);
253 else if (dynamic_cast<System *> (this))
254 substitute_object_links (SCM_UNDEFINED, object_alist_);
256 /* THIS element is `invalid'; it has been removed from all
257 dependencies, so let's junk the element itself.
259 Do not do this for System, since that would remove references
260 to the originals of score-grobs, which get then GC'd (a bad
265 /* Note that we still want references to this element to be
266 rearranged, and not silently thrown away, so we keep pointers like
267 {broken_into_{drul, array}, original}
275 for (int a = X_AXIS; a < NO_AXES; a++)
276 dim_cache_[a].clear ();
278 mutable_property_alist_ = SCM_EOL;
279 object_alist_ = SCM_EOL;
280 immutable_property_alist_ = SCM_EOL;
281 interfaces_ = SCM_EOL;
285 Grob::handle_prebroken_dependencies ()
287 /* Don't do this in the derived method, since we want to keep access to
288 object_alist_ centralized. */
291 Item *it = dynamic_cast<Item *> (this);
292 substitute_object_links (scm_from_int (it->break_status_dir ()),
293 original ()->object_alist_);
298 Grob::find_broken_piece (System *) const
303 /****************************************************************
305 ****************************************************************/
308 Grob::translate_axis (Real y, Axis a)
310 if (isinf (y) || isnan (y))
312 programming_error ("Infinity or NaN encountered");
316 if (!dim_cache_[a].offset_)
317 dim_cache_[a].offset_ = new Real (y);
319 *dim_cache_[a].offset_ += y;
322 /* Find the offset relative to D. If D equals THIS, then it is 0.
323 Otherwise, it recursively defd as
325 OFFSET_ + PARENT_L_->relative_coordinate (D) */
327 Grob::relative_coordinate (Grob const *refp, Axis a) const
329 /* eaa - hmmm, should we do a programming_error() here? */
330 if ((this == NULL) || (refp == this))
333 /* We catch PARENT_L_ == nil case with this, but we crash if we did
334 not ask for the absolute coordinate (ie. REFP == nil.) */
335 Real off = get_offset (a);
336 if (refp == dim_cache_[a].parent_)
339 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
345 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
352 if (dim_cache_[Y_AXIS].offset_)
354 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
355 programming_error ("cyclic chain in pure-Y-offset callbacks");
357 off = *dim_cache_[Y_AXIS].offset_;
361 SCM proc = get_property_data ("Y-offset");
363 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
364 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
365 off = robust_scm2double (call_pure_function (proc,
366 scm_list_1 (self_scm ()),
369 del_property ("pure-Y-offset-in-progress");
370 delete dim_cache_[Y_AXIS].offset_;
371 dim_cache_[Y_AXIS].offset_ = 0;
374 /* we simulate positioning-done if we are the child of a VerticalAlignment,
375 but only if we don't have a cached offset. If we do have a cached offset,
376 it probably means that the Alignment was fixed and it has already been
379 if (Grob *p = get_parent (Y_AXIS))
382 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
383 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
385 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
390 /* Invoke callbacks to get offset relative to parent. */
392 Grob::get_offset (Axis a) const
394 if (dim_cache_[a].offset_)
395 return *dim_cache_[a].offset_;
397 Grob *me = (Grob *) this;
399 SCM sym = axis_offset_symbol (a);
400 me->dim_cache_[a].offset_ = new Real (0.0);
403 UGH: can't fold next 2 statements together. Apparently GCC thinks
404 dim_cache_[a].offset_ is unaliased.
406 Real off = robust_scm2double (internal_get_property (sym), 0.0);
407 if (me->dim_cache_[a].offset_)
409 *me->dim_cache_[a].offset_ += off;
410 me->del_property (sym);
411 return *me->dim_cache_[a].offset_;
418 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
420 if (pure && a != Y_AXIS)
421 programming_error ("tried to get pure X-offset");
422 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
423 : relative_coordinate (refp, a);
426 /****************************************************************
428 ****************************************************************/
431 Grob::flush_extent_cache (Axis axis)
433 if (dim_cache_[axis].extent_)
436 Ugh, this is not accurate; will flush property, causing
437 callback to be called if.
439 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
440 delete dim_cache_[axis].extent_;
441 dim_cache_[axis].extent_ = 0;
442 if (get_parent (axis))
443 get_parent (axis)->flush_extent_cache (axis);
448 Grob::extent (Grob *refp, Axis a) const
450 Real offset = relative_coordinate (refp, a);
452 if (dim_cache_[a].extent_)
454 real_ext = *dim_cache_[a].extent_;
459 Order is significant: ?-extent may trigger suicide.
463 ? ly_symbol2scm ("X-extent")
464 : ly_symbol2scm ("Y-extent");
466 SCM ext = internal_get_property (ext_sym);
467 if (is_number_pair (ext))
468 real_ext.unite (ly_scm2interval (ext));
472 ? ly_symbol2scm ("minimum-X-extent")
473 : ly_symbol2scm ("minimum-Y-extent");
474 SCM min_ext = internal_get_property (min_ext_sym);
475 if (is_number_pair (min_ext))
476 real_ext.unite (ly_scm2interval (min_ext));
478 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
481 // We never want nan, so we avoid shifting infinite values.
483 real_ext.translate(offset);
485 this->warning(_f ("ignored infinite %s-offset",
486 a == X_AXIS ? "X" : "Y"));
492 Grob::pure_height (Grob *refp, int start, int end)
494 SCM iv_scm = get_pure_property ("Y-extent", start, end);
495 Interval iv = robust_scm2interval (iv_scm, Interval ());
496 Real offset = pure_relative_y_coordinate (refp, start, end);
498 SCM min_ext = get_property ("minimum-Y-extent");
500 /* we don't add minimum-Y-extent if the extent is empty. This solves
501 a problem with Hara-kiri spanners. They would request_suicide and
502 return empty extents, but we would force them here to be large. */
503 if (!iv.is_empty () && is_number_pair (min_ext))
504 iv.unite (ly_scm2interval (min_ext));
507 iv.translate (offset);
512 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
514 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
518 Grob::spanned_rank_interval () const
520 return Interval_t<int> (-1, 0);
524 Grob::pure_is_visible (int /* start */, int /* end */) const
529 /* Sort grobs according to their starting column. */
531 Grob::less (Grob *g1, Grob *g2)
533 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
536 /****************************************************************
538 ****************************************************************/
540 /* Find the group-element which has both #this# and #s# */
542 Grob::common_refpoint (Grob const *s, Axis a) const
545 /* Catching the trivial cases is likely costlier than just running
546 through: one can't avoid going to the respective chain ends
547 anyway. We might save the second run through when the chain ends
548 differ, but keeping track of the ends makes the loop more costly.
555 for (c = this; c; ++balance)
556 c = c->dim_cache_[a].parent_;
558 for (d = s; d; --balance)
559 d = d->dim_cache_[a].parent_;
561 /* Cut down ancestry to same size */
563 for (c = this; balance > 0; --balance)
564 c = c->dim_cache_[a].parent_;
566 for (d = s; balance < 0; ++balance)
567 d = d->dim_cache_[a].parent_;
569 /* Now find point where our lineages converge */
572 c = c->dim_cache_[a].parent_;
573 d = d->dim_cache_[a].parent_;
580 Grob::set_parent (Grob *g, Axis a)
582 dim_cache_[a].parent_ = g;
586 Grob::get_parent (Axis a) const
588 return dim_cache_[a].parent_;
592 Grob::fixup_refpoint ()
594 for (int a = X_AXIS; a < NO_AXES; a++)
597 Grob *parent = get_parent (ax);
602 if (parent->get_system () != get_system () && get_system ())
604 Grob *newparent = parent->find_broken_piece (get_system ());
605 set_parent (newparent, ax);
608 if (Item *i = dynamic_cast<Item *> (this))
610 Item *parenti = dynamic_cast<Item *> (parent);
614 Direction my_dir = i->break_status_dir ();
615 if (my_dir != parenti->break_status_dir ())
617 Item *newparent = parenti->find_prebroken_piece (my_dir);
618 set_parent (newparent, ax);
625 /****************************************************************
627 ****************************************************************/
630 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
634 if (Align_interface::has_interface (g))
635 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
636 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
641 Grob::get_root_vertical_alignment (Grob *g)
643 return get_maybe_root_vertical_alignment (g, 0);
647 Grob::get_vertical_axis_group (Grob *g)
651 if (!g->get_parent (Y_AXIS))
653 if (Axis_group_interface::has_interface (g)
654 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
656 return get_vertical_axis_group (g->get_parent (Y_AXIS));
661 Grob::get_vertical_axis_group_index (Grob *g)
663 Grob *val = get_root_vertical_alignment (g);
666 Grob *vax = get_vertical_axis_group (g);
667 extract_grob_set (val, "elements", elts);
668 for (vsize i = 0; i < elts.size (); i++)
671 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
676 Grob::vertical_less (Grob *g1, Grob *g2)
678 return internal_vertical_less (g1, g2, false);
682 Grob::pure_vertical_less (Grob *g1, Grob *g2)
684 return internal_vertical_less (g1, g2, true);
688 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
690 Grob *vag = get_root_vertical_alignment (g1);
693 g1->programming_error ("grob does not belong to a VerticalAlignment?");
697 Grob *ag1 = get_vertical_axis_group (g1);
698 Grob *ag2 = get_vertical_axis_group (g2);
700 extract_grob_set (vag, "elements", elts);
702 if (ag1 == ag2 && !pure)
704 Grob *common = g1->common_refpoint (g2, Y_AXIS);
705 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
708 for (vsize i = 0; i < elts.size (); i++)
716 g1->programming_error ("could not place this grob in its axis group");
720 /****************************************************************
722 ****************************************************************/
724 Grob::programming_error (const string &s) const
726 SCM cause = self_scm ();
727 while (Grob *g = Grob::unsmob (cause))
728 cause = g->get_property ("cause");
730 /* ES TODO: cause can't be Music*/
731 if (Music *m = Music::unsmob (cause))
732 m->origin ()->programming_error (s);
733 else if (Stream_event *ev = Stream_event::unsmob (cause))
734 ev->origin ()->programming_error (s);
736 ::programming_error (s);
740 Grob::warning (const string &s) const
742 SCM cause = self_scm ();
743 while (Grob *g = Grob::unsmob (cause))
744 cause = g->get_property ("cause");
746 /* ES TODO: cause can't be Music*/
747 if (Music *m = Music::unsmob (cause))
748 m->origin ()->warning (s);
749 else if (Stream_event *ev = Stream_event::unsmob (cause))
750 ev->origin ()->warning (s);
758 SCM meta = get_property ("meta");
759 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
760 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
761 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
765 "A grob represents a piece of music notation.\n"
767 "All grobs have an X and Y@tie{}position on the page. These"
768 " X and Y@tie{}positions are stored in a relative format, thus"
769 " they can easily be combined by stacking them, hanging one"
770 " grob to the side of another, or coupling them into grouping"
773 "Each grob has a reference point (a.k.a.@: parent): The"
774 " position of a grob is stored relative to that reference"
775 " point. For example, the X@tie{}reference point of a staccato"
776 " dot usually is the note head that it applies to. When the"
777 " note head is moved, the staccato dot moves along"
780 "A grob is often associated with a symbol, but some grobs do"
781 " not print any symbols. They take care of grouping objects."
782 " For example, there is a separate grob that stacks staves"
783 " vertically. The @ref{NoteCollision} object is also an"
784 " abstract grob: It only moves around chords, but doesn't print"
787 "Grobs have properties (Scheme variables) that can be read and"
788 " set. Two types of them exist: immutable and mutable."
789 " Immutable variables define the default style and behavior."
790 " They are shared between many objects. They can be changed"
791 " using @code{\\override} and @code{\\revert}. Mutable"
792 " properties are variables that are specific to one grob."
793 " Typically, lists of other objects, or results from"
794 " computations are stored in mutable properties. In"
795 " particular, every call to @code{ly:grob-set-property!}"
796 " (or its C++ equivalent) sets a mutable property.\n"
798 "The properties @code{after-line-breaking} and"
799 " @code{before-line-breaking} are dummies that are not"
800 " user-serviceable.",
807 "after-line-breaking "
809 "axis-group-parent-X "
810 "axis-group-parent-Y "
811 "before-line-breaking "
819 "horizontal-skylines "
825 "pure-Y-offset-in-progress "
827 "skyline-horizontal-padding "
836 /****************************************************************
838 ****************************************************************/
841 grob_stencil_extent (Grob *me, Axis a)
843 Stencil *m = me->get_stencil ();
847 return ly_interval2scm (e);
850 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
852 Grob::stencil_height (SCM smob)
854 Grob *me = Grob::unsmob (smob);
855 return grob_stencil_extent (me, Y_AXIS);
858 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
860 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
862 Grob *me = Grob::unsmob (smob);
863 if (Stencil::unsmob (me->get_property_data ("stencil")))
864 return grob_stencil_extent (me, Y_AXIS);
866 return ly_interval2scm (Interval ());
870 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
872 Grob::y_parent_positioning (SCM smob)
874 Grob *me = Grob::unsmob (smob);
875 Grob *par = me->get_parent (Y_AXIS);
877 (void) par->get_property ("positioning-done");
879 return scm_from_double (0.0);
882 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
884 Grob::x_parent_positioning (SCM smob)
886 Grob *me = Grob::unsmob (smob);
888 Grob *par = me->get_parent (X_AXIS);
890 (void) par->get_property ("positioning-done");
892 return scm_from_double (0.0);
895 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
897 Grob::stencil_width (SCM smob)
899 Grob *me = Grob::unsmob (smob);
900 return grob_stencil_extent (me, X_AXIS);
904 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
906 for (; scm_is_pair (elist); elist = scm_cdr (elist))
907 if (Grob *s = Grob::unsmob (scm_car (elist)))
910 common = common->common_refpoint (s, a);
919 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
921 for (vsize i = 0; i < arr.size (); i++)
923 common = common->common_refpoint (arr[i], a);
931 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
933 set<Grob *>::iterator it;
935 for (it = arr.begin (); it != arr.end (); it++)
937 common = common->common_refpoint (*it, a);
945 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
947 Interval ext = me->extent (refpoint, a);
949 ext.add_point (me->relative_coordinate (refpoint, a));
954 // Checks whether there is a vertical alignment in the chain of
955 // parents between this and commony.
957 Grob::check_cross_staff (Grob *commony)
959 if (Align_interface::has_interface (commony))
962 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
963 if (Align_interface::has_interface (g))
971 indirect_less (Grob **a, Grob **b)
973 // Use original order as tie breaker. That gives us a stable sort
974 // at the lower price tag of an unstable one, and we want a stable
975 // sort in order to reliably retain the first instance of a grob
977 return *a < *b || (*a == *b && a < b);
982 indirect_eq (Grob **a, Grob **b)
989 direct_less (Grob **a, Grob **b)
994 // uniquify uniquifies on the memory addresses of the Grobs, but then
995 // uses the original order. This makes results independent from the
996 // memory allocation of Grobs.
999 uniquify (vector <Grob *> & grobs)
1001 vector <Grob **> vec (grobs.size ());
1002 for (vsize i = 0; i < grobs.size (); i++)
1004 vector_sort (vec, indirect_less);
1005 vec.erase (unique (vec.begin (), vec.end (), indirect_eq), vec.end ());
1006 vector_sort (vec, direct_less);
1008 // Since the output is a sorted copy of the input with some elements
1009 // removed, we can fill in the vector in-place if we do it starting
1011 for (vsize i = 0; i < vec.size (); i++)
1013 grobs.erase (grobs.begin () + vec.size (), grobs.end ());