Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
The full guidance is in **[`AGENTS.md`](../AGENTS.md)** (source of truth) and the roadmap in [`docs/jsfeat-parity-and-refactor-audit.md`](../docs/jsfeat-parity-and-refactor-audit.md). Critical points, inlined because Copilot injects this file directly:

- **TypeScript port of [jsfeat](https://github.com/inspirit/jsfeat)** for WebARKit. npm: `@webarkit/jsfeat-next`. Node v24.18.0 (npm 11).
- Build: `npm run build-ts` → `dist/jsfeatNext.js` (UMD) + `types/`. Watch: `npm run dev-ts`. Format: `npm run format` (Prettier). **Tests:** `npm test` (Vitest, parity vs original jsfeat); also verify via `examples/*.html`.
- **Architecture:** most algorithms live **inline in `src/jsfeatNext.ts` (~3,900 lines)**, attached as `jsfeatNext.X = class X extends jsfeatNext`. Edit *there* for `imgproc, fast_corners, math, linalg, orb, yape06, motion_estimator, optical_flow_lk, pyramid_t`.
- **⚠️ Trap:** several `src/<module>/<module>.ts` files are **type-only stubs** that `throw new Error("Method not implemented.")` (e.g. `src/imgproc/imgproc.ts`). Do not treat them as the implementation and never instantiate them.
- **API notes:** consumers use `jsfeatNext.jsfeatNext` (double namespace); algorithm modules require `new` (instance methods), unlike jsfeat's static namespace; each `new` allocates its own cache. `haar` and `bbf` are not ported.
- **Conventions:** keep numeric/behavioral parity with jsfeat (typed-array/bitwise hot loops); preserve the public `jsfeatNext.<module>` API; avoid `any` in new code; LGPL-3.0-or-later.
- **Git workflow:** open PRs against **`dev`**, never `main` (`dev` = integration, `main` = release). Commit messages follow **Conventional Commits** (`feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `ci:` …).
- Build: `npm run build-ts` (Vite) → `dist/jsfeatNext.js` (UMD) + `dist/jsfeatNext.mjs` (ESM) + `types/`. Watch: `npm run dev-ts`. Format: `npm run format` (Prettier). **Tests:** `npm test` (Vitest, parity vs original jsfeat); also verify via `examples/*.html`.
- **Architecture:** one real module per algorithm under `src/<module>/`, extending the base in `src/core/core.ts`; `src/jsfeatNext.ts` is a thin aggregator.
- **Calling convention (0.9.0+, #41):** algorithm modules are **singletons** — `jsfeatNext.imgproc.grayscale(...)`, **no `new`**. Only the data structures (`matrix_t`, `keypoint_t`, `pyramid_t`, `ransac_params_t`) are constructors. All modules share ONE buffer pool (`jsfeatNext.cache`); balance `get_buffer`/`put_buffer`. See `docs/migration-0.9.md`.
- **Missing vs jsfeat:** `haar` and `bbf` are not ported (#43/#44).
- **Conventions:** keep numeric/behavioral parity with jsfeat (typed-array/bitwise hot loops; the Vitest suite pins outputs against a vendored jsfeat oracle); preserve the public `jsfeatNext.<module>` API; avoid `any` in new code; LGPL-3.0-or-later.
- **Git workflow:** open PRs against **`dev`**, never `main` (`dev` = integration, `main` = release). Commit messages follow **Conventional Commits** (`feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `ci:` …) — the release changelog is generated from them. Release tags are bare `X.Y.Z`.
44 changes: 21 additions & 23 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,31 @@

## What this project is

**jsfeatNext** is a TypeScript port of [jsfeat](https://github.com/inspirit/jsfeat) (a JS computer-vision library) for the **WebARKit** project. Published to npm as `@webarkit/jsfeat-next`. It ships a UMD bundle for browsers and TypeScript type declarations.
**jsfeatNext** is a TypeScript port of [jsfeat](https://github.com/inspirit/jsfeat) (a JS computer-vision library) for the **WebARKit** project. Published to npm as `@webarkit/jsfeat-next`. It ships UMD + ESM bundles and TypeScript type declarations.

## Environment & commands

- **Node:** v24.18.0 (see `.nvmrc`; npm 11). **Package manager:** npm.
- Install: `npm install`
- Build (prod): `npm run build-ts` → runs `tsc` (emits `.d.ts` to `types/`) then webpack → `dist/jsfeatNext.js`
- Install: `npm install` (regenerate the lockfile only with npm 11, e.g. `npx npm@11 install` — older npm writes incomplete cross-platform lockfiles that break CI's `npm ci`)
- Build: `npm run build-ts` → Vite library mode → `dist/jsfeatNext.js` (UMD) + `dist/jsfeatNext.mjs` (ESM) + `types/` (via vite-plugin-dts)
- Watch/dev: `npm run dev-ts`
- Format: `npm run format` (write) · `npm run format-check` (verify) — Prettier, config in `.prettierrc.json`
- **Test:** `npm test` (Vitest) runs characterization tests asserting parity against the original `jsfeat` (see `tests/`). Also verify visually via `examples/*.html` after building. Do **not** claim behavior is verified without a real check.
- Format: `npm run format` (write) · `npm run format-check` (verify) — Prettier, config in `.prettierrc.json`. On Windows, verify with `node_modules/.bin/prettier` directly, not bare `npx prettier` (which can silently resolve a different version).
- API docs: `npm run docs` (TypeDoc → `docs/api/`, gitignored)
- **Test:** `npm test` (Vitest) runs characterization tests asserting parity against the original `jsfeat` (see `tests/`, oracle vendored in `tests/vendor/`). Also verify visually via `examples/*.html` after building. Do **not** claim behavior is verified without a real check.

## Architecture — read this before editing

The runtime is centered on one large file: **`src/jsfeatNext.ts` (~3,900 lines)**.
- **One real module per algorithm** under `src/<module>/<module>.ts`, each extending the base class in **`src/core/core.ts`** (constants, data-type helpers, the shared cache). `src/jsfeatNext.ts` is a thin aggregator that only attaches modules to the namespace; `src/index.ts` default-exports the namespace directly.
- **Calling convention (since 0.9.0, issue #41):** the 14 algorithm modules (`imgproc`, `math`, `matmath`, `linalg`, `transform`, `fast_corners`, `yape`, `yape06`, `orb`, `optical_flow_lk`, `motion_estimator`, `affine2d`, `homography2d`, plus the `cache` pool) are **singleton instances** on the namespace — `jsfeatNext.imgproc.grayscale(...)`, no `new` — matching original jsfeat. The data-structure classes (`matrix_t`, `keypoint_t`, `pyramid_t`, `ransac_params_t`) remain constructors.
- **One shared buffer pool:** all modules borrow scratch buffers from the single `shared_cache` exported by `src/core/core.ts` (public as `jsfeatNext.cache`), exactly like jsfeat's global `jsfeat.cache`. Balance every `get_buffer` with a `put_buffer`.
- Full background: `docs/jsfeat-parity-and-refactor-audit.md` (the plan) and `docs/migration-0.9.md` (the 0.9.0 API break and its motivation).

- `src/index.ts` default-exports `{ jsfeatNext }`. The UMD global is `jsfeatNext`, so **consumers write `jsfeatNext.jsfeatNext`** (double namespace — known wart).
- The base `class jsfeatNext` holds `cache`, `data_type`, and all constants. Algorithm modules are attached as **static members**.
- **Two conflicting patterns coexist:**
- **Implemented INLINE in `src/jsfeatNext.ts`** (attached via `jsfeatNext.X = class X extends jsfeatNext {…}`): `imgproc, fast_corners, pyramid_t, math, linalg, orb, yape06, motion_estimator, optical_flow_lk` (plus `motion_model, affine2d, homography2d`).
- **Assigned from a REAL module file**: `cache, transform, matrix_t, keypoint_t, matmath, yape, ransac_params_t`.
### ⚠️ Gotchas

### ⚠️ Critical gotchas (these will bite you)

1. **Type-only stub files.** Several `src/<module>/<module>.ts` files (confirmed: `src/imgproc/imgproc.ts`) are **stubs** whose every method is `throw new Error("Method not implemented.")`. They exist only to type `static X: typeof X`. **The real code is inline in `src/jsfeatNext.ts`.** → When editing an inline-implemented algorithm, **edit `src/jsfeatNext.ts`**, not the stub. Never instantiate a stub class.
2. **Latent trap:** `src/orb/rectify_patch.ts` imports the *stub* `imgproc`.
3. **Instantiation required.** Unlike jsfeat's static namespace (`jsfeat.imgproc.grayscale()`), jsfeatNext modules are instance classes: `new jsfeatNext.jsfeatNext.imgproc().grayscale(...)`. Not drop-in compatible with jsfeat.
4. **Per-instance cache.** Every `new` of a module runs the base ctor `this.cache.allocate(30, 640*4)` — each instance gets its own buffer pool (jsfeat shares one global cache).
5. **Missing vs jsfeat:** `haar` and `bbf` (object/face detection) are **not ported**.
1. **Don't reintroduce `new jsfeatNext.<algorithm>()`** in examples, docs or tests — the slots hold instances, not classes. The classes still exist in their module files (importable for isolated instances if truly needed) and bind to the shared pool.
2. **Missing vs jsfeat:** `haar` and `bbf` (object/face detection) are **not ported** (#43/#44).
3. **`transform` signature divergence:** jsfeatNext's `transform` methods take `matrix_t`; original jsfeat's (never actually shipped in any jsfeat build) took raw arrays. Same math.
4. **The parity suite is the safety net.** Any change to algorithm code must keep `npm test` green — the tests pin outputs bit-for-bit/close-to against a vendored original-jsfeat oracle.

## Conventions

Expand All @@ -44,17 +41,18 @@ The runtime is centered on one large file: **`src/jsfeatNext.ts` (~3,900 lines)*
## Git & contribution workflow

- **Open PRs against the `dev` branch — never `main`.** `dev` is the integration branch; `main` is stable/release. Branch your work off `dev`.
- **Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/):** `type(scope): summary`, e.g. `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `perf:`, `ci:`. Keep the subject imperative and concise.
- **Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/):** `type(scope): summary`, e.g. `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `perf:`, `ci:`. Keep the subject imperative and concise. The release changelog is generated from these (git-cliff) — non-conforming commits are silently dropped from release notes.
- One feature branch per issue; reference the issue in the PR body.
- Release tags are bare `X.Y.Z` (never `vX.Y.Z`); releases are automated from the tag (see `MAINTAINERS.md`).
- Never commit `.idea/` (JetBrains IDE files).

## Before you make changes

- Small, incremental, reviewable diffs. Match surrounding code style.
- If a change could alter algorithm output, note that there is **no test net** and flag it for manual example verification.
- There is a full audit + refactoring roadmap in **[`docs/jsfeat-parity-and-refactor-audit.md`](docs/jsfeat-parity-and-refactor-audit.md)** — read it before proposing structural refactors, the webpack→Vite migration, or porting `haar`/`bbf`.
- Keep `npm test` green; add parity tests for new algorithm code.
- The audit + roadmap lives in **[`docs/jsfeat-parity-and-refactor-audit.md`](docs/jsfeat-parity-and-refactor-audit.md)**; the release runbook in **[`MAINTAINERS.md`](MAINTAINERS.md)**.

## Roadmap pointers

- **Refactor direction:** de-duplicate stub/monolith → one real class per module, `jsfeatNext.ts` becomes a thin aggregator, shared singleton cache, static-facade API for jsfeat parity. (See audit doc §4.)
- **Build direction:** webpack → **Vite library mode** + `vite-plugin-dts`; ship ESM+UMD; fix the double namespace. (See audit doc §5.)
- **Remaining vs jsfeat:** port `haar` (#43) and `bbf` (#44); exhaustive per-symbol parity audit (#45).
- **Toward 1.0:** prerelease-tag support in the release pipeline (#81); examples modernization (#79); new descriptors like FREAK (#80).
7 changes: 4 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ The canonical, tool-agnostic guidance for this repo lives in **AGENTS.md**. It i

## Claude-specific notes

- When editing an algorithm module (`imgproc`, `fast_corners`, `math`, `linalg`, `orb`, `yape06`, `motion_estimator`, `optical_flow_lk`, `pyramid_t`), remember the implementation is **inline in `src/jsfeatNext.ts`**, not in the same-named `src/<module>/<module>.ts` stub. Grep `jsfeatNext.<module> = class` to jump to it.
- There are **no automated tests**. Do not report a change as "verified" unless you actually built (`npm run build-ts`) and checked the relevant `examples/*.html`, or the user confirms.
- The audit/roadmap is in [`docs/jsfeat-parity-and-refactor-audit.md`](docs/jsfeat-parity-and-refactor-audit.md). Consult it before structural refactors, the Vite migration, or porting `haar`/`bbf`.
- Every algorithm lives in its own module under `src/<module>/<module>.ts`, extending the base in `src/core/core.ts`; `src/jsfeatNext.ts` is only the aggregator that attaches the singletons (the old inline monolith and its type-only stubs are gone since #47).
- **Always run `npm test`** (Vitest parity suite vs a vendored original-jsfeat oracle) before claiming an algorithm change is verified; for consumer-facing changes also build (`npm run build-ts`) and check the relevant `examples/*.html`, or ask the user to confirm.
- On this Windows machine: verify formatting with `node_modules/.bin/prettier` directly (bare `npx prettier` can resolve the wrong version) and regenerate `package-lock.json` only with `npx npm@11`.
- The audit/roadmap is in [`docs/jsfeat-parity-and-refactor-audit.md`](docs/jsfeat-parity-and-refactor-audit.md); the 0.9.0 API break is documented in [`docs/migration-0.9.md`](docs/migration-0.9.md); the release runbook is in [`MAINTAINERS.md`](MAINTAINERS.md).
- Keep numeric/behavioral parity with the original jsfeat; preserve the public `jsfeatNext.<module>` API.
23 changes: 10 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,26 @@ npm install @webarkit/jsfeat-next
```

```js
import pkg from "@webarkit/jsfeat-next";
import jsfeatNext from "@webarkit/jsfeat-next";

// consumers unwrap the double namespace: pkg.jsfeatNext, not pkg directly
// (a known wart, tracked for a future breaking-change cleanup — see #41)
const jsfeatNext = pkg.jsfeatNext;

const imgproc = new jsfeatNext.imgproc();
// algorithm modules are singletons — call them directly, no `new` (since 0.9.0)
const src = new jsfeatNext.matrix_t(width, height, jsfeatNext.U8_t | jsfeatNext.C1_t);
imgproc.grayscale(rgbaPixelData, width, height, src);
jsfeatNext.imgproc.grayscale(rgbaPixelData, width, height, src);
```

In the browser (UMD build), the same shape applies via the global:
In the browser (UMD build), the global is the namespace directly:

```html
<script src="dist/jsfeatNext.js"></script>
<script>
// note: reuse a *different* variable name — `var jsfeatNext = jsfeatNext.jsfeatNext`
// would shadow the global with itself and break.
var jsfeat = jsfeatNext.jsfeatNext;
var imgproc = new jsfeat.imgproc();
jsfeatNext.imgproc.grayscale(rgbaPixelData, width, height, src);
</script>
```

> **Upgrading from ≤ 0.8.x?** The `jsfeatNext.jsfeatNext` double namespace and the
> `new jsfeatNext.imgproc()` calling convention were removed in 0.9.0 — see the
> [migration guide](docs/migration-0.9.md).

## List of features ✨

- TypeScript definitions, with full TSDoc on every public class/method (`npm run docs` to generate a browsable API reference locally)
Expand Down Expand Up @@ -73,7 +70,7 @@ npm install @webarkit/jsfeat-next
## Known limitations 🔍

- Not every original jsfeat class is ported yet — `haar` and `bbf` (Haar-cascade / BBF object detection) are not implemented. Tracked in [#43](https://github.com/webarkit/jsfeatNext/issues/43) and [#44](https://github.com/webarkit/jsfeatNext/issues/44).
- jsfeatNext is **not a drop-in replacement** for jsfeat: algorithm modules are instantiated (`new jsfeatNext.imgproc()`) rather than called as static namespace functions, and consumers must unwrap the `jsfeatNext.jsfeatNext` double namespace. Tracked in [#41](https://github.com/webarkit/jsfeatNext/issues/41).
- The `transform` module takes `matrix_t` arguments where original jsfeat's (never-shipped) `transform` module used raw arrays — same math, slightly different calling convention (see the parity audit, Axis 2).

## Examples 🧪

Expand Down
2 changes: 1 addition & 1 deletion dist/jsfeatNext.js

Large diffs are not rendered by default.

Loading