]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
try to avoid NaNs and infs in alignment and pure-height
[lilypond.git] / lily / grob.cc
1 /*
2   grob.cc -- implement Grob
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "grob.hh"
10
11 #include <cstring>
12
13 #include "align-interface.hh"
14 #include "input.hh"
15 #include "international.hh"
16 #include "item.hh"
17 #include "main.hh"
18 #include "misc.hh"
19 #include "music.hh"
20 #include "output-def.hh"
21 #include "pointer-group-interface.hh"
22 #include "stencil.hh"
23 #include "stream-event.hh"
24 #include "system.hh"
25 #include "warn.hh"
26
27 #include "ly-smobs.icc"
28
29 Grob *
30 Grob::clone () const
31 {
32   return new Grob (*this);
33 }
34
35 Grob::Grob (SCM basicprops)         
36 {
37   
38   /* FIXME: default should be no callback.  */
39   self_scm_ = SCM_EOL;
40   layout_ = 0;
41   original_ = 0;
42   interfaces_ = SCM_EOL;
43   immutable_property_alist_ = basicprops;
44   mutable_property_alist_ = SCM_EOL;
45   object_alist_ = SCM_EOL;
46   
47   /* We do smobify_self () as the first step.  Since the object lives
48      on the heap, none of its SCM variables are protected from
49      GC. After smobify_self (), they are.  */
50   smobify_self ();
51
52   SCM meta = get_property ("meta");
53   if (scm_is_pair (meta))
54     {
55       interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
56
57       SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
58       if (scm_is_pair (object_cbs))
59         {
60           for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
61             set_object (scm_caar (s), scm_cdar (s)); 
62         }
63     }
64   
65   if (get_property_data ("X-extent") == SCM_EOL)
66     set_property ("X-extent", Grob::stencil_width_proc);
67   if (get_property_data ("Y-extent") == SCM_EOL)
68     set_property ("Y-extent", Grob::stencil_height_proc);
69 }
70
71 Grob::Grob (Grob const &s)
72   : dim_cache_ (s.dim_cache_)
73 {
74   original_ = (Grob *) & s;
75   self_scm_ = SCM_EOL;
76
77   immutable_property_alist_ = s.immutable_property_alist_;
78   mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
79   interfaces_ = s.interfaces_;
80   object_alist_ = SCM_EOL;
81
82   layout_ = 0;
83
84   smobify_self ();
85 }
86
87 Grob::~Grob ()
88 {
89 }
90 /****************************************************************
91   STENCILS
92 ****************************************************************/
93
94 Stencil *
95 Grob::get_stencil () const
96 {
97   if (!is_live ())
98     return 0;
99
100   SCM stil = get_property ("stencil");
101   return unsmob_stencil (stil);
102 }
103
104 Stencil
105 Grob::get_print_stencil () const
106 {
107   SCM stil = get_property ("stencil");
108
109   Stencil retval;
110   if (Stencil *m = unsmob_stencil (stil))
111     {
112       retval = *m;
113       if (to_boolean (get_property ("transparent")))
114         retval = Stencil (m->extent_box (), SCM_EOL);
115       else
116         {
117           SCM expr = m->expr ();
118           expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
119                              self_scm (), expr);
120
121           retval = Stencil (m->extent_box (), expr);
122         }
123
124       SCM rot = get_property ("rotation");
125       if (scm_is_pair (rot))
126         {
127           Real angle = scm_to_double (scm_car (rot));
128           Real x = scm_to_double (scm_cadr (rot));
129           Real y = scm_to_double (scm_caddr (rot));
130
131           retval.rotate_degrees (angle, Offset (x, y));
132         }
133
134       /* color support... see interpret_stencil_expression () for more... */
135       SCM color = get_property ("color");
136       if (scm_is_pair (color))
137         {
138           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
139                                  color,
140                                  retval.expr ());
141
142           retval = Stencil (retval.extent_box (), expr);
143         }
144
145     }
146
147   return retval;
148 }
149
150 /****************************************************************
151   VIRTUAL STUBS
152 ****************************************************************/
153 void
154 Grob::do_break_processing ()
155 {
156 }
157
158 void
159 Grob::discretionary_processing ()
160 {
161 }
162
163 System *
164 Grob::get_system () const
165 {
166   return 0;
167 }
168
169
170 void
171 Grob::handle_broken_dependencies ()
172 {
173   Spanner *sp = dynamic_cast<Spanner *> (this);
174   if (original () && sp)
175     return;
176
177   if (sp)
178     /* THIS, SP is the original spanner.  We use a special function
179        because some Spanners have enormously long lists in their
180        properties, and a special function fixes FOO  */
181     {
182       for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
183         sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
184     }
185   System *system = get_system ();
186
187   if (is_live ()
188       && system
189       && common_refpoint (system, X_AXIS)
190       && common_refpoint (system, Y_AXIS))
191     substitute_object_links (system->self_scm (), object_alist_);
192   else if (dynamic_cast<System *> (this))
193     substitute_object_links (SCM_UNDEFINED, object_alist_);
194   else
195     /* THIS element is `invalid'; it has been removed from all
196        dependencies, so let's junk the element itself.
197
198        Do not do this for System, since that would remove references
199        to the originals of score-grobs, which get then GC'd (a bad
200        thing).  */
201     suicide ();
202 }
203
204 /* Note that we still want references to this element to be
205    rearranged, and not silently thrown away, so we keep pointers like
206    {broken_into_{drul, array}, original}
207 */
208 void
209 Grob::suicide ()
210 {
211   if (!is_live ())
212     return;
213
214   for (int a = X_AXIS; a < NO_AXES; a++)
215     dim_cache_[a].clear ();
216
217   mutable_property_alist_ = SCM_EOL;
218   object_alist_ = SCM_EOL;
219   immutable_property_alist_ = SCM_EOL;
220   interfaces_ = SCM_EOL;
221 }
222
223 void
224 Grob::handle_prebroken_dependencies ()
225 {
226   /* Don't do this in the derived method, since we want to keep access to
227      object_alist_ centralized.  */
228   if (original ())
229     {
230       Item *it = dynamic_cast<Item *> (this);
231       substitute_object_links (scm_from_int (it->break_status_dir ()),
232                                original ()->object_alist_);
233     }
234 }
235
236 Grob *
237 Grob::find_broken_piece (System *) const
238 {
239   return 0;
240 }
241
242 /****************************************************************
243    OFFSETS
244 ****************************************************************/
245
246 void
247 Grob::translate_axis (Real y, Axis a)
248 {
249   if (isinf (y) || isnan (y))
250     {
251       programming_error (_ ("Infinity or NaN encountered"));
252       return ;
253     }
254   
255   if (!dim_cache_[a].offset_)
256     dim_cache_[a].offset_ = new Real (y);
257   else
258     *dim_cache_[a].offset_ += y;  
259 }
260
261 /* Find the offset relative to D.  If D equals THIS, then it is 0.
262    Otherwise, it recursively defd as
263
264    OFFSET_ + PARENT_L_->relative_coordinate (D) */
265 Real
266 Grob::relative_coordinate (Grob const *refp, Axis a) const
267 {
268   if (refp == this)
269     return 0.0;
270
271   /* We catch PARENT_L_ == nil case with this, but we crash if we did
272      not ask for the absolute coordinate (ie. REFP == nil.)  */
273   Real off = get_offset (a);
274   if (refp == dim_cache_[a].parent_)
275     return off;
276   
277   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
278
279   return off;
280 }
281
282 Real
283 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
284 {
285   if (refp == this)
286     return 0.0;
287
288   Real off = 0;
289
290   if (dim_cache_[Y_AXIS].offset_)
291     off = *dim_cache_[Y_AXIS].offset_;
292   else
293     {
294       SCM proc = get_property_data ("Y-offset");
295
296       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
297       off = robust_scm2double (call_pure_function (proc,
298                                                    scm_list_1 (self_scm ()),
299                                                    start, end),
300                                0.0);
301       delete dim_cache_[Y_AXIS].offset_;
302       dim_cache_[Y_AXIS].offset_ = 0;
303     }
304
305   /* we simulate positioning-done if we are the child of a VerticalAlignment,
306      but only if we don't have a cached offset. If we do have a cached offset,
307      it probably means that the Alignment was fixed and it has already been
308      calculated.
309   */
310   if (Grob *p = get_parent (Y_AXIS))
311     {
312       Real trans = 0;
313       if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
314         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
315
316       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
317     }
318   return off;
319 }
320
321 /* Invoke callbacks to get offset relative to parent.  */
322 Real
323 Grob::get_offset (Axis a) const
324 {
325   if (dim_cache_[a].offset_)
326     return *dim_cache_[a].offset_;
327
328   Grob *me = (Grob *) this;
329
330   SCM sym = axis_offset_symbol (a);
331   me->dim_cache_[a].offset_ = new Real (0.0);
332
333   /*
334     UGH: can't fold next 2 statements together. Apparently GCC thinks
335     dim_cache_[a].offset_ is unaliased.
336   */
337   Real off = robust_scm2double (internal_get_property (sym), 0.0);
338   if (me->dim_cache_[a].offset_)
339     {
340       *me->dim_cache_[a].offset_ += off;
341       me->del_property (sym);
342       return *me->dim_cache_[a].offset_;
343     }
344   else
345     return 0.0;
346 }
347
348 Real
349 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
350 {
351   if (pure && a != Y_AXIS)
352     programming_error ("tried to get pure X-offset");
353   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
354     : relative_coordinate (refp, a);
355 }
356
357 /****************************************************************
358   extents
359 ****************************************************************/
360
361 void
362 Grob::flush_extent_cache (Axis axis)
363 {
364   if (dim_cache_[axis].extent_)
365     {
366       /*
367         Ugh, this is not accurate; will flush property, causing
368         callback to be called if.
369        */
370       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
371       delete dim_cache_[axis].extent_;
372       dim_cache_[axis].extent_ = 0;
373       if (get_parent (axis))
374         get_parent (axis)->flush_extent_cache (axis);
375     }
376 }
377
378
379 Interval
380 Grob::extent (Grob *refp, Axis a) const
381 {
382   Real offset = relative_coordinate (refp, a);
383   Interval real_ext;
384   if (dim_cache_[a].extent_)
385     {
386       real_ext = *dim_cache_[a].extent_;
387     }
388   else
389     {
390       /*
391         Order is significant: ?-extent may trigger suicide.
392        */
393       SCM ext_sym =
394         (a == X_AXIS)
395         ? ly_symbol2scm ("X-extent")
396         : ly_symbol2scm ("Y-extent");
397         
398       SCM ext = internal_get_property (ext_sym);
399       if (is_number_pair (ext))
400         real_ext.unite (ly_scm2interval (ext));
401
402       SCM min_ext_sym =
403         (a == X_AXIS)
404         ? ly_symbol2scm ("minimum-X-extent")
405         : ly_symbol2scm ("minimum-Y-extent");
406       SCM min_ext = internal_get_property (min_ext_sym);
407       if (is_number_pair (min_ext))
408         real_ext.unite (ly_scm2interval (min_ext));
409
410       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
411     }
412   
413   real_ext.translate (offset);
414   
415   return real_ext;
416 }
417
418 Interval
419 Grob::pure_height (Grob *refp, int start, int end)
420 {
421   SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
422   SCM iv_scm = call_pure_function (proc,
423                                    scm_list_1 (self_scm ()),
424                                    start, end);
425   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
426   Real offset = pure_relative_y_coordinate (refp, start, end);
427
428   SCM min_ext = get_property ("minimum-Y-extent");
429
430   /* we don't add minimum-Y-extent if the extent is empty. This solves
431      a problem with Hara-kiri spanners. They would request_suicide and
432      return empty extents, but we would force them here to be large. */
433   if (!iv.is_empty () && is_number_pair (min_ext))
434     iv.unite (ly_scm2interval (min_ext));
435
436   if (!iv.is_empty ())
437     iv.translate (offset);
438   return iv;
439 }
440
441 Interval
442 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
443 {
444   if (pure && a != Y_AXIS)
445     programming_error ("tried to get pure width");
446   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
447 }
448
449 Interval_t<int>
450 Grob::spanned_rank_interval () const
451 {
452   return Interval_t<int> (-1, 0);
453 }
454
455 /****************************************************************
456   REFPOINTS
457 ****************************************************************/
458
459 /* Find the group-element which has both #this# and #s#  */
460 Grob *
461 Grob::common_refpoint (Grob const *s, Axis a) const
462 {
463   /* I don't like the quadratic aspect of this code, but I see no
464      other way.  The largest chain of parents might be 10 high or so,
465      so it shouldn't be a real issue.  */
466   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
467     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
468       if (d == c)
469         return (Grob *) d;
470
471   return 0;
472 }
473
474 void
475 Grob::set_parent (Grob *g, Axis a)
476 {
477   dim_cache_[a].parent_ = g;
478 }
479
480 Grob *
481 Grob::get_parent (Axis a) const
482 {
483   return dim_cache_[a].parent_;
484 }
485
486
487 void
488 Grob::fixup_refpoint ()
489 {
490   for (int a = X_AXIS; a < NO_AXES; a++)
491     {
492       Axis ax = (Axis)a;
493       Grob *parent = get_parent (ax);
494
495       if (!parent)
496         continue;
497
498       if (parent->get_system () != get_system () && get_system ())
499         {
500           Grob *newparent = parent->find_broken_piece (get_system ());
501           set_parent (newparent, ax);
502         }
503
504       if (Item *i = dynamic_cast<Item *> (this))
505         {
506           Item *parenti = dynamic_cast<Item *> (parent);
507
508           if (parenti && i)
509             {
510               Direction my_dir = i->break_status_dir ();
511               if (my_dir != parenti->break_status_dir ())
512                 {
513                   Item *newparent = parenti->find_prebroken_piece (my_dir);
514                   set_parent (newparent, ax);
515                 }
516             }
517         }
518     }
519 }
520
521
522 /****************************************************************
523   MESSAGES
524 ****************************************************************/
525 void
526 Grob::warning (string s) const
527 {
528   SCM cause = self_scm ();
529   while (Grob *g = unsmob_grob (cause))
530     cause = g->get_property ("cause");
531
532   /* ES TODO: cause can't be Music*/
533   if (Music *m = unsmob_music (cause))
534     m->origin ()->warning (s);
535   else if (Stream_event *ev = unsmob_stream_event (cause))
536     ev->origin ()->warning (s);
537   else
538     ::warning (s);
539 }
540
541
542 string
543 Grob::name () const
544 {
545   SCM meta = get_property ("meta");
546   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
547   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
548   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
549 }
550
551 void
552 Grob::programming_error (string s) const
553 {
554   SCM cause = self_scm ();
555   while (Grob *g = unsmob_grob (cause))
556     cause = g->get_property ("cause");
557
558   s = _f ("programming error: %s", s);
559
560   /* ES TODO: cause can't be Music*/
561   if (Music *m = unsmob_music (cause))
562     m->origin ()->message (s);
563   else if (Stream_event *ev = unsmob_stream_event (cause))
564     ev->origin ()->warning (s);
565   else
566     ::message (s);
567 }
568
569
570 ADD_INTERFACE (Grob,
571                "A grob represents a piece of music notation\n"
572                "\n"
573                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
574                "are stored in a relative format, so they can easily be combined by\n"
575                "stacking them, hanging one grob to the side of another, and coupling\n"
576                "them into a grouping objects.\n"
577                "\n"
578                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
579                "is stored relative to that reference point. For example the X-reference\n"
580                "point of a staccato dot usually is the note head that it applies\n"
581                "to. When the note head is moved, the staccato dot moves along\n"
582                "automatically.\n"
583                "\n"
584                "A grob is often associated with a symbol, but some grobs do not print\n"
585                "any symbols. They take care of grouping objects. For example, there is a\n"
586                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
587                "is also an abstract grob: it only moves around chords, but doesn't print\n"
588                "anything.\n"
589                "\n"
590                "Grobs have a properties: Scheme variables, that can be read and set. "
591                "They have two types. Immutable variables "
592                "define the default style and behavior.  They are shared between  many objects. "
593                "They can be changed using @code{\\override} and @code{\\revert}. "
594                "\n\n"
595                "Mutable properties are variables that are specific to one grob. Typically, "
596                "lists of other objects, or results from computations are stored in"
597                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
598                "sets a mutable property. "
599                "\n\n"
600                "The properties @code{after-line-breaking} and @code{before-line-breaking} "
601                "are dummies that are not user-serviceable. "
602
603                ,
604
605                /* properties */
606                "X-extent "
607                "X-offset "
608                "Y-extent "
609                "Y-offset "
610                "after-line-breaking "
611                "avoid-slur "
612                "axis-group-parent-X "
613                "axis-group-parent-Y "
614                "before-line-breaking "
615                "cause "
616                "color "
617                "cross-staff "
618                "extra-X-extent "
619                "extra-Y-extent "
620                "extra-offset "
621                "interfaces "
622                "layer "
623                "meta "
624                "minimum-X-extent "
625                "minimum-Y-extent "
626                "outside-staff-horizontal-padding "
627                "outside-staff-padding "
628                "outside-staff-priority "
629                "rotation "
630                "springs-and-rods "
631                "staff-symbol "
632                "stencil "
633                "transparent "
634                );
635
636 /****************************************************************
637   CALLBACKS
638 ****************************************************************/
639
640 static SCM
641 grob_stencil_extent (Grob *me, Axis a)
642 {
643   Stencil *m = me->get_stencil ();
644   Interval e;
645   if (m)
646     e = m->extent (a);
647   return ly_interval2scm (e);
648 }
649
650
651 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
652 SCM
653 Grob::stencil_height (SCM smob)
654 {
655   Grob *me = unsmob_grob (smob);
656   return grob_stencil_extent (me, Y_AXIS);
657 }
658
659 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
660 SCM
661 Grob::y_parent_positioning (SCM smob)
662 {
663   Grob *me = unsmob_grob (smob);
664   Grob *par = me->get_parent (Y_AXIS);
665   if (par)
666     (void) par->get_property ("positioning-done");
667
668   return scm_from_double (0.0);
669 }
670
671
672 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
673 SCM
674 Grob::x_parent_positioning (SCM smob)
675 {
676   Grob *me = unsmob_grob (smob);
677   
678   Grob *par = me->get_parent (X_AXIS);
679   if (par)
680     (void) par->get_property ("positioning-done");
681
682   return scm_from_double (0.0);
683 }
684
685 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
686 SCM
687 Grob::stencil_width (SCM smob)
688 {
689   Grob *me = unsmob_grob (smob);
690   return grob_stencil_extent (me, X_AXIS);
691 }
692
693
694
695 Grob *
696 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
697 {
698   for (; scm_is_pair (elist); elist = scm_cdr (elist))
699     if (Grob *s = unsmob_grob (scm_car (elist)))
700       {
701         if (common)
702           common = common->common_refpoint (s, a);
703         else
704           common = s;
705       }
706
707   return common;
708 }
709
710 Grob *
711 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
712 {
713   for (vsize i = arr.size (); i--;)
714     if (Grob *s = arr[i])
715       {
716         if (common)
717           common = common->common_refpoint (s, a);
718         else
719           common = s;
720       }
721
722   return common;
723 }
724
725 Interval
726 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
727 {
728   Interval ext = me->extent (refpoint, a);
729   if (ext.is_empty ())
730     ext.add_point (me->relative_coordinate (refpoint, a));
731
732   return ext;
733 }
734