ctx
module
Note
Some functions might be better documented in the upstream uctx docs
Apologies for the divergence.
- class ctx.Context(*args, **kwargs)
Context is the rendering/rasterization API used by st3m.
It’s a WebCanvas-style vector API, with an implicit pen which can be moved and can draw lines, arcs, text, etc.
In st3m, the Context object is backed by a drawlist generator. That is, any operation performed on the ctx object will cause an entry to be added to an in-memory draw list. Then, when the rasterizer is ready, it will rasterize said drawlist to pixels in a separate thread.
A Context object is passed to all draw() calls to Responder. This object should only be used within the lifecycle of the draw method and must not be persisted.
For more information, see: https://ctx.graphics/
- add_stop(pos: float, color: Tuple[float, float, float], alpha: float) Context
Adds a color stop for a linear or radial gradient. Pos is a position between 0.0 and 1.0. Should be called after linear_gradient or radial_gradient.
This example adds a radial gradiant to a rectangle:
>>> ctx.radial_gradient(30, 30, 80, -30, -50, 70) >>> ctx.add_stop(0, (100,0,100), 0.5) >>> ctx.add_stop(1, (100,0,0), 0.8) >>> ctx.rectangle(-100, -100, 200, 200).fill()
- apply_transform(a: float, b: float, c: float, d: float, e: float, f: float, g: float, h: float, i: float) Context
Adds a 3x3 matrix on top of the existing user to device space transform.
TODO(q3k): we probably shouldn’t expose this
- arc(x: float, y: float, radius: float, angle1: float, angle2: float, direction: bool) Context
Add a circular arc to the current sub-path at the coordinates x,y with a radius of r for corners and angle1 in radians specifying the angle at which the arc starts and angle2 in radians specifying the angle at which the arc ends. A truthful value for the direction draws the arc counter-clockwise between the start and end angles.
This example places two circles next to each other with some overlap. One is colored red and the other blue. The blue circle is on top of the red circle and where they overlap, only the blue of the blue circle is visible.
>>> # red left circle >>> ctx.rgb(1.0, 0, 0).arc(-30, 0, 40, 0, 2 * math.pi, True).fill() >>> # blue right circle >>> ctx.rgb(0, 0, 1.0).arc(30, 0, 40, 0, 2 * math.pi, True).fill()
- clip() Context
Use the current path as a clipping mask, subsequent draw calls are limited by the path. The only way to increase the visible area is to first call save and then later restore to undo the clip.
This example draws a blue circle and clips a square out of it and colors it red, resulting in a quarter of the circle being red.
>>> # Blue circle >>> ctx.arc(0, 0, 60, 0, 2 * math.pi, True) >>> ctx.rgb(0, 0, 255).fill() >>> # Define a clipping area for the circle >>> ctx.arc(0, 0, 60, 0, 2 * math.pi, True) >>> ctx.clip() >>> # Add the shape into the clipping area >>> ctx.rectangle(0, 0, 80, 80) >>> ctx.rgb(255, 0, 0).fill()
- close_path() Context
Close the current open path with a curve back to where the current sequence of path segments started.
This example draws a triangle:
>>> ctx.rgb(1.0, 0, 0).begin_path() >>> ctx.move_to(-30, -30) >>> ctx.line_to(-30, 30) >>> ctx.line_to(30, 0) >>> ctx.close_path() >>> ctx.stroke()
- curve_to(cx0: float, cy0: float, cx1: float, cy1: float, x: float, y: float) Context
Add a cubic bezier segment to current path, with control handles at cx0, cy0 and cx1,cy1, the virtual pens new position is x, y.
This example draws a red Bézier curve based on start and end points and the control points. The start and end points are additionally drawn in blue and green for the visualization.
>>> ctx.rgb(1, 0, 0).begin_path() >>> # start point: (-80, -80) >>> # first control point (a, b): (-70, -0) >>> # second control point (c, d): (60, 20) >>> # end point (e, f): (60, 80) >>> # Add cubic bézier curve >>> ctx.move_to(-80,-80) >>> ctx.curve_to(-70, 0, 60, 20, 60, 80) >>> ctx.stroke() >>> # Show start and end points in blue >>> ctx.rgb(0, 0, 1).begin_path() >>> ctx.arc(-80,-80, 5, 0, 2 * math.pi, True) # start point >>> ctx.arc(60, 80, 5, 0, 2 * math.pi, True) # end point >>> ctx.fill() >>> # Show control points in green >>> ctx.rgb(0, 1, 0).begin_path() >>> ctx.arc(-70, 0, 5, 0, 2 * math.pi, True) # first control point >>> ctx.arc(60, 20, 5, 0, 2 * math.pi, True) # second control point >>> ctx.fill()
- fill() Context
Fill the current path with the current source (color, gradient or texture).
This is a simple example renders a red square:
>>> ctx.rgb(0.8, 0, 0) >>> ctx.rectangle(0, -40, 5, 5).fill()
- get_font_name(ix: int) str
Returns font name from internal font index. See ctx_config.h for defined fonts.
TODO(q3k): expose these font indices into mpy
- gray(a: float) Context
Set current draw color to a floating point grayscale value from 0 to 1.
This example places three circles next to each other with some overlap. One is black, the next gray, and the last one white.
>>> ctx.gray(0.0).arc(-30, 0, 40, 0, 2 * math.pi, True).fill() >>> ctx.gray(0.5).arc(0, 0, 40, 0, 2 * math.pi, True).fill() >>> ctx.gray(1.0).arc(30, 0, 40, 0, 2 * math.pi, True).fill()
- image(path: str, x: float, y: float, w: float, h: float, clip_x: Optional[float] = None, clip_y: Optional[float] = None, clip_width: Optional[float] = None, clip_height: Optional[float] = None) Context
Draw the image at path a in a rectangle with upper left coordinates at x,y which is w wide and h high. If w or h is -1 the other is set maintaining aspect ratio, if both are -1 the pixel dimensions of the image is used. Supports PNG and JPEG files.
- line_to(x: float, y: float) Context
Adds a line segment to the path, from the position of the virtual pen to the given coordinates, the coordinates are the new position of the virtual pen.
This example draws two lines on the screen perpendicular to each other:
>>> ctx.rgb(0, 1, 0).begin_path() >>> ctx.move_to(-120,0) >>> ctx.line_to(120, 0) >>> ctx.move_to(0, 120) >>> ctx.line_to(0, -120) >>> ctx.stroke()
- linear_gradient(x0: float, y0: float, x1: float, y1: float) Context
Change the source to a linear gradient from x0,y0 to x1,y1, by default an empty gradient from black to white exists, add stops with add_stop to specify a custom gradient.
This is an example rendering a rainbow gradient on the right side of the screen:
>>> ctx.linear_gradient(0.18*120,0.5*120,0.95*120,0.5*120) >>> ctx.add_stop(0.0, [1.0,0.0,0.0], 1.0) >>> ctx.add_stop(0.2, [1.0,1.0,0.0], 1.0) >>> ctx.add_stop(0.4, [0.0,1.0,0.0], 1.0) >>> ctx.add_stop(0.6, [0.0,1.0,1.0], 1.0) >>> ctx.add_stop(0.8, [0.0,0.0,1.0], 1.0) >>> ctx.add_stop(1.0, [1.0,0.0,1.0], 1.0) >>> ctx.rectangle(-120, -120, 240, 240) >>> ctx.fill()
- logo(x: float, y: float, dim: float) Context
Draws the ctx logo, centered at x,y with a footprint of roughly dim units.
- move_to(x: float, y: float) Context
Moves the virtual pen to the given coordinates without drawing anything.
This example draws two lines on the screen perpendicular to each other:
>>> ctx.rgb(0, 1, 0).begin_path() >>> ctx.move_to(-120,0) >>> ctx.line_to(120, 0) >>> ctx.move_to(0, 120) >>> ctx.line_to(0, -120) >>> ctx.stroke()
- quad_to(cx: float, cy: float, x: float, y: float) Context
Add a quadratic bezier segment to current path with control point at cx,cy ending at x,y.
- radial_gradient(x0: float, y0: float, r0: float, x1: float, y1: float, r1: float) Context
Change the source to a radial gradient from a circle x0,y0 with radius0 to an outer circle x1,y1 with radidus r1.
This example adds a radial gradiant to a rectangle:
>>> ctx.radial_gradient(30, 30, 80, -30, -50, 70) >>> ctx.add_stop(0, (100,0,100), 0.5) >>> ctx.add_stop(1, (100,0,0), 0.8) >>> ctx.rectangle(-100, -100, 200, 200).fill()
NOTE: currently only the second circle’s origin is used, but both radiuses are in use.
- rectangle(x: float, y: float, w: float, h: float) Context
Trace the outline of a rectangle with upper left coordinates at x,y which is w wide and h high.
This is a simple example renders a red square:
>>> ctx.rgb(0.8, 0, 0) >>> ctx.rectangle(0, -40, 5, 5).fill()
- rel_curve_to(cx0: float, cy0: float, cx1: float, cy1: float, x: float, y: float) Context
Adds a cubic bezier segment using relative coordinates, behaves like curve_to but all coordinate arguments are relative to the coordinates of the virtual pen when called.
- rel_line_to(x: float, y: float) Context
Adds a line segment from the current point to current_x + x, current_y + y.
- rel_move_to(x: float, y: float) Context
Moves the virtual pen a relative amount without adding a segment to the current path.
- rel_quad_to(cx: float, cy: float, x: float, y: float) Context
Adds a quadratic bezier segment using relative coordinates, behaves like quad_to but all coordinate arguments are relative to the coordinates of the virtual pen when called.
- restore() Context
Restores the state previously saved with save, calls to save/restore should be balanced.
This example draws red squares and then saves teh state, draws one green square, and restores the state:
>>> ctx.rgb(200, 0, 0) >>> ctx.rectangle(0, -40, 5, 5).fill() >>> ctx.rectangle(0, -30, 5, 5).fill() >>> ctx.rectangle(0, -20, 5, 5).fill() >>> ctx.save() >>> ctx.rgb(0, 200, 0).rectangle(0, -10, 5, 5).fill() >>> # Restore to the state saved by the most recent call to save() >>> ctx.restore() >>> ctx.rectangle(0, 0, 5, 5).fill() >>> ctx.rectangle(0, 10, 5, 5).fill() >>> ctx.rectangle(0, 20, 5, 5).fill() >>> ctx.rectangle(0, 30, 5, 5).fill() >>> ctx.rectangle(0, 40, 5, 5).fill()
- rgb(r: float, g: float, b: float) Context
Set current draw color to an RGB color defined by component values from 0 to 1.
This example places two circles next to each other with some overlap. One is colored red and the other blue. The blue circle is on top of the red circle and where they overlap, only the blue of the blue circle is visible.
>>> # red left circle >>> ctx.rgb(1.0, 0, 0).arc(-30, 0, 40, 0, 2 * math.pi, True).fill() >>> # blue right circle >>> ctx.rgb(0, 0, 1.0).arc(30, 0, 40, 0, 2 * math.pi, True).fill()
- rgba(r: float, g: float, b: float, a: float) Context
Set current draw color to an RGBA color defined by component values from 0 to 1.
This example places two circles next to each other with some overlap. One is colored red and the other blue with 50% opacity. The blue circle is on top of the red circle and where they overlap, you can see the red through the blue.
>>> # red left circle >>> ctx.rgba(255, 0, 0, 1).arc(-30, 0, 40, 0, 2 * math.pi, True).fill() >>> # blue right circle, 50% opacity >>> ctx.rgba(0, 0, 100, 0.5).arc(30, 0, 40, 0, 2 * math.pi, True).fill()
- rotate(x: float) Context
Add rotation to the user to device space transform by specified radians (angle * math.pi / 180).
- round_rectangle(x: float, y: float, w: float, h: float, r: float) Context
Trace the outline of a rectangle with upper left coordinates at x,y which is w wide and h high. With quarter circles with a radius of r for corners.
- save() Context
Stores the transform, clipping state, fill and stroke sources, font size, stroking and dashing options.
This example draws red squares and then saves teh state, draws one green square, and restores the state:
>>> ctx.rgb(200, 0, 0) >>> ctx.rectangle(0, -40, 5, 5).fill() >>> ctx.rectangle(0, -30, 5, 5).fill() >>> ctx.rectangle(0, -20, 5, 5).fill() >>> ctx.save() >>> ctx.rgb(0, 200, 0).rectangle(0, -10, 5, 5).fill() >>> # Restore to the state saved by the most recent call to save() >>> ctx.restore() >>> ctx.rectangle(0, 0, 5, 5).fill() >>> ctx.rectangle(0, 10, 5, 5).fill() >>> ctx.rectangle(0, 20, 5, 5).fill() >>> ctx.rectangle(0, 30, 5, 5).fill() >>> ctx.rectangle(0, 40, 5, 5).fill()
- scope() Context
Draw an audio ‘oscilloscope’-style visualizer at -120,-120,120,120 bounding box.
Needs to be stroked/filled afterwards.
- stroke() Context
Stroke the current path with the current source (color, gradient or texture), with current line_width
This example draws a triangle:
>>> ctx.rgb(1.0, 0, 0).begin_path() >>> ctx.move_to(-30, -30) >>> ctx.line_to(-30, 30) >>> ctx.line_to(30, 0) >>> ctx.close_path() >>> ctx.stroke()
- text(text: str) Context
Add a text fragment using the current fill source, font and font size.
This example draws the text Hello world on a red rectangle:
>>> ctx.font = ctx.get_font_name(5) >>> ctx.rgb(0.4, 0, 0).rectangle(-120,-120,240,240).fill() >>> ctx.rgb(1.0, 0, 0).move_to(-80,0).text("Hello world")
- text_width(text: str) float
Calculates width of rendered text, without rendering it.
- ALPHABETIC: str = 'alphabetic'
- BOTTOM: str = 'bottom'
- CENTER: str = 'center'
- END: str = 'end'
- HANGING: str = 'hanging'
- IDEOGRAPHIC: str = 'ideographic'
- JUSTIFY: str = 'justify'
- LEFT: str = 'left'
- MIDDLE: str = 'middle'
- RIGHT: str = 'right'
- START: str = 'start'
- TOP: str = 'top'
- font: str = ''
- font_size: float = 10
- global_alpha: float = 1.0
- image_smoothing: bool = True
- line_width: float = 1.0
- text_align: str = 'start'
- text_baseline: str = 'alphabetic'