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