Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored the rotation transformation function and examples #64

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/dune
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,8 @@
(name higher_transforms)
(modules higher_transforms)
(libraries joy))

(executable
(name ellipse_rotate)
(modules ellipse_rotate)
(libraries joy))
17 changes: 17 additions & 0 deletions examples/ellipse_rotate.ml
Original file line number Diff line number Diff line change
@@ -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
nangahamandine marked this conversation as resolved.
Show resolved Hide resolved
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 ()
4 changes: 2 additions & 2 deletions examples/rotate.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
nangahamandine marked this conversation as resolved.
Show resolved Hide resolved
nums
in
show rotated;
close ()
close ()
90 changes: 73 additions & 17 deletions lib/shape.ml
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
open Graphics

(* Define types for various geometric shapes and their components *)
nangahamandine marked this conversation as resolved.
Show resolved Hide resolved
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 ->
Expand All @@ -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 }
Expand All @@ -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 ->
Expand All @@ -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
Expand All @@ -96,52 +112,92 @@ 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
nangahamandine marked this conversation as resolved.
Show resolved Hide resolved
let polar_rotated = (fst polar_center, snd polar_center +. radians) in
let rotated_center = polar_to_cartesian polar_rotated in
nangahamandine marked this conversation as resolved.
Show resolved Hide resolved
(* 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
nangahamandine marked this conversation as resolved.
Show resolved Hide resolved
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
nangahamandine marked this conversation as resolved.
Show resolved Hide resolved
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
nangahamandine marked this conversation as resolved.
Show resolved Hide resolved
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
draw_line half_x 0 half_x (size_y ());
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 ()
2 changes: 1 addition & 1 deletion lib/shape.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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
nangahamandine marked this conversation as resolved.
Show resolved Hide resolved

val compose : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

Expand Down