]> git.donarmstrong.com Git - lilypond.git/commitdiff
lilypond-0.0.76
authorfred <fred>
Sun, 24 Mar 2002 19:48:48 +0000 (19:48 +0000)
committerfred <fred>
Sun, 24 Mar 2002 19:48:48 +0000 (19:48 +0000)
COPYING
init/engraver.ini
lily/include/lyric-engraver.hh
lily/lyric-grav.cc [new file with mode: 0644]
lily/score-grav.cc
lily/spring-spacer.cc [new file with mode: 0644]

diff --git a/COPYING b/COPYING
index 54b93e6973e7ff749dbf4e46a5d115e9eba90009..da816022099e25d775851c80c863264f4972e891 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -1,10 +1,7 @@
 This license applies to all files except:
 
        - the included input files which explicitly state a different
-copyright
-
-       - the MetaFont sources, found in the subdirectory mf/. See
-mf/README.
+copyright policy
 
 
                    GNU GENERAL PUBLIC LICENSE
index fac0e15c060cdb717c2ed758f2c3b4b81a0e18c9..4d73ecf419cdc7e01519e49d72a90325d2f0eeb2 100644 (file)
@@ -2,7 +2,68 @@
 % setup for Request->Element conversion. Guru-only
 %
 
-\requesttranslator {
+staff_engraver = \requesttranslator {
+                 Engraver "Engraver_group_engraver"
+                 \alias "Staff";
+                 \consists "Line_group_engraver";
+                 \consists "Bar_engraver";
+                 \consists "Clef_engraver";
+                 \consists "Key_engraver";
+                 \consists "Meter_engraver";
+                 \consists "Local_key_engraver";
+                 \consists "Staff_sym_engraver";
+                 \consists "Collision_engraver";
+                 \consists "Rest_collision_engraver";
+                 \contains \requesttranslator {
+                         Engraver  "Voice_group_engravers"
+                         \alias "Voice_group";
+                         \consists "Dynamic_engraver";
+                         \consists "Stem_beam_engraver";
+                         \consists "Script_engraver";
+                         \consists "Note_column_engraver";
+                         \consists "Slur_engraver";
+                         \contains \requesttranslator {
+                                 Engraver "Engraver_group_engraver"
+                                 \alias "Voice";
+                                 \consists "Note_head_engraver" ;
+                                 \consists "Tie_engraver";
+                         }
+               }
+            }
+
+piano_staff_engraver = \requesttranslator {
+       Engraver "Engraver_group_engraver"
+       \alias "Piano";
+       \alias "Hoenoemjedat";
+       \consists "Span_bar_engraver";
+       \consists "Vertical_align_engraver";
+       \consists "Line_group_engraver";
+       \consists "Piano_bar_engraver";
+       \contains\requesttranslator { \staff_engraver }
+}
+
+staff_group_engraver = \requesttranslator {
+       Engraver "Engraver_group_engraver"
+       \alias "Staff_group";
+       \consists "Span_bar_engraver";
+       \consists "Vertical_align_engraver";
+       \consists "Line_group_engraver";
+       \contains\requesttranslator { \staff_engraver }
+}
+lyric_engraver = \requesttranslator {
+       Engraver "Engraver_group_engraver"
+       \alias "Lyric";
+
+       \contains\requesttranslator{
+               Engraver "Engraver_group_engraver"
+               \consists "Lyric_engraver";
+               \consists "Line_group_engraver";
+               \consists "Swallow_engraver";
+       }
+       \consists "Vertical_align_engraver";
+}
+
+orchestral_score_translator = \requesttranslator {
        Engraver Score_engraver
        \alias "Score";
 
        \consists "Key_align_engraver";
        \consists "Meter_align_engraver";
        \consists "Score_horizontal_align_engraver";
-
        \consists "Vertical_align_engraver";
-       \consists "Span_bar_engraver";
+       \consists "Span_score_bar_engraver";
 
-       \contains\requesttranslator {
-               Engraver "Staff_engravers"
-               \alias "Staff";         
-               \consists "Bar_engraver";
-               \consists "Clef_engraver";
-               \consists "Key_engraver";
-               \consists "Meter_engraver";
-               \consists "Local_key_engraver";
-               \consists "Staff_sym_engraver";
-               \consists "Collision_engraver";
-               \consists "Rest_collision_engraver";
-               \contains \requesttranslator {
-                       Engraver  "Voice_group_engravers"
-                       \alias "Voice_group";
-                       \consists "Dynamic_engraver";
-                       \consists "Stem_beam_engraver";
-                       \consists "Script_engraver";
-                       \consists "Note_column_engraver";
-                       \consists "Slur_engraver";
-                       \contains \requesttranslator {
-                               Engraver "Engraver_group_engraver"
-                               \alias "Voice";
-                               \consists "Note_head_engraver" ;
-                               \consists "Tie_engraver";
-                       }
-               }
-       }
+       \contains \requesttranslator { \staff_group_engraver }
+       \contains \requesttranslator { \lyric_engraver }
+       \contains \requesttranslator { \piano_staff_engraver }
 
-       \contains\requesttranslator {
-               Engraver "Staff_engravers"
-               \alias "Lyric";
-               \contains\requesttranslator{
-                       Engraver "Voice_group_engravers"
-                       \alias "Voice_group";
-                       \contains\requesttranslator{
-                               Engraver "Engraver_group_engraver"
-                               \consists "Lyric_engraver";
-                               \consists "Swallow_engraver";
-                       }
-               }
-       }
+       
 }
 
 
index 9c50d5c0682e36bb5d4c1223e0e60a534beaba8a..9c1aefadac0c9bc1aa30a88ee72ed1eab1addf9b 100644 (file)
 #include "lily-proto.hh"
 
 class Lyric_engraver : public Engraver {
-    Array<Lyric_req*> lreq_arr_;
+    Lyric_req* lreq_l_;
+    Text_item *lyric_item_p_;
+protected:
+    virtual void do_pre_move_processing();
     virtual bool do_try_request(Request*);
     virtual void do_process_requests();
     virtual void do_post_move_processing();
diff --git a/lily/lyric-grav.cc b/lily/lyric-grav.cc
new file mode 100644 (file)
index 0000000..08c8172
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+  lyric-engraver.cc -- implement Lyric_engraver
+
+  source file of the GNU LilyPond music typesetter
+
+  (c) 1997 Han-Wen Nienhuys <hanwen@stack.nl>
+*/
+
+#include "lyric-engraver.hh"
+#include "musical-request.hh"
+#include "text-item.hh"
+#include "paper-def.hh"
+#include "lookup.hh"
+
+Lyric_engraver::Lyric_engraver()
+{
+    lreq_l_ =0;
+    lyric_item_p_ =0;
+}
+
+bool
+Lyric_engraver::do_try_request(Request*r)
+{
+    Musical_req * m =r->musical();
+    if (!m || ! m->lreq_l()) 
+       return false;
+    lreq_l_ = m->lreq_l();
+
+    return true;
+}
+
+void
+Lyric_engraver::do_process_requests()
+{
+    if ( lreq_l_ ) {  
+       lyric_item_p_ =  new Text_item(lreq_l_->tdef_p_ );
+       lyric_item_p_->dir_i_ = -1;
+       lyric_item_p_->fat_b_ = true;
+       announce_element( Score_elem_info( lyric_item_p_, lreq_l_));
+    }
+}
+
+void
+Lyric_engraver::do_post_move_processing()
+{
+    lreq_l_ =0;
+}
+
+void
+Lyric_engraver::do_pre_move_processing()
+{
+    if ( lyric_item_p_ ){
+       typeset_element( lyric_item_p_);
+       lyric_item_p_ =0;
+    }
+}
+
+IMPLEMENT_STATIC_NAME(Lyric_engraver);
+IMPLEMENT_IS_TYPE_B1(Lyric_engraver,Engraver);
+ADD_THIS_ENGRAVER(Lyric_engraver);
index f837dba557e00f413b15ad71d6342bdbfa4b8c73..bcec7e71fb0faa44a4f660cc7f4595f9d8787743 100644 (file)
@@ -17,6 +17,7 @@
 #include "score.hh"
 #include "musical-request.hh"
 #include "score-column.hh"
+#include "command-request.hh"
 
 
 void
@@ -28,28 +29,29 @@ Score_engraver::set_score(Score *s)
 
 Score_engraver::Score_engraver()
 {
+    disallow_break_b_ = false;
     scoreline_l_ =0;
     command_column_l_ =0;
     musical_column_l_ =0;
+    breaks_i_ =0;
 }
 
  
 void
 Score_engraver::prepare(Moment w)
 {
-    Score_column* c1 = new Score_column(w);
-    Score_column* c2 = new Score_column(w);
+    command_column_l_ = new Score_column(w);
+    musical_column_l_ = new Score_column(w);
     
-    c1->musical_b_ = false;
-    c2->musical_b_ = true;
+    command_column_l_->musical_b_ = false;
+    musical_column_l_->musical_b_ = true;
     
-    score_l_->cols_.bottom().add(c1);
-    score_l_->cols_.bottom().add(c2);
-    set_cols(c1,c2);
-
-
+    score_l_->pscore_p_->add(command_column_l_);
+    score_l_->pscore_p_->add(musical_column_l_);
+    disallow_break_b_ = false;
     post_move_processing();
 }
+
 void
 Score_engraver::finish()
 {
@@ -65,13 +67,6 @@ Score_engraver::do_creation_processing()
     Engraver_group_engraver::do_creation_processing();
 }
 
-void
-Score_engraver::set_cols(Score_column*c1,Score_column*c2)
-{
-    command_column_l_ = c1;
-    musical_column_l_ = c2;
-}
-
 void
 Score_engraver::do_removal_processing()
 {
@@ -93,12 +88,9 @@ Score_engraver::process()
 void
 Score_engraver::announce_element(Score_elem_info info)
 {
+    announce_info_arr_.push(info);
     info.origin_grav_l_arr_.push(this);
-    if (info.elem_l_->name() == Bar::static_name()) {
-       get_staff_info().command_pcol_l()->set_breakable();
-    } 
         
-    announce_info_arr_.push(info);
 }
 void
 Score_engraver::do_announces()
@@ -118,7 +110,7 @@ Score_engraver::do_announces()
             */
            if (announce_info_arr_[i].req_l_) {
                Musical_req *m = announce_info_arr_[i].req_l_->musical();
-               if (m&&m->rhythmic()) {
+               if (m && m->rhythmic()) {
                    musical_column_l_->add_duration( m->duration());
                }
            }
@@ -172,6 +164,12 @@ Score_engraver::typeset_all()
 void
 Score_engraver::do_pre_move_processing()
 {
+    if ( !disallow_break_b_ ){ 
+       get_staff_info().command_pcol_l()->set_breakable();
+       breaks_i_ ++;
+       if ( ! (breaks_i_%8))
+           *mlog << "[" << breaks_i_ << "]" << flush;
+    }
     // this generates all items.
     Engraver_group_engraver::do_pre_move_processing();
     
@@ -204,6 +202,8 @@ Score_engraver::do_try_request(Request*r)
     for ( int i =0; !gotcha && i < nongroup_l_arr_.size() ; i++)
        gotcha = nongroup_l_arr_[i]->try_request(r);
   
+    if ( r->command() && r->command()->disallowbreak())
+           disallow_break_b_ = true;
     return gotcha;
 }
 
diff --git a/lily/spring-spacer.cc b/lily/spring-spacer.cc
new file mode 100644 (file)
index 0000000..0804161
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+  spring-spacer.cc -- implement Spring_spacer
+
+  source file of the GNU LilyPond music typesetter
+
+  (c) 1996,1997 Han-Wen Nienhuys <hanwen@stack.nl>
+*/
+
+
+#include <math.h>
+#include "spring-spacer.hh"
+#include "p-col.hh"
+#include "debug.hh"
+#include "qlp.hh"
+#include "unionfind.hh"
+#include "idealspacing.hh"
+#include "pointer.tcc"
+#include "score-column.hh"
+#include "paper-def.hh"
+#include "dimen.hh"
+#include "minterval.hh"
+
+Vector
+Spring_spacer::default_solution()const
+{
+       return try_initial_solution() ; 
+}
+
+Score_column*
+Spring_spacer::scol_l(int i) 
+{
+    return (Score_column*)cols[i].pcol_l_;
+}
+
+const Real COLFUDGE=1e-3;
+template class P<Real>;                // ugh.
+
+bool
+Spring_spacer::contains(PCol const *w)
+{
+    for (int i=0; i< cols.size(); i++)
+       if (cols[i].pcol_l_ == w)
+           return true;
+    return false;
+}
+
+
+void
+Spring_spacer::OK() const
+{
+#ifndef NDEBUG
+    for (int i = 1; i < cols.size(); i++)
+       assert(cols[i].rank_i_ > cols[i-1].rank_i_);
+    for (int i = 1; i < loose_col_arr_.size(); i++)
+       assert(loose_col_arr_[i].rank_i_ > loose_col_arr_[i-1].rank_i_);
+#endif 
+}
+
+/**
+  Make sure no unconnected columns happen. 
+ */
+void
+Spring_spacer::handle_loose_cols() 
+{
+    Union_find connected(cols.size());
+    Array<int> fixed;
+    for (PCursor<Idealspacing*> i(ideal_p_list_.top()); i.ok(); i++){
+       connected.connect(i->left_i_,i->right_i_);              
+    }
+    for (int i = 0; i < cols.size(); i++)
+       if (cols[i].fixed())
+           fixed.push(i);
+    for (int i=1; i < fixed.size(); i++)
+       connected.connect(fixed[i-1], fixed[i]);
+
+    for (int i = cols.size(); i--; ) {
+       if (! connected.equiv(fixed[0], i)) {
+           warning("unconnected column: " + String(i));
+           loosen_column(i);
+       }
+    }
+    OK();
+}
+
+
+/**
+  Guess a stupid position for loose columns.  Put loose columns at
+  regular distances from enclosing calced columns 
+  */
+void
+Spring_spacer::position_loose_cols(Vector &sol_vec)const
+{
+    if (!loose_col_arr_.size())
+       return ; 
+    assert(sol_vec.dim());
+    Array<bool> fix_b_arr;
+    fix_b_arr.set_size(cols.size() + loose_col_arr_.size());
+    Real utter_right_f=-INFTY;
+    Real utter_left_f =INFTY;
+    for (int i=0; i < loose_col_arr_.size(); i++) {
+       fix_b_arr[loose_col_arr_[i].rank_i_] = false;
+    }
+    for (int i=0; i < cols.size(); i++) {
+       int r= cols[i].rank_i_;
+       fix_b_arr[r] = true;
+       utter_right_f = utter_right_f >? sol_vec(i);
+       utter_left_f = utter_left_f <? sol_vec(i);
+    }
+    Vector v(fix_b_arr.size());
+    int j =0;
+    int k =0;
+    for (int i=0; i < v.dim(); i++) {
+       if (fix_b_arr[i]) {
+           assert(cols[j].rank_i_ == i);
+           v(i) = sol_vec(j++);
+       } else {
+           Real left_pos_f = 
+               (j>0) ?sol_vec(j-1) : utter_left_f;
+           Real right_pos_f = 
+               (j < sol_vec.dim()) ? sol_vec(j) : utter_right_f;
+           int left_rank = (j>0) ? cols[j-1].rank_i_ : 0;
+           int right_rank = (j<sol_vec.dim()) ? cols[j].rank_i_ : sol_vec.dim();
+
+           int d_r = right_rank - left_rank;
+           Colinfo loose=loose_col_arr_[k++];
+           int r = loose.rank_i_ ;
+           assert(r > left_rank && r < right_rank);
+
+           v(i) =  (r - left_rank)*left_pos_f/ d_r + 
+               (right_rank - r) *right_pos_f /d_r;
+       }
+    }
+    sol_vec = v;
+}
+bool
+Spring_spacer::check_constraints(Vector v) const 
+{
+    int dim=v.dim();
+    assert(dim == cols.size());
+    
+    for (int i=0; i < dim; i++) {
+
+       if (cols[i].fixed()&&
+           abs(cols[i].fixed_position() - v(i)) > COLFUDGE) 
+           return false;
+       
+       if (!i) 
+           continue;
+       
+       Real mindist=cols[i-1].minright()
+           +cols[i].minleft();
+
+       // ugh... compares
+       Real dif =v(i) - v(i-1)- mindist;
+       bool b = (dif > - COLFUDGE);
+       
+
+       if (!b)
+           return false;
+
+    }
+    return true;
+}
+
+bool
+Spring_spacer::check_feasible() const
+{
+    Vector sol(try_initial_solution());
+    return check_constraints(sol);     
+}
+
+/// generate a solution which obeys the min distances and fixed positions
+Vector
+Spring_spacer::try_initial_solution() const
+{
+    int dim=cols.size();
+    Vector initsol(dim);
+    for (int i=0; i < dim; i++) {
+       if (cols[i].fixed()) {
+           initsol(i)=cols[i].fixed_position();        
+
+           if (i > 0) {
+               Real r =initsol(i-1)  + cols[i-1].minright();
+               if (initsol(i) < r ) {
+                   warning("overriding fixed position");
+                   initsol(i) =r;
+               } 
+           }
+               
+       } else {
+           Real mindist=cols[i-1].minright()
+               +cols[i].minleft();
+           if (mindist < 0.0)
+               warning("Excentric column");
+           initsol(i)=initsol(i-1)+mindist;
+       }       
+    }
+
+    return initsol;
+}
+
+
+
+Vector
+Spring_spacer::find_initial_solution() const
+{
+    Vector v(try_initial_solution());     
+    assert(check_constraints(v));
+    return v;
+}
+
+// generate the matrices
+void
+Spring_spacer::make_matrices(Matrix &quad, Vector &lin, Real &c) const
+{
+    quad.fill(0);
+    lin.fill(0);
+    c = 0;
+    
+    for (PCursor<Idealspacing*> i(ideal_p_list_.top()); i.ok(); i++) {
+       int l = i->left_i_;
+       int r = i->right_i_;
+
+       quad(r,r) += i->hooke_f_;
+       quad(r,l) -= i->hooke_f_;
+       quad(l,r) -= i->hooke_f_;
+       quad(l,l) += i->hooke_f_;
+
+       lin(r) -= i->space_f_*i->hooke_f_;
+       lin(l) += i->space_f_*i->hooke_f_;
+
+       c += sqr(i->space_f_);
+    }
+}
+
+// put the constraints into the LP problem
+void
+Spring_spacer::make_constraints(Mixed_qp& lp) const
+{    
+    int dim=cols.size();
+    for (int j=0; j < dim; j++) {
+       Colinfo c=cols[j];
+       if (c.fixed()) {
+           lp.add_fixed_var(j,c.fixed_position());         
+       }
+       if (j > 0){
+           Vector c1(dim);
+           
+           c1(j)=1.0 ;
+           c1(j-1)=-1.0 ;
+           lp.add_inequality_cons(c1, cols[j-1].minright() +
+                                  cols[j].minleft());
+       }
+    }
+}
+
+Array<Real>
+Spring_spacer::solve() const
+{
+    assert(check_feasible());
+
+    Mixed_qp lp(cols.size());
+    make_matrices(lp.quad,lp.lin, lp.const_term);
+    make_constraints(lp);    
+    Vector start=find_initial_solution();    
+    Vector sol(lp.solve(start));
+    if (!check_constraints(sol)) {
+       WARN << "solution doesn't satisfy constraints.\n" ;
+    }
+    Real energy_f =lp.eval(sol);
+    position_loose_cols(sol);
+
+    Array<Real> posns(sol);
+
+    posns.push(energy_f);
+    return posns;
+}
+
+/**
+    add one column to the problem.
+*/    
+void
+Spring_spacer::add_column(PCol  *col, bool fixed, Real fixpos)
+{
+    Colinfo c(col,(fixed)? &fixpos :  0);
+    if (cols.size())
+       c.rank_i_ = cols.top().rank_i_+1;
+    else
+       c.rank_i_ = 0;
+    cols.push(c);
+}
+
+Array<PCol*>
+Spring_spacer::error_pcol_l_arr()const
+{
+    Array<PCol*> retval;
+    for (int i=0; i< cols.size(); i++)
+       if (cols[i].ugh_b_)
+           retval.push(cols[i].pcol_l_);
+    for (int i=0;  i < loose_col_arr_.size(); i++) {
+       retval.push(loose_col_arr_[i].pcol_l_);
+    }
+    return retval;
+}
+
+void
+Spring_spacer::loosen_column(int i)
+{
+    Colinfo c=cols.get(i);
+    for (PCursor<Idealspacing*> j(ideal_p_list_.top()); j.ok(); j++){
+       if (j->left_i_ == i|| j->right_i_ == i)
+           j.del();
+       else
+           j++;
+    }
+    c.ugh_b_ = true;
+    
+    int j=0;
+    for (; j < loose_col_arr_.size(); j++) {
+       if (loose_col_arr_[j].rank_i_ > c.rank_i_)
+           break;
+    }
+    loose_col_arr_.insert(c,j);
+}
+
+
+void
+Spring_spacer::print() const
+{
+#ifndef NPRINT
+    for (int i=0; i < cols.size(); i++) {
+       mtor << "col " << i<<' ';
+       cols[i].print();
+    }
+    for (PCursor<Idealspacing*> i(ideal_p_list_.top()); i.ok(); i++){
+       i->print();
+    }
+#endif
+    
+}
+
+
+void
+Spring_spacer::connect(int i, int j, Real d, Real h)
+{
+    Idealspacing * s = new Idealspacing;
+    s->left_i_ = i;
+    s->right_i_ = j;
+    s->space_f_ = d;
+    s->hooke_f_ = h;
+    
+    ideal_p_list_.bottom().add(s);
+}
+
+/**
+  walk through all durations in all Score_columns
+ */
+struct Durations_iter
+{
+    Spring_spacer * sp_l_;
+    int col_i_;
+    int d_i_;
+    
+    Durations_iter(Spring_spacer*);
+
+    Moment duration()const;
+    Moment when()const;
+    
+    bool ok()const;
+    void next();
+};
+
+Durations_iter::Durations_iter(Spring_spacer * s)
+{
+    col_i_ =0;
+    d_i_ =0;           // ugh
+    
+    sp_l_ = s;
+    if (! sp_l_->scol_l(col_i_)->durations.size() )
+       next();
+}
+
+Moment
+Durations_iter::duration() const 
+{
+    return sp_l_->scol_l(col_i_)->durations[d_i_];
+}
+
+bool
+Durations_iter::ok()const{
+    return col_i_ < sp_l_->cols.size();
+}
+
+Moment
+Durations_iter::when()const{
+    return sp_l_->scol_l(col_i_)->when();
+}
+
+void
+Durations_iter::next()
+{
+    d_i_ ++;
+    while ( col_i_ < sp_l_->cols.size() 
+           && d_i_ >= sp_l_->scol_l(col_i_)->durations.size()){
+       col_i_ ++;
+       d_i_ =0;
+    }
+}
+
+       
+/**
+  generate springs between columns.
+
+  UNDER DESTRUCTION
+  
+  TODO: This needs rethinking.  Spacing should take optical
+  effects into account, and should be local (measure wide)
+
+  The algorithm is taken from : 
+
+  John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
+  OSU-CISRC-10/87-TR35, Department of Computer and Information
+  Science, The Ohio State University, 1987.
+  
+  */
+void
+Spring_spacer::calc_idealspacing()
+{
+    for (int i=0; i < cols.size(); i++) 
+       scol_l(i)->preprocess();
+    
+    /* get the shortest running note at a time. */
+    Array<Moment> shortest_arr_;
+    {
+       Durations_iter d_iter(this);
+       for (int i=0; i < cols.size(); i++) {
+           Moment now = scol_l(i)->when();
+           while (  d_iter.ok() && now >= d_iter.when() ) {
+               if ( now < d_iter.when() + d_iter.duration())
+                   break;
+               d_iter.next();
+           }
+           if ( d_iter.ok() && now >= d_iter.when()) {
+               Durations_iter d2 = d_iter;
+               Moment shortest = INFTY;
+               while (d2.ok() && d2.when() <= now) {
+                   shortest = shortest <? d2.duration();
+                   d2.next();
+               }
+               shortest_arr_.push( shortest );
+           } else
+               shortest_arr_.push(0);
+       }
+       
+    }
+#ifndef NPRINT
+    mtor << "shortest:[ ";
+    for (int i=0; i < shortest_arr_.size(); i++)
+       mtor << shortest_arr_[i] << " ";
+    mtor << "]\n";
+#endif
+  
+    Array<Real> ideal_arr_;
+    Array<Real> hooke_arr_;    
+    for (int i=0; i < cols.size(); i++){
+       ideal_arr_.push(  -1.0);
+       hooke_arr_.push(1.0);
+    }
+    
+    for (int i=0; i < cols.size(); i++) {
+       if ( !scol_l(i)->musical_b()) {
+           ideal_arr_[i] = cols[i].minright() + 2 PT;
+           hooke_arr_[i] = 2.0;
+           if (i+1 < cols.size()) {
+               Moment delta_t =  scol_l(i+1)->when() - scol_l(i)->when() ;
+               Real dist = delta_t ? paper_l()->duration_to_dist(delta_t) : 0;
+               if (delta_t && dist > ideal_arr_[i])
+                   ideal_arr_[i] = dist;
+           }
+       }
+    }
+    for (int i=0; i < cols.size(); i++) {
+       if (scol_l(i)->musical_b()) {
+           Moment shortest_len = shortest_arr_[i];
+           if ( ! shortest_len ) {
+               warning( "Can't find a ruling note at " 
+                        +String( scol_l(i)->when()));
+               shortest_len = 1;
+           }
+           Moment delta_t = scol_l(i+1)->when() - scol_l(i)->when();
+           Real dist = paper_l()->duration_to_dist(shortest_len);
+           dist *= delta_t / shortest_len;
+           if (!scol_l(i+1)->musical_b() ) {
+
+               if (ideal_arr_[i+1] + cols[i+1].minleft() < dist) {
+                   ideal_arr_[i+1] = dist/2 + cols[i+1].minleft();
+                   hooke_arr_[i+1] =1.0;
+               } 
+               ideal_arr_[i] = dist/2;
+           } else
+               ideal_arr_[i] = dist;
+       }
+    }
+
+    for (int i=0; i < ideal_arr_.size()-1; i++) {
+       assert (ideal_arr_[i] >=0 && hooke_arr_[i] >=0);
+       connect(i, i+1, ideal_arr_[i], hooke_arr_[i]);
+    }
+}
+
+
+
+void
+Spring_spacer::prepare()
+{
+    calc_idealspacing();
+    handle_loose_cols();
+    print();
+}
+
+Line_spacer*
+Spring_spacer::constructor() 
+{
+    return new Spring_spacer;
+}
+   
+#if 0
+void obsolete()
+{
+    for (int i=0; i < cols.size(); i++) {
+       if (!scol_l(i)->used_b())
+           continue;
+       
+       
+       int j = i+1;
+
+       if (scol_l(i)->musical_b()) {
+           assert ( j < cols.size());
+           
+           for (int n=0; n < scol_l(i)->durations.size(); n++) {
+               Moment d = scol_l(i)->durations[n];
+               Real dist = paper_l()->duration_to_dist(d);
+               Real strength =  scol_l(i)->durations[0]/scol_l(i)->durations[n];
+               assert(strength <= 1.0);
+               
+               while (j < cols.size()) {
+                   if (scol_l(j)->used_b() 
+                       && scol_l(j)->when() >= d + scol_l(i)->when() )
+                       break;
+                   j++;
+               }
+               if ( j < cols.size() ){
+                   Moment delta_desired = scol_l(j)->when() - (d+scol_l(i)->when());
+                   dist += paper_l()->duration_to_dist(delta_desired);
+                   if (scol_l(j)->musical_b()) {
+                       dist += cols[j].minleft() + 2 PT;
+                   }
+                   connect(i, j, dist, strength);
+               }
+           }
+       } else if (j < cols.size()) {
+           while  (!scol_l(j)->used_b())
+               j++;
+           
+           /* attach i to the next column in use. This exists, since
+             the last col is breakable, and therefore in use
+             */
+           
+           Moment d = scol_l(j)->when() - scol_l(i)->when();
+           Real minimal_f = cols[i].minright()  +cols[j].minleft() + 2 PT;
+           Real durdist_f = (d) ? paper_l()->duration_to_dist(d) : 0; // todo
+           
+           connect(i, j, minimal_f <? durdist_f, (d) ? 1.0:1.0);
+       }
+       // !j.ok() might hold if we're at the last col.
+    }
+}
+#endif