context docs

This commit is contained in:
Ryan Oldenburg 2021-05-19 13:09:00 -05:00
parent 907fd88d31
commit 58c262849d
2 changed files with 107 additions and 19 deletions

View file

@ -1,7 +1,10 @@
import bumpy, chroma, pixie/blends, pixie/common, pixie/fonts, pixie/images,
pixie/paints, pixie/paths, vmath
# https://developer.mozilla.org/en-US/docs/Web/API/ContextRenderingContext2D
## This file provides a Nim version of the Canvas 2D API commonly used on the
## web. The goal is to make picking up Pixie easy for developers familiar with
## using CanvasRenderingContext2D on the web. For more info, see:
## https://developer.mozilla.org/en-US/docs/Web/API/ContextRenderingContext2D
type
Context* = ref object
@ -26,6 +29,7 @@ type
textAlign: HAlignMode
proc newContext*(image: Image): Context =
## Create a new Context that will draw to the parameter image.
result = Context()
result.image = image
result.mat = mat3()
@ -34,53 +38,87 @@ proc newContext*(image: Image): Context =
result.strokeStyle = Paint(kind: pkSolid, color: rgbx(0, 0, 0, 255))
proc newContext*(width, height: int): Context {.inline.} =
## Create a new Context that will draw to a new image of width and height.
newContext(newImage(width, height))
proc beginPath*(ctx: Context) {.inline.} =
## Starts a new path by emptying the list of sub-paths.
ctx.path = Path()
proc moveTo*(ctx: Context, v: Vec2) {.inline.} =
## Begins a new sub-path at the point (x, y).
ctx.path.moveTo(v)
proc moveTo*(ctx: Context, x, y: float32) {.inline.} =
## Begins a new sub-path at the point (x, y).
ctx.moveTo(vec2(x, y))
proc lineTo*(ctx: Context, v: Vec2) {.inline.} =
## Adds a straight line to the current sub-path by connecting the sub-path's
## last point to the specified (x, y) coordinates.
ctx.path.lineTo(v)
proc lineTo*(ctx: Context, x, y: float32) {.inline.} =
## Adds a straight line to the current sub-path by connecting the sub-path's
## last point to the specified (x, y) coordinates.
ctx.lineTo(vec2(x, y))
proc bezierCurveTo*(ctx: Context, cp1, cp2, to: Vec2) {.inline.} =
## Adds a cubic Bézier curve to the current sub-path. It requires three
## points: the first two are control points and the third one is the end
## point. The starting point is the latest point in the current path,
## which can be changed using moveTo() before creating the Bézier curve.
ctx.path.bezierCurveTo(cp1, cp2, to)
proc bezierCurveTo*(
ctx: Context, cp1x, cp1y, cp2x, cp2y, x, y: float32
) {.inline.} =
## Adds a cubic Bézier curve to the current sub-path. It requires three
## points: the first two are control points and the third one is the end
## point. The starting point is the latest point in the current path,
## which can be changed using moveTo() before creating the Bézier curve.
ctx.bezierCurveTo(vec2(cp1x, cp1y), vec2(cp2x, cp2y), vec2(x, y))
proc quadraticCurveTo*(ctx: Context, cpx, cpy, x, y: float32) {.inline.} =
## Adds a quadratic Bézier curve to the current sub-path. It requires two
## points: the first one is a control point and the second one is the end
## point. The starting point is the latest point in the current path,
## which can be changed using moveTo() before creating the quadratic
## Bézier curve.
ctx.path.quadraticCurveTo(cpx, cpy, x, y)
proc quadraticCurveTo*(ctx: Context, ctrl, to: Vec2) {.inline.} =
## Adds a quadratic Bézier curve to the current sub-path. It requires two
## points: the first one is a control point and the second one is the end
## point. The starting point is the latest point in the current path,
## which can be changed using moveTo() before creating the quadratic
## Bézier curve.
ctx.path.quadraticCurveTo(ctrl, to)
proc closePath*(ctx: Context) {.inline.} =
## Attempts to add a straight line from the current point to the start of
## the current sub-path. If the shape has already been closed or has only
## one point, this function does nothing.
ctx.path.closePath()
proc rect*(ctx: Context, rect: Rect) {.inline.} =
## Adds a rectangle to the current path.
ctx.path.rect(rect)
proc rect*(ctx: Context, x, y, width, height: float32) {.inline.} =
## Adds a rectangle to the current path.
ctx.path.rect(x, y, width, height)
proc ellipse*(ctx: Context, center: Vec2, rx, ry: float32) {.inline.} =
## Adds an ellipse to the current sub-path.
ctx.path.ellipse(center, rx, ry)
proc ellipse*(ctx: Context, x, y, rx, ry: float32) {.inline.} =
## Adds an ellipse to the current sub-path.
ctx.path.ellipse(x, y, rx, ry)
proc fill*(ctx: Context, path: Path, windingRule = wrNonZero) {.inline.} =
## Fills the current or given path with the current fillStyle.
ctx.image.fillPath(
path,
ctx.fillStyle,
@ -89,9 +127,11 @@ proc fill*(ctx: Context, path: Path, windingRule = wrNonZero) {.inline.} =
)
proc fill*(ctx: Context, windingRule = wrNonZero) {.inline.} =
## Fills the current or given path with the current fillStyle.
ctx.fill(ctx.path, windingRule)
proc stroke*(ctx: Context, path: Path) {.inline.} =
## Sstrokes (outlines) the current or given path with the current strokeStyle.
ctx.image.strokePath(
path,
ctx.strokeStyle,
@ -102,25 +142,32 @@ proc stroke*(ctx: Context, path: Path) {.inline.} =
)
proc stroke*(ctx: Context) {.inline.} =
## Sstrokes (outlines) the current or given path with the current strokeStyle.
ctx.stroke(ctx.path)
proc clearRect*(ctx: Context, rect: Rect) =
## Erases the pixels in a rectangular area.
var path: Path
path.rect(rect)
ctx.image.fillPath(path, rgbx(0, 0, 0, 0), ctx.mat, blendMode = bmOverwrite)
proc clearRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
## Erases the pixels in a rectangular area.
ctx.clearRect(rect(x, y, width, height))
proc fillRect*(ctx: Context, rect: Rect) =
## Draws a rectangle that is filled according to the current fillStyle.
var path: Path
path.rect(rect)
ctx.image.fillPath(path, ctx.fillStyle, ctx.mat)
proc fillRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
## Draws a rectangle that is filled according to the current fillStyle.
ctx.fillRect(rect(x, y, width, height))
proc strokeRect*(ctx: Context, rect: Rect) =
## Draws a rectangle that is stroked (outlined) according to the current
## strokeStyle and other context settings.
var path: Path
path.rect(rect)
ctx.image.strokePath(
@ -133,9 +180,14 @@ proc strokeRect*(ctx: Context, rect: Rect) =
)
proc strokeRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
## Draws a rectangle that is stroked (outlined) according to the current
## strokeStyle and other context settings.
ctx.strokeRect(rect(x, y, width, height))
proc fillText*(ctx: Context, text: string, at: Vec2) =
## Draws a text string at the specified coordinates, filling the string's
## characters with the current fillStyle
if ctx.font.typeface == nil:
raise newException(PixieError, "No font has been set on this Context")
@ -152,9 +204,14 @@ proc fillText*(ctx: Context, text: string, at: Vec2) =
)
proc fillText*(ctx: Context, text: string, x, y: float32) {.inline.} =
## Daws the outlines of the characters of a text string at the specified
## coordinates.
ctx.fillText(text, vec2(x, y))
proc strokeText*(ctx: Context, text: string, at: Vec2) =
## Daws the outlines of the characters of a text string at the specified
## coordinates.
if ctx.font.typeface == nil:
raise newException(PixieError, "No font has been set on this Context")
@ -174,42 +231,61 @@ proc strokeText*(ctx: Context, text: string, at: Vec2) =
)
proc strokeText*(ctx: Context, text: string, x, y: float32) {.inline.} =
## Daws the outlines of the characters of a text string at the specified
## coordinates.
ctx.strokeText(text, vec2(x, y))
proc getTransform*(ctx: Context): Mat3 {.inline.} =
## Retrieves the current transformation matrix being applied to the context.
ctx.mat
proc setTransform*(ctx: Context, transform: Mat3) {.inline.} =
## Overridest transformation matrix being applied to the context.
ctx.mat = transform
proc setTransform*(ctx: Context, a, b, c, d, e, f: float32) {.inline.} =
## Overridest transformation matrix being applied to the context.
ctx.mat = mat3(a, b, 0, c, d, 0, e, f, 1)
proc transform*(ctx: Context, transform: Mat3) {.inline.} =
## Multiplies the current transform with the matrix described by the
## arguments of this method.
ctx.mat = ctx.mat * transform
proc transform*(ctx: Context, a, b, c, d, e, f: float32) {.inline.} =
## Multiplies the current transform with the matrix described by the
## arguments of this method.
ctx.transform(mat3(a, b, 0, c, d, 0, e, f, 1))
proc translate*(ctx: Context, v: Vec2) {.inline.} =
## Adds a translation transformation to the current matrix.
ctx.mat = ctx.mat * translate(v)
proc translate*(ctx: Context, x, y: float32) {.inline.} =
## Adds a translation transformation to the current matrix.
ctx.mat = ctx.mat * translate(vec2(x, y))
proc scale*(ctx: Context, v: Vec2) {.inline.} =
## Adds a scaling transformation to the canvas units horizontally and/or
## vertically.
ctx.mat = ctx.mat * scale(v)
proc scale*(ctx: Context, x, y: float32) {.inline.} =
## Adds a scaling transformation to the canvas units horizontally and/or
## vertically.
ctx.mat = ctx.mat * scale(vec2(x, y))
proc rotate*(ctx: Context, angle: float32) {.inline.} =
## Adds a rotation to the transformation matrix.
ctx.mat = ctx.mat * rotate(-angle)
proc resetTransform*(ctx: Context) {.inline.} =
## Resets the current transform to the identity matrix.
ctx.mat = mat3()
proc save*(ctx: Context) =
## Saves the entire state of the canvas by pushing the current state onto
## a stack.
var state: ContextState
state.mat = ctx.mat
state.fillStyle = ctx.fillStyle
@ -222,6 +298,9 @@ proc save*(ctx: Context) =
ctx.stateStack.add(state)
proc restore*(ctx: Context) =
## Restores the most recently saved canvas state by popping the top entry
## in the drawing state stack. If there is no saved state, this method does
## nothing.
if ctx.stateStack.len > 0:
let state = ctx.stateStack.pop()
ctx.mat = state.mat

View file

@ -304,34 +304,38 @@ proc addPath*(path: var Path, other: Path) =
path.commands.add(other.commands)
proc closePath*(path: var Path) =
## Closes a path (draws a line to the start).
## Attempts to add a straight line from the current point to the start of
## the current sub-path. If the shape has already been closed or has only
## one point, this function does nothing.
path.commands.add(PathCommand(kind: Close))
path.at = path.start
proc moveTo*(path: var Path, x, y: float32) =
## Moves the current drawing pen to a new position and starts a new shape.
## Begins a new sub-path at the point (x, y).
path.commands.add(PathCommand(kind: Move, numbers: @[x, y]))
path.start = vec2(x, y)
path.at = path.start
proc moveTo*(path: var Path, v: Vec2) {.inline.} =
## Moves the current drawing pen to a new position and starts a new shape.
## Begins a new sub-path at the point (x, y).
path.moveTo(v.x, v.y)
proc lineTo*(path: var Path, x, y: float32) =
## Adds a line.
## Adds a straight line to the current sub-path by connecting the sub-path's
## last point to the specified (x, y) coordinates.
path.commands.add(PathCommand(kind: Line, numbers: @[x, y]))
path.at = vec2(x, y)
proc lineTo*(path: var Path, v: Vec2) {.inline.} =
## Adds a line.
## Adds a straight line to the current sub-path by connecting the sub-path's
## last point to the specified (x, y) coordinates.
path.lineTo(v.x, v.y)
proc bezierCurveTo*(path: var Path, x1, y1, x2, y2, x3, y3: float32) =
## Adds a cubic Bézier curve to the path. This requires three points.
## The first two points are control points and the third is the end point.
## The starting point is the last point in the current path, which can be
## changed using moveTo() before creating the curve.
## Adds a cubic Bézier curve to the current sub-path. It requires three
## points: the first two are control points and the third one is the end
## point. The starting point is the latest point in the current path,
## which can be changed using moveTo() before creating the Bézier curve.
path.commands.add(PathCommand(
kind: Cubic,
numbers: @[x1, y1, x2, y2, x3, y3]
@ -339,13 +343,18 @@ proc bezierCurveTo*(path: var Path, x1, y1, x2, y2, x3, y3: float32) =
path.at = vec2(x3, y3)
proc bezierCurveTo*(path: var Path, ctrl1, ctrl2, to: Vec2) {.inline.} =
## Adds a cubic Bézier curve to the current sub-path. It requires three
## points: the first two are control points and the third one is the end
## point. The starting point is the latest point in the current path,
## which can be changed using moveTo() before creating the Bézier curve.
path.bezierCurveTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, to.x, to.y)
proc quadraticCurveTo*(path: var Path, x1, y1, x2, y2: float32) =
## Adds a quadratic Bézier curve to the path. This requires 2 points.
## The first point is the control point and the second is the end point.
## The starting point is the last point in the current path, which can be
## changed using moveTo() before creating the curve.
## Adds a quadratic Bézier curve to the current sub-path. It requires two
## points: the first one is a control point and the second one is the end
## point. The starting point is the latest point in the current path,
## which can be changed using moveTo() before creating the quadratic
## Bézier curve.
path.commands.add(PathCommand(
kind: Quad,
numbers: @[x1, y1, x2, y2]
@ -353,11 +362,11 @@ proc quadraticCurveTo*(path: var Path, x1, y1, x2, y2: float32) =
path.at = vec2(x2, y2)
proc quadraticCurveTo*(path: var Path, ctrl, to: Vec2) {.inline.} =
## Adds a quadratic Bézier curve to the path. This requires 2 points.
## The first point is the control point and the second is the end point.
## The starting point is the last point in the current path, which can be
## changed using moveTo() before creating the curve.
##
## Adds a quadratic Bézier curve to the current sub-path. It requires two
## points: the first one is a control point and the second one is the end
## point. The starting point is the latest point in the current path,
## which can be changed using moveTo() before creating the quadratic
## Bézier curve.
path.quadraticCurveTo(ctrl.x, ctrl.y, to.x, to.y)
# proc arcTo*(path: var Path, ctrl1, ctrl2: Vec2, radius: float32) {.inline.} =