infinite_math_plot 0.1.1
infinite_math_plot: ^0.1.1 copied to clipboard
A Flutter package that renders interactive mathematical graphs on a simulated infinite coordinate plane.
Changelog #
All notable changes to this package will be documented here.
The format follows Keep a Changelog, and this project uses Semantic Versioning.
0.1.1 - 2026-05-20 #
This release fixes two rendering bugs that showed up during real-world testing with the example app.
Fixed #
Zoom applies cumulative scale instead of per-frame delta
Flutter's ScaleUpdateDetails.scale is a cumulative value measured from the start of the gesture — it goes 1.0 → 1.5 → 2.0 → 2.5 as you pinch. The previous code passed this directly to the zoom math, so every frame re-applied the full accumulated zoom from scratch. The result was that the viewport would explode or collapse in a single gesture, and the Y axis behaved worse than X because the math-to-screen ratio differs between axes on non-square screens.
The fix: GestureHandler now tracks _lastScale and computes the incremental delta each frame (delta = cumulative / lastScale). Only this small per-frame factor is applied to the viewport. onScaleStart() resets _lastScale = 1.0 at the beginning of each gesture, and the widget calls it via GestureDetector.onScaleStart.
Because _lastScale is mutable state, the GestureHandler constructor is no longer const. The instance is now kept at the widget state level so the scale tracking persists across frames within a gesture.
Steep continuous functions incorrectly broken into fragments
The asymptote detector used a single condition: |next - prev| > viewportHeight * 2. For y = x³ zoomed out to x ∈ [-10, 10], consecutive y-values near the edges (e.g. 729 and 1000) have a jump of 271, which easily exceeded the threshold of 40. The function is perfectly continuous — it's just steep and off-screen on the same side. This caused the curve to be broken into fragments with visible gaps.
The fix: isAsymptote now requires both conditions to be true:
- The jump exceeds
viewportHeight * 2— values are far apart in math space. - The values straddle the viewport — one is above
yMaxand the other is belowyMin(or inside). This is the signature of a true asymptote like1/xortan(x).
Steep-but-continuous functions like x³ have both consecutive values on the same side of the viewport, so they are no longer broken. isAsymptote now accepts optional yMin and yMax named parameters; the SampleGenerator passes the current viewport bounds when calling it.
Changed #
GestureHandlerconstructor is no longerconst(required by the mutable_lastScalefield)DiscontinuityDetector.isAsymptotesignature extended with optionalyMinandyMaxnamed parameters (defaults todouble.negativeInfinity/double.infinityfor backward compatibility)GestureHandleris now instantiated once ininitStateand stored in widget state, rather than being recreated on every buildpubspec.yamlupdated withrepositoryandissue_trackerfields pointing to the GitHub project
0.1.0 - 2026-05-12 #
First public release.
Added #
Core widget
InfiniteMathPlot— aStatefulWidgetthat accepts an equation string and renders an interactive graph- Single-equation mode via the
equationparameter - Multi-equation mode via the
equationsparameter (list ofEquationConfig) - Mutual exclusion assertion: providing both
equationandequations, or neither, throws at construction time xMin,xMax,yMin,yMaxparameters to set the initial viewport (default: -10 to 10 on both axes)showGrid,showAxes,enableZoom,enablePanboolean flags, all defaulting totruegraphColorandstrokeWidthfor single-equation stylingsamplingPrecisionto control sample density (default: 2.0 points per logical pixel)- Adaptive layout via
LayoutBuilder— fills available space and maintains math aspect ratio on resize
Equation parser
- Parses Cartesian (
y = f(x)), implicit (F(x,y) = 0), parametric (x = f(t), y = g(t)), and polar (r = f(θ)) equations from plain strings - Supported functions:
sin,cos,tan,asin,acos,atan,sqrt,abs,exp,log,pow - Supported constants:
pi,π,e,theta,θ ParseExceptionwith a human-readable message and character position on invalid input- Canonical serialization via
serialize(Expression)— stable across round-trips
Sampling
- Cartesian sampling: iterates x over the viewport, evaluates y = f(x)
- Polar sampling: iterates θ from 0 to 2π, computes x = r·cos(θ), y = r·sin(θ)
- Parametric sampling: iterates t from 0 to 2π, evaluates x = f(t) and y = g(t) independently
- Implicit sampling: marching squares over a 50×50 grid, linear interpolation of zero-crossings
- Sample cache keyed on
(equation, viewport, samplingPrecision, screenWidth)— no recomputation when nothing changes
Discontinuity detection
isUndefined: classifiesNaN,+Infinity,-Infinityas undefined — path is lifted at these pointsisAsymptote: detects true vertical asymptotes without breaking steep-but-continuous functions
Rendering
GraphPainter(CustomPainter) draws grid → axes → curves in that order- Grid interval snaps to "nice" numbers (1, 2, 5, 10, 20, 50, …) based on the current viewport scale
- Axes drawn only when x=0 or y=0 is within the viewport
- Tick marks with numeric labels on both axes
shouldRepaintreturnsfalsewhen nothing has changed
Gesture handling
- Pinch-to-zoom centered on the focal point
- Scroll-wheel zoom centered on the pointer position
- Drag-to-pan
- All gestures respect
enableZoomandenablePanflags
Coordinate transformer
toScreen(x, y)andtoMath(screenOffset)with round-trip accuracy < 1e-10
Data models
Viewport,ParsedEquation,ParseException,EquationConfig,SampleCacheKey,GraphState,EquationType
Tests
- 241 tests covering all components, including property-based round-trip tests and widget tests
Upcoming #
- Configurable parametric t-range (currently fixed at 0 to 2π)
- Tap-to-inspect: show the y-value at a tapped x position
- Isolate-based sampling for very high precision or many simultaneous equations
- Animation support for time-varying equations
- Export to image