2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
25 #include "align-interface.hh"
26 #include "axis-group-interface.hh"
28 #include "international.hh"
33 #include "output-def.hh"
34 #include "pointer-group-interface.hh"
35 #include "program-option.hh"
37 #include "stream-event.hh"
39 #include "unpure-pure-container.hh"
46 return new Grob (*this);
49 Grob::Grob (SCM basicprops)
52 /* FIXME: default should be no callback. */
55 interfaces_ = SCM_EOL;
56 immutable_property_alist_ = basicprops;
57 mutable_property_alist_ = SCM_EOL;
58 object_alist_ = SCM_EOL;
60 /* We do smobify_self () as the first step. Since the object lives
61 on the heap, none of its SCM variables are protected from
62 GC. After smobify_self (), they are. */
65 SCM meta = get_property ("meta");
66 if (scm_is_pair (meta))
68 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
70 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
71 if (scm_is_pair (object_cbs))
73 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
74 set_object (scm_caar (s), scm_cdar (s));
78 if (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));
94 Grob::Grob (Grob const &s)
97 original_ = (Grob *) & s;
99 immutable_property_alist_ = s.immutable_property_alist_;
100 mutable_property_alist_ = SCM_EOL;
102 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
103 dim_cache_ [a] = s.dim_cache_ [a];
105 interfaces_ = s.interfaces_;
106 object_alist_ = SCM_EOL;
112 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
119 /****************************************************************
121 ****************************************************************/
124 Grob::get_stencil () const
129 SCM stil = get_property ("stencil");
130 return unsmob<Stencil> (stil);
134 Grob::get_print_stencil () const
136 SCM stil = get_property ("stencil");
139 if (Stencil *m = unsmob<Stencil> (stil))
142 bool transparent = to_boolean (get_property ("transparent"));
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"))))
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)));
159 /* Calls the scheme procedure stencil-whiteout-box in scm/stencils.scm */
160 if (!transparent && to_boolean (get_property ("whiteout-box")))
162 SCM wh_proc = ly_lily_module_constant ("stencil-whiteout-box");
163 retval = *unsmob<Stencil> (scm_call_1 (wh_proc,
164 retval.smobbed_copy ()));
168 retval = Stencil (m->extent_box (), SCM_EOL);
171 SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
175 retval = Stencil (retval.extent_box (), expr);
178 SCM rot = get_property ("rotation");
179 if (scm_is_pair (rot))
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));
185 retval.rotate_degrees (angle, Offset (x, y));
188 /* color support... see interpret_stencil_expression () for more... */
189 SCM color = get_property ("color");
190 if (scm_is_pair (color))
192 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
196 retval = Stencil (retval.extent_box (), expr);
199 SCM id = get_property ("id");
200 if (scm_is_string (id))
202 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
206 retval = Stencil (retval.extent_box (), expr);
214 /****************************************************************
216 ****************************************************************/
218 Grob::do_break_processing ()
223 Grob::discretionary_processing ()
228 Grob::get_system () const
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
238 Grob::get_system (Grob *me)
240 Grob *p = me->get_parent (X_AXIS);
241 return p ? get_system (p) : dynamic_cast<System *>(me);
245 Grob::handle_broken_dependencies ()
247 Spanner *sp = dynamic_cast<Spanner *> (this);
248 if (original () && 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 */
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));
259 System *system = get_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_);
269 /* THIS element is `invalid'; it has been removed from all
270 dependencies, so let's junk the element itself.
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
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}
288 for (int a = X_AXIS; a < NO_AXES; a++)
289 dim_cache_[a].clear ();
291 mutable_property_alist_ = SCM_EOL;
292 object_alist_ = SCM_EOL;
293 immutable_property_alist_ = SCM_EOL;
294 interfaces_ = SCM_EOL;
298 Grob::handle_prebroken_dependencies ()
300 /* Don't do this in the derived method, since we want to keep access to
301 object_alist_ centralized. */
304 Item *it = dynamic_cast<Item *> (this);
305 substitute_object_links (scm_from_int (it->break_status_dir ()),
306 original ()->object_alist_);
311 Grob::find_broken_piece (System *) const
316 /****************************************************************
318 ****************************************************************/
321 Grob::translate_axis (Real y, Axis a)
323 if (isinf (y) || isnan (y))
325 programming_error ("Infinity or NaN encountered");
329 if (!dim_cache_[a].offset_)
330 dim_cache_[a].offset_ = new Real (y);
332 *dim_cache_[a].offset_ += y;
335 /* Find the offset relative to D. If D equals THIS, then it is 0.
336 Otherwise, it recursively defd as
338 OFFSET_ + PARENT_L_->relative_coordinate (D) */
340 Grob::relative_coordinate (Grob const *refp, Axis a) const
342 /* eaa - hmmm, should we do a programming_error() here? */
343 if ((this == NULL) || (refp == this))
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_)
352 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
358 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
365 if (dim_cache_[Y_AXIS].offset_)
367 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
368 programming_error ("cyclic chain in pure-Y-offset callbacks");
370 off = *dim_cache_[Y_AXIS].offset_;
374 SCM proc = get_property_data ("Y-offset");
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 ()),
382 del_property ("pure-Y-offset-in-progress");
383 delete dim_cache_[Y_AXIS].offset_;
384 dim_cache_[Y_AXIS].offset_ = 0;
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
392 if (Grob *p = get_parent (Y_AXIS))
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);
398 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
403 /* Invoke callbacks to get offset relative to parent. */
405 Grob::get_offset (Axis a) const
407 if (dim_cache_[a].offset_)
408 return *dim_cache_[a].offset_;
410 Grob *me = (Grob *) this;
412 SCM sym = axis_offset_symbol (a);
413 me->dim_cache_[a].offset_ = new Real (0.0);
416 UGH: can't fold next 2 statements together. Apparently GCC thinks
417 dim_cache_[a].offset_ is unaliased.
419 Real off = robust_scm2double (get_property (sym), 0.0);
420 if (me->dim_cache_[a].offset_)
422 *me->dim_cache_[a].offset_ += off;
423 me->del_property (sym);
424 return *me->dim_cache_[a].offset_;
431 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
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);
439 /****************************************************************
441 ****************************************************************/
444 Grob::flush_extent_cache (Axis axis)
446 if (dim_cache_[axis].extent_)
449 Ugh, this is not accurate; will flush property, causing
450 callback to be called if.
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);
461 Grob::extent (Grob *refp, Axis a) const
463 Real offset = relative_coordinate (refp, a);
465 if (dim_cache_[a].extent_)
467 real_ext = *dim_cache_[a].extent_;
472 Order is significant: ?-extent may trigger suicide.
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));
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));
486 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
489 // We never want nan, so we avoid shifting infinite values.
491 real_ext.translate(offset);
493 this->warning(_f ("ignored infinite %s-offset",
494 a == X_AXIS ? "X" : "Y"));
500 Grob::pure_height (Grob *refp, int start, int end)
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);
506 SCM min_ext = get_property ("minimum-Y-extent");
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));
515 iv.translate (offset);
520 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
522 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
526 Grob::spanned_rank_interval () const
528 return Interval_t<int> (-1, 0);
532 Grob::pure_is_visible (int /* start */, int /* end */) const
537 /* Sort grobs according to their starting column. */
539 Grob::less (Grob *g1, Grob *g2)
541 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
544 /****************************************************************
546 ****************************************************************/
548 /* Find the group-element which has both #this# and #s# */
550 Grob::common_refpoint (Grob const *s, Axis a) const
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.
563 for (c = this; c; ++balance)
564 c = c->dim_cache_[a].parent_;
566 for (d = s; d; --balance)
567 d = d->dim_cache_[a].parent_;
569 /* Cut down ancestry to same size */
571 for (c = this; balance > 0; --balance)
572 c = c->dim_cache_[a].parent_;
574 for (d = s; balance < 0; ++balance)
575 d = d->dim_cache_[a].parent_;
577 /* Now find point where our lineages converge */
580 c = c->dim_cache_[a].parent_;
581 d = d->dim_cache_[a].parent_;
588 Grob::set_parent (Grob *g, Axis a)
590 dim_cache_[a].parent_ = g;
594 Grob::get_parent (Axis a) const
596 return dim_cache_[a].parent_;
600 Grob::fixup_refpoint ()
602 for (int a = X_AXIS; a < NO_AXES; a++)
605 Grob *parent = get_parent (ax);
610 if (parent->get_system () != get_system () && get_system ())
612 Grob *newparent = parent->find_broken_piece (get_system ());
613 set_parent (newparent, ax);
616 if (Item *i = dynamic_cast<Item *> (this))
618 Item *parenti = dynamic_cast<Item *> (parent);
622 Direction my_dir = i->break_status_dir ();
623 if (my_dir != parenti->break_status_dir ())
625 Item *newparent = parenti->find_prebroken_piece (my_dir);
626 set_parent (newparent, ax);
633 /****************************************************************
635 ****************************************************************/
638 get_maybe_root_vertical_alignment (Grob *g, Grob *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);
649 Grob::get_root_vertical_alignment (Grob *g)
651 return get_maybe_root_vertical_alignment (g, 0);
655 Grob::get_vertical_axis_group (Grob *g)
659 if (!g->get_parent (Y_AXIS))
661 if (Axis_group_interface::has_interface (g)
662 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
664 return get_vertical_axis_group (g->get_parent (Y_AXIS));
669 Grob::get_vertical_axis_group_index (Grob *g)
671 Grob *val = get_root_vertical_alignment (g);
674 Grob *vax = get_vertical_axis_group (g);
675 extract_grob_set (val, "elements", elts);
676 for (vsize i = 0; i < elts.size (); i++)
679 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
684 Grob::vertical_less (Grob *g1, Grob *g2)
686 return internal_vertical_less (g1, g2, false);
690 Grob::pure_vertical_less (Grob *g1, Grob *g2)
692 return internal_vertical_less (g1, g2, true);
696 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
698 Grob *vag = get_root_vertical_alignment (g1);
701 g1->programming_error ("grob does not belong to a VerticalAlignment?");
705 Grob *ag1 = get_vertical_axis_group (g1);
706 Grob *ag2 = get_vertical_axis_group (g2);
708 extract_grob_set (vag, "elements", elts);
710 if (ag1 == ag2 && !pure)
712 Grob *common = g1->common_refpoint (g2, Y_AXIS);
713 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
716 for (vsize i = 0; i < elts.size (); i++)
724 g1->programming_error ("could not place this grob in its axis group");
728 /****************************************************************
730 ****************************************************************/
732 Grob::programming_error (const string &s) const
734 SCM cause = self_scm ();
735 while (Grob *g = unsmob<Grob> (cause))
736 cause = g->get_property ("cause");
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);
744 ::programming_error (s);
748 Grob::warning (const string &s) const
750 SCM cause = self_scm ();
751 while (Grob *g = unsmob<Grob> (cause))
752 cause = g->get_property ("cause");
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);
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 ();
773 "A grob represents a piece of music notation.\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"
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"
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"
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"
806 "The properties @code{after-line-breaking} and"
807 " @code{before-line-breaking} are dummies that are not"
808 " user-serviceable.",
815 "after-line-breaking "
817 "axis-group-parent-X "
818 "axis-group-parent-Y "
819 "before-line-breaking "
827 "horizontal-skylines "
833 "parenthesis-friends "
834 "pure-Y-offset-in-progress "
836 "skyline-horizontal-padding "
846 /****************************************************************
848 ****************************************************************/
851 grob_stencil_extent (Grob *me, Axis a)
853 Stencil *m = me->get_stencil ();
857 return ly_interval2scm (e);
860 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
862 Grob::stencil_height (SCM smob)
864 Grob *me = unsmob<Grob> (smob);
865 return grob_stencil_extent (me, Y_AXIS);
868 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
870 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
872 Grob *me = unsmob<Grob> (smob);
873 if (unsmob<Stencil> (me->get_property_data ("stencil")))
874 return grob_stencil_extent (me, Y_AXIS);
876 return ly_interval2scm (Interval ());
880 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
882 Grob::y_parent_positioning (SCM smob)
884 Grob *me = unsmob<Grob> (smob);
885 Grob *par = me->get_parent (Y_AXIS);
887 (void) par->get_property ("positioning-done");
889 return scm_from_double (0.0);
892 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
894 Grob::x_parent_positioning (SCM smob)
896 Grob *me = unsmob<Grob> (smob);
898 Grob *par = me->get_parent (X_AXIS);
900 (void) par->get_property ("positioning-done");
902 return scm_from_double (0.0);
905 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
907 Grob::stencil_width (SCM smob)
909 Grob *me = unsmob<Grob> (smob);
910 return grob_stencil_extent (me, X_AXIS);
914 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
916 for (; scm_is_pair (elist); elist = scm_cdr (elist))
917 if (Grob *s = unsmob<Grob> (scm_car (elist)))
920 common = common->common_refpoint (s, a);
929 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
931 for (vsize i = 0; i < arr.size (); i++)
933 common = common->common_refpoint (arr[i], a);
941 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
943 set<Grob *>::iterator it;
945 for (it = arr.begin (); it != arr.end (); it++)
947 common = common->common_refpoint (*it, a);
955 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
957 Interval ext = me->extent (refpoint, a);
959 ext.add_point (me->relative_coordinate (refpoint, a));
964 // Checks whether there is a vertical alignment in the chain of
965 // parents between this and commony.
967 Grob::check_cross_staff (Grob *commony)
969 if (Align_interface::has_interface (commony))
972 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
973 if (Align_interface::has_interface (g))
981 indirect_less (Grob **a, Grob **b)
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
987 return *a < *b || (*a == *b && a < b);
992 indirect_eq (Grob **a, Grob **b)
999 direct_less (Grob **a, Grob **b)
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.
1009 uniquify (vector <Grob *> & grobs)
1011 vector <Grob **> vec (grobs.size ());
1012 for (vsize i = 0; i < grobs.size (); 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);
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
1021 for (vsize i = 0; i < vec.size (); i++)
1023 grobs.erase (grobs.begin () + vec.size (), grobs.end ());