Interpolation cubic (bezier..) for drawing or sprite

Just starting out? Need help? Post your questions and find answers here.
User avatar
[blendman]
Enthusiast
Enthusiast
Posts: 297
Joined: Thu Apr 07, 2011 1:14 pm
Location: 3 arks
Contact:

Interpolation cubic (bezier..) for drawing or sprite

Post by [blendman] »

Hi

I would like to know How I can use interpolation (cubic, catmull, bezier..) to "smooth" the line with a 2D painting (it's the same for a game, using sprite to create some ribbon effect for exemple).

Here the code (modified from the canvas example) :

Code: Select all

#IMAGE_Content =0

Enumeration
  #GADGET_Canvas
  #GADGET_Brush
  #GADGET_Clear
EndEnumeration

Global Pas = 2,MouseX_Old,MouseY_Old

; Draw the mouse action result depending on the currently selected mode and event type
Macro point_distance(x1,y1,x2,y2)   
  Int(Sqr((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)) )       
EndMacro
Macro point_direction(x1,y1,x2,y2)
  ATan2((y2- y1),(x2- x1))
EndMacro

Procedure DrawBrush(x1,y1)
  x2 = MouseX_Old
  y2 = MouseY_Old
  flech = point_distance(x1,y1,x2,y2)
  d.d = point_direction(x1,y1,x2,y2)
 
  For i = 1 To flech   
    i+pas
    x_result = x1 + Sin(d) *i
    y_result = y1 + Cos(d) *i
    Circle(x_result,y_result,3,0)
    ;DrawImage(ImageID(#IMAGE_Brush),x_result-brush\size/2,y_result-brush\size/2)
  Next i
EndProcedure

Procedure DrawAction(x, y, EventType)
  If StartDrawing(CanvasOutput(#GADGET_Canvas))
    If EventType = #PB_EventType_LeftButtonDown Or EventType = #PB_EventType_MouseMove
      DrawBrush(x,y)
    EndIf
    StopDrawing()
  EndIf
EndProcedure

CreateImage(#IMAGE_Content, 380, 380, 24)

If OpenWindow(0, 0, 0, 800, 600, "Interpolation", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(#GADGET_Canvas, 10, 10, WindowWidth(0)-80, WindowHeight(0)-20, #PB_Canvas_ClipMouse)
  ButtonGadget(#GADGET_Clear,  WindowWidth(0)-60, 100, 50, 25, "Clear")
  SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Cursor, #PB_Cursor_Cross)
 
  Repeat
    Event = WaitWindowEvent()
   
    If Event = #PB_Event_Gadget
     
      Select EventGadget()
         
        Case #GADGET_Canvas
          X = GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_MouseX)
          Y = GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_MouseY)
          Type = EventType()
         
          Select EventType()
             
            Case #PB_EventType_LeftButtonDown
              If StartDrawing(ImageOutput(#IMAGE_Content))
                DrawImage(GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Image), 0, 0)
                StopDrawing()
              EndIf
             
              MouseY_Old = y
              MouseX_Old = x
              DrawAction(X, Y, EventType())
             
             
            Case #PB_EventType_LeftButtonUp
              DrawAction(X, Y, EventType())                       
             
            Case #PB_EventType_MouseMove
              If GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton
                DrawAction(X, Y, EventType())
                MouseY_Old = y
                MouseX_Old = x
              EndIf
             
          EndSelect
         
        Case #GADGET_Clear
          If StartDrawing(CanvasOutput(#GADGET_Canvas))
            Box(0, 0, GadgetWidth(#GADGET_Canvas), GadgetHeight(#GADGET_Canvas), $FFFFFF)
            StopDrawing()
          EndIf
         
         
      EndSelect
     
    EndIf
   
  Until Event = #PB_Event_CloseWindow

EndIf
The "line create are not "smoothed" or curved. I have find a lot of example (pb source) for bezier, lagrange or other interpolation, but I don't know how I can adapt them with this sort of code.

If you have an idea, it would be very great :).

Thank you.

EDIT :
I have the left Result, and I would like to have the right result for example :
Image
gnasen
Enthusiast
Enthusiast
Posts: 282
Joined: Wed Sep 24, 2008 12:21 am

Re: Interpolation cubic (bezier..) for drawing or sprite

Post by gnasen »

I did this some time ago, after a short search I found it:
post from the past
The theory behind it is pretty easy, however I used a quick'n'dirty solver (gauss without any factorization) which got unstable for too much points. So if you write/find a better solver for linear equations it wont be a problem to implement it.
For a better solver you need some kind of factorization: LU / QR / SVD are possible candidates.
pb 5.11
User avatar
[blendman]
Enthusiast
Enthusiast
Posts: 297
Joined: Thu Apr 07, 2011 1:14 pm
Location: 3 arks
Contact:

Re: Interpolation cubic (bezier..) for drawing or sprite

Post by [blendman] »

Hi

Thanks for the link.
With a code from Comtois, I have made that, but I d'ont know if I have to use the code like that :

Code: Select all

;{ constantes, enum,
#IMAGE_Content =0

Enumeration
  #GADGET_Canvas
  #GADGET_Brush
  #GADGET_Clear
EndEnumeration

;}

;{ structure
Structure Pointf
  x.f
  y.f
EndStructure

Structure Bezier
  P.Pointf[4]
EndStructure

Global Global_Bezier.Bezier, level.a =5
Global Pas=2,point0.point,point1.point,point2.point,point3.point
;}


;{ macros et procedures
Macro point_distance(x1,y1,x2,y2)   
  Int(Sqr((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)) )       
EndMacro
Macro point_direction(x1,y1,x2,y2)
  ATan2((y2- y1),(x2- x1))
EndMacro


Procedure DrawBezierRecursive ( *b.Bezier, level.l)
  Define.Bezier left, right
  If level <= 0;   
     Circle(*b\P[0]\x , *b\P[0]\y ,5, 0) ;Dessine un segment
   Else
    ;subdivide into 2 Bezier segments
    left\P[0]\x = *b\P[0]\x
    left\P[0]\y = *b\P[0]\y
    left\P[1]\x = (*b\P[0]\x + *b\P[1]\x) / 2
    left\P[1]\y = (*b\P[0]\y + *b\P[1]\y) / 2
    left\P[2]\x = (*b\P[0]\x + 2**b\P[1]\x + *b\P[2]\x) / 4
    left\P[2]\y = (*b\P[0]\y + 2**b\P[1]\y + *b\P[2]\y) / 4
    left\P[3]\x = (*b\P[0]\x + 3**b\P[1]\x + 3**b\P[2]\x + *b\P[3]\x) / 8
    left\P[3]\y = (*b\P[0]\y + 3**b\P[1]\y + 3**b\P[2]\y + *b\P[3]\y) / 8
   
    right\P[0]\x = left\P[3]\x;
    right\P[0]\y = left\P[3]\y;
    right\P[1]\x = (*b\P[1]\x + 2**b\P[2]\x + *b\P[3]\x) / 4
    right\P[1]\y = (*b\P[1]\y + 2**b\P[2]\y + *b\P[3]\y) / 4
    right\P[2]\x = (*b\P[2]\x + *b\P[3]\x) / 2
    right\P[2]\y = (*b\P[2]\y + *b\P[3]\y) / 2
    right\P[3]\x = *b\P[3]\x
    right\P[3]\y = *b\P[3]\y
    ;draw the 2 segments recursively
    DrawBezierRecursive (@left,level -1)
    DrawBezierRecursive (@right,level -1)
  EndIf
EndProcedure

Procedure DrawBrush(x1,y1)
  x2 = point1\x
  y2 = point1\y
  flech = point_distance(x1,y1,x2,y2)
  d.d = point_direction(x1,y1,x2,y2)
 
     

  For i = 1 To flech   
    i+pas
    x_result = x1 + Sin(d) *i
    y_result = y1 + Cos(d) *i
    Global_Bezier\P[0]\x = x_result
    Global_Bezier\P[0]\y = y_result
    DrawBezierRecursive(@Global_Bezier,level) ; bézier bug...
    ;Circle(x_result,y_result,3,0) ; normal, ok
  Next i
EndProcedure

Procedure DrawAction(x, y, EventType)
  If StartDrawing(CanvasOutput(#GADGET_Canvas))
    If EventType = #PB_EventType_LeftButtonDown Or EventType = #PB_EventType_MouseMove
      DrawBrush(x,y)
    EndIf
    StopDrawing()
  EndIf
EndProcedure



;}

;{ window & loop
CreateImage(#IMAGE_Content, 800, 600, 24)

If OpenWindow(0, 0, 0, 800, 600, "Interpolation", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(#GADGET_Canvas, 10, 10, WindowWidth(0)-80, WindowHeight(0)-20, #PB_Canvas_ClipMouse)
  ButtonGadget(#GADGET_Clear,  WindowWidth(0)-60, 100, 50, 25, "Clear")
  SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Cursor, #PB_Cursor_Cross)
 
  Repeat
    Event = WaitWindowEvent()
   
    If Event = #PB_Event_Gadget
     
      Select EventGadget()
         
        Case #GADGET_Canvas
          point0\X = GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_MouseX)
          point0\Y = GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_MouseY)
          Global_Bezier\P[0]\x = point0\x
          Global_Bezier\P[0]\y = point0\y
          Type = EventType()
         
          Select EventType()
             
            Case #PB_EventType_LeftButtonDown
              If StartDrawing(ImageOutput(#IMAGE_Content))
                DrawImage(GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Image), 0, 0)
                StopDrawing()
              EndIf
             
             
              point3\x = point2\x
              point3\y = point2\y
              point2\x = point1\x
              point2\y = point1\y
              point1\x = point0\x
              point1\y = point0\y
 
              Global_Bezier\P[1]\x = point1\x
              Global_Bezier\P[1]\y = point1\y
              Global_Bezier\P[2]\x = point2\x
              Global_Bezier\P[2]\y = point2\y
              Global_Bezier\P[3]\x = point3\x
              Global_Bezier\P[3]\y = point3\y
             
              DrawAction(point0\X, point0\Y, EventType())
             
             
            Case #PB_EventType_LeftButtonUp
              DrawAction(point0\X, point0\Y, EventType())                       
             
            Case #PB_EventType_MouseMove
              If GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton
                Global_Bezier\P[0]\x = point0\x
                Global_Bezier\P[0]\y = point0\y
                DrawAction(point0\X, point0\Y, EventType())
               
                point3\x = point2\x
                point3\y = point2\y
                point2\x = point1\x
                point2\y = point1\y
                point1\x = point0\x
                point1\y = point0\y
               
               
                Global_Bezier\P[1]\x = point1\x
                Global_Bezier\P[1]\y = point1\y
                Global_Bezier\P[2]\x = point2\x
                Global_Bezier\P[2]\y = point2\y
                Global_Bezier\P[3]\x = point3\x
                Global_Bezier\P[3]\y = point3\y
               
              EndIf
             
          EndSelect
         
        Case #GADGET_Clear
          If StartDrawing(CanvasOutput(#GADGET_Canvas))
            Box(0, 0, GadgetWidth(#GADGET_Canvas), GadgetHeight(#GADGET_Canvas), $FFFFFF)
            StopDrawing()
          EndIf
         
         
      EndSelect
     
    EndIf
   
  Until Event = #PB_Event_CloseWindow
 
EndIf
;}
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Interpolation cubic (bezier..) for drawing or sprite

Post by Danilo »

@[blendman]:
- Quick And Simple Bezier Curve Drawing

The code has procedures for Bezier interpolation.

The following code shows the interpolation. Add new points with a left button click @ canvas gadget.

Code: Select all

;
;-------------------------------------------------------------------------------------------
;
; Graphics Gems 5, Edited by Alan W. Paeth
;
; http://www.amazon.com/Graphics-Version-Morgan-Kaufmann-Computer/dp/0125434553/
;
; Chapter IV.8  -  Quick And Simple Bezier Curve Drawing
;
; By Robert D. Miller
;

;
; 2D version
;
Structure BezierPOINTF2D
    x.d
    y.d
EndStructure


Macro PtArray2D(_var_,_size_)
   Dim _var_.BezierPOINTF2D(_size_)
EndMacro

Macro BezArray2D(_var_,_size_)
    Dim _var_.BezierPOINTF2D(_size_)
EndMacro


Procedure BezierForm2D(NumCtlPoints, Array p.BezierPOINTF2D(1), Array c.BezierPOINTF2D(1))
    ;
    ; Setup Bezier coefficient array once for each control polygon
    ;
    Protected  k, choose.d
    Protected.i n = NumCtlPoints-1
    
    For k = 0 To n

        If     k = 0 : choose = 1
        ElseIf k = 1 : choose = n
        Else         : choose = choose * (n-k+1)/(k*1.0)
        EndIf
        
        c(k)\x = p(k)\x * choose
        c(k)\y = p(k)\y * choose

    Next k
EndProcedure


Procedure BezierCurve2D(NumCtlPoints, Array c.BezierPOINTF2D(1), *pt.BezierPOINTF2D, t.d)
    ;
    ; Return *pt.BezierPOINTF2D, t <= 0 <= 1,
    ; given the number of Points in control polygon,
    ; BezierForm2D must be called once for any given control polygon
    ;
    Protected.i k, n
    Protected.d t1, tt, u
    
    BezArray2D(b,NumCtlPoints)

    n = NumCtlPoints - 1
    u = t
    
    b(0)\x = c(0)\x
    b(0)\y = c(0)\y

    For k = 1 To n

        b(k)\x = c(k)\x * u
        b(k)\y = c(k)\y * u
    
        u = u * t

    Next k

    *pt\x = b(n)\x
    *pt\y = b(n)\y
    t1    = 1-t
    tt    = t1
    
    k = n-1
    While k >= 0

        *pt\x + ( b(k)\x * tt )
        *pt\y + ( b(k)\y * tt )
    
        tt = tt * t1
        
        k-1
    Wend
EndProcedure
;
;-------------------------------------------------------------------------------------------
;

Procedure DrawBezierCurveArray(Array pn.BezierPOINTF2D(1), stepSize, color.q=-1)
    Protected.BezierPOINTF2D pt, oldPt
    Protected k
    Protected numPoints = ArraySize(pn())

    BezArray2D(bc,numPoints)

    BezierForm2D(numPoints,pn(),bc())

    For k = 0 To stepSize

        BezierCurve2D(numPoints,bc(),@pt, k/(stepSize*1.0))
        
        If k = 0
            oldPt = pt
        Else
            If color = -1 : LineXY(oldPt\x,oldPt\y,pt\x,pt\y)
            Else          : LineXY(oldPt\x,oldPt\y,pt\x,pt\y,color)
            EndIf
            oldPt = pt
        EndIf

    Next k

EndProcedure

;
;-------------------------------------------------------------------------------------------
;


#IMAGE_Content =0

Enumeration
  #GADGET_Canvas
  #GADGET_Brush
  #GADGET_Clear
EndEnumeration

Global NewList points.POINT()

Procedure DrawStraight()
        FirstElement( points() )
        currentX = points()\x
        currentY = points()\y

        ForEach points()
            LineXY(currentX, currentY, points()\x, points()\y,RGB($00,$FF,$FF))
            currentX = points()\x
            currentY = points()\y
        Next
EndProcedure


Procedure Draw()
    If ListSize( points() )
        Dim pts.BezierPOINTF2D( ListSize( points() ) )
        i = 0
        ForEach points()
            pts(i)\x = points()\x
            pts(i)\y = points()\y
            i+1
        Next
        
                
        If StartDrawing( CanvasOutput(#GADGET_Canvas) )
            Box(0, 0, GadgetWidth(#GADGET_Canvas), GadgetHeight(#GADGET_Canvas), $FFFFFF)
            DrawStraight()
            DrawBezierCurveArray(pts(),50,RGB($00,$00,$00))
            StopDrawing()
        EndIf
    EndIf
EndProcedure

If OpenWindow(0, 0, 0, 800, 600, "Interpolation", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(#GADGET_Canvas, 10, 10, WindowWidth(0)-80, WindowHeight(0)-20, #PB_Canvas_ClipMouse)
  ButtonGadget(#GADGET_Clear,  WindowWidth(0)-60, 100, 50, 25, "Clear")
  SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Cursor, #PB_Cursor_Cross)
 
  Repeat
    Event = WaitWindowEvent()
   
    If Event = #PB_Event_Gadget
     
      Select EventGadget()
         
        Case #GADGET_Canvas
          Type = EventType()
         
          Select EventType()
            Case #PB_EventType_LeftClick
              AddElement( points() )
              points()\x = GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_MouseX)
              points()\y = GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_MouseY)
              Draw()
          EndSelect
         
        Case #GADGET_Clear
          If StartDrawing(CanvasOutput(#GADGET_Canvas))
            Box(0, 0, GadgetWidth(#GADGET_Canvas), GadgetHeight(#GADGET_Canvas), $FFFFFF)
            StopDrawing()
          EndIf
          ClearList( points() )
         
         
      EndSelect
     
    EndIf
   
  Until Event = #PB_Event_CloseWindow

EndIf
For drawing with pens I recommend gDrawing ;)
DarkDragon
Addict
Addict
Posts: 2344
Joined: Mon Jun 02, 2003 9:16 am
Location: Germany
Contact:

Re: Interpolation cubic (bezier..) for drawing or sprite

Post by DarkDragon »

Here are also some explanations:
http://www.purebasic.fr/english/viewtop ... 16#p332116

Btw I would recommend using cubic relaxed spline interpolation instead of just cubic spline interpolation. They are more "natural".
http://www.math.ucla.edu/~baker/149.1.0 ... plines.pdf
bye,
Daniel
User avatar
[blendman]
Enthusiast
Enthusiast
Posts: 297
Joined: Thu Apr 07, 2011 1:14 pm
Location: 3 arks
Contact:

Re: Interpolation cubic (bezier..) for drawing or sprite

Post by [blendman] »

Hi

Thank You Danilo for your example, it's very interesting.

If I add a sort of "vector line" tool in my application, I will probably use your code ;).
But for my actual tool (bitmap brush), I don't thing it's possible to use it easily, because I paint/draw with brush (drawimage()) on an image layer.
I don't want to add point , but I want to paint on the surface ;).

Here is the Tool I'm developping (to use it for my game) :
http://animatoon-blendman.blogspot.fr/

https://vimeo.com/45195243

Only windows version for the moment, but if you want to compile it (win, mac or linux), tell me and I send you the sources (which not great , but really simple to understand :))
http://dracaena-studio.com/animatoon/do ... 51_win.zip

Thank you again !
marc_256
Addict
Addict
Posts: 835
Joined: Thu May 06, 2010 10:16 am
Location: Belgium
Contact:

Re: Interpolation cubic (bezier..) for drawing or sprite

Post by marc_256 »

Hi Blendman,

Here I made a .exe file for test my software for Bezier-Curve.
Please test it if you like it.
And please give some advise ... if needed.

How to use:
Left Mouse Button Down + Mouse Move = Move the selected bezier point
Right Mouse Button Click = Add New point after selected point (not 100% ok till now, working on it)
Middle Mouse Button Down + Mouse Move = Move the entire bezier
Mouse Wheel Up/Down = Change bezier angle 1.0° to 6.0°


Program made with WinXP and PB4.61Final
http://www.marc-systems.be/PureBasic/PV ... e_0012.exe

Have fun,
Marc,
- every professional was once an amateur - greetings from Pajottenland - Belgium -
PS: sorry for my english I speak flemish ...
Post Reply