Basic Univers
; Comtois 21/01/07
; Reprise d'un code en C vite fait, faudra l'améliorer à l'occasion !
; http://fr.wikipedia.org/wiki/Courbe_de_B%C3%A9zier_en_C

InitSprite()
InitKeyboard()
InitMouse()

OpenScreen(800, 600, 32, "Bezier")

Structure Pointf
  x.f
  y.f
EndStructure

Structure Bezier
  P.Pointf[4]
EndStructure

#PointControle = 4

 ;-Variables globales
Global Global_Bezier.Bezier
Global level, Proche, PointControle

Global_Bezier\P[0]\x = 10
Global_Bezier\P[0]\y = 510
Global_Bezier\P[1]\x = 260
Global_Bezier\P[1]\y = 20
Global_Bezier\P[2]\x = 380
Global_Bezier\P[2]\y = 300
Global_Bezier\P[3]\x = 748
Global_Bezier\P[3]\y = 400
level = 5

;-Dessine une ligne de couleur (r,v,b)
Procedure DrawLine( x1.l, y1.l, x2.l, y2.l, r.l, v.l, b.l)
  If StartDrawing(ScreenOutput())
    LineXY(x1, y1, x2, y2, RGB(r, v, b))
    StopDrawing()
  EndIf
EndProcedure

Procedure DrawBezierBase(*p.Bezier, r.l, v.l, b.l)
  DrawLine(*p\P[0]\x, *p\P[0]\y, *p\P[1]\x, *p\P[1]\y, r, v, b)
  DrawLine(*p\P[1]\x, *p\P[1]\y, *p\P[2]\x, *p\P[2]\y, r, v, b)
  DrawLine(*p\P[2]\x, *p\P[2]\y, *p\P[3]\x, *p\P[3]\y, r, v, b)
EndProcedure

Procedure DrawBezierRecursive( *b.Bezier, level.l)
  Define.Bezier left, right
  If level <= 0
    ; Dessine un segment
    DrawLine(*b\P[0]\x + 0.5, *b\P[0]\y + 0.5, *b\P[3]\x + 0.5, *b\P[3]\y + 0.5, 255, 255, 255)
  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
    ; DrawBezierBase(@left,0,255,0);
    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
    ; DrawBezierBase(@right,0,0,255)
    ; draw the 2 segments recursively
    DrawBezierRecursive(@left, level - 1)
    DrawBezierRecursive(@right, level - 1)
  EndIf
EndProcedure

 Procedure DrawBezier()
  DrawBezierBase(@Global_Bezier, 255, 0, 0)
  DrawBezierRecursive(@Global_Bezier, level)
 EndProcedure

Repeat
  ClearScreen(#Black)
 
  ;-Souris
  If ExamineMouse()
    Mx = MouseX()
    My = MouseY()
    ; Test si la souris est proche d'un point de contrôle
    PointControle = - 1
    For i = 0 To 3
      If Mx > Global_Bezier\P[i]\x - #PointControle And Mx < Global_Bezier\P[i]\x + #PointControle
        If My > Global_Bezier\P[i]\y - #PointControle And My < Global_Bezier\P[i]\y + #PointControle
          Proche = i
          PointControle = i
          Break
        EndIf
      EndIf
    Next

    ; La souris est proche d'un point de contrôle
    If MouseButton(#PB_MouseButton_Left) And Proche>- 1
        Global_Bezier\P[Proche]\x = Mx
        Global_Bezier\P[Proche]\y = My
    Else
      Proche = - 1
    EndIf

  EndIf
  ; Calcule et trace la courbe de bézier
  DrawBezier()

  If StartDrawing(ScreenOutput())
    ; Affiche les points de contrôle
    Circle(Global_Bezier\P[0]\x, Global_Bezier\P[0]\y, 3, #Red)
    Circle(Global_Bezier\P[1]\x, Global_Bezier\P[1]\y, 3, #Red)
    Circle(Global_Bezier\P[2]\x, Global_Bezier\P[2]\y, 3, #Red)
    Circle(Global_Bezier\P[3]\x, Global_Bezier\P[3]\y, 3, #Red)
    ; Croix représentant la souris
    LineXY(0, My, 799, My, #Green)
    LineXY(Mx, 0, Mx, 599, #Green)
    ; La souris est proche d'un point de contrôle
    If PointControle>- 1
      DrawingMode(#PB_2DDrawing_Outlined)
      Circle(Mx, My, #PointControle + 2, #Green)
    EndIf
    StopDrawing()
  EndIf
  ExamineKeyboard()
  FlipBuffers()
Until KeyboardPushed(#PB_Key_Escape)