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>
13 #include "direction.hh"
15 #include "paper-def.hh"
18 #define SLUR_DOUT if (check_debug && !monitor->silent_b ("Slur")) cout
20 #define SLUR_DOUT cerr
26 for (int i
= 0; i
< size (); i
++)
27 (*this)[i
].mirror (Y_AXIS
);
31 Curve::largest_disturbing ()
35 for (int i
= 1; i
< size (); i
++)
37 if ((*this)[i
].y () > 0)
39 Real phi
= (*this)[i
].y () / (*this)[i
].x ();
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
]);
59 Curve::translate (Offset o
)
61 for (int i
= 0; i
< size (); i
++)
67 control_
.set_size (4);
71 Bezier::calc (int steps
)
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
);
80 for (int i
= 0; i
< curve_
.size (); i
++ )
82 curve_
[i
] = ((a
* t
+ b
) * t
+ c
) * t
+ control_
[0];
88 Bezier::set (Array
<Offset
> points
)
90 assert (points
.size () == 4);
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();
115 Bezier_bow::Bezier_bow (Paper_def
* paper_l
)
118 return_
.set_size (4);
122 Bezier_bow::blow_fit ()
124 Real dy1
= check_fit_f ();
128 // be careful not to take too big step
131 control_
[1].y () += h1
;
132 control_
[2].y () += h1
;
133 return_
[1].y () += h1
;
134 return_
[2].y () += h1
;
137 Real dy2
= check_fit_f ();
142 Real epsilon
= paper_l_
->rule_thickness ();
144 Real epsilon
= 1.5 * 0.4 PT
;
146 if (abs (dy2
- dy1
) < epsilon
)
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
);
169 control_
[1].y () += -h1
+h
;
170 control_
[2].y () += -h1
+h
;
171 return_
[1].y () += -h1
+h
;
172 return_
[2].y () += -h1
+h
;
176 Bezier_bow::calc_bezier ()
178 Real s
= sqrt (control_
[3].x () * control_
[3].x ()
179 + control_
[1].y () * control_
[2].y ());
181 Real internote
= paper_l_
->internote_f ();
183 Real internote
= STAFFHEIGHT
/ 8;
185 int steps
= (int)rint (s
/ internote
);
186 Bezier::calc (steps
);
190 Bezier_bow::calc_f (Real height
)
193 calc_default (height
);
197 Real dy
= check_fit_f ();
200 transform_controls_back ();
211 transform_controls_back ();
217 * document in Documentation/fonts.tex
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
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.
244 Bezier_bow::calc_clipping ()
246 if (!experimental_features_global_b
)
249 Real staffsize_f
= paper_l_
->get_var ("barsize");
251 Real staffsize_f
= STAFFHEIGHT
;
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
;
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
))
269 encompass_
.rotate (alpha_
);
271 origin_
.y () *= dir_
;
272 encompass_
.translate (origin_
);
276 if ((begin_dy
> 0) || (end_dy
> 0))
278 Real dy
= (begin_dy
+ end_dy
) / 4;
280 encompass_
[0].y () += dy
;
281 encompass_
[encompass_
.size () - 1].y () += dy
;
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 ();
300 origin_
= encompass_
[0];
301 encompass_
.translate (-origin_
);
303 origin_
.y () *= dir_
;
304 encompass_
.rotate (-alpha_
);
310 Bezier_bow::calc_controls ()
312 for (int i
= 0; i
< 3; i
++)
322 calc_tangent_controls ();
328 if (!calc_clipping ())
334 Bezier_bow::calc_return (Real begin_alpha
, Real end_alpha
)
337 Real thick
= 1.8 * paper_l_
->rule_thickness ();
339 Real thick
= 1.8 * 0.4 PT
;
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
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
)
368 begin_rc
= default_rc
;
372 reversed
.set_size (encompass_
.size ());
373 Real b
= control_
[3].x ();
374 for (int i
= 0; i
< encompass_
.size (); i
++ )
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
)
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
;
409 Real begin_alpha
= atan (begin_rc
);
410 Real end_alpha
= atan (-end_rc
);
411 Real theta
= (begin_alpha
- end_alpha
) / 2;
414 Real internote
= paper_l_
->internote_f ();
416 Real internote
= STAFFHEIGHT
/ 8;
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
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
);
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 ()))
458 Bezier_bow::check_fit_f ()
461 for (int i
= 1; i
< encompass_
.size () - 1; i
++)
462 dy
= dy
>? (encompass_
[i
].y () - y (encompass_
[i
].x ()));
467 Bezier_bow::set (Array
<Offset
> points
, int dir
)
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_
);
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
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
516 Bezier_bow::calc_default (Real h
)
520 Real staffsize_f
= paper_l_
->get_var ("barsize");
522 Real staffsize_f
= STAFFHEIGHT
;
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
;
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));
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);
551 Bezier::set (control
);