Advertisement
Guest User

Untitled

a guest
Oct 20th, 2019
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.03 KB | None | 0 0
  1. (** The "Graphics Context" component of the GUI library. *)
  2.  
  3. (** A graphics context represents a region of the window to which
  4. widgets will be drawn.
  5.  
  6. The drawing primitives in this module are all relative to the
  7. graphics context. This means that when a widget needs to draw on
  8. the screen, it need not know its absolute position. The graphics
  9. context is responsible for translating the relative positions
  10. passed into the drawing routines into absolute positions on the
  11. screen.
  12.  
  13. The graphics context also includes other information for basic
  14. drawing (such as the current pen color.)
  15.  
  16. Note that this module defines a persistent (immutable) data
  17. structure. The operations here use a given graphics context to
  18. create a new one with the specified characteristics. They do not
  19. modify their arguments. *)
  20.  
  21. (* (We use the module name Graphics in this module to refer to a "shim
  22. module" that connects to either the native or the javascript
  23. graphics. You do not need to understand the details of how this
  24. works.) *)
  25. module Graphics = G
  26.  
  27. (****************)
  28. (** Colors *)
  29. (****************)
  30.  
  31. (** A type for colors *)
  32. type color = {r:int; g:int; b:int}
  33.  
  34. let black : color = {r=0; g=0; b=0}
  35. let white : color = {r=255;g=255;b=255}
  36. let red : color = {r=255;g=0; b=0}
  37. let green : color = {r=0; g=255;b=0}
  38. let blue : color = {r=0; g=0; b=255}
  39. let yellow : color = {r=255;g=255;b=0}
  40. let cyan : color = {r=0; g=255;b=255}
  41. let magenta : color = {r=255;g=0; b=255}
  42.  
  43.  
  44. (*******************************)
  45. (** Basic Gctx operations *)
  46. (*******************************)
  47.  
  48. (** The main type of graphics contexts. Note that none of the
  49. components are mutable. (TODO: You will need to modify this type
  50. definition when you get to Task 5.) *)
  51. type gctx = {
  52. x: int; (** offset from (0,0) in local coordinates *)
  53. y: int;
  54. color: color; (** the current pen color *)
  55. }
  56.  
  57. (* Internal helper to set the text size *)
  58. let set_text_size (text_size: int) (font: string) : unit =
  59. if Graphics.js_mode then Graphics.set_font font;
  60. Graphics.set_text_size text_size
  61.  
  62. let clear_graph () =
  63. Graphics.clear_graph ();
  64. set_text_size 20 (if Graphics.js_mode then "sans-serif" else "")
  65.  
  66. (* Has the graphics window been opened already? *)
  67. let graphics_opened = {contents = false}
  68.  
  69. (** Open the graphics window (but only do it once) *)
  70. let open_graphics () =
  71. if not graphics_opened.contents then
  72. begin
  73. graphics_opened.contents <- true;
  74. Graphics.open_graph
  75. (if Graphics.js_mode then "width=640,height=480" else "");
  76. Graphics.resize_window 640 480;
  77. if not Graphics.js_mode then Graphics.auto_synchronize false;
  78. clear_graph ()
  79. end
  80.  
  81. (** The top-level graphics context *)
  82. (* TODO: you will need to modify this variable when you get to Task 5. *)
  83. let top_level : gctx =
  84. { x = 0;
  85. y = 0;
  86. color = black;
  87. }
  88.  
  89. (** Shift the gctx by (dx,dy). Used by widgets to translate (shift
  90. the origin of) a graphics context to obtain an appropriate graphics
  91. context for their children. *)
  92. let translate (g: gctx) ((dx, dy): int * int) : gctx =
  93. { g with x = g.x + dx; y = g.y + dy }
  94.  
  95. (** Produce a new Gctx.t with a different pen color *)
  96. let with_color (g: gctx) (c: color) : gctx =
  97. { g with color = c }
  98.  
  99.  
  100. (** Set the OCaml graphics library's internal state according to the
  101. Gctx settings. Initially, this just sets the current pen color. *)
  102. (* TODO: You will need to modify this definition for Task 5. *)
  103. let set_graphics_state (gc: gctx) : unit =
  104. let c = gc.color in
  105. Graphics.set_color (Graphics.rgb c.r c.g c.b)
  106.  
  107.  
  108. (************************************)
  109. (* Coordinate Transformations *)
  110. (************************************)
  111.  
  112. (* The default width and height of the graphics window that OCaml opens. *)
  113.  
  114. let graphics_size_x () =
  115. if graphics_opened.contents then Graphics.size_x () else 640
  116. let graphics_size_y () =
  117. if graphics_opened.contents then Graphics.size_y () else 480
  118.  
  119. (* A main purpose of the graphics context is to provide mapping between
  120. widget-local coordinates and the ocaml coordinates of the graphics
  121. library. Part of that translation comes from the offset stored in the
  122. graphics context itself. The translation needs to know where the widget
  123. is on the screen. The other part of the translation is the y axis flip.
  124. The OCaml library puts (0,0) at the bottom left corner of the window.
  125. We'd like our GUI library to put (0,0) at the top left corner and
  126. increase the y-coordinate as we go *down* the screen. *)
  127.  
  128. (** A widget-relative position *)
  129. type position = int * int
  130.  
  131. (* The next two functions translate between the coordinate system we
  132. are using for the widget library and the native coordinates of the
  133. Graphics module. Remember to ALWAYS call these functions before
  134. passing widget-local points to the Graphics module or
  135. vice-versa. *)
  136.  
  137. (** Convert widget-local coordinates (x,y) to OCaml graphics
  138. coordinates, relative to the graphics context. *)
  139. let ocaml_coords (g: gctx) ((x, y): position) : (int * int) =
  140. (g.x + x, graphics_size_y () - 1 - (g.y + y))
  141.  
  142. (** Convert OCaml Graphics coordinates (x,y) to widget-local graphics
  143. coordinates, relative to the graphics context *)
  144. let local_coords (g: gctx) ((x, y): int * int) : position =
  145. (x - g.x, (graphics_size_y () - 1 - y) - g.y)
  146.  
  147.  
  148. (*****************)
  149. (** Drawing *)
  150. (*****************)
  151.  
  152. (** A width and height, paired together. *)
  153. type dimension = int * int
  154.  
  155. (* Each of these functions takes inputs in widget-local coordinates,
  156. converts them to OCaml coordinates, and then draws the appropriate
  157. shape. *)
  158.  
  159. (** Draw a line between the two specified positions *)
  160. let draw_line (g: gctx) (p1: position) (p2: position) : unit =
  161. set_graphics_state g;
  162. let (x1, y1) = ocaml_coords g p1 in
  163. let (x2, y2) = ocaml_coords g p2 in
  164. Graphics.moveto x1 y1;
  165. Graphics.lineto x2 y2
  166.  
  167. (** Display text at the given position *)
  168. let draw_string (g: gctx) (p: position) (s: string) : unit =
  169. set_graphics_state g;
  170. let (_, height) = Graphics.text_size s in
  171. let (x, y) = ocaml_coords g p in
  172. (* Web browser font rendering bounding box adjusment *)
  173. let fudge = if Graphics.js_mode then 3 else 0 in
  174. (* subtract: working with Ocaml coordinates *)
  175. Graphics.moveto x (y - height + fudge);
  176. Graphics.draw_string s
  177.  
  178. (** Display a rectangle with lower-left corner at position
  179. with the specified dimension. *)
  180. (* TODO: you will need to make this function actually draw a
  181. rectangle for Task 0. *)
  182. let draw_rect (g: gctx) (p1: position) ((w, h): dimension) : unit =
  183. set_graphics_state g;
  184. let (x1, y1) = ocaml_coords g p1 in
  185. Graphics.moveto x1 y1;
  186. Graphics.draw_rect x1 y1 w h
  187.  
  188. (** Display a filled rectangle with lower-left corner at positions
  189. with the specified dimension. *)
  190. let fill_rect (g: gctx) (p1: position) ((w, h): dimension) : unit =
  191. set_graphics_state g;
  192. let (x, y) = ocaml_coords g p1 in
  193. Graphics.fill_rect x y w h
  194.  
  195. (** Draw an ellipse at the given position with the given radii *)
  196. (* TODO: you will need to make this function actually draw an
  197. ellipse for Task 0. *)
  198. let draw_ellipse (g: gctx) (p: position) (rx: int) (ry: int) : unit =
  199. set_graphics_state g;
  200. let (x, y) = ocaml_coords g p in
  201. Graphics.moveto x y;
  202. Graphics.draw_ellipse x y rx ry
  203.  
  204. (** Calculates the size of a text when rendered. *)
  205. let text_size (text: string) : dimension =
  206. (* First, we make sure that the graphics window has been opened.
  207. (All of the other functions require a graphics context, which is
  208. difficult to get without opening the graphics window first. But
  209. this one does not, which can be a source of subtle bugs.) *)
  210. open_graphics ();
  211. let (w,h) = Graphics.text_size text in
  212. (w+1, h) (* Web browser font widths seem to be smaller than desirable *)
  213.  
  214. (* TODO: You will need to add several "wrapped" versions of ocaml graphics *)
  215. (* functions here for Tasks 2, 4, and possibly 5 and 6 *)
  216.  
  217. let draw_point (g: gctx) (p: position) : unit =
  218. set_graphics_state g;
  219. let (x, y) = ocaml_coords g p in
  220. Graphics.moveto x y;
  221. Graphics.plot x y
  222.  
  223. let rec draw_points (g: gctx) (plist: position list) : unit =
  224. begin match plist with
  225. | [] -> ()
  226. | hd::tl -> draw_point g hd; draw_points g tl
  227. end
  228.  
  229. (************************)
  230. (** Event Handling *)
  231. (************************)
  232.  
  233. (* This part of the module adapts OCaml's native event handling to
  234. something that more closely resembles that found in Java. *)
  235.  
  236. (** Types of events that could occur *)
  237. type event_type =
  238. | KeyPress of char (* Key pressed on the keyboard. *)
  239. | MouseDown (* Mouse button pressed. *)
  240. | MouseUp (* Mouse button released. *)
  241. | MouseMove (* Mouse moved with the button up. *)
  242. | MouseDrag (* Mouse moved with the button down. *)
  243.  
  244. let string_of_event_type (et : event_type) : string =
  245. begin match et with
  246. | KeyPress k -> "KeyPress at " ^ (String.make 1 k)
  247. | MouseDrag -> "MouseDrag"
  248. | MouseMove -> "MouseMove"
  249. | MouseUp -> "MouseUp"
  250. | MouseDown -> "MouseDown"
  251. end
  252.  
  253. (** An event records its type and the widget-local position of
  254. the mouse when the event occurred. *)
  255. type event = event_type * position
  256.  
  257. (** Accessor for the type of an event. *)
  258. let event_type (e: event) : event_type =
  259. fst e
  260.  
  261. (** Accessor for the widget-local position of an event. *)
  262. let event_pos (e: event) (g : gctx) : position =
  263. local_coords g (snd e)
  264.  
  265. (** Convert an event to a string *)
  266. let string_of_event ((ty, (x, y)): event) : string =
  267. (string_of_event_type ty) ^ " at "
  268. ^ (string_of_int x) ^ ","
  269. ^ (string_of_int y)
  270.  
  271. (** Make an event by hand for testing. *)
  272. let make_test_event (et : event_type) ((x, y) : position) =
  273. (et, (x, graphics_size_y () - y))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement