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