From ddda896646b493a3d3398af19dab0032575e90dd Mon Sep 17 00:00:00 2001 From: Nangah Amandine Chi Date: Tue, 10 Oct 2023 02:59:58 +0100 Subject: [PATCH 1/6] Normalized to a co-ordinate system with (0, 0) at the center --- lib/shape.ml | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/shape.ml b/lib/shape.ml index 2e4f5b0..ef8b498 100644 --- a/lib/shape.ml +++ b/lib/shape.ml @@ -3,15 +3,15 @@ open Graphics type point = { x : int; y : int } type rectangle = { c : point; length : int; width : int } type circle = { c : point; radius : int } -type ellipse = {c : point; rx : int; ry: int} +type ellipse = { c : point; rx : int; ry: int } type shape = Circle of circle | Rectangle of rectangle | Ellipse of ellipse type shapes = shape list let dimensions = ref {x = 500; y = 500} let set_dimensions x y = - dimensions := {x;y} + dimensions := {x; y} -let canvas_mid = { x = (!dimensions.x / 2); y = (!dimensions.y / 2)} +let canvas_center = ref {x = 0; y = 0} let axes_flag = ref false let draw_axes flag = @@ -19,39 +19,41 @@ let draw_axes flag = let render_axes () = set_color (rgb 192 192 192); - moveto (size_x () / 2) 0; - lineto (size_x () / 2) (size_y ()); - moveto 0 (size_y () / 2); - lineto (size_x ()) (size_y () / 2) + moveto (!dimensions.x / 2) 0; + lineto (!dimensions.x / 2) (!dimensions.y); + moveto 0 (!dimensions.y / 2); + lineto (!dimensions.x) (!dimensions.y / 2) let render_shape s = match s with - | Circle circle -> draw_circle circle.c.x circle.c.y circle.radius + | Circle circle -> draw_circle (circle.c.x + !canvas_center.x) (-circle.c.y + !canvas_center.y) circle.radius | Rectangle rectangle -> - draw_rect rectangle.c.x rectangle.c.y rectangle.length rectangle.width + let c = rectangle.c in + draw_rect (c.x + !canvas_center.x - (rectangle.length / 2)) (-c.y + !canvas_center.y - (rectangle.width / 2)) rectangle.length rectangle.width | Ellipse ellipse -> - draw_ellipse ellipse.c.x ellipse.c.y ellipse.rx ellipse.ry + let c = ellipse.c in + draw_ellipse (c.x + !canvas_center.x) (-c.y + !canvas_center.y) ellipse.rx ellipse.ry let circle ?x ?y r = match (x, y) with | Some x, Some y -> Circle { c = { x; y }; radius = r } - | _ -> Circle { c = { x = canvas_mid.x; y = canvas_mid.y }; radius = r } + | _ -> Circle { c = { x = 0; y = 0 }; radius = r } let rectangle ?x ?y length width = match (x, y) with | Some x, Some y -> Rectangle { c = { x; y }; length; width } - | _ -> Rectangle { c = { x = canvas_mid.x; y = canvas_mid.y }; length; width } + | _ -> Rectangle { c = { x = 0; y = 0 }; length; width } let ellipse ?x ?y rx ry = match (x, y) with - | Some x, Some y -> Ellipse {c = {x; y}; rx; ry} - | _ -> Ellipse {c = { x = canvas_mid.x; y = canvas_mid.y}; rx; ry} + | Some x, Some y -> Ellipse { c = { x; y }; rx; ry } + | _ -> Ellipse { c = { x = 0; y = 0 }; rx; ry } - let translate dx dy shape = - match shape with - | Circle circle -> Circle { circle with c = { x = circle.c.x + dx; y = circle.c.y + dy } } - | Rectangle rectangle -> Rectangle { rectangle with c = { x = rectangle.c.x + dx; y = rectangle.c.y + dy } } - | Ellipse ellipse -> Ellipse { ellipse with c = { x = ellipse.c.x + dx; y = ellipse.c.y + dy } } +let translate dx dy shape = + match shape with + | Circle circle -> Circle { circle with c = { x = circle.c.x + dx; y = circle.c.y + dy } } + | Rectangle rectangle -> Rectangle { rectangle with c = { x = rectangle.c.x + dx; y = rectangle.c.y + dy } } + | Ellipse ellipse -> Ellipse { ellipse with c = { x = ellipse.c.x + dx; y = ellipse.c.y + dy } } let show shapes = List.iter render_shape shapes From 6309a49dc4408db9400db9fb4a04474ee6092da2 Mon Sep 17 00:00:00 2001 From: Nangah Amandine Chi Date: Wed, 11 Oct 2023 07:05:17 +0100 Subject: [PATCH 2/6] Updates based on the requested changes --- lib/shape.ml | 63 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/lib/shape.ml b/lib/shape.ml index ef8b498..b25c48a 100644 --- a/lib/shape.ml +++ b/lib/shape.ml @@ -3,65 +3,76 @@ open Graphics type point = { x : int; y : int } type rectangle = { c : point; length : int; width : int } type circle = { c : point; radius : int } -type ellipse = { c : point; rx : int; ry: int } +type ellipse = { c : point; rx : int; ry : int } type shape = Circle of circle | Rectangle of rectangle | Ellipse of ellipse type shapes = shape list -let dimensions = ref {x = 500; y = 500} -let set_dimensions x y = - dimensions := {x; y} +let dimensions = ref { x = 500; y = 500 } +let set_dimensions x y = + dimensions := { x; y } -let canvas_center = ref {x = 0; y = 0} +let canvas_center = ref { x = 0; y = 0 } let axes_flag = ref false -let draw_axes flag = +let draw_axes flag = axes_flag := flag -let render_axes () = +let render_axes () = set_color (rgb 192 192 192); moveto (!dimensions.x / 2) 0; lineto (!dimensions.x / 2) (!dimensions.y); moveto 0 (!dimensions.y / 2); lineto (!dimensions.x) (!dimensions.y / 2) +let denormalize x y = + { x = x + !canvas_center.x; y = -y + !canvas_center.y } + let render_shape s = match s with - | Circle circle -> draw_circle (circle.c.x + !canvas_center.x) (-circle.c.y + !canvas_center.y) circle.radius + | Circle circle -> + let c = denormalize circle.c.x circle.c.y in + draw_circle c.x c.y circle.radius | Rectangle rectangle -> - let c = rectangle.c in - draw_rect (c.x + !canvas_center.x - (rectangle.length / 2)) (-c.y + !canvas_center.y - (rectangle.width / 2)) rectangle.length rectangle.width + let c = denormalize rectangle.c.x rectangle.c.y in + draw_rect (c.x - (rectangle.length / 2)) (c.y - (rectangle.width / 2)) rectangle.length rectangle.width | Ellipse ellipse -> - let c = ellipse.c in - draw_ellipse (c.x + !canvas_center.x) (-c.y + !canvas_center.y) ellipse.rx ellipse.ry + let c = denormalize ellipse.c.x ellipse.c.y in + draw_ellipse c.x c.y ellipse.rx ellipse.ry let circle ?x ?y r = - match (x, y) with - | Some x, Some y -> Circle { c = { x; y }; radius = r } - | _ -> Circle { c = { x = 0; y = 0 }; radius = r } + let default_center = { x = 0; y = 0 } in + let center = match (x, y) with Some x, Some y -> { x; y } | _ -> default_center in + Circle { c = center; radius = r } let rectangle ?x ?y length width = - match (x, y) with - | Some x, Some y -> Rectangle { c = { x; y }; length; width } - | _ -> Rectangle { c = { x = 0; y = 0 }; length; width } + let default_center = { x = 0; y = 0 } in + let center = match (x, y) with Some x, Some y -> { x; y } | _ -> default_center in + Rectangle { c = center; length; width } let ellipse ?x ?y rx ry = - match (x, y) with - | Some x, Some y -> Ellipse { c = { x; y }; rx; ry } - | _ -> Ellipse { c = { x = 0; y = 0 }; rx; ry } + let default_center = { x = 0; y = 0 } in + let center = match (x, y) with Some x, Some y -> { x; y } | _ -> default_center in + Ellipse { c = center; rx; ry } let translate dx dy shape = match shape with - | Circle circle -> Circle { circle with c = { x = circle.c.x + dx; y = circle.c.y + dy } } - | Rectangle rectangle -> Rectangle { rectangle with c = { x = rectangle.c.x + dx; y = rectangle.c.y + dy } } - | Ellipse ellipse -> Ellipse { ellipse with c = { x = ellipse.c.x + dx; y = ellipse.c.y + dy } } + | Circle circle -> + let c = denormalize circle.c.x circle.c.y in + Circle { circle with c = { x = c.x + dx; y = c.y + dy } } + | Rectangle rectangle -> + let c = denormalize rectangle.c.x rectangle.c.y in + Rectangle { rectangle with c = { x = c.x + dx; y = c.y + dy } } + | Ellipse ellipse -> + let c = denormalize ellipse.c.x ellipse.c.y in + Ellipse { ellipse with c = { x = c.x + dx; y = c.y + dy } } -let show shapes = List.iter render_shape shapes +let show shapes = List.iter (fun shape -> render_shape shape) shapes let init () = open_graph (Printf.sprintf " %ix%i" !dimensions.x !dimensions.y); if !axes_flag then render_axes (); - + set_color black let close () = From c71d85bd2764394b8be1ba7ca74dfb2cc5fa4df4 Mon Sep 17 00:00:00 2001 From: Nangah Amandine Chi Date: Thu, 12 Oct 2023 06:10:48 +0100 Subject: [PATCH 3/6] New normalized changes to a co-ordinate system with (0, 0) at the center --- lib/shape.ml | 65 ++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/lib/shape.ml b/lib/shape.ml index 9f1688b..20db50d 100644 --- a/lib/shape.ml +++ b/lib/shape.ml @@ -4,46 +4,44 @@ type point = { x : int; y : int } type line = { a : point; b : point } type rectangle = { c : point; length : int; width : int } type circle = { c : point; radius : int } -type ellipse = { c : point; rx : int; ry : int } +type ellipse = {c : point; rx : int; ry: int} type shape = Circle of circle | Rectangle of rectangle | Ellipse of ellipse | Line of line type shapes = shape list -let dimensions = ref { x = 500; y = 500 } -let set_dimensions x y = - dimensions := { x; y } +let dimensions = ref {x = 500; y = 500} +let set_dimensions x y = + dimensions := {x;y} -let canvas_center = { x = 0; y = 0 } +let canvas_mid = { x = (!dimensions.x / 2); y = (!dimensions.y / 2)} let axes_flag = ref false -let draw_axes flag = +let draw_axes flag = axes_flag := flag -let draw_line x1 y1 x2 y2 = +let draw_line x1 y1 x2 y2 = draw_poly_line [|(x1, y1); (x2, y2)|] -let denormalize x y = - { x = x + canvas_center.x; y = -y + canvas_center.y } +let denormalize point = + { x = point.x + canvas_mid.x; y = point.y + canvas_mid.y } let render_shape s = match s with - | Circle circle -> - let c = denormalize circle.c.x circle.c.y in - draw_circle c.x c.y circle.radius + | Circle circle -> draw_circle (denormalize circle.c).x (denormalize circle.c).y circle.radius | Rectangle rectangle -> - let c = denormalize rectangle.c.x rectangle.c.y in - draw_rect (c.x - (rectangle.length / 2)) (c.y - (rectangle.width / 2)) rectangle.length rectangle.width + let c = denormalize rectangle.c in + draw_rect c.x c.y rectangle.length rectangle.width | Ellipse ellipse -> - let c = denormalize ellipse.c.x ellipse.c.y in - draw_ellipse c.x c.y ellipse.rx ellipse.ry + let c = denormalize ellipse.c in + draw_ellipse c.x c.y ellipse.rx ellipse.ry | Line line -> - let a = denormalize line.a.x line.a.y in - let b = denormalize line.b.x line.b.y in - draw_line a.x a.y b.x b.y + let a = denormalize line.a in + let b = denormalize line.b in + draw_line a.x a.y b.x b.y let circle ?x ?y r = - let default_center = { x = 0; y = 0 } in - let center = match (x, y) with Some x, Some y -> { x; y } | _ -> default_center in - Circle { c = center; radius = r } + match (x, y) with + | Some x, Some y -> Circle { c = { x; y }; radius = r } + | _ -> Circle { c = { x = 0; y = 0 }; radius = r } let rectangle ?x ?y length width = match (x, y) with @@ -51,35 +49,36 @@ let rectangle ?x ?y length width = | _ -> Rectangle { c = { x = 0; y = 0 }; length; width } let ellipse ?x ?y rx ry = - let default_center = { x = 0; y = 0 } in - let center = match (x, y) with Some x, Some y -> { x; y } | _ -> default_center in - Ellipse { c = center; rx; ry } + match (x, y) with + | Some x, Some y -> Ellipse {c = {x; y}; rx; ry} + | _ -> Ellipse {c = { x = 0; y = 0}; rx; ry} let line ?x1 ?y1 x2 y2 = - match (x1, y1) with - | Some x, Some y -> Line { a = { x; y }; b = { x = x2; y = y2 } } - | _ -> Line { a = { x = 0; y = 0 }; b = { x = x2; y = y2 } } + match (x1, y1) with + | Some x, Some y -> Line {a = {x;y}; b = {x = x2; y = y2}} + | _ -> Line {a = {x = 0; y = 0}; b = {x = x2; y = y2}} let translate dx dy shape = match shape with | Circle circle -> Circle { circle with c = { x = circle.c.x + dx; y = circle.c.y + dy } } | Rectangle rectangle -> Rectangle { rectangle with c = { x = rectangle.c.x + dx; y = rectangle.c.y + dy } } | Ellipse ellipse -> Ellipse { ellipse with c = { x = ellipse.c.x + dx; y = ellipse.c.y + dy } } - | Line line -> Line { a = { x = line.a.x + dx; y = line.a.y + dy }; b = { x = line.b.x + dx; y = line.b.y + dy }} + | Line line -> Line {a = {x = line.a.x + dx; y = line.a.y + dy}; b = {x = line.b.x + dx; y = line.b.y + dy}} -let show shapes = List.iter (fun shape -> render_shape shape) shapes +let show shapes = List.iter render_shape shapes -let render_axes () = +let render_axes () = set_color (rgb 192 192 192); - let half_x = (size_x ()) / 2 in + let half_x = (size_x ()) / 2 in draw_line half_x 0 half_x (size_y ()); - let half_y = (size_y ()) / 2 in + let half_y = (size_y ()) / 2 in draw_line 0 half_y (size_x ()) half_y let init () = open_graph (Printf.sprintf " %ix%i" !dimensions.x !dimensions.y); if !axes_flag then render_axes (); + set_color black let close () = From 5f137795c02f5021b013043d390a191f63cfb500 Mon Sep 17 00:00:00 2001 From: Nangah Amandine Chi Date: Tue, 24 Oct 2023 07:03:36 +0100 Subject: [PATCH 4/6] Refactored the rotation transformation and examples --- examples/dune | 5 +++ examples/ellipse_rotate.ml | 17 +++++++ examples/rotate.ml | 4 +- lib/shape.ml | 90 +++++++++++++++++++++++++++++++------- lib/shape.mli | 2 +- 5 files changed, 98 insertions(+), 20 deletions(-) create mode 100644 examples/ellipse_rotate.ml diff --git a/examples/dune b/examples/dune index 3643f4f..c182baf 100644 --- a/examples/dune +++ b/examples/dune @@ -92,3 +92,8 @@ (name higher_transforms) (modules higher_transforms) (libraries joy)) + +(executable + (name ellipse_rotate) + (modules ellipse_rotate) + (libraries joy)) diff --git a/examples/ellipse_rotate.ml b/examples/ellipse_rotate.ml new file mode 100644 index 0000000..a3c1622 --- /dev/null +++ b/examples/ellipse_rotate.ml @@ -0,0 +1,17 @@ +open Joy.Shape + +let max = 32 +let rec range a b = if a > b then [] else a :: range (a + 1) b + +let _ = + init (); + let ellipse = ellipse 20 10 in + let nums = range 0 max in + let rotated = + List.map + (fun i -> + rotate (float_of_int i /. float_of_int max *. 360.0) ellipse) + nums + in + show rotated; + close () \ No newline at end of file diff --git a/examples/rotate.ml b/examples/rotate.ml index 9612316..c37f557 100644 --- a/examples/rotate.ml +++ b/examples/rotate.ml @@ -10,8 +10,8 @@ let _ = let rotated = List.map (fun i -> - rotate (int_of_float (float_of_int i /. float_of_int max *. 360.0)) rect) + rotate (float_of_int i /. float_of_int max *. 360.0) rect) nums in show rotated; - close () + close () \ No newline at end of file diff --git a/lib/shape.ml b/lib/shape.ml index 3dfc94b..d783db5 100644 --- a/lib/shape.ml +++ b/lib/shape.ml @@ -1,29 +1,41 @@ open Graphics +(* Define types for various geometric shapes and their components *) type point = { x : int; y : int } type line = { a : point; b : point } type rectangle = { c : point; length : int; width : int } type circle = { c : point; radius : int } type ellipse = { c : point; rx : int; ry : int } +(* Define a union type 'shape' that represents various shapes *) type shape = | Circle of circle | Rectangle of rectangle | Ellipse of ellipse | Line of line +(* 'shapes' is a list of 'shape' type, representing a collection of shapes *) type shapes = shape list +(* 'dimensions' is a reference to the canvas dimensions *) let dimensions = ref { x = 500; y = 500 } let set_dimensions x y = dimensions := { x; y } + +(* Calculate the center of the canvas *) let canvas_mid = { x = !dimensions.x / 2; y = !dimensions.y / 2 } + +(* 'axes_flag' is a reference to control whether axes are drawn *) let axes_flag = ref false let draw_axes flag = axes_flag := flag + +(* Define a function to draw a line on the canvas *) let draw_line x1 y1 x2 y2 = draw_poly_line [| (x1, y1); (x2, y2) |] +(* Translate a point from canvas coordinates to screen coordinates *) let denormalize point = { x = point.x + canvas_mid.x; y = point.y + canvas_mid.y } +(* Function to render a shape on the canvas *) let render_shape s = match s with | Circle circle -> @@ -40,6 +52,7 @@ let render_shape s = let b = denormalize line.b in draw_line a.x a.y b.x b.y +(* Functions to create different shapes with optional position arguments *) let circle ?x ?y r = match (x, y) with | Some x, Some y -> Circle { c = { x; y }; radius = r } @@ -55,11 +68,13 @@ let ellipse ?x ?y rx ry = | Some x, Some y -> Ellipse { c = { x; y }; rx; ry } | _ -> Ellipse { c = { x = 0; y = 0 }; rx; ry } +(* Function to create a line with optional starting position *) let line ?x1 ?y1 x2 y2 = match (x1, y1) with | Some x, Some y -> Line { a = { x; y }; b = { x = x2; y = y2 } } | _ -> Line { a = { x = 0; y = 0 }; b = { x = x2; y = y2 } } +(* Translate a shape by a given dx and dy value *) let translate dx dy shape = match shape with | Circle circle -> @@ -80,6 +95,7 @@ let translate dx dy shape = b = { x = line.b.x + dx; y = line.b.y + dy }; } +(* Scale a shape by a given factor *) let scale factor s = let round x = int_of_float (x +. 0.5) in let scale_length len fact = round (float_of_int len *. sqrt fact) in @@ -96,39 +112,77 @@ let scale factor s = (scale_length ellipse'.ry factor) | Line _line' -> failwith "Not Implemented" +(* Show a list of shapes on the canvas *) let show shapes = List.iter render_shape shapes -let bi_to_uni x y = - let nx = (x *. 0.5) +. (float_of_int !dimensions.x *. 0.5) in - let ny = (y *. 0.5) +. (float_of_int !dimensions.y *. 0.5) in - (int_of_float nx, int_of_float ny) - +(* Convert degrees to radians *) let deg_to_rad degrees = degrees *. (Stdlib.Float.pi /. 180.) -let rot { x : int; y : int } degrees = - let radians = deg_to_rad (float_of_int degrees) in - let dx = (float_of_int x *. cos radians) -. (float_of_int y *. sin radians) in - let dy = (float_of_int x *. sin radians) +. (float_of_int y *. cos radians) in - let dx, dy = bi_to_uni dx dy in - { x = dx; y = dy } - +(* Rotate a shape by a specified number of degrees *) let rotate degrees shape = + let radians = deg_to_rad degrees in + + (* Convert a point from Cartesian to polar coordinates *) + let cartesian_to_polar point = + let r = sqrt (float_of_int (point.x * point.x + point.y * point.y)) in + let theta = atan2 (float_of_int point.y) (float_of_int point.x) in + (r, theta) + in + + (* Convert a point from polar to Cartesian coordinates *) + let polar_to_cartesian (r, theta) = + let x = int_of_float (r *. cos theta) in + let y = int_of_float (r *. sin theta) in + { x; y } + in + match shape with - | Circle circle -> Circle { c = rot circle.c degrees; radius = circle.radius } + | Circle circle -> + let polar_center = cartesian_to_polar (denormalize circle.c) in + let polar_rotated = (fst polar_center, snd polar_center +. radians) in + let rotated_center = polar_to_cartesian polar_rotated in + (* Make sure the rotated circle remains within the canvas *) + let max_x = !dimensions.x / 2 in + let max_y = !dimensions.y / 2 in + let clamped_x = max (min rotated_center.x max_x) (-max_x) in + let clamped_y = max (min rotated_center.y max_y) (-max_y) in + Circle { c = { x = clamped_x; y = clamped_y }; radius = circle.radius } | Rectangle rectangle -> + let polar_center = cartesian_to_polar (denormalize rectangle.c) in + let polar_rotated = (fst polar_center, snd polar_center +. radians) in + let rotated_center = polar_to_cartesian polar_rotated in + (* Make sure the rotated rectangle remains within the canvas *) + let max_x = !dimensions.x / 2 in + let max_y = !dimensions.y / 2 in + let clamped_x = max (min rotated_center.x max_x) (-max_x) in + let clamped_y = max (min rotated_center.y max_y) (-max_y) in Rectangle { - c = rot rectangle.c degrees; + c = { x = clamped_x; y = clamped_y }; length = rectangle.length; width = rectangle.width; } | Ellipse ellipse -> - Ellipse { c = rot ellipse.c degrees; rx = ellipse.rx; ry = ellipse.ry } + let polar_center = cartesian_to_polar (denormalize ellipse.c) in + let polar_rotated = (fst polar_center, snd polar_center +. radians) in + let rotated_center = polar_to_cartesian polar_rotated in + (* Make sure the rotated ellipse remains within the canvas *) + let max_x = !dimensions.x / 2 in + let max_y = !dimensions.y / 2 in + let clamped_x = max (min rotated_center.x max_x) (-max_x) in + let clamped_y = max (min rotated_center.y max_y) (-max_y) in + Ellipse + { + c = { x = clamped_x; y = clamped_y }; + rx = ellipse.rx; + ry = ellipse.ry; + } | Line _line -> failwith "Not Implemented" - +(* Compose two functions 'f' and 'g' and apply them to 'x' *) let compose f g x = g (f x) +(* Render the axes on the canvas *) let render_axes () = set_color (rgb 192 192 192); let half_x = size_x () / 2 in @@ -136,12 +190,14 @@ let render_axes () = let half_y = size_y () / 2 in draw_line 0 half_y (size_x ()) half_y +(* Initialize the graphics window *) let init () = open_graph (Printf.sprintf " %ix%i" !dimensions.x !dimensions.y); if !axes_flag then render_axes (); set_color black +(* Close the graphics window after waiting for user input *) let close () = ignore (read_line ()); - close_graph () + close_graph () \ No newline at end of file diff --git a/lib/shape.mli b/lib/shape.mli index d8d601e..942817d 100644 --- a/lib/shape.mli +++ b/lib/shape.mli @@ -9,7 +9,7 @@ val line : ?x1:int -> ?y1:int -> int -> int -> shape val translate : int -> int -> shape -> shape val show : shape list -> unit val scale : float -> shape -> shape -val rotate : int -> shape -> shape +val rotate : float -> shape -> shape val compose : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c From 4bf9c15c09e3b4fce3a2c21b8b294fadd343b8da Mon Sep 17 00:00:00 2001 From: Nangah Amandine Chi Date: Wed, 25 Oct 2023 18:56:09 +0100 Subject: [PATCH 5/6] Updated the code based on all the requested changes --- examples/ellipse_rotate.ml | 2 +- examples/rotate.ml | 2 +- lib/shape.ml | 170 +++++++++++++++---------------------- lib/shape.mli | 2 +- 4 files changed, 71 insertions(+), 105 deletions(-) diff --git a/examples/ellipse_rotate.ml b/examples/ellipse_rotate.ml index a3c1622..5f3ba78 100644 --- a/examples/ellipse_rotate.ml +++ b/examples/ellipse_rotate.ml @@ -10,7 +10,7 @@ let _ = let rotated = List.map (fun i -> - rotate (float_of_int i /. float_of_int max *. 360.0) ellipse) + rotate i ellipse) nums in show rotated; diff --git a/examples/rotate.ml b/examples/rotate.ml index c37f557..560d7eb 100644 --- a/examples/rotate.ml +++ b/examples/rotate.ml @@ -10,7 +10,7 @@ let _ = let rotated = List.map (fun i -> - rotate (float_of_int i /. float_of_int max *. 360.0) rect) + rotate i rect) nums in show rotated; diff --git a/lib/shape.ml b/lib/shape.ml index d783db5..3f5aa75 100644 --- a/lib/shape.ml +++ b/lib/shape.ml @@ -1,58 +1,71 @@ open Graphics -(* Define types for various geometric shapes and their components *) type point = { x : int; y : int } type line = { a : point; b : point } type rectangle = { c : point; length : int; width : int } type circle = { c : point; radius : int } type ellipse = { c : point; rx : int; ry : int } -(* Define a union type 'shape' that represents various shapes *) type shape = | Circle of circle | Rectangle of rectangle | Ellipse of ellipse | Line of line -(* 'shapes' is a list of 'shape' type, representing a collection of shapes *) type shapes = shape list -(* 'dimensions' is a reference to the canvas dimensions *) let dimensions = ref { x = 500; y = 500 } let set_dimensions x y = dimensions := { x; y } -(* Calculate the center of the canvas *) let canvas_mid = { x = !dimensions.x / 2; y = !dimensions.y / 2 } -(* 'axes_flag' is a reference to control whether axes are drawn *) let axes_flag = ref false let draw_axes flag = axes_flag := flag -(* Define a function to draw a line on the canvas *) let draw_line x1 y1 x2 y2 = draw_poly_line [| (x1, y1); (x2, y2) |] -(* Translate a point from canvas coordinates to screen coordinates *) let denormalize point = { x = point.x + canvas_mid.x; y = point.y + canvas_mid.y } -(* Function to render a shape on the canvas *) +let deg_to_rad degrees = degrees *. (Float.pi /. 180.) (* Define deg_to_rad *) + +let rotate_point point radians = + let cartesian_to_polar { x; y } = + let r = sqrt (float_of_int (x * x + y * y)) in + let theta = atan2 (float_of_int y) (float_of_int x) in + (r, theta) + in + + let polar_to_cartesian (r, theta) = + let x = int_of_float (r *. cos theta) in + let y = int_of_float (r *. sin theta) in + { x; y } + in + + match point with + | { x = x'; y = y' } -> + let (r, theta) = cartesian_to_polar point in + let { x; y } = polar_to_cartesian (r, theta +. radians) in + { x = x + x'; y = y + y' } + let render_shape s = match s with | Circle circle -> - draw_circle (denormalize circle.c).x (denormalize circle.c).y - circle.radius + let rotated_center = rotate_point circle.c (deg_to_rad 30.0) in + draw_circle (denormalize rotated_center).x (denormalize rotated_center).y circle.radius | Rectangle rectangle -> - let c = denormalize rectangle.c in - draw_rect c.x c.y rectangle.length rectangle.width + let rotated_center = rotate_point rectangle.c (deg_to_rad 30.0) in + let c = denormalize rotated_center in + draw_rect c.x c.y rectangle.length rectangle.width | Ellipse ellipse -> - let c = denormalize ellipse.c in - draw_ellipse c.x c.y ellipse.rx ellipse.ry + let rotated_center = rotate_point ellipse.c (deg_to_rad 30.0) in + let c = denormalize rotated_center in + draw_ellipse c.x c.y ellipse.rx ellipse.ry | Line line -> - let a = denormalize line.a in - let b = denormalize line.b in - draw_line a.x a.y b.x b.y + let a = denormalize (rotate_point line.a (deg_to_rad 30.0)) in + let b = denormalize (rotate_point line.b (deg_to_rad 30.0)) in + draw_line a.x a.y b.x b.y -(* Functions to create different shapes with optional position arguments *) let circle ?x ?y r = match (x, y) with | Some x, Some y -> Circle { c = { x; y }; radius = r } @@ -68,121 +81,76 @@ let ellipse ?x ?y rx ry = | Some x, Some y -> Ellipse { c = { x; y }; rx; ry } | _ -> Ellipse { c = { x = 0; y = 0 }; rx; ry } -(* Function to create a line with optional starting position *) let line ?x1 ?y1 x2 y2 = match (x1, y1) with | Some x, Some y -> Line { a = { x; y }; b = { x = x2; y = y2 } } | _ -> Line { a = { x = 0; y = 0 }; b = { x = x2; y = y2 } } -(* Translate a shape by a given dx and dy value *) let translate dx dy shape = match shape with | Circle circle -> - Circle { circle with c = { x = circle.c.x + dx; y = circle.c.y + dy } } + Circle { circle with c = { x = circle.c.x + dx; y = circle.c.y + dy } } | Rectangle rectangle -> - Rectangle - { - rectangle with - c = { x = rectangle.c.x + dx; y = rectangle.c.y + dy }; - } + Rectangle + { + rectangle with + c = { x = rectangle.c.x + dx; y = rectangle.c.y + dy }; + } | Ellipse ellipse -> - Ellipse - { ellipse with c = { x = ellipse.c.x + dx; y = ellipse.c.y + dy } } + Ellipse + { ellipse with c = { x = ellipse.c.x + dx; y = ellipse.c.y + dy } } | Line line -> - Line - { - a = { x = line.a.x + dx; y = line.a.y + dy }; - b = { x = line.b.x + dx; y = line.b.y + dy }; - } + Line + { + a = { x = line.a.x + dx; y = line.a.y + dy }; + b = { x = line.b.x + dx; y = line.b.y + dy }; + } -(* Scale a shape by a given factor *) let scale factor s = let round x = int_of_float (x +. 0.5) in let scale_length len fact = round (float_of_int len *. sqrt fact) in match s with | Circle circle' -> - circle ~x:circle'.c.x ~y:circle'.c.y (scale_length circle'.radius factor) + circle ~x:circle'.c.x ~y:circle'.c.y (scale_length circle'.radius factor) | Rectangle rectangle' -> - rectangle ~x:rectangle'.c.x ~y:rectangle'.c.y - (scale_length rectangle'.length factor) - (scale_length rectangle'.width factor) + rectangle ~x:rectangle'.c.x ~y:rectangle'.c.y + (scale_length rectangle'.length factor) + (scale_length rectangle'.width factor) | Ellipse ellipse' -> - ellipse ~x:ellipse'.c.x ~y:ellipse'.c.y - (scale_length ellipse'.rx factor) - (scale_length ellipse'.ry factor) + ellipse ~x:ellipse'.c.x ~y:ellipse'.c.y + (scale_length ellipse'.rx factor) + (scale_length ellipse'.ry factor) | Line _line' -> failwith "Not Implemented" -(* Show a list of shapes on the canvas *) let show shapes = List.iter render_shape shapes -(* Convert degrees to radians *) let deg_to_rad degrees = degrees *. (Stdlib.Float.pi /. 180.) -(* Rotate a shape by a specified number of degrees *) let rotate degrees shape = - let radians = deg_to_rad degrees in - - (* Convert a point from Cartesian to polar coordinates *) - let cartesian_to_polar point = - let r = sqrt (float_of_int (point.x * point.x + point.y * point.y)) in - let theta = atan2 (float_of_int point.y) (float_of_int point.x) in - (r, theta) - in - - (* Convert a point from polar to Cartesian coordinates *) - let polar_to_cartesian (r, theta) = - let x = int_of_float (r *. cos theta) in - let y = int_of_float (r *. sin theta) in - { x; y } - in - match shape with | Circle circle -> - let polar_center = cartesian_to_polar (denormalize circle.c) in - let polar_rotated = (fst polar_center, snd polar_center +. radians) in - let rotated_center = polar_to_cartesian polar_rotated in - (* Make sure the rotated circle remains within the canvas *) - let max_x = !dimensions.x / 2 in - let max_y = !dimensions.y / 2 in - let clamped_x = max (min rotated_center.x max_x) (-max_x) in - let clamped_y = max (min rotated_center.y max_y) (-max_y) in - Circle { c = { x = clamped_x; y = clamped_y }; radius = circle.radius } + let rotated_center = rotate_point circle.c (deg_to_rad (float_of_int degrees)) in + Circle { c = rotated_center; radius = circle.radius } | Rectangle rectangle -> - let polar_center = cartesian_to_polar (denormalize rectangle.c) in - let polar_rotated = (fst polar_center, snd polar_center +. radians) in - let rotated_center = polar_to_cartesian polar_rotated in - (* Make sure the rotated rectangle remains within the canvas *) - let max_x = !dimensions.x / 2 in - let max_y = !dimensions.y / 2 in - let clamped_x = max (min rotated_center.x max_x) (-max_x) in - let clamped_y = max (min rotated_center.y max_y) (-max_y) in - Rectangle - { - c = { x = clamped_x; y = clamped_y }; - length = rectangle.length; - width = rectangle.width; - } + let rotated_center = rotate_point rectangle.c (deg_to_rad (float_of_int degrees)) in + Rectangle + { + c = rotated_center; + length = rectangle.length; + width = rectangle.width; + } | Ellipse ellipse -> - let polar_center = cartesian_to_polar (denormalize ellipse.c) in - let polar_rotated = (fst polar_center, snd polar_center +. radians) in - let rotated_center = polar_to_cartesian polar_rotated in - (* Make sure the rotated ellipse remains within the canvas *) - let max_x = !dimensions.x / 2 in - let max_y = !dimensions.y / 2 in - let clamped_x = max (min rotated_center.x max_x) (-max_x) in - let clamped_y = max (min rotated_center.y max_y) (-max_y) in - Ellipse - { - c = { x = clamped_x; y = clamped_y }; - rx = ellipse.rx; - ry = ellipse.ry; - } + let rotated_center = rotate_point ellipse.c (deg_to_rad (float_of_int degrees)) in + Ellipse + { + c = rotated_center; + rx = ellipse.rx; + ry = ellipse.ry; + } | Line _line -> failwith "Not Implemented" -(* Compose two functions 'f' and 'g' and apply them to 'x' *) let compose f g x = g (f x) -(* Render the axes on the canvas *) let render_axes () = set_color (rgb 192 192 192); let half_x = size_x () / 2 in @@ -190,14 +158,12 @@ let render_axes () = let half_y = size_y () / 2 in draw_line 0 half_y (size_x ()) half_y -(* Initialize the graphics window *) let init () = open_graph (Printf.sprintf " %ix%i" !dimensions.x !dimensions.y); if !axes_flag then render_axes (); set_color black -(* Close the graphics window after waiting for user input *) let close () = ignore (read_line ()); close_graph () \ No newline at end of file diff --git a/lib/shape.mli b/lib/shape.mli index 942817d..d8d601e 100644 --- a/lib/shape.mli +++ b/lib/shape.mli @@ -9,7 +9,7 @@ val line : ?x1:int -> ?y1:int -> int -> int -> shape val translate : int -> int -> shape -> shape val show : shape list -> unit val scale : float -> shape -> shape -val rotate : float -> shape -> shape +val rotate : int -> shape -> shape val compose : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c From 509cb37ffa12b5a8056a2443b58ecd4134912a35 Mon Sep 17 00:00:00 2001 From: Nangah Amandine Chi Date: Thu, 26 Oct 2023 11:16:55 +0100 Subject: [PATCH 6/6] Refactored the CI --- .github/workflows/ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec2bbbe..f4e9cf1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,9 @@ name: main on: - pull_request: push: branches: - - main - + - main jobs: build: @@ -13,6 +11,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] + ocaml-compiler: ["ocaml.5.0.0", "ocaml-option-mingw"] steps: - name: Checkout code @@ -23,7 +22,7 @@ jobs: with: opam-pin: false opam-depext: false - ocaml-compiler: ocaml.5.0.0,ocaml-option-mingw + ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-repositories: | dra27: https://github.com/dra27/opam-repository.git#windows-5.0 default: https://github.com/ocaml-opam/opam-repository-mingw.git#sunset