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