lilypond-0.1.59
[lilypond.git] / lily / bezier.cc
blobbb360581e24df1d18a3ab4e1fb3da95d952e98e6
1 /*
2 bezier.cc -- implement Bezier and Bezier_bow
4 source file of the GNU LilyPond music typesetter
6 (c) 1998 Jan Nieuwenhuizen <jan@digicash.com>
7 */
9 #include <math.h>
10 #include "bezier.hh"
12 #ifndef STANDALONE
13 #include "direction.hh"
14 #include "dimen.hh"
15 #include "paper-def.hh"
16 #include "debug.hh"
17 #include "main.hh"
18 #define SLUR_DOUT if (check_debug && !monitor->silent_b ("Slur")) cout
19 #else
20 #define SLUR_DOUT cerr
21 #endif
23 void
24 Curve::flipy ()
26 for (int i = 0; i < size (); i++)
27 (*this)[i].mirror (Y_AXIS);
30 int
31 Curve::largest_disturbing ()
33 Real alpha = 0;
34 int j = 0;
35 for (int i = 1; i < size (); i++)
37 if ((*this)[i].y () > 0)
39 Real phi = (*this)[i].y () / (*this)[i].x ();
40 if (phi > alpha)
42 alpha = phi;
43 j = i;
47 return j;
50 void
51 Curve::rotate (Real phi)
53 Offset rot (complex_exp (Offset (0, phi)));
54 for (int i = 0; i < size (); i++)
55 (*this)[i] = complex_multiply (rot, (*this)[i]);
58 void
59 Curve::translate (Offset o)
61 for (int i = 0; i < size (); i++)
62 (*this)[i] += o;
65 Bezier::Bezier ()
67 control_.set_size (4);
70 void
71 Bezier::calc (int steps)
73 steps = steps >? 10;
74 curve_.set_size (steps);
75 Real dt = 1.0 / curve_.size ();
76 Offset c = 3.0 * (control_[1] - control_[0]);
77 Offset b = 3.0 * (control_[2] - control_[1]) - c;
78 Offset a = control_[3] - (control_[0] + c + b);
79 Real t = 0.0;
80 for (int i = 0; i < curve_.size (); i++ )
82 curve_[i] = ((a * t + b) * t + c) * t + control_[0];
83 t += dt;
87 void
88 Bezier::set (Array<Offset> points)
90 assert (points.size () == 4);
91 control_ = points;
94 Real
95 Bezier::y (Real x)
97 for (int i = 1; i < curve_.size (); i++ )
99 if (x < curve_[i].x () || (i == curve_.size () - 1))
101 Offset z1 = curve_[i-1];
102 Offset z2 = curve_[i];
103 Real multiplier = (x - z2.x ()) / (z1.x () - z2.x ());
104 Real y = z1.y () * multiplier + (1.0 - multiplier) * z2.y();
106 return y;
109 assert (false);
110 // silly c++
111 return 0;
115 Bezier_bow::Bezier_bow (Paper_def* paper_l)
117 paper_l_ = paper_l;
118 return_.set_size (4);
121 void
122 Bezier_bow::blow_fit ()
124 Real dy1 = check_fit_f ();
125 if (!dy1)
126 return;
128 // be careful not to take too big step
129 Real f = 0.3;
130 Real h1 = dy1 * f;
131 control_[1].y () += h1;
132 control_[2].y () += h1;
133 return_[1].y () += h1;
134 return_[2].y () += h1;
136 calc_bezier ();
137 Real dy2 = check_fit_f ();
138 if (!dy2)
139 return;
141 #ifndef STANDALONE
142 Real epsilon = paper_l_->rule_thickness ();
143 #else
144 Real epsilon = 1.5 * 0.4 PT;
145 #endif
146 if (abs (dy2 - dy1) < epsilon)
147 return;
150 Assume
151 dy = B (h)
152 with
153 B (h) = a * h + b;
155 Then we get for h : B (h) = 0
157 B(0) = dy1 = a * 0 + b => b = dy1
158 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
162 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
165 Real a = (dy2 - dy1) / (f * dy1);
166 Real b = dy1;
167 Real h = -b / a;
169 control_[1].y () += -h1 +h;
170 control_[2].y () += -h1 +h;
171 return_[1].y () += -h1 +h;
172 return_[2].y () += -h1 +h;
175 void
176 Bezier_bow::calc_bezier ()
178 Real s = sqrt (control_[3].x () * control_[3].x ()
179 + control_[1].y () * control_[2].y ());
180 #ifndef STANDALONE
181 Real internote = paper_l_->internote_f ();
182 #else
183 Real internote = STAFFHEIGHT / 8;
184 #endif
185 int steps = (int)rint (s / internote);
186 Bezier::calc (steps);
189 Real
190 Bezier_bow::calc_f (Real height)
192 transform ();
193 calc_default (height);
195 calc_bezier ();
197 Real dy = check_fit_f ();
198 calc_return (0, 0);
200 transform_controls_back ();
201 return dy;
204 void
205 Bezier_bow::calc ()
207 transform ();
209 calc_controls ();
211 transform_controls_back ();
215 [TODO]
216 * see if it works
217 * document in Documentation/fonts.tex
221 Clipping
223 This function tries to address two issues:
224 * the tangents of the slur should always point inwards
225 in the actual slur, i.e. *after rotating back*.
226 * slurs shouldn't be too high ( <= 1.5 staffheight?)
228 We could calculate the tangent of the bezier curve from
229 both ends going inward, and clip the slur at the point
230 where the tangent (after rotation) points up (or inward
231 with a certain maximum angle).
233 However, we assume that real clipping is not the best
234 answer. We expect that moving the outer control point up
235 if the slur becomes too high will result in a nicer slur
236 after recalculation.
238 Knowing that the tangent is the line through the first
239 two control points, we'll clip (move the outer control
240 point upwards) too if the tangent points outwards.
243 bool
244 Bezier_bow::calc_clipping ()
246 if (!experimental_features_global_b)
247 return false;
248 #ifndef STANDALONE
249 Real staffsize_f = paper_l_->get_var ("barsize");
250 #else
251 Real staffsize_f = STAFFHEIGHT;
252 #endif
254 Real clip_h = staffsize_f;
255 Real begin_h = control_[1].y () - control_[0].y ();
256 Real end_h = control_[2].y () - control_[3].y ();
257 Real begin_dy = begin_h - clip_h;
258 Real end_dy = end_h - clip_h;
260 Real pi = M_PI;
261 Real begin_alpha = (control_[1] - control_[0]).arg () + alpha_;
262 Real end_alpha = pi - (control_[2] - control_[3]).arg () - alpha_;
264 Real max_alpha = 1.1 * pi/2;
265 if ((begin_dy < 0) && (end_dy < 0)
266 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
267 return false;
269 encompass_.rotate (alpha_);
270 // ugh
271 origin_.y () *= dir_;
272 encompass_.translate (origin_);
274 bool again = true;
275 //ugh
276 if ((begin_dy > 0) || (end_dy > 0))
278 Real dy = (begin_dy + end_dy) / 4;
279 dy *= cos (alpha_);
280 encompass_[0].y () += dy;
281 encompass_[encompass_.size () - 1].y () += dy;
283 else
285 //ugh
286 Real c = -0.4;
287 if (begin_alpha >= max_alpha)
288 begin_dy = c * begin_alpha / max_alpha * begin_h;
289 if (end_alpha >= max_alpha)
290 end_dy = c * end_alpha / max_alpha * end_h;
291 encompass_[0].y () += begin_dy;
292 encompass_[encompass_.size () - 1].y () += end_dy;
294 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
295 alpha_ = delta.arg ();
296 alpha_ *= dir_;
297 // again = false;
300 origin_ = encompass_[0];
301 encompass_.translate (-origin_);
302 // ugh
303 origin_.y () *= dir_;
304 encompass_.rotate (-alpha_);
306 return again;
309 void
310 Bezier_bow::calc_controls ()
312 for (int i = 0; i < 3; i++)
314 calc_default (0);
315 calc_bezier ();
317 if (check_fit_bo ())
319 calc_return (0, 0);
320 return;
322 calc_tangent_controls ();
323 blow_fit ();
325 // ugh
326 blow_fit ();
328 if (!calc_clipping ())
329 return;
333 void
334 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
336 #ifndef STANDALONE
337 Real thick = 1.8 * paper_l_->rule_thickness ();
338 #else
339 Real thick = 1.8 * 0.4 PT;
340 #endif
342 return_[0] = control_[3];
343 return_[3] = control_[0];
345 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
346 return_[2] = control_[1]
347 - thick * complex_exp (Offset (0, 90 - begin_alpha));
351 See Documentation/fonts.tex
353 void
354 Bezier_bow::calc_tangent_controls ()
356 Offset ijk_p (control_[3].x () / 2, control_[1].y ());
357 SLUR_DOUT << "ijk: " << ijk_p.x () << ", " << ijk_p.y () << endl;
359 Real default_rc = ijk_p.y () / ijk_p.x ();
361 int begin_disturb = encompass_.largest_disturbing ();
362 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb].x (),
363 encompass_[begin_disturb].y ()) : ijk_p;
364 Real begin_rc = begin_p.y () / begin_p.x ();
365 if (default_rc > begin_rc)
367 begin_p = ijk_p;
368 begin_rc = default_rc;
371 Curve reversed;
372 reversed.set_size (encompass_.size ());
373 Real b = control_[3].x ();
374 for (int i = 0; i < encompass_.size (); i++ )
376 // b 1 0
377 // r = - * c
378 // 0 0 -1
379 reversed[i].x () = b - encompass_[encompass_.size () - i - 1].x ();
380 reversed[i].y () = encompass_[encompass_.size () - i - 1].y ();
383 int end_disturb = reversed.largest_disturbing ();
384 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
385 Offset end_p = end_disturb ? Offset (encompass_[end_disturb].x (),
386 encompass_[end_disturb].y ()) : ijk_p;
387 Real end_rc = end_p.y () / (control_[3].x () - end_p.x ());
388 if (default_rc > end_rc)
390 end_p = ijk_p;
391 end_rc = default_rc;
393 SLUR_DOUT << "begin " << begin_p.x () << ", " << begin_p.y () << endl;
394 SLUR_DOUT << "end " << end_p.x () << ", " << end_p.y () << endl;
396 Real height =control_[1].y ();
397 for (int i = 0; i < encompass_.size (); i++ )
398 height = height >? encompass_[i].y ();
400 // emperic computer science:
401 // * tangents somewhat steeper than minimal line
402 Real rc_correct = 2.4;
404 begin_rc *= rc_correct;
405 end_rc *= rc_correct;
406 Real rc1 = begin_rc;
407 Real rc2 = -end_rc;
409 Real begin_alpha = atan (begin_rc);
410 Real end_alpha = atan (-end_rc);
411 Real theta = (begin_alpha - end_alpha) / 2;
413 #ifndef STANDALONE
414 Real internote = paper_l_->internote_f ();
415 #else
416 Real internote = STAFFHEIGHT / 8;
417 #endif
418 Real epsilon = internote / 5;
420 // if we have two disturbing points, have height line through those...
421 if (!((abs (begin_p.x () - end_p.x ()) < epsilon)
422 && (abs (begin_p.y () - end_p.y ()) < epsilon)))
423 theta = atan (end_p.y () - begin_p.y ()) / (end_p.x () - begin_p.x ());
425 Real rc3 = tan (theta);
426 // ugh: be less steep
427 rc3 /= 2*rc_correct;
429 Real c2 = -rc2 * control_[3].x ();
430 Real c3 = begin_p.y () > end_p.y () ? begin_p.y ()
431 - rc3 * begin_p.x () : end_p.y () - rc3 * end_p.x ();
433 SLUR_DOUT << "y1 = " << rc1 << " x + 0" << endl;
434 SLUR_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
435 SLUR_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
436 control_[1].x () = c3 / (rc1 - rc3);
437 control_[1].y () = rc1 * control_[1].x ();
438 control_[2].x () = (c3 - c2) / (rc2 - rc3);
439 SLUR_DOUT << "c2.x () = " << control_[2].x () << endl;
440 SLUR_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
441 SLUR_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
442 control_[2].y () = rc2 * control_[2].x () + c2;
443 SLUR_DOUT << "c2.y ()" << control_[2].y () << endl;
445 calc_return (begin_alpha, end_alpha);
448 bool
449 Bezier_bow::check_fit_bo ()
451 for (int i = 1; i < encompass_.size () - 1; i++)
452 if (encompass_[i].y () > y (encompass_[i].x ()))
453 return false;
454 return true;
457 Real
458 Bezier_bow::check_fit_f ()
460 Real dy = 0;
461 for (int i = 1; i < encompass_.size () - 1; i++)
462 dy = dy >? (encompass_[i].y () - y (encompass_[i].x ()));
463 return dy;
466 void
467 Bezier_bow::set (Array<Offset> points, int dir)
469 dir_ = dir;
470 encompass_ = points;
473 void
474 Bezier_bow::transform ()
476 origin_ = encompass_[0];
477 encompass_.translate (-origin_);
479 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
480 alpha_ = delta.arg ();
482 encompass_.rotate (-alpha_);
484 if (dir_ == DOWN)
485 encompass_.flipy ();
488 void
489 Bezier_bow::transform_controls_back ()
491 // silly name; let's transform encompass back too
492 // to allow recalculation without re-set()ting encompass array
494 if (dir_ == DOWN)
496 control_.flipy ();
497 return_.flipy ();
498 encompass_.flipy ();
501 control_.rotate (alpha_);
502 control_.translate (origin_);
504 return_.rotate (alpha_);
505 return_.translate (origin_);
507 encompass_.rotate (alpha_);
508 encompass_.translate (origin_);
513 See Documentation/fonts.tex
515 void
516 Bezier_bow::calc_default (Real h)
518 Real pi = M_PI;
519 #ifndef STANDALONE
520 Real staffsize_f = paper_l_->get_var ("barsize");
521 #else
522 Real staffsize_f = STAFFHEIGHT;
523 #endif
525 Real height_limit = staffsize_f;
526 Real ratio = 1.0/3.0;
528 Real alpha = height_limit * 2.0 / pi;
529 Real beta = pi * ratio / (2.0 * height_limit);
531 Offset delta (encompass_[encompass_.size () - 1].x ()
532 - encompass_[0].x (), 0);
533 Real b = delta.length ();
534 Real indent = alpha * atan (beta * b);
535 Real height = indent + h;
537 #define RESIZE_ICE
538 #ifndef RESIZE_ICE
539 Array<Offset> control;
540 control.push (Offset (0, 0));
541 control.push (Offset (indent, height));
542 control.push (Offset (b - indent, height));
543 control.push (Offset (b, 0));
544 #else
545 Array<Offset> control (4);
546 control[0] = Offset (0, 0);
547 control[1] = Offset (indent, height);
548 control[2] = Offset (b - indent, height);
549 control[3] = Offset (b, 0);
550 #endif
551 Bezier::set (control);