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