refactor(math): extract core base class and de-duplicate math module (#47)#62
Merged
Conversation
…47) First de-duplication step of the stub/monolith cleanup (#47), guarded by the 57-test parity suite. - NEW src/core/core.ts: the base jsfeatNext class (constants, per-instance cache/data-type helpers, static module slots), extracted verbatim from the monolith. Modules can now extend it without a circular import against the aggregator — the keystone every subsequent #47 extraction reuses. - src/math/math.ts: replace the type-only stub (every method threw 'Method not implemented') with the REAL implementation moved verbatim from the monolith (get_gaussian_kernel, deprecated perspective_4point_transform, qsort, median). Only deliberate change: median() now accepts typed arrays in its signature — motion_estimator.lmeds always called it with a Float32Array cache buffer; the old stub hid that behind 'any'. - src/jsfeatNext.ts: shrinks by ~560 lines; imports the base from core and attaches math from its module (jsfeatNext.math = math). Temporary (tightened as #47 proceeds): the affine2d/homography2d static slots on the base are typed 'any' until those classes move out of the monolith. Verified behavior-preserving: tsc --noEmit clean; npm test 57/57; UMD require shape, instanceof chain (new jsfeatNext.math() instanceof jsfeatNext), static-constant inheritance (jsfeatNext.math.EPSILON) and gaussian-kernel numerics all checked against the pre-refactor build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 8, 2026
kalwalt
added a commit
that referenced
this pull request
Jul 8, 2026
Second de-duplication step of #47, following the pattern proven in #62. - src/imgproc/imgproc.ts: replace the type-only stub with the REAL implementation moved verbatim from the monolith (grayscale, resample, box_blur_gray, gaussian_blur, hough_transform, pyrdown, scharr/sobel derivatives, compute_integral_image, equalize_histogram, canny, warp_perspective, warp_affine, skindetector). Only deliberate change: gaussian_blur instantiates the math module directly (import from ../math/math) instead of via the jsfeatNext.math static slot, removing an attach-order dependency on the aggregator. - src/jsfeatNext.ts: shrinks by ~1050 lines; attaches imgproc from its module (jsfeatNext.imgproc = imgproc). - Side effect: the latent trap in src/orb/rectify_patch.ts (it imports imgproc, which until now was the throwing stub) is healed — it resolves to the real implementation. Verified behavior-preserving: tsc --noEmit clean; npm test 57/57 (14 of those pin imgproc bit-for-bit vs original jsfeat); UMD build checked (instanceof chain, static-constant inheritance, grayscale + gaussian_blur smoke on the bundle). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kalwalt
added a commit
that referenced
this pull request
Jul 8, 2026
Third de-duplication step of #47, following the #62/#63 pattern. - src/fast_corners/fast_corners.ts: replace the type-only stub with the REAL implementation moved verbatim from the monolith (set_threshold, detect, _cmp_offsets; imports _cmp_score_16 from ./fast_private, which was already a real module). - src/jsfeatNext.ts: shrinks by ~225 lines; attaches fast_corners from its module. Verified behavior-preserving: tsc --noEmit clean; npm test 57/57 (detector parity suite pins fast_corners.detect against original jsfeat); UMD bundle smoke-checked (instanceof chain, static inheritance, corner detection on a synthetic image). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kalwalt
added a commit
that referenced
this pull request
Jul 8, 2026
Third de-duplication step of #47, following the #62/#63 pattern. - src/fast_corners/fast_corners.ts: replace the type-only stub with the REAL implementation moved verbatim from the monolith (set_threshold, detect, _cmp_offsets; imports _cmp_score_16 from ./fast_private, which was already a real module). - src/jsfeatNext.ts: shrinks by ~225 lines; attaches fast_corners from its module. Verified behavior-preserving: tsc --noEmit clean; npm test 57/57 (detector parity suite pins fast_corners.detect against original jsfeat); UMD bundle smoke-checked (instanceof chain, static inheritance, corner detection on a synthetic image). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kalwalt
added a commit
that referenced
this pull request
Jul 8, 2026
… kernels (#47) Eighth de-duplication step of #47 — the biggest structural win. - NEW src/motion_model/motion_model.ts: motion_model + affine2d + homography2d kernel classes, moved verbatim from the monolith (they were module-local classes there). Kernels instantiate linalg via direct module import instead of the jsfeatNext.linalg static slot. - src/motion_estimator/motion_estimator.ts: replace the type-only stub with the REAL implementation (get_subset, find_inliers, ransac, lmeds); lmeds instantiates math via direct module import. - src/core/core.ts: the temporary any-typed affine2d/homography2d static slots (a known TODO since #62) now have precise typeof types via type-only imports. - src/jsfeatNext.ts: down to ~380 lines — pure aggregator except for optical_flow_lk, the last inline module. Verified behavior-preserving: tsc --noEmit clean; npm test 57/57 (3 parity tests pin ransac/lmeds with identical models AND inlier masks vs original jsfeat on seeded data); UMD bundle smoke-checked (instanceof for estimator and both kernels, affine RANSAC recovers the synthetic model). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kalwalt
added a commit
that referenced
this pull request
Jul 8, 2026
One-shot artifact rebuild for the integration of the stub/monolith de-duplication (#62-#70) into dev, per the agreed convention (refactor PRs ship source only; artifacts are rebuilt at integration/release points). - dist/jsfeatNext.js (UMD) + dist/jsfeatNext.mjs (ESM) rebuilt from the refactored source - types/ regenerated: now mirrors the new module layout, including types/src/core/core.d.ts and types/src/motion_model/motion_model.d.ts Verified: npm test 57/57; all 18 public modules attach on the fresh UMD bundle; VERSION 0.7.6 intact; no .d.ts files leak into dist/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kalwalt
added a commit
that referenced
this pull request
Jul 9, 2026
Second de-duplication step of #47, following the pattern proven in #62. - src/imgproc/imgproc.ts: replace the type-only stub with the REAL implementation moved verbatim from the monolith (grayscale, resample, box_blur_gray, gaussian_blur, hough_transform, pyrdown, scharr/sobel derivatives, compute_integral_image, equalize_histogram, canny, warp_perspective, warp_affine, skindetector). Only deliberate change: gaussian_blur instantiates the math module directly (import from ../math/math) instead of via the jsfeatNext.math static slot, removing an attach-order dependency on the aggregator. - src/jsfeatNext.ts: shrinks by ~1050 lines; attaches imgproc from its module (jsfeatNext.imgproc = imgproc). - Side effect: the latent trap in src/orb/rectify_patch.ts (it imports imgproc, which until now was the throwing stub) is healed — it resolves to the real implementation. Verified behavior-preserving: tsc --noEmit clean; npm test 57/57 (14 of those pin imgproc bit-for-bit vs original jsfeat); UMD build checked (instanceof chain, static-constant inheritance, grayscale + gaussian_blur smoke on the bundle). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kalwalt
added a commit
that referenced
this pull request
Jul 9, 2026
Third de-duplication step of #47, following the #62/#63 pattern. - src/fast_corners/fast_corners.ts: replace the type-only stub with the REAL implementation moved verbatim from the monolith (set_threshold, detect, _cmp_offsets; imports _cmp_score_16 from ./fast_private, which was already a real module). - src/jsfeatNext.ts: shrinks by ~225 lines; attaches fast_corners from its module. Verified behavior-preserving: tsc --noEmit clean; npm test 57/57 (detector parity suite pins fast_corners.detect against original jsfeat); UMD bundle smoke-checked (instanceof chain, static inheritance, corner detection on a synthetic image). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kalwalt
added a commit
that referenced
this pull request
Jul 9, 2026
… kernels (#47) Eighth de-duplication step of #47 — the biggest structural win. - NEW src/motion_model/motion_model.ts: motion_model + affine2d + homography2d kernel classes, moved verbatim from the monolith (they were module-local classes there). Kernels instantiate linalg via direct module import instead of the jsfeatNext.linalg static slot. - src/motion_estimator/motion_estimator.ts: replace the type-only stub with the REAL implementation (get_subset, find_inliers, ransac, lmeds); lmeds instantiates math via direct module import. - src/core/core.ts: the temporary any-typed affine2d/homography2d static slots (a known TODO since #62) now have precise typeof types via type-only imports. - src/jsfeatNext.ts: down to ~380 lines — pure aggregator except for optical_flow_lk, the last inline module. Verified behavior-preserving: tsc --noEmit clean; npm test 57/57 (3 parity tests pin ransac/lmeds with identical models AND inlier masks vs original jsfeat on seeded data); UMD bundle smoke-checked (instanceof for estimator and both kernels, affine RANSAC recovers the synthetic model). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kalwalt
added a commit
that referenced
this pull request
Jul 9, 2026
One-shot artifact rebuild for the integration of the stub/monolith de-duplication (#62-#70) into dev, per the agreed convention (refactor PRs ship source only; artifacts are rebuilt at integration/release points). - dist/jsfeatNext.js (UMD) + dist/jsfeatNext.mjs (ESM) rebuilt from the refactored source - types/ regenerated: now mirrors the new module layout, including types/src/core/core.d.ts and types/src/motion_model/motion_model.d.ts Verified: npm test 57/57; all 18 public modules attach on the fresh UMD bundle; VERSION 0.7.6 intact; no .d.ts files leak into dist/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First de-duplication step of #47 — the proof-of-pattern PR that every subsequent module extraction will follow. Guarded by the 57-test parity suite (#39/#49).
What changed
New
src/core/core.ts— the base class, extractedThe base
jsfeatNextclass (constants, per-instance cache/data-type helpers, static module slots) moves out of the monolith verbatim. Why it must be its own file: modulesextend jsfeatNext(that's the public contract — instance methods likeget_data_type()and static inheritance likejsfeatNext.math.EPSILONdepend on it), and a module can't extend a class defined in the same file that imports the module back — that's a circular ESM import that throws at load time. The one-way flow is now:This is the audit's §4 target architecture starting to materialize (see also #40).
src/math/math.ts— stub replaced by the real implementationThe type-only stub (every method
throw new Error("Method not implemented.")) is gone; the inline implementation from the monolith moved in verbatim (get_gaussian_kernel, deprecatedperspective_4point_transform,qsort,median). Extraction was done mechanically (sedline-range copy), not retyped.One deliberate signature fix:
median()now acceptsInt32Array | Float32Arrayas well asnumber[]—motion_estimator.lmedsalways called it with aFloat32Arraycache buffer at runtime; the old stub hid that behindany. Types only, zero behavior change.src/jsfeatNext.ts— shrinks by ~560 linesImports the base from
core, attachesmathfrom its module. On its way to becoming a thin aggregator.Known-temporary (tightened as #47 proceeds)
The
affine2d/homography2dstatic slots on the base are typedanyuntil those classes are extracted from the monolith in a later PR.Verification (behavior-preserving, proven)
tsc --noEmit→ cleannpm test→ 57/57 parity tests pass{ jsfeatNext }),new jsfeatNext.math() instanceof jsfeatNext→true, static-constant inheritance (jsfeatNext.math.EPSILON === jsfeatNext.EPSILON) →true, gaussian-kernel numerics identicaldist//types/deliberately untouched (rebuilt at release time, per fix(motion_estimator): restore affine2d error() and check_subset() (#51) #52 precedent)Next modules in the queue (same pattern)
imgproc→fast_corners→pyramid_t→linalg→orb→yape06→motion_estimator(+affine2d/homography2d/motion_model) →optical_flow_lkAdvances #47.
🤖 Generated with Claude Code