@stroke-stabilizer/core 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +397 -0
  2. package/dist/StabilizedPointer.d.ts +246 -0
  3. package/dist/StabilizedPointer.d.ts.map +1 -0
  4. package/dist/filters/EmaFilter.d.ts +25 -0
  5. package/dist/filters/EmaFilter.d.ts.map +1 -0
  6. package/dist/filters/KalmanFilter.d.ts +21 -0
  7. package/dist/filters/KalmanFilter.d.ts.map +1 -0
  8. package/dist/filters/LinearPredictionFilter.d.ts +54 -0
  9. package/dist/filters/LinearPredictionFilter.d.ts.map +1 -0
  10. package/dist/filters/MovingAverageFilter.d.ts +16 -0
  11. package/dist/filters/MovingAverageFilter.d.ts.map +1 -0
  12. package/dist/filters/NoiseFilter.d.ts +16 -0
  13. package/dist/filters/NoiseFilter.d.ts.map +1 -0
  14. package/dist/filters/OneEuroFilter.d.ts +45 -0
  15. package/dist/filters/OneEuroFilter.d.ts.map +1 -0
  16. package/dist/filters/StringFilter.d.ts +16 -0
  17. package/dist/filters/StringFilter.d.ts.map +1 -0
  18. package/dist/filters/index.d.ts +15 -0
  19. package/dist/filters/index.d.ts.map +1 -0
  20. package/dist/index.cjs +1086 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.ts +11 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +1086 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/kernels/BilateralKernel.d.ts +48 -0
  27. package/dist/kernels/BilateralKernel.d.ts.map +1 -0
  28. package/dist/kernels/boxKernel.d.ts +16 -0
  29. package/dist/kernels/boxKernel.d.ts.map +1 -0
  30. package/dist/kernels/gaussianKernel.d.ts +17 -0
  31. package/dist/kernels/gaussianKernel.d.ts.map +1 -0
  32. package/dist/kernels/index.d.ts +11 -0
  33. package/dist/kernels/index.d.ts.map +1 -0
  34. package/dist/kernels/triangleKernel.d.ts +16 -0
  35. package/dist/kernels/triangleKernel.d.ts.map +1 -0
  36. package/dist/kernels/types.d.ts +38 -0
  37. package/dist/kernels/types.d.ts.map +1 -0
  38. package/dist/presets.d.ts +36 -0
  39. package/dist/presets.d.ts.map +1 -0
  40. package/dist/smooth.d.ts +27 -0
  41. package/dist/smooth.d.ts.map +1 -0
  42. package/dist/types.d.ts +33 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,397 @@
1
+ # @stroke-stabilizer/core
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@stroke-stabilizer/core.svg)](https://www.npmjs.com/package/@stroke-stabilizer/core)
4
+
5
+ [日本語](./docs/README.ja.md)
6
+
7
+ > This is part of the [stroke-stabilizer](https://github.com/usapopopooon/stroke-stabilizer) monorepo
8
+
9
+ A lightweight, framework-agnostic stroke stabilization library for digital drawing applications.
10
+
11
+ Reduce hand tremor and smooth pen/mouse input in real-time using a flexible filter pipeline.
12
+
13
+ ## Features
14
+
15
+ - **[Dynamic Pipeline Pattern](https://dev.to/usapopopooon/the-dynamic-pipeline-pattern-a-mutable-method-chaining-for-real-time-processing-16e1)** - Add, remove, and update filters at runtime without rebuilding
16
+ - **Two-layer Processing** - Real-time filters + post-processing convolution
17
+ - **rAF Batch Processing** - Coalesce high-frequency pointer events into animation frames
18
+ - **8 Built-in Filters** - From simple moving average to adaptive One Euro Filter
19
+ - **Edge-preserving Smoothing** - Bilateral kernel for sharp corner preservation
20
+ - **TypeScript First** - Full type safety with exported types
21
+ - **Zero Dependencies** - Pure JavaScript, works anywhere
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install @stroke-stabilizer/core
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```ts
32
+ import {
33
+ StabilizedPointer,
34
+ emaFilter,
35
+ oneEuroFilter,
36
+ } from '@stroke-stabilizer/core'
37
+
38
+ const pointer = new StabilizedPointer()
39
+ .addFilter(emaFilter({ alpha: 0.5 }))
40
+ .addFilter(oneEuroFilter({ minCutoff: 1.0, beta: 0.007 }))
41
+
42
+ canvas.addEventListener('pointermove', (e) => {
43
+ const result = pointer.process({
44
+ x: e.clientX,
45
+ y: e.clientY,
46
+ pressure: e.pressure,
47
+ timestamp: e.timeStamp,
48
+ })
49
+
50
+ if (result) {
51
+ draw(result.x, result.y)
52
+ }
53
+ })
54
+
55
+ canvas.addEventListener('pointerup', () => {
56
+ pointer.reset()
57
+ })
58
+ ```
59
+
60
+ ## Filters
61
+
62
+ > **📖 [Detailed Filter Reference](../../docs/filters.md)** - Mathematical formulas, technical explanations, and usage recommendations
63
+
64
+ ### Real-time Filters
65
+
66
+ | Filter | Description | Use Case |
67
+ | ------------------------ | --------------------------------- | ---------------------------------- |
68
+ | `noiseFilter` | Rejects points too close together | Remove jitter |
69
+ | `movingAverageFilter` | Simple moving average (FIR) | Basic smoothing |
70
+ | `emaFilter` | Exponential moving average (IIR) | Low-latency smoothing |
71
+ | `kalmanFilter` | Kalman filter | Noisy input smoothing |
72
+ | `stringFilter` | Lazy Brush algorithm | Delayed, smooth strokes |
73
+ | `oneEuroFilter` | Adaptive lowpass filter | Best balance of smoothness/latency |
74
+ | `linearPredictionFilter` | Predicts next position | Lag compensation |
75
+
76
+ ### Post-processing Kernels
77
+
78
+ | Kernel | Description |
79
+ | ----------------- | ------------------------- |
80
+ | `gaussianKernel` | Gaussian blur |
81
+ | `boxKernel` | Simple average |
82
+ | `triangleKernel` | Linear falloff |
83
+ | `bilateralKernel` | Edge-preserving smoothing |
84
+
85
+ ## Usage Examples
86
+
87
+ ### Basic Real-time Stabilization
88
+
89
+ ```ts
90
+ import { StabilizedPointer, oneEuroFilter } from '@stroke-stabilizer/core'
91
+
92
+ const pointer = new StabilizedPointer().addFilter(
93
+ oneEuroFilter({ minCutoff: 1.0, beta: 0.007 })
94
+ )
95
+
96
+ // Process each point
97
+ const smoothed = pointer.process({ x, y, timestamp })
98
+ ```
99
+
100
+ ### Dynamic Filter Updates
101
+
102
+ ```ts
103
+ // Add filter
104
+ pointer.addFilter(emaFilter({ alpha: 0.3 }))
105
+
106
+ // Update parameters at runtime
107
+ pointer.updateFilter('ema', { alpha: 0.5 })
108
+
109
+ // Remove filter
110
+ pointer.removeFilter('ema')
111
+ ```
112
+
113
+ ### Post-processing with Bidirectional Convolution
114
+
115
+ ```ts
116
+ import { StabilizedPointer, gaussianKernel } from '@stroke-stabilizer/core'
117
+
118
+ const pointer = new StabilizedPointer()
119
+ .addFilter(oneEuroFilter({ minCutoff: 1.0, beta: 0.007 }))
120
+ .addPostProcess(gaussianKernel({ size: 7 }), { padding: 'reflect' })
121
+
122
+ // Process points in real-time
123
+ pointer.process(point)
124
+
125
+ // After stroke ends, apply post-processing
126
+ const finalPoints = pointer.finish()
127
+ ```
128
+
129
+ ### Re-applying Post-processing
130
+
131
+ Use `finishWithoutReset()` to preview or re-apply post-processing with different settings without losing the buffer.
132
+
133
+ ```ts
134
+ import {
135
+ StabilizedPointer,
136
+ gaussianKernel,
137
+ bilateralKernel,
138
+ } from '@stroke-stabilizer/core'
139
+
140
+ const pointer = new StabilizedPointer()
141
+
142
+ // Process points
143
+ pointer.process(point1)
144
+ pointer.process(point2)
145
+ pointer.process(point3)
146
+
147
+ // Preview with gaussian kernel
148
+ pointer.addPostProcess(gaussianKernel({ size: 5 }))
149
+ const preview1 = pointer.finishWithoutReset()
150
+ draw(preview1)
151
+
152
+ // Change to bilateral kernel and re-apply
153
+ pointer.removePostProcess('gaussian')
154
+ pointer.addPostProcess(bilateralKernel({ size: 7, sigmaValue: 10 }))
155
+ const preview2 = pointer.finishWithoutReset()
156
+ draw(preview2)
157
+
158
+ // Finalize when satisfied (resets buffer)
159
+ const final = pointer.finish()
160
+ ```
161
+
162
+ **Difference between `finishWithoutReset()` and `finish()`:**
163
+
164
+ | Method | Post-process | Reset buffer |
165
+ | ---------------------- | ------------ | ------------ |
166
+ | `finishWithoutReset()` | ✅ | ❌ |
167
+ | `finish()` | ✅ | ✅ |
168
+
169
+ ### Edge-preserving Smoothing
170
+
171
+ ```ts
172
+ import { smooth, bilateralKernel } from '@stroke-stabilizer/core'
173
+
174
+ // Smooth while preserving sharp corners
175
+ const smoothed = smooth(points, {
176
+ kernel: bilateralKernel({ size: 7, sigmaValue: 10 }),
177
+ padding: 'reflect',
178
+ })
179
+ ```
180
+
181
+ ### Endpoint Preservation
182
+
183
+ By default, `smooth()` preserves exact start and end points so the stroke reaches the actual pointer position.
184
+
185
+ ```ts
186
+ import { smooth, gaussianKernel } from '@stroke-stabilizer/core'
187
+
188
+ // Default: endpoints preserved (recommended)
189
+ const smoothed = smooth(points, {
190
+ kernel: gaussianKernel({ size: 5 }),
191
+ })
192
+
193
+ // Disable endpoint preservation
194
+ const smoothedAll = smooth(points, {
195
+ kernel: gaussianKernel({ size: 5 }),
196
+ preserveEndpoints: false,
197
+ })
198
+ ```
199
+
200
+ ### rAF Batch Processing
201
+
202
+ For high-frequency input devices (pen tablets, etc.), batch processing reduces CPU load by coalescing pointer events into animation frames.
203
+
204
+ ```ts
205
+ import { StabilizedPointer, oneEuroFilter } from '@stroke-stabilizer/core'
206
+
207
+ const pointer = new StabilizedPointer()
208
+ .addFilter(oneEuroFilter({ minCutoff: 1.0, beta: 0.007 }))
209
+ .enableBatching({
210
+ onBatch: (points) => {
211
+ // Called once per frame with all processed points
212
+ drawPoints(points)
213
+ },
214
+ onPoint: (point) => {
215
+ // Called for each processed point (optional)
216
+ updatePreview(point)
217
+ },
218
+ })
219
+
220
+ canvas.addEventListener('pointermove', (e) => {
221
+ // Points are queued and processed on next animation frame
222
+ pointer.queue({
223
+ x: e.clientX,
224
+ y: e.clientY,
225
+ pressure: e.pressure,
226
+ timestamp: e.timeStamp,
227
+ })
228
+ })
229
+
230
+ canvas.addEventListener('pointerup', () => {
231
+ // Flush any pending points and apply post-processing
232
+ const finalPoints = pointer.finish()
233
+ })
234
+ ```
235
+
236
+ **Batch processing methods:**
237
+
238
+ ```ts
239
+ // Enable/disable batching (method chaining)
240
+ pointer.enableBatching({ onBatch, onPoint })
241
+ pointer.disableBatching()
242
+
243
+ // Queue points for batch processing
244
+ pointer.queue(point)
245
+ pointer.queueAll(points)
246
+
247
+ // Force immediate processing
248
+ pointer.flushBatch()
249
+
250
+ // Check state
251
+ pointer.isBatchingEnabled // boolean
252
+ pointer.pendingCount // number of queued points
253
+ ```
254
+
255
+ ### Presets
256
+
257
+ ```ts
258
+ import { createFromPreset } from '@stroke-stabilizer/core'
259
+
260
+ // Quick setup with predefined configurations
261
+ const pointer = createFromPreset('smooth') // Heavy smoothing
262
+ const pointer = createFromPreset('responsive') // Low latency
263
+ const pointer = createFromPreset('balanced') // Default balance
264
+ ```
265
+
266
+ ## Filter Parameters
267
+
268
+ ### oneEuroFilter (Recommended)
269
+
270
+ ```ts
271
+ oneEuroFilter({
272
+ minCutoff: 1.0, // Smoothing at low speed (lower = smoother)
273
+ beta: 0.007, // Speed adaptation (higher = more responsive)
274
+ dCutoff: 1.0, // Derivative cutoff (usually 1.0)
275
+ })
276
+ ```
277
+
278
+ ### emaFilter
279
+
280
+ ```ts
281
+ emaFilter({
282
+ alpha: 0.5, // 0-1, higher = more responsive
283
+ })
284
+ ```
285
+
286
+ ### kalmanFilter
287
+
288
+ ```ts
289
+ kalmanFilter({
290
+ processNoise: 0.1, // Expected movement variance
291
+ measurementNoise: 0.5, // Input noise level
292
+ })
293
+ ```
294
+
295
+ ### linearPredictionFilter
296
+
297
+ ```ts
298
+ linearPredictionFilter({
299
+ historySize: 4, // Points used for prediction
300
+ predictionFactor: 0.5, // Prediction strength (0-1)
301
+ smoothing: 0.6, // Output smoothing
302
+ })
303
+ ```
304
+
305
+ ### stringFilter (Lazy Brush)
306
+
307
+ ```ts
308
+ stringFilter({
309
+ stringLength: 10, // Distance before anchor moves
310
+ })
311
+ ```
312
+
313
+ ### bilateralKernel
314
+
315
+ ```ts
316
+ bilateralKernel({
317
+ size: 7, // Kernel size (odd number)
318
+ sigmaValue: 10, // Edge preservation (lower = sharper edges)
319
+ sigmaSpace: 2, // Spatial falloff (optional)
320
+ })
321
+ ```
322
+
323
+ ## API Reference
324
+
325
+ ### StabilizedPointer
326
+
327
+ ```ts
328
+ class StabilizedPointer {
329
+ // Filter management
330
+ addFilter(filter: Filter): this
331
+ removeFilter(type: string): boolean
332
+ updateFilter<T>(type: string, params: Partial<T>): boolean
333
+ getFilter(type: string): Filter | undefined
334
+
335
+ // Post-processing
336
+ addPostProcess(kernel: Kernel, options?: { padding?: PaddingMode }): this
337
+ removePostProcess(type: string): boolean
338
+
339
+ // Processing
340
+ process(point: PointerPoint): PointerPoint | null
341
+ finish(): Point[] // Apply post-process and reset
342
+ finishWithoutReset(): Point[] // Apply post-process without reset (for preview)
343
+ reset(): void // Reset filters and clear buffer
344
+
345
+ // Batch processing (rAF)
346
+ enableBatching(config?: BatchConfig): this
347
+ disableBatching(): this
348
+ queue(point: PointerPoint): this
349
+ queueAll(points: PointerPoint[]): this
350
+ flushBatch(): PointerPoint[]
351
+ isBatchingEnabled: boolean
352
+ pendingCount: number
353
+ }
354
+ ```
355
+
356
+ ### Types
357
+
358
+ ```ts
359
+ interface Point {
360
+ x: number
361
+ y: number
362
+ }
363
+
364
+ interface PointerPoint extends Point {
365
+ pressure?: number
366
+ timestamp: number
367
+ }
368
+
369
+ type PaddingMode = 'reflect' | 'edge' | 'zero'
370
+
371
+ interface BatchConfig {
372
+ onBatch?: (points: PointerPoint[]) => void
373
+ onPoint?: (point: PointerPoint) => void
374
+ }
375
+ ```
376
+
377
+ ## Architecture
378
+
379
+ ```
380
+ Input → [Real-time Filters] → process() → Output
381
+
382
+ [Buffer]
383
+
384
+ [Post-processors] → finish() → Final Output
385
+ ```
386
+
387
+ **Real-time filters** run on each input point with O(1) complexity.
388
+ **Post-processors** run once at stroke end with bidirectional convolution.
389
+
390
+ ## Framework Adapters
391
+
392
+ - `@stroke-stabilizer/react` - React hooks
393
+ - `@stroke-stabilizer/vue` - Vue composables
394
+
395
+ ## License
396
+
397
+ [MIT](../../LICENSE)
@@ -0,0 +1,246 @@
1
+ import type { Filter, Point, PointerPoint } from './types';
2
+ import type { Kernel, PaddingMode } from './kernels/types';
3
+ /**
4
+ * Batch processing configuration
5
+ */
6
+ export interface BatchConfig {
7
+ /** Callback when a batch of points is processed */
8
+ onBatch?: (points: PointerPoint[]) => void;
9
+ /** Callback for each processed point */
10
+ onPoint?: (point: PointerPoint) => void;
11
+ }
12
+ /**
13
+ * Dynamic Pipeline Pattern implementation
14
+ *
15
+ * A pipeline that allows adding, removing, and updating filters at runtime.
16
+ * Always ready to execute without requiring a .build() call.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const pointer = new StabilizedPointer()
21
+ * // Real-time layer
22
+ * .addFilter(noiseFilter({ minDistance: 2 }))
23
+ * .addFilter(kalmanFilter({ processNoise: 0.1 }))
24
+ * // Post-processing layer
25
+ * .addPostProcess(gaussianKernel({ size: 7 }))
26
+ *
27
+ * // During drawing
28
+ * pointer.process(point)
29
+ *
30
+ * // On completion
31
+ * const finalStroke = pointer.finish()
32
+ * ```
33
+ */
34
+ export declare class StabilizedPointer {
35
+ private filters;
36
+ private postProcessors;
37
+ private buffer;
38
+ private lastRawPoint;
39
+ private batchConfig;
40
+ private pendingPoints;
41
+ private rafId;
42
+ /**
43
+ * Add a filter to the pipeline
44
+ * @returns this (for method chaining)
45
+ */
46
+ addFilter(filter: Filter): this;
47
+ /**
48
+ * Remove a filter by type
49
+ * @returns true if removed, false if not found
50
+ */
51
+ removeFilter(type: string): boolean;
52
+ /**
53
+ * Update parameters of a filter by type
54
+ * @returns true if updated, false if not found
55
+ */
56
+ updateFilter<TParams>(type: string, params: Partial<TParams>): boolean;
57
+ /**
58
+ * Get a filter by type
59
+ */
60
+ getFilter(type: string): Filter | undefined;
61
+ /**
62
+ * Get list of current filter types
63
+ */
64
+ getFilterTypes(): string[];
65
+ /**
66
+ * Check if a filter exists
67
+ */
68
+ hasFilter(type: string): boolean;
69
+ /**
70
+ * Process a point through the pipeline
71
+ * @returns Processed result (null if rejected by a filter)
72
+ */
73
+ process(point: PointerPoint): PointerPoint | null;
74
+ /**
75
+ * Process multiple points at once
76
+ * @returns Array of processed results (nulls excluded)
77
+ */
78
+ processAll(points: PointerPoint[]): PointerPoint[];
79
+ /**
80
+ * Get the processed buffer
81
+ */
82
+ getBuffer(): readonly PointerPoint[];
83
+ /**
84
+ * Clear and return the processed buffer
85
+ */
86
+ flushBuffer(): PointerPoint[];
87
+ /**
88
+ * Reset all filters and clear the buffer
89
+ *
90
+ * Call this to prepare for a new stroke without destroying the pipeline configuration.
91
+ * Filters are reset to their initial state and the buffer is cleared.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * // After finishing a stroke
96
+ * const result = pointer.finish() // automatically calls reset()
97
+ *
98
+ * // Or manually reset without finishing
99
+ * pointer.reset()
100
+ * ```
101
+ */
102
+ reset(): void;
103
+ /**
104
+ * Clear the pipeline (remove all filters)
105
+ */
106
+ clear(): void;
107
+ /**
108
+ * Get number of filters
109
+ */
110
+ get length(): number;
111
+ /**
112
+ * Add post-processor to the pipeline
113
+ * @returns this (for method chaining)
114
+ */
115
+ addPostProcess(kernel: Kernel, options?: {
116
+ padding?: PaddingMode;
117
+ }): this;
118
+ /**
119
+ * Remove a post-processor by type
120
+ * @returns true if removed, false if not found
121
+ */
122
+ removePostProcess(type: string): boolean;
123
+ /**
124
+ * Check if a post-processor exists
125
+ */
126
+ hasPostProcess(type: string): boolean;
127
+ /**
128
+ * Get list of post-processor types
129
+ */
130
+ getPostProcessTypes(): string[];
131
+ /**
132
+ * Get number of post-processors
133
+ */
134
+ get postProcessLength(): number;
135
+ /**
136
+ * Finish the stroke and return post-processed results, without resetting
137
+ *
138
+ * Use this when you want to get the final result but keep the buffer intact.
139
+ * Useful for previewing post-processing results or comparing different settings.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * pointer.addPostProcess(gaussianKernel({ size: 5 }))
144
+ * const preview1 = pointer.finishWithoutReset()
145
+ *
146
+ * // Change settings and re-apply
147
+ * pointer.removePostProcess('gaussian')
148
+ * pointer.addPostProcess(bilateralKernel({ size: 7, sigmaValue: 10 }))
149
+ * const preview2 = pointer.finishWithoutReset()
150
+ *
151
+ * // Finalize when done
152
+ * const final = pointer.finish()
153
+ * ```
154
+ */
155
+ finishWithoutReset(): Point[];
156
+ /**
157
+ * Track if drain has been called to avoid duplicate draining
158
+ */
159
+ private hasDrained;
160
+ /**
161
+ * Append the final raw input point to ensure stroke ends at the actual endpoint
162
+ *
163
+ * Instead of draining filters (which adds many extra points),
164
+ * we simply append the raw endpoint. The post-processing phase
165
+ * with bidirectional convolution will naturally smooth the transition.
166
+ */
167
+ private appendEndpoint;
168
+ /**
169
+ * Finish the stroke and return post-processed results
170
+ *
171
+ * This applies all post-processors to the buffer, then resets filters and clears the buffer.
172
+ * Use this at the end of a stroke to get the final smoothed result.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * // During drawing
177
+ * pointer.process(point)
178
+ *
179
+ * // On stroke end
180
+ * const finalStroke = pointer.finish()
181
+ * ```
182
+ */
183
+ finish(): Point[];
184
+ /**
185
+ * Enable requestAnimationFrame batch processing
186
+ *
187
+ * When enabled, points queued via queue() are batched and processed
188
+ * on the next animation frame, reducing CPU load for high-frequency
189
+ * pointer events.
190
+ *
191
+ * @returns this (for method chaining)
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * const pointer = new StabilizedPointer()
196
+ * .addFilter(noiseFilter({ minDistance: 2 }))
197
+ * .enableBatching({
198
+ * onBatch: (points) => drawPoints(points),
199
+ * onPoint: (point) => updatePreview(point)
200
+ * })
201
+ *
202
+ * canvas.onpointermove = (e) => {
203
+ * pointer.queue({ x: e.clientX, y: e.clientY, timestamp: e.timeStamp })
204
+ * }
205
+ * ```
206
+ */
207
+ enableBatching(config?: BatchConfig): this;
208
+ /**
209
+ * Disable batch processing
210
+ * Flushes any pending points before disabling
211
+ * @returns this (for method chaining)
212
+ */
213
+ disableBatching(): this;
214
+ /**
215
+ * Check if batch processing is enabled
216
+ */
217
+ get isBatchingEnabled(): boolean;
218
+ /**
219
+ * Queue a point for batch processing
220
+ *
221
+ * If batching is enabled, the point is queued and processed on the next
222
+ * animation frame. If batching is disabled, the point is processed immediately.
223
+ *
224
+ * @returns this (for method chaining, useful for queueing multiple points)
225
+ */
226
+ queue(point: PointerPoint): this;
227
+ /**
228
+ * Queue multiple points for batch processing
229
+ * @returns this (for method chaining)
230
+ */
231
+ queueAll(points: PointerPoint[]): this;
232
+ /**
233
+ * Force flush pending batched points immediately
234
+ * @returns Array of processed points
235
+ */
236
+ flushBatch(): PointerPoint[];
237
+ /**
238
+ * Get number of pending points in the batch queue
239
+ */
240
+ get pendingCount(): number;
241
+ private scheduleFlush;
242
+ private cancelScheduledFlush;
243
+ private processPendingPoints;
244
+ private isUpdatableFilter;
245
+ }
246
+ //# sourceMappingURL=StabilizedPointer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StabilizedPointer.d.ts","sourceRoot":"","sources":["../src/StabilizedPointer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Filter } from '../types';
2
+ export interface EmaFilterParams {
3
+ /**
4
+ * Smoothing coefficient (0-1)
5
+ * - Lower value: stronger smoothing (emphasizes past values)
6
+ * - Higher value: more responsive (emphasizes new values)
7
+ */
8
+ alpha: number;
9
+ }
10
+ /**
11
+ * Create an Exponential Moving Average (EMA) filter
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * // Strong smoothing
16
+ * const pointer = new StabilizedPointer()
17
+ * .addFilter(emaFilter({ alpha: 0.2 }))
18
+ *
19
+ * // Light smoothing
20
+ * const pointer = new StabilizedPointer()
21
+ * .addFilter(emaFilter({ alpha: 0.7 }))
22
+ * ```
23
+ */
24
+ export declare function emaFilter(params: EmaFilterParams): Filter;
25
+ //# sourceMappingURL=EmaFilter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmaFilter.d.ts","sourceRoot":"","sources":["../../src/filters/EmaFilter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,UAAU,CAAA;AAErE,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;CACd;AA6DD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,21 @@
1
+ import type { Filter } from '../types';
2
+ export interface KalmanFilterParams {
3
+ /** Process noise (Q): Lower values trust prediction more */
4
+ processNoise: number;
5
+ /** Measurement noise (R): Higher values result in stronger smoothing */
6
+ measurementNoise: number;
7
+ }
8
+ /**
9
+ * Create a Kalman filter
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const pointer = new StabilizedPointer()
14
+ * .addFilter(kalmanFilter({
15
+ * processNoise: 0.1,
16
+ * measurementNoise: 0.5
17
+ * }))
18
+ * ```
19
+ */
20
+ export declare function kalmanFilter(params: KalmanFilterParams): Filter;
21
+ //# sourceMappingURL=KalmanFilter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KalmanFilter.d.ts","sourceRoot":"","sources":["../../src/filters/KalmanFilter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,UAAU,CAAA;AAErE,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,gBAAgB,EAAE,MAAM,CAAA;CACzB;AA4ED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAE/D"}