X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbezier-bow.cc;h=e049bd734c244f78c8411324296514e8de219cc1;hb=5d84bfad4626892bcffd05adcced53c8a2329047;hp=1610af6ec11f462f651f05352c87610023ae84d3;hpb=d26be70e8f3942df6f13e25a7f79321b9b98d1a8;p=lilypond.git diff --git a/lily/bezier-bow.cc b/lily/bezier-bow.cc index 1610af6ec1..e049bd734c 100644 --- a/lily/bezier-bow.cc +++ b/lily/bezier-bow.cc @@ -1,405 +1,128 @@ /* - bezier.cc -- implement Bezier and Bezier_bow + This file is part of LilyPond, the GNU music typesetter. - source file of the GNU LilyPond music typesetter + Copyright (C) 1998--2015 Jan Nieuwenhuizen - (c) 1998--1999 Jan Nieuwenhuizen + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . */ -#include -#include "bezier-bow.hh" #include "misc.hh" #include "bezier.hh" -#include "dimensions.hh" -#include "direction.hh" -#include "paper-def.hh" -#include "debug.hh" -#include "main.hh" -void -flipy (Array &c) +static Real +F0_1 (Real x) { - for (int i = c.size (); i--;) - c[i][Y_AXIS] = - c[i][Y_AXIS]; + return 2 / M_PI * atan (M_PI * x / 2); } -void -rotate (Array &c, Real phi) +Real +slur_height (Real width, Real h_inf, Real r_0) { - Offset rot (complex_exp (Offset (0, phi))); - for (int i = 0; i < c.size (); i++) - c[i] = complex_multiply (rot, c[i]); + return F0_1 (width * r_0 / h_inf) * h_inf; } -void -translate (Array &c, Offset o) -{ - for (int i = 0; i < c.size (); i++) - c[i] += o; -} +/* + ^ x x + | + height + | + v x x -Bezier_bow::Bezier_bow (Paper_def* paper_l) -{ - paper_l_ = paper_l; -} -void -Bezier_bow::blow_fit () -{ - Real x1 = encompass_[0][X_AXIS]; - Real x2 = encompass_.top ()[X_AXIS]; - - Real factor = 1.0; - for (int i=1; i < encompass_.size ()-1; i++) - { - if (encompass_[i][X_AXIS] > x1 && encompass_[i][X_AXIS] < x2) - { - Real y = curve_.get_other_coordinate (X_AXIS, encompass_[i][X_AXIS]); - if (y>0) - { - Real f = encompass_[i][Y_AXIS] / y; - factor = factor >? f; - } - } - } - - curve_.control_[1][Y_AXIS] *= factor; - curve_.control_[2][Y_AXIS] *= factor; - return_.control_[1][Y_AXIS] *= factor; - return_.control_[2][Y_AXIS] *= factor; - - curve_.check_sanity (); -} + For small w, the height should be proportional to w, for w -> + infinity, the height should rise to a limit asymptotically. -Real -Bezier_bow::calc_f (Real height) -{ - transform (); - calc_default (height); + Hence we take F (x) such that + F (0) = 0 , F' (0) = 1, and F (infty) = 1 - Real dy = check_fit_f (); - calc_return (0, 0); + and use - transform_back (); - return dy; -} + h = h_infinity * F (x * r_0 / h_infinity) -void -Bezier_bow::calc () -{ - transform (); - calc_controls (); - transform_back (); -} + Examples: + * F (x) = 2/pi * atan (pi x/2) -/* - [TODO] - * see if it works - * document in Documentation/fonts.tex - */ + * F (x) = 1/alpha * x^alpha / (1 + x^alpha) -/* - Clipping - - This function tries to address two issues: - * the tangents of the slur should always point inwards - in the actual slur, i.e. *after rotating back*. - - * slurs shouldn't be too high - let's try : h <= 1.2 b && h <= 3 staffheight? - - We could calculate the tangent of the bezier curve from - both ends going inward, and clip the slur at the point - where the tangent (after rotation) points up (or inward - with a certain maximum angle). - - However, we assume that real clipping is not the best - answer. We expect that moving the outer control point up - if the slur becomes too high will result in a nicer slur - after recalculation. - - Knowing that the tangent is the line through the first - two control points, we'll clip (move the outer control - point upwards) too if the tangent points outwards. - */ - -bool -Bezier_bow::calc_clipping () -{ - Real clip_height = paper_l_->get_var ("slur_clip_height"); - Real clip_ratio = paper_l_->get_var ("slur_clip_ratio"); - Real clip_angle = paper_l_->get_var ("slur_clip_angle"); - - Real b = curve_.control_[3][X_AXIS] - curve_.control_[0][X_AXIS]; - Real clip_h = clip_ratio * b ? begin_h - clip_h; - Real end_dy = 0 >? end_h - clip_h; - - Real pi = M_PI; - Real begin_alpha = (curve_.control_[1] - curve_.control_[0]).arg () + dir_ * alpha_; - Real end_alpha = pi - (curve_.control_[2] - curve_.control_[3]).arg () - dir_ * alpha_; - - Real max_alpha = clip_angle / 90 * pi / 2; - if ((begin_dy < 0) && (end_dy < 0) - && (begin_alpha < max_alpha) && (end_alpha < max_alpha)) - return false; - - transform_back (); - - bool again = true; - - if ((begin_dy > 0) || (end_dy > 0)) - { - Real dy = (begin_dy + end_dy) / 4; - dy *= cos (alpha_); - encompass_[0][Y_AXIS] += dir_ * dy; - encompass_.top ()[Y_AXIS] += dir_ * dy; - } - else - { - //ugh - Real c = 0.4; - if (begin_alpha >= max_alpha) - begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h; - if (end_alpha >= max_alpha) - end_dy = 0 >? c * end_alpha / max_alpha * end_h; - - encompass_[0][Y_AXIS] += dir_ * begin_dy; - encompass_.top ()[Y_AXIS] += dir_ * end_dy; - - Offset delta = encompass_.top () - encompass_[0]; - alpha_ = delta.arg (); - } - - transform (); - - return again; -} + * (etc.) -void -Bezier_bow::calc_controls () -{ - for (int i = 0; i < 3; i++) - { - - if (i && !calc_clipping ()) - return; - - /* - why do we always recalc from 0? - shouldn't calc_f () be used (too), rather than blow_fit () (only)? - */ - calc_default (0); - curve_.check_sanity (); - if (check_fit_f () > 0) - { - calc_tangent_controls (); - blow_fit (); - } - else - { - calc_return (0, 0); - return; - } - } -} + [with the 2nd recipe you can determine how quickly the conversion from + `small' slurs to `big' slurs occurs.] -void -Bezier_bow::calc_return (Real begin_alpha, Real end_alpha) -{ - Real thick = paper_l_->get_var ("slur_thickness"); + Although this might seem cand_idates to SCM-ify, it is not all clear + which parameters (ie. h_inf, r_0, F (.)) should be candidates for + this. At present h_inf and r_0 come from layout settings, but we did + no experiments for determining the best combinations of F, h_inf and + r_0. - return_.control_[0] = curve_.control_[3]; - return_.control_[3] = curve_.control_[0]; - return_.control_[1] = curve_.control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha)); - return_.control_[2] = curve_.control_[1] - thick * complex_exp (Offset (0, 90 - begin_alpha)); -} + The indent is proportional to the height of the slur for small + slurs. For large slurs, this gives a certain hookiness at the end, + so we increase the indent. -/* -This function calculates 2 center control points, based on - - See Documentation/fonts.tex - */ -void -Bezier_bow::calc_tangent_controls () -{ - Real b = curve_.control_[3][X_AXIS]; - Real h = curve_.control_[1][Y_AXIS]; - - - Drul_array disturb; - Drul_array maxtan; - maxtan[LEFT] = maxtan[RIGHT] = h/(b/2); - disturb[LEFT] = disturb[RIGHT] = Offset (b / 2, h); - - for (int i = 1; i < encompass_.size () -1; i++) - { - Real y= encompass_[i][Y_AXIS]; - if (y> 0) - { - Real x = encompass_[i][X_AXIS]; - - Direction d = LEFT; - do - { - // 1 if d == LEFT - int k = (1 - d)/2; - Real tan = y / ((1-k)* b - d * x); - - if (tan > maxtan[d]) - { - maxtan[d] = tan; - disturb[d] = Offset (x,y); - } - } - while (flip (&d)!=LEFT); - } - } - - for (int i = 0; i < encompass_.size (); i++ ) - h = h >? encompass_[i][Y_AXIS]; - - /* - The curve will always be under line between curve_.control_0 -> curve_.control_1, so - make it extra steep by slur_rc_factor - */ - Real rc_correct = paper_l_->get_var ("slur_rc_factor"); - - Drul_array angles; - Direction d = LEFT; - do - { - maxtan[d] *= rc_correct; - angles[d] = atan (-d * maxtan[d]); - } - while (flip(&d) != LEFT); - - Real rc3 = 0.0; - - // if we have two disturbing points, have line through those... - if (disturb[LEFT][Y_AXIS] != disturb[RIGHT][Y_AXIS]) - rc3 = (disturb[RIGHT][Y_AXIS] - disturb[LEFT][Y_AXIS]) / (disturb[RIGHT][X_AXIS] - disturb[LEFT][X_AXIS]); - - else - rc3 = tan ((angles[RIGHT] - angles[LEFT]) / 2); - - - // ugh: be less steep - rc3 /= 2*rc_correct; - - - Real c2 = -maxtan[RIGHT] * curve_.control_[3][X_AXIS]; - - // use highest because rc3 is damped. - Real maxy = disturb[LEFT][Y_AXIS] >? disturb[RIGHT][Y_AXIS]; - Real c3 = disturb[LEFT][Y_AXIS] > disturb[RIGHT][Y_AXIS] ? - maxy - rc3 * disturb[LEFT][X_AXIS] : - maxy - rc3 * disturb[RIGHT][X_AXIS]; - - curve_.control_[1][X_AXIS] = c3 / (maxtan[LEFT] - rc3); - curve_.control_[1][Y_AXIS] = maxtan[LEFT] * curve_.control_[1][X_AXIS]; - - curve_.control_[2][X_AXIS] = (c3 - c2) / (maxtan[RIGHT] - rc3); - curve_.control_[2][Y_AXIS] = maxtan[RIGHT] * curve_.control_[2][X_AXIS] + c2; - - - curve_.check_sanity(); - - calc_return (angles[LEFT], angles[RIGHT]); -} + indent = G (w) -/* - The maximum amount that the encompass points stick out above the bezier curve. - */ -Real -Bezier_bow::check_fit_f () const -{ - Real dy = 0; - Real x1 = encompass_[0][X_AXIS]; - Real x2 = encompass_.top ()[X_AXIS]; - for (int i = 1; i < encompass_.size () - 1; i++) - { - Real x = encompass_[i][X_AXIS]; - if (x1< x&& x < x2) - dy = dy >? (encompass_[i][Y_AXIS] - curve_.get_other_coordinate (X_AXIS, x)); - } - return dy; -} + w -> 0, G (w) -> .33 w -void -Bezier_bow::set (Array points, Direction dir) -{ - dir_ = dir; - encompass_ = points; -} + (due to derivative constraints, we cannot have indent > len/3) -void -Bezier_bow::transform () -{ - origin_ = encompass_[0]; - translate (encompass_,-origin_); + w -> inf, G (w) -> 2*h_inf - Offset delta = encompass_.top () - encompass_[0]; - alpha_ = delta.arg (); + i.e. - rotate (encompass_, -alpha_); - if (dir_ == DOWN) - flipy (encompass_); -} + G (0) = 0 , G'(0) 1/3, G (infty) = 2h_inf -void -Bezier_bow::transform_back () -{ - if (dir_ == DOWN) - { - curve_.flip (Y_AXIS); - return_.flip (Y_AXIS); - flipy (encompass_); - } - - curve_.rotate (alpha_); - curve_.translate (origin_); - - return_.rotate (alpha_); - return_.translate (origin_); - - rotate (encompass_,alpha_); - translate (encompass_,origin_); -} + solve from -/* - See Documentation/fonts.tex - */ -void -Bezier_bow::calc_default (Real h) -{ - Real pi = M_PI; + G (w) = r + p/(w+q) - Real height_limit = paper_l_->get_var ("slur_height_limit"); - Real ratio = paper_l_->get_var ("slur_ratio"); + yields - Real alpha = height_limit * 2.0 / pi; - Real beta = pi * ratio / (2.0 * height_limit); + G (w) = 2 h_inf - max_fraction * q^2/ (w + q) - Offset delta (encompass_.top ()[X_AXIS] - - encompass_[0][X_AXIS], 0); + with q = 2 h_inf +*/ + +void +get_slur_indent_height (Real *indent, Real *height, + Real width, Real h_inf, Real r_0) +{ + Real max_fraction = 1.0 / 3.1; + *height = slur_height (width, h_inf, r_0); - Real b = delta.length (); - Real indent = alpha * atan (beta * b); - Real height = indent + h; - - curve_.control_ [0] = Offset (0, 0); - curve_.control_ [1] = Offset (indent, height); - curve_.control_ [2] = Offset (b - indent, height); - curve_.control_ [3] = Offset (b, 0); + Real q = 2 * h_inf / max_fraction; + *indent = 2 * h_inf - sqr (q) * max_fraction / (width + q); } +Bezier +slur_shape (Real width, Real h_inf, Real r_0) +{ + Real indent; + Real height; + + get_slur_indent_height (&indent, &height, + width, h_inf, r_0); + Bezier curve; + curve.control_[0] = Offset (0, 0); + curve.control_[1] = Offset (indent, height); + curve.control_[2] = Offset (width - indent, height); + curve.control_[3] = Offset (width, 0); + return curve; +}