1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3 
4   Copyright (C) 1997--2021 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 
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.
10 
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.
15 
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/>.
18 */
19 
20 #include "grob.hh"
21 
22 #include "align-interface.hh"
23 #include "axis-group-interface.hh"
24 #include "input.hh"
25 #include "international.hh"
26 #include "item.hh"
27 #include "misc.hh"
28 #include "music.hh"
29 #include "output-def.hh"
30 #include "pointer-group-interface.hh"
31 #include "program-option.hh"
32 #include "stencil.hh"
33 #include "stream-event.hh"
34 #include "system.hh"
35 #include "unpure-pure-container.hh"
36 #include "warn.hh"
37 #include "lily-imports.hh"
38 
39 #include <cstring>
40 #include <set>
41 #include <unordered_set>
42 
43 using std::set;
44 using std::string;
45 using std::vector;
46 
Grob(SCM basicprops)47 Grob::Grob (SCM basicprops)
48 {
49 
50   /* FIXME: default should be no callback.  */
51   layout_ = 0;
52   original_ = 0;
53   interfaces_ = SCM_EOL;
54   immutable_property_alist_ = basicprops;
55   mutable_property_alist_ = SCM_EOL;
56   object_alist_ = SCM_EOL;
57 
58   /* We do smobify_self () as the first step.  Since the object lives
59      on the heap, none of its SCM variables are protected from
60      GC. After smobify_self (), they are.  */
61   smobify_self ();
62 
63   SCM meta = get_property (this, "meta");
64   if (scm_is_pair (meta))
65     {
66       interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
67 
68       SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
69       if (scm_is_pair (object_cbs))
70         {
71           for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
72             set_object (this, scm_caar (s), scm_cdar (s));
73         }
74     }
75 
76   if (scm_is_null (get_property_data (this, "X-extent")))
77     set_property (this, "X-extent", Grob::stencil_width_proc);
78   if (scm_is_null (get_property_data (this, "Y-extent")))
79     set_property (this, "Y-extent",
80                   Unpure_pure_container::make_smob (Grob::stencil_height_proc,
81                                                     Grob::pure_stencil_height_proc));
82   if (scm_is_null (get_property_data (this, "vertical-skylines")))
83     set_property (this, "vertical-skylines",
84                   Unpure_pure_container::make_smob (Grob::simple_vertical_skylines_from_extents_proc,
85                                                     Grob::pure_simple_vertical_skylines_from_extents_proc));
86   if (scm_is_null (get_property_data (this, "horizontal-skylines")))
87     set_property (this, "horizontal-skylines",
88                   Unpure_pure_container::make_smob (Grob::simple_horizontal_skylines_from_extents_proc,
89                                                     Grob::pure_simple_horizontal_skylines_from_extents_proc));
90 }
91 
Grob(Grob const & s)92 Grob::Grob (Grob const &s)
93   : Smob<Grob> ()
94 {
95   original_ = const_cast<Grob *> (&s);
96 
97   immutable_property_alist_ = s.immutable_property_alist_;
98   mutable_property_alist_ = SCM_EOL;
99 
100   for (const auto a : {X_AXIS, Y_AXIS})
101     dim_cache_ [a] = s.dim_cache_ [a];
102 
103   interfaces_ = s.interfaces_;
104   object_alist_ = SCM_EOL;
105 
106   layout_ = 0;
107 
108   smobify_self ();
109 
110   mutable_property_alist_ = ly_alist_copy (s.mutable_property_alist_);
111 }
112 
~Grob()113 Grob::~Grob ()
114 {
115 }
116 /****************************************************************
117   STENCILS
118 ****************************************************************/
119 
120 const Stencil *
get_stencil() const121 Grob::get_stencil () const
122 {
123   if (!is_live ())
124     return 0;
125 
126   SCM stil = get_property (this, "stencil");
127   return unsmob<const Stencil> (stil);
128 }
129 
130 Stencil
get_print_stencil() const131 Grob::get_print_stencil () const
132 {
133   SCM stil = get_property (this, "stencil");
134 
135   Stencil retval;
136   if (auto *m = unsmob<const Stencil> (stil))
137     {
138       retval = *m;
139       bool transparent = from_scm<bool> (get_property (this, "transparent"));
140 
141       /* Process whiteout before color and grob-cause to prevent colored */
142       /* whiteout background and larger file sizes with \pointAndClickOn. */
143       /* A grob has to be visible, otherwise the whiteout property has no effect. */
144       /* Calls the scheme procedure stencil-whiteout in scm/stencils.scm */
145       if (!transparent && (scm_is_number (get_property (this, "whiteout"))
146                            || from_scm<bool> (get_property (this, "whiteout"))))
147         {
148           Real line_thickness = layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
149           retval = *unsmob<const Stencil>
150                    (Lily::stencil_whiteout (retval.smobbed_copy (),
151                                             get_property (this, "whiteout-style"),
152                                             get_property (this, "whiteout"),
153                                             to_scm (line_thickness)));
154         }
155 
156       if (transparent)
157         retval = Stencil (m->extent_box (), SCM_EOL);
158       else
159         {
160           SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
161                                  self_scm (),
162                                  retval.expr ());
163 
164           retval = Stencil (retval.extent_box (), expr);
165         }
166 
167       SCM rot = get_property (this, "rotation");
168       if (scm_is_pair (rot))
169         {
170           Real angle = scm_to_double (scm_car (rot));
171           Real x = scm_to_double (scm_cadr (rot));
172           Real y = scm_to_double (scm_caddr (rot));
173 
174           retval.rotate_degrees (angle, Offset (x, y));
175         }
176 
177       /* color support... see interpret_stencil_expression () for more... */
178       SCM color = get_property (this, "color");
179       if (scm_is_string (color))
180         {
181           retval = retval.in_color (ly_scm2string (color));
182         }
183       else if (scm_is_pair (color))
184         {
185           retval = retval.in_color (from_scm<Real> (scm_car (color)),
186                                     from_scm<Real> (scm_cadr (color)),
187                                     from_scm<Real> (scm_caddr (color)),
188                                     scm_is_pair (scm_cdddr (color))
189                                     ? from_scm<Real> (scm_cadddr (color))
190                                     : 1.0);
191         }
192 
193       SCM attributes = get_property (this, "output-attributes");
194       if (scm_is_pair (attributes))
195         {
196           SCM expr = scm_list_3 (ly_symbol2scm ("output-attributes"),
197                                  attributes,
198                                  retval.expr ());
199 
200           retval = Stencil (retval.extent_box (), expr);
201         }
202 
203     }
204 
205   return retval;
206 }
207 
208 /****************************************************************
209   VIRTUAL STUBS
210 ****************************************************************/
211 void
do_break_processing()212 Grob::do_break_processing ()
213 {
214 }
215 
216 void
break_breakable_item(System *)217 Grob::break_breakable_item (System *)
218 {
219 }
220 
221 System *
get_system() const222 Grob::get_system () const
223 {
224   return 0;
225 }
226 
227 /* This version of get_system is more reliable than this->get_system ()
228    before line-breaking has been done, at which point there is only
229    one system in the whole score and we can find it just by following
230    parent pointers. */
231 System *
get_system(Grob * me)232 Grob::get_system (Grob *me)
233 {
234   Grob *p = me->get_x_parent ();
235   return p ? get_system (p) : dynamic_cast<System *> (me);
236 }
237 
238 void
handle_broken_dependencies()239 Grob::handle_broken_dependencies ()
240 {
241   Spanner *sp = dynamic_cast<Spanner *> (this);
242   if (original () && sp)
243     return;
244 
245   if (sp)
246     /* THIS, SP is the original spanner.  We use a special function
247        because some Spanners have enormously long lists in their
248        properties, and a special function fixes FOO  */
249     {
250       for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
251         sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
252     }
253   System *system = get_system ();
254 
255   if (is_live ()
256       && system
257       && common_refpoint (system, X_AXIS)
258       && common_refpoint (system, Y_AXIS))
259     substitute_object_links (system, object_alist_);
260   else if (dynamic_cast<System *> (this))
261     substitute_object_links (nullptr, object_alist_);
262   else
263     /* THIS element is `invalid'; it has been removed from all
264        dependencies, so let's junk the element itself.
265 
266        Do not do this for System, since that would remove references
267        to the originals of score-grobs, which get then GC'd (a bad
268        thing).  */
269     suicide ();
270 }
271 
272 /* Note that we still want references to this element to be
273    rearranged, and not silently thrown away, so we keep pointers like
274    {broken_into_{drul, array}, original}
275 */
276 void
suicide()277 Grob::suicide ()
278 {
279   if (!is_live ())
280     return;
281 
282   for (const auto a : {X_AXIS, Y_AXIS})
283     dim_cache_[a].clear ();
284 
285   mutable_property_alist_ = SCM_EOL;
286   object_alist_ = SCM_EOL;
287   immutable_property_alist_ = SCM_EOL;
288   interfaces_ = SCM_EOL;
289 }
290 
291 void
handle_prebroken_dependencies()292 Grob::handle_prebroken_dependencies ()
293 {
294   /* Don't do this in the derived method, since we want to keep access to
295      object_alist_ centralized.  */
296   if (original ())
297     {
298       Item *it = dynamic_cast<Item *> (this);
299       substitute_object_links (it->break_status_dir (),
300                                original ()->object_alist_);
301     }
302 }
303 
304 Grob *
find_broken_piece(System *) const305 Grob::find_broken_piece (System *) const
306 {
307   return 0;
308 }
309 
310 /****************************************************************
311    OFFSETS
312 ****************************************************************/
313 
314 void
translate_axis(Real y,Axis a)315 Grob::translate_axis (Real y, Axis a)
316 {
317   if (!std::isfinite (y))
318     {
319       programming_error ("Infinity or NaN encountered");
320       return;
321     }
322 
323   if (!dim_cache_[a].offset_)
324     dim_cache_[a].offset_ = y;
325   else
326     *dim_cache_[a].offset_ += y;
327 }
328 
329 /* Find the offset on axis a relative to refp. */
330 Real
relative_coordinate(Grob const * refp,Axis a) const331 Grob::relative_coordinate (Grob const *refp, Axis a) const
332 {
333   // refp should really always be non-null, but this
334   // does not hold currently.
335   Real result = 0.0;
336   for (const Grob *ancestor = this;
337        ancestor != refp;
338        ancestor = ancestor->get_parent (a))
339     {
340       // !ancestor here means that we asked for a coordinate
341       // relative to something that is not a reference point.
342       // This shouldn't occur (issue #6149), but does at the moment.
343       if (!ancestor)
344         break;
345       result += ancestor->get_offset (a);
346     }
347   return result;
348 }
349 
350 Real
parent_relative(Grob const * refp,Axis a) const351 Grob::parent_relative (Grob const *refp, Axis a) const
352 {
353   if (Grob *p = get_parent (a))
354     return p->relative_coordinate (refp, a);
355   return 0.0;
356 }
357 
358 Real
pure_relative_y_coordinate(Grob const * refp,vsize start,vsize end)359 Grob::pure_relative_y_coordinate (Grob const *refp, vsize start, vsize end)
360 {
361   if (refp == this)
362     return 0.0;
363 
364   Real off = 0;
365 
366   if (dim_cache_[Y_AXIS].offset_)
367     {
368       if (from_scm<bool> (get_property (this, "pure-Y-offset-in-progress")))
369         programming_error ("cyclic chain in pure-Y-offset callbacks");
370 
371       off = *dim_cache_[Y_AXIS].offset_;
372     }
373   else
374     {
375       SCM proc = get_property_data (this, "Y-offset");
376 
377       dim_cache_[Y_AXIS].offset_ = 0;
378       set_property (this, "pure-Y-offset-in-progress", SCM_BOOL_T);
379       off = from_scm<double> (call_pure_function (proc,
380                                                   scm_list_1 (self_scm ()),
381                                                   start, end),
382                               0.0);
383       del_property (this, "pure-Y-offset-in-progress");
384       dim_cache_[Y_AXIS].offset_.reset ();
385     }
386 
387   /* we simulate positioning-done if we are the child of a VerticalAlignment,
388      but only if we don't have a cached offset. If we do have a cached offset,
389      it probably means that the Alignment was fixed and it has already been
390      calculated.
391   */
392   if (Grob *p = get_y_parent ())
393     {
394       Real trans = 0;
395       if (has_interface<Align_interface> (p) && !dim_cache_[Y_AXIS].offset_)
396         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
397 
398       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
399     }
400   return off;
401 }
402 
403 /* Invoke callbacks to get offset relative to parent.  */
404 Real
get_offset(Axis a) const405 Grob::get_offset (Axis a) const
406 {
407   if (dim_cache_[a].offset_)
408     return *dim_cache_[a].offset_;
409 
410   SCM sym = axis_offset_symbol (a);
411   dim_cache_[a].offset_ = 0;
412 
413   /*
414     UGH: can't fold next 2 statements together. Apparently GCC thinks
415     dim_cache_[a].offset_ is unaliased.
416   */
417   Real off = from_scm<double> (get_property (this, sym), 0.0);
418   if (dim_cache_[a].offset_)
419     {
420       *dim_cache_[a].offset_ += off;
421       del_property (const_cast<Grob *> (this), sym);
422       return *dim_cache_[a].offset_;
423     }
424   else
425     return 0.0;
426 }
427 
428 Real
maybe_pure_coordinate(Grob const * refp,Axis a,bool pure,vsize start,vsize end)429 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure,
430                              vsize start, vsize end)
431 {
432   if (pure && a != Y_AXIS)
433     programming_error ("tried to get pure X-offset");
434   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
435          : relative_coordinate (refp, a);
436 }
437 
438 /****************************************************************
439   extents
440 ****************************************************************/
441 
442 void
flush_extent_cache(Axis axis)443 Grob::flush_extent_cache (Axis axis)
444 {
445   if (dim_cache_[axis].extent_)
446     {
447       /*
448         Ugh, this is not accurate; will flush property, causing
449         callback to be called if.
450        */
451       if (axis == X_AXIS)
452         del_property (this, "X-extent");
453       else
454         del_property (this, "Y-extent");
455       dim_cache_[axis].extent_.reset ();
456       if (get_parent (axis))
457         get_parent (axis)->flush_extent_cache (axis);
458     }
459 }
460 
461 Interval
extent(Grob const * refp,Axis a) const462 Grob::extent (Grob const *refp, Axis a) const
463 {
464   Real offset = relative_coordinate (refp, a);
465   Interval real_ext;
466   if (dim_cache_[a].extent_)
467     {
468       real_ext = *dim_cache_[a].extent_;
469     }
470   else
471     {
472       /*
473         Order is significant: ?-extent may trigger suicide.
474        */
475       SCM ext = (a == X_AXIS)
476                 ? get_property (this, "X-extent")
477                 : get_property (this, "Y-extent");
478       if (is_number_pair (ext))
479         real_ext.unite (from_scm<Interval> (ext));
480 
481       SCM min_ext = (a == X_AXIS)
482                     ? get_property (this, "minimum-X-extent")
483                     : get_property (this, "minimum-Y-extent");
484       if (is_number_pair (min_ext))
485         real_ext.unite (from_scm<Interval> (min_ext));
486 
487       dim_cache_[a].extent_ = real_ext;
488     }
489 
490   // We never want nan, so we avoid shifting infinite values.
491   if (!std::isinf (offset))
492     real_ext.translate (offset);
493   else
494     warning (_f ("ignored infinite %s-offset",
495                  a == X_AXIS ? "X" : "Y"));
496 
497   return real_ext;
498 }
499 
500 Interval
pure_y_extent(Grob * refp,vsize start,vsize end)501 Grob::pure_y_extent (Grob *refp, vsize start, vsize end)
502 {
503   SCM iv_scm = get_pure_property (this, "Y-extent", start, end);
504   Interval iv = from_scm (iv_scm, Interval ());
505   Real offset = pure_relative_y_coordinate (refp, start, end);
506 
507   SCM min_ext = get_property (this, "minimum-Y-extent");
508 
509   /* we don't add minimum-Y-extent if the extent is empty. This solves
510      a problem with Hara-kiri spanners. They would request_suicide and
511      return empty extents, but we would force them here to be large. */
512   if (!iv.is_empty () && is_number_pair (min_ext))
513     iv.unite (from_scm<Interval> (min_ext));
514 
515   if (!iv.is_empty ())
516     iv.translate (offset);
517   return iv;
518 }
519 
520 Interval
maybe_pure_extent(Grob * refp,Axis a,bool pure,vsize start,vsize end)521 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, vsize start, vsize end)
522 {
523   return (pure && a == Y_AXIS) ? pure_y_extent (refp, start, end) : extent (refp, a);
524 }
525 
526 /* Sort grobs according to their starting column. */
527 bool
less(Grob * g1,Grob * g2)528 Grob::less (Grob *g1, Grob *g2)
529 {
530   return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
531 }
532 
533 /****************************************************************
534   REFPOINTS
535 ****************************************************************/
536 
537 /* Find the group-element which has both #this# and #s#  */
538 Grob *
common_refpoint(Grob const * s,Axis a) const539 Grob::common_refpoint (Grob const *s, Axis a) const
540 {
541 
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.
546   */
547 
548   int balance = 0;
549   Grob const *c;
550   Grob const *d;
551 
552   for (c = this; c; ++balance)
553     c = c->dim_cache_[a].parent_;
554 
555   for (d = s; d; --balance)
556     d = d->dim_cache_[a].parent_;
557 
558   /* Cut down ancestry to same size */
559 
560   for (c = this; balance > 0; --balance)
561     c = c->dim_cache_[a].parent_;
562 
563   for (d = s; balance < 0; ++balance)
564     d = d->dim_cache_[a].parent_;
565 
566   /* Now find point where our lineages converge */
567   while (c != d)
568     {
569       c = c->dim_cache_[a].parent_;
570       d = d->dim_cache_[a].parent_;
571     }
572 
573   return const_cast<Grob *> (c);
574 }
575 
576 void
fixup_refpoint()577 Grob::fixup_refpoint ()
578 {
579   for (const auto ax : {X_AXIS, Y_AXIS})
580     {
581       Grob *parent = get_parent (ax);
582 
583       if (!parent)
584         continue;
585 
586       if (parent->get_system () != get_system () && get_system ())
587         {
588           Grob *newparent = parent->find_broken_piece (get_system ());
589           set_parent (newparent, ax);
590         }
591 
592       if (Item *i = dynamic_cast<Item *> (this))
593         {
594           Item *parenti = dynamic_cast<Item *> (parent);
595 
596           if (parenti && i)
597             {
598               Direction my_dir = i->break_status_dir ();
599               if (my_dir != parenti->break_status_dir ())
600                 {
601                   Item *newparent = parenti->find_prebroken_piece (my_dir);
602                   set_parent (newparent, ax);
603                 }
604             }
605         }
606     }
607 }
608 
609 // Is possible_ancestor a parent, or parent of parent, etc. of this
610 // on axis a?
611 bool
has_in_ancestry(const Grob * possible_ancestor,Axis a) const612 Grob::has_in_ancestry (const Grob *possible_ancestor, Axis a) const
613 {
614   for (const Grob *parent = this; parent; parent = parent->get_parent (a))
615     {
616       if (parent == possible_ancestor)
617         return true;
618     }
619   return false;
620 }
621 
622 /****************************************************************
623   VERTICAL ORDERING
624 ****************************************************************/
625 
626 Grob *
get_maybe_root_vertical_alignment(Grob * g,Grob * maybe)627 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
628 {
629   if (!g)
630     return maybe;
631   if (has_interface<Align_interface> (g))
632     return get_maybe_root_vertical_alignment (g->get_y_parent (), g);
633   return get_maybe_root_vertical_alignment (g->get_y_parent (), maybe);
634 
635 }
636 
637 Grob *
get_root_vertical_alignment(Grob * g)638 Grob::get_root_vertical_alignment (Grob *g)
639 {
640   return get_maybe_root_vertical_alignment (g, 0);
641 }
642 
643 Grob *
get_vertical_axis_group(Grob * g)644 Grob::get_vertical_axis_group (Grob *g)
645 {
646   if (!g)
647     return 0;
648   if (!g->get_y_parent ())
649     return 0;
650   if (has_interface<Axis_group_interface> (g)
651       && has_interface<Align_interface> (g->get_y_parent ()))
652     return g;
653   return get_vertical_axis_group (g->get_y_parent ());
654 
655 }
656 
657 int
get_vertical_axis_group_index(Grob * g)658 Grob::get_vertical_axis_group_index (Grob *g)
659 {
660   Grob *val = get_root_vertical_alignment (g);
661   if (!val)
662     return -1;
663   Grob *vax = get_vertical_axis_group (g);
664   extract_grob_set (val, "elements", elts);
665   for (vsize i = 0; i < elts.size (); i++)
666     if (elts[i] == vax)
667       return static_cast<int> (i);
668   g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
669   return -1;
670 }
671 
672 bool
vertical_less(Grob * g1,Grob * g2)673 Grob::vertical_less (Grob *g1, Grob *g2)
674 {
675   return internal_vertical_less (g1, g2, false);
676 }
677 
678 bool
pure_vertical_less(Grob * g1,Grob * g2)679 Grob::pure_vertical_less (Grob *g1, Grob *g2)
680 {
681   return internal_vertical_less (g1, g2, true);
682 }
683 
684 bool
internal_vertical_less(Grob * g1,Grob * g2,bool pure)685 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
686 {
687   Grob *vag = get_root_vertical_alignment (g1);
688   if (!vag)
689     {
690       g1->programming_error ("grob does not belong to a VerticalAlignment?");
691       return false;
692     }
693 
694   Grob *ag1 = get_vertical_axis_group (g1);
695   Grob *ag2 = get_vertical_axis_group (g2);
696 
697   extract_grob_set (vag, "elements", elts);
698 
699   if (ag1 == ag2 && !pure)
700     {
701       Grob *common = g1->common_refpoint (g2, Y_AXIS);
702       return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
703     }
704 
705   for (vsize i = 0; i < elts.size (); i++)
706     {
707       if (elts[i] == ag1)
708         return true;
709       if (elts[i] == ag2)
710         return false;
711     }
712 
713   g1->programming_error ("could not place this grob in its axis group");
714   return false;
715 }
716 
717 /****************************************************************
718   CAUSES
719 ****************************************************************/
720 Stream_event *
event_cause() const721 Grob::event_cause () const
722 {
723   SCM cause = get_property (this, "cause");
724   return unsmob<Stream_event> (cause);
725 }
726 
727 Stream_event *
ultimate_event_cause() const728 Grob::ultimate_event_cause () const
729 {
730   SCM cause = get_property (this, "cause");
731   while (Grob *g = unsmob<Grob> (cause))
732     {
733       cause = get_property (g, "cause");
734     }
735   return unsmob<Stream_event> (cause);
736 }
737 
738 /****************************************************************
739   MESSAGES
740 ****************************************************************/
741 Input *
origin() const742 Grob::origin () const
743 {
744   if (Stream_event *ev = ultimate_event_cause ())
745     return ev->origin ();
746   return nullptr;
747 }
748 
749 string
name() const750 Grob::name () const
751 {
752   SCM meta = get_property (this, "meta");
753   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
754   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
755   return scm_is_symbol (nm) ? ly_symbol2string (nm) : class_name ();
756 }
757 
758 ADD_INTERFACE (Grob,
759                "A grob represents a piece of music notation.\n"
760                "\n"
761                "All grobs have an X and Y@tie{}position on the page.  These"
762                " X and Y@tie{}positions are stored in a relative format, thus"
763                " they can easily be combined by stacking them, hanging one"
764                " grob to the side of another, or coupling them into grouping"
765                " objects.\n"
766                "\n"
767                "Each grob has a reference point (a.k.a.@: parent): The"
768                " position of a grob is stored relative to that reference"
769                " point.  For example, the X@tie{}reference point of a staccato"
770                " dot usually is the note head that it applies to.  When the"
771                " note head is moved, the staccato dot moves along"
772                " automatically.\n"
773                "\n"
774                "A grob is often associated with a symbol, but some grobs do"
775                " not print any symbols.  They take care of grouping objects."
776                " For example, there is a separate grob that stacks staves"
777                " vertically.  The @ref{NoteCollision} object is also an"
778                " abstract grob: It only moves around chords, but doesn't print"
779                " anything.\n"
780                "\n"
781                "Grobs have properties (Scheme variables) that can be read and"
782                " set.  Two types of them exist: immutable and mutable."
783                "  Immutable variables define the default style and behavior."
784                "  They are shared between many objects.  They can be changed"
785                " using @code{\\override} and @code{\\revert}.  Mutable"
786                " properties are variables that are specific to one grob."
787                "  Typically, lists of other objects, or results from"
788                " computations are stored in mutable properties.  In"
789                " particular, every call to @code{ly:grob-set-property!}"
790                " (or its C++ equivalent) sets a mutable property.\n"
791                "\n"
792                "The properties @code{after-line-breaking} and"
793                " @code{before-line-breaking} are dummies that are not"
794                " user-serviceable.",
795 
796                /* properties */
797                "X-extent "
798                "X-offset "
799                "Y-extent "
800                "Y-offset "
801                "after-line-breaking "
802                "avoid-slur "
803                "axis-group-parent-X "
804                "axis-group-parent-Y "
805                "before-line-breaking "
806                "cause "
807                "color "
808                "cross-staff "
809                "extra-offset "
810                "footnote-music "
811                "forced-spacing "
812                "horizontal-skylines "
813                "id "
814                "interfaces "
815                "layer "
816                "meta "
817                "minimum-X-extent "
818                "minimum-Y-extent "
819                "output-attributes "
820                "parenthesis-id "
821                "parenthesis-friends "
822                "parenthesized "
823                "pure-Y-offset-in-progress "
824                "rotation "
825                "skyline-horizontal-padding "
826                "springs-and-rods "
827                "staff-symbol "
828                "stencil "
829                "transparent "
830                "vertical-skylines "
831                "whiteout "
832                "whiteout-style "
833               );
834 
835 /****************************************************************
836   CALLBACKS
837 ****************************************************************/
838 
839 static SCM
grob_stencil_extent(Grob * me,Axis a)840 grob_stencil_extent (Grob *me, Axis a)
841 {
842   Interval e;
843   if (auto *m = me->get_stencil ())
844     e = m->extent (a);
845   return to_scm (e);
846 }
847 
848 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
849 SCM
stencil_height(SCM smob)850 Grob::stencil_height (SCM smob)
851 {
852   Grob *me = unsmob<Grob> (smob);
853   return grob_stencil_extent (me, Y_AXIS);
854 }
855 
856 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
857 SCM
pure_stencil_height(SCM smob,SCM,SCM)858 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
859 {
860   Grob *me = unsmob<Grob> (smob);
861   if (unsmob<const Stencil> (get_property_data (me, "stencil")))
862     return grob_stencil_extent (me, Y_AXIS);
863 
864   return to_scm (Interval ());
865 
866 }
867 
868 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
869 SCM
y_parent_positioning(SCM smob)870 Grob::y_parent_positioning (SCM smob)
871 {
872   Grob *me = unsmob<Grob> (smob);
873   Grob *par = me->get_y_parent ();
874   if (par)
875     (void) get_property (par, "positioning-done");
876 
877   return to_scm (0.0);
878 }
879 
880 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
881 SCM
x_parent_positioning(SCM smob)882 Grob::x_parent_positioning (SCM smob)
883 {
884   Grob *me = unsmob<Grob> (smob);
885 
886   Grob *par = me->get_x_parent ();
887   if (par)
888     (void) get_property (par, "positioning-done");
889 
890   return to_scm (0.0);
891 }
892 
893 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
894 SCM
stencil_width(SCM smob)895 Grob::stencil_width (SCM smob)
896 {
897   Grob *me = unsmob<Grob> (smob);
898   return grob_stencil_extent (me, X_AXIS);
899 }
900 
901 Grob *
common_refpoint_of_list(SCM elist,Grob * common,Axis a)902 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
903 {
904   for (; scm_is_pair (elist); elist = scm_cdr (elist))
905     if (Grob *s = unsmob<Grob> (scm_car (elist)))
906       {
907         if (common)
908           common = common->common_refpoint (s, a);
909         else
910           common = s;
911       }
912 
913   return common;
914 }
915 
916 Grob *
common_refpoint_of_array(vector<Grob * > const & arr,Grob * common,Axis a)917 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
918 {
919   for (vsize i = 0; i < arr.size (); i++)
920     if (common)
921       common = common->common_refpoint (arr[i], a);
922     else
923       common = arr[i];
924 
925   return common;
926 }
927 
928 Grob *
common_refpoint_of_array(set<Grob * > const & arr,Grob * common,Axis a)929 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
930 {
931   set<Grob *>::iterator it;
932 
933   for (it = arr.begin (); it != arr.end (); it++)
934     if (common)
935       common = common->common_refpoint (*it, a);
936     else
937       common = *it;
938 
939   return common;
940 }
941 
942 Interval
robust_relative_extent(Grob * me,Grob * refpoint,Axis a)943 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
944 {
945   Interval ext = me->extent (refpoint, a);
946   if (ext.is_empty ())
947     ext.add_point (me->relative_coordinate (refpoint, a));
948 
949   return ext;
950 }
951 
952 Interval
robust_relative_pure_y_extent(Grob * me,Grob * refpoint,vsize start,vsize end)953 robust_relative_pure_y_extent (Grob *me, Grob *refpoint, vsize start, vsize end)
954 {
955   Interval ext = me->pure_y_extent (refpoint, start, end);
956   if (ext.is_empty ())
957     ext.add_point (me->pure_relative_y_coordinate (refpoint, start, end));
958 
959   return ext;
960 }
961 
962 // Checks whether there is a vertical alignment in the chain of
963 // parents between this and commony.
964 bool
check_cross_staff(Grob * commony)965 Grob::check_cross_staff (Grob *commony)
966 {
967   if (has_interface<Align_interface> (commony))
968     return true;
969 
970   for (Grob *g = this; g && g != commony; g = g->get_y_parent ())
971     if (has_interface<Align_interface> (g))
972       return true;
973 
974   return false;
975 }
976 
977 void
uniquify(vector<Grob * > & grobs)978 uniquify (vector <Grob *> &grobs)
979 {
980   std::unordered_set<Grob *> seen (grobs.size ());
981   vsize j = 0;
982   for (vsize i = 0; i < grobs.size (); i++)
983     {
984       if (seen.insert (grobs[i]).second)
985         {
986           grobs[j++] = grobs[i];
987         }
988     }
989 
990   grobs.resize (j);
991 }
992