# quadratic bezier to parabola matrix equation

I'm trying to follow the matrix equation solution presented here: Convert quadratic bezier curve to parabola by @robjohn

I'm assuming his solution can be used for any quadratic coordinates. My goal is to be able to pass in the coordinates defining a quadratic bezier curve, an x within the bounds of the curve, and get a y value out of it.

here's the python that I came up with:

a_x, a_y, b_x, b_y, c_x, c_y, x, y = symbols('a_x a_y b_x b_y c_x c_y x y')
u = Matrix([[b_y - c_y],[c_x - b_x],[b_x*c_y - b_y*c_x]])
v = Matrix([[c_y - a_y],[a_x - c_x],[c_x*a_y - c_y*a_x]])
w = Matrix([[a_y - b_y],[b_x - a_x],[a_x*b_y - c_y*a_x]])
expr = 2*(u*w.T + w*u.T) - v*v.T
lh = Matrix([[x,y,1]])
rh = Matrix([x,y,1])


My next steps were to run collectExpr = collect(lh * expr * rh, y) and then collectExpr, which gave me an extremely long expression following the format Ax^2 + Bx + C = 0, which I was then able to convert into the quadratic equation form.

Executing collectExpr at the python prompt will show the A, B, and C. it's very long, so I'm leaving it out.

the problem I'm running into is when I solve for y and then compare that y against other solvers, the values aren't the same. For example, I've got the following juce::Path drawing a quadratic:

juce::Path path;
path.startNewSubPath(0.0, 215.0);


there are member functions in the Path class to compute the intersection point with a line:

Line<float> line(28.984375, -5.0, 28.984375, 305.0);


When I check via the juce::Path member functions, I get an intersection point with a y = 204.859955, which I can visually confirm in my window as correct.

When I run those same values through the solver that computes the y for a Quadratic's control points and an x within those bounds, I get a different value. https://coliru.stacked-crooked.com/a/9c7556d1da7f7b0e

I've triple checked my C++ implementation against the resulting python from @robjohn's math via collectExpr output, and it's accurate. But I can't figure out why I'm not getting the same y values as the juce::Path::intersectsLine functions to satisfy the equation. Does anyone have any idea if that matrix solution is correct or usable?

the juce::Path::intersectsLine() looks similar to this:

bool CurveTableAudioProcessorEditor::intersectsPath(const Path &p, Line<float> line, Array<Point<float>> &possibleIntersections)
{
//this is taken from Path::intersectsPath and modified
//to store intersections in the array
PathFlatteningIterator i (p);

Point<float> intersectionPoint;
while (i.next())
{
if (line.intersects (Line<float> (i.x1, i.y1, i.x2, i.y2), intersectionPoint))
{
DBG( "  intersection: " << intersectionPoint.toString() );
{
DBG( "**** intersected point found twice! " << intersectionPoint.toString() << " for line: " << line.getStart().toString() << " " << line.getEnd().toString());

}
}
}


As I understand it, you have a quadratic Bezier curve, and you want to find the $$y$$ value of a point on this curve corresponding to a given $$x$$ value.
Suppose the quadratic Bezier curve has control points $$P_0$$, $$P_1$$, $$P_2$$, and suppose $$P_i = (x_i, y_i)$$ for $$i=0,1,2$$. Then points $$(x,y)$$ lying on the curve satisfy the equations: \begin{align} x(t) &= (1-t)^2 x_0 + 2t(1-t)x_1 + t^2 x_2 \\ y(t) &= (1-t)^2 y_0 + 2t(1-t)y_1 + t^2 y_2 \end{align}
Suppose we are given an $$x$$ value $$x = \bar x$$. Then we solve the equation $$\bar x = (1-t)^2 x_0 + 2t(1-t)x_1 + t^2 x_2$$ to get the corresponding parameter value(s) $$t=\bar t$$. There might be zero, one, or two of these in the interval $$[0,1]$$. Then we compute the corresponding $$y$$ value(s) $$y = \bar y$$ from $$\bar y = (1-\bar t)^2 x_0 + 2{\bar t}(1- {\bar t})x_1 + {\bar t}^2 x_2$$