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_DEBUG_OUT if (flower_dstream && !flower_dstream->silent_b ("Bezier_bow")) cout
21 #define BEZIER_BOW_DEBUG_OUT 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
36 UGH. Remove debugging junk.
43 for (int i
= 0; i
< size (); i
++)
44 (*this)[i
].mirror (Y_AXIS
);
48 Curve::largest_disturbing ()
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
];
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
]);
76 Curve::translate (Offset o
)
78 for (int i
= 0; i
< size (); i
++)
84 control_
.set_size (4);
88 Bezier::calc (int steps
)
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
);
97 for (int i
= 0; i
< curve_
.size (); i
++ )
99 curve_
[i
] = ((a
* t
+ b
) * t
+ c
) * t
+ control_
[0];
105 Bezier::print () const
108 if (flower_dstream
&& !flower_dstream
->silent_b ("Bezier_controls"))
110 if (control_
[1].length ())
113 cout
<< "Controls: ";
114 for (int i
=0; i
< control_
.size (); i
++)
115 cout
<< control_
[i
].str () << ", ";
122 Bezier::set (Array
<Offset
> points
)
124 assert (points
.size () == 4);
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
);
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
];
148 Bezier_bow::Bezier_bow (Paper_def
* paper_l
)
151 return_
.set_size (4);
155 Bezier_bow::blow_fit ()
157 Real dy1
= check_fit_f ();
161 // be careful not to take too big step
164 control_
[1][Y_AXIS
] += h1
;
165 control_
[2][Y_AXIS
] += h1
;
166 return_
[1][Y_AXIS
] += h1
;
167 return_
[2][Y_AXIS
] += h1
;
170 Real dy2
= check_fit_f ();
175 Real internote_f
= paper_l_
->get_var ("interline")/2.0;
177 Real internote_f
= STAFFHEIGHT
/ 8;
180 Real epsilon
= internote_f
/ 4;
181 if (abs (dy2
- dy1
) < epsilon
)
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
);
204 if (sign (h
) != sign (h1
))
207 if (sign (h
) != sign (h1
))
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
;
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
]);
222 Real internote
= paper_l_
->get_var ("interline")/2.0;
224 Real internote
= STAFFHEIGHT
/ 8;
226 int steps
= (int)rint (s
/ internote
);
227 Bezier::calc (steps
);
231 Bezier_bow::calc_f (Real height
)
234 calc_default (height
);
237 Real dy
= check_fit_f ();
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";
254 cout
<< "************************\n";
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
));
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";
283 cout
<< "************************\n";
291 * document in Documentation/fonts.tex
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
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.
320 Bezier_bow::calc_clipping ()
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");
327 Real staffsize_f
= STAFFHEIGHT
;
328 Real clip_height
= 3.0 * staffsize_f
;
329 Real clip_ratio
= 1.2;
330 Real clip_angle
= 100;
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
;
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
))
353 if ((begin_dy
> 0) || (end_dy
> 0))
355 Real dy
= (begin_dy
+ end_dy
) / 4;
357 encompass_
[0][Y_AXIS
] += get_direction () * dy
;
358 encompass_
[encompass_
.size () - 1][Y_AXIS
] += get_direction () * dy
;
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 ();
382 Bezier_bow::calc_controls ()
384 for (int i
= 0; i
< 3; i
++)
386 if (i
&& !calc_clipping ())
390 why do we always recalc from 0?
391 shouldn't calc_f () be used (too), rather than blow_fit () (only)?
401 calc_tangent_controls ();
410 Bezier_bow::calc_return (Real begin_alpha
, Real end_alpha
)
413 Real thick
= paper_l_
->get_var ("slur_thickness");
415 Real thick
= 1.8 * 0.4 PT
;
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
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
)
444 begin_rc
= default_rc
;
448 reversed
.set_size (encompass_
.size ());
449 Real b
= control_
[3][X_AXIS
];
450 for (int i
= 0; i
< encompass_
.size (); i
++ )
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
)
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
477 Real internote
= paper_l_
->get_var ("interline")/2.0;
478 Real rc_correct
= paper_l_
->get_var ("slur_rc_factor");
480 Real internote
= STAFFHEIGHT
/ 8;
481 Real rc_correct
= 2.4;
484 begin_rc
*= rc_correct
;
485 end_rc
*= rc_correct
;
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
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
);
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
]))
528 Bezier_bow::check_fit_f ()
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
]));
539 Bezier_bow::print () const
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 () << ", ";
555 Bezier_bow::set (Array
<Offset
> points
, Direction dir
)
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
)
577 Bezier_bow::transform_back ()
579 if (get_direction () == DOWN
)
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
600 Bezier_bow::calc_default (Real h
)
604 Real height_limit
= paper_l_
->get_var ("slur_height_limit");
605 Real ratio
= paper_l_
->get_var ("slur_ratio");
607 Real staffsize_f
= STAFFHEIGHT
;
608 Real height_limit
= staffsize_f
;
609 Real ratio
= 1.0/3.0;
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
);