lilypond-1.3.7
[lilypond.git] / lily / bezier.cc
blob024db5f7b84b55edfaf2d1398327db84cf6960f9
1 /*
2 bezier.cc -- implement Bezier and Bezier_bow
4 source file of the GNU LilyPond music typesetter
6 (c) 1998--1999 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
9 #include <math.h>
10 #include "bezier.hh"
11 #include "misc.hh"
13 #ifndef STANDALONE
14 #include "dimensions.hh"
15 #include "direction.hh"
16 #include "paper-def.hh"
17 #include "debug.hh"
18 #include "main.hh"
19 #define BEZIER_BOW_DEBUG_OUT if (flower_dstream && !flower_dstream->silent_b ("Bezier_bow")) cout
20 #else
21 #define BEZIER_BOW_DEBUG_OUT cerr
22 #endif
26 [TODO]
28 * better names, esp. for all calc_foo functions
29 * blow_fit vs calc_default (Real) and calc_f (Real)
30 * exact height / tangent calculation
36 UGH. Remove debugging junk.
40 void
41 Curve::flipy ()
43 for (int i = 0; i < size (); i++)
44 (*this)[i].mirror (Y_AXIS);
47 int
48 Curve::largest_disturbing ()
50 Real alpha = 0;
51 int j = 0;
52 for (int i = 1; i < size (); i++)
54 if ((*this)[i][Y_AXIS] > 0)
56 Real phi = (*this)[i][Y_AXIS] / (*this)[i][X_AXIS];
57 if (phi > alpha)
59 alpha = phi;
60 j = i;
64 return j;
67 void
68 Curve::rotate (Real phi)
70 Offset rot (complex_exp (Offset (0, phi)));
71 for (int i = 0; i < size (); i++)
72 (*this)[i] = complex_multiply (rot, (*this)[i]);
75 void
76 Curve::translate (Offset o)
78 for (int i = 0; i < size (); i++)
79 (*this)[i] += o;
82 Bezier::Bezier ()
84 control_.set_size (4);
87 void
88 Bezier::calc (int steps)
90 steps = steps >? 10;
91 curve_.set_size (steps);
92 Real dt = 1.0 / curve_.size ();
93 Offset c = 3.0 * (control_[1] - control_[0]);
94 Offset b = 3.0 * (control_[2] - control_[1]) - c;
95 Offset a = control_[3] - (control_[0] + c + b);
96 Real t = 0.0;
97 for (int i = 0; i < curve_.size (); i++ )
99 curve_[i] = ((a * t + b) * t + c) * t + control_[0];
100 t += dt;
104 void
105 Bezier::print () const
107 #ifndef NPRINT
108 if (flower_dstream && !flower_dstream->silent_b ("Bezier_controls"))
110 if (control_[1].length ())
112 cout << "Bezier\n";
113 cout << "Controls: ";
114 for (int i=0; i < control_.size (); i++)
115 cout << control_[i].str () << ", ";
118 #endif
121 void
122 Bezier::set (Array<Offset> points)
124 assert (points.size () == 4);
125 control_ = points;
128 Real
129 Bezier::y (Real x)
131 // ugh
132 // bounds func should be templatised to take array of offsets too?
133 Array<Real> positions;
134 for (int i = 0; i < curve_.size (); i++)
135 positions.push (curve_[i][X_AXIS]);
137 Slice slice = get_bounds_slice (positions, x);
138 // ugh
139 Offset z1 = curve_[0 >? slice[BIGGER] - 1];
140 Offset z2 = curve_[1 >? slice[BIGGER]];
141 Real multiplier = (x - z2[X_AXIS]) / (z1[X_AXIS] - z2[X_AXIS]);
142 Real y = z1[Y_AXIS] * multiplier + (1.0 - multiplier) * z2[Y_AXIS];
144 return y;
148 Bezier_bow::Bezier_bow (Paper_def* paper_l)
150 paper_l_ = paper_l;
151 return_.set_size (4);
154 void
155 Bezier_bow::blow_fit ()
157 Real dy1 = check_fit_f ();
158 if (!dy1)
159 return;
161 // be careful not to take too big step
162 Real f = 0.3;
163 Real h1 = dy1 * f;
164 control_[1][Y_AXIS] += h1;
165 control_[2][Y_AXIS] += h1;
166 return_[1][Y_AXIS] += h1;
167 return_[2][Y_AXIS] += h1;
169 calc_bezier ();
170 Real dy2 = check_fit_f ();
171 if (!dy2)
172 return;
174 #ifndef STANDALONE
175 Real internote_f = paper_l_->get_var ("interline")/2.0;
176 #else
177 Real internote_f = STAFFHEIGHT / 8;
178 #endif
180 Real epsilon = internote_f / 4;
181 if (abs (dy2 - dy1) < epsilon)
182 return;
185 Assume
186 dy = B (h)
187 with
188 B (h) = a * h + b;
190 Then we get for h : B (h) = 0
192 B(0) = dy1 = a * 0 + b => b = dy1
193 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
197 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
200 Real a = (dy2 - dy1) / (f * dy1);
201 Real b = dy1;
202 Real h = -b / a;
204 if (sign (h) != sign (h1))
205 return;
207 if (sign (h) != sign (h1))
208 return;
210 control_[1][Y_AXIS] += -h1 +h;
211 control_[2][Y_AXIS] += -h1 +h;
212 return_[1][Y_AXIS] += -h1 +h;
213 return_[2][Y_AXIS] += -h1 +h;
216 void
217 Bezier_bow::calc_bezier ()
219 Real s = sqrt (control_[3][X_AXIS] * control_[3][X_AXIS]
220 + control_[1][Y_AXIS] * control_[2][Y_AXIS]);
221 #ifndef STANDALONE
222 Real internote = paper_l_->get_var ("interline")/2.0;
223 #else
224 Real internote = STAFFHEIGHT / 8;
225 #endif
226 int steps = (int)rint (s / internote);
227 Bezier::calc (steps);
230 Real
231 Bezier_bow::calc_f (Real height)
233 transform ();
234 calc_default (height);
235 calc_bezier ();
237 Real dy = check_fit_f ();
238 calc_return (0, 0);
240 transform_back ();
241 return dy;
244 void
245 Bezier_bow::calc ()
247 #ifndef NPRINT
248 // if (flower_dstream && !flower_dstream->silent_b ("Bezier_bow_controls"))
249 if (flower_dstream && !(flower_dstream->silent_b ("Bezier_controls")
250 && flower_dstream->silent_b ("Bezier_bow_controls")))
252 cout << "Before transform*********\n";
253 print ();
254 cout << "************************\n";
256 #endif
257 transform ();
258 print ();
260 calc_controls ();
263 duh, this is crude (control-points)
264 perhaps it's even better to check the amount of blow_fit ()
266 for (int i=0; i < control_.size (); i++)
268 Real y = control_[i][Y_AXIS];
269 curve_extent_drul_[Y].unite (Interval (y, y));
270 Real x = control_[i][X_AXIS];
271 curve_extent_drul_[X].unite (Interval (x, x));
274 print ();
275 transform_back ();
276 #ifndef NPRINT
277 // if (flower_dstream && !flower_dstream->silent_b ("Bezier_bow_controls"))
278 if (flower_dstream && !(flower_dstream->silent_b ("Bezier_controls")
279 && flower_dstream->silent_b ("Bezier_bow_controls")))
281 cout << "After transform*********\n";
282 print ();
283 cout << "************************\n";
285 #endif
289 [TODO]
290 * see if it works
291 * document in Documentation/fonts.tex
295 Clipping
297 This function tries to address two issues:
298 * the tangents of the slur should always point inwards
299 in the actual slur, i.e. *after rotating back*.
301 * slurs shouldn't be too high
302 let's try : h <= 1.2 b && h <= 3 staffheight?
304 We could calculate the tangent of the bezier curve from
305 both ends going inward, and clip the slur at the point
306 where the tangent (after rotation) points up (or inward
307 with a certain maximum angle).
309 However, we assume that real clipping is not the best
310 answer. We expect that moving the outer control point up
311 if the slur becomes too high will result in a nicer slur
312 after recalculation.
314 Knowing that the tangent is the line through the first
315 two control points, we'll clip (move the outer control
316 point upwards) too if the tangent points outwards.
319 bool
320 Bezier_bow::calc_clipping ()
322 #ifndef STANDALONE
323 Real clip_height = paper_l_->get_var ("slur_clip_height");
324 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
325 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
326 #else
327 Real staffsize_f = STAFFHEIGHT;
328 Real clip_height = 3.0 * staffsize_f;
329 Real clip_ratio = 1.2;
330 Real clip_angle = 100;
331 #endif
333 Real b = control_[3][X_AXIS] - control_[0][X_AXIS];
334 Real clip_h = clip_ratio * b <? clip_height;
335 Real begin_h = control_[1][Y_AXIS] - control_[0][Y_AXIS];
336 Real end_h = control_[2][Y_AXIS] - control_[3][Y_AXIS];
337 Real begin_dy = 0 >? begin_h - clip_h;
338 Real end_dy = 0 >? end_h - clip_h;
340 Real pi = M_PI;
341 Real begin_alpha = (control_[1] - control_[0]).arg () + get_direction () * alpha_;
342 Real end_alpha = pi - (control_[2] - control_[3]).arg () - get_direction () * alpha_;
344 Real max_alpha = clip_angle / 90 * pi / 2;
345 if ((begin_dy < 0) && (end_dy < 0)
346 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
347 return false;
349 transform_back ();
351 bool again = true;
353 if ((begin_dy > 0) || (end_dy > 0))
355 Real dy = (begin_dy + end_dy) / 4;
356 dy *= cos (alpha_);
357 encompass_[0][Y_AXIS] += get_direction () * dy;
358 encompass_[encompass_.size () - 1][Y_AXIS] += get_direction () * dy;
360 else
362 //ugh
363 Real c = 0.4;
364 if (begin_alpha >= max_alpha)
365 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
366 if (end_alpha >= max_alpha)
367 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
369 encompass_[0][Y_AXIS] += get_direction () * begin_dy;
370 encompass_[encompass_.size () - 1][Y_AXIS] += get_direction () * end_dy;
372 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
373 alpha_ = delta.arg ();
376 transform ();
378 return again;
381 void
382 Bezier_bow::calc_controls ()
384 for (int i = 0; i < 3; i++)
386 if (i && !calc_clipping ())
387 return;
390 why do we always recalc from 0?
391 shouldn't calc_f () be used (too), rather than blow_fit () (only)?
393 calc_default (0);
394 calc_bezier ();
396 if (check_fit_bo ())
398 calc_return (0, 0);
399 return;
401 calc_tangent_controls ();
403 blow_fit ();
404 // ugh
405 blow_fit ();
409 void
410 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
412 #ifndef STANDALONE
413 Real thick = paper_l_->get_var ("slur_thickness");
414 #else
415 Real thick = 1.8 * 0.4 PT;
416 #endif
418 return_[0] = control_[3];
419 return_[3] = control_[0];
421 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
422 return_[2] = control_[1]
423 - thick * complex_exp (Offset (0, 90 - begin_alpha));
427 See Documentation/fonts.tex
429 void
430 Bezier_bow::calc_tangent_controls ()
432 Offset ijk_p (control_[3][X_AXIS] / 2, control_[1][Y_AXIS]);
433 BEZIER_BOW_DEBUG_OUT << "ijk: " << ijk_p[X_AXIS] << ", " << ijk_p[Y_AXIS] << endl;
435 Real default_rc = ijk_p[Y_AXIS] / ijk_p[X_AXIS];
437 int begin_disturb = encompass_.largest_disturbing ();
438 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb][X_AXIS],
439 encompass_[begin_disturb][Y_AXIS]) : ijk_p;
440 Real begin_rc = begin_p[Y_AXIS] / begin_p[X_AXIS];
441 if (default_rc > begin_rc)
443 begin_p = ijk_p;
444 begin_rc = default_rc;
447 Curve reversed;
448 reversed.set_size (encompass_.size ());
449 Real b = control_[3][X_AXIS];
450 for (int i = 0; i < encompass_.size (); i++ )
452 // b 1 0
453 // r = - * c
454 // 0 0 -1
455 reversed[i][X_AXIS] = b - encompass_[encompass_.size () - i - 1][X_AXIS];
456 reversed[i][Y_AXIS] = encompass_[encompass_.size () - i - 1][Y_AXIS];
459 int end_disturb = reversed.largest_disturbing ();
460 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
461 Offset end_p = end_disturb ? Offset (encompass_[end_disturb][X_AXIS],
462 encompass_[end_disturb][Y_AXIS]) : ijk_p;
463 Real end_rc = end_p[Y_AXIS] / (control_[3][X_AXIS] - end_p[X_AXIS]);
464 if (default_rc > end_rc)
466 end_p = ijk_p;
467 end_rc = default_rc;
470 Real height =control_[1][Y_AXIS];
471 for (int i = 0; i < encompass_.size (); i++ )
472 height = height >? encompass_[i][Y_AXIS];
474 // emperic computer science:
475 // * tangents somewhat steeper than minimal line
476 #ifndef STANDALONE
477 Real internote = paper_l_->get_var ("interline")/2.0;
478 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
479 #else
480 Real internote = STAFFHEIGHT / 8;
481 Real rc_correct = 2.4;
482 #endif
484 begin_rc *= rc_correct;
485 end_rc *= rc_correct;
486 Real rc1 = begin_rc;
487 Real rc2 = -end_rc;
489 Real begin_alpha = atan (begin_rc);
490 Real end_alpha = atan (-end_rc);
491 Real theta = (begin_alpha - end_alpha) / 2;
493 Real epsilon = internote / 5;
495 // if we have two disturbing points, have height line through those...
496 if (!((abs (begin_p[X_AXIS] - end_p[X_AXIS]) < epsilon)
497 && (abs (begin_p[Y_AXIS] - end_p[Y_AXIS]) < epsilon)))
498 theta = atan (end_p[Y_AXIS] - begin_p[Y_AXIS]) / (end_p[X_AXIS] - begin_p[X_AXIS]);
500 Real rc3 = tan (theta);
501 // ugh: be less steep
502 rc3 /= 2*rc_correct;
504 Real c2 = -rc2 * control_[3][X_AXIS];
505 Real c3 = begin_p[Y_AXIS] > end_p[Y_AXIS] ? begin_p[Y_AXIS]
506 - rc3 * begin_p[X_AXIS] : end_p[Y_AXIS] - rc3 * end_p[X_AXIS];
508 control_[1][X_AXIS] = c3 / (rc1 - rc3);
509 control_[1][Y_AXIS] = rc1 * control_[1][X_AXIS];
510 control_[2][X_AXIS] = (c3 - c2) / (rc2 - rc3);
511 control_[2][Y_AXIS] = rc2 * control_[2][X_AXIS] + c2;
513 calc_return (begin_alpha, end_alpha);
516 bool
517 Bezier_bow::check_fit_bo ()
519 for (int i = 1; i < encompass_.size () - 1; i++)
520 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
521 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
522 if (encompass_[i][Y_AXIS] > y (encompass_[i][X_AXIS]))
523 return false;
524 return true;
527 Real
528 Bezier_bow::check_fit_f ()
530 Real dy = 0;
531 for (int i = 1; i < encompass_.size () - 1; i++)
532 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
533 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
534 dy = dy >? (encompass_[i][Y_AXIS] - y (encompass_[i][X_AXIS]));
535 return dy;
538 void
539 Bezier_bow::print () const
541 #ifndef NPRINT
542 Bezier::print ();
543 if (flower_dstream && !flower_dstream->silent_b ("Bezier_bow_controls"))
545 cout << "Bezier_bow\n";
546 cout << "Encompass: ";
547 for (int i=0; i < encompass_.size (); i++)
548 cout << encompass_[i].str () << ", ";
549 // cout << "\n";
551 #endif
554 void
555 Bezier_bow::set (Array<Offset> points, Direction dir)
557 set_direction (dir);
558 encompass_ = points;
561 void
562 Bezier_bow::transform ()
564 origin_ = encompass_[0];
565 encompass_.translate (-origin_);
567 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
568 alpha_ = delta.arg ();
570 encompass_.rotate (-alpha_);
572 if (get_direction () == DOWN)
573 encompass_.flipy ();
576 void
577 Bezier_bow::transform_back ()
579 if (get_direction () == DOWN)
581 control_.flipy ();
582 return_.flipy ();
583 encompass_.flipy ();
586 control_.rotate (alpha_);
587 control_.translate (origin_);
589 return_.rotate (alpha_);
590 return_.translate (origin_);
592 encompass_.rotate (alpha_);
593 encompass_.translate (origin_);
597 See Documentation/fonts.tex
599 void
600 Bezier_bow::calc_default (Real h)
602 Real pi = M_PI;
603 #ifndef STANDALONE
604 Real height_limit = paper_l_->get_var ("slur_height_limit");
605 Real ratio = paper_l_->get_var ("slur_ratio");
606 #else
607 Real staffsize_f = STAFFHEIGHT;
608 Real height_limit = staffsize_f;
609 Real ratio = 1.0/3.0;
610 #endif
612 Real alpha = height_limit * 2.0 / pi;
613 Real beta = pi * ratio / (2.0 * height_limit);
615 Offset delta (encompass_[encompass_.size () - 1][X_AXIS]
616 - encompass_[0][X_AXIS], 0);
617 Real b = delta.length ();
618 Real indent = alpha * atan (beta * b);
619 Real height = indent + h;
621 Array<Offset> control;
622 control.push (Offset (0, 0));
623 control.push (Offset (indent, height));
624 control.push (Offset (b - indent, height));
625 control.push (Offset (b, 0));
626 Bezier::set (control);