diff --git a/examples/complex.ml b/examples/complex.ml new file mode 100644 index 0000000..b0580da --- /dev/null +++ b/examples/complex.ml @@ -0,0 +1,34 @@ +open Joy.Shape + +(* + Complex shapes can also be created from lists of shapes. + This allows us to apply any of the libraries' transformations to a group of + shapes, like we're doing here with translate +*) + +(* creates a list containing numbers between a and b *) +let rec range a b = if a > b then [] else a :: range (a + 1) b + +(* creates a list of (int * int) tuples, + containing every combination of the elements of the two passsed lists *) +let cartesian_product l l' = + List.concat (List.map (fun e -> List.map (fun e' -> (e, e')) l') l) + +let () = + init (); + (* radius which also acts as grid spacing *) + let radius = 50 in + let half_radius = radius / 2 in + (* creating a grid with cartesian_product *) + let coords = cartesian_product (range (-5) 5) (range (-5) 5) in + (* using map to turn that into a complex shape that is a grid of circles *) + let complex_shape = + complex + (List.map + (fun (x, y) -> circle ~x:(x * radius) ~y:(y * radius) radius) + coords) + in + (* translating that complex shape by radius / 2 *) + let complex_transformed = translate half_radius half_radius complex_shape in + show [ complex_shape; complex_transformed ]; + close () diff --git a/examples/dune b/examples/dune index 3643f4f..27ea231 100644 --- a/examples/dune +++ b/examples/dune @@ -92,3 +92,8 @@ (name higher_transforms) (modules higher_transforms) (libraries joy)) + +(executable + (name complex) + (modules complex) + (libraries joy)) diff --git a/examples/higher_transforms.ml b/examples/higher_transforms.ml index a3347f8..6309e10 100644 --- a/examples/higher_transforms.ml +++ b/examples/higher_transforms.ml @@ -1,21 +1,20 @@ open Joy.Shape -(* Higher order transformations can be composed with `comp`, +(* Higher order transformations can be composed with `comp`, which applies its function args right-to-left. This allows us to create complex series transformations, that can be applied iteratively *) let transform = compose (translate 10 10) (scale 0.9) - let rec range a b = if a > b then [] else a :: range (a + 1) b -let () = +let () = init (); - let initial = rectangle ~x:(-250) ~y:(-250) 100 100 in - let match_list l = - match l with - | [] -> [(transform initial)] - | last :: _ -> (transform last) :: l - in - let shapes = List.fold_right (fun _ acc -> (match_list acc)) (range 0 32) [] in + let initial = rectangle ~x:(-250) ~y:(-250) 100 100 in + let match_list l = + match l with + | [] -> [ transform initial ] + | last :: _ -> transform last :: l + in + let shapes = List.fold_right (fun _ acc -> match_list acc) (range 0 32) [] in show shapes; - close () \ No newline at end of file + close () diff --git a/lib/shape.ml b/lib/shape.ml index 1a223dc..3dffdad 100644 --- a/lib/shape.ml +++ b/lib/shape.ml @@ -11,6 +11,7 @@ type shape = | Rectangle of rectangle | Ellipse of ellipse | Line of line + | Complex of shape list type shapes = shape list @@ -24,7 +25,7 @@ let draw_line x1 y1 x2 y2 = draw_poly_line [| (x1, y1); (x2, y2) |] let denormalize point = { x = point.x + canvas_mid.x; y = point.y + canvas_mid.y } -let render_shape s = +let rec render_shape s = match s with | Circle circle -> draw_circle (denormalize circle.c).x (denormalize circle.c).y @@ -39,6 +40,7 @@ let render_shape s = let a = denormalize line.a in let b = denormalize line.b in draw_line a.x a.y b.x b.y + | Complex complex -> List.iter render_shape complex let circle ?x ?y r = match (x, y) with @@ -60,7 +62,10 @@ let line ?x1 ?y1 x2 y2 = | 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 = +let complex shapes = + match shapes with _ :: _ -> Complex shapes | [] -> Complex [] + +let rec translate dx dy shape = match shape with | Circle circle -> Circle { circle with c = { x = circle.c.x + dx; y = circle.c.y + dy } } @@ -79,8 +84,9 @@ let translate dx dy shape = a = { x = line.a.x + dx; y = line.a.y + dy }; b = { x = line.b.x + dx; y = line.b.y + dy }; } + | Complex shapes -> Complex (List.map (translate dx dy) shapes) -let scale factor s = +let rec 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 @@ -95,6 +101,7 @@ let scale factor s = (scale_length ellipse'.rx factor) (scale_length ellipse'.ry factor) | Line _line' -> failwith "Not Implemented" + | Complex shapes -> Complex (List.map (scale factor) shapes) let show shapes = List.iter render_shape shapes @@ -112,7 +119,7 @@ let rot { x : int; y : int } degrees = let dx, dy = bi_to_uni dx dy in { x = dx; y = dy } -let rotate degrees shape = +let rec rotate degrees shape = match shape with | Circle circle -> Circle { c = rot circle.c degrees; radius = circle.radius } | Rectangle rectangle -> @@ -124,9 +131,8 @@ let rotate degrees shape = } | Ellipse ellipse -> Ellipse { c = rot ellipse.c degrees; rx = ellipse.rx; ry = ellipse.ry } - | Line line -> - Line { a= rot line.a degrees; b = rot line.b degrees } - + | Line _line -> failwith "Not Implemented" + | Complex shapes -> Complex (List.map (rotate degrees) shapes) let compose f g x = g (f x) diff --git a/lib/shape.mli b/lib/shape.mli index d8d601e..34eba11 100644 --- a/lib/shape.mli +++ b/lib/shape.mli @@ -5,14 +5,13 @@ val render_shape : shape -> unit val circle : ?x:int -> ?y:int -> int -> shape val rectangle : ?x:int -> ?y:int -> int -> int -> shape val ellipse : ?x:int -> ?y:int -> int -> int -> shape +val complex : shape list -> shape 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 compose : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c - val draw_axes : bool -> unit val set_dimensions : int -> int -> unit val init : unit -> unit