plot3d-2 directive#
The plot3d-2 directive creates 3D mathematical figures in MyST / Jupyter Book using a compact key-value syntax. It uses Matplotlib’s 3D axes as the backend, but draws custom centered axes, arrowheads, depth-aware lines, and geometry primitives intended for textbook-style figures.
plot3d-2 is experimental and is meant to evolve toward feature parity with the mature plot directive while keeping the implementation easier to refactor.
Basic usage#
:::{plot3d-2}
width: 70%
xrange: (-2, 4)
yrange: (-2, 4)
zrange: (-1, 4)
vector: (0, 0, 0), (2, 1, 3), blue
point: (2, 1, 3), red
text: at=(2, 1, 3), value="$P$", ha=left, va=bottom
Vectoren $\vec{OP}$ i rommet.
:::
The directive also has the alias plot3d2.
Syntax overview#
The directive is usually written in MyST form:
:::{plot3d-2}
key: value
key: value
Optional caption text.
:::
You can also use classic reStructuredText syntax:
```{eval-rst}
.. plot3d-2::
width: 70%
vector: (0, 0, 0), (1, 2, 3), blue
Optional caption text.
```
How front matter works#
Each non-empty line before the first blank line is interpreted as
key: valuefront matter.Repeated drawing keys are allowed.
Lines after the first blank line become the figure caption.
Most numeric values support arithmetic expressions such as
sqrt(2),pi/3, and variables created withlet.
Supported keys#
Repeated drawing keys:
curvelineline-segmentngonnormal-segmentpointplaneprismpyramidright-anglespheresolid-of-revolutiontextvector
Macro keys:
letdefrepeatmacrouseendmacro
Global options#
Option |
Default |
Meaning |
|---|---|---|
|
none |
CSS width such as |
|
|
Matplotlib figure size in inches |
|
|
|
|
none |
Extra CSS classes |
|
generated |
Stable output name / figure anchor |
|
|
Alt text for accessibility |
|
off |
Force regeneration |
|
|
Base font size for labels and text |
|
|
Default line width |
|
|
Visible x-axis range |
|
|
Visible y-axis range |
|
|
Visible z-axis range |
|
|
x-axis tick spacing |
|
|
y-axis tick spacing |
|
|
z-axis tick spacing |
|
|
x-axis label; use |
|
|
y-axis label; use |
|
|
z-axis label; use |
|
|
Turn ticks on or off |
|
|
Camera elevation in degrees |
|
|
Camera azimuth in degrees |
|
|
Camera zoom factor |
Example:
:::{plot3d-2}
width: 100%
figsize: (6, 4)
fontsize: 18
lw: 2
xrange: (-1, 5)
yrange: (-1, 4)
zrange: (-1, 4)
xstep: 1
ystep: 1
zstep: 1
elev: 20
azim: -70
ylabel: none
vector: (0, 0, 0), (3, 2, 2), teal
:::
Ticks never include the origin or the two endpoints of an axis. This prevents tick labels from colliding with the origin and the arrowheads.
Expression support#
Numeric fields support SymPy-style expressions:
arithmetic:
1/3,2*sqrt(5),3*pi/4constants:
pi,Efunctions:
sqrt,exp,log,sin,cos,tan,asin,acos,atan,sinh,cosh,tanh,Absmacro variables and functions created with
letanddef
Use ** for powers.
:::{plot3d-2}
point: (sqrt(2), cos(pi/3), 1/2), red
vector: (0, 0, 0), (2*cos(pi/6), 2*sin(pi/6), sqrt(3)), blue
:::
Colors and line styles#
Color values may be named plotmath colors such as blue, red, green, orange, purple, teal, gray, and black, or CSS/Matplotlib color values such as #13579b.
Supported line styles for line-like primitives:
soliddasheddashdotdotted
Vectors#
vector draws an arrow from a start point to an end point.
Syntax:
vector: (x0, y0, z0), (x1, y1, z1), color
The color is optional and defaults to blue.
:::{plot3d-2}
xrange: (-1, 4)
yrange: (-1, 4)
zrange: (-1, 4)
vector: (0, 0, 0), (3, 1, 2), blue
vector: (1, 0, 0), (1, 2, 3), orange
:::
Points#
point draws a marker at a coordinate.
Syntax:
point: (x, y, z), color
The color is optional and defaults to blue.
:::{plot3d-2}
point: (0, 0, 0), black
point: (1, 2, 3), red
text: at=(1, 2, 3), value="$P$", ha=left, va=bottom
:::
Text#
text places a label at a 3D coordinate.
Syntax:
text: at=(x, y, z), value="label", color=color, fontsize=12, offset=(dx, dy, dz), ha=center, va=center
Options:
Option |
Default |
Meaning |
|---|---|---|
|
required |
Anchor coordinate |
|
required |
Text content |
|
|
Text color |
|
global |
Override font size |
|
|
Coordinate offset applied to |
|
|
|
|
|
|
:::{plot3d-2}
point: (2, 1, 3), black
text: at=(2, 1, 3), value="$A$", offset=(0.1, 0, 0.1), ha=left, va=bottom
:::
Lines#
line draws an infinite line clipped to the visible plotting box. Lines use depth shading.
Syntax forms:
line: point=(x0, y0, z0), direction=(dx, dy, dz), color=blue, lw=1.5, style=solid
line: through=[(x0, y0, z0), (x1, y1, z1)], color=blue, lw=1.5, style=solid
line: (x0, y0, z0), (x1, y1, z1), color=blue, lw=1.5, style=solid
:::{plot3d-2}
xrange: (-1, 5)
yrange: (-1, 4)
zrange: (-1, 4)
line: point=(0, 0, 0), direction=(2, 1, 1), color=blue, lw=2
line: through=[(0, 3, 0), (4, 0, 3)], color=red, style=dashed
:::
Line segments#
line-segment draws only the finite segment between two points. Segments use the same depth-aware rendering as line.
Syntax forms:
line-segment: from=(x0, y0, z0), to=(x1, y1, z1), color=blue, lw=1.5, style=solid
line-segment: start=(x0, y0, z0), end=(x1, y1, z1), color=blue, lw=1.5, style=solid
line-segment: (x0, y0, z0), (x1, y1, z1), color, lw=1.5, style=solid
:::{plot3d-2}
line-segment: (0, 0, 0), (3, 2, 1), blue, lw=2
line-segment: from=(3, 2, 1), to=(1, 3, 3), color=orange, style=dashed
point: (0, 0, 0), black
point: (3, 2, 1), black
point: (1, 3, 3), black
:::
Normal segments#
normal-segment draws a perpendicular connector. It has two main forms.
Normal segment between two lines#
Syntax:
normal-segment: point1=(x, y, z), direction1=(dx, dy, dz), point2=(x, y, z), direction2=(dx, dy, dz), color=blue, style=solid
Aliases:
p1forpoint1p2forpoint2dir1orv1fordirection1dir2orv2fordirection2
:::{plot3d-2}
ticks: off
xrange: (-1, 7)
yrange: (-1, 5)
zrange: (-1, 5)
line: point=(3, 0, 4), direction=(2, 0, 0.5), color=blue, lw=2
line: point=(1, 3, 1), direction=(2, 0, -0.5), color=red, lw=2
normal-segment: point1=(3, 0, 4), direction1=(2, 0, 0.5), point2=(1, 3, 1), direction2=(2, 0, -0.5), color=gray, style=dashed, right-angle-size=0.35
:::
By default, the directive draws right-angle markers at both endpoints.
Normal segment from a point to a plane#
Syntax with plane equation:
normal-segment: point=(px, py, pz), plane=z = x + y, color=blue, style=solid
Syntax with normal vector and point on the plane:
normal-segment: point=(px, py, pz), plane-normal=(a, b, c), plane-point=(x0, y0, z0), color=blue, style=solid
Aliases:
pforpointnormalforplane-normalplane_point,on-plane, oron_planeforplane-pointequationforplane
Options:
Option |
Default |
Meaning |
|---|---|---|
|
|
Segment color |
|
global |
Segment width |
|
|
Segment style |
|
|
Draw right-angle marker |
|
|
Right-angle marker color |
|
|
Right-angle marker size |
|
|
Draw endpoints automatically |
|
|
Endpoint marker color |
:::{plot3d-2}
xrange: (-1, 4)
yrange: (-1, 4)
zrange: (-1, 5)
plane: equation=z = 1, xrange=(-1, 4), yrange=(-1, 4), color=orange, alpha=0.25
normal-segment: point=(2, 2, 4), plane=z = 1, color=gray, style=dashed, right-angle-size=0.35
text: at=(2, 2, 4), value="$P$", ha=left, va=bottom
:::
The point-plane form automatically draws a point at the external point and at the foot on the plane. Use points=off to suppress those endpoint markers.
Right angles#
right-angle draws a square right-angle marker at a coordinate.
Syntax with directions:
right-angle: at=(x, y, z), dir1=(dx, dy, dz), dir2=(dx, dy, dz), size=0.35, color=black, lw=1.5
Syntax with target points:
right-angle: at=(x, y, z), to1=(x1, y1, z1), to2=(x2, y2, z2), size=0.35, color=black, lw=1.5
The to1 / to2 form clamps the marker to the distances from at to the two target points. This is useful for short segments.
:::{plot3d-2}
line-segment: (0, 0, 0), (2, 0, 0), black
line-segment: (0, 0, 0), (0, 2, 0), black
right-angle: at=(0, 0, 0), to1=(2, 0, 0), to2=(0, 2, 0), size=0.4, color=red
:::
Planes#
plane draws a finite patch of a plane.
Equation form#
Syntax:
plane: equation=z = x + y, xrange=(-2, 2), yrange=(-2, 2), color=orange, alpha=0.35
The equation may solve for x, y, or z. Use xrange, yrange, and zrange to limit the displayed patch.
:::{plot3d-2}
xrange: (-2, 2)
yrange: (-2, 2)
zrange: (-2, 4)
plane: equation=z = x + y, xrange=(-2, 2), yrange=(-2, 2), color=orange, alpha=0.35
:::
Normal-point form#
Syntax:
plane: normal=(a, b, c), point=(x0, y0, z0), span=(width, height), color=orange, alpha=0.35
span controls the finite width and height of the plane patch. If only one value is supplied, the patch is square.
:::{plot3d-2}
plane: normal=(1, 1, 1), point=(0, 0, 1), span=(4, 3), color=teal, alpha=0.3
normal-segment: point=(2, 2, 4), plane-normal=(1, 1, 1), plane-point=(0, 0, 1), color=gray, style=dashed
:::
Curves#
curve draws a parametric 3D curve.
Syntax:
curve: x=f(t), y=g(t), z=h(t), t=(tmin, tmax), color=blue, lw=1.5, samples=300, arrows=true, arrow-count=3
Aliases:
trangefortarrows-countforarrow-count
Rendering notes:
Curves use local depth shading.
Curve style changes by xy-quadrant: solid for
x > 0, y < 0, dashdot for mixed foreground quadrants, dashed forx < 0, y < 0.Arrowheads lie on the curve and indicate direction.
:::{plot3d-2}
xrange: (-2, 2)
yrange: (-2, 2)
zrange: (-1, 14)
curve: x=sin(t), y=cos(t), z=t, t=(0, 4*pi), color=blue, lw=2, samples=400, arrow-count=5
:::
Disable arrows with arrows=false.
Solid of revolution#
solid-of-revolution draws the surface obtained by rotating f(x) about the x-axis.
Syntax:
solid-of-revolution: f(x), (xmin, xmax), color
The color is optional and defaults to blue.
:::{plot3d-2}
xrange: (-1, 5)
yrange: (-3, 3)
zrange: (-3, 3)
solid-of-revolution: sqrt(x), (0, 4), green
:::
N-gons#
ngon draws one filled polygonal face with n corners.
Syntax forms:
ngon: [(x0, y0, z0), (x1, y1, z1), ...], color=blue, edgecolor=black, alpha=0.45
ngon: points=[(x0, y0, z0), (x1, y1, z1), ...], color=blue, edgecolor=black, alpha=0.45
ngon: vertices=[(x0, y0, z0), (x1, y1, z1), ...], color=blue, edgecolor=black, alpha=0.45
:::{plot3d-2}
ngon: [(0, 0, 0), (2, 0, 0), (2, 1, 1), (0, 1, 1)], color=green, alpha=0.4
:::
Pyramids#
pyramid draws a pyramid with an n-gon base and an apex.
Syntax with explicit base:
pyramid: base=[(x0, y0, z0), (x1, y1, z1), ...], apex=(x, y, z), color=blue, edgecolor=black, alpha=0.45
Syntax with regular n-gon base in the xy-plane:
pyramid: center=(cx, cy, cz), radius=r, sides=n, apex=(x, y, z), rotation=0, color=blue, edgecolor=black, alpha=0.45
:::{plot3d-2}
xrange: (-2, 2)
yrange: (-2, 2)
zrange: (-1, 3)
pyramid: center=(0, 0, 0), radius=1.4, sides=5, apex=(0, 0, 2.4), color=purple, alpha=0.5
:::
Prisms#
prism draws a prism from an n-gon base and an extrusion.
Syntax with explicit base and vector:
prism: base=[(x0, y0, z0), (x1, y1, z1), ...], vector=(dx, dy, dz), color=blue, edgecolor=black, alpha=0.45
Syntax with explicit base and vertical height:
prism: base=[(x0, y0, z0), (x1, y1, z1), ...], height=h, color=blue, edgecolor=black, alpha=0.45
Syntax with regular n-gon base:
prism: center=(cx, cy, cz), radius=r, sides=n, height=h, rotation=0, color=blue, edgecolor=black, alpha=0.45
:::{plot3d-2}
xrange: (-2, 2)
yrange: (-2, 2)
zrange: (-1, 3)
prism: center=(0, 0, 0), radius=1.2, sides=6, height=2, color=yellow, alpha=0.35
:::
Spheres#
sphere draws a sphere with depth-aware surface shading and guide curves.
Syntax:
sphere: center=(x, y, z), radius=r, color=blue, alpha=0.55, resolution=48
resolution is clamped between 8 and 128.
:::{plot3d-2}
xrange: (-2, 2)
yrange: (-2, 2)
zrange: (-2, 2)
sphere: center=(0, 0, 0), radius=1, color=skyblue, alpha=0.65, resolution=48
:::
Macros and reusable constructions#
plot3d-2 supports the same macro system style as plot.
let#
let defines a constant expression.
:::{plot3d-2}
let: h = 2
point: (1, 0, h), red
vector: (0, 0, 0), (1, 0, h), blue
:::
def#
def defines a helper function. Multi-argument definitions are supported.
:::{plot3d-2}
def: px(i, j) = i + j/2
def: pz(i, j) = i*j/3
point: (px(1, 2), 1, pz(1, 2)), red
:::
repeat#
repeat expands one line many times.
:::{plot3d-2}
xrange: (-1, 5)
yrange: (-1, 2)
zrange: (-1, 4)
repeat: n=0..4; point: (n, 0, n/2), blue
repeat: n=0..3; line-segment: (n, 0, n/2), (n + 1, 0, (n + 1)/2), gray
:::
macro and use#
Macros package several plot lines into a reusable block.
:::{plot3d-2}
macro: pillar(x, y, h, c)
line-segment: (x, y, 0), (x, y, h), color=c, lw=2
point: (x, y, h), c
endmacro
use: pillar(0, 0, 1, blue)
use: pillar(1, 1, 2, red)
use: pillar(2, 0, 3, green)
:::
Complete example#
:::{plot3d-2}
width: 100%
fontsize: 18
elev: 20
azim: -70
xrange: (-1, 5)
yrange: (-1, 4)
zrange: (-1, 5)
ylabel: none
plane: equation=z = 1, xrange=(-1, 5), yrange=(-1, 4), color=orange, alpha=0.25
line: point=(0, 0, 1), direction=(1, 0.4, 0), color=blue, lw=2
curve: x=1 + sin(t), y=1 + cos(t), z=1 + t/4, t=(0, 4*pi), color=teal, lw=2, arrow-count=4
normal-segment: point=(3, 2, 4), plane=z = 1, color=gray, style=dashed, right-angle-size=0.35
vector: (0, 0, 0), (3, 2, 4), purple
text: at=(3, 2, 4), value="$P$", ha=left, va=bottom
Et punkt, en normal til planet og en romkurve.
:::
Tips#
Use
elevandazimdeliberately; the same figure can read very differently from another camera angle.Use
ylabel: noneor similar when a label collides with the figure.Use
nocache:while authoring figures that change often.Prefer
line-segmentfor finite geometry andlinefor infinite objects clipped to the plotting box.Use
normal-segmentfor perpendicular constructions instead of manually computing foot points.Use
points=offon point-plane normal segments when automatic endpoint markers create clutter.Increase
right-angle-sizewhen a perpendicular marker is too subtle.Keep
samplesmoderate for curves unless the curve really needs high resolution.
Source#
The implementation lives in src/munchboka_edutools/directives/plot3d_2.py.