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