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"
41 #include "lily-imports.hh"
47 return new Grob (*this);
50 Grob::Grob (SCM basicprops)
53 /* FIXME: default should be no callback. */
56 interfaces_ = SCM_EOL;
57 immutable_property_alist_ = basicprops;
58 mutable_property_alist_ = SCM_EOL;
59 object_alist_ = SCM_EOL;
61 /* We do smobify_self () as the first step. Since the object lives
62 on the heap, none of its SCM variables are protected from
63 GC. After smobify_self (), they are. */
66 SCM meta = get_property ("meta");
67 if (scm_is_pair (meta))
69 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
71 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
72 if (scm_is_pair (object_cbs))
74 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
75 set_object (scm_caar (s), scm_cdar (s));
79 if (scm_is_null (get_property_data ("X-extent")))
80 set_property ("X-extent", Grob::stencil_width_proc);
81 if (scm_is_null (get_property_data ("Y-extent")))
82 set_property ("Y-extent",
83 Unpure_pure_container::make_smob (Grob::stencil_height_proc,
84 Grob::pure_stencil_height_proc));
85 if (scm_is_null (get_property_data ("vertical-skylines")))
86 set_property ("vertical-skylines",
87 Unpure_pure_container::make_smob (Grob::simple_vertical_skylines_from_extents_proc,
88 Grob::pure_simple_vertical_skylines_from_extents_proc));
89 if (scm_is_null (get_property_data ("horizontal-skylines")))
90 set_property ("horizontal-skylines",
91 Unpure_pure_container::make_smob (Grob::simple_horizontal_skylines_from_extents_proc,
92 Grob::pure_simple_horizontal_skylines_from_extents_proc));
95 Grob::Grob (Grob const &s)
98 original_ = (Grob *) & s;
100 immutable_property_alist_ = s.immutable_property_alist_;
101 mutable_property_alist_ = SCM_EOL;
103 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
104 dim_cache_ [a] = s.dim_cache_ [a];
106 interfaces_ = s.interfaces_;
107 object_alist_ = SCM_EOL;
113 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
120 /****************************************************************
122 ****************************************************************/
125 Grob::get_stencil () const
130 SCM stil = get_property ("stencil");
131 return unsmob<Stencil> (stil);
135 Grob::get_print_stencil () const
137 SCM stil = get_property ("stencil");
140 if (Stencil *m = unsmob<Stencil> (stil))
143 bool transparent = to_boolean (get_property ("transparent"));
145 /* Process whiteout before color and grob-cause to prevent colored */
146 /* whiteout background and larger file sizes with \pointAndClickOn. */
147 /* A grob has to be visible, otherwise the whiteout property has no effect. */
148 /* Calls the scheme procedure stencil-whiteout in scm/stencils.scm */
149 if (!transparent && (scm_is_number (get_property("whiteout"))
150 || to_boolean (get_property ("whiteout"))))
152 Real thickness = robust_scm2double (get_property("whiteout"), 3.0)
153 * layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
154 retval = *unsmob<Stencil>
155 (Lily::stencil_whiteout (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 retval = *unsmob<Stencil>
163 (Lily::stencil_whiteout_box (retval.smobbed_copy ()));
167 retval = Stencil (m->extent_box (), SCM_EOL);
170 SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
174 retval = Stencil (retval.extent_box (), expr);
177 SCM rot = get_property ("rotation");
178 if (scm_is_pair (rot))
180 Real angle = scm_to_double (scm_car (rot));
181 Real x = scm_to_double (scm_cadr (rot));
182 Real y = scm_to_double (scm_caddr (rot));
184 retval.rotate_degrees (angle, Offset (x, y));
187 /* color support... see interpret_stencil_expression () for more... */
188 SCM color = get_property ("color");
189 if (scm_is_pair (color))
191 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
195 retval = Stencil (retval.extent_box (), expr);
198 SCM id = get_property ("id");
199 if (scm_is_string (id))
201 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
205 retval = Stencil (retval.extent_box (), expr);
213 /****************************************************************
215 ****************************************************************/
217 Grob::do_break_processing ()
222 Grob::discretionary_processing ()
227 Grob::get_system () const
232 /* This version of get_system is more reliable than this->get_system ()
233 before line-breaking has been done, at which point there is only
234 one system in the whole score and we can find it just by following
237 Grob::get_system (Grob *me)
239 Grob *p = me->get_parent (X_AXIS);
240 return p ? get_system (p) : dynamic_cast<System *>(me);
244 Grob::handle_broken_dependencies ()
246 Spanner *sp = dynamic_cast<Spanner *> (this);
247 if (original () && sp)
251 /* THIS, SP is the original spanner. We use a special function
252 because some Spanners have enormously long lists in their
253 properties, and a special function fixes FOO */
255 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
256 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
258 System *system = get_system ();
262 && common_refpoint (system, X_AXIS)
263 && common_refpoint (system, Y_AXIS))
264 substitute_object_links (system->self_scm (), object_alist_);
265 else if (dynamic_cast<System *> (this))
266 substitute_object_links (SCM_UNDEFINED, object_alist_);
268 /* THIS element is `invalid'; it has been removed from all
269 dependencies, so let's junk the element itself.
271 Do not do this for System, since that would remove references
272 to the originals of score-grobs, which get then GC'd (a bad
277 /* Note that we still want references to this element to be
278 rearranged, and not silently thrown away, so we keep pointers like
279 {broken_into_{drul, array}, original}
287 for (int a = X_AXIS; a < NO_AXES; a++)
288 dim_cache_[a].clear ();
290 mutable_property_alist_ = SCM_EOL;
291 object_alist_ = SCM_EOL;
292 immutable_property_alist_ = SCM_EOL;
293 interfaces_ = SCM_EOL;
297 Grob::handle_prebroken_dependencies ()
299 /* Don't do this in the derived method, since we want to keep access to
300 object_alist_ centralized. */
303 Item *it = dynamic_cast<Item *> (this);
304 substitute_object_links (scm_from_int (it->break_status_dir ()),
305 original ()->object_alist_);
310 Grob::find_broken_piece (System *) const
315 /****************************************************************
317 ****************************************************************/
320 Grob::translate_axis (Real y, Axis a)
322 if (isinf (y) || isnan (y))
324 programming_error ("Infinity or NaN encountered");
328 if (!dim_cache_[a].offset_)
329 dim_cache_[a].offset_ = new Real (y);
331 *dim_cache_[a].offset_ += y;
334 /* Find the offset relative to D. If D equals THIS, then it is 0.
335 Otherwise, it recursively defd as
337 OFFSET_ + PARENT_L_->relative_coordinate (D) */
339 Grob::relative_coordinate (Grob const *refp, Axis a) const
341 /* eaa - hmmm, should we do a programming_error() here? */
342 if ((this == NULL) || (refp == this))
345 /* We catch PARENT_L_ == nil case with this, but we crash if we did
346 not ask for the absolute coordinate (ie. REFP == nil.) */
347 Real off = get_offset (a);
348 if (refp == dim_cache_[a].parent_)
351 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
357 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
364 if (dim_cache_[Y_AXIS].offset_)
366 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
367 programming_error ("cyclic chain in pure-Y-offset callbacks");
369 off = *dim_cache_[Y_AXIS].offset_;
373 SCM proc = get_property_data ("Y-offset");
375 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
376 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
377 off = robust_scm2double (call_pure_function (proc,
378 scm_list_1 (self_scm ()),
381 del_property ("pure-Y-offset-in-progress");
382 delete dim_cache_[Y_AXIS].offset_;
383 dim_cache_[Y_AXIS].offset_ = 0;
386 /* we simulate positioning-done if we are the child of a VerticalAlignment,
387 but only if we don't have a cached offset. If we do have a cached offset,
388 it probably means that the Alignment was fixed and it has already been
391 if (Grob *p = get_parent (Y_AXIS))
394 if (has_interface<Align_interface> (p) && !dim_cache_[Y_AXIS].offset_)
395 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
397 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
402 /* Invoke callbacks to get offset relative to parent. */
404 Grob::get_offset (Axis a) const
406 if (dim_cache_[a].offset_)
407 return *dim_cache_[a].offset_;
409 Grob *me = (Grob *) this;
411 SCM sym = axis_offset_symbol (a);
412 me->dim_cache_[a].offset_ = new Real (0.0);
415 UGH: can't fold next 2 statements together. Apparently GCC thinks
416 dim_cache_[a].offset_ is unaliased.
418 Real off = robust_scm2double (get_property (sym), 0.0);
419 if (me->dim_cache_[a].offset_)
421 *me->dim_cache_[a].offset_ += off;
422 me->del_property (sym);
423 return *me->dim_cache_[a].offset_;
430 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
432 if (pure && a != Y_AXIS)
433 programming_error ("tried to get pure X-offset");
434 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
435 : relative_coordinate (refp, a);
438 /****************************************************************
440 ****************************************************************/
443 Grob::flush_extent_cache (Axis axis)
445 if (dim_cache_[axis].extent_)
448 Ugh, this is not accurate; will flush property, causing
449 callback to be called if.
451 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
452 delete dim_cache_[axis].extent_;
453 dim_cache_[axis].extent_ = 0;
454 if (get_parent (axis))
455 get_parent (axis)->flush_extent_cache (axis);
460 Grob::extent (Grob *refp, Axis a) const
462 Real offset = relative_coordinate (refp, a);
464 if (dim_cache_[a].extent_)
466 real_ext = *dim_cache_[a].extent_;
471 Order is significant: ?-extent may trigger suicide.
473 SCM ext = (a == X_AXIS)
474 ? get_property ("X-extent")
475 : get_property ("Y-extent");
476 if (is_number_pair (ext))
477 real_ext.unite (ly_scm2interval (ext));
479 SCM min_ext = (a == X_AXIS)
480 ? get_property ("minimum-X-extent")
481 : get_property ("minimum-Y-extent");
482 if (is_number_pair (min_ext))
483 real_ext.unite (ly_scm2interval (min_ext));
485 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
488 // We never want nan, so we avoid shifting infinite values.
490 real_ext.translate(offset);
492 warning(_f ("ignored infinite %s-offset",
493 a == X_AXIS ? "X" : "Y"));
499 Grob::pure_y_extent (Grob *refp, int start, int end)
501 SCM iv_scm = get_pure_property ("Y-extent", start, end);
502 Interval iv = robust_scm2interval (iv_scm, Interval ());
503 Real offset = pure_relative_y_coordinate (refp, start, end);
505 SCM min_ext = get_property ("minimum-Y-extent");
507 /* we don't add minimum-Y-extent if the extent is empty. This solves
508 a problem with Hara-kiri spanners. They would request_suicide and
509 return empty extents, but we would force them here to be large. */
510 if (!iv.is_empty () && is_number_pair (min_ext))
511 iv.unite (ly_scm2interval (min_ext));
514 iv.translate (offset);
519 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
521 return (pure && a == Y_AXIS) ? pure_y_extent (refp, start, end) : extent (refp, a);
525 Grob::spanned_rank_interval () const
527 return Interval_t<int> (-1, 0);
531 Grob::pure_is_visible (int /* start */, int /* end */) const
536 /* Sort grobs according to their starting column. */
538 Grob::less (Grob *g1, Grob *g2)
540 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
543 /****************************************************************
545 ****************************************************************/
547 /* Find the group-element which has both #this# and #s# */
549 Grob::common_refpoint (Grob const *s, Axis a) const
552 /* Catching the trivial cases is likely costlier than just running
553 through: one can't avoid going to the respective chain ends
554 anyway. We might save the second run through when the chain ends
555 differ, but keeping track of the ends makes the loop more costly.
562 for (c = this; c; ++balance)
563 c = c->dim_cache_[a].parent_;
565 for (d = s; d; --balance)
566 d = d->dim_cache_[a].parent_;
568 /* Cut down ancestry to same size */
570 for (c = this; balance > 0; --balance)
571 c = c->dim_cache_[a].parent_;
573 for (d = s; balance < 0; ++balance)
574 d = d->dim_cache_[a].parent_;
576 /* Now find point where our lineages converge */
579 c = c->dim_cache_[a].parent_;
580 d = d->dim_cache_[a].parent_;
587 Grob::set_parent (Grob *g, Axis a)
589 dim_cache_[a].parent_ = g;
593 Grob::get_parent (Axis a) const
595 return dim_cache_[a].parent_;
599 Grob::fixup_refpoint ()
601 for (int a = X_AXIS; a < NO_AXES; a++)
604 Grob *parent = get_parent (ax);
609 if (parent->get_system () != get_system () && get_system ())
611 Grob *newparent = parent->find_broken_piece (get_system ());
612 set_parent (newparent, ax);
615 if (Item *i = dynamic_cast<Item *> (this))
617 Item *parenti = dynamic_cast<Item *> (parent);
621 Direction my_dir = i->break_status_dir ();
622 if (my_dir != parenti->break_status_dir ())
624 Item *newparent = parenti->find_prebroken_piece (my_dir);
625 set_parent (newparent, ax);
632 /****************************************************************
634 ****************************************************************/
637 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
641 if (has_interface<Align_interface> (g))
642 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
643 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
648 Grob::get_root_vertical_alignment (Grob *g)
650 return get_maybe_root_vertical_alignment (g, 0);
654 Grob::get_vertical_axis_group (Grob *g)
658 if (!g->get_parent (Y_AXIS))
660 if (has_interface<Axis_group_interface> (g)
661 && has_interface<Align_interface> (g->get_parent (Y_AXIS)))
663 return get_vertical_axis_group (g->get_parent (Y_AXIS));
668 Grob::get_vertical_axis_group_index (Grob *g)
670 Grob *val = get_root_vertical_alignment (g);
673 Grob *vax = get_vertical_axis_group (g);
674 extract_grob_set (val, "elements", elts);
675 for (vsize i = 0; i < elts.size (); i++)
678 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
683 Grob::vertical_less (Grob *g1, Grob *g2)
685 return internal_vertical_less (g1, g2, false);
689 Grob::pure_vertical_less (Grob *g1, Grob *g2)
691 return internal_vertical_less (g1, g2, true);
695 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
697 Grob *vag = get_root_vertical_alignment (g1);
700 g1->programming_error ("grob does not belong to a VerticalAlignment?");
704 Grob *ag1 = get_vertical_axis_group (g1);
705 Grob *ag2 = get_vertical_axis_group (g2);
707 extract_grob_set (vag, "elements", elts);
709 if (ag1 == ag2 && !pure)
711 Grob *common = g1->common_refpoint (g2, Y_AXIS);
712 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
715 for (vsize i = 0; i < elts.size (); i++)
723 g1->programming_error ("could not place this grob in its axis group");
727 /****************************************************************
729 ****************************************************************/
731 Grob::programming_error (const string &s) const
733 SCM cause = self_scm ();
734 while (Grob *g = unsmob<Grob> (cause))
735 cause = g->get_property ("cause");
737 /* ES TODO: cause can't be Music*/
738 if (Music *m = unsmob<Music> (cause))
739 m->origin ()->programming_error (s);
740 else if (Stream_event *ev = unsmob<Stream_event> (cause))
741 ev->origin ()->programming_error (s);
743 ::programming_error (s);
747 Grob::warning (const string &s) const
749 SCM cause = self_scm ();
750 while (Grob *g = unsmob<Grob> (cause))
751 cause = g->get_property ("cause");
753 /* ES TODO: cause can't be Music*/
754 if (Music *m = unsmob<Music> (cause))
755 m->origin ()->warning (s);
756 else if (Stream_event *ev = unsmob<Stream_event> (cause))
757 ev->origin ()->warning (s);
765 SCM meta = get_property ("meta");
766 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
767 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
768 return scm_is_symbol (nm) ? ly_symbol2string (nm) : class_name ();
772 "A grob represents a piece of music notation.\n"
774 "All grobs have an X and Y@tie{}position on the page. These"
775 " X and Y@tie{}positions are stored in a relative format, thus"
776 " they can easily be combined by stacking them, hanging one"
777 " grob to the side of another, or coupling them into grouping"
780 "Each grob has a reference point (a.k.a.@: parent): The"
781 " position of a grob is stored relative to that reference"
782 " point. For example, the X@tie{}reference point of a staccato"
783 " dot usually is the note head that it applies to. When the"
784 " note head is moved, the staccato dot moves along"
787 "A grob is often associated with a symbol, but some grobs do"
788 " not print any symbols. They take care of grouping objects."
789 " For example, there is a separate grob that stacks staves"
790 " vertically. The @ref{NoteCollision} object is also an"
791 " abstract grob: It only moves around chords, but doesn't print"
794 "Grobs have properties (Scheme variables) that can be read and"
795 " set. Two types of them exist: immutable and mutable."
796 " Immutable variables define the default style and behavior."
797 " They are shared between many objects. They can be changed"
798 " using @code{\\override} and @code{\\revert}. Mutable"
799 " properties are variables that are specific to one grob."
800 " Typically, lists of other objects, or results from"
801 " computations are stored in mutable properties. In"
802 " particular, every call to @code{ly:grob-set-property!}"
803 " (or its C++ equivalent) sets a mutable property.\n"
805 "The properties @code{after-line-breaking} and"
806 " @code{before-line-breaking} are dummies that are not"
807 " user-serviceable.",
814 "after-line-breaking "
816 "axis-group-parent-X "
817 "axis-group-parent-Y "
818 "before-line-breaking "
826 "horizontal-skylines "
832 "parenthesis-friends "
833 "pure-Y-offset-in-progress "
835 "skyline-horizontal-padding "
845 /****************************************************************
847 ****************************************************************/
850 grob_stencil_extent (Grob *me, Axis a)
852 Stencil *m = me->get_stencil ();
856 return ly_interval2scm (e);
859 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
861 Grob::stencil_height (SCM smob)
863 Grob *me = unsmob<Grob> (smob);
864 return grob_stencil_extent (me, Y_AXIS);
867 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
869 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
871 Grob *me = unsmob<Grob> (smob);
872 if (unsmob<Stencil> (me->get_property_data ("stencil")))
873 return grob_stencil_extent (me, Y_AXIS);
875 return ly_interval2scm (Interval ());
879 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
881 Grob::y_parent_positioning (SCM smob)
883 Grob *me = unsmob<Grob> (smob);
884 Grob *par = me->get_parent (Y_AXIS);
886 (void) par->get_property ("positioning-done");
888 return scm_from_double (0.0);
891 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
893 Grob::x_parent_positioning (SCM smob)
895 Grob *me = unsmob<Grob> (smob);
897 Grob *par = me->get_parent (X_AXIS);
899 (void) par->get_property ("positioning-done");
901 return scm_from_double (0.0);
904 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
906 Grob::stencil_width (SCM smob)
908 Grob *me = unsmob<Grob> (smob);
909 return grob_stencil_extent (me, X_AXIS);
913 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
915 for (; scm_is_pair (elist); elist = scm_cdr (elist))
916 if (Grob *s = unsmob<Grob> (scm_car (elist)))
919 common = common->common_refpoint (s, a);
928 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
930 for (vsize i = 0; i < arr.size (); i++)
932 common = common->common_refpoint (arr[i], a);
940 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
942 set<Grob *>::iterator it;
944 for (it = arr.begin (); it != arr.end (); it++)
946 common = common->common_refpoint (*it, a);
954 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
956 Interval ext = me->extent (refpoint, a);
958 ext.add_point (me->relative_coordinate (refpoint, a));
963 // Checks whether there is a vertical alignment in the chain of
964 // parents between this and commony.
966 Grob::check_cross_staff (Grob *commony)
968 if (has_interface<Align_interface> (commony))
971 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
972 if (has_interface<Align_interface> (g))
980 indirect_less (Grob **a, Grob **b)
982 // Use original order as tie breaker. That gives us a stable sort
983 // at the lower price tag of an unstable one, and we want a stable
984 // sort in order to reliably retain the first instance of a grob
986 return *a < *b || (*a == *b && a < b);
991 indirect_eq (Grob **a, Grob **b)
998 direct_less (Grob **a, Grob **b)
1003 // uniquify uniquifies on the memory addresses of the Grobs, but then
1004 // uses the original order. This makes results independent from the
1005 // memory allocation of Grobs.
1008 uniquify (vector <Grob *> & grobs)
1010 vector <Grob **> vec (grobs.size ());
1011 for (vsize i = 0; i < grobs.size (); i++)
1013 vector_sort (vec, indirect_less);
1014 vec.erase (unique (vec.begin (), vec.end (), indirect_eq), vec.end ());
1015 vector_sort (vec, direct_less);
1017 // Since the output is a sorted copy of the input with some elements
1018 // removed, we can fill in the vector in-place if we do it starting
1020 for (vsize i = 0; i < vec.size (); i++)
1022 grobs.erase (grobs.begin () + vec.size (), grobs.end ());