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 ... />
Name | Description | Default |
---|---|---|
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 ... />
Name | Description | Default |
---|---|---|
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" | — |
Inequalities of x and y
Inequalities represent the region less than or greater than one or two functions. Mafs allows you to plot the region between two functions, or a function and a constant. The inequality can be a function of x or y.
You cannot provide an x
and a y
prop to Inequality—it will throw a runtime exception. Similarly, you cannot pass conflicting inequality operators—like both <
and ≤
.
import { Mafs, Coordinates, Plot, Theme, useMovablePoint } from "mafs"
function InequalitiesExample() {
const a = useMovablePoint([0, -1])
return (
<Mafs>
<Coordinates.Cartesian />
<Plot.Inequality
x={{
"<=": (y) => Math.cos(y + a.y) - a.x,
">": (y) => Math.sin(y - a.y) + a.x,
}}
color={Theme.blue}
/>
<Plot.Inequality
y={{
"<=": (x) => Math.cos(x + a.x) - a.y,
">": (x) => Math.sin(x - a.x) + a.y,
}}
color={Theme.pink}
/>
{a.element}
</Mafs>
)
}
Props
<Plot.Inequality ... />
Name | Description | Default |
---|---|---|
y | { ">"?: FnX; "<="?: FnX; "<"?: FnX | undefined; ">="?: FnX | undefined; } | undefined | — |
x | { ">"?: FnY; "<="?: FnY; "<"?: FnY | undefined; ">="?: FnY | undefined; } | undefined | — |
color | string | var(--mafs-fg) |
weight | number | 2 |
strokeColor | string | var(--mafs-fg) |
strokeOpacity | number | 1 |
fillColor | string | var(--mafs-fg) |
fillOpacity | number | 0.15 |
minSamplingDepth | number | 10 |
maxSamplingDepth | number | 14 |
upperColor | string | var(--mafs-fg) |
upperOpacity | number | 1 |
upperWeight | number | 2 |
lowerColor | string | var(--mafs-fg) |
lowerOpacity | number | 1 |
lowerWeight | number | 2 |
svgUpperPathProps | SVGProps<SVGPathElement> | {} |
svgLowerPathProps | SVGProps<SVGPathElement> | {} |
svgFillPathProps | SVGProps<SVGPathElement> | {} |
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 ... />
Name | Description | Default |
---|---|---|
xy | A function that takes a (t: number) => Vector2 | — |
t | The domain 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 ... />
Name | Description | Default |
---|---|---|
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.