pLTlib

A lightweight framework for making interactive mathematical graphics with a focus on refinement, stability, and performance of finalized presentation.



Getting started

Plot element

Plots are composed as a container marked with the Plot class and holding multiple layers of various types. Example structure: <div class="Plot" PlotRange="[0, 7]">
 <svg class="Layer" id="plotCanvas"></svg>
 <div class="Layer" Axes Arrowheads Ticks=1></div>
 <svg class="Layer" id="epilog"></svg>
 <div class="Label">Example</div>
</div>
The layers are rendered in reverse order: the first one is placed at the bottom of the final illustration with each next layer getting stacked on top of the previous one. To draw the axes underneath everything else in the code above, for example, the first two layers should be swapped.

Graphical content can be added using the provided plotting methods and combined with static elements hardcoded directly into the respective layers: <svg class="Layer" id="plotCanvas">
 <circle r=0.5 cx=2 cy=1 ></circle>
</svg>

Overall plot properties are set up via html attributes, while the appearance of any component is customized with CSS rules written for the provided classes. Interactive controls can be made from standard HTML inputs and connected to responsive graphical components using simple update queue.

Downloads plt_template.html plt_example.html article_template.html

Boilerplate

Import

The code for importing the framework: <script defer src="https://cdn.jsdelivr.net/gh/Eltaurus-Lt/SquirrelsAndSpace/pLTlib/pLTlib.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Eltaurus-Lt/SquirrelsAndSpace/pLTlib/pLTlib.css">
<script defer src="https://cdn.jsdelivr.net/npm/mathjax@4/tex-mml-chtml.js"></script>
Should be appended to the <head> of an .html file. The last line is for importing MathJax and can be ommited if LaTeX fonts and formatting are not used.

Scripting

Scripts relying on pLTlib methods should be wrapped in a DOMContentLoaded callback to make sure the dependencies are loaded before being used: <script>
  
   … // non-plt scripts
  
  document.addEventListener("DOMContentLoaded", () => {
    
     … // scripts creating plt objects
    
    plt.dynamic.push((dynamic_vars) => {
      
       … // scripts fot dynamic plt content
      
    });
  });
</script>
(scripts outside of the DOMContentLoaded can predefine framework's input parameters programmatically ahead of its initialization).

Embedding

Independent illustrations are intended to be coded in individual .html files (each can contain multiple .Plot objects), which then could be embedded into a collective article using the following tag: <iframe src="plot.html" width="600px" onload="window.addEventListener('message', e=>{if(e.source===this.contentWindow && e.data && e.data.type==='embed-height'){this.style.height=e.data.height+'px';}});"></iframe>

Replace plot.html with the actual path to the plot file being embedded. The width attribute can be modified as needed and additional attributes, like classes and styling, can be appended as usual. The included onload function ensures the height of the embedded plot is fit to the proper aspect ratio of the referenced plot html and accurately reacts to page resizing.

Layers

Types of layers to be used in plot composition. A plot can contain any number of layers of any kind.

Vector layers

Layers for adding various plot lines and elements of vector graphics to the overall illustration. <svg class="Layer"></svg> Coordinates and their units are matched with the plot space. In particular, vertical axis is directed upward, as expected from a mathematical plot, instead of going down, as is the default for svg (and 2D images in general). Note, that if a plot has different scales on horizontal and vertical axes, graphical elements will appear accordingly stretched or squashed. Line stroke thickness is an exception from this rule and is set in pixels of the screen space, remaining fixed independently of the plot's display size. When plotting points, to ensure circular appearance at arbitrary ScaleRatio, a markup layer with markers should be used instead of a vector layer with <circle> tags.

Markup layers

Layers for adding elements, that should be positioned in the plot space, but sized in the screen space. This includes construction elements, such as axes, grids, ticks, etc., plot markers, labels, as well as any kind of custom insets. <div class="Layer"></div> The plot-space coordinates of a markup-layer element can be set via left and bottom CSS properties for the x and y respectively (note that top and right cannot be used for this purpose). For the respective values, the % unit is getting repurposed as the universal plot-space length unit (and, consequently, loses its standard CSS meaning). For example, the following CSS rules put an element exactly at the point with coordinates (1.4, 2): left: 1.4%; /* x = 1.4 */
bottom: 2%; /* y = 2 */
By default, all elements are aligned to the specified position with their centers. To change the location of an anchor point, translate CSS property should be set to translate: Ah Av; with Ah being the anchor point's relative horizontal position (0 = left side, -100% = right side, -50% = horizontal middle) and Av being the relative vertical position (0 = bottom side, -100% = top side, -50% = vertical middle). For example: translate: 0 -100%; /* sets the top left corner as the anchor point */

Raster layers (WIP)

Layers for drawing with canvas methods. <canvas class="Layer"></canvas> Resolution is defined using layer attributes and stays fixed independently of the actual displayed plot size. Useful for ArrayPlot's, in particular.

Shader layers (WIP)

Layers for producing raster assets rendered at screen resolution, regardless of the overall displaying size of the plot. <canvas class="Layer glsl"></canvas> Serve as attachment points for GLSL fragment shaders. Plot-space coordinates are accessible from within a shader as vec2 xy = (gl_FragCoord.xy - plotOrigin) / plotResolution;

Non-layer elements

Any element inside a .Plot container, if not marked with the Layer class, keeps its access to the default page space and can be positioned and sized as ordinary HTML, using px, % (which, in this case, would measure fractions of the width and height of the .Plot itself), or any other HTML length units. This, in particular, can be used to add plot legends, buttons, and other overlays. PlotLabels are also generated as non-layer elements.

Dynamic evalutation

Control elements

Basic controls can be build using native html <input> elements. To make a slider, for example, the following should be put anywhere inside the body of the plot html file (outside of the .Plot container itself): <input pltvar="k" type="range" min=0 max=1 step="any" value=0.5> The attributes here are standard html slider parameters, which can be used to configure the inputs. The only part essential for the plt framework is the pltvar property that indicate a text name for the JavaScript variable to be controlled by the slider. No other setup for the input element itself, regardless of the type, is required to make it fully functional.

Mouse controls (WIP)

Update queue

For components of the plot to dynamically respond to changing inputs, their parameters should be updated on each interaction. To automate the associated setup, plt provides the dynamic object, which can be used to push individual update functions into a unified queue: plt.dynamic.push(({k, t}) => {
  circleL.setAttribute("r", 1 + k * Math.sin(t) );
});
In this example, the radius of the circle will get updated in accordance with the provided function each time the value of either k or t is modified. Note that, in general, the arguments referenced in the curly braces can be listed in any order, as long as their names correspond to pltvar values of <input> elements present in the document (a function can use only the necessary subset of pltvar variables, not every <input> has to be referenced in each instance).

The functions from plt.dynamic are automatically connected to the input elements they depend upon and get called back in real time when the associated inputs are being interacted with. The callback execution follows the order in which the functions were originally pushed in. This, in particular, can be leveraged to avoid excessive reevaluations by calculating all reusable values at the head of the queue and saving them as global variables to be accessable from successive functions. Such optimisation is especially important for demanding computations, like ODE integration.

To manually trigger an update, the .update method can be called, specifying the the pltvar variables to be refreshed as names in string format: plt.update("t", "ω"); (WiP) Calling update() without arguments performs a global update, refreshing all variables and the whole dynamic queue. In particular, update() is useful for initiating the state of the plot at the end of the script, after all dynamic functions were pushed to the queue.

Dynamic PlotRange (WIP)

Custom inputs (advanced)

Extra interactivity, such as plot reacting to keyboard events or mouse clicks on its canvas, can be developed by adding a suitable event listener. The values of auxiliary dynamic variables not represented by any explicit <input> controls are to be stored in dummy html elements, which can be placed anywhere inside the document (by default, they are not being displayed in any way): <var pltvar="ω" value=42 ></var> Code for modifying the value of the respective variable from the event listener: document.querySelector('var[pltvar="ω"]').value = ...; Followed by the manual plot state update described in the previous section.

Appearance

Attributes

Attributes are used to define structural parameters of the plots and are included inside the opening tag of the respective element: <div class="Plot" PlotRange=5 AspectRatio=1 ... > Can be listed in arbitrary order. The only mandatory attribute is PlotRange – any other can be ommited, if the respective setting is not needed.

Plot

Attributes attached to the div.Plot object itself (control overall sizing):

Note that vertical range cannot be set directly, because it depends on the properties above, which are more important for final presentation of the illustration than exact Ymin and Ymax values (aspect ratio is typically expected to be standardized across an entire article, while scale ratio is required for plotting data without distortion).

Markup layers

Attributes attached to a div.Layer element: <div class="Layer" Frame Grid Ticks=1 TicksRight="[[.5, '$\\frac{1}{2}$'], 1, [1.618, '$\\varphi$']]" ... > Primarily control overlayed details, such as ticks and text labels. The details do not have any default values – plots display what have been explicitly declared. Attributes that appear without values practically serve as toggles, with the result depending only on whether their name is attached to the <div> at all:

Labeling attributes:

All text labels (including manual tick labels) support MathJax for LaTeX expressions.

Styling

Appearance of any plot component is controlled directly, using standard CSS. Styling code can be put in a <style>...</style> tag (or referenced from a separate file) inside the <head> of the document after pltlib import tags.

CSS properties (WiP)

Inherited. --Color, --Thickness, --PointSize, --TickLength, --Dashing, --DashingPeriod. To reset bounding to plot origin for a specific object (e.g., plot legend): translate: 0;

Selector classes

Classes .Axis, .Arrowhead, .Frame, .Gridline, .Tick, as well as general .Label class, are provided for targeting certain kinds of elements generated from attributes. Example uses: .Frame {
  --Thickness: 5px; /* inherited by the frame ticks */
}

.Axis, .Gridline {
  --Color: green; /* inherited by the axes' ticks and arrowheads */
  --Dashing: 0.5;
  --DashingPeriod: calc(4 * var(--Thickness));
}

.Tick {
  --TickLength: 15px;
  --Thickness: 2px; /* overrides inherited value */
}

.Axis {
  justify-content: 0; /* tick alignment */
}
All generated components are <div> elements. Axes, frames, and gridlines are direct children of HTML layers they are set up for. Frame ticks are attached to edges of a frame, while arrowheads and axes ticks are parented to respective axes. Labels of all kinds are direct descendants of the elements they are labeling. Children elements are set up to inherit basic properties from their parents by default. In particular, ticks inherit thickness and color from their axes and frame (which can be overriden by a css rule targeting ticks).

For selecting subsets of the components within a certain kind, supplementary classes can be appended to selectors. Classes .X and .Y target any element (axis, arrowhead, gridline, tick, ...) associated with respective direction: .Axis.X,
.Frame .Tick.Y {
  border-color: #bada55;
}
.Arrowhead.X {
  background-color: gold;
}
Additionally, classes .Bottom, .Left, .Top, and .Right are available for arrowheads, frame labels, frame edges, and frame ticks. Subticks do not have a dedicated class, instead they are marked with an auxiliary .Sub class on top of the general .Tick and can be targeted with .Sub.Tick selector.

Similarly, different kinds of labels do not have distinguishing classes of their own. Instead, they can be selected using the class of its parent element and the child combinator: .Plot > .Label {
  font-size: 20px;
}

.Tick > .Label {
  font-size: small;
}

Positioning ticks and labels

align-items: center; flex-start (default/inner ticks); flex-end (outer ticks); for individual ticks: align-self To align top right corner instead, the following transform can be applied: transform: translate(-100%, 100%); . To align the element to the coordinates with its center, transform values (-50%, 50%) can be used. This, in particular, is the method for adding plot Markers.

For controlling tick alignment relative to its parent axis (or frame edge), the translate property can be used:

labels (WiP)

Extra classes

For styling elements created in the plot scripts, custom classes can be assigned in the .add() method. Some extra class names have predefined appearance settings associated with them:

Custom layouts

The framework puts no restrictions on the number and positions of plot elements and inputs. They can be arranged freely, using any means available in HTML. In particular, CSS Grid is useful for making multi-panel layouts and complex interfaces.
It should be noted, that, by default, height of a .Plot element is connected to its width through the AspectRatio. If a different CSS rule is manually set for the height, it might be practical to change AspectRatio to a sufficiently small value and add overflow-y: hidden; to the .Plot styles.

Numerical methods

Provided implementations of common numerical methods covering basic needs of plotting functions and solving algebraic and differential equations. Because finalized illustrations imply predefined well-posed conditions, the provided routines perform no data checks and rely on the incoming arguments always having appropriate formats. To further cut on overhead and avoid jittering typically associated with real-time auto-tuning, all parameter values are taken in explicitly.

Plots

Path methods

Methods called from the plt object that return path data in the d-string format expected by a <path> element of a vector layer: const vectorLayer = document.querySelector("svg.Layer");
vectorLayer.add("path", "PlotLine").setAttribute("d", path_string);
Here .add is the provided utility method for creating and attaching plot elements, white the path_string is a result returned by either of the path methods listed below (for static plots, each has a simplified Plot counterpart):

Note that multiple paths constructed using different methods can be stitched together with the .merge() utility function.

Plotting methods

Methods abstracting the creation of required elements with all required data attributes assigned internally. Take in optional classList argument to mark the created elements for selective styling.

Solvers

Provided methods for solving algebraic and differential equations. Called from the plt object.

Utility

Quality-of-life features automating certain frequently repeated patterns.

3D plots (WIP)

Layers additionally marked with the .3D class present their inner elements as located in 3D space of the plot with the positions and sizes automatically adjusted with camera movements. Elements added to these layers must have the required 3D data attribute attached (all positions are specified in global plot space). In general, all the same types of layers as in regular 2D plots can be used for 3D, but each type has its own set of features and differences associated with the extended functionality.

3D vector layers

Layers for adding vector lines, wireframes (of 3D plots, in particular), arrows, curves, and other elements that can be drawn with purely vector means. <svg class="Layer 3D"></svg>

3D markup layers

Similarly to 2D, markup layers can display axes, ticks, boxes, as well as markers and text labels placed in the 3D space. Elements can be scaled with the distance, or rendered with a constant screen-space size. <div class="Layer 3D"></div>

3D raster layers

Layers for drawing fully-shaded 3D objects and surfaces. Requires additionally importing three.js library. Can be used for displaying imported 3D models or for procedurally generated 3D plots and sets of primitives. <canvas class="Layer 3D"></canvas>

Shader layers

Shaders do not require any additional attributes for the layers. <canvas class="Layer glsl"></canvas> Any shader layer within a 3D plot will automatically get access to 3D camera parameters cameraOrigin (vec3), cameraRotation (mat3), fov (float). In combination with the plotResolution they can be used to calculate the direction of a cast ray for the pixel: vec3 d = normalize( cameraRotation * ( vec3(0, 0, -1) + fov * vec3(gl_FragCoord.xy - .5 * plotResolution, 0) / plotResolution ) );

2D layers and elements

4D plots (WIP)