Plots

Mafs supports numerically plotting a number of function types by passing in plain JavaScript functions.

Functions of x and y

import { Mafs, Coordinates, Plot, Theme } from "mafs" function FunctionsOfXAndY() { const sigmoid1 = (x: number) => 2 / (1 + Math.exp(-x)) - 1 return ( <Mafs> <Coordinates.Cartesian /> <Plot.OfX y={Math.sin} color={Theme.blue} /> <Plot.OfY x={sigmoid1} color={Theme.pink} /> </Mafs> ) }

Props

<Plot.OfX ... />
NameDescriptionDefault
y
(x: number) => number
β€”
svgPathProps
SVGProps<SVGPathElement>
β€”
color
string
β€”
minSamplingDepth

The minimum recursive depth of the sampling algorithm.

number
β€”
maxSamplingDepth

The maximum recursive depth of the sampling algorithm.

number
β€”
opacity
number
β€”
weight
number
β€”
style
"solid" | "dashed"
β€”

Props

<Plot.OfY ... />
NameDescriptionDefault
x
(y: number) => number
β€”
svgPathProps
SVGProps<SVGPathElement>
β€”
color
string
β€”
minSamplingDepth

The minimum recursive depth of the sampling algorithm.

number
β€”
maxSamplingDepth

The maximum recursive depth of the sampling algorithm.

number
β€”
opacity
number
β€”
weight
number
β€”
style
"solid" | "dashed"
β€”

Parametric functions

import { Mafs, Coordinates, Plot, useMovablePoint } from "mafs" import { clamp } from "lodash" function TwistyBoi() { const point = useMovablePoint([0.5, 0], { constrain: ([x]) => [clamp(x, -1, 1), 0], }) const k = point.x * 25 * Math.PI return ( <Mafs viewBox={{ x: [-1, 1], y: [-1, 1] }}> <Coordinates.Cartesian subdivisions={4} /> <Plot.Parametric t={[0, k]} xy={(t) => [Math.cos(t), (t / k) * Math.sin(t)]} /> {point.element} </Mafs> ) }

Props

<Plot.Parametric ... />
NameDescriptionDefault
xy

A function that takes a t value and returns a point.

(t: number) => Vector2
β€”
t

The domain t between which to evaluate xy.

Vector2
β€”
minSamplingDepth

The minimum recursive depth of the sampling algorithm.

number
8
maxSamplingDepth

The maximum recursive depth of the sampling algorithm.

number
14
svgPathProps
SVGProps<SVGPathElement>
{}
color
string
β€”
opacity
number
1
weight
number
2
style
"solid" | "dashed"
solid

Vector fields

Vector fields take a function that is passed a point [x, y] and returns a vector at that point. Vectors are then artificially scaled down (for legibility) and plotted on the coordinate plane. You must also pass a step to indicate how dense the vector field is.

import { Mafs, Plot, Coordinates, useMovablePoint } from "mafs" function VectorFieldExample() { const a = useMovablePoint([0.6, 0.6]) return ( <Mafs> <Coordinates.Cartesian subdivisions={2} /> <Plot.VectorField xy={([x, y]) => [ y - a.y - (x - a.x), -(x - a.x) - (y - a.y), ]} step={0.5} xyOpacity={([x, y]) => (Math.abs(x) + Math.abs(y)) / 10 } /> {a.element} </Mafs> ) }

Props

<Plot.VectorField ... />
NameDescriptionDefault
xy
(point: Vector2) => Vector2
β€”
xyOpacity
((point: Vector2) => number)
() => 1
step
number
1
opacityStep
number
xyOpacity === xyOpacityDefault ? 1 : 0.2
color
string
var(--mafs-fg)

Render quality

Function sampling

Plot.OfX, Plot.OfY, and Plot.Parametric use numerical methods for evaluating a function and attempting to plot it accurately. The approach works well for most functions, but it's far from perfect.

Mafs samples functions by by recursively subdividing the domain until an estimated error threshold is met (or the recursion limit limit is reached).

Sampling depth

To force more subdivisions (and therefore improve quality), the minSamplingDepth and maxSamplingDepth props can be tuned. Increasing minSamplingDepth can help when you want to ensure more subdivisions and improve accuracy, and lowering maxSamplingDepth can help improve performance. These two props should be tuned to meet your needs.

Here's an example of a common "stress test" function for plotters, sin(1/x). The top plot has the default sampling depths, while the bottom has minSamplingDepth increased to 15. Neither approach is perfect, but the bottom render is indistinguishable from a perfect plot.

import { Coordinates, Mafs, Plot } from "mafs" function SineStressTest() { const fn = (x: number) => Math.sin(1 / x) return ( <Mafs viewBox={{ x: [-1/32, 1/32], y: [-3.5, 3.5], padding: 0 }} preserveAspectRatio={false} > <Coordinates.Cartesian /> <Plot.OfX y={(x) => fn(x) + 1.5} /> <Plot.OfX y={(x) => fn(x) - 1.5} minSamplingDepth={15} /> </Mafs> ) }

Vector fields

Vector field rendering quality can be tuned with the step prop. This declares the spacing between arrows, so lowering it will decrease performance.