A simple, pgfplots-like function plotting library for Typst. Create beautiful mathematical plots with minimal code.
Note: This package is built on top of CeTZ v0.5.2.
Manual
A full manual is available in docs/manual.pdf.
Gallery
Click on an image to see the source code.
Features
- Simple API — Plot functions with just a few lines of code
- Multiple plot types — Functions, scatter plots, line plots with markers
- Customizable axes — Position, labels, ticks, and tick labels; default $x$/$y$ labels at arrow tips (tkz-fct style)
- Stealth arrows — Elegant axis arrowheads matching LaTeX/pgfplots style
- Axis extension — Extend axes beyond plot area for cleaner appearance
- Grid support — Major and minor grids with custom styling
- 14 marker types — Circles, squares, triangles, diamonds, stars, and more
- Origin label control — Toggle origin ‘0’ label display
- Global defaults — Set defaults for all plots in your document
- Riemann sums — Left/right/midpoint/lower/upper rectangles; endpoint dots with labels and arrows; Δx bracket; $x_i$ subdivision labels
- Volume of revolution — 3D-style solids: arbitrary axis (horizontal, shifted, or oblique), end caps, disk cross-sections, optional coordinate axes
- Zoom / spy insets — Magnified sub-views with rectangular or circular spy glass, connector lines, and customizable accent colors
- Automatic label placement — function labels avoid overlapping curves, tick labels, and axes automatically
- Smart tick density — auto-selects nice tick spacing for large ranges; crossed labels hidden automatically
- Full styling — Customize colors, strokes, backgrounds, and more
Quick Start
#import "@preview/simple-plot:1.1.0": plot
#plot(
xmin: -3, xmax: 3,
ymin: -1, ymax: 9,
show-grid: true,
(fn: x => calc.pow(x, 2), stroke: blue + 1.5pt),
)
Axis labels default to $x$ and $y$ — no need to set them explicitly.
Basic Usage
Plotting Functions
#import "@preview/simple-plot:1.1.0": plot
// Single function
#plot(
xmin: -5, xmax: 5,
ymin: -5, ymax: 5,
show-grid: "major",
(fn: x => calc.sin(x), stroke: blue + 1.5pt),
)
// Multiple functions
#plot(
xmin: -2 * calc.pi, xmax: 2 * calc.pi,
ymin: -1.5, ymax: 1.5,
(fn: x => calc.sin(x), stroke: blue + 1.2pt, label: $sin(x)$),
(fn: x => calc.cos(x), stroke: red + 1.2pt, label: $cos(x)$),
)
Scatter Plots
#import "@preview/simple-plot:1.1.0": plot, data
#plot(
xmin: 0, xmax: 10,
ymin: 0, ymax: 10,
show-grid: true,
data(
((1, 2), (2, 3.5), (3, 2.8), (4, 5.2), (5, 4.8)),
mark: "*",
mark-fill: blue,
),
)
Line Plots with Markers
#import "@preview/simple-plot:1.1.0": plot, line-plot
#plot(
xmin: 0, xmax: 10,
ymin: 0, ymax: 12,
axis-x-pos: "bottom",
axis-y-pos: "left",
line-plot(
((0, 0), (1, 0.5), (2, 1.8), (3, 4.2), (4, 5.1)),
stroke: blue + 1.2pt,
mark: "*",
mark-fill: blue,
),
)
Function Labels with Positioning
Control the placement of function labels using label-pos and label-side:
#plot(
xmin: -5, xmax: 5,
ymin: -3, ymax: 5,
show-grid: true,
(fn: x => 0.2 * calc.pow(x, 2) - 2,
stroke: blue + 1.5pt,
label: $f(x)$,
label-pos: 0.9, // position along curve (0–1)
label-side: "below-right" // placement relative to point
),
(fn: x => -0.5 * x + 1,
stroke: red + 1.5pt,
label: $g(x)$,
label-pos: 0.2,
label-side: "above-right"
),
)
Available label-side options: "above", "below", "left", "right", "above-left", "above-right", "below-left", "below-right"
Mathematical Functions
| Function | Typst syntax |
|---|---|
| Power $x^n$ | calc.pow(x, n) |
| Square root | calc.sqrt(x) |
| Absolute value | calc.abs(x) |
| Sine, Cosine, Tangent | calc.sin(x), calc.cos(x), calc.tan(x) |
| Exponential $e^x$ | calc.exp(x) |
| Natural log | calc.ln(x) |
| Log base b | calc.log(x, base: b) |
Important: Use decimal notation (e.g.
2.0not2) inside lambda functions to avoid type errors.
Pedagogical Plot Helpers
Rational Function Wrapper
plot-rational is a thin wrapper around plot with defaults suited to rational-function exercises: centered axes, a visible grid, and one main function curve. Extra positional arguments are forwarded as additional series, so asymptotes and points can be added normally.
#import "@preview/simple-plot:1.1.0": plot-rational, hline
#plot-rational(
x => (x + 1) / (x - 2),
xmin: -5, xmax: 5,
ymin: -6, ymax: 6,
vertical-asymptotes: (2,),
hline(1, stroke: stroke(paint: red, thickness: 0.7pt, dash: "dashed")),
)
Local Limit Schemas
limit-schema draws compact schematic behavior near a point a. Use finite numbers for one-sided limits, "+oo" / "-oo" for vertical asymptotic behavior, and val for the defined value at the point. schema-lim is available as an alias.
#import "@preview/simple-plot:1.1.0": limit-schema
#limit-schema(a: 1, left: 4, right: 4) // removable hole
#limit-schema(a: 2, left: "+oo", right: "-oo") // vertical asymptote
#limit-schema(a: 0, left: -1, right: 1, val: 0) // jump with defined value
Parameters Reference
Plot Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
xmin, xmax |
float | -5, 5 | X-axis range |
ymin, ymax |
float | -5, 5 | Y-axis range |
width, height |
length/float | 6, 6 | Plot size — a length (2cm, 30mm, …) or a number in cm |
scale |
float | 1 | Scale factor for the entire plot |
xlabel, ylabel |
content | $x$, $y$ |
Axis labels (tkz-fct style: tucked beside the arrow tips) |
show-grid |
bool/str | false | true, false, "major", "minor", "both" |
minor-grid-step |
int | 5 | Minor grid subdivisions per major tick |
grid-label-break |
bool | true | Gap in grid lines around tick labels |
axis-x-pos |
float/str/none | 0 | X-axis position: value, "bottom", "center"; none hides the axis |
axis-y-pos |
float/str/none | 0 | Y-axis position: value, "left", "center"; none hides the axis |
axis-x-extend |
length/float/array | (0pt, 0.3cm) | Extend X-axis beyond the grid: value or (left, right). Lengths are absolute; bare numbers are data units (legacy) |
axis-y-extend |
length/float/array | (0pt, 0.3cm) | Extend Y-axis beyond the grid: value or (bottom, top). Lengths are absolute; bare numbers are data units (legacy) |
show-origin |
bool | true | Show “0” label at origin |
origin-label-offset |
array | (-0.11, -0.11) | Origin “0” label offset from (0,0) in cm |
origin-label-anchor |
str | “north-east” | Origin “0” label anchor |
origin-leader |
bool | true | Draw a subtle leader line from the origin label toward (0,0) |
origin-leader-stroke |
stroke | black + 0.6pt |
Origin leader stroke |
origin-leader-gap |
float | 0.025 | Gap from (0,0) before the leader starts (cm) |
origin-leader-end-gap |
float | 0.025 | Gap before the label anchor (cm) |
tick-label-size |
length | 0.65em | Tick label font size |
axis-label-size |
length | 0.8em | Axis label (x/y) font size |
samples |
int | 100 | Points evaluated per curve. Raise it for rapidly oscillating functions, lower it to speed up a heavy document. A single curve can override it with its own samples: key |
label-bg |
color/none | white | Background painted behind the axis labels where a grid line, a tick mark or a curve runs behind them. none turns it off — what you want on a coloured page |
tick-label-bg |
color/none | white | Same, for tick labels (painted only where a curve crosses them) |
unit-label-only |
bool | false | Show only “1” on axes for minimal style |
show-end-ticks |
bool | true | Keep the tick/label at xmax/ymax when it lands on a tick (e.g. show “5”), pushing the axis slightly past it so the arrow clears the label |
font |
str/array | document font | Font applied to all generated text (tick labels, axis labels, origin, annotations) |
label-sizing |
str | "inherit" |
"inherit": generated text takes the surrounding document text size. "plot": it takes the plot’s own tick-label-size / axis-label-size and follows scale, so the plot keeps its proportions inside unusually large or small body text |
style |
dictionary | none | Style overrides (see Custom Styling) |
series |
array | none | Pre-built array of series specs (alternative to positional args) |
Axis Label Placement
Labels default to tkz-fct style: $x$ sits just above the axis a little left of the right arrow, $y$ sits just left of the axis a little below the top arrow. This keeps them clear of the tick numbers (which sit below / left of the axis).
| Parameter | Type | Default | Description |
|---|---|---|---|
xlabel-pos |
str/array | "end" |
"end", "center", or (x, y) in data coords |
ylabel-pos |
str/array | "end" |
same |
xlabel-anchor |
str/auto | auto |
CeTZ anchor; auto → "south-east" at the right arrow tip |
ylabel-anchor |
str/auto | auto |
CeTZ anchor; auto → "north-east" at the top arrow tip |
xlabel-offset |
array | (-0.05, 0.08) |
Offset (x, y) in cm from arrow tip |
ylabel-offset |
array | (-0.08, -0.05) |
Offset (x, y) in cm from arrow tip |
Tick Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
xtick, ytick |
auto/none/array | auto | Tick positions |
xtick-step, ytick-step |
auto/float | 1 | Step between ticks |
xtick-label-step, ytick-label-step |
int | 1 | Show label every N ticks |
xtick-labels, ytick-labels |
auto/array | auto | Custom tick labels |
Function Specification
(
fn: x => ..., // Required: the function
stroke: blue + 1.2pt, // Line style
domain: (min, max), // Restrict domain
samples: 100, // Sample points
label: $f(x)$, // Label content
label-pos: 0.8, // Position along curve (0–1)
label-side: "above", // "above", "below", "left", "right", "above-left", …
mark: "o", // Marker type
mark-size: 0.1,
mark-fill: white,
mark-stroke: blue,
mark-interval: 10, // Show marker every N points
)
Riemann Sums
#import "@preview/simple-plot:1.1.0": plot, riemann-sum
#plot(
xmin: 0, xmax: 3, ymin: 0, ymax: 5,
riemann-sum(
x => calc.pow(x, 2),
domain: (0.0, 3.0),
n: 6,
method: "left",
color: blue.lighten(75%),
show-points: true, // dots at evaluation points
show-dx: true, // Δx bracket under one rectangle
show-xi: true, // x₀, x₁, …, x₆ labels along axis
),
(fn: x => calc.pow(x, 2), stroke: blue + 1.5pt),
)
riemann-sum Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
fn |
function | — | Function to integrate (positional or fn:) |
domain |
array | plot range | (a, b) |
n |
int | 4 | Number of rectangles |
method |
str | "right" |
"left", "right", "mid", "lower", "upper" |
baseline |
float | 0 | Y-level of rectangle bases |
color |
color | luma(220) |
Rectangle fill |
stroke |
stroke | luma(80) + 0.6pt |
Rectangle border |
hatch |
str/dict/none | none | Hatch pattern: "ne", "nw", "h", "v", "cross", "grid" — or (style: "ne", spacing: 5pt, stroke: red + 1pt) |
hatch-spacing |
length | 5pt |
Spacing between hatch lines |
hatch-stroke |
stroke | luma(80) + 0.5pt |
Stroke for hatch lines |
samples |
int | 20 | Samples per subinterval for "lower"/"upper" |
show-points |
bool | false | Draw dot at each evaluation point |
point-color |
color | rgb("#c94a00") |
Dot fill color |
point-size |
float | 0.07 | Dot radius in cm |
point-label |
content/auto/none | none |
Label with arrows to the nearest dots; auto = method name |
point-label-pos |
array/auto | auto |
(x, y) in data coords; auto = upper-right of dots |
show-dx |
bool | false | Draw Δx dimension bracket under one rectangle |
dx-rect |
int/auto | auto |
Rectangle index to annotate (0-based); auto = middle |
dx-label |
content | $Delta x$ |
Bracket label |
show-xi |
bool | false | Draw $x_0, x_1, \ldots, x_n$ at subdivision points |
xi-labels |
array/auto | auto |
Custom labels array; auto = subscripted $x_i$ |
xi-show-values |
bool | false | Show numeric x-values instead of $x_i$ notation |
Methods:
"left"/"right"/"mid"— evaluation at left endpoint, right endpoint, or midpoint"lower"/"upper"— true infimum/supremum (sampled within each subinterval); works for any function shape including U-curves
Volume of Revolution
#import "@preview/simple-plot:1.1.0": volume-of-revolution
#volume-of-revolution(
x => calc.sqrt(x),
domain: (0.0, 4.0),
n-disks: 5,
width: 8.0,
height: 4.0,
show-y-axis: true,
label-a: $0$,
label-b: $4$,
label-f: $f(x)=sqrt(x)$,
)
solid-of-revolution is an alias for backward compatibility.
Hollow solids (washers)
Pass inner-fn to revolve the region between two curves. The end caps become
annuli and every cross-section becomes a washer — the picture that goes with
$V = \pi \int_a^b \left( f(x)^2 - g(x)^2 \right) , dx$:
#volume-of-revolution(
x => 1.6 + 0.25 * x, // outer radius
inner-fn: x => 0.5 + 0.18 * x, // inner radius (the hole)
domain: (0.0, 4.0),
n-disks: 4,
label-f: $f$,
label-inner: $g$,
)
inner-fn must satisfy $0 \le g(x) \le f(x)$ on the domain: larger values are
clamped to the outer radius, and a hole that would leave no material is not
drawn.
volume-of-revolution Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
fn |
function | — | Outer profile function $y = f(x) > 0$ |
inner-fn |
function/none | none |
Inner profile $0 \le g(x) \le f(x)$; makes the solid hollow |
domain |
array | (0, 4) |
(a, b) — interval of revolution |
n-disks |
int | 4 | Intermediate disk cross-sections to show |
width, height |
length/float | 5, 3.5 | Canvas size — a length or a number in cm |
samples |
int | 60 | Profile sampling points |
axis-y |
float | 0 | Y-value of revolution axis (default: x-axis) |
axis-slope |
float | 0 | Slope $m$ of revolution axis: $y = mx + \text{axis_y}$ |
show-axis |
bool | true | Draw the revolution axis arrow |
show-y-axis |
bool | false | Draw a coordinate y-axis (show-yaxis is a deprecated alias) |
y-axis-x |
auto/float | auto |
X position for the y-axis; auto = left of volume |
y-axis-offset |
float | 0.45 | Gap between the y-axis and the volume when y-axis-x: auto |
y-axis-extend |
array | (0.35, 0.45) |
Y-axis padding (below, above) the volume |
show-radius-marker |
bool | false | Draw a vertical radius dimension marker at radius-marker-x |
radius-marker-x |
auto/float | auto |
X position of the radius marker; auto = left cap (yaxis-x is a deprecated alias) |
show-back |
bool | true | Draw the back face and bottom profile |
show-labels |
bool | true | Show $a$, $b$, $f$ labels |
profile-stroke |
stroke | blue + 1.5pt |
Top profile curve |
disk-color |
color | luma(218) |
Solid body fill |
disk-stroke |
stroke | luma(90) + 0.6pt |
Disk edge stroke |
inner-stroke |
auto/stroke | auto |
Inner profile stroke; auto = dashed, in the profile-stroke colour |
hole-color |
color | white |
Fill of the opening at the annular end caps |
axis-stroke |
stroke | black + 0.7pt |
Revolution axis stroke |
label-a, label-b |
content | $a$, $b$ |
Domain endpoint labels |
label-f |
content | $f$ |
Outer profile label |
label-inner |
content/none | none |
Inner profile label (hollow solids) |
label-y |
content | $y$ |
Y-axis label |
Marker Types
| Type | Description | Type | Description |
|---|---|---|---|
"o" |
Hollow circle | "*" |
Filled circle |
"square" |
Hollow square | "square*" |
Filled square |
"triangle" |
Hollow triangle | "triangle*" |
Filled triangle |
"diamond" |
Hollow diamond | "diamond*" |
Filled diamond |
"star" |
Hollow star | "star*" |
Filled star |
"+" |
Plus sign | "x" |
Cross |
| `" | "` | Vertical bar | "-" |
"none" |
No marker |
Custom Styling
#plot(
style: (
background: (
fill: rgb("#202124"),
),
axis: (
stroke: white + 1pt,
arrow: (symbol: "stealth", fill: white, scale: 0.55),
),
grid: (
major: (stroke: luma(120) + 0.5pt),
minor: (stroke: luma(80) + 0.3pt),
),
ticks: (
length: 0.1,
stroke: white + 0.6pt,
label-offset: 0.15,
label-size: 10pt,
label-fill: white,
),
labels: (
fill: white,
),
),
// ...
)
Setting Global Defaults
#import "@preview/simple-plot:1.1.0": set-plot-defaults, reset-plot-defaults
#set-plot-defaults(width: 6, height: 4, show-grid: "major")
// All subsequent plots use these defaults
#plot(xmin: -2, xmax: 2, ymin: 0, ymax: 4,
(fn: x => calc.pow(x, 2)))
#reset-plot-defaults()
Comparison with Other Plotting Libraries
simple-plot is designed for mathematical function plotting with a focus on simplicity and ease of use. Choose simple-plot when you need to:
- Plot mathematical functions quickly with minimal boilerplate code
- Create publication-quality plots for math, physics, or engineering documents
- Use a familiar API similar to pgfplots/matplotlib for straightforward plotting tasks
Alternatives:
- cetz-plot: Better for business charts and general data visualization (pie, bar, pyramid charts).
- lilaq: More powerful for complex scientific visualizations (colormesh, contour, quiver) but steeper learning curve.
Dependencies
- CeTZ (v0.5.2+)
License
MIT License — see LICENSE file for details.
Changelog
[1.1.0] - 2026-08-19
Fixed
- Dashed and dotted strokes rendered as solid lines (#11) — a curve was drawn as one
line()per sample, so the dash pattern restarted every few tenths of a millimetre and never reached its first gap. The narrower the plot, the more solid it looked, which is why widening it seemed to “fix” it. Contiguous samples are now drawn as a single polyline, forfnand for parametric curves - A curve through the origin took the “0” with it (#9) — the origin label is dropped when a curve crosses it, which for
y = x,y = x²orsinmeant no zero at all. The label now tries the other three corners of the origin and only disappears if all four are crossed. A placement set throughorigin-label-offset/origin-label-anchoris left alone - Empty custom tick labels still opened gaps in the grid —
xtick-labels: ("", "", "1", ""), the usual way to keep a single label, broke the grid lines at every blanked tick. Gaps are now computed from the labels actually drawn, and their width from the custom text rather than from the number it replaces - The axis labels punched a white rectangle into coloured backgrounds (#10) — the background behind
xandywas painted unconditionally. It is now painted only where something would otherwise show through: a grid line, a tick mark or a curve. On a white page the result is unchanged; on any other background the rectangle is gone where it served no purpose. It also no longer punches a hole in an area fill sitting behind the label
Added
samplesonplot— the sampling rate for every curve of a plot, where before it could only be set curve by curve. Raise it for rapidly oscillating functions, lower it to speed up a heavy document. Also accepted byset-plot-defaults. Contributed by @FramHerel in #8, closing #7label-bgandtick-label-bgonplot— the colour painted behind axis and tick labels, ornone. They were reachable only through thestyledictionary
[1.0.1] - 2026-08-08
Added
label-sizingonplot—"inherit"(default, unchanged behaviour) lets generated text take the surrounding document text size;"plot"makes it take the plot’s own label sizes and followscale, so a plot keeps its proportions when the document body text is unusually large or small. Also settable throughset-plot-defaults- Hollow solids of revolution —
volume-of-revolutionacceptsinner-fn, revolving the region between two curves. End caps become annuli and every cross-section becomes a washer, which is the picture behind $V = \pi \int (f^2 - g^2)$. Comes withinner-stroke,hole-colorandlabel-inner; an inner radius larger than the outer one is clamped, and a hole leaving no material is not drawn
Fixed
volume-of-revolution: the closing cap at $x = b$ had no visible rim — the right cap was filled before the body and only its back arc was stroked, so the body fill painted over it and the solid ended in an outline-less grey bulge. The rim is now stroked after the body, matching the left cap and the intermediate cross-sections. Affects every solid, includingshow-back: falsehalf-views and obliqueaxis-slopeaxes
[1.0.0] - 2026-07-06
First stable release. The API documented in the manual is now considered stable.
Added
width/heightaccept lengths —plot,zoom,volume-of-revolution(and the wrappers) now take real lengths (width: 6cm,height: 30mm) in addition to plain numbers, which keep their historical meaning of cm- Named
fn:in series helpers —fill-area,riemann-sum,func-plotacceptfn:(andarea-betweenacceptsfn1:/fn2:) in addition to the positional function, matching the(fn: ..., ...)series dictionaries axis-x-pos: none/axis-y-pos: nonehide an axis — the axis line, arrow, ticks and labels are skipped entirely (used bylimit-schema, which previously drew an unintended y-axis)- Unknown named arguments to
plot()now raise an error — typos likextick-lables:were previously silently ignored - Unified
hatchargument —hatchalso accepts a dict(style: "ne", spacing: 10pt, stroke: red + 1pt)infill-area,area-between,riemann-sumandfill-closed; the flathatch-spacing/hatch-strokearguments keep working as fallbacks volume-of-revolutioncleanup —radius-marker-xis the new canonical name for the radius-marker position (yaxis-xandshow-yaxisremain accepted as deprecated aliases); unknown named arguments now raise an errorset-plot-defaultsvalidates option names — a typo likeset-plot-defaults(widht: 10)now raises an error instead of being silently stored and ignored- Zoom insets re-render every series type —
fill-area,area-between,fill-closed,riemann-sumrectangles,vline/hlineand parametric curves now appear insidezoom()insets (previously only function curves and point series did), correctly clipped to both the rectangular and the circular lens
Fixed
- Riemann rectangles cut by the plot window no longer draw false edges — a bar extending past
xmax(e.g. decimalxmax: 2.5withdomain: (0, 3)) or aboveymaxwas clamped and then stroked on all four sides, showing a wrong width and a flat top at the window boundary; clipped edges are now left open so the bar reads as continuing beyond the window, and bars fully outside are dropped show-xilabels no longer collide with numeric tick labels — every tick label near anx_ilabel (measured in cm, not only exact position matches) is hidden, andx_0takes the origin’s0spot instead of being shifted intox_1show-dx+show-xikeep all labels — theΔxbracket now drops below thex_irow (dimension-line style) instead of silently skipping the two straddlingx_ilabelsshow-pointsno longer sprays a label by default —point-labeldefaults tonone(dots only); when a label is requested, arrows point to the 3 nearest dots instead of criss-crossing the plot- Label backgrounds no longer punch white holes into area fills — tick labels and axis-name labels suppress their white background when they sit on a Riemann bar or an area fill (the digits print directly on the fill)
compilerrequirement corrected to0.13.0— the hatch patterns usetiling(), which does not exist before Typst 0.13; the manifest previously claimed 0.11.0- Raw
(annotation: ...)dicts default to 9pt — aligned with thenote()helper, which already used 9pt - Axis arrow extensions are no longer scale-dependent —
axis-x-extend/axis-y-extenddefault to absolute lengths (0.3cm), so a small data range (e.g. y from 0 to 0.5) no longer produces a huge axis overshoot; lengths are accepted for explicit values, bare numbers keep the legacy data-unit meaning - Thick hatch strokes no longer show holes — hatch lines are extended past the tile boundary and repeated at the opposite corners, so
hatch-strokethicknesses comparable tohatch-spacingtile seamlessly plot-fnnow forwardssamplesto the drawn curve (it was only used for the auto y-range)zoom(box-fill: none)now really makes the inset background transparent (the value was previously dropped and fell back to white)
[0.9.1] - 2026-07-03
Added
- Automatic function-label placement — labels now find a clear spot along the curve automatically, avoiding overlaps with other curves, tick labels, axes, and previously placed labels;
label-anchor/label-sideoverride when explicit placement is needed min-tick-spacing— auto-widens tick step to the next “nice” value (1, 2, 5, 10…) when the axis range would crowd labels below the threshold; keeps large-range plots legible without manualxtick-stephide-crossed-tick-labels— hides individual tick labels that a plotted curve crosses, so digits stay readable even when curves pass through the label band- White label backgrounds — tick labels and axis-name labels are drawn over a white background so curves behind them don’t bleed through
Fixed
ylabel-offsetdefault corrected: was(-0.08, -0.05), now(0.08, -0.05)— the $y$ axis label was shifted left instead of right- Area specs are now clipped to the plot rectangle —
fill-area,area-between,fill-closedandriemann-sumrectangles no longer overflow the axes area when the function or the givendomainexceeds the axis range; they are clipped exactly like curves - Riemann annotations flip for negative functions — the
Δxbracket andx_ilabels move above the baseline when the rectangles extend below it, instead of printing through the fill x_ilabels no longer collide with x tick labels — tick labels undershow-xi/show-dxannotations near the baseline are hidden automaticallyshow-pointsdots are clipped — evaluation dots (and their label arrows) outside the plot area are dropped- Circular zoom lens is a true circle — the spy glass and its inset are drawn as circles with uniform canvas-space magnification (shapes are preserved); previously both were ellipses derived from the region’s bounding box
- Zoom connector lines follow external tangents — for circular lenses the dashed connectors touch both circle borders exactly and never cross the shapes; the inset size is capped at 80% of the plot and auto placement keeps it on canvas
- Guard against division by zero in
volume-of-revolutionfor degenerate domains
[0.9.0] - 2026-06-15
Added
- Complete parameter reference for all public functions —
scatter,data,line-plot,func-plot,plot-fn,parametric, andseriesnow have full parameter tables in the manual - New manual sections — dedicated sections for
fill-area,area-between,fill-closed,note,vline,hline, and parametric plots origin-*parameters —origin-x,origin-y,origin-label,origin-sizedocumented in axis configuration table- Discontinuity handling — added note on returning
nonefrom plot functions to create gaps
Fixed
- Wrong defaults corrected in axis configuration table (
xlabel-anchor,ylabel-anchor, offsets) - Removed non-existent marker aliases (
s,^,d) from marker reference table - Incorrect
label-posdescription and removed fakelabel-atfield from function spec table - Minor grid added to Grid Modes gallery preview
[0.8.0] - 2026-05-21
Added
riemann-sum: complete feature documentation — full API table with all 23 parameters:method("left","right","mid","lower","upper"), hatch controls (hatch,hatch-spacing,hatch-stroke), annotation flags (show-points,point-color,point-size,point-label,point-label-pos,show-dx,dx-rect,dx-label,show-xi,xi-labels,xi-show-values), andsamplesvolume-of-revolution: complete feature documentation — full API table with all parameters includingaxis-y,axis-slope,show-y-axis,yaxis-x,y-axis-offset,y-axis-extend,show-radius-marker,show-back,show-labels, label params;solid-of-revolutionalias documented- Gallery:
riemann-features.typ— 8-demo showcase of all Riemann sum features side by side
[0.7.0] - 2026-05-21
Fixed
- Riemann sum xi-label x-shift on y-axis increased from 0.18 to 0.35 to prevent overlap with axis line
[0.6.0] - 2026-05-21
Added
volume-of-revolution— renamed fromsolid-of-revolution(alias kept); supports arbitrary revolution axis viaaxis-yandaxis-slope; newshow-yaxis,show-back,label-yparameters; ellipses rendered via polygon points for smoother output- Riemann sum:
"lower"/"upper"methods — true infimum/supremum by sampling within each subinterval; works for U-curves and any shape - Riemann sum: endpoint dots —
show-points,point-color,point-size,point-label,point-label-pos; draws dots at evaluation points with a text label and stealth arrows - Riemann sum: Δx bracket —
show-dx,dx-rect,dx-label; dimension bracket with tick marks under a chosen rectangle - Riemann sum: subdivision labels —
show-xi,xi-labels; draws $x_0, x_1, \ldots, x_n$ at subdivision points - Default axis labels —
xlabelandylabelnow default to$x$and$y$(tkz-fct style) - Axis label placement — new defaults:
xbelow-right of arrowhead ("north-west"anchor),yto the left ("east"anchor); matches standard math textbook style
Fixed
- Axis arrow style changed from bare
"stealth"string (inherited large global scale) to explicit(symbol: "stealth", fill: black, scale: 0.55)— arrows are now elegantly sized - Global mark scale removed from canvas
set-styleto avoid oversizing user-defined marks
[0.3.0] - 2026-02-18
Changed
- Grid label break: gap-based grid drawing replaces white-box overlay; works on any background color
Fixed
- Origin label no longer duplicates when
show-origin: trueand axes cross at origin
[0.2.6] - 2026-02-04
Added
- Grid label breaks, integer tick defaults,
xtick-label-step/ytick-label-step,unit-label-only, axis arrows extending beyond grid
[0.2.5] - 2026-01-27
Fixed
label-posnow respects explicit function domains
[0.2.0] - 2026-01-15
Added
axis-x-extend,axis-y-extend,show-origin,label-side, Liang-Barsky clipping
[0.1.0] - 2026-01-13
Added
- Initial release: function plotting, scatter/line plots, 14 marker types, customizable axes, grid, ticks, labels, global defaults