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