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