Compute the control points for an arbitrary bezier curve

Just starting out? Need help? Post your questions and find answers here.
Seymour Clufley
Addict
Addict
Posts: 1267
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Compute the control points for an arbitrary bezier curve

Post by Seymour Clufley »

The goal is: given the begin and end points of a cubic bezier curve as well as two points that it has to pass through along the way, compute the control points necessary.
I am trying to translate this code to PB, which seems to be able to do exactly this task. However, the result isn't working.

Here is the original function:

Code: Select all

// Assume we need to calculate the control
// points between (x1,y1) and (x2,y2).
// Then x0,y0 - the previous vertex,
//      x3,y3 - the next one.

double xc1 = (x0 + x1) / 2.0;
double yc1 = (y0 + y1) / 2.0;
double xc2 = (x1 + x2) / 2.0;
double yc2 = (y1 + y2) / 2.0;
double xc3 = (x2 + x3) / 2.0;
double yc3 = (y2 + y3) / 2.0;

double len1 = sqrt((x1-x0) * (x1-x0) + (y1-y0) * (y1-y0));
double len2 = sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1));
double len3 = sqrt((x3-x2) * (x3-x2) + (y3-y2) * (y3-y2));

double k1 = len1 / (len1 + len2);
double k2 = len2 / (len2 + len3);

double xm1 = xc1 + (xc2 - xc1) * k1;
double ym1 = yc1 + (yc2 - yc1) * k1;

double xm2 = xc2 + (xc3 - xc2) * k2;
double ym2 = yc2 + (yc3 - yc2) * k2;

// Resulting control points. Here smooth_value is mentioned
// above coefficient K whose value should be in range [0...1].
ctrl1_x = xm1 + (xc2 - xm1) * smooth_value + x1 - xm1;
ctrl1_y = ym1 + (yc2 - ym1) * smooth_value + y1 - ym1;

ctrl2_x = xm2 + (xc2 - xm2) * smooth_value + x2 - xm2;
ctrl2_y = ym2 + (yc2 - ym2) * smooth_value + y2 - ym2;
Here is my complete PB code, including a translation of the above function:

Code: Select all

Procedure RI(img.i,title.s="",bgclr.i=#Gray,timelimit.i=0)
	
	iw.d = ImageWidth(img)
	ih.d = ImageHeight(img)
	
	simg = CreateImage(#PB_Any,iw,ih,32,bgclr)
	StartDrawing(ImageOutput(simg))
	DrawAlphaImage(ImageID(img),0,0)
	StopDrawing()
	
	If title="" : title="Report Image" : EndIf
	win = OpenWindow(#PB_Any,0,0,iw,ih,title,#PB_Window_BorderLess|#PB_Window_ScreenCentered)
	imgad = ImageGadget(#PB_Any,0,0,iw,ih,ImageID(simg))
	escapekey = 1
	spacekey = 2
	returnkey = 3
	AddKeyboardShortcut(win,#PB_Shortcut_Escape,escapekey)
	AddKeyboardShortcut(win,#PB_Shortcut_Space,spacekey)
	AddKeyboardShortcut(win,#PB_Shortcut_Return,returnkey)
	SetWindowPos_(WindowID(win),#HWND_TOPMOST,0,0,0,0,#SWP_NOMOVE|#SWP_NOSIZE)    ; gets it on top to stay 
	killtime = ElapsedMilliseconds()+timelimit
	Repeat
		we = WindowEvent()
		If we
			If we=#PB_Event_Menu
				Break
			EndIf
		Else
			Delay(10)
		EndIf
		If timelimt>0 And ElapsedMilliseconds()>killtime : Break : EndIf
	ForEver
	CloseWindow(win)
	
	FreeImage(simg)
	
EndProcedure



Structure PointD
  x.d
  y.d
EndStructure


Procedure.b ComputeCubicBezierControlPoints(*anchor1.PointD,*passthrough1.PointD,*passthrough2.PointD,*anchor2.PointD,*controlpoint1.PointD,*controlpoint2.PointD)
  
  ; calculate the control points between (anchor1) and (anchor2) so that the curve will pass through (passthrough1) and (passthrough2)
  
  xc1.d = (*anchor1\x + *passthrough1\x) / 2.0
  yc1.d = (*anchor1\y + *passthrough1\y) / 2.0
  xc2.d = (*passthrough1\x + *passthrough2\x) / 2.0
  yc2.d = (*passthrough1\y + *passthrough2\y) / 2.0
  xc3.d = (*passthrough2\x + *anchor2\x) / 2.0
  yc3.d = (*passthrough2\y + *anchor2\y) / 2.0
  
  len1.d = Sqr( (*passthrough1\x-*anchor1\x) * (*passthrough1\x-*anchor1\x) + (*passthrough1\y-*anchor1\y) * (*passthrough1\y-*anchor1\y) )
  len2.d = Sqr( (*passthrough2\x-*passthrough1\x) * (*passthrough2\x-*passthrough1\x) + (*passthrough2\y-*passthrough1\y) * (*passthrough2\y-*passthrough1\y) )
  len3.d = Sqr( (*anchor2\x-*passthrough2\x) * (*anchor2\x-*passthrough2\x) + (*anchor2\y-*passthrough2\y) * (*anchor2\y-*passthrough2\y) )
  
  k1.d = len1 / (len1 + len2)
  k2.d = len2 / (len2 + len3)
  
  xm1.d = xc1 + (xc2 - xc1) * k1
  ym1.d = yc1 + (yc2 - yc1) * k1
  
  xm2.d = xc2 + (xc3 - xc2) * k2
  ym2.d = yc2 + (yc3 - yc2) * k2
  
  
  ; Resulting control points. Here smooth_value is mentioned above coefficient K whose value should be in range [0...1]
  smooth_value.d = 0.1
  
  *controlpoint1\x = xm1 + (xc2 - xm1) * smooth_value + *passthrough1\x - xm1
  *controlpoint1\y = ym1 + (yc2 - ym1) * smooth_value + *passthrough1\y - ym1
  Debug "Control Point 1: "+StrD(*controlpoint1\x,0)+","+StrD(*controlpoint1\y,0)
  
  *controlpoint2\x = xm2 + (xc2 - xm2) * smooth_value + *passthrough2\x - xm2
  *controlpoint2\y = ym2 + (yc2 - ym2) * smooth_value + *passthrough2\y - ym2
  Debug "Control Point 2: "+StrD(*controlpoint2\x,0)+","+StrD(*controlpoint2\y,0)
  
EndProcedure



Procedure.b RasterizeCubicBezier(*anchor1.PointD,*control1.PointD,*control2.PointD,*anchor2.PointD,NUM_STEPS.i,Array rstpnt.PointD(1))
  
  dx1.d = *control1\x - *anchor1\x
  dy1.d = *control1\y - *anchor1\y
  dx2.d = *control2\x - *control1\x
  dy2.d = *control2\y - *control1\y
  dx3.d = *anchor2\x - *control2\x
  dy3.d = *anchor2\y - *control2\y
  
  subdiv_step.d  = 1.0 / (NUM_STEPS + 1)
  subdiv_step2.d = subdiv_step*subdiv_step
  subdiv_step3.d = subdiv_step*subdiv_step*subdiv_step
  
  pre1.d = 3.0 * subdiv_step
  pre2.d = 3.0 * subdiv_step2
  pre4.d = 6.0 * subdiv_step2
  pre5.d = 6.0 * subdiv_step3
  
  tmp1x.d = *anchor1\x - *control1\x * 2.0 + *control2\x
  tmp1y.d = *anchor1\y - *control1\y * 2.0 + *control2\y
  
  tmp2x.d = (*control1\x - *control2\x)*3.0 - *anchor1\x + *anchor2\x
  tmp2y.d = (*control1\y - *control2\y)*3.0 - *anchor1\y + *anchor2\y
  
  fx.d = *anchor1\x
  fy.d = *anchor1\y
  
  dfx.d = (*control1\x - *anchor1\x)*pre1 + tmp1x*pre2 + tmp2x*subdiv_step3
  dfy.d = (*control1\y - *anchor1\y)*pre1 + tmp1y*pre2 + tmp2y*subdiv_step3
  
  ddfx.d = tmp1x*pre4 + tmp2x*pre5
  ddfy.d = tmp1y*pre4 + tmp2y*pre5
  
  dddfx.d = tmp2x*pre5
  dddfy.d = tmp2y*pre5
  
  ReDim rstpnt(NUM_STEPS+2)
  For p = 1 To NUM_STEPS
    fx + dfx
    fy + dfy
    dfx + ddfx
    dfy + ddfy
    ddfx + dddfx
    ddfy + dddfy
    rstpnt(p)\x = fx
    rstpnt(p)\y = fy
  Next p
  p+1
  rstpnt(p)\x = *anchor2\x
  rstpnt(p)\y = *anchor2\y
  
EndProcedure



iw.d = 1200
ih.d = 800

anchor1.PointD
passthrough1.PointD
passthrough2.PointD
anchor2.PointD
anchor1\x = 0
anchor1\y = ih
passthrough1\x = iw * 0.25
passthrough1\y = ih * 0.975
passthrough2\x = iw * 0.85
passthrough2\y = ih * 0.65
anchor2\x = iw
anchor2\y = 0

ComputeCubicBezierControlPoints(@anchor1,@passthrough1,@passthrough2,@anchor2,@ctrl1.PointD,@ctrl2.PointD)

rpnts = 1000
Dim rpnt.PointD(1)
RasterizeCubicBezier(@anchor1,@ctrl1,@ctrl2,@anchor2,rpnts,rpnt())

img = CreateImage(#PB_Any,iw,ih)
StartDrawing(ImageOutput(img))
For a = 1 To rpnts
  x = rpnt(a)\x
  y = rpnt(a)\y
  Circle(x,y,1,#White)
Next a
Circle(anchor1\x,anchor1\y,10,#Green)
Circle(passthrough1\x,passthrough1\y,10,#Blue)
Circle(passthrough2\x,passthrough2\y,10,#Blue)
Circle(ctrl1\x,ctrl1\y,20,#Yellow)
Circle(ctrl2\x,ctrl2\y,20,#Yellow)
Circle(anchor2\x,anchor2\y,10,#Red)
StopDrawing()
RI(img)
Unfortunately, the bezier curve that results from this code does not pass through either of the arbitrary passthrough points! I don't know what is going wrong. If anyone could help with this, I would be most grateful. The man who wrote the original function explains how it works in detail on the page.

Thank you for your time.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
acreis
Enthusiast
Enthusiast
Posts: 248
Joined: Fri Jun 01, 2012 12:20 am

Re: Compute the control points for an arbitrary bezier curve

Post by acreis »

not too late?

viewtopic.php?t=87744
User avatar
Mindphazer
Enthusiast
Enthusiast
Posts: 506
Joined: Mon Sep 10, 2012 10:41 am
Location: Savoie

Re: Compute the control points for an arbitrary bezier curve

Post by Mindphazer »

6 years after ??? :shock:
MacBook Pro 16" M4 Pro - 24 Gb - MacOS 26.1 - Iphone 17 Pro Max - iPad at home
...and unfortunately... Windows at work...
Post Reply