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>
14 #include "dimensions.hh"
15 #include "direction.hh"
16 #include "paper-def.hh"
19 #define BEZIER_BOW_DOUT if (check_debug && !monitor->silent_b ("Bezier_bow")) cout
21 #define BEZIER_BOW_DOUT cerr
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
37 for (int i
= 0; i
< size (); i
++)
38 (*this)[i
].mirror (Y_AXIS
);
42 Curve::largest_disturbing ()
46 for (int i
= 1; i
< size (); i
++)
48 if ((*this)[i
][Y_AXIS
] > 0)
50 Real phi
= (*this)[i
][Y_AXIS
] / (*this)[i
][X_AXIS
];
62 Curve::rotate (Real phi
)
64 Offset
rot (complex_exp (Offset (0, phi
)));
65 for (int i
= 0; i
< size (); i
++)
66 (*this)[i
] = complex_multiply (rot
, (*this)[i
]);
70 Curve::translate (Offset o
)
72 for (int i
= 0; i
< size (); i
++)
78 control_
.set_size (4);
82 Bezier::calc (int steps
)
85 curve_
.set_size (steps
);
86 Real dt
= 1.0 / curve_
.size ();
87 Offset c
= 3.0 * (control_
[1] - control_
[0]);
88 Offset b
= 3.0 * (control_
[2] - control_
[1]) - c
;
89 Offset a
= control_
[3] - (control_
[0] + c
+ b
);
91 for (int i
= 0; i
< curve_
.size (); i
++ )
93 curve_
[i
] = ((a
* t
+ b
) * t
+ c
) * t
+ control_
[0];
99 Bezier::print () const
102 if (check_debug
&& !monitor
->silent_b ("Bezier_controls"))
104 if (control_
[1].length ())
107 cout
<< "Controls: ";
108 for (int i
=0; i
< control_
.size (); i
++)
109 cout
<< control_
[i
].str () << ", ";
117 Bezier::set (Array
<Offset
> points
)
119 assert (points
.size () == 4);
127 // bounds func should be templatised to take array of offsets too?
128 Array
<Real
> positions
;
129 for (int i
= 0; i
< curve_
.size (); i
++)
130 positions
.push (curve_
[i
][X_AXIS
]);
132 Slice slice
= get_bounds_slice (positions
, x
);
134 Offset z1
= curve_
[0 >? slice
[BIGGER
] - 1];
135 Offset z2
= curve_
[1 >? slice
[BIGGER
]];
136 Real multiplier
= (x
- z2
[X_AXIS
]) / (z1
[X_AXIS
] - z2
[X_AXIS
]);
137 Real y
= z1
[Y_AXIS
] * multiplier
+ (1.0 - multiplier
) * z2
[Y_AXIS
];
143 Bezier_bow::Bezier_bow (Paper_def
* paper_l
)
146 return_
.set_size (4);
150 Bezier_bow::blow_fit ()
152 Real dy1
= check_fit_f ();
156 // be careful not to take too big step
159 control_
[1][Y_AXIS
] += h1
;
160 control_
[2][Y_AXIS
] += h1
;
161 return_
[1][Y_AXIS
] += h1
;
162 return_
[2][Y_AXIS
] += h1
;
165 Real dy2
= check_fit_f ();
170 Real internote_f
= paper_l_
->get_realvar (interline_scm_sym
)/2.0;
172 Real internote_f
= STAFFHEIGHT
/ 8;
175 Real epsilon
= internote_f
/ 4;
176 if (abs (dy2
- dy1
) < epsilon
)
185 Then we get for h : B (h) = 0
187 B(0) = dy1 = a * 0 + b => b = dy1
188 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
192 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
195 Real a
= (dy2
- dy1
) / (f
* dy1
);
199 if (sign (h
) != sign (h1
))
202 if (sign (h
) != sign (h1
))
205 control_
[1][Y_AXIS
] += -h1
+h
;
206 control_
[2][Y_AXIS
] += -h1
+h
;
207 return_
[1][Y_AXIS
] += -h1
+h
;
208 return_
[2][Y_AXIS
] += -h1
+h
;
212 Bezier_bow::calc_bezier ()
214 Real s
= sqrt (control_
[3][X_AXIS
] * control_
[3][X_AXIS
]
215 + control_
[1][Y_AXIS
] * control_
[2][Y_AXIS
]);
217 Real internote
= paper_l_
->get_realvar (interline_scm_sym
)/2.0;
219 Real internote
= STAFFHEIGHT
/ 8;
221 int steps
= (int)rint (s
/ internote
);
222 Bezier::calc (steps
);
226 Bezier_bow::calc_f (Real height
)
229 calc_default (height
);
233 Real dy
= check_fit_f ();
244 // if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
245 if (check_debug
&& !(monitor
->silent_b ("Bezier_controls")
246 && monitor
->silent_b ("Bezier_bow_controls")))
248 cout
<< "Before transform*********\n";
250 cout
<< "************************\n";
259 duh, this is crude (control-points)
260 perhaps it's even better to check the amount of blow_fit ()
262 for (int i
=0; i
< control_
.size (); i
++)
264 Real y
= control_
[i
][Y_AXIS
];
265 curve_extent_drul_
[Y
].unite (Interval (y
, y
));
266 Real x
= control_
[i
][X_AXIS
];
267 curve_extent_drul_
[X
].unite (Interval (x
, x
));
273 // if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
274 if (check_debug
&& !(monitor
->silent_b ("Bezier_controls")
275 && monitor
->silent_b ("Bezier_bow_controls")))
277 cout
<< "After transform*********\n";
279 cout
<< "************************\n";
287 * document in Documentation/fonts.tex
293 This function tries to address two issues:
294 * the tangents of the slur should always point inwards
295 in the actual slur, i.e. *after rotating back*.
297 * slurs shouldn't be too high
298 let's try : h <= 1.2 b && h <= 3 staffheight?
300 We could calculate the tangent of the bezier curve from
301 both ends going inward, and clip the slur at the point
302 where the tangent (after rotation) points up (or inward
303 with a certain maximum angle).
305 However, we assume that real clipping is not the best
306 answer. We expect that moving the outer control point up
307 if the slur becomes too high will result in a nicer slur
310 Knowing that the tangent is the line through the first
311 two control points, we'll clip (move the outer control
312 point upwards) too if the tangent points outwards.
316 Bezier_bow::calc_clipping ()
319 Real clip_height
= paper_l_
->get_var ("slur_clip_height");
320 Real clip_ratio
= paper_l_
->get_var ("slur_clip_ratio");
321 Real clip_angle
= paper_l_
->get_var ("slur_clip_angle");
323 Real staffsize_f
= STAFFHEIGHT
;
324 Real clip_height
= 3.0 * staffsize_f
;
325 Real clip_ratio
= 1.2;
326 Real clip_angle
= 100;
329 Real b
= control_
[3][X_AXIS
] - control_
[0][X_AXIS
];
330 Real clip_h
= clip_ratio
* b
<? clip_height
;
331 Real begin_h
= control_
[1][Y_AXIS
] - control_
[0][Y_AXIS
];
332 Real end_h
= control_
[2][Y_AXIS
] - control_
[3][Y_AXIS
];
333 Real begin_dy
= 0 >? begin_h
- clip_h
;
334 Real end_dy
= 0 >? end_h
- clip_h
;
337 Real begin_alpha
= (control_
[1] - control_
[0]).arg () + dir_
* alpha_
;
338 Real end_alpha
= pi
- (control_
[2] - control_
[3]).arg () - dir_
* alpha_
;
340 Real max_alpha
= clip_angle
/ 90 * pi
/ 2;
341 if ((begin_dy
< 0) && (end_dy
< 0)
342 && (begin_alpha
< max_alpha
) && (end_alpha
< max_alpha
))
349 if ((begin_dy
> 0) || (end_dy
> 0))
351 Real dy
= (begin_dy
+ end_dy
) / 4;
353 encompass_
[0][Y_AXIS
] += dir_
* dy
;
354 encompass_
[encompass_
.size () - 1][Y_AXIS
] += dir_
* dy
;
360 if (begin_alpha
>= max_alpha
)
361 begin_dy
= 0 >? c
* begin_alpha
/ max_alpha
* begin_h
;
362 if (end_alpha
>= max_alpha
)
363 end_dy
= 0 >? c
* end_alpha
/ max_alpha
* end_h
;
365 encompass_
[0][Y_AXIS
] += dir_
* begin_dy
;
366 encompass_
[encompass_
.size () - 1][Y_AXIS
] += dir_
* end_dy
;
368 Offset delta
= encompass_
[encompass_
.size () - 1] - encompass_
[0];
369 alpha_
= delta
.arg ();
378 Bezier_bow::calc_controls ()
380 for (int i
= 0; i
< 3; i
++)
382 if (i
&& !calc_clipping ())
386 why do we always recalc from 0?
387 shouldn't calc_f () be used (too), rather than blow_fit () (only)?
397 calc_tangent_controls ();
406 Bezier_bow::calc_return (Real begin_alpha
, Real end_alpha
)
409 Real thick
= paper_l_
->get_var ("slur_thickness");
411 Real thick
= 1.8 * 0.4 PT
;
414 return_
[0] = control_
[3];
415 return_
[3] = control_
[0];
417 return_
[1] = control_
[2] - thick
* complex_exp (Offset (0, 90 + end_alpha
));
418 return_
[2] = control_
[1]
419 - thick
* complex_exp (Offset (0, 90 - begin_alpha
));
423 See Documentation/fonts.tex
426 Bezier_bow::calc_tangent_controls ()
428 Offset
ijk_p (control_
[3][X_AXIS
] / 2, control_
[1][Y_AXIS
]);
429 BEZIER_BOW_DOUT
<< "ijk: " << ijk_p
[X_AXIS
] << ", " << ijk_p
[Y_AXIS
] << endl
;
431 Real default_rc
= ijk_p
[Y_AXIS
] / ijk_p
[X_AXIS
];
433 int begin_disturb
= encompass_
.largest_disturbing ();
434 Offset begin_p
= begin_disturb
? Offset (encompass_
[begin_disturb
][X_AXIS
],
435 encompass_
[begin_disturb
][Y_AXIS
]) : ijk_p
;
436 Real begin_rc
= begin_p
[Y_AXIS
] / begin_p
[X_AXIS
];
437 if (default_rc
> begin_rc
)
440 begin_rc
= default_rc
;
444 reversed
.set_size (encompass_
.size ());
445 Real b
= control_
[3][X_AXIS
];
446 for (int i
= 0; i
< encompass_
.size (); i
++ )
451 reversed
[i
][X_AXIS
] = b
- encompass_
[encompass_
.size () - i
- 1][X_AXIS
];
452 reversed
[i
][Y_AXIS
] = encompass_
[encompass_
.size () - i
- 1][Y_AXIS
];
455 int end_disturb
= reversed
.largest_disturbing ();
456 end_disturb
= end_disturb
? encompass_
.size () - end_disturb
- 1 : 0;
457 Offset end_p
= end_disturb
? Offset (encompass_
[end_disturb
][X_AXIS
],
458 encompass_
[end_disturb
][Y_AXIS
]) : ijk_p
;
459 Real end_rc
= end_p
[Y_AXIS
] / (control_
[3][X_AXIS
] - end_p
[X_AXIS
]);
460 if (default_rc
> end_rc
)
465 BEZIER_BOW_DOUT
<< "begin " << begin_p
[X_AXIS
] << ", " << begin_p
[Y_AXIS
] << endl
;
466 BEZIER_BOW_DOUT
<< "end " << end_p
[X_AXIS
] << ", " << end_p
[Y_AXIS
] << endl
;
468 Real height
=control_
[1][Y_AXIS
];
469 for (int i
= 0; i
< encompass_
.size (); i
++ )
470 height
= height
>? encompass_
[i
][Y_AXIS
];
472 // emperic computer science:
473 // * tangents somewhat steeper than minimal line
475 Real internote
= paper_l_
->get_realvar (interline_scm_sym
)/2.0;
476 Real rc_correct
= paper_l_
->get_var ("slur_rc_factor");
478 Real internote
= STAFFHEIGHT
/ 8;
479 Real rc_correct
= 2.4;
482 begin_rc
*= rc_correct
;
483 end_rc
*= rc_correct
;
487 Real begin_alpha
= atan (begin_rc
);
488 Real end_alpha
= atan (-end_rc
);
489 Real theta
= (begin_alpha
- end_alpha
) / 2;
491 Real epsilon
= internote
/ 5;
493 // if we have two disturbing points, have height line through those...
494 if (!((abs (begin_p
[X_AXIS
] - end_p
[X_AXIS
]) < epsilon
)
495 && (abs (begin_p
[Y_AXIS
] - end_p
[Y_AXIS
]) < epsilon
)))
496 theta
= atan (end_p
[Y_AXIS
] - begin_p
[Y_AXIS
]) / (end_p
[X_AXIS
] - begin_p
[X_AXIS
]);
498 Real rc3
= tan (theta
);
499 // ugh: be less steep
502 Real c2
= -rc2
* control_
[3][X_AXIS
];
503 Real c3
= begin_p
[Y_AXIS
] > end_p
[Y_AXIS
] ? begin_p
[Y_AXIS
]
504 - rc3
* begin_p
[X_AXIS
] : end_p
[Y_AXIS
] - rc3
* end_p
[X_AXIS
];
506 BEZIER_BOW_DOUT
<< "y1 = " << rc1
<< " x + 0" << endl
;
507 BEZIER_BOW_DOUT
<< "y2 = " << rc2
<< " x + " << c2
<< endl
;
508 BEZIER_BOW_DOUT
<< "y3 = " << rc3
<< " x + " << c3
<< endl
;
509 control_
[1][X_AXIS
] = c3
/ (rc1
- rc3
);
510 control_
[1][Y_AXIS
] = rc1
* control_
[1][X_AXIS
];
511 control_
[2][X_AXIS
] = (c3
- c2
) / (rc2
- rc3
);
512 BEZIER_BOW_DOUT
<< "c2[X_AXIS] = " << control_
[2][X_AXIS
] << endl
;
513 BEZIER_BOW_DOUT
<< "(c3 - c2) = " << (c3
- c2
) << endl
;
514 BEZIER_BOW_DOUT
<< "(rc2 - rc3) = " << (rc2
- rc3
) << endl
;
515 control_
[2][Y_AXIS
] = rc2
* control_
[2][X_AXIS
] + c2
;
516 BEZIER_BOW_DOUT
<< "c2[Y_AXIS]" << control_
[2][Y_AXIS
] << endl
;
518 calc_return (begin_alpha
, end_alpha
);
522 Bezier_bow::check_fit_bo ()
524 for (int i
= 1; i
< encompass_
.size () - 1; i
++)
525 if ((encompass_
[i
][X_AXIS
] > encompass_
[0][X_AXIS
])
526 && (encompass_
[i
][X_AXIS
] < encompass_
[encompass_
.size () -1][X_AXIS
]))
527 if (encompass_
[i
][Y_AXIS
] > y (encompass_
[i
][X_AXIS
]))
533 Bezier_bow::check_fit_f ()
536 for (int i
= 1; i
< encompass_
.size () - 1; i
++)
537 if ((encompass_
[i
][X_AXIS
] > encompass_
[0][X_AXIS
])
538 && (encompass_
[i
][X_AXIS
] < encompass_
[encompass_
.size () -1][X_AXIS
]))
539 dy
= dy
>? (encompass_
[i
][Y_AXIS
] - y (encompass_
[i
][X_AXIS
]));
544 Bezier_bow::print () const
548 if (check_debug
&& !monitor
->silent_b ("Bezier_bow_controls"))
550 cout
<< "Bezier_bow\n";
551 cout
<< "Encompass: ";
552 for (int i
=0; i
< encompass_
.size (); i
++)
553 cout
<< encompass_
[i
].str () << ", ";
560 Bezier_bow::set (Array
<Offset
> points
, int dir
)
567 Bezier_bow::transform ()
569 origin_
= encompass_
[0];
570 encompass_
.translate (-origin_
);
572 Offset delta
= encompass_
[encompass_
.size () - 1] - encompass_
[0];
573 alpha_
= delta
.arg ();
575 encompass_
.rotate (-alpha_
);
582 Bezier_bow::transform_back ()
591 control_
.rotate (alpha_
);
592 control_
.translate (origin_
);
594 return_
.rotate (alpha_
);
595 return_
.translate (origin_
);
597 encompass_
.rotate (alpha_
);
598 encompass_
.translate (origin_
);
602 See Documentation/fonts.tex
605 Bezier_bow::calc_default (Real h
)
609 Real height_limit
= paper_l_
->get_var ("slur_height_limit");
610 Real ratio
= paper_l_
->get_var ("slur_ratio");
612 Real staffsize_f
= STAFFHEIGHT
;
613 Real height_limit
= staffsize_f
;
614 Real ratio
= 1.0/3.0;
617 Real alpha
= height_limit
* 2.0 / pi
;
618 Real beta
= pi
* ratio
/ (2.0 * height_limit
);
620 Offset
delta (encompass_
[encompass_
.size () - 1][X_AXIS
]
621 - encompass_
[0][X_AXIS
], 0);
622 Real b
= delta
.length ();
623 Real indent
= alpha
* atan (beta
* b
);
624 Real height
= indent
+ h
;
626 Array
<Offset
> control
;
627 control
.push (Offset (0, 0));
628 control
.push (Offset (indent
, height
));
629 control
.push (Offset (b
- indent
, height
));
630 control
.push (Offset (b
, 0));
631 Bezier::set (control
);