From 245e26e4f0f2b8cf3588fe6de0cd9eef6490ccf2 Mon Sep 17 00:00:00 2001 From: Fay Carsons Date: Tue, 27 Feb 2024 18:13:02 -0500 Subject: [PATCH 1/2] first draft of doc comments --- lib/joy.ml | 4 ++++ lib/render.ml | 1 + lib/shape.ml | 11 +++++++++++ lib/transform.ml | 15 +++++++++++++-- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/joy.ml b/lib/joy.ml index b6eb36a..f645838 100644 --- a/lib/joy.ml +++ b/lib/joy.ml @@ -34,12 +34,16 @@ let map_fill = Transform.map_fill let map_stroke = Transform.map_stroke let set_line_width = Context.set_line_width +(** Initializes drawing context, takes optional arguments to set background color, + line width, and resolution(`size`) *) let init ?(background = Color.white) ?(line_width = 2) ?(size = (500, 500)) ?(axes = false) () = Context.init_context (Color.opaque background) (float_of_int line_width) size axes +(** Writes the current digital canvas to a PNG file, takes a filename + as an optonal argument *) let write ?(filename = "joy.png") () = match !Context.context with | Some ctx -> diff --git a/lib/render.ml b/lib/render.ml index eca4a77..8408c9e 100644 --- a/lib/render.ml +++ b/lib/render.ml @@ -113,6 +113,7 @@ let rec render_shape ctx = function let render shape = match !context with Some ctx -> render_shape ctx shape | None -> fail () +(** Renders a list of shapes *) let show shapes = match !context with | Some ctx -> List.iter (render_shape ctx) shapes diff --git a/lib/shape.ml b/lib/shape.ml index ffc347a..f09b77e 100644 --- a/lib/shape.ml +++ b/lib/shape.ml @@ -23,6 +23,7 @@ type polygon = { fill : color option; } +(** Shape variant type *) type shape = | Circle of circle | Ellipse of ellipse @@ -30,6 +31,7 @@ type shape = | Polygon of polygon | Complex of shape list +(** List of shapes *) type shapes = shape list (* point -> point arithmetic *) @@ -41,18 +43,22 @@ let ( /! ) { x; y } scalar = { x = x /. scalar; y = y /. scalar } let ( *! ) { x; y } scalar = { x = x *. scalar; y = y *. scalar } let pmap f { x; y } = { x = f x; y = f y } +(** Point constructor function *) let point x y = let x, y = (float_of_int x, float_of_int y) in { x; y } let center = { x = 0.; y = 0. } +(** Circle constructor function *) let circle ?(c = center) r = Circle { c; radius = float_of_int r; stroke = Some Color.black; fill = None } +(** Polygon constructor function *) let polygon vertices = Polygon { vertices; stroke = Some Color.black; fill = None } +(** Rectangle constructor function *) let rectangle ?(c = center) width height = let w, h = (float_of_int width, float_of_int height) in let x1 = c.x -. (w /. 2.) in @@ -65,15 +71,19 @@ let rectangle ?(c = center) width height = { x = x1 +. w; y = y1 }; ] +(** Ellipse constructor function *) let ellipse ?(c = center) rx ry = let rx, ry = (float_of_int rx, float_of_int ry) in Ellipse { c; rx; ry; stroke = Some Color.black; fill = None } +(** Line constructor function *) let line ?(a = center) b = Line { a; b; stroke = Color.black } +(** Complex shape constructor function *) let complex shapes = match shapes with _ :: _ -> Complex shapes | [] -> Complex [] +(** Takes a color and a shape, and returns a new shape with its stroke set to that color *) let rec with_stroke stroke = function | Circle circle' -> Circle { circle' with stroke = Some stroke } | Ellipse ellipse' -> Ellipse { ellipse' with stroke = Some stroke } @@ -81,6 +91,7 @@ let rec with_stroke stroke = function | Polygon polygon' -> Polygon { polygon' with stroke = Some stroke } | Complex complex' -> Complex (List.map (with_stroke stroke) complex') +(** Takes a color and a shape, and returns a new shape with its fill set to that color *) let rec with_fill fill = function | Circle circle' -> Circle { circle' with fill = Some fill } | Ellipse ellipse' -> Ellipse { ellipse' with fill = Some fill } diff --git a/lib/transform.ml b/lib/transform.ml index 536f936..0c04636 100644 --- a/lib/transform.ml +++ b/lib/transform.ml @@ -1,7 +1,9 @@ open Shape +(** Represents a function that takes a shape, and returns that shape transformed in some way *) type transformation = shape -> shape +(** Translation - takes an int for each axis and adds those values to the shape's position *) let rec translate dx dy = function | Circle circle -> let dx, dy = (float_of_int dx, float_of_int dy) in @@ -33,6 +35,8 @@ let rec translate dx dy = function let scale_length fact len = len *. fact let pmap f { x; y } = { x = f x; y = f y } +(** Multipllies a shape's size paramters (i.e. a circle's radius) + by the factor argument *) let rec scale factor = function | Circle circle' -> Circle @@ -73,6 +77,7 @@ let rotate_point degrees point = let r, theta = to_polar point in from_polar (r, theta +. radians) +(** Rotates a shape by 0-360 degrees *) let rec rotate degrees = function | Circle circle' -> Circle { circle' with c = rotate_point degrees circle'.c } | Ellipse ellipse' -> @@ -86,9 +91,13 @@ let rec rotate degrees = function } | Complex shapes -> Complex (List.map (rotate degrees) shapes) +(** Composes two `transformation`s, creating a function that feeds the + result of the first into the second *) let compose f g x = g (f x) let range n = List.init n Fun.id +(** Iterative transformation - takes a number, an operation, and a shape and folds over `range n` feeding + the result of the last transformation into the next *) let repeat n op shape = let match_list l = match l with [] -> [ op shape ] | last :: _ -> op last :: l @@ -96,8 +105,8 @@ let repeat n op shape = let shapes = List.fold_right (fun _ acc -> match_list acc) (range n) [] in complex shapes -(** Takes a function and a shape and returns a new shape with the - function applied to the original's color *) +(** Takes a function and a shape, returns a new shape with the + function applied to the original's `stroke` *) let rec map_stroke f = function | Circle circle' -> Circle { circle' with stroke = Option.map f circle'.stroke } @@ -108,6 +117,8 @@ let rec map_stroke f = function Polygon { polygon' with stroke = Option.map f polygon'.stroke } | Complex complex' -> Complex (List.map (map_stroke f) complex') +(** Takes a function and a shape, returns a new shape with the + function applied to the original's `fill` *) let rec map_fill f = function | Circle circle' -> Circle { circle' with fill = Option.map f circle'.fill } | Ellipse ellipse' -> From ed037e8cefb22bb121781a73d9f8371d71c74011 Mon Sep 17 00:00:00 2001 From: Fay Carsons Date: Wed, 28 Feb 2024 15:39:06 -0500 Subject: [PATCH 2/2] moving doc comments to joy.mli --- examples/circle_packing.ml | 4 +- lib/color.ml | 16 -------- lib/color.mli | 2 + lib/context.ml | 4 -- lib/context.mli | 3 ++ lib/joy.ml | 13 +------ lib/joy.mli | 75 +++++++++++++++++++++++++++++++++++++- lib/render.ml | 7 +--- lib/shape.ml | 13 +------ lib/transform.ml | 13 ------- 10 files changed, 86 insertions(+), 64 deletions(-) diff --git a/examples/circle_packing.ml b/examples/circle_packing.ml index e94d733..4b20e27 100644 --- a/examples/circle_packing.ml +++ b/examples/circle_packing.ml @@ -94,9 +94,7 @@ let () = let circles = List.map (fun ((x, y), radius) -> - circle - ~c:{x; y} - (int_of_float radius) + circle ~c:{ x; y } (int_of_float radius) |> with_stroke (rand_nth palette)) concentric in diff --git a/lib/color.ml b/lib/color.ml index 18a8caa..bee833c 100644 --- a/lib/color.ml +++ b/lib/color.ml @@ -1,26 +1,10 @@ type color = int * int * int -(** RGB code for black *) let black = (0, 0, 0) - -(** RGB code for white *) let white = (255, 255, 255) - -(** RGB code for red *) let red = (255, 1, 1) - -(** RGB code for green *) let green = (1, 255, 1) - -(** RGB code for blue *) let blue = (1, 1, 255) - -(** RGB code for yellow *) let yellow = (255, 255, 1) - -(** RGBA constant to set transparent background *) let transparent = (0, 0, 0, 0) - -(** Converts RGB color into opaque RGBA color. - For use w/ `Context.background` *) let opaque (r, g, b) = (r, g, b, 255) diff --git a/lib/color.mli b/lib/color.mli index 7ac3165..97f1064 100644 --- a/lib/color.mli +++ b/lib/color.mli @@ -1,3 +1,5 @@ +(** Color handling & color constants *) + type color = int * int * int val black : color diff --git a/lib/context.ml b/lib/context.ml index 33ede8f..d36dcbf 100644 --- a/lib/context.ml +++ b/lib/context.ml @@ -6,7 +6,6 @@ type context = { axes : bool; } -(* Renders context to PNG *) let write ctx filename = Cairo.PNG.write ctx.surface filename; Cairo.Surface.finish ctx.surface @@ -45,9 +44,6 @@ let background color = Cairo.fill ctx | None -> fail () -(** Sets the width of lines for both stroke of shapes and line primitives. - Can be any positive integer, with larger numbers producing thicker lines. - default is 2 *) let set_line_width line_width = match !context with | Some ctx -> Cairo.set_line_width ctx.ctx (float_of_int line_width) diff --git a/lib/context.mli b/lib/context.mli index d9f36a9..5d26941 100644 --- a/lib/context.mli +++ b/lib/context.mli @@ -15,6 +15,9 @@ val resolution : unit -> int * int val set_color : int * int * int -> unit val background : int * int * int * int -> unit val set_line_width : int -> unit + val write : context -> string -> unit +(** Writes the current digital canvas to a PNG file *) + val save : unit -> unit val restore : unit -> unit diff --git a/lib/joy.ml b/lib/joy.ml index f645838..ed6e364 100644 --- a/lib/joy.ml +++ b/lib/joy.ml @@ -1,12 +1,8 @@ -let context = Context.context - type 'a point = 'a Shape.point type shape = Shape.shape type shapes = Shape.shapes type transformation = Transform.transformation - type color = Color.color -(** Three-tuple representing a 24-bit RGB color *) let black = Color.black let white = Color.white @@ -34,16 +30,11 @@ let map_fill = Transform.map_fill let map_stroke = Transform.map_stroke let set_line_width = Context.set_line_width -(** Initializes drawing context, takes optional arguments to set background color, - line width, and resolution(`size`) *) let init ?(background = Color.white) ?(line_width = 2) ?(size = (500, 500)) ?(axes = false) () = - Context.init_context (Color.opaque background) - (float_of_int line_width) - size axes + Context.init_context (Color.opaque background) (float_of_int line_width) size + axes -(** Writes the current digital canvas to a PNG file, takes a filename - as an optonal argument *) let write ?(filename = "joy.png") () = match !Context.context with | Some ctx -> diff --git a/lib/joy.mli b/lib/joy.mli index bfd4982..dbd948b 100644 --- a/lib/joy.mli +++ b/lib/joy.mli @@ -1,35 +1,101 @@ type 'a point = 'a Shape.point +(** A point in 2d space + generic but library functions require numeric types *) + type shape = Shape.shape +(** Shape variant type, this is what all shape constructor functions return. + Contains types that are only meant for internal use, you should never need + to construct this type yourself. + *) + type shapes = Shape.shapes +(** A list of {!shape} *) + type transformation = Transform.transformation +(** A function that takes a {!shape} and returns that + {!shape} with an operation applied to at least one field *) + type color = Color.color +(** Represents an RGB color *) val point : int -> int -> float point +(** {!point} constructor function *) + val circle : ?c:float point -> int -> shape +(** {!circle} constructor function *) + val rectangle : ?c:float point -> int -> int -> shape +(** {!rectangle} constructor function *) + val ellipse : ?c:float point -> int -> int -> shape +(** {!ellipse} constructor function *) + val line : ?a:float point -> float point -> shape +(** {!line} constructor function *) + val polygon : float point list -> shape +(** {!polygon} consructor function *) + val complex : shapes -> shape +(** {!complex} constructor function *) + val with_stroke : color -> shape -> shape +(** Creates a new {!shape} with stroke [color] *) + val with_fill : color -> shape -> shape +(** Creates a new {!shape} with fill [color], does not affect {!line} *) + val rotate : int -> transformation +(** Rotates {!shape} by n degrees *) + val translate : int -> int -> transformation +(** Adds {[ { dx; dy } ]} to {!shape}'s position *) + val scale : float -> transformation +(** Multiplies {!shape}'s size by scaling factor *) + val compose : transformation -> transformation -> transformation +(** Composes two {!transformation}, returning a function that executes + both, feeding the result of the first into the second *) + val repeat : int -> transformation -> transformation +(** Iterative transformation - applies {!transformation} to {!shape} n + times, feeding the result into the next iteration *) + val map_stroke : (color -> color) -> shape -> shape +(** Applies f to stroke of {!shape} *) + val map_fill : (color -> color) -> shape -> shape -val context : Context.context option ref +(** Applies f to fill of {!shape}, does not affect {!line} *) + val set_line_width : int -> unit +(** Sets the width of lines for both stroke of shapes and line primitives. + Can be any positive integer, with larger numbers producing thicker lines. + default is 2 *) + val black : color +(** RGB code for black *) + val white : color +(** RGB code for white *) + val red : color +(** RGB code for red *) + val green : color +(** RGB code for green *) + val blue : color +(** RGB code for blue *) + val yellow : color +(** RGB code for yellow *) + val transparent : int * int * int * int +(** RGBA code for a transparent color to use when setting the background color of sketches *) + val opaque : color -> int * int * int * int +(** Takes an RGB color and returns an opaque RGBA color *) val init : ?background:color -> @@ -38,7 +104,14 @@ val init : ?axes:bool -> unit -> unit +(** Initializes drawing context, takes optional arguments to set background color, + line width, and resolution(size) *) val render : shape -> unit + val show : shapes -> unit +(** Renders a list of shapes to canvas *) + val write : ?filename:string -> unit -> unit +(** Writes the current digital canvas to a PNG file, takes a filename + (i.e. "joy.png") as an optional argument *) diff --git a/lib/render.ml b/lib/render.ml index 8408c9e..46486b2 100644 --- a/lib/render.ml +++ b/lib/render.ml @@ -17,7 +17,7 @@ let draw_circle ctx ({ c; radius; stroke; fill } : circle) = Option.iter fill_circle fill; Cairo.Path.clear ctx.ctx -let create_control_points ({x; y}, rx, ry) = +let create_control_points ({ x; y }, rx, ry) = let half_height = ry /. 2. in let width_two_thirds = rx *. (2. /. 3.) *. 2. in ( { x; y = y -. half_height }, @@ -74,10 +74,7 @@ let rec partition n ?(step = 0) lst = | [] -> [] | _ -> let taken, _ = take n lst in - if List.length taken = n then - taken - :: - partition n ~step (List.tl lst) + if List.length taken = n then taken :: partition n ~step (List.tl lst) else [] let draw_polygon ctx { vertices = points; stroke; fill } = diff --git a/lib/shape.ml b/lib/shape.ml index f09b77e..e0a01ec 100644 --- a/lib/shape.ml +++ b/lib/shape.ml @@ -1,4 +1,6 @@ type 'a point = { x : 'a; y : 'a } +(** A point in 2d space *) + type color = Color.color type line = { a : float point; b : float point; stroke : color } @@ -23,7 +25,6 @@ type polygon = { fill : color option; } -(** Shape variant type *) type shape = | Circle of circle | Ellipse of ellipse @@ -31,7 +32,6 @@ type shape = | Polygon of polygon | Complex of shape list -(** List of shapes *) type shapes = shape list (* point -> point arithmetic *) @@ -43,22 +43,18 @@ let ( /! ) { x; y } scalar = { x = x /. scalar; y = y /. scalar } let ( *! ) { x; y } scalar = { x = x *. scalar; y = y *. scalar } let pmap f { x; y } = { x = f x; y = f y } -(** Point constructor function *) let point x y = let x, y = (float_of_int x, float_of_int y) in { x; y } let center = { x = 0.; y = 0. } -(** Circle constructor function *) let circle ?(c = center) r = Circle { c; radius = float_of_int r; stroke = Some Color.black; fill = None } -(** Polygon constructor function *) let polygon vertices = Polygon { vertices; stroke = Some Color.black; fill = None } -(** Rectangle constructor function *) let rectangle ?(c = center) width height = let w, h = (float_of_int width, float_of_int height) in let x1 = c.x -. (w /. 2.) in @@ -71,19 +67,15 @@ let rectangle ?(c = center) width height = { x = x1 +. w; y = y1 }; ] -(** Ellipse constructor function *) let ellipse ?(c = center) rx ry = let rx, ry = (float_of_int rx, float_of_int ry) in Ellipse { c; rx; ry; stroke = Some Color.black; fill = None } -(** Line constructor function *) let line ?(a = center) b = Line { a; b; stroke = Color.black } -(** Complex shape constructor function *) let complex shapes = match shapes with _ :: _ -> Complex shapes | [] -> Complex [] -(** Takes a color and a shape, and returns a new shape with its stroke set to that color *) let rec with_stroke stroke = function | Circle circle' -> Circle { circle' with stroke = Some stroke } | Ellipse ellipse' -> Ellipse { ellipse' with stroke = Some stroke } @@ -91,7 +83,6 @@ let rec with_stroke stroke = function | Polygon polygon' -> Polygon { polygon' with stroke = Some stroke } | Complex complex' -> Complex (List.map (with_stroke stroke) complex') -(** Takes a color and a shape, and returns a new shape with its fill set to that color *) let rec with_fill fill = function | Circle circle' -> Circle { circle' with fill = Some fill } | Ellipse ellipse' -> Ellipse { ellipse' with fill = Some fill } diff --git a/lib/transform.ml b/lib/transform.ml index 0c04636..4dc3eb7 100644 --- a/lib/transform.ml +++ b/lib/transform.ml @@ -1,9 +1,7 @@ open Shape -(** Represents a function that takes a shape, and returns that shape transformed in some way *) type transformation = shape -> shape -(** Translation - takes an int for each axis and adds those values to the shape's position *) let rec translate dx dy = function | Circle circle -> let dx, dy = (float_of_int dx, float_of_int dy) in @@ -35,8 +33,6 @@ let rec translate dx dy = function let scale_length fact len = len *. fact let pmap f { x; y } = { x = f x; y = f y } -(** Multipllies a shape's size paramters (i.e. a circle's radius) - by the factor argument *) let rec scale factor = function | Circle circle' -> Circle @@ -77,7 +73,6 @@ let rotate_point degrees point = let r, theta = to_polar point in from_polar (r, theta +. radians) -(** Rotates a shape by 0-360 degrees *) let rec rotate degrees = function | Circle circle' -> Circle { circle' with c = rotate_point degrees circle'.c } | Ellipse ellipse' -> @@ -91,13 +86,9 @@ let rec rotate degrees = function } | Complex shapes -> Complex (List.map (rotate degrees) shapes) -(** Composes two `transformation`s, creating a function that feeds the - result of the first into the second *) let compose f g x = g (f x) let range n = List.init n Fun.id -(** Iterative transformation - takes a number, an operation, and a shape and folds over `range n` feeding - the result of the last transformation into the next *) let repeat n op shape = let match_list l = match l with [] -> [ op shape ] | last :: _ -> op last :: l @@ -105,8 +96,6 @@ let repeat n op shape = let shapes = List.fold_right (fun _ acc -> match_list acc) (range n) [] in complex shapes -(** Takes a function and a shape, returns a new shape with the - function applied to the original's `stroke` *) let rec map_stroke f = function | Circle circle' -> Circle { circle' with stroke = Option.map f circle'.stroke } @@ -117,8 +106,6 @@ let rec map_stroke f = function Polygon { polygon' with stroke = Option.map f polygon'.stroke } | Complex complex' -> Complex (List.map (map_stroke f) complex') -(** Takes a function and a shape, returns a new shape with the - function applied to the original's `fill` *) let rec map_fill f = function | Circle circle' -> Circle { circle' with fill = Option.map f circle'.fill } | Ellipse ellipse' ->