2 bezier.cc -- implement Bezier and Bezier_bow
4 source file of the GNU LilyPond music typesetter
6 (c) 1998--2000 Jan Nieuwenhuizen <janneke@gnu.org>
10 #include "bezier-bow.hh"
13 #include "dimensions.hh"
14 #include "direction.hh"
17 #include "lily-guile.hh"
20 flipy (Array
<Offset
> &c
)
22 for (int i
= c
.size (); i
--;)
23 c
[i
][Y_AXIS
] = - c
[i
][Y_AXIS
];
27 rotate (Array
<Offset
> &c
, Real phi
)
29 Offset
rot (complex_exp (Offset (0, phi
)));
30 for (int i
= 0; i
< c
.size (); i
++)
31 c
[i
] = complex_multiply (rot
, c
[i
]);
35 translate (Array
<Offset
> &c
, Offset o
)
37 for (int i
= 0; i
< c
.size (); i
++)
42 Bezier_bow::Bezier_bow (Array
<Offset
> points
, Direction dir
)
54 default_height (Real len
)
58 Real staff_space
= 5.0;
59 Real h_inf
= 2.0* staff_space
;
61 return h_inf
* 2.0 / M_PI
* atan ( M_PI
* r_0
/ (2.0 * h_inf
) * len
);
65 Bezier_bow::blow_fit ()
67 Real len
= curve_
.control_
[3][X_AXIS
];
68 Real h
= curve_
.control_
[1][Y_AXIS
] * fit_factor () / len
;
69 curve_
.control_
[1][Y_AXIS
] = h
* len
;
70 curve_
.control_
[2][Y_AXIS
] = h
* len
;
71 curve_
.check_sanity ();
75 Bezier_bow::de_uglyfy ()
77 Real len
= curve_
.control_
[3][X_AXIS
] ;
78 Real ff
= fit_factor ();
79 for (int i
= 1; i
< 3; i
++)
81 Real ind
= abs (curve_
.control_
[(i
-1)*3][X_AXIS
]
82 - curve_
.control_
[i
][X_AXIS
]) / len
;
83 Real h
= curve_
.control_
[i
][Y_AXIS
] * ff
/ len
;
85 // ugh. Unhardcode this
93 Real f
= default_height (len
) / len
;
100 if (h
> 0.8 + -2 * ind
)
105 curve_
.control_
[i
][Y_AXIS
] = h
* len
;
108 curve_
.check_sanity ();
112 Bezier_bow::calc_enclosed_area_f () const
115 for (int i
=0; i
< encompass_
.size (); i
++)
121 x
= Interval (0, encompass_
[1][X_AXIS
] / 2);
123 curve_
.get_other_coordinate (X_AXIS
,
124 encompass_
[1][X_AXIS
]
127 else if (i
== encompass_
.size () - 1)
129 x
= Interval ((encompass_
[i
-1][X_AXIS
] + encompass_
[i
][X_AXIS
])/2,
130 encompass_
[i
][X_AXIS
]);
132 (curve_
.get_other_coordinate (X_AXIS
,
133 (x
[MIN
] + x
[MAX
]) / 2)));
137 x
= Interval ((encompass_
[i
-1][X_AXIS
] + encompass_
[i
][X_AXIS
]) / 2,
138 (encompass_
[i
][X_AXIS
] + encompass_
[i
+1][X_AXIS
]) / 2);
139 y
= Interval (encompass_
[i
][Y_AXIS
],
140 (curve_
.get_other_coordinate (X_AXIS
, x
[MIN
])
141 + curve_
.get_other_coordinate (X_AXIS
,
142 (x
[MIN
] + x
[MAX
]) / 2)
143 + curve_
.get_other_coordinate (X_AXIS
, x
[MAX
])) / 3);
146 Real da
= x
.length () * y
.length ();
153 Bezier_bow::area_gradient_offset_arr ()
155 Real len
= curve_
.control_
[3][X_AXIS
];
156 Real area
= calc_enclosed_area_f ();
158 Real grow
= len
/ 10.0;
159 Array
<Offset
> da (2);
160 for (int i
=1; i
< 3; i
++)
162 for (Axis a
=X_AXIS
; a
< NO_AXES
; incr (a
))
164 Real r
= curve_
.control_
[i
][a
];
165 curve_
.control_
[i
][a
] += grow
;
166 da
[i
-1][a
] = (calc_enclosed_area_f () - area
) / grow
;
168 curve_
.control_
[i
][a
] = r
;
175 Bezier_bow::minimise_enclosed_area ()
177 Real len
= curve_
.control_
[3][X_AXIS
];
178 Real beautiful
= len
* default_height (len
) / 2.0;
180 DEBUG_OUT
<< to_str ("Beautiful: %f\n", beautiful
);
181 DEBUG_OUT
<< to_str ("Length: %f\n", len
);
183 for (int i
=0; i
< steps
; i
++)
185 Real ff
= fit_factor ();
189 DEBUG_OUT
<< to_str ("FitFac: %f\n", ff
);
191 // slur must be higher at every point
195 DEBUG_OUT
<< to_str ("Blown area: %f\n", calc_enclosed_area_f ());
198 DEBUG_OUT
<< to_str ("Init area: %f\n", calc_enclosed_area_f ());
200 Real area
= calc_enclosed_area_f ();
203 if (area
<= beautiful
)
206 Array
<Offset
> da
= area_gradient_offset_arr ();
210 Small slurs are easily too asymmetric,
211 while big slurs are too symmetric
213 This makes short slurs strictly x-bound,
214 long slurs become y-bound.
217 //Real xpct = (0.07 * len * len / 1000.0) <? 0.80;
218 Real xpct
= (0.1 * len
* len
* len
/ 100000.0) <? 0.80;
220 Real yu
= (abs (curve_
.control_
[1][Y_AXIS
] / da
[0][Y_AXIS
])
221 <? abs (curve_
.control_
[2][Y_AXIS
] / da
[1][Y_AXIS
]))
223 Real xu
= (abs (curve_
.control_
[1][X_AXIS
] / da
[0][X_AXIS
])
224 <? abs ((curve_
.control_
[3][X_AXIS
]
225 - curve_
.control_
[2][X_AXIS
]) / da
[1][X_AXIS
]))
228 DEBUG_OUT
<< to_str ("u (xu, yu): %f (%f, %f)\n", u
, xu
, yu
);
229 DEBUG_OUT
<< to_str ("pct (x, y): (%f, %f)\n", xpct
, ypct
);
231 DEBUG_OUT
<< to_str ("da1: (%f, %f)\n", da
[0][X_AXIS
], da
[0][Y_AXIS
]);
232 DEBUG_OUT
<< to_str ("da2: (%f, %f)\n", da
[1][X_AXIS
], da
[1][Y_AXIS
]);
234 curve_
.control_
[1] -= da
[0] * u
;
235 curve_
.control_
[2] -= da
[1] * u
;
239 if (fit_factor () > 1.5)
242 DEBUG_OUT
<< to_str ("Exarea: %f\n", calc_enclosed_area_f ());
243 Real area
= calc_enclosed_area_f ();
245 Slurs that fit beautifully are not ugly
247 if (area
> beautiful
)
249 DEBUG_OUT
<< "DE-UGLYFY\n";
256 Bezier_bow::calculate ()
259 if (fit_factor () > 1.0)
261 // calc_tangent_controls ();
263 minimise_enclosed_area ();
270 Bezier_bow::get_curve ()const
279 rv
.translate (origin_
);
284 static Real
const FUDGE
= 1e-8;
287 This function calculates 2 center control points,
288 based on lines through c_0 --> left disturbing
289 and c_3--> right disturbing encompass points.
291 See Documentation/fonts.tex
294 Bezier_bow::calc_tangent_controls ()
296 Real b
= curve_
.control_
[3][X_AXIS
];
297 Real h
= curve_
.control_
[1][Y_AXIS
];
300 Drul_array
<Offset
> disturb
;
301 Drul_array
<Real
> maxtan
;
302 maxtan
[LEFT
] = maxtan
[RIGHT
] = h
/(b
/2);
303 disturb
[LEFT
] = disturb
[RIGHT
] = Offset (b
/ 2, h
);
305 for (int i
= 1; i
< encompass_
.size () -1; i
++)
307 Real y
= encompass_
[i
][Y_AXIS
];
310 Real x
= encompass_
[i
][X_AXIS
];
317 Real tan
= y
/ ((1-k
)* b
- d
* x
);
322 disturb
[d
] = Offset (x
,y
);
325 while (flip (&d
)!=LEFT
);
329 for (int i
= 0; i
< encompass_
.size (); i
++ )
330 h
= h
>? encompass_
[i
][Y_AXIS
];
333 The curve will always be under line between curve_.control_0 -> curve_.control_1, so
334 make it extra steep by slur_rc_factor
338 Drul_array
<Real
> angles
;
342 maxtan
[d
] *= -d
* rc_factor_
;
343 angles
[d
] = atan (maxtan
[d
]);
345 while (flip(&d
) != LEFT
);
350 if we have two disturbing points, have line through those...
351 in order to get a sane line, make sure points are reasonably far apart
352 X distance must be reasonably(!) big (division)
354 if (abs (disturb
[LEFT
][X_AXIS
] - disturb
[RIGHT
][X_AXIS
]) > FUDGE
)
355 rc3
= (disturb
[RIGHT
][Y_AXIS
] - disturb
[LEFT
][Y_AXIS
]) / (disturb
[RIGHT
][X_AXIS
] - disturb
[LEFT
][X_AXIS
]);
358 rc3
= tan ((angles
[LEFT
] - angles
[RIGHT
]) / 2);
361 // ugh: be less steep
365 Real c2
= -maxtan
[RIGHT
] * curve_
.control_
[3][X_AXIS
];
367 // use highest because rc3 is damped.
368 Real maxy
= disturb
[LEFT
][Y_AXIS
] >? disturb
[RIGHT
][Y_AXIS
];
369 Real c3
= disturb
[LEFT
][Y_AXIS
] > disturb
[RIGHT
][Y_AXIS
] ?
370 maxy
- rc3
* disturb
[LEFT
][X_AXIS
] :
371 maxy
- rc3
* disturb
[RIGHT
][X_AXIS
];
373 curve_
.control_
[1][X_AXIS
] = c3
/ (maxtan
[LEFT
] - rc3
);
374 curve_
.control_
[1][Y_AXIS
] = maxtan
[LEFT
] * curve_
.control_
[1][X_AXIS
];
376 curve_
.control_
[2][X_AXIS
] = (c3
- c2
) / (maxtan
[RIGHT
] - rc3
);
377 curve_
.control_
[2][Y_AXIS
] = maxtan
[RIGHT
] * curve_
.control_
[2][X_AXIS
] + c2
;
380 curve_
.check_sanity();
384 max ( encompass.y / curve.y )
388 Bezier_bow::fit_factor () const
390 Real x1
= encompass_
[0][X_AXIS
];
391 Real x2
= encompass_
.top ()[X_AXIS
];
394 for (int i
=1; i
< encompass_
.size ()-1; i
++)
396 if (encompass_
[i
][X_AXIS
] > x1
&& encompass_
[i
][X_AXIS
] < x2
)
398 Real y
= curve_
.get_other_coordinate (X_AXIS
, encompass_
[i
][X_AXIS
]);
401 Real f
= encompass_
[i
][Y_AXIS
] / y
;
402 factor
= factor
>? f
;
415 Bezier_bow::to_canonic_form ()
417 origin_
= encompass_
[0];
418 translate (encompass_
,-origin_
);
420 Offset delta
= encompass_
.top () - encompass_
[0];
421 alpha_
= delta
.arg ();
423 rotate (encompass_
, -alpha_
);
429 while (encompass_
.size () > 1 && encompass_
[1][X_AXIS
] <= 0.0)
431 programming_error ("Degenerate slur: infinite steepness reqd");
435 Real l
= encompass_
.top ()[X_AXIS
];
436 while (encompass_
.size () > 1 && encompass_
.top (1)[X_AXIS
] >= l
)
438 programming_error ("Degenerate slur: infinite steepness reqd");
439 encompass_
.del (encompass_
.size ()-2);
446 See Documentation/fonts.tex
449 Bezier_bow::calc_default ()
453 Real alpha
= height_limit_
* 2.0 / pi
;
454 Real beta
= pi
* ratio_
/ (2.0 * height_limit_
);
456 Offset
delta (encompass_
.top ()[X_AXIS
]
457 - encompass_
[0][X_AXIS
], 0);
459 Real b
= delta
.length ();
460 Real indent
= alpha
* atan (beta
* b
);
461 Real height
= indent
;
463 curve_
.control_
[0] = Offset (0, 0);
464 curve_
.control_
[1] = Offset (indent
, height
);
465 curve_
.control_
[2] = Offset (b
- indent
, height
);
466 curve_
.control_
[3] = Offset (b
, 0);