Daniel Sapoundjiev on
  Bounding box of cubic bezier curve

Split cubic bezier curve into 2 parts

Created: 31 October 2018 Last updated: 31.10.2018

Background

Basics

One direction bezier

Line segment equations
P = A + (B - A)t where t changes from 0 to 1.
P = A + Bt - At
P = (1-t)A + tB

Bounding box border

The bounding box border can be formed by the end points of the cubic bezier curve or by inner point.
The inner point should have a vertical slope for left and right border and horizontal slope for top bottom borders.

Calculations

To have vertical or horizontal slope P and Q should be equal
P = Q

P = K + (L - K)t
Q = L + (M - L)t

Substitute in P = Q
K + (L - K)t = L + (M - L)t
K - L + (L - K)t - (M - L)t = 0
K - L + (L - K - M + L)t = 0
(2L - K - M)t + (K - L) = 0

We have for K, L, M
K = A + (B - A)t
L = B + (C - B)t
M = C + (D - C)t
Substitute in (2L - K - M)t + (K - L) = 0
(2*(B + (C - B)t) - (A + (B - A)t) - (C + (D - C)t))t + (A + (B - A)t) - (B + (C - B)t) = 0

(2*(B + Ct - Bt) - (A + Bt - At) - (C + Dt - Ct))t + (A + Bt - At) - (B + Ct - Bt) = 0

(2*B + 2*Ct - 2*Bt - A - Bt + At - C - Dt + Ct)t + A + Bt - At - B - Ct + Bt = 0
(2*Ct - 2*Bt - Bt + At - Dt + Ct + 2*B - C - A)t + A + Bt - At - B - Ct + Bt = 0
(3*Ct - 3*Bt + At - Dt + 2*B - C - A)t + A + Bt - At - B - Ct + Bt = 0
(3*C - 3*B + A - D)tt + (2*B - C - A)2t + A - B = 0

a = 3(C - B) + A - D
b = 2(2*B - C - A)
c = A - B

To find the value we have to solve Quadratic quation
at^2 + bt + c = 0

Code

function getBezierSegment(ap1, ap2, ap3, ap4: TPoint2D64; at: Double; out oCtrlPt2: TPoint2D64): TPoint2D64;
var
  x, y, t1, t2, t1_, t2_, v1x, v1y, v2x, v2y, a1, a2: Double;

  function getValueX(at1: Double): Double;
  begin
    Result := (1-at1)*(1-at1)*(1-at1)*ap1.X + 3*(1-at1)*(1-at1)*at1*ap2.X + 3*(1-at1)*at1*at1*ap3.X + at1*at1*at1*ap4.X;
  end;

  function getValueY(at1: Double): Double;
  begin
    Result := (1-at1)*(1-at1)*(1-at1)*ap1.Y + 3*(1-at1)*(1-at1)*at1*ap2.Y + 3*(1-at1)*at1*at1*ap3.Y + at1*at1*at1*ap4.Y;
  end;

begin
  x := getValueX(at);
  y := getValueY(at);

  t1 := 0.6;
  t2 := 0.4;

  t1_ := t1 * at;
  t2_ := t2 * at;

  v1x := getValueX(t1_);
  v1y := getValueY(t1_);

  v2x := getValueX(t2_);
  v2y := getValueY(t2_);

  a1 := (125*v1X - 8*ap1.X - 27*X)/90;
  a2 := (125*v2X - 27*ap1.X - 8*X)/30;
  v1x := a2 - 2 * a1;

  Result.X := v1x;

  v2x := 5/3*a1 - 2/3*v1x;
  oCtrlPt2.X := v2x;

  a1 := (125*v1Y - 8*ap1.Y - 27*Y)/90;
  a2 := (125*v2Y - 27*ap1.Y - 8*Y)/30;
  v1y := a2 - 2 * a1;

  Result.Y := v1y;
  v2y := 5/3*a1 -  2/3*v1y;
  oCtrlPt2.Y := v2y;

end;

function getBezierSegment2(ap1, ap2, ap3, ap4: TPoint2D64; at: Double; out oCtrlPt2: TPoint2D64): TPoint2D64;
var
  x, y, t1, t2, t1_, t2_, v1x, v1y, v2x, v2y, a1, a2: Double;

  function getValueX(at1: Double): Double;
  begin
    Result := (1-at1)*(1-at1)*(1-at1)*ap1.X + 3*(1-at1)*(1-at1)*at1*ap2.X + 3*(1-at1)*at1*at1*ap3.X + at1*at1*at1*ap4.X;
  end;

  function getValueY(at1: Double): Double;
  begin
    Result := (1-at1)*(1-at1)*(1-at1)*ap1.Y + 3*(1-at1)*(1-at1)*at1*ap2.Y + 3*(1-at1)*at1*at1*ap3.Y + at1*at1*at1*ap4.Y;
  end;

begin
  x := getValueX(at);
  y := getValueY(at);

  t1 := 0.6;
  t2 := 0.4;

  t1_ := (1-at) * t1 + at;
  t2_ := (1-at) * t2 + at;

  v1x := getValueX(t1_);
  v1y := getValueY(t1_);

  v2x := getValueX(t2_);
  v2y := getValueY(t2_);

  a1 := (125*v1X - 8*X - 27*ap4.X)/90;
  a2 := (125*v2X - 27*X - 8*ap4.X)/30;
  v1x := a2 - 2 * a1;

  Result.X := v1x;

  v2x := 5/3*a1 - 2/3*v1x;
  oCtrlPt2.X := v2x;

  a1 := (125*v1Y - 8*Y - 27*ap4.Y)/90;
  a2 := (125*v2Y - 27*Y - 8*ap4.Y)/30;
  v1y := a2 - 2 * a1;

  Result.Y := v1y;
  v2y := 5/3*a1 -  2/3*v1y;
  oCtrlPt2.Y := v2y;
end;
		
		

Back to main menu