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