]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* input/regression/grace-auto-beam.ly: new file
[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--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9
10 #include <string.h>
11 #include <math.h>
12
13 #include "main.hh"
14 #include "input-smob.hh"
15 #include "warn.hh"
16 #include "group-interface.hh"
17 #include "misc.hh"
18 #include "paper-score.hh"
19 #include "molecule.hh"
20 #include "grob.hh"
21 #include "spanner.hh"
22 #include "system.hh"
23 #include "item.hh"
24 #include "molecule.hh"
25 #include "misc.hh"
26 #include "music.hh"
27 #include "item.hh"
28
29 #include "ly-smobs.icc"
30
31 /*
32 TODO:
33
34 remove dynamic_cast<Spanner,Item> and put this code into respective
35   subclass.
36 */
37
38 //#define HASHING_FOR_MUTABLE_PROPS
39 #define HASH_SIZE 3
40 #define INFINITY_MSG "Infinity or NaN encountered"
41
42 Grob::Grob (SCM basicprops)
43 {
44   /*
45     fixme: default should be no callback.
46    */
47
48   pscore_l_=0;
49   status_c_ = 0;
50   original_l_ = 0;
51   immutable_property_alist_ =  basicprops;
52   mutable_property_alist_ = SCM_EOL;
53
54   /*
55     We do smobify_self() as the first step. Since the object lives on
56     the heap, none of its SCM variables are protected from GC. After
57     smobify_self(), they are.
58    */
59   smobify_self ();
60
61
62 #ifdef HASHING_FOR_MUTABLE_PROPS
63   mutable_property_alist_ = scm_c_make_hash_table (HASH_SIZE);
64 #endif
65   
66   SCM meta = get_grob_property ("meta");
67   if (gh_pair_p (meta))
68     {
69       SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
70
71       /*
72         Switch off interface checks for the moment.
73        */
74       bool itc = internal_type_checking_global_b;
75       internal_type_checking_global_b = false;
76       internal_set_grob_property (ly_symbol2scm ("interfaces"), gh_cdr(ifs));
77       internal_type_checking_global_b = itc;
78     }
79   
80   /*
81     TODO:
82
83     destill this into a function, so we can re-init the immutable
84     properties with a new BASICPROPS value after creation. Convenient
85     eg. when using \override with StaffSymbol.  */
86   
87   char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
88   char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
89   
90   for (int a = X_AXIS; a <= Y_AXIS; a++)
91     {
92       SCM l = get_grob_property (onames[a]);
93
94       if (scm_ilength (l) >=0)
95         {
96           dim_cache_[a].offset_callbacks_ = l;
97           dim_cache_[a].offsets_left_ = scm_ilength (l);
98         }
99       else
100         {
101           programming_error ("[XY]-offset-callbacks must be a list");
102         }
103
104       SCM cb = get_grob_property (enames[a]);
105
106       /*
107         Should change default to be empty? 
108       */
109       if (cb != SCM_BOOL_F
110           && !gh_procedure_p (cb) && !gh_pair_p (cb)
111           && gh_procedure_p (get_grob_property ("molecule-callback"))
112           )
113         cb = molecule_extent_proc;
114     
115       dim_cache_[a].dimension_ = cb;
116     }
117
118 }
119
120 Grob::Grob (Grob const&s)
121    : dim_cache_ (s.dim_cache_)
122 {
123   original_l_ = (Grob*) &s;
124   immutable_property_alist_ = s.immutable_property_alist_;
125
126   mutable_property_alist_ = SCM_EOL;
127   
128   /*
129     No properties are copied. That is the job of handle_broken_dependencies.
130    */
131   
132   status_c_ = s.status_c_;
133   pscore_l_ = s.pscore_l_;
134
135   smobify_self ();
136
137 #ifdef HASHING_FOR_MUTABLE_PROPS
138   mutable_property_alist_ = scm_c_make_hash_table (HASH_SIZE);
139 #endif
140 }
141
142 Grob::~Grob ()
143 {
144   /*
145     do nothing scm-ish and no unprotecting here.
146    */
147 }
148
149
150
151
152
153 MAKE_SCHEME_CALLBACK (Grob,molecule_extent,2);
154 SCM
155 Grob::molecule_extent (SCM element_smob, SCM scm_axis)
156 {
157   Grob *s = unsmob_grob (element_smob);
158   Axis a = (Axis) gh_scm2int (scm_axis);
159
160   Molecule *m = s->get_molecule ();
161   Interval e ;
162   if (m)
163     e = m->extent (a);
164   return ly_interval2scm (e);
165 }
166
167 MAKE_SCHEME_CALLBACK (Grob,preset_extent,2);
168 SCM
169 Grob::preset_extent (SCM element_smob, SCM scm_axis)
170 {
171   Grob *s = unsmob_grob (element_smob);
172   Axis a = (Axis) gh_scm2int (scm_axis);
173
174   SCM ext = s->get_grob_property ((a == X_AXIS)
175                                  ? "extent-X"
176                                  : "extent-Y");
177   
178   if (gh_pair_p (ext))
179     {
180       Real l = gh_scm2double (ly_car (ext));
181       Real r = gh_scm2double (ly_cdr (ext));
182       return ly_interval2scm (Interval (l, r));
183     }
184   
185   return ly_interval2scm (Interval ());
186 }
187
188
189
190 Paper_def*
191 Grob::paper_l ()  const
192 {
193  return pscore_l_ ? pscore_l_->paper_l_ : 0;
194 }
195
196 void
197 Grob::calculate_dependencies (int final, int busy, SCM funcname)
198 {
199   if (status_c_ >= final)
200     return;
201
202   if (status_c_== busy)
203     {
204       programming_error ("Element is busy, come back later");
205       return;
206     }
207   
208   status_c_= busy;
209
210   for (SCM d = get_grob_property ("dependencies"); gh_pair_p (d);
211        d = ly_cdr (d))
212     {
213       unsmob_grob (ly_car (d))
214         ->calculate_dependencies (final, busy, funcname);
215     }
216
217   
218   SCM proc = internal_get_grob_property (funcname);
219   if (gh_procedure_p (proc))
220     gh_call1 (proc, this->self_scm ());
221  
222   status_c_= final;
223 }
224
225 Molecule *
226 Grob::get_molecule ()  const
227 {
228   if (!live())
229     {
230       return 0;
231       
232     }
233   
234   SCM mol = get_grob_property ("molecule");
235   if (unsmob_molecule (mol))
236     return unsmob_molecule (mol);
237
238   mol =  get_uncached_molecule ();
239   
240   Grob *me = (Grob*)this;
241   me->set_grob_property ("molecule", mol);
242   
243   return unsmob_molecule (mol);  
244 }
245 SCM
246 Grob::get_uncached_molecule ()const
247 {
248   SCM proc = get_grob_property ("molecule-callback");
249
250   SCM  mol = SCM_EOL;
251   if (gh_procedure_p (proc)) 
252     mol = gh_apply (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
253
254   
255   Molecule *m = unsmob_molecule (mol);
256   
257   if (unsmob_molecule (mol))
258     {
259       SCM origin = ly_symbol2scm ("no-origin");
260       
261       if (store_locations_global_b){
262         SCM cause = get_grob_property ("cause");
263         if (Music*m = unsmob_music (cause))
264           {
265             SCM music_origin = m->get_mus_property ("origin");
266             if (unsmob_input (music_origin))
267               origin = music_origin;
268           }
269       }
270
271       // ugr.
272       
273       mol = Molecule (m->extent_box (),
274                       scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
275                       ). smobbed_copy ();
276
277       m = unsmob_molecule (mol);
278     }
279   
280   /*
281     transparent retains dimensions of element.
282    */
283   if (m && to_boolean (get_grob_property ("transparent")))
284     mol = Molecule (m->extent_box (), SCM_EOL).smobbed_copy ();
285
286   return mol;
287 }
288
289 /*
290   
291   VIRTUAL STUBS
292
293  */
294 void
295 Grob::do_break_processing ()
296 {
297 }
298
299
300
301
302
303
304 System *
305 Grob::line_l () const
306 {
307   return 0;
308 }
309
310 void
311 Grob::add_dependency (Grob*e)
312 {
313   if (e)
314     {
315       Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
316     }
317   else
318     programming_error ("Null dependency added");
319 }
320
321
322
323 void
324 Grob::handle_broken_dependencies ()
325 {
326   Spanner * s= dynamic_cast<Spanner*> (this);
327   if (original_l_ && s)
328     return;
329
330   if (s)
331     {
332       for (int i = 0;  i< s->broken_into_l_arr_ .size (); i++)
333         {
334           Grob * sc = s->broken_into_l_arr_[i];
335           System * l = sc->line_l ();
336
337           sc->substitute_mutable_properties (l ? l->self_scm () : SCM_UNDEFINED,
338                                    mutable_property_alist_);
339         }
340     }
341
342
343   System *line = line_l ();
344
345   if (live ()
346       && line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
347     {
348       substitute_mutable_properties (line ? line->self_scm () : SCM_UNDEFINED,
349                                mutable_property_alist_);
350     }
351   else if (dynamic_cast <System*> (this))
352     {
353       substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
354     }
355   else
356     {
357       /*
358         This element is `invalid'; it has been removed from all
359         dependencies, so let's junk the element itself.
360
361         do not do this for System, since that would remove references
362         to the originals of score-grobs, which get then GC'd (a bad
363         thing.)
364  
365       */
366       suicide ();
367     }
368 }
369
370 /*
371  Note that we still want references to this element to be
372  rearranged, and not silently thrown away, so we keep pointers
373  like {broken_into_{drul,array}, original}
374 */
375 void
376 Grob::suicide ()
377 {
378   mutable_property_alist_ = SCM_EOL;
379   immutable_property_alist_ = SCM_EOL;
380
381   set_extent (SCM_EOL, Y_AXIS);
382   set_extent (SCM_EOL, X_AXIS);
383
384   for (int a= X_AXIS; a <= Y_AXIS; a++)
385     {
386       dim_cache_[a].offset_callbacks_ = SCM_EOL;
387       dim_cache_[a].offsets_left_ = 0;
388     }
389 }
390
391 void
392 Grob::handle_prebroken_dependencies ()
393 {
394   /*
395     Don't do this in the derived method, since we want to keep access to
396     mutable_property_alist_ centralized.
397    */
398   if (original_l_)
399     {
400       Item * it = dynamic_cast<Item*> (this);
401       substitute_mutable_properties (gh_int2scm (it->break_status_dir ()),
402                                original_l_->mutable_property_alist_);
403     }
404 }
405
406 Grob*
407 Grob::find_broken_piece (System*) const
408 {
409   return 0;
410 }
411
412 /*
413   translate in one direction
414 */
415 void
416 Grob::translate_axis (Real y, Axis a)
417 {
418   if (isinf (y) || isnan (y))
419     programming_error (_ (INFINITY_MSG));
420   else
421     {
422       dim_cache_[a].offset_ += y;
423     }
424 }  
425
426
427 /*
428   Find the offset relative to D.  If   D equals THIS, then it is 0.
429   Otherwise, it recursively defd as
430   
431   OFFSET_ + PARENT_L_->relative_coordinate (D)
432 */
433 Real
434 Grob::relative_coordinate (Grob const*refp, Axis a) const
435 {
436   if (refp == this)
437     return 0.0;
438
439   /*
440     We catch PARENT_L_ == nil case with this, but we crash if we did
441     not ask for the absolute coordinate (ie. REFP == nil.)
442     
443    */
444   if (refp == dim_cache_[a].parent_l_)
445     return get_offset (a);
446   else
447     return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
448 }
449
450
451   
452 /*
453   Invoke callbacks to get offset relative to parent.
454 */
455 Real
456 Grob::get_offset (Axis a) const
457 {
458   Grob *me = (Grob*) this;
459   while (dim_cache_[a].offsets_left_)
460     {
461       int l = --me->dim_cache_[a].offsets_left_;
462       SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_,  gh_int2scm (l));
463       SCM retval = gh_call2 (cb, self_scm (), gh_int2scm (a));
464
465       Real r =  gh_scm2double (retval);
466       if (isinf (r) || isnan (r))
467         {
468           programming_error (INFINITY_MSG);
469           r = 0.0;
470         }
471       me->dim_cache_[a].offset_ +=r;
472     }
473   return dim_cache_[a].offset_;
474 }
475
476
477 MAKE_SCHEME_CALLBACK (Grob,point_dimension_callback,2);
478 SCM
479 Grob::point_dimension_callback (SCM , SCM)
480 {
481   return ly_interval2scm (Interval (0,0));
482 }
483
484 bool
485 Grob::empty_b (Axis a)const
486 {
487   return ! (gh_pair_p (dim_cache_[a].dimension_) ||
488             gh_procedure_p (dim_cache_[a].dimension_));
489 }
490
491 Interval
492 Grob::extent (Grob * refp, Axis a) const
493 {
494   Real x = relative_coordinate (refp, a);
495
496   
497   Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
498   Interval ext ;   
499   if (gh_pair_p (d->dimension_))
500     ;
501   else if (gh_procedure_p (d->dimension_))
502     {
503       /*
504         FIXME: add doco on types, and should typecheck maybe? 
505        */
506       d->dimension_= gh_call2 (d->dimension_, self_scm (), gh_int2scm (a));
507     }
508   else
509     return ext;
510
511   if (!gh_pair_p (d->dimension_))
512     return ext;
513   
514   ext = ly_scm2interval (d->dimension_);
515
516   SCM extra = get_grob_property (a == X_AXIS
517                                 ? "extra-extent-X"
518                                 : "extra-extent-Y");
519
520   /*
521     signs ?
522    */
523   if (gh_pair_p (extra))
524     {
525       ext[BIGGER] +=  gh_scm2double (ly_cdr (extra));
526       ext[SMALLER] +=   gh_scm2double (ly_car (extra));
527     }
528   
529   extra = get_grob_property (a == X_AXIS
530                                 ? "minimum-extent-X"
531                                 : "minimum-extent-Y");
532   if (gh_pair_p (extra))
533     {
534       ext.unite (Interval (gh_scm2double (ly_car (extra)),
535                            gh_scm2double (ly_cdr (extra))));
536     }
537
538   ext.translate (x);
539   
540   return ext;
541 }
542
543 /*
544   Find the group-element which has both #this# and #s#
545 */
546 Grob * 
547 Grob::common_refpoint (Grob const* s, Axis a) const
548 {
549   /*
550     I don't like the quadratic aspect of this code, but I see no other
551     way. The largest chain of parents might be 10 high or so, so
552     it shouldn't be a real issue. */
553   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_l_)
554     for (Grob const * d = s; d; d = d->dim_cache_[a].parent_l_)
555       if (d == c)
556         return (Grob*)d;
557
558   return 0;
559 }
560
561
562 Grob *
563 common_refpoint_of_list (SCM elist, Grob *common, Axis a) 
564 {
565   for (; gh_pair_p (elist); elist = ly_cdr (elist))
566     {
567       Grob * s = unsmob_grob (ly_car (elist));
568       if (!s)
569         continue;
570       if (common)
571         common = common->common_refpoint (s, a);
572       else
573         common = s;
574     }
575
576   return common;
577 }
578
579
580
581 Grob *
582 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a) 
583 {
584   for (int i = arr.size() ; i--; )
585     {
586       Grob * s = arr[i];
587       if (!s)
588         continue;
589
590       if (common)
591         common = common->common_refpoint (s, a);
592       else
593         common = s;
594     }
595
596   return common;
597 }
598
599 String
600 Grob::name () const
601 {
602   SCM meta = get_grob_property ("meta");
603   SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
604   nm = (gh_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
605   return  gh_symbol_p (nm) ? ly_symbol2string (nm) :  classname (this);  
606 }
607
608 void
609 Grob::add_offset_callback (SCM cb, Axis a)
610 {
611   if (!has_offset_callback_b (cb, a))
612   {
613     dim_cache_[a].offset_callbacks_ = gh_cons (cb, dim_cache_[a].offset_callbacks_);
614     dim_cache_[a].offsets_left_ ++;
615   }
616 }
617
618 bool
619 Grob::has_extent_callback_b (SCM cb, Axis a)const
620 {
621   return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
622 }
623
624
625 bool
626 Grob::has_offset_callback_b (SCM cb, Axis a)const
627 {
628   return scm_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
629 }
630
631 void
632 Grob::set_extent (SCM dc, Axis a)
633 {
634   dim_cache_[a].dimension_ =dc;
635 }
636
637 void
638 Grob::set_parent (Grob *g, Axis a)
639 {
640   dim_cache_[a].parent_l_ = g;
641 }
642
643 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
644 SCM
645 Grob::fixup_refpoint (SCM smob)
646 {
647   Grob *me = unsmob_grob (smob);
648   for (int a = X_AXIS; a < NO_AXES; a ++)
649     {
650       Axis ax = (Axis)a;
651       Grob * parent = me->get_parent (ax);
652
653       if (!parent)
654         continue;
655       
656       if (parent->line_l () != me->line_l () && me->line_l ())
657         {
658           Grob * newparent = parent->find_broken_piece (me->line_l ());
659           me->set_parent (newparent, ax);
660         }
661
662       if (Item * i  = dynamic_cast<Item*> (me))
663         {
664           Item *parenti = dynamic_cast<Item*> (parent);
665
666           if (parenti && i)
667             {
668               Direction  my_dir = i->break_status_dir () ;
669               if (my_dir!= parenti->break_status_dir ())
670                 {
671                   Item *newparent =  parenti->find_prebroken_piece (my_dir);
672                   me->set_parent (newparent, ax);
673                 }
674             }
675         }
676     }
677   return smob;
678 }
679
680 void
681 Grob::warning (String s)const
682 {
683   SCM cause = self_scm();
684   while (cause != SCM_EOL && !unsmob_music (cause))
685     {
686       Grob * g = unsmob_grob (cause);
687       cause = g->get_grob_property ("cause");
688     }
689
690   if (Music *m = unsmob_music (cause))
691     {
692       m->origin()->warning (s);
693     }
694   else
695     ::warning (s);
696 }
697
698 void
699 Grob::programming_error (String s)const
700 {
701   s = "Programming error: "  + s;
702   warning (s);
703 }
704
705
706 /****************************************************
707   SMOB funcs
708  ****************************************************/
709
710
711
712 IMPLEMENT_SMOBS (Grob);
713 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
714
715 SCM
716 Grob::mark_smob (SCM ses)
717 {
718   Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
719   scm_gc_mark (s->immutable_property_alist_);
720
721   for (int a =0 ; a < 2; a++)
722     {
723       scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
724       scm_gc_mark (s->dim_cache_[a].dimension_);
725       
726       /*
727         don't mark the parents. The pointers in the mutable property
728         list form two tree like structures (one for X relations, one
729         for Y relations). Marking these can be done in limited stack
730         space.  If we add the parents, we will jump between X and Y in
731         an erratic manner, leading to much more recursion depth (and
732         core dumps if we link to pthreads.)
733        */
734     }
735   
736   if (s->original_l_)
737     scm_gc_mark (s->original_l_->self_scm ());
738
739   s->do_derived_mark ();  
740   return s->mutable_property_alist_;
741 }
742
743 int
744 Grob::print_smob (SCM s, SCM port, scm_print_state *)
745 {
746   Grob *sc = (Grob *) ly_cdr (s);
747      
748   scm_puts ("#<Grob ", port);
749   scm_puts ((char *)sc->name ().ch_C (), port);
750
751   /*
752     don't try to print properties, that is too much hassle.
753    */
754   scm_puts (" >", port);
755   return 1;
756 }
757
758 SCM
759 Grob::do_derived_mark ()
760 {
761   return SCM_EOL;
762 }
763
764
765
766 void
767 Grob::discretionary_processing ()
768 {
769 }
770
771 bool
772 Grob::internal_has_interface (SCM k)
773 {
774   SCM ifs = get_grob_property ("interfaces");
775
776   return scm_memq (k, ifs) != SCM_BOOL_F;
777 }
778
779 IMPLEMENT_TYPE_P (Grob, "ly-grob?");
780
781 ADD_INTERFACE (Grob, "grob-interface",
782   "In music notation, lots of symbols are related in some way.  You can
783 think of music notation as a graph where nodes are formed by the
784 symbols, and the arcs by their relations. A grob is a node in that
785 graph.  The directed edges in the graph are formed by references to
786 other grobs (i.e. pointers).  This big graph of grobs specifies the
787 notation problem. The solution of this problem is a description of the
788 printout in closed form, i.e. a list of values.  These values are
789 Molecules.
790
791 All grobs have an X and Y-position on the page.  These X and Y positions
792 are stored in a relative format, so they can easily be combined by
793 stacking them, hanging one grob to the side of another, and coupling
794 them into a grouping-grob.
795
796 Each grob has a reference point (a.k.a.  parent): the position of a grob
797 is stored relative to that reference point. For example the X-reference
798 point of a staccato dot usually is the note head that it applies
799 to. When the note head is moved, the staccato dot moves along
800 automatically.
801
802 A grob is often associated with a symbol, but some grobs do not print
803 any symbols. They take care of grouping objects. For example, there is a
804 separate grob that stacks staves vertically. The @ref{NoteCollision}
805 is also an abstract grob: it only moves around chords, but doesn't print
806 anything.
807 ",
808   "X-offset-callbacks Y-offset-callbacks X-extent-callback molecule cause
809 Y-extent-callback molecule-callback extra-offset
810 spacing-procedure
811 staff-symbol interfaces dependencies extra-extent-X causes meta
812 layer before-line-breaking-callback after-line-breaking-callback extra-extent-Y minimum-extent-X minimum-extent-Y transparent");
813
814