# 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 ... />`
View on GitHub
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 ... />`
View on GitHub
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"`

## 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 ... />`
View on GitHub
NameDescriptionDefault
`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 ... />`
View on GitHub
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 ... />`
View on GitHub
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 recursively subdividing the domain until an estimated error threshold is met (or the recursion 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.

Here's an example of a common "stress test" function for plotters, sin(1/x). This function exhibits an infinite oscillation frequency as x approaches 0, requiring theoretically infinite sampling to render perfectly.

The top plot has the default sampling depths, while the bottom has `minSamplingDepth` increased to `16`. More samples still doesn't render the function perfectly, but it's much closer (at the cost of performance: the bottom plot has nearly 3 megabytes of SVG path data).

``````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} weight={1} />
<Plot.OfX y={(x) => fn(x) - 1.5} minSamplingDepth={16} weight={1} />
</Mafs>
)
}``````

If you pan this example around, you may see a considerably slow framerate. Interestingly, this slowness is happening in the browser code itself, not in JavaScript (and therefore not in Mafs). It would seem that merely rendering large SVG paths is expensive.

### 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.