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"
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 Unpure_pure_container::make_smob (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 Unpure_pure_container::make_smob (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 Unpure_pure_container::make_smob (Grob::simple_horizontal_skylines_from_extents_proc,
91 Grob::pure_simple_horizontal_skylines_from_extents_proc));
94 Grob::Grob (Grob const &s)
97 original_ = (Grob *) & s;
99 immutable_property_alist_ = s.immutable_property_alist_;
100 mutable_property_alist_ = SCM_EOL;
102 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
103 dim_cache_ [a] = s.dim_cache_ [a];
105 interfaces_ = s.interfaces_;
106 object_alist_ = SCM_EOL;
112 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
119 /****************************************************************
121 ****************************************************************/
124 Grob::get_stencil () const
129 SCM stil = get_property ("stencil");
130 return Stencil::unsmob (stil);
134 Grob::get_print_stencil () const
136 SCM stil = get_property ("stencil");
139 if (Stencil *m = Stencil::unsmob (stil))
142 bool transparent = to_boolean (get_property ("transparent"));
145 retval = Stencil (m->extent_box (), SCM_EOL);
148 SCM expr = m->expr ();
149 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
152 retval = Stencil (m->extent_box (), expr);
155 SCM rot = get_property ("rotation");
156 if (scm_is_pair (rot))
158 Real angle = scm_to_double (scm_car (rot));
159 Real x = scm_to_double (scm_cadr (rot));
160 Real y = scm_to_double (scm_caddr (rot));
162 retval.rotate_degrees (angle, Offset (x, y));
165 /* color support... see interpret_stencil_expression () for more... */
166 SCM color = get_property ("color");
167 if (scm_is_pair (color))
169 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
173 retval = Stencil (retval.extent_box (), expr);
176 /* process whiteout */
177 /* a grob has to be visible, otherwise the whiteout property has no effect */
178 if (!transparent && to_boolean (get_property ("whiteout")))
180 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
181 /* to add a round-filled-box stencil to the stencil list */
183 = *Stencil::unsmob (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
184 retval.smobbed_copy ()));
187 SCM id = get_property ("id");
188 if (scm_is_string (id))
190 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
194 retval = Stencil (retval.extent_box (), expr);
202 /****************************************************************
204 ****************************************************************/
206 Grob::do_break_processing ()
211 Grob::discretionary_processing ()
216 Grob::get_system () const
221 /* This version of get_system is more reliable than this->get_system ()
222 before line-breaking has been done, at which point there is only
223 one system in the whole score and we can find it just by following
226 Grob::get_system (Grob *me)
228 Grob *p = me->get_parent (X_AXIS);
229 return p ? get_system (p) : dynamic_cast<System *>(me);
233 Grob::handle_broken_dependencies ()
235 Spanner *sp = dynamic_cast<Spanner *> (this);
236 if (original () && sp)
240 /* THIS, SP is the original spanner. We use a special function
241 because some Spanners have enormously long lists in their
242 properties, and a special function fixes FOO */
244 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
245 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
247 System *system = get_system ();
251 && common_refpoint (system, X_AXIS)
252 && common_refpoint (system, Y_AXIS))
253 substitute_object_links (system->self_scm (), object_alist_);
254 else if (dynamic_cast<System *> (this))
255 substitute_object_links (SCM_UNDEFINED, object_alist_);
257 /* THIS element is `invalid'; it has been removed from all
258 dependencies, so let's junk the element itself.
260 Do not do this for System, since that would remove references
261 to the originals of score-grobs, which get then GC'd (a bad
266 /* Note that we still want references to this element to be
267 rearranged, and not silently thrown away, so we keep pointers like
268 {broken_into_{drul, array}, original}
276 for (int a = X_AXIS; a < NO_AXES; a++)
277 dim_cache_[a].clear ();
279 mutable_property_alist_ = SCM_EOL;
280 object_alist_ = SCM_EOL;
281 immutable_property_alist_ = SCM_EOL;
282 interfaces_ = SCM_EOL;
286 Grob::handle_prebroken_dependencies ()
288 /* Don't do this in the derived method, since we want to keep access to
289 object_alist_ centralized. */
292 Item *it = dynamic_cast<Item *> (this);
293 substitute_object_links (scm_from_int (it->break_status_dir ()),
294 original ()->object_alist_);
299 Grob::find_broken_piece (System *) const
304 /****************************************************************
306 ****************************************************************/
309 Grob::translate_axis (Real y, Axis a)
311 if (isinf (y) || isnan (y))
313 programming_error ("Infinity or NaN encountered");
317 if (!dim_cache_[a].offset_)
318 dim_cache_[a].offset_ = new Real (y);
320 *dim_cache_[a].offset_ += y;
323 /* Find the offset relative to D. If D equals THIS, then it is 0.
324 Otherwise, it recursively defd as
326 OFFSET_ + PARENT_L_->relative_coordinate (D) */
328 Grob::relative_coordinate (Grob const *refp, Axis a) const
330 /* eaa - hmmm, should we do a programming_error() here? */
331 if ((this == NULL) || (refp == this))
334 /* We catch PARENT_L_ == nil case with this, but we crash if we did
335 not ask for the absolute coordinate (ie. REFP == nil.) */
336 Real off = get_offset (a);
337 if (refp == dim_cache_[a].parent_)
340 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
346 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
353 if (dim_cache_[Y_AXIS].offset_)
355 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
356 programming_error ("cyclic chain in pure-Y-offset callbacks");
358 off = *dim_cache_[Y_AXIS].offset_;
362 SCM proc = get_property_data ("Y-offset");
364 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
365 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
366 off = robust_scm2double (call_pure_function (proc,
367 scm_list_1 (self_scm ()),
370 del_property ("pure-Y-offset-in-progress");
371 delete dim_cache_[Y_AXIS].offset_;
372 dim_cache_[Y_AXIS].offset_ = 0;
375 /* we simulate positioning-done if we are the child of a VerticalAlignment,
376 but only if we don't have a cached offset. If we do have a cached offset,
377 it probably means that the Alignment was fixed and it has already been
380 if (Grob *p = get_parent (Y_AXIS))
383 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
384 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
386 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
391 /* Invoke callbacks to get offset relative to parent. */
393 Grob::get_offset (Axis a) const
395 if (dim_cache_[a].offset_)
396 return *dim_cache_[a].offset_;
398 Grob *me = (Grob *) this;
400 SCM sym = axis_offset_symbol (a);
401 me->dim_cache_[a].offset_ = new Real (0.0);
404 UGH: can't fold next 2 statements together. Apparently GCC thinks
405 dim_cache_[a].offset_ is unaliased.
407 Real off = robust_scm2double (get_property (sym), 0.0);
408 if (me->dim_cache_[a].offset_)
410 *me->dim_cache_[a].offset_ += off;
411 me->del_property (sym);
412 return *me->dim_cache_[a].offset_;
419 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
421 if (pure && a != Y_AXIS)
422 programming_error ("tried to get pure X-offset");
423 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
424 : relative_coordinate (refp, a);
427 /****************************************************************
429 ****************************************************************/
432 Grob::flush_extent_cache (Axis axis)
434 if (dim_cache_[axis].extent_)
437 Ugh, this is not accurate; will flush property, causing
438 callback to be called if.
440 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
441 delete dim_cache_[axis].extent_;
442 dim_cache_[axis].extent_ = 0;
443 if (get_parent (axis))
444 get_parent (axis)->flush_extent_cache (axis);
449 Grob::extent (Grob *refp, Axis a) const
451 Real offset = relative_coordinate (refp, a);
453 if (dim_cache_[a].extent_)
455 real_ext = *dim_cache_[a].extent_;
460 Order is significant: ?-extent may trigger suicide.
462 SCM ext = (a == X_AXIS)
463 ? get_property ("X-extent")
464 : get_property ("Y-extent");
465 if (is_number_pair (ext))
466 real_ext.unite (ly_scm2interval (ext));
468 SCM min_ext = (a == X_AXIS)
469 ? get_property ("minimum-X-extent")
470 : get_property ("minimum-Y-extent");
471 if (is_number_pair (min_ext))
472 real_ext.unite (ly_scm2interval (min_ext));
474 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
477 // We never want nan, so we avoid shifting infinite values.
479 real_ext.translate(offset);
481 this->warning(_f ("ignored infinite %s-offset",
482 a == X_AXIS ? "X" : "Y"));
488 Grob::pure_height (Grob *refp, int start, int end)
490 SCM iv_scm = get_pure_property ("Y-extent", start, end);
491 Interval iv = robust_scm2interval (iv_scm, Interval ());
492 Real offset = pure_relative_y_coordinate (refp, start, end);
494 SCM min_ext = get_property ("minimum-Y-extent");
496 /* we don't add minimum-Y-extent if the extent is empty. This solves
497 a problem with Hara-kiri spanners. They would request_suicide and
498 return empty extents, but we would force them here to be large. */
499 if (!iv.is_empty () && is_number_pair (min_ext))
500 iv.unite (ly_scm2interval (min_ext));
503 iv.translate (offset);
508 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
510 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
514 Grob::spanned_rank_interval () const
516 return Interval_t<int> (-1, 0);
520 Grob::pure_is_visible (int /* start */, int /* end */) const
525 /* Sort grobs according to their starting column. */
527 Grob::less (Grob *g1, Grob *g2)
529 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
532 /****************************************************************
534 ****************************************************************/
536 /* Find the group-element which has both #this# and #s# */
538 Grob::common_refpoint (Grob const *s, Axis a) const
541 /* Catching the trivial cases is likely costlier than just running
542 through: one can't avoid going to the respective chain ends
543 anyway. We might save the second run through when the chain ends
544 differ, but keeping track of the ends makes the loop more costly.
551 for (c = this; c; ++balance)
552 c = c->dim_cache_[a].parent_;
554 for (d = s; d; --balance)
555 d = d->dim_cache_[a].parent_;
557 /* Cut down ancestry to same size */
559 for (c = this; balance > 0; --balance)
560 c = c->dim_cache_[a].parent_;
562 for (d = s; balance < 0; ++balance)
563 d = d->dim_cache_[a].parent_;
565 /* Now find point where our lineages converge */
568 c = c->dim_cache_[a].parent_;
569 d = d->dim_cache_[a].parent_;
576 Grob::set_parent (Grob *g, Axis a)
578 dim_cache_[a].parent_ = g;
582 Grob::get_parent (Axis a) const
584 return dim_cache_[a].parent_;
588 Grob::fixup_refpoint ()
590 for (int a = X_AXIS; a < NO_AXES; a++)
593 Grob *parent = get_parent (ax);
598 if (parent->get_system () != get_system () && get_system ())
600 Grob *newparent = parent->find_broken_piece (get_system ());
601 set_parent (newparent, ax);
604 if (Item *i = dynamic_cast<Item *> (this))
606 Item *parenti = dynamic_cast<Item *> (parent);
610 Direction my_dir = i->break_status_dir ();
611 if (my_dir != parenti->break_status_dir ())
613 Item *newparent = parenti->find_prebroken_piece (my_dir);
614 set_parent (newparent, ax);
621 /****************************************************************
623 ****************************************************************/
626 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
630 if (Align_interface::has_interface (g))
631 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
632 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
637 Grob::get_root_vertical_alignment (Grob *g)
639 return get_maybe_root_vertical_alignment (g, 0);
643 Grob::get_vertical_axis_group (Grob *g)
647 if (!g->get_parent (Y_AXIS))
649 if (Axis_group_interface::has_interface (g)
650 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
652 return get_vertical_axis_group (g->get_parent (Y_AXIS));
657 Grob::get_vertical_axis_group_index (Grob *g)
659 Grob *val = get_root_vertical_alignment (g);
662 Grob *vax = get_vertical_axis_group (g);
663 extract_grob_set (val, "elements", elts);
664 for (vsize i = 0; i < elts.size (); i++)
667 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
672 Grob::vertical_less (Grob *g1, Grob *g2)
674 return internal_vertical_less (g1, g2, false);
678 Grob::pure_vertical_less (Grob *g1, Grob *g2)
680 return internal_vertical_less (g1, g2, true);
684 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
686 Grob *vag = get_root_vertical_alignment (g1);
689 g1->programming_error ("grob does not belong to a VerticalAlignment?");
693 Grob *ag1 = get_vertical_axis_group (g1);
694 Grob *ag2 = get_vertical_axis_group (g2);
696 extract_grob_set (vag, "elements", elts);
698 if (ag1 == ag2 && !pure)
700 Grob *common = g1->common_refpoint (g2, Y_AXIS);
701 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
704 for (vsize i = 0; i < elts.size (); i++)
712 g1->programming_error ("could not place this grob in its axis group");
716 /****************************************************************
718 ****************************************************************/
720 Grob::programming_error (const string &s) const
722 SCM cause = self_scm ();
723 while (Grob *g = Grob::unsmob (cause))
724 cause = g->get_property ("cause");
726 /* ES TODO: cause can't be Music*/
727 if (Music *m = Music::unsmob (cause))
728 m->origin ()->programming_error (s);
729 else if (Stream_event *ev = Stream_event::unsmob (cause))
730 ev->origin ()->programming_error (s);
732 ::programming_error (s);
736 Grob::warning (const string &s) const
738 SCM cause = self_scm ();
739 while (Grob *g = Grob::unsmob (cause))
740 cause = g->get_property ("cause");
742 /* ES TODO: cause can't be Music*/
743 if (Music *m = Music::unsmob (cause))
744 m->origin ()->warning (s);
745 else if (Stream_event *ev = Stream_event::unsmob (cause))
746 ev->origin ()->warning (s);
754 SCM meta = get_property ("meta");
755 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
756 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
757 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
761 "A grob represents a piece of music notation.\n"
763 "All grobs have an X and Y@tie{}position on the page. These"
764 " X and Y@tie{}positions are stored in a relative format, thus"
765 " they can easily be combined by stacking them, hanging one"
766 " grob to the side of another, or coupling them into grouping"
769 "Each grob has a reference point (a.k.a.@: parent): The"
770 " position of a grob is stored relative to that reference"
771 " point. For example, the X@tie{}reference point of a staccato"
772 " dot usually is the note head that it applies to. When the"
773 " note head is moved, the staccato dot moves along"
776 "A grob is often associated with a symbol, but some grobs do"
777 " not print any symbols. They take care of grouping objects."
778 " For example, there is a separate grob that stacks staves"
779 " vertically. The @ref{NoteCollision} object is also an"
780 " abstract grob: It only moves around chords, but doesn't print"
783 "Grobs have properties (Scheme variables) that can be read and"
784 " set. Two types of them exist: immutable and mutable."
785 " Immutable variables define the default style and behavior."
786 " They are shared between many objects. They can be changed"
787 " using @code{\\override} and @code{\\revert}. Mutable"
788 " properties are variables that are specific to one grob."
789 " Typically, lists of other objects, or results from"
790 " computations are stored in mutable properties. In"
791 " particular, every call to @code{ly:grob-set-property!}"
792 " (or its C++ equivalent) sets a mutable property.\n"
794 "The properties @code{after-line-breaking} and"
795 " @code{before-line-breaking} are dummies that are not"
796 " user-serviceable.",
803 "after-line-breaking "
805 "axis-group-parent-X "
806 "axis-group-parent-Y "
807 "before-line-breaking "
815 "horizontal-skylines "
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 = Grob::unsmob (smob);
851 return grob_stencil_extent (me, Y_AXIS);
854 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
856 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
858 Grob *me = Grob::unsmob (smob);
859 if (Stencil::is_smob (me->get_property_data ("stencil")))
860 return grob_stencil_extent (me, Y_AXIS);
862 return ly_interval2scm (Interval ());
866 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
868 Grob::y_parent_positioning (SCM smob)
870 Grob *me = Grob::unsmob (smob);
871 Grob *par = me->get_parent (Y_AXIS);
873 (void) par->get_property ("positioning-done");
875 return scm_from_double (0.0);
878 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
880 Grob::x_parent_positioning (SCM smob)
882 Grob *me = Grob::unsmob (smob);
884 Grob *par = me->get_parent (X_AXIS);
886 (void) par->get_property ("positioning-done");
888 return scm_from_double (0.0);
891 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
893 Grob::stencil_width (SCM smob)
895 Grob *me = Grob::unsmob (smob);
896 return grob_stencil_extent (me, X_AXIS);
900 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
902 for (; scm_is_pair (elist); elist = scm_cdr (elist))
903 if (Grob *s = Grob::unsmob (scm_car (elist)))
906 common = common->common_refpoint (s, a);
915 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
917 for (vsize i = 0; i < arr.size (); i++)
919 common = common->common_refpoint (arr[i], a);
927 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
929 set<Grob *>::iterator it;
931 for (it = arr.begin (); it != arr.end (); it++)
933 common = common->common_refpoint (*it, a);
941 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
943 Interval ext = me->extent (refpoint, a);
945 ext.add_point (me->relative_coordinate (refpoint, a));
950 // Checks whether there is a vertical alignment in the chain of
951 // parents between this and commony.
953 Grob::check_cross_staff (Grob *commony)
955 if (Align_interface::has_interface (commony))
958 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
959 if (Align_interface::has_interface (g))
967 indirect_less (Grob **a, Grob **b)
969 // Use original order as tie breaker. That gives us a stable sort
970 // at the lower price tag of an unstable one, and we want a stable
971 // sort in order to reliably retain the first instance of a grob
973 return *a < *b || (*a == *b && a < b);
978 indirect_eq (Grob **a, Grob **b)
985 direct_less (Grob **a, Grob **b)
990 // uniquify uniquifies on the memory addresses of the Grobs, but then
991 // uses the original order. This makes results independent from the
992 // memory allocation of Grobs.
995 uniquify (vector <Grob *> & grobs)
997 vector <Grob **> vec (grobs.size ());
998 for (vsize i = 0; i < grobs.size (); i++)
1000 vector_sort (vec, indirect_less);
1001 vec.erase (unique (vec.begin (), vec.end (), indirect_eq), vec.end ());
1002 vector_sort (vec, direct_less);
1004 // Since the output is a sorted copy of the input with some elements
1005 // removed, we can fill in the vector in-place if we do it starting
1007 for (vsize i = 0; i < vec.size (); i++)
1009 grobs.erase (grobs.begin () + vec.size (), grobs.end ());