DPoodle Graphics
Section 1. Introduction
DPoodle is a graphics library written in ReasonML at DePauw University during Spring 2020. DPoodle is based on the Doodle graphics library from Creative Scala.
Section 2. image
type
The basic type of a drawing in DPoodle is image
. Seven built-in functions used to construct geometric shapes are ellipse, circle, rectangle, square, triangle, polygon, and regularPolygon. The size arguments for all of these functions are of type float
, plus the regularPolygon
function also takes the number of sides as an int
. Every image in DPoodle has a bounding box, which is a minimal rectangle that can cover the image. The center of the bounding box by default is at (0, 0). The built-in triangle function creates an isoceles triangle with the base on the bottom edge of the bounding box and the vertex in the middle of the top edge. Detail about the built-in functions to create geometric shape images in DPoodle are in the following table:
Function | Argument(s) | Bounding box size |
---|---|---|
ellipse(w, h) | Horizontal axis (w) and Vertical axis (h) | |
circle(r) | Radius (r) | |
rectangle(w, h) | Width (w) and Height (h) | |
square(w) | Side length (w) | |
triangle(w, h) | Base (w) and Height (h) | |
polygon(points) | List of vertex points | Smallest rectangle containing all points |
regularPolygon(n, s, a) | Number of sides (n), Distance from center to vertex (s), and Initial angle (a) | (roughly) |
Function draw(image)
is used to visualize the image
:
An empty
value is use to create an empty image whose bounding box is (0., 0., 0., 0.); it is often useful as a default image when there is nothing to draw, and it is an identity element for the image combination operations described below. The polyline
function is closely related to polygon
. A polyline is a non-closed polygon:
We can also construct a shape by specifying a colection of points and the connections between these points (using straight line or curve). These shapes can be:
- Open-path: using
openPath(pathElements)
function. - Close-path: using
closedPath(pathElements)
function.
These two functions take a list of pathElement
values as input. A pathElement
may be created by the following functions:
moveXY(x, y)
: move to point (x, y), without drawing a line.moveP(p)
: move to point p.lineXY(x, y)
: draw a line from the current point to point (x, y).lineP(p)
: draw a line from the current point to point p.curveXY(c1x, c1y, c2x, c2y, px, py)
: draw a curve from the current point to point (px, py). The points (c1x, c1y) and (c2x, c2y) are called control points; the intuition is that the curve will start out headed toward the first control point, and then approach its destination as if coming from the second control point.curveP(c1, c2, p)
: draw a curve from the current point to point p, with control points c1 and c2.
Here is an example to help you visualize the control points:
In the following example, we draw an AND gate using the closedPath
function, on top of input and output wires drawn with openPath
:
Here are corresponding definitions of OR and NOT gates. Note how the NOT gate is built from other primitive geometric shapes:
Information about the bounding box (bbox) of an image
can be retrieved by following functions, which take an image
as input. The first 4 functions return a float
and the rest return a point
, which is equivalent to a pair of floats.
Function | Return |
---|---|
left(image) | Minimum x-coordinate of corresponding bbox |
right(image) | Maximum x-coordinate of corresponding bbox |
top(image) | Minium y-coordinate of corresponding bbox |
bottom(image) | Maximum y-coordinate of corresponding bbox |
topLeft(image) | Top left point of corresponding bbox |
topRight(image) | Top right point of corresponding bbox |
bottomLeft(image) | Bottom left point of corresponding bbox |
bottomRight(image) | Bottom right point of corresponding bbox |
Here are some examples:
We can also visuallize the bounding box and its center using the showBounds
function, which takes an image as input:
Elements in an image can also be text. The function text(string)
is used to create a text image. This function return an image with 0 by 0 bounding box (this is a limitation of the way fonts are handled—the DPoodle library is not able to calculate an accurate bounding box on its own). Since the draw(image)
function only renders the area inside of the bounding box, we often need to reset the size of the bounding box for text: setBounds(left, right, top, bottom, text(string))
. You may have to play around with showBounds
a bit to choose the correct values for the bounds. Here is an example:
Section 3. Position and Manipulation
We can combine two images and control their relative positions using the following functions:
Function | Return | Alternative operation |
---|---|---|
beside(a, b) | Image a is on the left of image b. The centers of a and b are aligned | a ||| b |
above (a, b) | Image a is vertically above image b. The centers of a and b are aligned | a --- b |
on(a, b) | Image a on top of image b. The centers of a and b are superimposed | a +++ b |
The operator symbols should remind you of how a and b are arranged; imagine either drawing a line between them (|
or -
) or centering one on the other (+
).
We can also scale, rotate, and translate the image:
Function | Arguments | Effect |
---|---|---|
rotate(a, img) | Angle a (degrees) and image img | Rotate img by angle a clockwise. |
translate(dx, dy, img) | Changes in x- and y-coordinates dx and dy, image img | Translate the points of img from (x, y) to (x + dx, y + dy). |
translateP(p, img) | Point p and image img | Translate the origin of img to point p. |
scalexy(sx, sy, img) | Horizontal and vertical scale factors sx and sy, image img | Scale image horizontally by sx and vertically by sy. |
scale(s, img) | Scale factor s and image img | Scale img in both directions by factor s. |
setBounds(l, r, t, b, img) | Min x, max x , min y, max y of the new bounding box respectively, and the image img | Create a new image that looks just like img, except its bounding box has the specified coordinates. The origin is unchanged. |
For example:
Focus(position, img)
is a special case of the translate(dx, dy, img)
function. It produce a new image based on image img with the origin at the specified point on its bounding box. position
is a type that has the nine following cases: TL (top left), TC (top center), TR (top right), ML (middle left), MC (middle center), MR (middle right), BL (bottom left), BC (bottom center), BR (bottom right).
Every image has a bounding box and a reference point. At the begining when the image is created, the reference point of the image is at the center. Translating an image (via the translate
, translateP
, or focus
functions) translates the whole image but leaves the reference point behind. Think of the reference point as a spot on a table, and the image starts off as a piece of paper centered over that spot. Translating amounts to shifting the paper so that a different point is over the spot. Putting two images together with ||| or --- is like pushing two tables next to each other, lining up their spots horizontally or vertically. The papers come along for the ride and overlap as the tables are shifted. When you're done, you imagine a new combined table with a new spot underneath the overlapped (and now merged) papers.
Section 4. Format
The image
type can be formatted using the following functions:
Functions | Arguments | Effect |
---|---|---|
fill(c, img) | Color c (color ) and image img | Fill img with color c. |
stroke(c, img) | Color c (color ) and image img | Change the border of img to color c. |
solid(c, img) | Color c (color ) and image img | Change the border of img and fill it with color c. |
strokeWidth(w, img) | Thickness w (float ) and image img | Change the thickness of img's border to w. |
dashed(img) | image img | Draw the border of img with dashed lines. |
withFont(fontSize, fontFamily, fontWeight, fontStyle, img) | Font size (float ), font family (fontFamily ), font weight (fontWeight ), font style (fontStyle ), and image img | Format the text in img as specified. |
The type color
can be generated by one of the following functions:
color(c)
: Takes a named CSS Level 4 color (see list below) as itsstring
argument.rgb(r, g, b)
: Create a color with the given red, green, and blue components. All arguments should be integers between 0 and 255.rgba(r, g, b, a)
: Similar to thergb(r, g, b)
function but also have a fourth argument which is thealpha
level. Alpha level determines the opacity of the color and it should be a float between 0 and 1. An alpha of 1.0 is fully opaque, while 0.0 is fully transparent.hsl(h, s, l)
: Create a color with the given hue, saturation, and lightness components. The first argument (hue) should be a float from 0 to 360, representing an angle in the color wheel (0 is red, 120 is green, and 240 is blue). The second argument (saturation) should be a float from 0 to 1, measuring how pure the hue is (1 is fully pure, while 0 is a shade of gray). The third argument (lightness) should be a float from 0 to 1, measuring how close to white (1) or black (0) it is. For example, a fully pure blue is represented by (hue, saturation, lightness) numbers (240., 1., 0.5).hsla(h, s, l, a)
: similar to thehsl(h, s, l)
function, but also have the alpha level as thergba(r, g, b, a)
function.
Here are the known named colors:
transparent | aliceBlue | antiqueWhite | aqua |
aquamarine | azure | beige | bisque |
black | blanchedAlmond | blue | blueViolet |
brown | burlyWood | cadetBlue | chartreuse |
chocolate | coral | cornflowerBlue | cornSilk |
cyan | darkBlue | darkCyan | darkGoldenrod |
darkGray | darkGrey | darkGreen | darkKhaki |
darkMagenta | darkOliveGreen | darkOrange | darkOrchid |
darkRed | darkSalmon | darkSeaGreen | darkSlateBlue |
darkSlateGray | darkSlateGrey | darkTurquoise | darkViolet |
deepPink | deepSkyBlue | dimGray | dimGrey |
dodgerBlue | fireBrick | floralWhite | forestGreen |
fuchsia | gainsboro | ghostWhite | gold |
goldenrod | gray | grey | green |
greenYellow | honeydew | hotpink | indianRed |
indigo | ivory | khaki | lavender |
lavenderBlush | lawngreen | lemonChiffon | lightBlue |
lightCoral | lightCyan | lightGoldenrodYellow | lightGray |
lightGrey | lightGreen | lightPink | lightSalmon |
lightSeaGreen | lightSkyBlue | lightSlateGray | lightSlateGrey |
lightSteelBlue | lightYellow | lime | limeGreen |
linen | magenta | maroon | mediumAquamarine |
mediumBlue | mediumOrchid | mediumPurple | mediumSeaGreen |
mediumSlateBlue | mediumSpringGreen | mediumTurquoise | mediumVioletRed |
midnightBlue | mintCream | mistyRose | moccasin |
navajoWhite | navy | oldLace | olive |
oliveDrab | orange | orangeRed | orchid |
paleGoldenrod | paleGreen | paleTurquoise | paleVioletRed |
papayaWhip | peachPuff | peru | pink |
plum | powderBlue | purple | rebeccaPurple |
red | rosyBrown | royalBlue | saddleBrown |
salmon | sandyBrown | seaGreen | seaShell |
sienna | silver | skyBlue | slateBlue |
slateGray | slateGrey | snow | springGreen |
steelBlue | tan | teal | thistle |
tomato | turquoise | violet | wheat |
white | whiteSmoke | yellow | yellowGreen |
Arguments for the withFont(fontSize, fontFamily, fontWeight, fontStyle, img)
function have the following values:
fontSize
is afloat
, where 1.0 gives the default size (about 16.0 units from the top of a capital letter to the bottom of a descending stroke).fontFamily
is a type that has 3 cases: Mono, Sans, and Serif.fontWeight
is a type that has 2 cases: Bold and Regular.fontStyle
is a type that has 2 cases: Italic and Normal.
Here are some examples:
Here is an example of text formatting:
Section 5. Some Demonstrations
Here is an arrow using openPath
. This also shows examples of using focus
and showBounds
.
Using the arrow, here is a function to visualize a linked list:
The second list is smaller because the draw
function will adjust the scale so that the entire bounding box is displayed on screen.