Skip to content

Release MaxMath 1.1.1 3D plotting improvements - #5

Merged
ParuhParhat merged 3 commits into
mainfrom
perf/gl-3d-path
Aug 8, 2026
Merged

Release MaxMath 1.1.1 3D plotting improvements#5
ParuhParhat merged 3 commits into
mainfrom
perf/gl-3d-path

Conversation

@ParuhParhat

Copy link
Copy Markdown
Owner

Summary / 摘要

Release MaxMath 1.1.1 with a substantially leaner 3D/contour rendering path, data-aware gesture resampling, and visible numeric axes.

发布 MaxMath 1.1.1:优化 3D/等高线渲染热路径,加入跟随数据范围的手势重采样,并显示完整数值坐标轴。

What changed / 改动

  • Added CompiledExpr, which compiles the parsed AST once into positional-slot nodes for 3D and contour grid evaluation.

  • Reworked contour marching to traverse cells once and prune levels outside each cell's value range.

  • Added in-place matrix operations, cached camera/projection work, and limited blending to the translucent heatmap pass.

  • Re-evaluates contour meshes over the transformed data range after pan/zoom; 3D zoom keeps the range fixed and increases grid density.

  • Added bounding boxes, tick marks, and numeric labels to 3D and contour plots using shared PlotTicks logic and a GL glyph atlas, so labels are included in PNG exports.

  • Fixed stuck loading state during overlapping redraws, double-applied 3D zoom, near/far clipping, and precision loss in small 2D root labels.

  • Added parity, tick, gesture, matrix, mesh, axes, glyph-layout, and full-rotation camera-framing tests.

  • Bumped the app to 1.1.1 (versionCode 17) and updated both README languages and release notes.

  • 新增 CompiledExpr,将解析后的 AST 一次编译为定长变量槽位,用于 3D/等高线网格求值。

  • 等高线遍历改为按单元执行,并跳过超出单元值域的等值层级。

  • 加入原地矩阵运算、相机/投影缓存,并只在半透明热力图阶段启用混合。

  • 等高线平移/缩放结束后按变换后的数据范围重新采样;3D 放大保持范围不变并提高网格密度。

  • 3D 与等高线新增边界框、刻度及数值标签,共用 PlotTicks 并通过 GL 字形图集绘制,因此 PNG 导出也保留标签。

  • 修复重绘竞争导致加载状态卡住、3D 双重缩放、近远裁剪及小数零点标签精度问题。

  • 新增表达式一致性、刻度、手势、矩阵、网格、坐标轴、字形布局和全旋转相机取景测试。

  • 版本更新为 1.1.1(versionCode 17),同步中英文 README 与发布记录。

Why / 原因

The previous 3D path interpreted the AST at every sample, allocated avoidable matrices on the GL thread, and showed only fixed axis lines without numeric scale. Contour gestures transformed a mesh sampled over the original range, so panning could expose blank space. Several redraw and camera issues were found while making the new axes testable.

旧 3D 路径会在每个采样点重复解释 AST,并在 GL 线程分配不必要的矩阵;坐标轴也只有固定线条而没有数值尺度。等高线手势只变换原始范围生成的网格,拖动后可能露出空白。为新坐标轴建立测试时还发现了多项重绘与相机问题,本次一并修复。

These are structural performance improvements; no device benchmark claim is made in this PR.

这些属于结构性性能优化,本 PR 不宣称尚未实测的设备端性能数字。

Validation / 验证

  • git diff --check origin/main..HEAD passed.
  • All Android XML resources parsed successfully.
  • No signing credentials or private-key material were found in the diff.
  • The APK was built after the source and version changes; it contains a manifest, DEX files, 13 arm64 native libraries, and engine assets, with no other ABI bundled.
  • APK SHA-256: 1598528F39F3BC814540DA5968B4115C6453F77ECE2DC68BB8700AC50DC33CAF.
  • GitHub Actions will run unit tests, assembleDebug, and Android lint for this PR.

yueye6811 and others added 3 commits August 8, 2026 20:38
3D/contour rendering ran the AST interpreter 14,400 times per plot and
allocated roughly 45 matrices per frame on the GL thread, and the plot
carried no numeric scale at all -- three fixed lines that matched no data
value. Dragging a contour only moved a mesh built once over the original
range, so you could drag off the edge into blank space.

Performance
- CompiledExpr compiles the AST once into positional-slot nodes: no string
  parsing, no boxed map lookups, no per-call List. MathFunctions is the
  single source for the unary formulas so Evaluator and CompiledExpr cannot
  diverge; Evaluator stays as the readable reference and the parity target.
- Contour marching is cell-major with level pruning: ~141,610 cell visits
  become ~14,161.
- Mat4 works in place; the renderer builds the camera once per frame and
  caches the projection. Blend is enabled only around the contour heatmap,
  the only geometry with alpha < 1.

Presentation
- Contour pan/zoom is folded back into a data range on release and the mesh
  is re-evaluated there. 3D zoom raises grid density instead, holding the
  range fixed -- rotating something whose axes silently rescale is
  disorienting.
- Both modes draw a bounding box at the data bounds with tick marks and
  numeric labels, sharing PlotTicks with the 2D canvas so the same function
  shows the same ticks in either mode. Labels render through a GL glyph
  atlas so they appear in the glReadPixels PNG export.

Defects found in review
- resample no longer shares a Job with regenerate. Tapping Plot and then
  dragging before the build finished cancelled the redraw on a path that
  never clears `loading`, stranding the progress spinner.
- 2D zero/extremum labels use the new PlotTicks.formatValue (4 significant
  digits); routing them through the tick formatter printed a root at
  0.000123 as 0.0001. Both formatters pin Locale.ROOT, which "%.4g" did not.
- 3D zoom was applied twice -- model scale and camera dolly -- so a 2x pinch
  magnified 4x and the box's nearest corner crossed the near plane at
  zoom ~1.7, inside the 8x gesture range. Zoom is now applied once by
  narrowing the field of view with the camera fixed, and near/far come from
  the content radius (3.10..7.61 instead of 0.1..100).
- CompiledExprTest iterated a hand-copied function-name list, so a name
  added to the parser was never checked. It now iterates the real
  MathParser.FUNCTION_NAMES.

Structure
- PlotGlModels.kt holds the model matrices formerly on the renderer
  companion; PlotGlRendererTest becomes PlotGlModelsTest.
- GlyphMetrics splits the pure vertex layout out of GlyphAtlas's GL upload,
  which is what makes it testable at all.

Note: contour segments now come out cell-major rather than level-major.
They are drawn as independent GL_LINES in one colour, so the image is
identical, but it is an observable array-order change.

Verification: this has NOT been compiled -- there is no JDK or Android SDK
on this machine, so CI is the first compiler. Node cross-checks confirm the
new projection magnifies linearly in zoom (2.8e-16 deviation) and that no
point at the content radius crosses near or far at any zoom, that the
contour range inverse round-trips to 1.7e-8 px, and that in-place translate
and scale are bit-identical to the allocating originals. The new unit tests
are the real gate.

Known limitation: in portrait the horizontal field of view contains only
radius ~1.0-1.2 at zoom 1 while the axis geometry reaches 2.03, so corner
labels can sit off-screen. Pre-existing framing behaviour, but the new
labels make it matter; fixing it trades plot size for label visibility and
needs a separate decision.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous commit's "known limitation" was wrong, and so was the KDoc on
EYE_DISTANCE. Both claimed that in portrait the horizontal field of view is
too narrow and corner labels fall off-screen. That reasoning assumed the GL
surface fills a portrait screen (aspect ~0.5). It does not: PlotScreen wraps
it in fillMaxWidth().aspectRatio(Sizing.PLOT_ASPECT) with PLOT_ASPECT = 3/2,
so the aspect is 1.5 regardless of device orientation.

Recomputed with the real geometry and real rotations rather than a bounding
sphere: at aspect 1.5 the worst orientation needs eye distance 5.11 against
the 5.36 in use, and the worst |ndc| over 64,800 orientations is 0.939.
Everything is framed, at every orientation. There is nothing to fix.

The actual defect was that this was verified by hand, so:

- PlotGlCamera holds FOV, CONTENT_RADIUS, EYE_DISTANCE, NEAR/FAR and
  fovForZoom, out of the renderer's companion where nothing could reach them.
- PlotGlCameraTest projects every PlotGlAxes vertex and label anchor through
  the real Mat4 pipeline at Sizing.PLOT_ASPECT across azimuth 0..360 and
  elevation 0..180, asserting |ndc| <= 1 and w > 0. It also pins that nothing
  crosses near/far at any zoom, that magnification is proportional to zoom,
  and that fovForZoom survives zoom = 0.

The test imports Sizing.PLOT_ASPECT rather than hardcoding 1.5, so changing
the plot area's aspect ratio now fails the framing test instead of silently
cropping the axes.

Not compiled -- no JDK or Android SDK here. The Node cross-check that
produced the numbers above is the only thing that has actually run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ParuhParhat
ParuhParhat marked this pull request as ready for review August 8, 2026 15:23
@ParuhParhat
ParuhParhat merged commit 9720868 into main Aug 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant