I’m looking for a previously published PureBasic code (or example) that displays a graph with multiple points, where:
• A smooth curve passes through the points
• The points can be dragged interactively with the mouse
• The curve updates dynamically as the points move
Has anyone seen something like this shared before? I’d really appreciate a link or any hints on where to find it. Thanks in advance!
Solved:
Code: Select all
;
; https://www.purebasic.fr/english/viewtopic.php?p=646693#p646693
; https://apoorvaj.io/cubic-bezier-through-four-points
; function vec2_dist(a, b) {
; return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
; }
;
; function refit_bezier(
; passthru_0, passthru_1, passthru_2, passthru_3,
; out_tangent_1, out_tangent_2
; ) {
; let d1 = Math.pow(vec2_dist(passthru_1, passthru_0), alpha);
; let d2 = Math.pow(vec2_dist(passthru_2, passthru_1), alpha);
; let d3 = Math.pow(vec2_dist(passthru_3, passthru_2), alpha);
; // Modify tangent 1
; {
; let a = d1 * d1;
; let b = d2 * d2;
; let c = (2 * d1 * d1) + (3 * d1 * d2) + (d2 * d2);
; let d = 3 * d1 * (d1 + d2);
; out_tangent_1.x = (a * passthru_2.x - b * passthru_0.x + c * passthru_1.x) / d;
; out_tangent_1.y = (a * passthru_2.y - b * passthru_0.y + c * passthru_1.y) / d;
; }
; // Modify tangent 2
; {
; let a = d3 * d3;
; let b = d2 * d2;
; let c = (2 * d3 * d3) + (3 * d3 * d2) + (d2 * d2);
; let d = 3 * d3 * (d3 + d2);
; out_tangent_2.x = (a * passthru_1.x - b * passthru_3.x + c * passthru_2.x) / d;
; out_tangent_2.y = (a * passthru_1.y - b * passthru_3.y + c * passthru_2.y) / d;
; }
;
; }
EnableExplicit
; -Structures
Structure POINTF
x.f
y.f
EndStructure
Structure TREFITTEDBEZIER
*window
*canvas
*text
width.i
height.i
alpha.f
Array p.POINTF(11)
ptMouseMovePoint.POINT
ptMouseDownPoint.POINT
ptInitPosPoint.POINTF
hotpoint.i
smalldistance.f
LButtonDown.i
EndStructure
;- Globals
Global *app.TREFITTEDBEZIER = AllocateStructure(TREFITTEDBEZIER)
;- Macros
Macro CanvasMouseX(_canvas_)
GetGadgetAttribute(_canvas_, #PB_Canvas_MouseX)
EndMacro
Macro CanvasMouseY(_canvas_)
GetGadgetAttribute(_canvas_, #PB_Canvas_MouseY)
EndMacro
Macro vec2_dist(_point_a_, _point_b_)
Sqr(Pow(_point_a_\x - _point_b_\x, 2) + Pow(_point_a_\y - _point_b_\y, 2))
EndMacro
Macro AddPathCirclePT(_point_, _size_ = 8)
AddPathCircle(_point_\x, _point_\y, _size_)
EndMacro
Procedure refit_bezier(*passthru_0.POINTF, *passthru_1.POINTF, *passthru_2.POINTF, *passthru_3.POINTF, alpha.f, *out_tangent_1.POINTF, *out_tangent_2.POINTF)
Define d1.f, d2.f, d3.f
Define a.f, b.f, c.f, d.f
d1 = Pow(vec2_dist(*passthru_1, *passthru_0), alpha)
d2 = Pow(vec2_dist(*passthru_2, *passthru_1), alpha)
d3 = Pow(vec2_dist(*passthru_3, *passthru_2), alpha)
; Modify tangent 1
a = d1 * d1
b = d2 * d2
c = (2 * d1 * d1) + (3 * d1 * d2) + (d2 * d2)
d = 3 * d1 * (d1 + d2)
*out_tangent_1\x = (a * *passthru_2\x - b * *passthru_0\x + c * *passthru_1\x) / d
*out_tangent_1\y = (a * *passthru_2\y - b * *passthru_0\y + c * *passthru_1\y) / d
; Modify tangent 2
a = d3 * d3
b = d2 * d2
c = (2 * d3 * d3) + (3 * d3 * d2) + (d2 * d2)
d = 3 * d3 * (d3 + d2)
*out_tangent_2\x = (a * *passthru_1\x - b * *passthru_3\x + c * *passthru_2\x) / d
*out_tangent_2\y = (a * *passthru_1\y - b * *passthru_3\y + c * *passthru_2\y) / d
EndProcedure
Procedure Bezier_Draw(*passthru_0.POINTF, *passthru_1.POINTF, *passthru_2.POINTF, *passthru_3.POINTF)
Define out_tangent_1.POINTF, out_tangent_2.POINTF
refit_bezier(*passthru_0, *passthru_1, *passthru_2, *passthru_3, *app\alpha, out_tangent_1, out_tangent_2)
VectorSourceColor(RGBA(100, 255, 0, 255))
;AddPathCirclePT(out_tangent_1)
;AddPathCirclePT(out_tangent_2)
VectorSourceColor(RGBA(255, 100, 100, 255))
AddPathCirclePT(*passthru_0)
AddPathCirclePT(*passthru_3)
MovePathCursor(*passthru_1\x,*passthru_1\y)
VectorSourceColor(RGBA(0, 0, 0, 255))
AddPathCurve(out_tangent_1\x, out_tangent_1\y, out_tangent_2\x, out_tangent_2\y, *passthru_2\x, *passthru_2\y)
StrokePath(1)
; Draw original points
AddPathCirclePT(*passthru_1)
AddPathCirclePT(*passthru_2)
VectorSourceColor(RGBA(0, 0, 0, 255))
StrokePath(1)
; Draw tangents
; VectorSourceColor(RGBA(0, 0, 0, 255))
; MovePathCursor(*passthru_1\x,*passthru_1\y)
; AddPathLine(p1\x, p1\y)
; MovePathCursor(passthru_2\x,*passthru_2\y)
; AddPathLine(p2\x, p2\y)
; StrokePath(1)
EndProcedure
Procedure Canvas_Draw()
If StartVectorDrawing(CanvasVectorOutput(*app\Canvas))
With *app
Protected i
VectorSourceColor(RGBA(255,255,255,255))
Protected r.rect
GetClientRect_(GadgetID(\canvas), r)
AddPathBox(0,0, r\right, r\bottom)
FillPath()
If \hotpoint > -1
VectorSourceColor(RGBA(255,0,0,100))
AddPathCirclePT(\p(\hotpoint))
StrokePath(8)
EndIf
Protected n = ArraySize(\p())-3
For i = 0 To n
Bezier_Draw(\p(0+i), \p(1+i), \p(2+i), \p(3+i))
Next
EndWith
StopVectorDrawing()
EndIf
EndProcedure
Procedure Canvas_GetHotPoint()
With *app
\hotpoint = -2
Protected i, n = ArraySize(\p())
For i = 0 To n
If vec2_dist(\ptMouseMovePoint, \p(i)) < \smalldistance
\hotpoint = i
ProcedureReturn #True
EndIf
Next
EndWith
ProcedureReturn #False
EndProcedure
Procedure Canvas_LeftButtonUp()
With *app
\LButtonDown = #False
EndWith
Canvas_Draw()
EndProcedure
Procedure Canvas_LeftButtonDown()
With *app
\LButtonDown = #True
\ptMouseDownPoint\x = CanvasMouseX(\canvas)
\ptMouseDownPoint\y = CanvasMouseY(\canvas)
If Canvas_GetHotPoint()
\ptInitPosPoint = \p(\hotpoint)
EndIf
EndWith
EndProcedure
Procedure Canvas_DragPoint()
With *app
If \hotpoint > -1
\p(\hotpoint)\x = \ptInitPosPoint\x + (\ptMouseMovePoint\x - \ptMouseDownPoint\x)
\p(\hotpoint)\y = \ptInitPosPoint\y + (\ptMouseMovePoint\y - \ptMouseDownPoint\y)
EndIf
EndWith
EndProcedure
Procedure Canvas_MouseMove()
With *app
\ptMouseMovePoint\x = CanvasMouseX(\canvas)
\ptMouseMovePoint\y = CanvasMouseY(\canvas)
SetGadgetText(\text, "" + \ptMouseMovePoint\x + ", " + \ptMouseMovePoint\y )
If \LButtonDown And \hotpoint > -1
Canvas_DragPoint()
Canvas_Draw()
ProcedureReturn
EndIf
Canvas_GetHotPoint()
EndWith
Canvas_Draw()
EndProcedure
Procedure Main()
With *app
\width = 800
\height = 600
\alpha = 0.5
\smalldistance = 8
\hotpoint = -2
\window = OpenWindow(#PB_Any, 100, 100, \width, \height, "Refitted Bezier Curve")
\Canvas = CanvasGadget(#PB_Any, 0, 0, \width, \height, #PB_Canvas_Container)
\Text = TextGadget(#PB_Any,0,0, \width, 16, "")
BindGadgetEvent(\Canvas, @Canvas_MouseMove(), #PB_EventType_MouseMove)
BindGadgetEvent(\Canvas, @Canvas_LeftButtonUp(), #PB_EventType_LeftButtonUp)
BindGadgetEvent(\Canvas, @Canvas_LeftButtonDown(), #PB_EventType_LeftButtonDown)
Protected i, n = ArraySize(\p())
For i = 0 To n
\p(i)\x = i*50 + 50
\p(i)\y = i*40 + 40
Next
Canvas_Draw()
EndWith
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
EndSelect
ForEver
EndProcedure
Main()