That's a good idea.
I am too lazy now to calculate that angle, but I added a Debug message that contains a hint.
Code: Select all
; tested with PB 5.42 beta 2 x64 on Windows 10
EnableExplicit
Structure PointD
x.d
y.d
EndStructure
Procedure DeCasteljau (x0.d, y0.d, x1.d, y1.d, x2.d, y2.d, x3.d, y3.d, t.d, *p.PointD)
; -- De Casteljau's algorithm
; <http://stackoverflow.com/questions/14174252/how-to-find-out-y-coordinate-of-specific-point-in-bezier-curve-in-canvas>, 2016-02-22
; in : x0, y0, x1, y1, x2, y2, x3, y3: see PB's AddPathCurve()
; (x0, y0 = current position of the cursor)
; t: rational number from the interval [0.0; 1.0]
; out: *p: Coordinates of the wanted point
Protected.d Ax, Ay, Bx, By, Cx, Cy, Dx, Dy, Ex, Ey
Ax = (1.0 - t) * x0 + t * x1
Ay = (1.0 - t) * y0 + t * y1
Bx = (1.0 - t) * x1 + t * x2
By = (1.0 - t) * y1 + t * y2
Cx = (1.0 - t) * x2 + t * x3
Cy = (1.0 - t) * y2 + t * y3
Dx = (1.0 - t) * Ax + t * Bx
Dy = (1.0 - t) * Ay + t * By
Ex = (1.0 - t) * Bx + t * Cx
Ey = (1.0 - t) * By + t * Cy
*p\x = (1.0 - t) * Dx + t * Ex
*p\y = (1.0 - t) * Dy + t * Ey
EndProcedure
Procedure.d Distance (*p0.PointD, *p1.PointD)
; in : Coordinates of two points
; out: Distance between both points
Protected.d dx, dy
dx = *p1\x - *p0\x
dy = *p1\y - *p0\y
ProcedureReturn Sqr(dx*dx + dy*dy)
EndProcedure
Procedure MidPoint (x0.d, y0.d, x1.d, y1.d, x2.d, y2.d, x3.d, y3.d, n.i, *m.PointD)
; in : x0, y0, x1, y1, x2, y2, x3, y3: see PB's AddPathCurve()
; (x0, y0 = current position of the cursor)
; n: number of line segments for approximation
; (the higher this number, the more precise will be the result)
; out: *m: Coordinates of the midpoint of the curve
Protected i.i, t.d, t0.d, t1.d, halfLen.d=0.0, temp.d=0.0
Protected p0.PointD, p1.PointD
; get approximately half the length of the curve
DeCasteljau(x0, y0, x1, y1, x2, y2, x3, y3, 0.0, @p0)
For i = 1 To n
t = i / n
DeCasteljau(x0, y0, x1, y1, x2, y2, x3, y3, t, @p1)
halfLen + Distance(@p0, @p1)
CopyStructure(@p1, @p0, PointD)
Next
halfLen / 2.0
; get the segment where the midpoint of the length of the curve is located
DeCasteljau(x0, y0, x1, y1, x2, y2, x3, y3, 0.0, @p0)
For i = 1 To n
t = i / n
DeCasteljau(x0, y0, x1, y1, x2, y2, x3, y3, t, @p1)
temp + Distance(@p0, @p1)
If temp > halfLen
t0 = (i - 1) / n
t1 = t
Break
EndIf
CopyStructure(@p1, @p0, PointD)
Next
; calculate approximately the coordinates of the midpoint
*m\x = p0\x + (p1\x - p0\x) / 2
*m\y = p0\y + (p1\y - p0\y) / 2
Debug "The midpoint is between t0 = " + StrD(t0, 2) + " and t1 = " + StrD(t1, 2) + "."
Debug "It is approximately located at Mx = " + StrD(*m\x, 2) + ", My = " + StrD(*m\y, 2) + "."
Debug "(Use p0 and p1 together with Atan2() to get the angle of the segment that contains the midpoint.)"
EndProcedure
; -- Demo
Define.d x0, y0, x1, y1, x2, y2, x3, y3
Define.i i
Define p.PointD
x0 = 50 : y0 = 100
x1 = 90 : y1 = 30
x2 = 250 : y2 = 180
x3 = 350 : y3 = 100
OpenWindow(0, 0, 0, 400, 200, "Midpoint of bezier curve", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(0, 0, 0, 400, 200)
; AddPathCurve(): red line
If StartVectorDrawing(CanvasVectorOutput(0))
MovePathCursor(x0, y0)
AddPathCurve(x1, y1, x2, y2, x3, y3)
VectorSourceColor(RGBA(255, 0, 0, 255))
StrokePath(10)
StopVectorDrawing()
EndIf
; DeCasteljau() for t = 0.5: blue circle
DeCasteljau(x0, y0, x1, y1, x2, y2, x3, y3, 0.5, @p)
If StartVectorDrawing(CanvasVectorOutput(0))
AddPathCircle(p\x, p\y, 5)
VectorSourceColor(RGBA(0, 0, 255, 255))
StrokePath(2)
StopVectorDrawing()
EndIf
; Midpoint of the curve: green circle
MidPoint(x0, y0, x1, y1, x2, y2, x3, y3, 10, @p)
If StartVectorDrawing(CanvasVectorOutput(0))
AddPathCircle(p\x, p\y, 5)
VectorSourceColor(RGBA(0, 255, 0, 255))
StrokePath(2)
StopVectorDrawing()
EndIf
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow