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