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
6 changes: 3 additions & 3 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@
- [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)
- [x] Cubic barrier potential (energy, gradient, Hessian evaluation)
- [ ] Stiffness design principle for frozen barrier stiffness
- [ ] Contact barrier with extended direction handling
- [ ] Pin constraint barrier using cubic barrier formulation
- [x] Stiffness design principle for frozen barrier stiffness
- [x] Contact barrier with extended direction handling
- [x] Pin constraint barrier using cubic barrier formulation
- [ ] Wall constraint barrier for plane collisions
- [ ] Triangle strain-limiting barrier driven by deformation singular values
- **Integrator and solver**
Expand Down
14 changes: 14 additions & 0 deletions docs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export const examples: {
readonly createCubicBarrier: 'examples/foldCubicBarrier.ts';
readonly computeFrozenStiffness: 'examples/foldStiffness.ts';
readonly createContactBarrier: 'examples/foldContactBarrier.ts';
readonly createPinBarrier: 'examples/foldPinBarrier.ts';
};
readonly performance: {
readonly debounce: 'examples/requestDedup.ts';
Expand Down Expand Up @@ -3347,6 +3348,19 @@ export interface ContactBarrierOptions {
}
export function createContactBarrier(options?: ContactBarrierOptions): FoldConstraint;

/**
* Pin constraint barrier using cubic barrier formulation.
* Use for: soft positional pinning with Fold barrier guarantees.
* Import: physics/fold/pinBarrier.ts
*/
export interface PinBarrierOptions {
id?: string;
stiffnessOverride?: number;
maxGap?: number;
direction?: Vector3D;
}
export function createPinBarrier(options?: PinBarrierOptions): FoldConstraint;

export type FoldConstraintType =
| 'cubic-barrier'
| 'contact-barrier'
Expand Down
23 changes: 23 additions & 0 deletions examples/foldPinBarrier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createPinBarrier } from '../src/index.js';

const barrier = createPinBarrier();

const evaluation = barrier.evaluate(
{
gap: -0.015,
maxGap: 0,
stiffness: 0,
direction: { x: 1, y: 0, z: 0 },
effectiveMass: 0.3,
metadata: {
hessian: [
[2, 0, 0],
[0, 2, 0],
[0, 0, 2],
],
},
},
{ deltaTime: 1 / 60 }
);

console.log('pin energy', evaluation.energy);
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export const examples = {
createCubicBarrier: 'examples/foldCubicBarrier.ts',
computeFrozenStiffness: 'examples/foldStiffness.ts',
createContactBarrier: 'examples/foldContactBarrier.ts',
createPinBarrier: 'examples/foldPinBarrier.ts',
},
performance: {
debounce: 'examples/requestDedup.ts',
Expand Down Expand Up @@ -1195,6 +1196,7 @@ export {
createCubicBarrier,
computeFrozenStiffness,
createContactBarrier,
createPinBarrier,
} from './physics/fold/index.js';

export type {
Expand All @@ -1211,6 +1213,7 @@ export type {
StiffnessDesignInput,
StiffnessDesignOptions,
ContactBarrierOptions,
PinBarrierOptions,
} from './physics/fold/index.js';

// ============================================================================
Expand Down
1 change: 1 addition & 0 deletions src/physics/fold/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './types.js';
export * from './cubicBarrier.js';
export * from './stiffness.js';
export * from './contactBarrier.js';
export * from './pinBarrier.js';
70 changes: 70 additions & 0 deletions src/physics/fold/pinBarrier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { Matrix3x3, Vector3D } from '../../types.js';
import type {
FoldComputationContext,
FoldConstraint,
FoldConstraintEvaluation,
FoldConstraintState,
} from './types.js';
import { computeFrozenStiffness } from './stiffness.js';
import { createCubicBarrier } from './cubicBarrier.js';

export interface PinBarrierOptions {
id?: string;
stiffnessOverride?: number;
maxGap?: number;
direction?: Vector3D;
}

const ZERO_GRADIENT: Vector3D = { x: 0, y: 0, z: 0 };
const ZERO_HESSIAN: Matrix3x3 = [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
];

export function createPinBarrier(options: PinBarrierOptions = {}): FoldConstraint {
const baseBarrier = createCubicBarrier({
id: options.id,
maxGap: options.maxGap,
direction: options.direction,
});

return {
type: 'pin-barrier',
id: options.id,
enabled: true,
evaluate(state: FoldConstraintState, context: FoldComputationContext): FoldConstraintEvaluation {
const direction = options.direction ?? state.direction;
if (!direction) {
return { energy: 0, gradient: ZERO_GRADIENT, hessian: ZERO_HESSIAN };
}

const stiffness = options.stiffnessOverride ??
computeFrozenStiffness(
{
gap: state.gap,
effectiveMass: state.effectiveMass ?? 0,
direction,
hessian: (state.metadata?.hessian as Matrix3x3 | undefined) ?? ZERO_HESSIAN,
},
{ min: 0 }
);

if (stiffness <= 0) {
return { energy: 0, gradient: ZERO_GRADIENT, hessian: ZERO_HESSIAN };
}

const evaluation = baseBarrier.evaluate(
{
...state,
stiffness,
direction,
maxGap: options.maxGap ?? state.maxGap,
},
context
);

return evaluation;
},
};
}
2 changes: 2 additions & 0 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ describe('package entry point', () => {
expect(examples.physics.createCubicBarrier).toBe('examples/foldCubicBarrier.ts');
expect(examples.physics.computeFrozenStiffness).toBe('examples/foldStiffness.ts');
expect(examples.physics.createContactBarrier).toBe('examples/foldContactBarrier.ts');
expect(examples.physics.createPinBarrier).toBe('examples/foldPinBarrier.ts');
});

it('provides strong typing for example categories and names', () => {
Expand Down Expand Up @@ -205,6 +206,7 @@ describe('package entry point', () => {
| 'createCubicBarrier'
| 'computeFrozenStiffness'
| 'createContactBarrier'
| 'createPinBarrier'
>();

expectTypeOf<ExampleName<'ai'>>().toEqualTypeOf<
Expand Down
44 changes: 44 additions & 0 deletions tests/pinBarrier.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, expect, it } from 'vitest';

import { createPinBarrier } from '../src/physics/fold/pinBarrier.js';

describe('pin barrier', () => {
it('returns zero energy when stiffness is zero', () => {
const barrier = createPinBarrier({ stiffnessOverride: 0 });
const evaluation = barrier.evaluate(
{
gap: -0.05,
maxGap: 0,
stiffness: 0,
direction: { x: 1, y: 0, z: 0 },
},
{ deltaTime: 1 }
);

expect(evaluation.energy).toBe(0);
});

it('derives stiffness from design principle', () => {
const barrier = createPinBarrier();
const evaluation = barrier.evaluate(
{
gap: -0.02,
maxGap: 0,
stiffness: 0,
direction: { x: 0, y: 0, z: 1 },
effectiveMass: 0.4,
metadata: {
hessian: [
[4, 0, 0],
[0, 4, 0],
[0, 0, 4],
],
},
},
{ deltaTime: 1 / 120 }
);

expect(evaluation.energy).toBeGreaterThan(0);
expect(evaluation.gradient.z).toBeGreaterThan(0);
});
});