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"
50 return new Grob (*this);
53 Grob::Grob (SCM basicprops)
56 /* FIXME: default should be no callback. */
59 interfaces_ = SCM_EOL;
60 immutable_property_alist_ = basicprops;
61 mutable_property_alist_ = SCM_EOL;
62 object_alist_ = SCM_EOL;
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. */
69 SCM meta = get_property ("meta");
70 if (scm_is_pair (meta))
72 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
74 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
75 if (scm_is_pair (object_cbs))
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));
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));
98 Grob::Grob (Grob const &s)
101 original_ = (Grob *) & s;
103 immutable_property_alist_ = s.immutable_property_alist_;
104 mutable_property_alist_ = SCM_EOL;
106 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
107 dim_cache_ [a] = s.dim_cache_ [a];
109 interfaces_ = s.interfaces_;
110 object_alist_ = SCM_EOL;
116 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
123 /****************************************************************
125 ****************************************************************/
128 Grob::get_stencil () const
133 SCM stil = get_property ("stencil");
134 return unsmob<Stencil> (stil);
138 Grob::get_print_stencil () const
140 SCM stil = get_property ("stencil");
143 if (Stencil *m = unsmob<Stencil> (stil))
146 bool transparent = to_boolean (get_property ("transparent"));
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"))))
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)));
162 /* Calls the scheme procedure stencil-whiteout-box in scm/stencils.scm */
163 if (!transparent && to_boolean (get_property ("whiteout-box")))
165 retval = *unsmob<Stencil>
166 (Lily::stencil_whiteout_box (retval.smobbed_copy ()));
170 retval = Stencil (m->extent_box (), SCM_EOL);
173 SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
177 retval = Stencil (retval.extent_box (), expr);
180 SCM rot = get_property ("rotation");
181 if (scm_is_pair (rot))
183 Real angle = scm_to_double (scm_car (rot));
184 Real x = scm_to_double (scm_cadr (rot));
185 Real y = scm_to_double (scm_caddr (rot));
187 retval.rotate_degrees (angle, Offset (x, y));
190 /* color support... see interpret_stencil_expression () for more... */
191 SCM color = get_property ("color");
192 if (scm_is_pair (color))
194 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
198 retval = Stencil (retval.extent_box (), expr);
201 SCM id = get_property ("id");
202 if (scm_is_string (id))
204 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
208 retval = Stencil (retval.extent_box (), expr);
216 /****************************************************************
218 ****************************************************************/
220 Grob::do_break_processing ()
225 Grob::discretionary_processing ()
230 Grob::get_system () const
235 /* This version of get_system is more reliable than this->get_system ()
236 before line-breaking has been done, at which point there is only
237 one system in the whole score and we can find it just by following
240 Grob::get_system (Grob *me)
242 Grob *p = me->get_parent (X_AXIS);
243 return p ? get_system (p) : dynamic_cast<System *>(me);
247 Grob::handle_broken_dependencies ()
249 Spanner *sp = dynamic_cast<Spanner *> (this);
250 if (original () && sp)
254 /* THIS, SP is the original spanner. We use a special function
255 because some Spanners have enormously long lists in their
256 properties, and a special function fixes FOO */
258 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
259 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
261 System *system = get_system ();
265 && common_refpoint (system, X_AXIS)
266 && common_refpoint (system, Y_AXIS))
267 substitute_object_links (system->self_scm (), object_alist_);
268 else if (dynamic_cast<System *> (this))
269 substitute_object_links (SCM_UNDEFINED, object_alist_);
271 /* THIS element is `invalid'; it has been removed from all
272 dependencies, so let's junk the element itself.
274 Do not do this for System, since that would remove references
275 to the originals of score-grobs, which get then GC'd (a bad
280 /* Note that we still want references to this element to be
281 rearranged, and not silently thrown away, so we keep pointers like
282 {broken_into_{drul, array}, original}
290 for (int a = X_AXIS; a < NO_AXES; a++)
291 dim_cache_[a].clear ();
293 mutable_property_alist_ = SCM_EOL;
294 object_alist_ = SCM_EOL;
295 immutable_property_alist_ = SCM_EOL;
296 interfaces_ = SCM_EOL;
300 Grob::handle_prebroken_dependencies ()
302 /* Don't do this in the derived method, since we want to keep access to
303 object_alist_ centralized. */
306 Item *it = dynamic_cast<Item *> (this);
307 substitute_object_links (scm_from_int (it->break_status_dir ()),
308 original ()->object_alist_);
313 Grob::find_broken_piece (System *) const
318 /****************************************************************
320 ****************************************************************/
323 Grob::translate_axis (Real y, Axis a)
325 if (isinf (y) || isnan (y))
327 programming_error ("Infinity or NaN encountered");
331 if (!dim_cache_[a].offset_)
332 dim_cache_[a].offset_ = new Real (y);
334 *dim_cache_[a].offset_ += y;
337 /* Find the offset relative to D. If D equals THIS, then it is 0.
338 Otherwise, it recursively defd as
340 OFFSET_ + PARENT_L_->relative_coordinate (D) */
342 Grob::relative_coordinate (Grob const *refp, Axis a) const
344 /* eaa - hmmm, should we do a programming_error() here? */
345 if ((this == NULL) || (refp == this))
348 /* We catch PARENT_L_ == nil case with this, but we crash if we did
349 not ask for the absolute coordinate (ie. REFP == nil.) */
350 Real off = get_offset (a);
351 if (refp == dim_cache_[a].parent_)
354 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
360 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
367 if (dim_cache_[Y_AXIS].offset_)
369 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
370 programming_error ("cyclic chain in pure-Y-offset callbacks");
372 off = *dim_cache_[Y_AXIS].offset_;
376 SCM proc = get_property_data ("Y-offset");
378 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
379 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
380 off = robust_scm2double (call_pure_function (proc,
381 scm_list_1 (self_scm ()),
384 del_property ("pure-Y-offset-in-progress");
385 delete dim_cache_[Y_AXIS].offset_;
386 dim_cache_[Y_AXIS].offset_ = 0;
389 /* we simulate positioning-done if we are the child of a VerticalAlignment,
390 but only if we don't have a cached offset. If we do have a cached offset,
391 it probably means that the Alignment was fixed and it has already been
394 if (Grob *p = get_parent (Y_AXIS))
397 if (has_interface<Align_interface> (p) && !dim_cache_[Y_AXIS].offset_)
398 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
400 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
405 /* Invoke callbacks to get offset relative to parent. */
407 Grob::get_offset (Axis a) const
409 if (dim_cache_[a].offset_)
410 return *dim_cache_[a].offset_;
412 Grob *me = (Grob *) this;
414 SCM sym = axis_offset_symbol (a);
415 me->dim_cache_[a].offset_ = new Real (0.0);
418 UGH: can't fold next 2 statements together. Apparently GCC thinks
419 dim_cache_[a].offset_ is unaliased.
421 Real off = robust_scm2double (get_property (sym), 0.0);
422 if (me->dim_cache_[a].offset_)
424 *me->dim_cache_[a].offset_ += off;
425 me->del_property (sym);
426 return *me->dim_cache_[a].offset_;
433 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
435 if (pure && a != Y_AXIS)
436 programming_error ("tried to get pure X-offset");
437 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
438 : relative_coordinate (refp, a);
441 /****************************************************************
443 ****************************************************************/
446 Grob::flush_extent_cache (Axis axis)
448 if (dim_cache_[axis].extent_)
451 Ugh, this is not accurate; will flush property, causing
452 callback to be called if.
454 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
455 delete dim_cache_[axis].extent_;
456 dim_cache_[axis].extent_ = 0;
457 if (get_parent (axis))
458 get_parent (axis)->flush_extent_cache (axis);
463 Grob::extent (Grob *refp, Axis a) const
465 Real offset = relative_coordinate (refp, a);
467 if (dim_cache_[a].extent_)
469 real_ext = *dim_cache_[a].extent_;
474 Order is significant: ?-extent may trigger suicide.
476 SCM ext = (a == X_AXIS)
477 ? get_property ("X-extent")
478 : get_property ("Y-extent");
479 if (is_number_pair (ext))
480 real_ext.unite (ly_scm2interval (ext));
482 SCM min_ext = (a == X_AXIS)
483 ? get_property ("minimum-X-extent")
484 : get_property ("minimum-Y-extent");
485 if (is_number_pair (min_ext))
486 real_ext.unite (ly_scm2interval (min_ext));
488 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
491 // We never want nan, so we avoid shifting infinite values.
493 real_ext.translate(offset);
495 warning(_f ("ignored infinite %s-offset",
496 a == X_AXIS ? "X" : "Y"));
502 Grob::pure_y_extent (Grob *refp, int start, int end)
504 SCM iv_scm = get_pure_property ("Y-extent", start, end);
505 Interval iv = robust_scm2interval (iv_scm, Interval ());
506 Real offset = pure_relative_y_coordinate (refp, start, end);
508 SCM min_ext = get_property ("minimum-Y-extent");
510 /* we don't add minimum-Y-extent if the extent is empty. This solves
511 a problem with Hara-kiri spanners. They would request_suicide and
512 return empty extents, but we would force them here to be large. */
513 if (!iv.is_empty () && is_number_pair (min_ext))
514 iv.unite (ly_scm2interval (min_ext));
517 iv.translate (offset);
522 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
524 return (pure && a == Y_AXIS) ? pure_y_extent (refp, start, end) : extent (refp, a);
528 Grob::spanned_rank_interval () const
530 return Interval_t<int> (-1, 0);
534 Grob::pure_is_visible (int /* start */, int /* end */) const
539 /* Sort grobs according to their starting column. */
541 Grob::less (Grob *g1, Grob *g2)
543 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
546 /****************************************************************
548 ****************************************************************/
550 /* Find the group-element which has both #this# and #s# */
552 Grob::common_refpoint (Grob const *s, Axis a) const
555 /* Catching the trivial cases is likely costlier than just running
556 through: one can't avoid going to the respective chain ends
557 anyway. We might save the second run through when the chain ends
558 differ, but keeping track of the ends makes the loop more costly.
565 for (c = this; c; ++balance)
566 c = c->dim_cache_[a].parent_;
568 for (d = s; d; --balance)
569 d = d->dim_cache_[a].parent_;
571 /* Cut down ancestry to same size */
573 for (c = this; balance > 0; --balance)
574 c = c->dim_cache_[a].parent_;
576 for (d = s; balance < 0; ++balance)
577 d = d->dim_cache_[a].parent_;
579 /* Now find point where our lineages converge */
582 c = c->dim_cache_[a].parent_;
583 d = d->dim_cache_[a].parent_;
590 Grob::set_parent (Grob *g, Axis a)
592 dim_cache_[a].parent_ = g;
596 Grob::get_parent (Axis a) const
598 return dim_cache_[a].parent_;
602 Grob::fixup_refpoint ()
604 for (int a = X_AXIS; a < NO_AXES; a++)
607 Grob *parent = get_parent (ax);
612 if (parent->get_system () != get_system () && get_system ())
614 Grob *newparent = parent->find_broken_piece (get_system ());
615 set_parent (newparent, ax);
618 if (Item *i = dynamic_cast<Item *> (this))
620 Item *parenti = dynamic_cast<Item *> (parent);
624 Direction my_dir = i->break_status_dir ();
625 if (my_dir != parenti->break_status_dir ())
627 Item *newparent = parenti->find_prebroken_piece (my_dir);
628 set_parent (newparent, ax);
635 /****************************************************************
637 ****************************************************************/
640 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
644 if (has_interface<Align_interface> (g))
645 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
646 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
651 Grob::get_root_vertical_alignment (Grob *g)
653 return get_maybe_root_vertical_alignment (g, 0);
657 Grob::get_vertical_axis_group (Grob *g)
661 if (!g->get_parent (Y_AXIS))
663 if (has_interface<Axis_group_interface> (g)
664 && has_interface<Align_interface> (g->get_parent (Y_AXIS)))
666 return get_vertical_axis_group (g->get_parent (Y_AXIS));
671 Grob::get_vertical_axis_group_index (Grob *g)
673 Grob *val = get_root_vertical_alignment (g);
676 Grob *vax = get_vertical_axis_group (g);
677 extract_grob_set (val, "elements", elts);
678 for (vsize i = 0; i < elts.size (); i++)
681 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
686 Grob::vertical_less (Grob *g1, Grob *g2)
688 return internal_vertical_less (g1, g2, false);
692 Grob::pure_vertical_less (Grob *g1, Grob *g2)
694 return internal_vertical_less (g1, g2, true);
698 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
700 Grob *vag = get_root_vertical_alignment (g1);
703 g1->programming_error ("grob does not belong to a VerticalAlignment?");
707 Grob *ag1 = get_vertical_axis_group (g1);
708 Grob *ag2 = get_vertical_axis_group (g2);
710 extract_grob_set (vag, "elements", elts);
712 if (ag1 == ag2 && !pure)
714 Grob *common = g1->common_refpoint (g2, Y_AXIS);
715 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
718 for (vsize i = 0; i < elts.size (); i++)
726 g1->programming_error ("could not place this grob in its axis group");
730 /****************************************************************
732 ****************************************************************/
734 Grob::programming_error (const string &s) const
736 SCM cause = self_scm ();
737 while (Grob *g = unsmob<Grob> (cause))
738 cause = g->get_property ("cause");
740 /* ES TODO: cause can't be Music*/
741 if (Music *m = unsmob<Music> (cause))
742 m->origin ()->programming_error (s);
743 else if (Stream_event *ev = unsmob<Stream_event> (cause))
744 ev->origin ()->programming_error (s);
746 ::programming_error (s);
750 Grob::warning (const string &s) const
752 SCM cause = self_scm ();
753 while (Grob *g = unsmob<Grob> (cause))
754 cause = g->get_property ("cause");
756 /* ES TODO: cause can't be Music*/
757 if (Music *m = unsmob<Music> (cause))
758 m->origin ()->warning (s);
759 else if (Stream_event *ev = unsmob<Stream_event> (cause))
760 ev->origin ()->warning (s);
768 SCM meta = get_property ("meta");
769 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
770 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
771 return scm_is_symbol (nm) ? ly_symbol2string (nm) : class_name ();
775 "A grob represents a piece of music notation.\n"
777 "All grobs have an X and Y@tie{}position on the page. These"
778 " X and Y@tie{}positions are stored in a relative format, thus"
779 " they can easily be combined by stacking them, hanging one"
780 " grob to the side of another, or coupling them into grouping"
783 "Each grob has a reference point (a.k.a.@: parent): The"
784 " position of a grob is stored relative to that reference"
785 " point. For example, the X@tie{}reference point of a staccato"
786 " dot usually is the note head that it applies to. When the"
787 " note head is moved, the staccato dot moves along"
790 "A grob is often associated with a symbol, but some grobs do"
791 " not print any symbols. They take care of grouping objects."
792 " For example, there is a separate grob that stacks staves"
793 " vertically. The @ref{NoteCollision} object is also an"
794 " abstract grob: It only moves around chords, but doesn't print"
797 "Grobs have properties (Scheme variables) that can be read and"
798 " set. Two types of them exist: immutable and mutable."
799 " Immutable variables define the default style and behavior."
800 " They are shared between many objects. They can be changed"
801 " using @code{\\override} and @code{\\revert}. Mutable"
802 " properties are variables that are specific to one grob."
803 " Typically, lists of other objects, or results from"
804 " computations are stored in mutable properties. In"
805 " particular, every call to @code{ly:grob-set-property!}"
806 " (or its C++ equivalent) sets a mutable property.\n"
808 "The properties @code{after-line-breaking} and"
809 " @code{before-line-breaking} are dummies that are not"
810 " user-serviceable.",
817 "after-line-breaking "
819 "axis-group-parent-X "
820 "axis-group-parent-Y "
821 "before-line-breaking "
829 "horizontal-skylines "
835 "parenthesis-friends "
836 "pure-Y-offset-in-progress "
838 "skyline-horizontal-padding "
848 /****************************************************************
850 ****************************************************************/
853 grob_stencil_extent (Grob *me, Axis a)
855 Stencil *m = me->get_stencil ();
859 return ly_interval2scm (e);
862 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
864 Grob::stencil_height (SCM smob)
866 Grob *me = unsmob<Grob> (smob);
867 return grob_stencil_extent (me, Y_AXIS);
870 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
872 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
874 Grob *me = unsmob<Grob> (smob);
875 if (unsmob<Stencil> (me->get_property_data ("stencil")))
876 return grob_stencil_extent (me, Y_AXIS);
878 return ly_interval2scm (Interval ());
882 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
884 Grob::y_parent_positioning (SCM smob)
886 Grob *me = unsmob<Grob> (smob);
887 Grob *par = me->get_parent (Y_AXIS);
889 (void) par->get_property ("positioning-done");
891 return scm_from_double (0.0);
894 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
896 Grob::x_parent_positioning (SCM smob)
898 Grob *me = unsmob<Grob> (smob);
900 Grob *par = me->get_parent (X_AXIS);
902 (void) par->get_property ("positioning-done");
904 return scm_from_double (0.0);
907 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
909 Grob::stencil_width (SCM smob)
911 Grob *me = unsmob<Grob> (smob);
912 return grob_stencil_extent (me, X_AXIS);
916 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
918 for (; scm_is_pair (elist); elist = scm_cdr (elist))
919 if (Grob *s = unsmob<Grob> (scm_car (elist)))
922 common = common->common_refpoint (s, a);
931 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
933 for (vsize i = 0; i < arr.size (); i++)
935 common = common->common_refpoint (arr[i], a);
943 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
945 set<Grob *>::iterator it;
947 for (it = arr.begin (); it != arr.end (); it++)
949 common = common->common_refpoint (*it, a);
957 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
959 Interval ext = me->extent (refpoint, a);
961 ext.add_point (me->relative_coordinate (refpoint, a));
966 // Checks whether there is a vertical alignment in the chain of
967 // parents between this and commony.
969 Grob::check_cross_staff (Grob *commony)
971 if (has_interface<Align_interface> (commony))
974 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
975 if (has_interface<Align_interface> (g))
983 indirect_less (Grob **a, Grob **b)
985 // Use original order as tie breaker. That gives us a stable sort
986 // at the lower price tag of an unstable one, and we want a stable
987 // sort in order to reliably retain the first instance of a grob
989 return *a < *b || (*a == *b && a < b);
994 indirect_eq (Grob **a, Grob **b)
1001 direct_less (Grob **a, Grob **b)
1006 // uniquify uniquifies on the memory addresses of the Grobs, but then
1007 // uses the original order. This makes results independent from the
1008 // memory allocation of Grobs.
1011 uniquify (vector <Grob *> & grobs)
1013 vector <Grob **> vec (grobs.size ());
1014 for (vsize i = 0; i < grobs.size (); i++)
1016 vector_sort (vec, indirect_less);
1017 vec.erase (unique (vec.begin (), vec.end (), indirect_eq), vec.end ());
1018 vector_sort (vec, direct_less);
1020 // Since the output is a sorted copy of the input with some elements
1021 // removed, we can fill in the vector in-place if we do it starting
1023 for (vsize i = 0; i < vec.size (); i++)
1025 grobs.erase (grobs.begin () + vec.size (), grobs.end ());