diff --git a/docs/components/guide-examples/examples/ProjectileMotion.tsx b/docs/components/guide-examples/examples/ProjectileMotion.tsx index 22fabd3e..c233031a 100644 --- a/docs/components/guide-examples/examples/ProjectileMotion.tsx +++ b/docs/components/guide-examples/examples/ProjectileMotion.tsx @@ -75,7 +75,7 @@ export default function ProjectileMotion() { <> diff --git a/docs/components/guide-examples/plots/twisty-boi.tsx b/docs/components/guide-examples/plots/twisty-boi.tsx index 74fefe8c..5974e73c 100644 --- a/docs/components/guide-examples/plots/twisty-boi.tsx +++ b/docs/components/guide-examples/plots/twisty-boi.tsx @@ -20,7 +20,7 @@ export default function TwistyBoi() { [Math.cos(t), (t / k) * Math.sin(t)]} /> diff --git a/src/display/Plot/OfX.tsx b/src/display/Plot/OfX.tsx index 4e8cd138..e9cc76a2 100644 --- a/src/display/Plot/OfX.tsx +++ b/src/display/Plot/OfX.tsx @@ -3,20 +3,25 @@ import { usePaneContext } from "../../context/PaneContext" import { Parametric, ParametricProps } from "./Parametric" import { vec } from "../../vec" -export interface OfXProps extends Omit { +export interface OfXProps extends Omit { y: (x: number) => number + domain?: vec.Vector2 svgPathProps?: React.SVGProps } -export function OfX({ y, ...props }: OfXProps) { +export function OfX({ y, domain, ...props }: OfXProps) { + const [xuMin, xuMax] = domain ?? [-Infinity, Infinity] const { - xPaneRange: [xMin, xMax], + xPaneRange: [xpMin, xpMax], } = usePaneContext() + // Determine the most restrictive range values (either user-provided or the pane context) + const xMin = Math.max(xuMin, xpMin) + const xMax = Math.min(xuMax, xpMax) const xy = React.useCallback((x) => [x, y(x)], [y]) - const t = React.useMemo(() => [xMin, xMax], [xMin, xMax]) + const parametricDomain = React.useMemo(() => [xMin, xMax], [xMin, xMax]) - return + return } OfX.displayName = "Plot.OfX" diff --git a/src/display/Plot/OfY.tsx b/src/display/Plot/OfY.tsx index 273ab363..c3c36472 100644 --- a/src/display/Plot/OfY.tsx +++ b/src/display/Plot/OfY.tsx @@ -3,20 +3,25 @@ import { usePaneContext } from "../../context/PaneContext" import { Parametric, ParametricProps } from "./Parametric" import { vec } from "../../vec" -export interface OfYProps extends Omit { +export interface OfYProps extends Omit { x: (y: number) => number + domain?: vec.Vector2 svgPathProps?: React.SVGProps } -export function OfY({ x, ...props }: OfYProps) { +export function OfY({ x, domain, ...props }: OfYProps) { + const [yuMin, yuMax] = domain ?? [-Infinity, Infinity] const { - yPaneRange: [yMin, yMax], + yPaneRange: [ypMin, ypMax], } = usePaneContext() + // Determine the most restrictive range values (either user-provided or the pane context) + const yMin = Math.max(yuMin, ypMin) + const yMax = Math.min(yuMax, ypMax) const xy = React.useCallback((y) => [x(y), y], [x]) - const t = React.useMemo(() => [yMin, yMax], [yMin, yMax]) + const parametricDomain = React.useMemo(() => [yMin, yMax], [yMin, yMax]) - return + return } OfY.displayName = "Plot.OfY" diff --git a/src/display/Plot/Parametric.tsx b/src/display/Plot/Parametric.tsx index d0abc9c1..c618e44e 100644 --- a/src/display/Plot/Parametric.tsx +++ b/src/display/Plot/Parametric.tsx @@ -4,10 +4,19 @@ import { Stroked } from "../Theme" import { useTransformContext } from "../../context/TransformContext" import { sampleParametric } from "./PlotUtils" -export interface ParametricProps extends Stroked { +// TODO: (v1.0.0) When the project has it's first major (breaking) update, +// remove the Legacy interface and just have the new props interface (renamed, of course). +// Also, remove the `t` property at that time. +// Waiting until the major update to batch them together, +// and to give time for consumers to update their usage. +interface ParametricPropsLegacy extends Stroked { /** A function that takes a `t` value and returns a point. */ xy: (t: number) => vec.Vector2 - /** The domain `t` between which to evaluate `xy`. */ + /** The domain between which to evaluate `xy`. */ + domain?: never + /** + * @deprecated - use the `domain` prop. + */ t: vec.Vector2 /** The minimum recursive depth of the sampling algorithm. */ minSamplingDepth?: number @@ -17,8 +26,28 @@ export interface ParametricProps extends Stroked { svgPathProps?: React.SVGProps } +interface ParametricPropsNew extends Stroked { + /** A function that takes a `t` value and returns a point. */ + xy: (t: number) => vec.Vector2 + /** The domain between which to evaluate `xy`. */ + domain: vec.Vector2 + /** + * @deprecated - use the `domain` prop. + */ + t?: never + /** The minimum recursive depth of the sampling algorithm. */ + minSamplingDepth?: number + /** The maximum recursive depth of the sampling algorithm. */ + maxSamplingDepth?: number + + svgPathProps?: React.SVGProps +} + +export type ParametricProps = ParametricPropsNew | ParametricPropsLegacy + export function Parametric({ xy, + domain, t, color, style = "solid", @@ -33,7 +62,7 @@ export function Parametric({ // Negative because the y-axis is flipped in the SVG coordinate system. const pixelsPerSquare = -vec.det(viewTransform) - const [tMin, tMax] = t + const [tMin, tMax] = domain || t const errorThreshold = 0.1 / pixelsPerSquare const svgPath = React.useMemo(