P4: extend sketch constraints and expression functions

This commit is contained in:
2026-08-02 17:35:56 -04:00
parent 9c76f33f3d
commit 171fd670af
6 changed files with 142 additions and 8 deletions

View File

@@ -125,6 +125,12 @@ test('quantity expressions convert units and reject incompatible dimensions', ()
assert.equal(evaluateQuantityExpression('2 + 3').value.value, 5)
assert.equal(quantityFromNumber(7).dimension, 'dimensionless')
assert.equal(evaluateQuantityExpression('50%').value.dimension, 'percent')
assert.ok(Math.abs(evaluateQuantityExpression('sin(90 deg)').value.value - 1) < 1e-12)
assert.equal(evaluateQuantityExpression('max(2 mm, 1 cm)').value.value, 10)
assert.equal(evaluateQuantityExpression('clamp(15, 0, 10)').value.value, 10)
assert.equal(evaluateQuantityExpression('round(2.6)').value.value, 3)
assert.throws(() => evaluateQuantityExpression('sin(2 mm)'), /angle or dimensionless/)
assert.throws(() => evaluateQuantityExpression('unknown(1)'), /Unknown expression function/)
})
test('topology signatures are independent of transient face indexes and flag ambiguity', () => {
@@ -174,6 +180,24 @@ test('basic sketch solver applies geometric constraints and reports remaining de
assert.ok(conflict.diagnostics.some((diagnostic) => diagnostic.code === 'SOLVER_NOT_CONVERGED'))
})
test('Sketcher solver supports diameter, symmetric and tangent constraints at the model boundary', () => {
const sketch = createSketch('advanced-constraints', [
{ id: 'circle-1', type: 'circle', center: { x: 0, y: 1 }, radius: 1 },
{ id: 'line-1', type: 'line', start: { x: -5, y: 0 }, end: { x: 5, y: 0 } },
{ id: 'point-a', type: 'point', position: { x: -2, y: 2 } },
{ id: 'point-b', type: 'point', position: { x: 4, y: 0 } },
{ id: 'center', type: 'point', position: { x: 1, y: 1 } },
], [
{ id: 'diameter-1', type: 'diameter', geometryId: 'circle-1', value: 4 },
{ id: 'tangent-1', type: 'tangent', firstGeometryId: 'line-1', secondGeometryId: 'circle-1' },
{ id: 'symmetric-1', type: 'symmetric', first: { geometryId: 'point-a', point: 'position' }, second: { geometryId: 'point-b', point: 'position' }, center: { geometryId: 'center', point: 'position' } },
])
const result = solveSketch(sketch)
assert.ok(result.residual <= 1e-7)
assert.equal((result.snapshot.geometry.find((geometry) => geometry.id === 'circle-1') as Extract<typeof result.snapshot.geometry[number], { type: 'circle' }>).radius, 2)
assert.equal((result.snapshot.geometry.find((geometry) => geometry.id === 'circle-1') as Extract<typeof result.snapshot.geometry[number], { type: 'circle' }>).center.y, 2)
})
test('sketcher operations are facade transactions and survive the project fallback store', async () => {
const facade = createMockFacade()
const before = facade.app.document.getActive().version