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