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
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
- [ ] Closest pair of points solver for geometry toolkit

## Milestone 0.6.0 – Fold Barrier Physics Suite (Planned)
- [ ] Align Fold barrier scope with the paper and define shared constraint interfaces in `src/physics/fold`
- [x] Align Fold barrier scope with the paper and define shared constraint interfaces in `src/physics/fold`
- **Barrier primitives** (each item: runtime module + `docs/index.d.ts` entry + Vitest coverage + runnable example when feasible)
- [ ] Cubic barrier potential (energy, gradient, Hessian evaluation)
- [ ] Stiffness design principle for frozen barrier stiffness
Expand Down
83 changes: 83 additions & 0 deletions docs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ export const examples: {
readonly lz77Compress: 'examples/lz77.ts';
readonly lz77Decompress: 'examples/lz77.ts';
};
readonly physics: {
readonly createFoldConstraintRegistry: 'examples/foldSetup.ts';
};
readonly performance: {
readonly debounce: 'examples/requestDedup.ts';
readonly throttle: 'examples/requestDedup.ts';
Expand Down Expand Up @@ -3285,6 +3288,80 @@ export function bresenhamLine(start: Point, end: Point): Point[];
export interface ClosestPairResult { distance: number; pair: [Point, Point] | null }
export function closestPair(points: ReadonlyArray<Point>): ClosestPairResult;

// ============================================================================
// ⚙️ PHYSICS & FOLD BARRIERS
// ============================================================================

/**
* Registry for Fold constraint factory implementations.
* Use for: hooking cubic/contact/wall barrier evaluators into the solver.
* Import: physics/fold/index.ts
*/
export function createFoldConstraintRegistry(): FoldConstraintRegistry;

export type FoldConstraintType =
| 'cubic-barrier'
| 'contact-barrier'
| 'pin-barrier'
| 'wall-barrier'
| 'strain-barrier'
| 'friction'
| 'assembly'
| 'gap-evaluator';

export interface FoldConstraintState {
gap: number;
maxGap: number;
stiffness: number;
direction: Vector3D;
extendedDirection?: Vector3D;
effectiveMass?: number;
metadata?: Record<string, unknown>;
}

export interface FoldComputationContext {
deltaTime: number;
iteration?: number;
time?: number;
}

export interface FoldConstraintEvaluation {
energy: number;
gradient: Vector3D;
hessian: Matrix3x3;
}

export interface FoldConstraint<TState = FoldConstraintState, TResult = FoldConstraintEvaluation> {
readonly type: FoldConstraintType;
readonly id?: string;
enabled: boolean;
evaluate(state: TState, context: FoldComputationContext): TResult;
}

export interface FoldConstraintFactory<TConfig, TState = FoldConstraintState, TResult = FoldConstraintEvaluation> {
readonly type: FoldConstraintType;
create(config: TConfig): FoldConstraint<TState, TResult>;
}

export interface FoldConstraintRegistry {
register<TConfig>(factory: FoldConstraintFactory<TConfig>): void;
get(type: FoldConstraintType): FoldConstraintFactory<unknown> | undefined;
list(): ReadonlyArray<FoldConstraintFactory<unknown>>;
}

export interface FoldSolverSettings {
maxIterations: number;
tolerance: number;
allowEarlyExit?: boolean;
}

export interface FoldSystemState {
positions: Array<Vector3D>;
velocities: Array<Vector3D>;
constraints: Array<FoldConstraint>;
settings: FoldSolverSettings;
}

/**
* Common easing curves for animation.
* Use for: UI transitions, motion design, data viz.
Expand Down Expand Up @@ -3717,6 +3794,12 @@ export interface Vector3D {
z: number;
}

export type Matrix3x3 = [
[number, number, number],
[number, number, number],
[number, number, number]
];

export interface Rect {
x: number;
y: number;
Expand Down
27 changes: 27 additions & 0 deletions examples/foldSetup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createFoldConstraintRegistry } from '../src/index.js';

const registry = createFoldConstraintRegistry();

registry.register({
type: 'cubic-barrier',
create(config: { stiffness: number }) {
return {
type: 'cubic-barrier',
enabled: true,
evaluate(state) {
const stiffness = config.stiffness;
return {
energy: stiffness * state.gap ** 2,
gradient: { x: 0, y: 0, z: 0 },
hessian: [
[stiffness, 0, 0],
[0, stiffness, 0],
[0, 0, stiffness],
],
};
},
};
},
});

console.log(registry.list().length);
21 changes: 21 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ export const examples = {
lz77Compress: 'examples/lz77.ts',
lz77Decompress: 'examples/lz77.ts',
},
physics: {
createFoldConstraintRegistry: 'examples/foldSetup.ts',
},
performance: {
debounce: 'examples/requestDedup.ts',
throttle: 'examples/requestDedup.ts',
Expand Down Expand Up @@ -1180,6 +1183,24 @@ export { bresenhamLine } from './geometry/bresenham.js';
export { closestPair } from './geometry/closestPair.js';
export type { ClosestPairResult } from './geometry/closestPair.js';

// ============================================================================
// ⚙️ PHYSICS & FOLD BARRIERS
// ============================================================================

export { createFoldConstraintRegistry } from './physics/fold/index.js';

export type {
FoldConstraintType,
FoldConstraintState,
FoldComputationContext,
FoldConstraintEvaluation,
FoldConstraint,
FoldConstraintFactory,
FoldConstraintRegistry,
FoldSolverSettings,
FoldSystemState,
} from './physics/fold/index.js';

// ============================================================================
// 🎨 VISUAL & ANIMATION
// ============================================================================
Expand Down
1 change: 1 addition & 0 deletions src/physics/fold/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types.js';
90 changes: 90 additions & 0 deletions src/physics/fold/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import type { Matrix3x3, Vector3D } from '../../types.js';

export type FoldConstraintType =
| 'cubic-barrier'
| 'contact-barrier'
| 'pin-barrier'
| 'wall-barrier'
| 'strain-barrier'
| 'friction'
| 'assembly'
| 'gap-evaluator';

export interface FoldConstraintState {
/** Signed distance or constraint gap value. */
gap: number;
/** Maximum admissible distance before penalties engage. */
maxGap: number;
/** Effective stiffness (frozen) for this evaluation. */
stiffness: number;
/** Primary constraint direction or normal. */
direction: Vector3D;
/** Optional extended direction used by Fold contact formulations. */
extendedDirection?: Vector3D;
/** Effective mass or mass-like term used for stiffness design. */
effectiveMass?: number;
/** Optional metadata bag for constraint-specific state. */
metadata?: Record<string, unknown>;
}

export interface FoldComputationContext {
/** Current simulation time step. */
deltaTime: number;
/** Iteration index inside the solver/integrator. */
iteration?: number;
/** Optional absolute time for schedule dependent systems. */
time?: number;
}

export interface FoldConstraintEvaluation {
energy: number;
gradient: Vector3D;
hessian: Matrix3x3;
}

export interface FoldConstraint<TState = FoldConstraintState, TResult = FoldConstraintEvaluation> {
readonly type: FoldConstraintType;
readonly id?: string;
enabled: boolean;
evaluate(state: TState, context: FoldComputationContext): TResult;
}

export interface FoldConstraintFactory<TConfig, TState = FoldConstraintState, TResult = FoldConstraintEvaluation> {
readonly type: FoldConstraintType;
create(config: TConfig): FoldConstraint<TState, TResult>;
}

export interface FoldConstraintRegistry {
register<TConfig>(factory: FoldConstraintFactory<TConfig>): void;
get(type: FoldConstraintType): FoldConstraintFactory<unknown> | undefined;
list(): ReadonlyArray<FoldConstraintFactory<unknown>>;
}

export function createFoldConstraintRegistry(): FoldConstraintRegistry {
const factories = new Map<FoldConstraintType, FoldConstraintFactory<unknown>>();

return {
register<TConfig>(factory: FoldConstraintFactory<TConfig>) {
factories.set(factory.type, factory as FoldConstraintFactory<unknown>);
},
get(type: FoldConstraintType) {
return factories.get(type);
},
list() {
return Array.from(factories.values());
},
};
}

export interface FoldSolverSettings {
maxIterations: number;
tolerance: number;
allowEarlyExit?: boolean;
}

export interface FoldSystemState {
positions: Array<Vector3D>;
velocities: Array<Vector3D>;
constraints: Array<FoldConstraint>;
settings: FoldSolverSettings;
}
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ export interface Vector3D {
z: number;
}

export type Matrix3x3 = [
[number, number, number],
[number, number, number],
[number, number, number]
];

export interface Rect {
x: number;
y: number;
Expand Down
72 changes: 72 additions & 0 deletions tests/fold.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { describe, expect, expectTypeOf, it } from 'vitest';

import {
createFoldConstraintRegistry,
type FoldConstraint,
type FoldConstraintEvaluation,
type FoldConstraintState,
type FoldConstraintType,
} from '../src/physics/fold/index.js';

const testConstraint: FoldConstraint = {
type: 'cubic-barrier',
enabled: true,
evaluate(state) {
return {
energy: state.gap ** 2,
gradient: { x: 0, y: 0, z: 0 },
hessian: [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
],
};
},
};

const registry = createFoldConstraintRegistry();
registry.register({
type: 'cubic-barrier',
create: () => testConstraint,
});

describe('Fold constraint registry', () => {
it('registers and retrieves factories', () => {
expect(registry.get('cubic-barrier')).toBeDefined();
expect(registry.list()).toHaveLength(1);
});

it('evaluates through registered constraint', () => {
const factory = registry.get('cubic-barrier');
expect(factory).toBeDefined();
if (!factory) return;

const instance = factory.create({} as unknown);
const evaluation = instance.evaluate(
{
gap: 0.1,
maxGap: 1,
stiffness: 10,
direction: { x: 0, y: 0, z: 1 },
},
{ deltaTime: 1 }
);

expect(evaluation.energy).toBeTypeOf('number');
});

it('provides type safety helpers', () => {
expectTypeOf<FoldConstraintType>().toEqualTypeOf<
| 'cubic-barrier'
| 'contact-barrier'
| 'pin-barrier'
| 'wall-barrier'
| 'strain-barrier'
| 'friction'
| 'assembly'
| 'gap-evaluator'
>();

expectTypeOf<FoldConstraint>().toMatchTypeOf<FoldConstraint<FoldConstraintState, FoldConstraintEvaluation>>();
});
});
6 changes: 6 additions & 0 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ describe('package entry point', () => {
expect(examples.spatial.buildBvh).toBe('examples/bvh.ts');
expect(examples.spatial.queryBvh).toBe('examples/bvh.ts');
expect(examples.spatial.raycastBvh).toBe('examples/bvh.ts');
expect(examples.physics.createFoldConstraintRegistry).toBe('examples/foldSetup.ts');
});

it('provides strong typing for example categories and names', () => {
Expand All @@ -71,6 +72,7 @@ describe('package entry point', () => {
| 'gameplay'
| 'graph'
| 'geometry'
| 'physics'
| 'visual'
>();

Expand Down Expand Up @@ -195,6 +197,10 @@ describe('package entry point', () => {
| 'closestPair'
>();

expectTypeOf<ExampleName<'physics'>>().toEqualTypeOf<
'createFoldConstraintRegistry'
>();

expectTypeOf<ExampleName<'ai'>>().toEqualTypeOf<
| 'seek'
| 'flee'
Expand Down