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 line_thickness = layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
153 retval = *unsmob<Stencil>
154 (Lily::stencil_whiteout (retval.smobbed_copy (),
155 get_property ("whiteout-style"),
156 get_property ("whiteout"),
157 scm_from_double (line_thickness)));
161 retval = Stencil (m->extent_box (), SCM_EOL);
164 SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
168 retval = Stencil (retval.extent_box (), expr);
171 SCM rot = get_property ("rotation");
172 if (scm_is_pair (rot))
174 Real angle = scm_to_double (scm_car (rot));
175 Real x = scm_to_double (scm_cadr (rot));
176 Real y = scm_to_double (scm_caddr (rot));
178 retval.rotate_degrees (angle, Offset (x, y));
181 /* color support... see interpret_stencil_expression () for more... */
182 SCM color = get_property ("color");
183 if (scm_is_pair (color))
185 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
189 retval = Stencil (retval.extent_box (), expr);
192 SCM id = get_property ("id");
193 if (scm_is_string (id))
195 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
199 retval = Stencil (retval.extent_box (), expr);
207 /****************************************************************
209 ****************************************************************/
211 Grob::do_break_processing ()
216 Grob::discretionary_processing ()
221 Grob::get_system () const
226 /* This version of get_system is more reliable than this->get_system ()
227 before line-breaking has been done, at which point there is only
228 one system in the whole score and we can find it just by following
231 Grob::get_system (Grob *me)
233 Grob *p = me->get_parent (X_AXIS);
234 return p ? get_system (p) : dynamic_cast<System *>(me);
238 Grob::handle_broken_dependencies ()
240 Spanner *sp = dynamic_cast<Spanner *> (this);
241 if (original () && sp)
245 /* THIS, SP is the original spanner. We use a special function
246 because some Spanners have enormously long lists in their
247 properties, and a special function fixes FOO */
249 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
250 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
252 System *system = get_system ();
256 && common_refpoint (system, X_AXIS)
257 && common_refpoint (system, Y_AXIS))
258 substitute_object_links (system->self_scm (), object_alist_);
259 else if (dynamic_cast<System *> (this))
260 substitute_object_links (SCM_UNDEFINED, object_alist_);
262 /* THIS element is `invalid'; it has been removed from all
263 dependencies, so let's junk the element itself.
265 Do not do this for System, since that would remove references
266 to the originals of score-grobs, which get then GC'd (a bad
271 /* Note that we still want references to this element to be
272 rearranged, and not silently thrown away, so we keep pointers like
273 {broken_into_{drul, array}, original}
281 for (int a = X_AXIS; a < NO_AXES; a++)
282 dim_cache_[a].clear ();
284 mutable_property_alist_ = SCM_EOL;
285 object_alist_ = SCM_EOL;
286 immutable_property_alist_ = SCM_EOL;
287 interfaces_ = SCM_EOL;
291 Grob::handle_prebroken_dependencies ()
293 /* Don't do this in the derived method, since we want to keep access to
294 object_alist_ centralized. */
297 Item *it = dynamic_cast<Item *> (this);
298 substitute_object_links (scm_from_int (it->break_status_dir ()),
299 original ()->object_alist_);
304 Grob::find_broken_piece (System *) const
309 /****************************************************************
311 ****************************************************************/
314 Grob::translate_axis (Real y, Axis a)
316 if (isinf (y) || isnan (y))
318 programming_error ("Infinity or NaN encountered");
322 if (!dim_cache_[a].offset_)
323 dim_cache_[a].offset_ = new Real (y);
325 *dim_cache_[a].offset_ += y;
328 /* Find the offset relative to D. If D equals THIS, then it is 0.
329 Otherwise, it recursively defd as
331 OFFSET_ + PARENT_L_->relative_coordinate (D) */
333 Grob::relative_coordinate (Grob const *refp, Axis a) const
335 /* eaa - hmmm, should we do a programming_error() here? */
339 /* We catch PARENT_L_ == nil case with this, but we crash if we did
340 not ask for the absolute coordinate (ie. REFP == nil.) */
341 Real off = get_offset (a);
342 if (refp == dim_cache_[a].parent_)
345 if (dim_cache_[a].parent_ != NULL)
346 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
352 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
359 if (dim_cache_[Y_AXIS].offset_)
361 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
362 programming_error ("cyclic chain in pure-Y-offset callbacks");
364 off = *dim_cache_[Y_AXIS].offset_;
368 SCM proc = get_property_data ("Y-offset");
370 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
371 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
372 off = robust_scm2double (call_pure_function (proc,
373 scm_list_1 (self_scm ()),
376 del_property ("pure-Y-offset-in-progress");
377 delete dim_cache_[Y_AXIS].offset_;
378 dim_cache_[Y_AXIS].offset_ = 0;
381 /* we simulate positioning-done if we are the child of a VerticalAlignment,
382 but only if we don't have a cached offset. If we do have a cached offset,
383 it probably means that the Alignment was fixed and it has already been
386 if (Grob *p = get_parent (Y_AXIS))
389 if (has_interface<Align_interface> (p) && !dim_cache_[Y_AXIS].offset_)
390 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
392 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
397 /* Invoke callbacks to get offset relative to parent. */
399 Grob::get_offset (Axis a) const
401 if (dim_cache_[a].offset_)
402 return *dim_cache_[a].offset_;
404 Grob *me = (Grob *) this;
406 SCM sym = axis_offset_symbol (a);
407 me->dim_cache_[a].offset_ = new Real (0.0);
410 UGH: can't fold next 2 statements together. Apparently GCC thinks
411 dim_cache_[a].offset_ is unaliased.
413 Real off = robust_scm2double (get_property (sym), 0.0);
414 if (me->dim_cache_[a].offset_)
416 *me->dim_cache_[a].offset_ += off;
417 me->del_property (sym);
418 return *me->dim_cache_[a].offset_;
425 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
427 if (pure && a != Y_AXIS)
428 programming_error ("tried to get pure X-offset");
429 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
430 : relative_coordinate (refp, a);
433 /****************************************************************
435 ****************************************************************/
438 Grob::flush_extent_cache (Axis axis)
440 if (dim_cache_[axis].extent_)
443 Ugh, this is not accurate; will flush property, causing
444 callback to be called if.
446 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
447 delete dim_cache_[axis].extent_;
448 dim_cache_[axis].extent_ = 0;
449 if (get_parent (axis))
450 get_parent (axis)->flush_extent_cache (axis);
455 Grob::extent (Grob *refp, Axis a) const
457 Real offset = relative_coordinate (refp, a);
459 if (dim_cache_[a].extent_)
461 real_ext = *dim_cache_[a].extent_;
466 Order is significant: ?-extent may trigger suicide.
468 SCM ext = (a == X_AXIS)
469 ? get_property ("X-extent")
470 : get_property ("Y-extent");
471 if (is_number_pair (ext))
472 real_ext.unite (ly_scm2interval (ext));
474 SCM min_ext = (a == X_AXIS)
475 ? get_property ("minimum-X-extent")
476 : get_property ("minimum-Y-extent");
477 if (is_number_pair (min_ext))
478 real_ext.unite (ly_scm2interval (min_ext));
480 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
483 // We never want nan, so we avoid shifting infinite values.
485 real_ext.translate(offset);
487 warning(_f ("ignored infinite %s-offset",
488 a == X_AXIS ? "X" : "Y"));
494 Grob::pure_y_extent (Grob *refp, int start, int end)
496 SCM iv_scm = get_pure_property ("Y-extent", start, end);
497 Interval iv = robust_scm2interval (iv_scm, Interval ());
498 Real offset = pure_relative_y_coordinate (refp, start, end);
500 SCM min_ext = get_property ("minimum-Y-extent");
502 /* we don't add minimum-Y-extent if the extent is empty. This solves
503 a problem with Hara-kiri spanners. They would request_suicide and
504 return empty extents, but we would force them here to be large. */
505 if (!iv.is_empty () && is_number_pair (min_ext))
506 iv.unite (ly_scm2interval (min_ext));
509 iv.translate (offset);
514 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
516 return (pure && a == Y_AXIS) ? pure_y_extent (refp, start, end) : extent (refp, a);
520 Grob::spanned_rank_interval () const
522 return Interval_t<int> (-1, 0);
526 Grob::pure_is_visible (int /* start */, int /* end */) const
531 /* Sort grobs according to their starting column. */
533 Grob::less (Grob *g1, Grob *g2)
535 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
538 /****************************************************************
540 ****************************************************************/
542 /* Find the group-element which has both #this# and #s# */
544 Grob::common_refpoint (Grob const *s, Axis a) const
547 /* Catching the trivial cases is likely costlier than just running
548 through: one can't avoid going to the respective chain ends
549 anyway. We might save the second run through when the chain ends
550 differ, but keeping track of the ends makes the loop more costly.
557 for (c = this; c; ++balance)
558 c = c->dim_cache_[a].parent_;
560 for (d = s; d; --balance)
561 d = d->dim_cache_[a].parent_;
563 /* Cut down ancestry to same size */
565 for (c = this; balance > 0; --balance)
566 c = c->dim_cache_[a].parent_;
568 for (d = s; balance < 0; ++balance)
569 d = d->dim_cache_[a].parent_;
571 /* Now find point where our lineages converge */
574 c = c->dim_cache_[a].parent_;
575 d = d->dim_cache_[a].parent_;
582 Grob::set_parent (Grob *g, Axis a)
584 dim_cache_[a].parent_ = g;
588 Grob::get_parent (Axis a) const
590 return dim_cache_[a].parent_;
594 Grob::fixup_refpoint ()
596 for (int a = X_AXIS; a < NO_AXES; a++)
599 Grob *parent = get_parent (ax);
604 if (parent->get_system () != get_system () && get_system ())
606 Grob *newparent = parent->find_broken_piece (get_system ());
607 set_parent (newparent, ax);
610 if (Item *i = dynamic_cast<Item *> (this))
612 Item *parenti = dynamic_cast<Item *> (parent);
616 Direction my_dir = i->break_status_dir ();
617 if (my_dir != parenti->break_status_dir ())
619 Item *newparent = parenti->find_prebroken_piece (my_dir);
620 set_parent (newparent, ax);
627 /****************************************************************
629 ****************************************************************/
632 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
636 if (has_interface<Align_interface> (g))
637 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
638 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
643 Grob::get_root_vertical_alignment (Grob *g)
645 return get_maybe_root_vertical_alignment (g, 0);
649 Grob::get_vertical_axis_group (Grob *g)
653 if (!g->get_parent (Y_AXIS))
655 if (has_interface<Axis_group_interface> (g)
656 && has_interface<Align_interface> (g->get_parent (Y_AXIS)))
658 return get_vertical_axis_group (g->get_parent (Y_AXIS));
663 Grob::get_vertical_axis_group_index (Grob *g)
665 Grob *val = get_root_vertical_alignment (g);
668 Grob *vax = get_vertical_axis_group (g);
669 extract_grob_set (val, "elements", elts);
670 for (vsize i = 0; i < elts.size (); i++)
673 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
678 Grob::vertical_less (Grob *g1, Grob *g2)
680 return internal_vertical_less (g1, g2, false);
684 Grob::pure_vertical_less (Grob *g1, Grob *g2)
686 return internal_vertical_less (g1, g2, true);
690 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
692 Grob *vag = get_root_vertical_alignment (g1);
695 g1->programming_error ("grob does not belong to a VerticalAlignment?");
699 Grob *ag1 = get_vertical_axis_group (g1);
700 Grob *ag2 = get_vertical_axis_group (g2);
702 extract_grob_set (vag, "elements", elts);
704 if (ag1 == ag2 && !pure)
706 Grob *common = g1->common_refpoint (g2, Y_AXIS);
707 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
710 for (vsize i = 0; i < elts.size (); i++)
718 g1->programming_error ("could not place this grob in its axis group");
722 /****************************************************************
724 ****************************************************************/
726 Grob::programming_error (const string &s) const
728 SCM cause = self_scm ();
729 while (Grob *g = unsmob<Grob> (cause))
730 cause = g->get_property ("cause");
732 /* ES TODO: cause can't be Music*/
733 if (Music *m = unsmob<Music> (cause))
734 m->origin ()->programming_error (s);
735 else if (Stream_event *ev = unsmob<Stream_event> (cause))
736 ev->origin ()->programming_error (s);
738 ::programming_error (s);
742 Grob::warning (const string &s) const
744 SCM cause = self_scm ();
745 while (Grob *g = unsmob<Grob> (cause))
746 cause = g->get_property ("cause");
748 /* ES TODO: cause can't be Music*/
749 if (Music *m = unsmob<Music> (cause))
750 m->origin ()->warning (s);
751 else if (Stream_event *ev = unsmob<Stream_event> (cause))
752 ev->origin ()->warning (s);
760 SCM meta = get_property ("meta");
761 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
762 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
763 return scm_is_symbol (nm) ? ly_symbol2string (nm) : class_name ();
767 "A grob represents a piece of music notation.\n"
769 "All grobs have an X and Y@tie{}position on the page. These"
770 " X and Y@tie{}positions are stored in a relative format, thus"
771 " they can easily be combined by stacking them, hanging one"
772 " grob to the side of another, or coupling them into grouping"
775 "Each grob has a reference point (a.k.a.@: parent): The"
776 " position of a grob is stored relative to that reference"
777 " point. For example, the X@tie{}reference point of a staccato"
778 " dot usually is the note head that it applies to. When the"
779 " note head is moved, the staccato dot moves along"
782 "A grob is often associated with a symbol, but some grobs do"
783 " not print any symbols. They take care of grouping objects."
784 " For example, there is a separate grob that stacks staves"
785 " vertically. The @ref{NoteCollision} object is also an"
786 " abstract grob: It only moves around chords, but doesn't print"
789 "Grobs have properties (Scheme variables) that can be read and"
790 " set. Two types of them exist: immutable and mutable."
791 " Immutable variables define the default style and behavior."
792 " They are shared between many objects. They can be changed"
793 " using @code{\\override} and @code{\\revert}. Mutable"
794 " properties are variables that are specific to one grob."
795 " Typically, lists of other objects, or results from"
796 " computations are stored in mutable properties. In"
797 " particular, every call to @code{ly:grob-set-property!}"
798 " (or its C++ equivalent) sets a mutable property.\n"
800 "The properties @code{after-line-breaking} and"
801 " @code{before-line-breaking} are dummies that are not"
802 " user-serviceable.",
809 "after-line-breaking "
811 "axis-group-parent-X "
812 "axis-group-parent-Y "
813 "before-line-breaking "
821 "horizontal-skylines "
827 "parenthesis-friends "
828 "pure-Y-offset-in-progress "
830 "skyline-horizontal-padding "
840 /****************************************************************
842 ****************************************************************/
845 grob_stencil_extent (Grob *me, Axis a)
847 Stencil *m = me->get_stencil ();
851 return ly_interval2scm (e);
854 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
856 Grob::stencil_height (SCM smob)
858 Grob *me = unsmob<Grob> (smob);
859 return grob_stencil_extent (me, Y_AXIS);
862 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
864 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
866 Grob *me = unsmob<Grob> (smob);
867 if (unsmob<Stencil> (me->get_property_data ("stencil")))
868 return grob_stencil_extent (me, Y_AXIS);
870 return ly_interval2scm (Interval ());
874 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
876 Grob::y_parent_positioning (SCM smob)
878 Grob *me = unsmob<Grob> (smob);
879 Grob *par = me->get_parent (Y_AXIS);
881 (void) par->get_property ("positioning-done");
883 return scm_from_double (0.0);
886 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
888 Grob::x_parent_positioning (SCM smob)
890 Grob *me = unsmob<Grob> (smob);
892 Grob *par = me->get_parent (X_AXIS);
894 (void) par->get_property ("positioning-done");
896 return scm_from_double (0.0);
899 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
901 Grob::stencil_width (SCM smob)
903 Grob *me = unsmob<Grob> (smob);
904 return grob_stencil_extent (me, X_AXIS);
908 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
910 for (; scm_is_pair (elist); elist = scm_cdr (elist))
911 if (Grob *s = unsmob<Grob> (scm_car (elist)))
914 common = common->common_refpoint (s, a);
923 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
925 for (vsize i = 0; i < arr.size (); i++)
927 common = common->common_refpoint (arr[i], a);
935 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
937 set<Grob *>::iterator it;
939 for (it = arr.begin (); it != arr.end (); it++)
941 common = common->common_refpoint (*it, a);
949 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
951 Interval ext = me->extent (refpoint, a);
953 ext.add_point (me->relative_coordinate (refpoint, a));
958 // Checks whether there is a vertical alignment in the chain of
959 // parents between this and commony.
961 Grob::check_cross_staff (Grob *commony)
963 if (has_interface<Align_interface> (commony))
966 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
967 if (has_interface<Align_interface> (g))
975 indirect_less (Grob **a, Grob **b)
977 // Use original order as tie breaker. That gives us a stable sort
978 // at the lower price tag of an unstable one, and we want a stable
979 // sort in order to reliably retain the first instance of a grob
981 return *a < *b || (*a == *b && a < b);
986 indirect_eq (Grob **a, Grob **b)
993 direct_less (Grob **a, Grob **b)
998 // uniquify uniquifies on the memory addresses of the Grobs, but then
999 // uses the original order. This makes results independent from the
1000 // memory allocation of Grobs.
1003 uniquify (vector <Grob *> & grobs)
1005 vector <Grob **> vec (grobs.size ());
1006 for (vsize i = 0; i < grobs.size (); i++)
1008 vector_sort (vec, indirect_less);
1009 vec.erase (unique (vec.begin (), vec.end (), indirect_eq), vec.end ());
1010 vector_sort (vec, direct_less);
1012 // Since the output is a sorted copy of the input with some elements
1013 // removed, we can fill in the vector in-place if we do it starting
1015 for (vsize i = 0; i < vec.size (); i++)
1017 grobs.erase (grobs.begin () + vec.size (), grobs.end ());