or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

basic-types.mdcollections.mdcolor-spaces.mdgeneric.mdindex.mdmathematical.mdtransforms-utilities.md
tile.json

mathematical.mddocs/

Mathematical Functions

Advanced mathematical interpolation including B-splines, smooth zoom transitions, and piecewise interpolation.

Capabilities

B-Spline Interpolation

Uniform nonrational B-spline interpolation through arrays of numbers.

/**
 * Returns a uniform nonrational B-spline interpolator through an array of numbers.
 * Returns values[0] at t=0 and values[values.length-1] at t=1.
 * @param values - Array of numbers to interpolate through smoothly
 * @returns Interpolator function returning smoothly interpolated number
 */
function interpolateBasis(values: number[]): (t: number) => number;

Usage Examples:

import { interpolateBasis } from "d3-interpolate";

// Smooth curve through control points
const spline = interpolateBasis([0, 10, 5, 15, 8]);
console.log(spline(0.0));   // 0 (start value)
console.log(spline(0.25));  // ~6.5 (smooth interpolation)
console.log(spline(0.5));   // ~8.1 (smooth interpolation) 
console.log(spline(0.75));  // ~10.8 (smooth interpolation)
console.log(spline(1.0));   // 8 (end value)

// Animation easing curve
const easing = interpolateBasis([0, 0.1, 0.9, 1]);
console.log(easing(0.5)); // Smooth S-curve transition

// Smooth data interpolation
const temperatures = interpolateBasis([20, 25, 30, 28, 22]);
for (let i = 0; i <= 10; i++) {
  console.log(`t=${i/10}: ${temperatures(i/10).toFixed(1)}°C`);
}

Closed B-Spline Interpolation

Cyclical B-spline with continuous derivatives at boundaries.

/**
 * Returns a uniform nonrational B-spline interpolator with cyclical continuity.
 * Control points are repeated such that the spline has cyclical C² continuity.
 * @param values - Array of numbers to interpolate through cyclically
 * @returns Interpolator function returning smoothly interpolated number
 */
function interpolateBasisClosed(values: number[]): (t: number) => number;

Usage Examples:

import { interpolateBasisClosed } from "d3-interpolate";

// Smooth cyclical animation
const cyclical = interpolateBasisClosed([0, 10, 5]);
console.log(cyclical(0.0));  // 0
console.log(cyclical(0.33)); // ~10 (smooth transition)
console.log(cyclical(0.66)); // ~5 (smooth transition)
console.log(cyclical(1.0));  // 0 (back to start, continuous)

// Periodic motion (like sine wave approximation)
const wave = interpolateBasisClosed([0, 1, 0, -1]);
for (let i = 0; i < 8; i++) {
  console.log(`Phase ${i}: ${wave(i/8).toFixed(2)}`);
}

// Smooth color channel cycling
const redChannel = interpolateBasisClosed([128, 255, 200, 100]);

Zoom Interpolation

Smooth zooming and panning transitions using van Wijk and Nuij's algorithm.

/**
 * Returns an interpolator for smooth zoom/pan transitions between two views.
 * Each view is [cx, cy, width] where cx,cy is center and width is viewport size.
 * @param a - Starting view [centerX, centerY, width]
 * @param b - Ending view [centerX, centerY, width]
 * @returns Zoom interpolator with duration property
 */
function interpolateZoom(a: [number, number, number], b: [number, number, number]): ZoomInterpolator;

interface ZoomInterpolator extends ((t: number) => [number, number, number]) {
  /** Recommended transition duration in milliseconds based on path length */
  duration: number;
  /** Configure curvature parameter rho (default: sqrt(2)) */
  rho(rho: number): ZoomInterpolator;
}

Features:

  • Smooth paths: Follows curved trajectory through x,y space for natural motion
  • Duration estimation: Provides recommended duration based on path complexity
  • Configurable curvature: Adjust rho parameter for different path shapes

Usage Examples:

import { interpolateZoom } from "d3-interpolate";

// Basic zoom transition
const zoomInterp = interpolateZoom(
  [0, 0, 10],     // Start: center at origin, width 10
  [100, 50, 2]    // End: center at (100,50), width 2 (zoomed in)
);

console.log(zoomInterp(0.0)); // [0, 0, 10]
console.log(zoomInterp(0.5)); // [~50, ~25, ~4.5] (smooth curve)
console.log(zoomInterp(1.0)); // [100, 50, 2]

// Use recommended duration for animation
console.log(`Recommended duration: ${zoomInterp.duration}ms`);

// Configure curvature (rho close to 0 = more linear)
const linearZoom = interpolateZoom([0, 0, 100], [200, 100, 1]).rho(0.1);
const curvedZoom = interpolateZoom([0, 0, 100], [200, 100, 1]).rho(5);

// Map-style zoom transition
const mapZoom = interpolateZoom(
  [0, 0, 1000],        // Zoomed out view
  [500, 300, 10]       // Zoomed in to specific location
);
console.log(`Map transition duration: ${mapZoom.duration}ms`);

// Zoom out then zoom in (complex path)
const complexZoom = interpolateZoom(
  [100, 100, 1],       // Very zoomed in
  [110, 110, 1]        // Slightly different position, same zoom
);
// Creates smooth arc rather than direct line

Piecewise Interpolation

Compose multiple interpolators for adjacent value pairs.

/**
 * Returns a piecewise interpolator composing interpolators for adjacent pairs.
 * Maps t in [0, 1/(n-1)] to interpolate(values[0], values[1]), etc.
 * @param interpolate - Interpolator function to use (defaults to d3.interpolate)
 * @param values - Array of values to interpolate between sequentially
 * @returns Interpolator function that transitions through all values
 */
function piecewise(interpolate?: Function, values?: any[]): (t: number) => any;

// Alternative single-argument form
function piecewise(values: any[]): (t: number) => any;

Usage Examples:

import { piecewise, interpolateRgb, interpolateNumber } from "d3-interpolate";

// Color progression using RGB interpolation
const colorProg = piecewise(interpolateRgb.gamma(2.2), ["red", "green", "blue"]);
console.log(colorProg(0.0));  // "rgb(255, 0, 0)" (red)
console.log(colorProg(0.25)); // red to green transition
console.log(colorProg(0.5));  // "rgb(0, 255, 0)" (green)  
console.log(colorProg(0.75)); // green to blue transition
console.log(colorProg(1.0));  // "rgb(0, 0, 255)" (blue)

// Multi-stage animation values
const animation = piecewise([0, 10, 5, 20]);
console.log(animation(0.0));    // 0
console.log(animation(0.167));  // ~3.3 (0 to 10)
console.log(animation(0.333));  // 10
console.log(animation(0.5));    // ~7.5 (10 to 5)
console.log(animation(0.667));  // 5
console.log(animation(0.833));  // ~12.5 (5 to 20)
console.log(animation(1.0));    // 20

// Custom interpolator for specific types
const stringPiecewise = piecewise(
  (a, b) => (t) => t < 0.5 ? a : b, // Snap interpolation
  ["start", "middle", "end"]
);
console.log(stringPiecewise(0.25)); // "start"
console.log(stringPiecewise(0.5));  // "middle"
console.log(stringPiecewise(0.75)); // "middle"

// Temperature progression through day
const tempCurve = piecewise([
  15,  // 6am
  18,  // 9am  
  25,  // 12pm
  28,  // 3pm
  24,  // 6pm
  18   // 9pm
]);
for (let hour = 0; hour < 24; hour += 3) {
  const t = (hour - 6) / 18; // Map 6am-9pm to [0,1]
  if (t >= 0 && t <= 1) {
    console.log(`${hour}:00 - ${tempCurve(t).toFixed(1)}°C`);
  }
}

Sampling

Generate uniformly-spaced samples from any interpolator.

/**
 * Returns n uniformly-spaced samples from the specified interpolator.
 * First sample at t=0, last sample at t=1.
 * @param interpolator - Interpolator function to sample
 * @param n - Number of samples (must be >= 2)
 * @returns Array of n sampled values
 */
function quantize(interpolator: (t: number) => any, n: number): any[];

Caution: Does not work with interpolators that don't return defensive copies (like interpolateArray, interpolateDate, interpolateObject). For those, wrap the interpolator to create copies.

Usage Examples:

import { quantize, interpolateNumber, interpolateRgb, interpolateBasis } from "d3-interpolate";

// Sample numeric interpolation
const numSamples = quantize(interpolateNumber(0, 100), 5);
console.log(numSamples); // [0, 25, 50, 75, 100]

// Color palette generation
const colorPalette = quantize(interpolateRgb("red", "blue"), 7);
console.log(colorPalette);
// ["rgb(255, 0, 0)", "rgb(213, 0, 42)", ..., "rgb(0, 0, 255)"]

// Sample B-spline curve
const curveSamples = quantize(interpolateBasis([0, 10, 2, 8]), 9);
console.log(curveSamples); // [0, ~2.4, ~5.7, ..., 8]

// Generate gradient stops for CSS
const gradient = quantize(interpolateRgb("#ff0000", "#0000ff"), 5);
const css = gradient.map((color, i) => `${color} ${i * 25}%`).join(", ");
console.log(`background: linear-gradient(to right, ${css})`);

// Safe wrapper for object interpolation
const objInterp = (a, b) => (t) => {
  const result = interpolateObject(a, b)(t);
  return JSON.parse(JSON.stringify(result)); // Deep copy
};
const objSamples = quantize(objInterp({x: 0}, {x: 10}), 3);
console.log(objSamples); // [{x: 0}, {x: 5}, {x: 10}]

Mathematical Properties

B-Spline Characteristics

  • C² continuity: Smooth second derivatives
  • Local control: Changing one control point affects only nearby curve segments
  • Endpoint behavior: Open splines reach first/last control points; closed splines loop

Zoom Path Algorithm

Based on "Smooth and efficient zooming and panning" by van Wijk and Nuij:

  • Curved paths: Follows hyperbolic trajectory through scale-space
  • Optimal speed: Minimizes perceived motion while maintaining smoothness
  • Configurable curvature: rho parameter controls path shape (default: √2)

Piecewise Mapping

For n values, maps parameter t:

  • [0, 1/(n-1))interpolate(values[0], values[1])
  • [1/(n-1), 2/(n-1))interpolate(values[1], values[2])
  • etc.

This creates a lightweight linear scale for sequential value transitions.