2

I'm trying to create a bar that looks like this:


(source: hostingpics.net)

It's a bar containing buttons separated by a glowy separator. Those button have a non rectangular shape so I thought "I'll just put a transparent button and to draw the non-rectangular shape i'll use a path".

The problem I encounter is that I need to color the buttons depending on their state (enabled, disabled), so I need a path per button to change its color easily.

So I made a function to calculate a Bézier curve from the 4 points and a T value which represents the point on the curve between 0 and 1 (0 is the start point of the curve, 1 is the end point and 0.5 is the point at the middle of the curve).

public static Point CalculateBezierPoint(double t, Point p1, Point p2, Point p3, Point p4) {
    Point p = new Point();
    double tPower3 = t * t * t;
    double tPower2 = t * t;
    double oneMinusT = 1 - t;
    double oneMinusTPower3 = oneMinusT * oneMinusT*oneMinusT;
    double oneMinusTPower2 = oneMinusT * oneMinusT;
    p.X = oneMinusTPower3 * p1.X + (3 * oneMinusTPower2 * t * p2.X) + (3 * oneMinusT * tPower2 * p3.X) + tPower3 * p4.X;
    p.Y = oneMinusTPower3 * p1.Y + (3 * oneMinusTPower2 * t * p2.Y) + (3 * oneMinusT * tPower2 * p3.Y) + tPower3 * p4.Y; 
    return p;
}

This function is working well.

So I can draw the curve:


(source: hostingpics.net)

So this is pretty accurate. Except that button widths aren't equal (they should be delimited by the separators). So what I'd need is a function that can find the Y axis value of the point on the curve knowing its X axis value.

So knowing that the equation to find a point on a Bézier curve is:

(x coordinate)

Bx(t) = (1-t)^3 * P1.x + 3 * (1-t)^2 * t * P2.X + 3*(1-t)*t^2 * P3.X + t^3 * P4.X

(y coordinate)

By(t) = (1-t)^3 * P1.Y + 3 * (1-t)^2 * t * P2.Y + 3*(1-t)*t^2 * P3.Y + t^3 * P4.Y

Where:

  • Bx is the X axis value of the point on the curve, By is its Y axis value;
  • P1 is the start point of the curve
  • P2 is the first control point of the curve
  • P3 is the second control point of the curve
  • P4 is the end point of the curve
  • t is the position between 0 and 1 of the point we want to find on the curve

I thought that I could just resolve the Bx(t) equation according to t because I know at runtime the Bx, P1, P2, P3 and P4, only t is unknown. So I would like an equation which looks like:

t = ...

This was a good idea until I remembered that I'm terrible at math. I tried a lot of thing which didn't work and then tried to enter the equation in Wolframalpha, which gave me a ~50 long lines equation that doesn't work (here it is if you want to see) (I may have made mistakes recopying it in the function).

Anyway, here I am looking for help. Thank you for your help

Community
  • 1
  • 1
  • 2
    Is your curve really a 2d curve, or is it a function y=f(x)? I.e. can the curve ever loop back on itself? – dbc Sep 10 '14 at 06:47
  • @dbc the curve won't ever loop back (it's the shape in the image, it may bend but never change its shape) –  Sep 10 '14 at 06:52

1 Answers1

0

Well, if your curve is really a function y=f(x), why not represent it that way, with X simply a linear parameter?

    /// <summary>
    /// Calculate a bezier height Y of a parameter in the range [start..end]
    /// </summary>
    public static double CalculateBezierHeightInInterval(double start, double end, double param, double y1, double y2, double y3, double y4)
    {
        return CalculateBezierHeight((param - start) / (end - start), y1, y2, y3, y4);
    }

    /// <summary>
    /// Calculate a bezier height Y of a parameter in the range [0..1]
    /// </summary>
    public static double CalculateBezierHeight(double t, double y1, double y2, double y3, double y4)
    {
        double tPower3 = t * t * t;
        double tPower2 = t * t;
        double oneMinusT = 1 - t;
        double oneMinusTPower3 = oneMinusT * oneMinusT * oneMinusT;
        double oneMinusTPower2 = oneMinusT * oneMinusT;
        double Y = oneMinusTPower3 * y1 + (3 * oneMinusTPower2 * t * y2) + (3 * oneMinusT * tPower2 * y3) + tPower3 * y4;
        return Y;
    }

In this formulation you no longer need to solve for X since X is an input for Y. On the other hand, your sample heights are assumed to occur at uniform X intervals, which might be undesirable for you.

If you need the extra shape control you can get from controlling the X locations of the control points, you can use the method here: y coordinate for a given x cubic bezier

Community
  • 1
  • 1
dbc
  • 104,963
  • 20
  • 228
  • 340
  • 1
    @user3564358 - my simplified method gives you less control of the shape of the curve, but you still have some control -- and it may be enough for your purposes. If not, you can use the binary search method suggested in the link. – dbc Sep 10 '14 at 07:26
  • 1
    Nevermind about what I thought: It works very well! http://img4.hostingpics.net/pics/424171myCommandBar3.png Thank you so much, I lost so much time with this. –  Sep 10 '14 at 07:28