]> git.donarmstrong.com Git - lilypond.git/blob - lily/score-element.cc
release: 1.3.73
[lilypond.git] / lily / score-element.cc
1 /*
2   score-elem.cc -- implement Score_element
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9
10 #include <string.h>
11 #include <math.h>
12
13 #include "input-smob.hh"
14 #include "libc-extension.hh"
15 #include "group-interface.hh"
16 #include "misc.hh"
17 #include "paper-score.hh"
18 #include "paper-def.hh"
19 #include "lookup.hh"
20 #include "molecule.hh"
21 #include "score-element.hh"
22 #include "debug.hh"
23 #include "spanner.hh"
24 #include "line-of-score.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 "dimension-cache.hh"
31 #include "side-position-interface.hh"
32 #include "item.hh"
33
34 /*
35 TODO:
36
37 remove dynamic_cast<Spanner,Item> and put this code into respective
38   subclass.
39 */
40
41
42 #define INFINITY_MSG "Infinity or NaN encountered"
43
44 Score_element::Score_element(SCM basicprops)
45 {
46   set_extent_callback (molecule_extent, X_AXIS);
47   set_extent_callback (molecule_extent, Y_AXIS);    
48
49   pscore_l_=0;
50   lookup_l_ =0;
51   status_i_ = 0;
52   self_scm_ = SCM_EOL;
53   original_l_ = 0;
54   immutable_property_alist_ =  basicprops;
55   mutable_property_alist_ = SCM_EOL;
56
57   smobify_self ();
58   set_elt_property ("dependencies", SCM_EOL);
59
60   if (get_elt_property ("interfaces") == SCM_UNDEFINED)
61     set_elt_property ("interfaces", SCM_EOL);
62 }
63
64
65 Score_element::Score_element (Score_element const&s)
66    : dim_cache_ (s.dim_cache_)
67 {
68   self_scm_ = SCM_EOL;
69   original_l_ =(Score_element*) &s;
70   immutable_property_alist_ = s.immutable_property_alist_;
71   mutable_property_alist_ = SCM_EOL;
72   
73   status_i_ = s.status_i_;
74   lookup_l_ = s.lookup_l_;
75   pscore_l_ = s.pscore_l_;
76
77   smobify_self ();
78 }
79
80 Score_element::~Score_element()
81 {
82   /*
83     do nothing scm-ish and no unprotecting here.
84    */
85 }
86
87
88 SCM
89 Score_element::get_elt_property (const char *nm) const
90 {
91   SCM sym = ly_symbol2scm (nm);
92   return get_elt_property (sym);
93 }
94
95 SCM
96 Score_element::get_elt_property (SCM sym) const
97 {
98   SCM s = scm_sloppy_assq(sym, mutable_property_alist_);
99   if (s != SCM_BOOL_F)
100     return gh_cdr (s);
101
102   s = scm_sloppy_assq (sym, immutable_property_alist_);
103   return (s == SCM_BOOL_F) ? SCM_EOL : gh_cdr (s); 
104 }
105
106 /*
107   Remove the value associated with KEY, and return it. The result is
108   that a next call will yield SCM_UNDEFINED (and not the underlying
109   `basic' property.
110 */
111 SCM
112 Score_element::remove_elt_property (const char* key)
113 {
114   SCM val = get_elt_property (key);
115   if (val != SCM_EOL)
116     set_elt_property (key, SCM_EOL);
117   return val;
118 }
119
120 void
121 Score_element::set_elt_property (const char* k, SCM v)
122 {
123   SCM s = ly_symbol2scm (k);
124   set_elt_property (s, v);
125 }
126
127 /*
128   Puts the k, v in the immutable_property_alist_, which is convenient for
129   storing variables that are needed during the breaking process. (eg.
130   Line_of_score::rank : int )
131  */
132 void
133 Score_element::set_immutable_elt_property (const char*k, SCM v)
134 {
135   SCM s = ly_symbol2scm (k);
136   set_immutable_elt_property (s, v);
137 }
138
139 void
140 Score_element::set_immutable_elt_property (SCM s, SCM v)
141 {
142   immutable_property_alist_ = gh_cons (gh_cons (s,v), mutable_property_alist_);
143   mutable_property_alist_ = scm_assq_remove_x (mutable_property_alist_, s);
144 }
145 void
146 Score_element::set_elt_property (SCM s, SCM v)
147 {
148   mutable_property_alist_ = scm_assq_set_x (mutable_property_alist_, s, v);
149 }
150
151
152 Interval
153 Score_element::molecule_extent (Score_element *s, Axis a)
154 {
155   Molecule m = s->get_molecule ();
156   return m.extent(a);
157 }
158
159 Interval
160 Score_element::preset_extent (Score_element  *s , Axis a)
161 {
162   SCM ext = s->get_elt_property ((a == X_AXIS)
163                                  ? "extent-X"
164                                  : "extent-Y");
165   
166   if (gh_pair_p (ext))
167     {
168       Real l = gh_scm2double (gh_car (ext));
169       Real r = gh_scm2double (gh_cdr (ext));
170       l *= s->paper_l ()->get_var ("staffspace");
171       r *= s->paper_l ()->get_var ("staffspace");
172       return Interval (l, r);
173     }
174   
175   return Interval ();
176 }
177
178
179
180 Paper_def*
181 Score_element::paper_l ()  const
182 {
183  return pscore_l_ ? pscore_l_->paper_l_ : 0;
184 }
185
186 Lookup const *
187 Score_element::lookup_l () const
188 {
189   if (!lookup_l_)
190     {
191       Score_element * urg = (Score_element*)this;
192       SCM sz = urg->remove_elt_property ("fontsize");
193       int i = (gh_number_p (sz))
194         ? gh_scm2int  (sz)
195         : 0;
196
197       urg->lookup_l_ =  (Lookup*)pscore_l_->paper_l_->lookup_l (i);
198     }
199   return lookup_l_;
200 }
201
202 void
203 Score_element::add_processing()
204 {
205   assert (status_i_ >=0);
206   if (status_i_)
207     return;
208   status_i_ ++;
209
210   do_add_processing();
211 }
212
213 void
214 Score_element::calculate_dependencies (int final, int busy, SCM funcname)
215 {
216   assert (status_i_ >=0);
217
218   if (status_i_ >= final)
219     return;
220
221   if (status_i_== busy)
222     {
223       programming_error ("Element is busy, come back later");
224       return;
225     }
226   
227   status_i_= busy;
228
229   for (SCM d=  get_elt_property ("dependencies"); gh_pair_p (d); d = gh_cdr (d))
230     {
231       unsmob_element (gh_car (d))
232         ->calculate_dependencies (final, busy, funcname);
233     }
234
235   // ughugh.
236   String s = ly_symbol2string (funcname);
237   SCM proc = get_elt_property (s.ch_C());
238   if (gh_procedure_p (proc))
239     gh_call1 (proc, this->self_scm_);
240   
241   status_i_= final;
242
243 }
244
245 Molecule
246 Score_element::get_molecule ()  const
247 {
248   SCM proc = get_elt_property ("molecule-callback");
249
250   SCM mol = SCM_EOL;
251   if (gh_procedure_p (proc)) 
252     mol = gh_apply (proc, gh_list (this->self_scm_, SCM_UNDEFINED));
253
254
255   SCM origin =get_elt_property ("origin");
256   if (!unsmob_input (origin))
257     origin =ly_symbol2scm ("no-origin");
258   
259   if (gh_pair_p (mol))
260     {
261       // ugr.
262         mol = gh_cons (gh_list (origin, gh_car (mol), SCM_UNDEFINED), gh_cdr (mol));
263     }
264
265   
266   return create_molecule (mol);
267 }
268
269
270 /*
271   
272   VIRTUAL STUBS
273
274  */
275 void
276 Score_element::do_break_processing()
277 {
278 }
279
280
281 void
282 Score_element::do_add_processing()
283 {
284 }
285
286
287 MAKE_SCHEME_CALLBACK(Score_element,brew_molecule)
288
289 /*
290   ugh.
291  */  
292 SCM
293 Score_element::brew_molecule (SCM smob) 
294 {
295   Score_element * sc = unsmob_element (smob);
296   SCM glyph = sc->get_elt_property ("glyph");
297   if (gh_string_p (glyph))
298     {
299       return sc->lookup_l ()->afm_find (String (ly_scm2string (glyph))).create_scheme ();
300     }
301   else
302     {
303       return SCM_EOL;
304     }
305 }
306
307
308 Line_of_score *
309 Score_element::line_l() const
310 {
311   return 0;
312 }
313
314 void
315 Score_element::add_dependency (Score_element*e)
316 {
317   if (e)
318     {
319       Pointer_group_interface gi (this, "dependencies");
320       gi.add_element (e);
321     }
322   else
323     programming_error ("Null dependency added");
324 }
325
326
327
328
329 /**
330       Do break substitution in S, using CRITERION. Return new value.
331       CRITERION is either a SMOB pointer to the desired line, or a number
332       representing the break direction. Do not modify SRC.
333 */
334 SCM
335 Score_element::handle_broken_smobs (SCM src, SCM criterion)
336 {
337  again:
338   Score_element *sc = unsmob_element (src);
339   if (sc)
340     {
341       if (gh_number_p (criterion))
342         {
343           Item * i = dynamic_cast<Item*> (sc);
344           Direction d = to_dir (criterion);
345           if (i && i->break_status_dir () != d)
346             {
347               Item *br = i->find_prebroken_piece (d);
348               return  (br) ? br->self_scm_ : SCM_UNDEFINED;
349             }
350         }
351       else
352         {
353           Line_of_score * line
354             = dynamic_cast<Line_of_score*> (unsmob_element (criterion));
355           if (sc->line_l () != line)
356             {
357               sc = sc->find_broken_piece (line);
358
359             }
360
361           /* now: !sc || (sc && sc->line_l () == line) */
362           if (!sc)
363             return SCM_UNDEFINED;
364
365           /* now: sc && sc->line_l () == line */
366           if (!line
367               || (sc->common_refpoint (line, X_AXIS)
368                   && sc->common_refpoint (line, Y_AXIS)))
369             {
370               return sc->self_scm_;
371             }
372           return SCM_UNDEFINED;
373         }
374     }
375   else if (gh_pair_p (src))
376     {
377       SCM oldcar =gh_car (src);
378       /*
379         UGH! breaks on circular lists.
380       */
381       SCM newcar = handle_broken_smobs (oldcar, criterion);
382       SCM oldcdr = gh_cdr (src);
383       
384       if (newcar == SCM_UNDEFINED
385           && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
386         {
387           /*
388             This is tail-recursion, ie. 
389             
390             return handle_broken_smobs (cdr, criterion);
391
392             We don't want to rely on the compiler to do this.  Without
393             tail-recursion, this easily crashes with a stack overflow.  */
394           src =  oldcdr;
395           goto again;
396         }
397
398       SCM newcdr = handle_broken_smobs (oldcdr, criterion);
399       return gh_cons (newcar, newcdr);
400     }
401   else
402     return src;
403
404   return src;
405 }
406
407 void
408 Score_element::handle_broken_dependencies()
409 {
410   Spanner * s= dynamic_cast<Spanner*> (this);
411   if (original_l_ && s)
412     return;
413
414   if (s)
415     {
416       for (int i = 0;  i< s->broken_into_l_arr_ .size (); i++)
417         {
418           Score_element * sc = s->broken_into_l_arr_[i];
419           Line_of_score * l = sc->line_l ();
420           sc->mutable_property_alist_ =
421             handle_broken_smobs (mutable_property_alist_,
422                                  l ? l->self_scm_ : SCM_UNDEFINED);
423         }
424     }
425
426
427   Line_of_score *line = line_l();
428
429   if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
430     {
431       mutable_property_alist_
432         = handle_broken_smobs (mutable_property_alist_,
433                                line ? line->self_scm_ : SCM_UNDEFINED);
434     }
435   else if (dynamic_cast <Line_of_score*> (this))
436     {
437       mutable_property_alist_ = handle_broken_smobs (mutable_property_alist_,
438                                             SCM_UNDEFINED);
439     }
440   else
441     {
442       /*
443         This element is `invalid'; it has been removed from all
444         dependencies, so let's junk the element itself.
445
446         do not do this for Line_of_score, since that would remove
447         references to the originals of score-elts, which get then GC'd
448         (a bad thing.)
449       */
450       suicide();
451     }
452 }
453
454 /*
455  Note that we still want references to this element to be
456  rearranged, and not silently thrown away, so we keep pointers
457  like {broken_into_{drul,array}, original}
458 */
459 void
460 Score_element::suicide ()
461 {
462   mutable_property_alist_ = SCM_EOL;
463   immutable_property_alist_ = SCM_EOL;
464   set_extent_callback (0, Y_AXIS);
465   set_extent_callback (0, X_AXIS);
466
467   for (int a= X_AXIS; a <= Y_AXIS; a++)
468     {
469       dim_cache_[a].off_callbacks_.clear ();
470     }
471 }
472
473 void
474 Score_element::handle_prebroken_dependencies()
475 {
476 }
477
478 Score_element*
479 Score_element::find_broken_piece (Line_of_score*) const
480 {
481   return 0;
482 }
483
484 void
485 Score_element::translate_axis (Real y, Axis a)
486 {
487   if (isinf (y) || isnan (y))
488     programming_error (_(INFINITY_MSG));
489   else
490     {
491       dim_cache_[a].offset_ += y;
492     }
493 }  
494
495 Real
496 Score_element::relative_coordinate (Score_element const*refp, Axis a) const
497 {
498   if (refp == this)
499     return 0.0;
500
501   /*
502     We catch PARENT_L_ == nil case with this, but we crash if we did
503     not ask for the absolute coordinate (ie. REFP == nil.)
504     
505    */
506   if (refp == dim_cache_[a].parent_l_)
507     return get_offset (a);
508   else
509     return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
510 }
511
512 Real
513 Score_element::get_offset (Axis a) const
514 {
515   Score_element *me = (Score_element*) this;
516   while (dim_cache_[a].off_callbacks_.size ())
517     {
518       Offset_callback c = dim_cache_[a].off_callbacks_[0];
519       me->dim_cache_[a].off_callbacks_.del (0);
520       Real r =  (*c) (me,a );
521       if (isinf (r) || isnan (r))
522         {
523           r = 0.0;
524           programming_error (INFINITY_MSG);
525         }
526       me->dim_cache_[a].offset_ +=r;
527     }
528   return dim_cache_[a].offset_;
529 }
530
531
532 Interval
533 Score_element::point_dimension_callback (Score_element* , Axis)
534 {
535   return Interval (0,0);
536 }
537
538 bool
539 Score_element::empty_b (Axis a)const
540 {
541   return !dim_cache_[a].extent_callback_l_;
542 }
543
544 Interval
545 Score_element::extent (Axis a) const
546 {
547   Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
548   if (!d->extent_callback_l_)
549     {
550       d->dim_.set_empty ();
551     }
552   else if (!d->valid_b_)
553     {
554       d->dim_= (*d->extent_callback_l_ ) ((Score_element*)this, a);
555       d->valid_b_ = true;
556     }
557
558   Interval ext = d->dim_;
559   
560   if (empty_b (a)) 
561     return ext;
562
563   SCM extra = get_elt_property (a == X_AXIS
564                                 ? "extra-extent-X"
565                                 : "extra-extent-Y");
566
567   /*
568     signs ?
569    */
570   Real s = paper_l ()->get_var ("staffspace");
571   if (gh_pair_p (extra))
572     {
573       ext[BIGGER] +=  s * gh_scm2double (gh_cdr (extra));
574       ext[SMALLER] +=  s * gh_scm2double (gh_car (extra));
575     }
576   
577   extra = get_elt_property (a == X_AXIS
578                                 ? "minimum-extent-X"
579                                 : "minimum-extent-Y");
580   if (gh_pair_p (extra))
581     {
582       ext.unite (Interval (s * gh_scm2double (gh_car (extra)),
583                            s * gh_scm2double (gh_cdr (extra))));
584     }
585   
586   return ext;
587 }
588
589
590 Score_element*
591 Score_element::parent_l (Axis a) const
592 {
593   return  dim_cache_[a].parent_l_;
594 }
595
596 Score_element * 
597 Score_element::common_refpoint (Score_element const* s, Axis a) const
598 {
599   /*
600     I don't like the quadratic aspect of this code, but I see no other
601     way. The largest chain of parents might be 10 high or so, so
602     it shouldn't be a real issue. */
603   for (Score_element const *c = this; c; c = c->dim_cache_[a].parent_l_)
604     for (Score_element const * d = s; d; d = d->dim_cache_[a].parent_l_)
605       if (d == c)
606         return (Score_element*)d;
607
608   return 0;
609 }
610
611
612 Score_element *
613 Score_element::common_refpoint (SCM elist, Axis a) const
614 {
615   Score_element * common = (Score_element*) this;
616   for (; gh_pair_p (elist); elist = gh_cdr (elist))
617     {
618       Score_element * s = unsmob_element (gh_car (elist));
619       if (s)
620         common = common->common_refpoint (s, a);
621     }
622
623   return common;
624 }
625
626 char const *
627 Score_element::name () const
628 {
629   return classname (this);
630 }
631
632 void
633 Score_element::add_offset_callback (Offset_callback cb, Axis a)
634 {
635   dim_cache_[a].off_callbacks_.push (cb);
636 }
637
638 bool
639 Score_element::has_extent_callback_b (Extent_callback cb, Axis a)const
640 {
641   return cb == dim_cache_[a].extent_callback_l_;
642 }
643
644 bool
645 Score_element::has_offset_callback_b (Offset_callback cb, Axis a)const
646 {
647   for (int i= dim_cache_[a].off_callbacks_.size (); i--;)
648     {
649       if (dim_cache_[a].off_callbacks_[i] == cb)
650         return true;
651     }
652   return false;
653 }
654
655 void
656 Score_element::set_extent_callback (Dim_cache_callback dc, Axis a)
657 {
658   dim_cache_[a].extent_callback_l_ = dc ;
659 }
660
661                                     
662 void
663 Score_element::set_parent (Score_element *g, Axis a)
664 {
665   dim_cache_[a].parent_l_ = g;
666 }
667
668 MAKE_SCHEME_CALLBACK(Score_element,fixup_refpoint);
669 SCM
670 Score_element::fixup_refpoint (SCM smob)
671 {
672   Score_element *me = unsmob_element (smob);
673   for (int a = X_AXIS; a < NO_AXES; a ++)
674     {
675       Axis ax = (Axis)a;
676       Score_element * parent = me->parent_l (ax);
677
678       if (!parent)
679         continue;
680       
681       if (parent->line_l () != me->line_l () && me->line_l ())
682         {
683           Score_element * newparent = parent->find_broken_piece (me->line_l ());
684           me->set_parent (newparent, ax);
685         }
686
687       if (Item * i  = dynamic_cast<Item*> (me))
688         {
689           Item *parenti = dynamic_cast<Item*> (parent);
690
691           if (parenti && i)
692             {
693               Direction  my_dir = i->break_status_dir () ;
694               if (my_dir!= parenti->break_status_dir())
695                 {
696                   Item *newparent =  parenti->find_prebroken_piece (my_dir);
697                   me->set_parent (newparent, ax);
698                 }
699             }
700         }
701     }
702   return smob;
703 }
704
705
706
707 /****************************************************
708   SMOB funcs
709  ****************************************************/
710
711 #include "ly-smobs.icc"
712
713 IMPLEMENT_UNSMOB(Score_element, element);
714 IMPLEMENT_SMOBS(Score_element);
715
716 SCM
717 Score_element::mark_smob (SCM ses)
718 {
719   Score_element * s = SMOB_TO_TYPE (Score_element, ses);
720   if (s->self_scm_ != ses)
721     {
722       programming_error ("SMOB marking gone awry");
723       return SCM_EOL;
724     }
725   scm_gc_mark (s->immutable_property_alist_);
726   scm_gc_mark (s->mutable_property_alist_);
727   
728   if (s->parent_l (Y_AXIS))
729     scm_gc_mark (s->parent_l (Y_AXIS)->self_scm_);
730   if (s->parent_l (X_AXIS))
731     scm_gc_mark (s->parent_l (X_AXIS)->self_scm_);
732
733   if (s->original_l_)
734     scm_gc_mark (s->original_l_->self_scm_);
735   return s->do_derived_mark ();
736 }
737
738 int
739 Score_element::print_smob (SCM s, SCM port, scm_print_state *)
740 {
741   Score_element *sc = (Score_element *) gh_cdr (s);
742      
743   scm_puts ("#<Score_element ", port);
744   scm_puts ((char *)sc->name (), port);
745
746   /*
747     don't try to print properties, that is too much hassle.
748    */
749   scm_puts (" >", port);
750   return 1;
751 }
752
753 SCM
754 Score_element::do_derived_mark ()
755 {
756   return SCM_EOL;
757 }
758
759 void
760 Score_element::do_smobify_self ()
761 {
762 }
763
764 SCM
765 Score_element::equal_p (SCM a, SCM b)
766 {
767   return gh_cdr(a) == gh_cdr(b) ? SCM_BOOL_T : SCM_BOOL_F;
768 }
769
770
771 SCM
772 ly_set_elt_property (SCM elt, SCM sym, SCM val)
773 {
774   Score_element * sc = unsmob_element (elt);
775
776   if (!gh_symbol_p (sym))
777     {
778       error ("Not a symbol");
779       ly_display_scm (sym);
780       return SCM_UNSPECIFIED;
781     }
782
783   if (sc)
784     {
785       sc->set_elt_property (sym, val);
786     }
787   else
788     {
789       error ("Not a score element");
790       ly_display_scm (elt);
791     }
792
793   return SCM_UNSPECIFIED;
794 }
795
796
797 SCM
798 ly_get_elt_property (SCM elt, SCM sym)
799 {
800   Score_element * sc = unsmob_element (elt);
801   
802   if (sc)
803     {
804       return sc->get_elt_property (sym);
805     }
806   else
807     {
808       error ("Not a score element");
809       ly_display_scm (elt);
810     }
811   return SCM_UNSPECIFIED;
812 }
813
814
815 void
816 Score_element::discretionary_processing()
817 {
818 }
819
820
821
822 SCM
823 spanner_get_bound (SCM slur, SCM dir)
824 {
825   return dynamic_cast<Spanner*> (unsmob_element (slur))->get_bound (to_dir (dir))->self_scm_;
826 }
827
828
829
830 static SCM interfaces_sym;
831
832 static void
833 init_functions ()
834 {
835   interfaces_sym = scm_permanent_object (ly_symbol2scm ("interfaces"));
836   
837   scm_make_gsubr ("ly-get-elt-property", 2, 0, 0, (SCM(*)(...))ly_get_elt_property);
838   scm_make_gsubr ("ly-set-elt-property", 3, 0, 0, (SCM(*)(...))ly_set_elt_property);
839   scm_make_gsubr ("ly-get-spanner-bound", 2 , 0, 0, (SCM(*)(...)) spanner_get_bound);
840 }
841
842 bool
843 Score_element::has_interface (SCM k)
844 {
845   if (mutable_property_alist_ == SCM_EOL)
846     return false;
847   
848   SCM ifs = get_elt_property (interfaces_sym);
849
850   return scm_memq (k, ifs) != SCM_BOOL_F;
851 }
852
853 void
854 Score_element::set_interface (SCM k)
855 {
856   if (has_interface (k))
857     return ;
858   else
859     {
860       set_elt_property (interfaces_sym,
861                         gh_cons  (k, get_elt_property (interfaces_sym)));
862     }
863 }
864
865
866 ADD_SCM_INIT_FUNC(scoreelt, init_functions);
867