]> git.donarmstrong.com Git - lilypond.git/blob - lily/slur-bezier-bow.cc
8c1babb399cf1f9ac0419e6825bc9e78f8cba104
[lilypond.git] / lily / slur-bezier-bow.cc
1 /*
2   slur-bezier-bow.cc -- implement Slur_bezier_bow
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2000--2001  Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "debug.hh"
10 #include "paper-def.hh"
11 #include "slur-bezier-bow.hh"
12 #include "main.hh"
13
14 Slur_bezier_bow::Slur_bezier_bow (Array<Offset> encompass, Direction dir,
15                                   Real h_inf, Real r_0)
16 {
17   h_inf_ = h_inf;
18   r_0_ = r_0;
19   alpha_ = 0;
20   dir_ = dir;
21   encompass_ = encompass;
22   to_canonical_form ();
23
24   Real w = encompass_.top ()[X_AXIS] - encompass_[0][X_AXIS];
25   curve_ = slur_shape (w, h_inf, r_0);
26 }
27
28 Bezier
29 Slur_bezier_bow::get_bezier () const
30 {
31   Bezier rv = curve_;
32   if (dir_ == DOWN)
33     {
34       rv.scale (1, -1);
35     }
36
37   rv.rotate (alpha_);
38   rv.translate (origin_);
39   
40   return rv;
41 }
42
43 void
44 Slur_bezier_bow::to_canonical_form ()
45 {
46   origin_ = encompass_[0];
47   translate (&encompass_, -origin_);
48
49   Offset delta = encompass_.top () - encompass_[0];
50   alpha_ = delta.arg ();
51
52   rotate (&encompass_, -alpha_);
53   if (dir_ == DOWN)
54     {
55       scale (&encompass_, 1, -1);
56     }
57
58   while (encompass_.size () > 1 && encompass_[1][X_AXIS] <= 0.0)
59     {
60       programming_error ("Degenerate bow: infinite steepness reqd");
61       encompass_.del (1);
62     }
63
64   Real l = encompass_.top ()[X_AXIS];
65   while (encompass_.size () > 1 && encompass_.top (1)[X_AXIS] >= l)
66     {
67       programming_error ("Degenerate bow: infinite steepness reqd");
68       encompass_.del (encompass_.size ()-2);
69     }
70 }
71
72
73
74 void
75 Slur_bezier_bow::blow_fit ()
76 {
77   Real len = curve_.control_[3][X_AXIS]; 
78   Real h = curve_.control_[1][Y_AXIS] * fit_factor () / len;
79   curve_.control_[1][Y_AXIS] = h * len;
80   curve_.control_[2][Y_AXIS] = h * len;  
81   curve_.assert_sanity ();
82 }
83
84
85 Real
86 Slur_bezier_bow::enclosed_area_f () const
87 {
88   Real a = 0;
89   for (int i=0; i < encompass_.size (); i++)
90     {
91       Interval x;
92       Interval y;
93       if (i == 0)
94         {
95           x = Interval (0, encompass_[1][X_AXIS] / 2);
96           y = Interval (0,
97                         curve_.get_other_coordinate (X_AXIS,
98                                                      encompass_[1][X_AXIS]
99                                                      / 2));
100         }
101       else if (i == encompass_.size () - 1)
102         {
103           x = Interval ((encompass_[i-1][X_AXIS] + encompass_[i][X_AXIS])/2, 
104                         encompass_[i][X_AXIS]);
105           y = Interval (0,
106  (curve_.get_other_coordinate (X_AXIS,
107  (x[MIN] + x[MAX]) / 2)));
108         }
109       else
110         {
111           x = Interval ((encompass_[i-1][X_AXIS] + encompass_[i][X_AXIS]) / 2, 
112  (encompass_[i][X_AXIS] + encompass_[i+1][X_AXIS]) / 2);
113           y = Interval (encompass_[i][Y_AXIS],
114  (curve_.get_other_coordinate (X_AXIS, x[MIN])
115                          + curve_.get_other_coordinate (X_AXIS,
116  (x[MIN] + x[MAX]) / 2)
117                          + curve_.get_other_coordinate (X_AXIS, x[MAX])) / 3);
118         }
119       
120       Real da = x.length () * y.length ();
121       a += da;
122     }
123   return a;
124 }
125
126 Array<Real>
127 Slur_bezier_bow::area_x_gradients_array (Real area)
128 {
129   Real len = curve_.control_[3][X_AXIS]; 
130   Real grow = len / 10.0;
131   Array<Real> da (2);
132   for (int i=0; i < 2; i++)
133     {
134       Real r = curve_.control_[i+1][X_AXIS];
135       curve_.control_[i+1][X_AXIS] += grow;
136       da[i] = (enclosed_area_f () - area) / grow;
137       curve_.control_[i+1][X_AXIS] = r; 
138     }
139   return da;
140 }
141
142 /*
143   ugh, should have another look, and use a regular optimization
144   algorithm, instead of this homebrew.
145 */
146 void
147 Slur_bezier_bow::minimise_enclosed_area (Real beauty,
148                                          SCM bezier_props)
149 {
150   Real length = curve_.control_[3][X_AXIS]; 
151   Real beautiful = beauty * length * slur_height (length, h_inf_, r_0_);
152
153
154   if (fit_factor () > 1.0)
155     blow_fit ();
156   
157   Real pct_c0 = gh_scm2double (ly_cdr (scm_assoc (ly_symbol2scm ("bezier-pct-c0"), bezier_props)));
158   Real pct_c3 = gh_scm2double (ly_cdr (scm_assoc (ly_symbol2scm ("bezier-pct-c3"), bezier_props)));
159   Real pct_in_max =  gh_scm2double (ly_cdr (scm_assoc (ly_symbol2scm ("bezier-pct-in-max"), bezier_props)));
160   Real pct_out_max = gh_scm2double (ly_cdr (scm_assoc (ly_symbol2scm ("bezier-pct-out-max"), bezier_props)));
161   Real steps =  gh_scm2double (ly_cdr (scm_assoc (ly_symbol2scm ("bezier-area-steps"),bezier_props)));
162
163   for (int i=0; i < steps; i++)
164     {
165       Real area = enclosed_area_f ();
166       if (area <= beautiful)
167         break;
168
169       Array<Real> da = area_x_gradients_array (area);
170
171       // urg
172       Real pct = pct_c0 + pct_c3 * length * length * length;
173       pct *= (steps - i) / steps;
174       if (da[0] > 0 || da[1] < 0)
175         pct = pct <? pct_out_max;
176       else
177         pct = pct <? pct_in_max;
178
179       Real u = (abs (curve_.control_[1][X_AXIS] / da[0])
180                 <? abs ((curve_.control_[3][X_AXIS]
181                          - curve_.control_[2][X_AXIS]) / da[1]));
182
183       curve_.control_[1][X_AXIS] -= da[0] * u * pct;
184       curve_.control_[2][X_AXIS] -= da[1] * u * pct;
185     }
186 }
187
188
189
190 /*
191   max (encompass.y / curve.y)
192   
193  */
194 Real
195 Slur_bezier_bow::fit_factor () const
196 {
197   Real x1 = encompass_[0][X_AXIS];
198   Real x2 = encompass_.top ()[X_AXIS];
199
200   Real factor = 0.0;
201   for (int i=1; i < encompass_.size ()-1; i++)
202     {
203       if (encompass_[i][X_AXIS] > x1 && encompass_[i][X_AXIS] < x2)
204         {
205          Real y = curve_.get_other_coordinate (X_AXIS, encompass_[i][X_AXIS]);
206          if (y>0)
207            {
208              Real f = encompass_[i][Y_AXIS] / y;
209              factor = factor >? f;
210            }
211         }
212     }
213
214
215   return factor;
216 }
217
218
219
220