MathBox Documentation

repository·master·Indexed 23 days ago

https://github.com/unconed/mathbox

A library for rendering presentation-quality mathematical diagrams in the browser using WebGL. Built on Three.js and ShaderGraph, MathBox provides a declarative, tree-based API to visualize and animate mathematical relationships through a selection-based system similar to D3 or jQuery.

Tokens
23.1K
Snippets
38
Records
127
Agent score
81%

What's inside MathBox

  1. List of MathBox Primitives

    master

    MathBox is organized into several functional modules. Primitives are grouped as follows:

    • base: Core tree and element management (group, inherit, root, unit).
    • camera: Camera control (camera).
    • data: Data structures and sampling (array, interval, matrix, area, voxel, volume, scale, latch).
    • draw: Geometric drawing primitives (axis, face, grid, line, point, strip, surface, ticks, vector).
    • operator: Data manipulation and transformations (clamp, grow, join, lerp, memo, readback, resample, repeat, reverse, swizzle, spread, split, slice, subdivide, transpose).
    • overlay: HTML and DOM integration (html, dom).
    • present: Animation and presentation controls (move, play, present, reveal, slide, step).
    • rtt: Render-to-texture capabilities (rtt, compose).
    • shader: Custom shader integration (shader).
    • text: Text rendering and formatting (text, format, label, retext).
    • time: Time management (clock, now).
    • transform: Geometry and shader transformations (transform, transform4, vertex, fragment, layer, mask).
    • view: View projection and range adjustment (view, cartesian, cartesian4, polar, spherical, stereographic, stereographic4).
  2. Understand the MathBox DOM and Node structure

    master

    MathBox uses a virtual DOM to manage its hierarchical structure of nodes.

    • Node: An instance of a Primitive inserted into the MathBox DOM.
    • Primitive: The basic building blocks of MathBox.
    • Prop (Property): An individual value set on a node. Multiple props are referred to as props.
    • Selection: A subset of the DOM used to target specific nodes. Selections use CSS-like selectors, such as:
      • Primitive name: "camera"
      • ID: "#colors"
      • Class: ".points"
  3. Understand MathBox graphics and rendering concepts

    master

    MathBox leverages WebGL and several supporting technologies for high-performance math graphing:

    • WebGL: The underlying JavaScript API used for rendering 3D scenes.
    • Shader: A GLSL program running on the GPU (syntax similar to C++).
    • ShaderGraph: A MathBox dependency that dynamically compiles small GLSL snippets into a single shader.
    • RTT (Render To Texture): A technique where instead of drawing directly to the screen, the scene is rendered to an image for further processing.
    • Three.js: A WebGL library used by MathBox for managing cameras and controls.
    • Threestrap: A bootstrapping tool used to set options for Three.js.
  4. How MathBox selections and properties work

    master

    MathBox uses a selection-based API similar to D3 or jQuery. The main object returned by the mathBox() constructor is a selection pointing to the <root /> node.

    To modify properties on a selection, use .set(). You can set a single property or an object of multiple properties. To add new elements to the scene, call the matching .type() function (e.g., .cartesian(), .line(), .points()) on a selection.

    To create animations or dynamic behavior, you can pass an object of live expressions as a second argument to a creation function, or use .bind() on an existing selection. Expressions are evaluated every frame and receive time (elapsed clock time) and delta (time since the previous frame) as arguments.

    // Creating an element with dynamic properties
    mathbox.line({
      // initial properties
    }, {
      width: function (time, delta) {
        return 2 + Math.sin(time);
      }
    });
    
    // Alternatively, using .bind() on an existing selection
    mathbox.bind({
      width: function (time, delta) {
        return 2 + Math.sin(time);
      }
    });
  5. Understand data dimensions and shapes in MathBox

    master

    When working with data primitives, MathBox uses specific terminology to describe the structure of the data arrays:

    • Width: The size in the x direction (number of rows).
    • Height: The size in the y direction (number of columns).
    • Depth: The size in the z direction (number of stacks).
    • Items: The size in the w direction (number of data points per spatial location, or the number of times emit is called in an expr function).
    • Channels: The number of values associated with a single data point (the number of arguments passed to emit). This is the size of an individual array element, not an array dimension.
    • History: The process of storing previous 1D or 2D data in an unused dimension.
  6. Perform Swizzling and Transposition on data

    master

    MathBox provides two ways to manipulate data structure and element order:

    • Swizzling: Used to isolate, reorder, or duplicate elements of a vector by listing indices (e.g., a swizzle of "yxz" switches the x and y components). The swizzle primitive operates on array elements.
    • Transposition: The transpose primitive operates on the dimensions of the array itself.
  7. Use multiple data sources in a shader

    master

    By default, a shader samples from one implied source. To use multiple sources, pass an array of selectors or nodes to the sources prop in .shader().

    In your GLSL code, you define functions that match the order of the sources array. The names of these functions are ignored, but their signatures must match the provided indices and channels. The primary source is still accessed via getSample().

    Example configuration for two additional sources:

    .shader({
      code: "#multi-shader",
      sources: ["#array1", "#array2"]
    })

    Corresponding GLSL:

    <script type="application/glsl" id="multi-shader">
      // External sources (matched by order in the 'sources' array)
      vec4 getArray1Sample(vec4 xyzw);
      vec4 getArray2Sample(vec4 xyzw);
    
      // The primary source
      vec4 getSample(vec4 xyzw);
    
      vec4 getFramesSample(vec4 xyzw) {
        return (getArray1Sample(xyzw) + getArray2Sample(xyzw)) * getSample(xyzw);
      }
    </script>
  8. How MathBox primitives and the component tree work

    master

    MathBox uses a declarative, tree-based API. When you call a method on the MathBox API object (like .cartesian() or .axis()), it performs three actions:

    1. Creates a new element (primitive).
    2. Inserts it into the component tree.
    3. Returns a new version of the API object with its selection focused on the newly created element.

    This allows for method chaining to build complex scenes. You can use .select(selector) with CSS-like selectors to navigate the tree and retrieve specific elements.

    // Chaining primitives to build a scene
    const view = root
      .cartesian({
        range: [
          [-2, 2],
          [-1, 1],
          [-1, 1],
        ],
        scale: [2, 1, 1],
      })
      .axis({
        axis: 1,
      })
      .axis({
        axis: 2,
      });
    
    // Selecting elements using CSS-like selectors
    root.select("cartesian > axis");
  9. How MathBox scenes are composed

    master

    MathBox scenes are created by composing an object tree in JavaScript, which functions similarly to the HTML DOM. You define elements like cameras, coordinate systems, and shapes by calling methods on the MathBox instance. This declarative model allows you to mix static definitions with custom expressions and dynamic data.

    To render a scene, you typically need four components:

    1. A camera (defines the viewpoint).
    2. A coordinate system (defines the space).
    3. Geometrical data (the raw values).
    4. A shape (how the data is visualized, e.g., lines, points, or surfaces).
  10. Run MathBox tests in a browser

    master

    MathBox uses Karma to run tests in real browsers, ensuring that WebGL contexts are available and not mocked. New tests should be written in TypeScript. You can run the tests in watch mode to monitor changes.

    npm run tests -- --watch