58.14 Function, Parametric, and Data Plots
Choose a function graph for y = f(x), a parametric curve for an ordered parameter domain, an implicit curve for a scalar-field contour, or a data plot for an already ordered point series. Samplers return local path geometry; styled constructors return ordinary path Visuals. Sampling and the axes transform are construction-time snapshots unless an API explicitly says it is derived.
See also Arrows and Coordinate Axes, Graph Helpers and Traced Paths, ODE Trajectories and Streamlines.
58.14.1 Coordinate Curves and Plots
The procedures in this section convert ordered numeric coordinates to semantic path geometry. They use the local coordinate system of an axes Visual. They do not store a sampling procedure or a caller-owned point list in the result.
58.14.1.1 Interpolation Modes
procedure
(curve-interpolation? value) → boolean?
value : any/c
'linear connects each accepted pair with a line segment.
'smooth creates cubic Bézier segments that pass through all accepted samples in order.
Sampled function and parametric-curve procedures use 'smooth by default. Ordered data-series procedures use 'linear by default. An unsupported symbol or another value returns #f.
Smooth interpolation is applied separately to every accepted run. Suppose one run contains points P0 through Pn. For a segment from Pi to Pi+1, the usual interior control points are:
C1 = Pi + (Pi+1 - Pi-1) / 6
C2 = Pi+1 + (Pi - Pi+2) / 6
At an end of a run, the endpoint is repeated for the missing neighboring point. A run containing exactly two points uses a line-equivalent cubic whose controls are one third and two thirds of the way along the segment. The result therefore follows the same traversal order and reaches every accepted sample.
When clipping is enabled, sample pairs are clipped as line segments before smooth interpolation is calculated. Generated control points are then clamped to the closed axes rectangle. A cubic Bézier curve lies inside the convex hull of its endpoints and controls, so the resulting visible curve stays inside the rectangle. Clamping may reduce smoothness where a run touches a boundary.
58.14.1.2 Sampled Curves and Fields
procedure
(sample-implicit-path axes field [ #:level level #:x-count x-count #:y-count y-count]) → path-geometry? axes : axes-visual? field : (procedure-arity-includes/c 2) level : finite-real? = 0 x-count : (and/c exact-integer? (>=/c 2)) = 65 y-count : (and/c exact-integer? (>=/c 2)) = 65
procedure
(implicit-curve axes field #:id id [ #:level level #:x-count x-count #:y-count y-count #:opacity opacity #:stroke stroke #:stroke-width stroke-width]) → path-visual? axes : axes-visual? field : (procedure-arity-includes/c 2) id : symbol? level : finite-real? = 0 x-count : (and/c exact-integer? (>=/c 2)) = 65 y-count : (and/c exact-integer? (>=/c 2)) = 65 opacity : opacity? = 1 stroke : any/c = "darkorange" stroke-width : (and/c finite-real? (>=/c 0)) = 2
procedure
(vector-field axes field #:id id [ #:x-count x-count #:y-count y-count #:scale scale #:opacity opacity #:stroke stroke #:stroke-width stroke-width #:tip-length tip-length #:tip-width tip-width]) → group-visual? axes : axes-visual? field : (procedure-arity-includes/c 2) id : symbol? x-count : (and/c exact-integer? (>=/c 1)) = 9 y-count : (and/c exact-integer? (>=/c 1)) = 7 scale : finite-real? = 1/4 opacity : opacity? = 1 stroke : any/c = "seagreen" stroke-width : (and/c finite-real? (>=/c 0)) = 2 tip-length : (and/c finite-real? (>/c 0)) = 3/20 tip-width : (and/c finite-real? (>/c 0)) = 1/8
58.14.2 Function Graphs
procedure
(sample-function-path axes function [ #:x-min x-min #:x-max x-max #:sample-count sample-count #:clip? clip? #:max-jump max-jump #:detect-discontinuities? detect-discontinuities? #:interpolation interpolation]) → path-geometry? axes : axes-visual? function : (procedure-arity-includes/c 1) x-min : (or/c finite-real? false/c) = #f x-max : (or/c finite-real? false/c) = #f sample-count : (and/c exact-integer? (>=/c 2)) = 201 clip? : boolean? = #t
max-jump :
(or/c false/c (and/c finite-real? (>=/c 0))) = #f detect-discontinuities? : boolean? = #f interpolation : curve-interpolation? = 'smooth
For a logarithmic x axis, spacing is uniform in the selected base-logarithm display coordinate instead. Thus a base-ten range from one through one thousand samples successive decades evenly. Explicit x-min and x-max bounds follow the same rule and must be strictly positive on a log axis.
All arguments are checked before function is called. When sampling completes without an error, the function is called exactly once for each sample x value. Sampling stops at the first invalid result or exception. Each call must return exactly one value. That value has these meanings:
A finite real is one numeric y sample.
#f is an explicit gap and breaks the current run.
Positive infinity, negative infinity, and NaN also create a gap.
Any other result raises an exception that reports the x value and the returned value.
Returning zero values or more than one value raises an exception that reports the x value and result count. An exception raised by function is not converted to a gap. It is reported together with the sample x value and the original exception message. This keeps programming errors separate from explicit discontinuities.
When max-jump is a number, two adjacent finite samples are connected only when the absolute difference between their numeric y values is no greater than that number. The threshold is applied before axes scaling and before clipping. The default #f performs no jump rejection. Use an explicit #f result when the location of a discontinuity is known.
When clip? is true, every accepted sample pair is clipped to the closed rectangle described by the axes x and y ranges. Clipping is performed on segments, so an intersection with a boundary becomes an exact path endpoint when exact arithmetic permits it. When an inexact coordinate difference would overflow, clipping temporarily uses the exact represented input values. When clip? is false, finite out-of-range samples remain in the path. Clipping does not decide whether a segment crossing the rectangle is a true discontinuity.
The interpolation argument controls the path segment kind as described by curve-interpolation?. Linear interpolation stores line segments. Smooth interpolation stores cubic Bézier segments through each accepted run. Breaks from non-finite values, explicit #f results, maximum-jump rejection, or clipping keep the runs separate.
When detect-discontinuities? is true, two adjacent samples that lie beyond opposite sides of the visible numeric y interval are treated as the hidden sides of a vertical asymptote and are not connected. This opt-in rule prevents clipping from drawing a false segment through the plot window while preserving the historical default behavior for steep continuous graphs.
The result contains zero or more open subpaths in sampling order. An isolated finite sample with no accepted adjacent pair does not create a point-only subpath. Every stored point uses the untransformed local coordinate system of axes. Numeric x is multiplied by axes-x-unit-length, and numeric y is multiplied by axes-y-unit-length. The axes translation, rotation, and scale are not applied to the returned geometry.
The sampling grid is deterministic. Exact bounds produce exact rational intermediate x values when ordinary exact arithmetic permits it. The result contains only immutable path geometry. Rendering it later does not call function again.
procedure
(function-graph axes function #:id id [ #:x-min x-min #:x-max x-max #:sample-count sample-count #:clip? clip? #:max-jump max-jump #:detect-discontinuities? detect-discontinuities? #:interpolation interpolation #:opacity opacity #:stroke stroke #:stroke-width stroke-width]) → path-visual? axes : axes-visual? function : (procedure-arity-includes/c 1) id : symbol? x-min : (or/c finite-real? false/c) = #f x-max : (or/c finite-real? false/c) = #f sample-count : (and/c exact-integer? (>=/c 2)) = 201 clip? : boolean? = #t
max-jump :
(or/c false/c (and/c finite-real? (>=/c 0))) = #f detect-discontinuities? : boolean? = #f interpolation : curve-interpolation? = 'smooth opacity : opacity? = 1 stroke : any/c = "royalblue" stroke-width : (and/c finite-real? (>=/c 0)) = 3
The graph copies the current translation, rotation, and scale of axes. Its local geometry already uses the axes x and y unit lengths, so the graph and axes coincide at construction time even when the axes are translated, rotated, or non-uniformly scaled. This is a snapshot. Updating either immutable Visual later does not update the other. Put both in a group or apply matching animation requests when they should continue to move together.
The graph has no fill. The identity, opacity, and stroke width are checked before the numeric function is called. stroke and stroke-width are used by the ordinary path renderer. The result works with create, uncreate, path replacement, movement, rotation, non-uniform scaling, fading, groups, layout, and custom path renderers. There is no graph-specific renderer or timeline request.
A constructed function graph is an ordinary path Visual.
> (path-visual? (function-graph (axes #:id 'coordinates) (lambda (x) (* x x)) #:id 'parabola #:sample-count 9)) #t
58.14.2.1 Adaptive Sampling
procedure
(sample-adaptive-function-path axes function [ #:x-min x-min #:x-max x-max #:initial-sample-count initial-sample-count #:max-deviation max-deviation #:max-depth max-depth #:clip? clip? #:max-jump max-jump #:detect-discontinuities? detect-discontinuities? #:excluded-intervals excluded-intervals #:interpolation interpolation]) → path-geometry? axes : axes-visual? function : (procedure-arity-includes/c 1) x-min : (or/c finite-real? false/c) = #f x-max : (or/c finite-real? false/c) = #f initial-sample-count : (and/c exact-integer? (>=/c 2)) = 17 max-deviation : (and/c finite-real? (>=/c 0)) = 1/100 max-depth : (and/c exact-integer? (>=/c 0)) = 12 clip? : boolean? = #t max-jump : (or/c false/c (and/c finite-real? (>=/c 0))) = #f detect-discontinuities? : boolean? = #t excluded-intervals : list? = '() interpolation : curve-interpolation? = 'linear
The x-coordinate rule is the same as sample-function-path: linear axes use arithmetic interpolation and log axes use uniform logarithmic display interpolation. Callback values follow the same finite-real/#f/nonfinite rules, except that an exact numeric division-by-zero exception is treated as a gap. Other callback exceptions are reported with their x value.
With detect-discontinuities? true, an interval whose adjacent samples lie beyond opposite visible y boundaries is refined and ultimately broken, rather than clipped through the axes. max-jump adds an independent numeric y-distance break rule. excluded-intervals is a list of either (cons minimum maximum) or (list minimum maximum) values; each finite increasing interval splits the domain and no segment crosses its interior. Overlapping exclusions are merged deterministically.
The result uses the ordinary clipping and linear/smooth path interpolation machinery. It contains immutable axes-local geometry and retains neither the function nor adaptive evaluation cache. No finite initial grid can detect an oscillation that aliases every one of its samples; raise initial-sample-count for that case.
procedure
(adaptive-function-graph axes function #:id id [ #:x-min x-min #:x-max x-max #:initial-sample-count initial-sample-count #:max-deviation max-deviation #:max-depth max-depth #:clip? clip? #:max-jump max-jump #:detect-discontinuities? detect-discontinuities? #:excluded-intervals excluded-intervals #:interpolation interpolation #:opacity opacity #:stroke stroke #:stroke-width stroke-width]) → path-visual? axes : axes-visual? function : (procedure-arity-includes/c 1) id : symbol? x-min : (or/c finite-real? false/c) = #f x-max : (or/c finite-real? false/c) = #f initial-sample-count : (and/c exact-integer? (>=/c 2)) = 17 max-deviation : (and/c finite-real? (>=/c 0)) = 1/100 max-depth : (and/c exact-integer? (>=/c 0)) = 12 clip? : boolean? = #t max-jump : (or/c false/c (and/c finite-real? (>=/c 0))) = #f detect-discontinuities? : boolean? = #t excluded-intervals : list? = '() interpolation : curve-interpolation? = 'linear opacity : opacity? = 1 stroke : any/c = "royalblue" stroke-width : (and/c finite-real? (>=/c 0)) = 3
58.14.2.2 Derived Function Graphs
procedure
(derived-function-graph axes field #:id id [ #:x-min x-min #:x-max x-max #:sample-count sample-count #:clip? clip? #:max-jump max-jump #:detect-discontinuities? detect-discontinuities? #:interpolation interpolation #:opacity opacity #:stroke stroke #:stroke-width stroke-width]) → derived-visual? axes : axes-visual? field : (procedure-arity-includes/c 2) id : symbol? x-min : (or/c finite-real? false/c) = #f x-max : (or/c finite-real? false/c) = #f sample-count : (and/c exact-integer? (>=/c 2)) = 201 clip? : boolean? = #t
max-jump :
(or/c false/c (and/c finite-real? (>=/c 0))) = #f detect-discontinuities? : boolean? = #f interpolation : curve-interpolation? = 'linear opacity : opacity? = 1 stroke : any/c = "royalblue" stroke-width : (and/c finite-real? (>=/c 0)) = 3
For each resolved scene state, the field is sampled anew and produces one concrete path Visual with the requested identity, style, and axes transform. This permits an immutable parameter or another resolved Visual to drive a plot without a mutable updater. As with any derived-visual?, animate its source values or dependencies rather than applying a direct Visual animation to the derived graph.
58.14.3 Parametric Curves
struct
(struct parameter-range (start end) #:transparent) start : finite-real? end : finite-real?
start is the first parameter passed to a sampling procedure.
end is the last parameter passed to a sampling procedure.
The values must be distinct finite reals. The computed difference (- end start) must also remain a nonzero finite real. The order is significant. When start is greater than end, sampling proceeds in decreasing order.
The structure is immutable and transparent. Its public bindings include parameter-range, parameter-range?, both field accessors, and struct:parameter-range.
procedure
(sample-parametric-path axes function [ #:parameter-range domain #:sample-count sample-count #:clip? clip? #:max-distance max-distance #:interpolation interpolation]) → path-geometry? axes : axes-visual? function : (procedure-arity-includes/c 1) domain : parameter-range? = (parameter-range 0 1) sample-count : (and/c exact-integer? (>=/c 2)) = 201 clip? : boolean? = #t
max-distance :
(or/c false/c (and/c finite-real? (>=/c 0))) = #f interpolation : curve-interpolation? = 'smooth
All arguments are checked before function is called. Each sampling call must return exactly one value:
A vec2 is one finite numeric coordinate.
#f is an explicit gap.
Another value, zero values, or multiple values raise an exception that reports the parameter value. An exception from function is reported with the same parameter and the original exception message. Sampling stops at the first error.
When max-distance is a number, two adjacent coordinates are connected only when their Euclidean distance in numeric-coordinate units is no greater than that number. The distance is measured before independent axes scaling. The default #f applies no distance rejection.
Clipping and interpolation follow the common rules described above. The result contains axes-local open subpaths and does not retain function or domain. Empty runs and isolated finite coordinates produce no drawn segment.
procedure
(parametric-curve axes function #:id id [ #:parameter-range domain #:sample-count sample-count #:clip? clip? #:max-distance max-distance #:interpolation interpolation #:opacity opacity #:stroke stroke #:stroke-width stroke-width]) → path-visual? axes : axes-visual? function : (procedure-arity-includes/c 1) id : symbol? domain : parameter-range? = (parameter-range 0 1) sample-count : (and/c exact-integer? (>=/c 2)) = 201 clip? : boolean? = #t
max-distance :
(or/c false/c (and/c finite-real? (>=/c 0))) = #f interpolation : curve-interpolation? = 'smooth opacity : opacity? = 1 stroke : any/c = "royalblue" stroke-width : (and/c finite-real? (>=/c 0)) = 3
The returned path has no fill and copies the current axes translation, rotation, and scale. This is a construction-time snapshot, not a live link. The result can use every operation available to an ordinary path Visual, including create, uncreate, morphing, affine animation, opacity, groups, and renderer-aware layout.
58.14.4 Ordered Data Plots
procedure
(data-series-path axes points [ #:clip? clip? #:max-distance max-distance #:interpolation interpolation]) → path-geometry? axes : axes-visual? points : (listof (or/c vec2? false/c)) clip? : boolean? = #t
max-distance :
(or/c false/c (and/c finite-real? (>=/c 0))) = #f interpolation : curve-interpolation? = 'linear
List order is traversal order. The procedure does not sort by x, infer time order, remove repeated coordinates, or retain the input list. An empty list, a one-point list, or a finite coordinate isolated by gaps produces no drawn segment.
The max-distance, clip?, and interpolation arguments have the same meanings as for sample-parametric-path. Distance is Euclidean in numeric-coordinate units. The result contains only immutable path geometry.
An explicit gap separates two accepted runs.
> (define data (data-series-path (axes #:id 'data-axes) (list (vec2 -2 0) (vec2 -1 1) #f (vec2 1 1) (vec2 2 0)))) > (length (path-geometry-subpaths data)) 2
procedure
(data-plot axes points #:id id [ #:clip? clip? #:max-distance max-distance #:interpolation interpolation #:opacity opacity #:stroke stroke #:stroke-width stroke-width]) → path-visual? axes : axes-visual? points : (listof (or/c vec2? false/c)) id : symbol? clip? : boolean? = #t
max-distance :
(or/c false/c (and/c finite-real? (>=/c 0))) = #f interpolation : curve-interpolation? = 'linear opacity : opacity? = 1 stroke : any/c = "seagreen" stroke-width : (and/c finite-real? (>=/c 0)) = 3
The returned path has no fill and copies the axes translation, rotation, and scale at construction time. It works with ordinary path rendering, creation, removal, morphing, movement, rotation, non-uniform scaling, fading, groups, and relative layout.