Bitmap/Bézier curves/Cubic

Revision as of 23:02, 18 February 2009 by rosettacode>ShinTakezou (fortran)

Using the data storage type defined on this page for raster images, and the draw_line function defined in this other one, draw a cubic bezier curves (definition on Wikipedia).

Task
Bitmap/Bézier curves/Cubic
You are encouraged to solve this task according to the task description, using any language you may know.


Ada

<lang ada> procedure Cubic_Bezier

         (  Picture        : in out Image;
            P1, P2, P3, P4 : Point;
            Color          : Pixel;
            N              : Positive := 20
         )  is
  Points : array (0..N) of Point;

begin

  for I in Points'Range loop
     declare
        T : constant Float := Float (I) / Float (N);
        A : constant Float := (1.0 - T)**3;
        B : constant Float := 3.0 * T * (1.0 - T)**2;
        C : constant Float := 3.0 * T**2 * (1.0 - T);
        D : constant Float := T**3;
     begin
        Points (I).X := Positive (A * Float (P1.X) + B * Float (P2.X) + C * Float (P3.X) + D * Float (P4.X));
        Points (I).Y := Positive (A * Float (P1.Y) + B * Float (P2.Y) + C * Float (P3.Y) + D * Float (P4.Y));
     end;
  end loop;
  for I in Points'First..Points'Last - 1 loop
     Line (Picture, Points (I), Points (I + 1), Color);
  end loop;

end Cubic_Bezier; </lang> The following test <lang ada>

  X : Image (1..16, 1..16);

begin

  Fill (X, White);
  Cubic_Bezier (X, (16, 1), (1, 4), (3, 16), (15, 11), Black);
  Print (X);

</lang> should produce output:





       HH
     HH  HH
    H      H
    H      H
   H       H
  H        H
 H         H
 H         H
 H         H
 H         H
H         H
H

C

<lang c>#include <math.h>

/* number of segments for the curve */

  1. define N_SEG 20
  1. define plot(x, y) put_pixel_clip(img, x, y, r, g, b)
  2. define line(x0,y0,x1,y1) draw_line(img, x0,y0,x1,y1, r,g,b)

void cubic_bezier(

      	image img,
       unsigned int x1, unsigned int y1,
       unsigned int x2, unsigned int y2,
       unsigned int x3, unsigned int y3,
       unsigned int x4, unsigned int y4,
       color_component r,
       color_component g,
       color_component b )

{

   unsigned int i;
   double pts[N_SEG+1][2];
   for (i=0; i <= N_SEG; ++i)
   {
       double t = (double)i / (double)N_SEG;
       double a = pow((1.0 - t), 3.0);
       double b = 3.0 * t * pow((1.0 - t), 2.0);
       double c = 3.0 * pow(t, 2.0) * (1.0 - t);
       double d = pow(t, 3.0);
       double x = a * x1 + b * x2 + c * x3 + d * x4;
       double y = a * y1 + b * y2 + c * y3 + d * y4;
       pts[i][0] = x;
       pts[i][1] = y;
   }

  1. if 0
   /* draw only points */
   for (i=0; i <= N_SEG; ++i)
   {
       plot( pts[i][0],
             pts[i][1] );
   }
  1. else
   /* draw segments */
   for (i=0; i < N_SEG; ++i)
   {
       int j = i + 1;

line( pts[i][0], pts[i][1],

             pts[j][0], pts[j][1] );
   }
  1. endif

}

  1. undef plot
  2. undef line</lang>

Fortran

Translation of: C

This subroutine should go inside the RCImagePrimitive module (see Bresenham's line algorithm)

<lang fortran> subroutine cubic_bezier(img, p1, p2, p3, p4, color)

   type(rgbimage), intent(inout) :: img
   type(point), intent(in) :: p1, p2, p3, p4
   type(rgb), intent(in) :: color
   integer :: i, j
   real :: pts(0:N_SEG,0:1), t, a, b, c, d, x, y
   do i = 0, N_SEG
      t = real(i) / real(N_SEG)
      a = (1.0 - t)**3.0
      b = 3.0 * t * (1.0 - t)**2.0
      c = 3.0 * (1.0 - t) * t**2.0
      d = t**3.0
      x = a * p1%x + b * p2%x + c * p3%x + d * p4%x
      y = a * p1%y + b * p2%y + c * p3%y + d * p4%y
      pts(i,0) = x
      pts(i,1) = y
   end do
   do i = 0, N_SEG-1
      j = i + 1
      call draw_line(img, point(pts(i,0), pts(i,1)), &
                     point(pts(j,0), pts(j,1)), color)
   end do
 end subroutine cubic_bezier</lang>

OCaml

<lang ocaml>let cubic_bezier ~img ~color

       ~p1:(_x1, _y1)
       ~p2:(_x2, _y2)
       ~p3:(_x3, _y3)
       ~p4:(_x4, _y4) =
 let x1, y1, x2, y2, x3, y3, x4, y4 =
   (float _x1, float _y1,
    float _x2, float _y2,
    float _x3, float _y3,
    float _x4, float _y4)
 in
 let bz t =
   let a = (1.0 -. t) ** 3.0
   and b = 3.0 *. t *. ((1.0 -. t) ** 2.0)
   and c = 3.0 *. (t ** 2.0) *. (1.0 -. t)
   and d = t ** 3.0
   in
   let x = a *. x1 +. b *. x2 +. c *. x3 +. d *. x4
   and y = a *. y1 +. b *. y2 +. c *. y3 +. d *. y4
   in
   (int_of_float x, int_of_float y)
 in
 let rec loop _t acc =
   if _t > 20 then acc else
   begin
     let t = (float _t) /. 20.0 in
     let x, y = bz t in
     loop (succ _t) ((x,y)::acc)
   end
 in
 let pts = loop 0 [] in
 (*
 (* draw only points *)
 List.iter (fun (x, y) -> put_pixel img color x y) pts;
 *)
 (* draw segments *)
 let line = draw_line ~img ~color in
 let by_pair li f =
   let rec aux prev = function
     | [] -> ()
     | x::xs ->
         f prev x;
         aux x xs
   in
   aux (List.hd li) (List.tl li)
 in
 by_pair pts (fun p0 p1 -> line ~p0 ~p1);
</lang>