Files
Web_FreeCAD_Bitbybit/public/planegcs-browser-harness.html

456 lines
62 KiB
HTML

<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>planegcs browser harness</title></head>
<body>
<pre id="status">loading</pre>
<script type="module">
import { createSketch } from '/src/facade/sketcher.ts'
import { PlanegcsWorkerProvider } from '/src/facade/planegcsWorkerClient.ts'
import { SketchSolverCoordinator } from '/src/facade/sketchSolverProtocol.ts'
const browserId = new URL(location.href).searchParams.get('browser') || 'chrome'
const report = { schemaVersion: 1, browserId, executionContext: 'dedicated-worker', crossOriginIsolated, sharedArrayBuffer: typeof SharedArrayBuffer === 'function', status: 'failed' }
const worker = new Worker('/planegcs-browser-worker.js', { type: 'module', name: 'freecad-planegcs-verification' })
const provider = new PlanegcsWorkerProvider()
try {
const requestId = crypto.randomUUID()
const response = await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('planegcs Worker solve timed out.')), 60_000)
worker.addEventListener('message', (event) => {
if (event.data?.requestId !== requestId) return
clearTimeout(timeout)
if (event.data.ok) resolve(event.data)
else reject(new Error(event.data.error || 'planegcs Worker solve failed.'))
})
worker.addEventListener('error', (event) => {
clearTimeout(timeout)
reject(new Error(event.message || 'planegcs Worker crashed.'))
}, { once: true })
worker.postMessage({ requestId, operation: 'solve-horizontal-distance', input: { startX: 0, startY: 0, endX: 3, endY: 4, targetLength: 5 } })
})
const values = response.values
const [solveStatus, startX, startY, endX, endY, residual] = values
const nativePassed = solveStatus <= 1 && Math.abs(startY - endY) <= 1e-7 && Math.abs(Math.hypot(endX - startX, endY - startY) - 5) <= 1e-7 && Math.abs(residual) <= 1e-7
const verticalRequestId = crypto.randomUUID()
const verticalResponse = await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('planegcs vertical Worker solve timed out.')), 60_000)
const onMessage = (event) => {
if (event.data?.requestId !== verticalRequestId) return
clearTimeout(timeout)
worker.removeEventListener('message', onMessage)
if (event.data.ok) resolve(event.data)
else reject(new Error(event.data.error || 'planegcs vertical Worker solve failed.'))
}
worker.addEventListener('message', onMessage)
worker.postMessage({ requestId: verticalRequestId, operation: 'solve-vertical-distance', input: { startX: 0, startY: 0, endX: 4, endY: 3, targetLength: 5 } })
})
const verticalValues = verticalResponse.values
const [verticalSolveStatus, verticalStartX, verticalStartY, verticalEndX, verticalEndY, verticalResidual] = verticalValues
const verticalPassed = verticalSolveStatus <= 1 && Math.abs(verticalStartX - verticalEndX) <= 1e-7 && Math.abs(Math.hypot(verticalEndX - verticalStartX, verticalEndY - verticalStartY) - 5) <= 1e-7 && Math.abs(verticalResidual) <= 1e-7
const distanceXRequestId = crypto.randomUUID()
const distanceXResponse = await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('planegcs DistanceX Worker solve timed out.')), 60_000)
const onMessage = (event) => {
if (event.data?.requestId !== distanceXRequestId) return
clearTimeout(timeout)
worker.removeEventListener('message', onMessage)
if (event.data.ok) resolve(event.data)
else reject(new Error(event.data.error || 'planegcs DistanceX Worker solve failed.'))
}
worker.addEventListener('message', onMessage)
worker.postMessage({ requestId: distanceXRequestId, operation: 'solve-distance-x', input: { startX: 0, startY: 0, endX: 3, endY: 4, targetDistance: 6 } })
})
const distanceXValues = distanceXResponse.values
const [distanceXSolveStatus, distanceXStartX, distanceXStartY, distanceXEndX, distanceXEndY, distanceXResidual] = distanceXValues
const distanceXPassed = distanceXSolveStatus <= 1 && Math.abs(distanceXEndX - distanceXStartX - 6) <= 1e-7 && Math.abs(distanceXStartY - distanceXEndY) <= 1e-7 && Math.abs(distanceXResidual) <= 1e-7
const distanceYRequestId = crypto.randomUUID()
const distanceYResponse = await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('planegcs DistanceY Worker solve timed out.')), 60_000)
const onMessage = (event) => {
if (event.data?.requestId !== distanceYRequestId) return
clearTimeout(timeout)
worker.removeEventListener('message', onMessage)
if (event.data.ok) resolve(event.data)
else reject(new Error(event.data.error || 'planegcs DistanceY Worker solve failed.'))
}
worker.addEventListener('message', onMessage)
worker.postMessage({ requestId: distanceYRequestId, operation: 'solve-distance-y', input: { startX: 0, startY: 0, endX: 3, endY: 4, targetDistance: 6 } })
})
const distanceYValues = distanceYResponse.values
const [distanceYSolveStatus, distanceYStartX, distanceYStartY, distanceYEndX, distanceYEndY, distanceYResidual] = distanceYValues
const distanceYPassed = distanceYSolveStatus <= 1 && Math.abs(distanceYEndY - distanceYStartY - 6) <= 1e-7 && Math.abs(distanceYStartX - distanceYEndX) <= 1e-7 && Math.abs(distanceYResidual) <= 1e-7
const angleRequestId = crypto.randomUUID()
const angleResponse = await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('planegcs Angle Worker solve timed out.')), 60_000)
const onMessage = (event) => {
if (event.data?.requestId !== angleRequestId) return
clearTimeout(timeout)
worker.removeEventListener('message', onMessage)
if (event.data.ok) resolve(event.data)
else reject(new Error(event.data.error || 'planegcs Angle Worker solve failed.'))
}
worker.addEventListener('message', onMessage)
worker.postMessage({ requestId: angleRequestId, operation: 'solve-angle', input: { startX: 0, startY: 0, endX: 3, endY: 4, targetLength: 5, targetAngle: Math.PI / 4 } })
})
const angleValues = angleResponse.values
const [angleSolveStatus, angleStartX, angleStartY, angleEndX, angleEndY, angleLengthResidual, angleResidual] = angleValues
const anglePassed = angleSolveStatus <= 1 && Math.abs(Math.hypot(angleEndX - angleStartX, angleEndY - angleStartY) - 5) <= 1e-7 && Math.abs(Math.atan2(angleEndY - angleStartY, angleEndX - angleStartX) - Math.PI / 4) <= 1e-7 && Math.abs(angleLengthResidual) <= 1e-7 && Math.abs(angleResidual) <= 1e-7
const circleRadiusRequestId = crypto.randomUUID()
const circleRadiusResponse = await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('planegcs Circle Radius Worker solve timed out.')), 60_000)
const onMessage = (event) => {
if (event.data?.requestId !== circleRadiusRequestId) return
clearTimeout(timeout)
worker.removeEventListener('message', onMessage)
if (event.data.ok) resolve(event.data)
else reject(new Error(event.data.error || 'planegcs Circle Radius Worker solve failed.'))
}
worker.addEventListener('message', onMessage)
worker.postMessage({ requestId: circleRadiusRequestId, operation: 'solve-circle-radius', input: { centerX: 2, centerY: 3, radius: 1, targetRadius: 4 } })
})
const circleRadiusValues = circleRadiusResponse.values
const [circleRadiusSolveStatus, circleCenterX, circleCenterY, circleRadius, circleRadiusResidual] = circleRadiusValues
const circleRadiusPassed = circleRadiusSolveStatus <= 1 && Math.abs(circleCenterX - 2) <= 1e-7 && Math.abs(circleCenterY - 3) <= 1e-7 && Math.abs(circleRadius - 4) <= 1e-7 && Math.abs(circleRadiusResidual) <= 1e-7
const circleDiameterRequestId = crypto.randomUUID()
const circleDiameterResponse = await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('planegcs Circle Diameter Worker solve timed out.')), 60_000)
const onMessage = (event) => {
if (event.data?.requestId !== circleDiameterRequestId) return
clearTimeout(timeout)
worker.removeEventListener('message', onMessage)
if (event.data.ok) resolve(event.data)
else reject(new Error(event.data.error || 'planegcs Circle Diameter Worker solve failed.'))
}
worker.addEventListener('message', onMessage)
worker.postMessage({ requestId: circleDiameterRequestId, operation: 'solve-circle-diameter', input: { centerX: 2, centerY: 3, radius: 1, targetDiameter: 8 } })
})
const circleDiameterValues = circleDiameterResponse.values
const [circleDiameterSolveStatus, circleDiameterCenterX, circleDiameterCenterY, circleDiameterRadius, circleDiameterResidual] = circleDiameterValues
const circleDiameterPassed = circleDiameterSolveStatus <= 1 && Math.abs(circleDiameterCenterX - 2) <= 1e-7 && Math.abs(circleDiameterCenterY - 3) <= 1e-7 && Math.abs(circleDiameterRadius * 2 - 8) <= 1e-7 && Math.abs(circleDiameterResidual) <= 1e-7
const relationInput = { firstStartX: 0, firstStartY: 0, firstEndX: 4, firstEndY: 0, secondStartX: 0, secondStartY: 2, secondEndX: 3, secondEndY: 6, secondLength: 5 }
const relationRequest = (operation, requestId, input = relationInput) => new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error(`planegcs ${operation} Worker solve timed out.`)), 60_000)
const onMessage = (event) => {
if (event.data?.requestId !== requestId) return
clearTimeout(timeout)
worker.removeEventListener('message', onMessage)
if (event.data.ok) resolve(event.data)
else reject(new Error(event.data.error || `planegcs ${operation} Worker solve failed.`))
}
worker.addEventListener('message', onMessage)
worker.postMessage({ requestId, operation, input })
})
const equalLinesResponse = await relationRequest('solve-equal-lines', crypto.randomUUID())
const equalLinesValues = equalLinesResponse.values
const [equalLinesSolveStatus, equalLinesFirstStartX, equalLinesFirstStartY, equalLinesFirstEndX, equalLinesFirstEndY, equalLinesSecondStartX, equalLinesSecondStartY, equalLinesSecondEndX, equalLinesSecondEndY, equalLinesResidual] = equalLinesValues
const equalLinesPassed = equalLinesSolveStatus <= 1 && Math.abs(Math.hypot(equalLinesFirstEndX - equalLinesFirstStartX, equalLinesFirstEndY - equalLinesFirstStartY) - Math.hypot(equalLinesSecondEndX - equalLinesSecondStartX, equalLinesSecondEndY - equalLinesSecondStartY)) <= 1e-7 && Math.abs(equalLinesResidual) <= 1e-7
const equalCirclesResponse = await relationRequest('solve-equal-circles', crypto.randomUUID(), { firstCenterX: 2, firstCenterY: 3, firstRadius: 4, secondCenterX: 8, secondCenterY: 9, secondRadius: 1 })
const equalCirclesValues = equalCirclesResponse.values
const [equalCirclesSolveStatus, equalCirclesFirstCenterX, equalCirclesFirstCenterY, equalCirclesFirstRadius, equalCirclesSecondCenterX, equalCirclesSecondCenterY, equalCirclesSecondRadius, equalCirclesResidual] = equalCirclesValues
const equalCirclesPassed = equalCirclesSolveStatus <= 1 && Math.abs(equalCirclesFirstCenterX - 2) <= 1e-7 && Math.abs(equalCirclesFirstCenterY - 3) <= 1e-7 && Math.abs(equalCirclesFirstRadius - 4) <= 1e-7 && Math.abs(equalCirclesSecondCenterX - 8) <= 1e-7 && Math.abs(equalCirclesSecondCenterY - 9) <= 1e-7 && Math.abs(equalCirclesSecondRadius - 4) <= 1e-7 && Math.abs(equalCirclesResidual) <= 1e-7
const tangentCirclesResponse = await relationRequest('solve-tangent-circles', crypto.randomUUID(), { firstCenterX: 2, firstCenterY: 3, firstRadius: 4, secondCenterX: 10, secondCenterY: 3, secondRadius: 1 })
const tangentCirclesValues = tangentCirclesResponse.values
const [tangentCirclesSolveStatus, tangentCirclesFirstCenterX, tangentCirclesFirstCenterY, tangentCirclesFirstRadius, tangentCirclesSecondCenterX, tangentCirclesSecondCenterY, tangentCirclesSecondRadius, tangentCirclesResidual] = tangentCirclesValues
const tangentCirclesPassed = tangentCirclesSolveStatus <= 1 && Math.abs(tangentCirclesFirstCenterX - 2) <= 1e-7 && Math.abs(tangentCirclesFirstCenterY - 3) <= 1e-7 && Math.abs(tangentCirclesFirstRadius - 4) <= 1e-7 && Math.abs(tangentCirclesSecondCenterX - 10) <= 1e-7 && Math.abs(tangentCirclesSecondCenterY - 3) <= 1e-7 && Math.abs(tangentCirclesSecondRadius - 4) <= 1e-7 && Math.abs(tangentCirclesResidual) <= 1e-7
const pointSymmetryResponse = await relationRequest('solve-point-symmetry', crypto.randomUUID(), { firstX: 1, firstY: 2, secondX: 0, secondY: 0, centerX: 4, centerY: 6 })
const pointSymmetryValues = pointSymmetryResponse.values
const [pointSymmetrySolveStatus, pointSymmetryFirstX, pointSymmetryFirstY, pointSymmetrySecondX, pointSymmetrySecondY, pointSymmetryCenterX, pointSymmetryCenterY, pointSymmetryResidual] = pointSymmetryValues
const pointSymmetryPassed = pointSymmetrySolveStatus <= 1 && Math.abs(pointSymmetryFirstX - 1) <= 1e-7 && Math.abs(pointSymmetryFirstY - 2) <= 1e-7 && Math.abs(pointSymmetrySecondX - 7) <= 1e-7 && Math.abs(pointSymmetrySecondY - 10) <= 1e-7 && Math.abs(pointSymmetryCenterX - 4) <= 1e-7 && Math.abs(pointSymmetryCenterY - 6) <= 1e-7 && Math.abs(pointSymmetryResidual) <= 1e-7
const pointOnLineResponse = await relationRequest('solve-point-on-line', crypto.randomUUID(), { pointX: 2, pointY: 3, startX: 0, startY: 0, endX: 4, endY: 0, vertical: false })
const pointOnLineValues = pointOnLineResponse.values
const [pointOnLineSolveStatus, pointOnLineX, pointOnLineY, pointOnLineStartX, pointOnLineStartY, pointOnLineEndX, pointOnLineEndY, pointOnLineResidual] = pointOnLineValues
const pointOnLinePassed = pointOnLineSolveStatus <= 1 && Math.abs(pointOnLineX - 2) <= 1e-7 && Math.abs(pointOnLineY) <= 1e-7 && Math.abs(pointOnLineStartX) <= 1e-7 && Math.abs(pointOnLineStartY) <= 1e-7 && Math.abs(pointOnLineEndX - 4) <= 1e-7 && Math.abs(pointOnLineEndY) <= 1e-7 && Math.abs(pointOnLineResidual) <= 1e-7
const pointOnCircleResponse = await relationRequest('solve-point-on-circle', crypto.randomUUID(), { pointX: 4, pointY: 6, centerX: 2, centerY: 3, radius: 4 })
const pointOnCircleValues = pointOnCircleResponse.values
const [pointOnCircleSolveStatus, pointOnCircleX, pointOnCircleY, pointOnCircleCenterX, pointOnCircleCenterY, pointOnCircleRadius, pointOnCircleResidual] = pointOnCircleValues
const pointOnCirclePassed = pointOnCircleSolveStatus <= 1 && Math.abs(pointOnCircleX - 4) <= 1e-7 && Math.abs(pointOnCircleY - 6.464101615137754) <= 1e-7 && Math.abs(pointOnCircleCenterX - 2) <= 1e-7 && Math.abs(pointOnCircleCenterY - 3) <= 1e-7 && Math.abs(pointOnCircleRadius - 4) <= 1e-7 && Math.abs(pointOnCircleResidual) <= 1e-7
const pointOnArcResponse = await relationRequest('solve-point-on-arc', crypto.randomUUID(), { pointX: 2, pointY: 6, centerX: 2, centerY: 3, radius: 4, startAngle: 0, endAngle: Math.PI })
const pointOnArcValues = pointOnArcResponse.values
const [pointOnArcSolveStatus, pointOnArcX, pointOnArcY, pointOnArcCenterX, pointOnArcCenterY, pointOnArcRadius, pointOnArcStartAngle, pointOnArcEndAngle, pointOnArcResidual] = pointOnArcValues
const pointOnArcPassed = pointOnArcSolveStatus <= 1 && Math.abs(pointOnArcX - 2) <= 1e-7 && Math.abs(pointOnArcY - 7) <= 1e-7 && Math.abs(pointOnArcCenterX - 2) <= 1e-7 && Math.abs(pointOnArcCenterY - 3) <= 1e-7 && Math.abs(pointOnArcRadius - 4) <= 1e-7 && Math.abs(pointOnArcStartAngle) <= 1e-7 && Math.abs(pointOnArcEndAngle - Math.PI) <= 1e-7 && Math.abs(pointOnArcResidual) <= 1e-7
const pointOnEllipseResponse = await relationRequest('solve-point-on-ellipse', crypto.randomUUID(), { pointX: 2, pointY: 7, centerX: 2, centerY: 3, focusX: 6, focusY: 3, minorRadius: 3 })
const pointOnEllipseValues = pointOnEllipseResponse.values
const [pointOnEllipseSolveStatus, pointOnEllipseX, pointOnEllipseY, pointOnEllipseCenterX, pointOnEllipseCenterY, pointOnEllipseFocusX, pointOnEllipseFocusY, pointOnEllipseMinorRadius, pointOnEllipseResidual] = pointOnEllipseValues
const pointOnEllipsePassed = pointOnEllipseSolveStatus <= 1 && Math.abs(pointOnEllipseX - 2) <= 1e-7 && Math.abs(pointOnEllipseY - 6) <= 1e-7 && Math.abs(pointOnEllipseCenterX - 2) <= 1e-7 && Math.abs(pointOnEllipseCenterY - 3) <= 1e-7 && Math.abs(pointOnEllipseFocusX - 6) <= 1e-7 && Math.abs(pointOnEllipseFocusY - 3) <= 1e-7 && Math.abs(pointOnEllipseMinorRadius - 3) <= 1e-7 && Math.abs(pointOnEllipseResidual) <= 1e-7
const pointOnBsplineResponse = await relationRequest('solve-point-on-cubic-bspline', crypto.randomUUID(), { pointX: 2, pointY: 1, pole0X: 0, pole0Y: 0, pole1X: 1, pole1Y: 2, pole2X: 3, pole2Y: 2, pole3X: 4, pole3Y: 0, pointParameter: 0.4 })
const pointOnBsplineValues = pointOnBsplineResponse.values
const [pointOnBsplineSolveStatus, pointOnBsplineX, pointOnBsplineY, pointOnBsplineParameter, pointOnBsplineResidualX, pointOnBsplineResidualY] = pointOnBsplineValues
const pointOnBsplinePassed = pointOnBsplineSolveStatus <= 1 && Math.abs(pointOnBsplineX - 2) <= 1e-7 && Math.abs(pointOnBsplineY - 1.5) <= 1e-7 && Math.abs(pointOnBsplineParameter - 0.5) <= 1e-7 && Math.abs(pointOnBsplineResidualX) <= 1e-7 && Math.abs(pointOnBsplineResidualY) <= 1e-7
const bsplineWeightResponse = await relationRequest('solve-cubic-bspline-weight', crypto.randomUUID(), { pole0X: 0, pole0Y: 0, pole1X: 1, pole1Y: 2, pole2X: 3, pole2Y: 2, pole3X: 4, pole3Y: 0, weight0: 1, weight1: 0.75, weight2: 1.25, weight3: 1, controlPointIndex: 1, targetWeight: 1.5 })
const [bsplineWeightSolveStatus, bsplineWeight0, bsplineWeight1, bsplineWeight2, bsplineWeight3, bsplineWeightHelperX, bsplineWeightHelperY, bsplineWeightHelperRadius, bsplineWeightAlignmentResidual, bsplineWeightResidual] = bsplineWeightResponse.values
const bsplineWeightPassed = bsplineWeightSolveStatus <= 1 && Math.abs(bsplineWeight0 - 1) <= 1e-7 && Math.abs(bsplineWeight1 - 1.5) <= 1e-7 && Math.abs(bsplineWeight2 - 1.25) <= 1e-7 && Math.abs(bsplineWeight3 - 1) <= 1e-7 && Math.abs(bsplineWeightHelperX - 1) <= 1e-7 && Math.abs(bsplineWeightHelperY - 2) <= 1e-7 && Math.abs(bsplineWeightHelperRadius - 1.5) <= 1e-7 && Math.abs(bsplineWeightAlignmentResidual) <= 1e-7 && Math.abs(bsplineWeightResidual) <= 1e-7
const parallelResponse = await relationRequest('solve-parallel-lines', crypto.randomUUID())
const parallelValues = parallelResponse.values
const [parallelSolveStatus, parallelFirstStartX, parallelFirstStartY, parallelFirstEndX, parallelFirstEndY, parallelSecondStartX, parallelSecondStartY, parallelSecondEndX, parallelSecondEndY, parallelResidual] = parallelValues
const parallelPassed = parallelSolveStatus <= 1 && Math.abs(parallelFirstEndY - parallelFirstStartY) <= 1e-7 && Math.abs(parallelSecondEndY - parallelSecondStartY) <= 1e-7 && Math.abs(parallelSecondEndX - 5) <= 1e-7 && Math.abs(parallelResidual) <= 1e-7
const perpendicularResponse = await relationRequest('solve-perpendicular-lines', crypto.randomUUID())
const perpendicularValues = perpendicularResponse.values
const [perpendicularSolveStatus, perpendicularFirstStartX, perpendicularFirstStartY, perpendicularFirstEndX, perpendicularFirstEndY, perpendicularSecondStartX, perpendicularSecondStartY, perpendicularSecondEndX, perpendicularSecondEndY, perpendicularResidual] = perpendicularValues
const perpendicularPassed = perpendicularSolveStatus <= 1 && Math.abs(perpendicularFirstEndY - perpendicularFirstStartY) <= 1e-7 && Math.abs(perpendicularSecondEndX - perpendicularSecondStartX) <= 1e-7 && Math.abs(perpendicularSecondEndY - 7) <= 1e-7 && Math.abs(perpendicularResidual) <= 1e-7
const coincidentResponse = await relationRequest('solve-coincident-lines', crypto.randomUUID())
const coincidentValues = coincidentResponse.values
const [coincidentSolveStatus, coincidentFirstStartX, coincidentFirstStartY, coincidentFirstEndX, coincidentFirstEndY, coincidentSecondStartX, coincidentSecondStartY, coincidentSecondEndX, coincidentSecondEndY, coincidentResidual] = coincidentValues
const coincidentPassed = coincidentSolveStatus <= 1 && Math.abs(coincidentFirstEndX - coincidentSecondStartX) <= 1e-7 && Math.abs(coincidentFirstEndY - coincidentSecondStartY) <= 1e-7 && Math.abs(coincidentResidual) <= 1e-7
const coincidentEndpointSolves = []
for (const firstPoint of ['start', 'end']) {
for (const secondPoint of ['start', 'end']) {
const firstEnd = firstPoint === 'end'
const secondEnd = secondPoint === 'end'
const endpointResponse = await relationRequest('solve-coincident-line-points', crypto.randomUUID(), { ...relationInput, firstEnd, secondEnd })
const [endpointSolveStatus, endpointFirstStartX, endpointFirstStartY, endpointFirstEndX, endpointFirstEndY, endpointSecondStartX, endpointSecondStartY, endpointSecondEndX, endpointSecondEndY, endpointResidual] = endpointResponse.values
const first = firstEnd ? { x: endpointFirstEndX, y: endpointFirstEndY } : { x: endpointFirstStartX, y: endpointFirstStartY }
const second = secondEnd ? { x: endpointSecondEndX, y: endpointSecondEndY } : { x: endpointSecondStartX, y: endpointSecondStartY }
coincidentEndpointSolves.push({ firstPoint, secondPoint, solveStatus: endpointSolveStatus, first, second, secondLength: Math.hypot(endpointSecondEndX - endpointSecondStartX, endpointSecondEndY - endpointSecondStartY), residual: endpointResidual })
}
}
const coincidentEndpointSolvesPassed = coincidentEndpointSolves.length === 4 && coincidentEndpointSolves.every((entry) => entry.solveStatus <= 1 && Math.abs(entry.first.x - entry.second.x) <= 1e-7 && Math.abs(entry.first.y - entry.second.y) <= 1e-7 && Math.abs(entry.secondLength - 5) <= 1e-7 && Math.abs(entry.residual) <= 1e-7)
const snellsLawResponse = await relationRequest('solve-snells-law-lines', crypto.randomUUID(), { firstStartX: 0, firstStartY: 0, firstEndX: -4, firstEndY: 4, secondStartX: 0, secondStartY: 0, secondEndX: 4, secondEndY: 3, boundaryStartX: -5, boundaryStartY: 0, boundaryEndX: 5, boundaryEndY: 0, refractiveRatio: 1.5, firstEnd: false, secondEnd: false })
const [snellsLawSolveStatus, snellsFirstStartX, snellsFirstStartY, snellsFirstEndX, snellsFirstEndY, snellsSecondStartX, snellsSecondStartY, snellsSecondEndX, snellsSecondEndY, snellsBoundaryStartX, snellsBoundaryStartY, snellsBoundaryEndX, snellsBoundaryEndY, snellsLawResidual] = snellsLawResponse.values
const snellsLawPassed = snellsLawSolveStatus <= 1 && Math.abs(snellsFirstStartX - snellsSecondStartX) <= 1e-7 && Math.abs(snellsFirstStartY - snellsSecondStartY) <= 1e-7 && Math.abs(Math.hypot(snellsSecondEndX - snellsSecondStartX, snellsSecondEndY - snellsSecondStartY) - 5) <= 1e-7 && Math.abs(snellsBoundaryStartY) <= 1e-7 && Math.abs(snellsBoundaryEndY) <= 1e-7 && Math.abs(snellsLawResidual) <= 1e-7
const ellipseInternalAlignmentSolves = []
for (const alignmentType of [1, 2, 3, 4]) {
const response = await relationRequest('solve-ellipse-internal-alignment', crypto.randomUUID(), { centerX: 2, centerY: 3, majorRadius: 5, minorRadius: 3, rotation: 0.25, alignmentType })
const [solveStatus, centerX, centerY, focusX, focusY, minorRadius, helperStartX, helperStartY, helperEndX, helperEndY, residual] = response.values
const focalDistance = 4
const major = { x: Math.cos(0.25), y: Math.sin(0.25) }
const minor = { x: -major.y, y: major.x }
const expectedStart = alignmentType === 1 ? { x: 2 + 5 * major.x, y: 3 + 5 * major.y } : alignmentType === 2 ? { x: 2 + 3 * minor.x, y: 3 + 3 * minor.y } : { x: 2 + (alignmentType === 3 ? 1 : -1) * focalDistance * major.x, y: 3 + (alignmentType === 3 ? 1 : -1) * focalDistance * major.y }
const expectedEnd = alignmentType === 1 ? { x: 2 - 5 * major.x, y: 3 - 5 * major.y } : alignmentType === 2 ? { x: 2 - 3 * minor.x, y: 3 - 3 * minor.y } : expectedStart
const helperError = Math.max(Math.abs(helperStartX - expectedStart.x), Math.abs(helperStartY - expectedStart.y), Math.abs(helperEndX - expectedEnd.x), Math.abs(helperEndY - expectedEnd.y))
ellipseInternalAlignmentSolves.push({ alignmentType, solveStatus, center: { x: centerX, y: centerY }, focus: { x: focusX, y: focusY }, minorRadius, helperStart: { x: helperStartX, y: helperStartY }, helperEnd: { x: helperEndX, y: helperEndY }, helperError, residual })
}
const ellipseInternalAlignmentSolvesPassed = ellipseInternalAlignmentSolves.length === 4 && ellipseInternalAlignmentSolves.every((entry, index) => entry.alignmentType === index + 1 && entry.solveStatus <= 1 && Math.abs(entry.center.x - 2) <= 1e-7 && Math.abs(entry.center.y - 3) <= 1e-7 && Math.abs(entry.minorRadius - 3) <= 1e-7 && entry.helperError <= 1e-7 && Math.abs(entry.residual) <= 1e-7)
const capabilities = await provider.initialize()
const snapshot = createSketch('chrome-provider', [{ id: 'line', type: 'line', start: { x: 0, y: 0 }, end: { x: 3, y: 4 } }], [
{ id: 'horizontal', type: 'horizontal', geometryId: 'line' },
{ id: 'length', type: 'distance', first: { geometryId: 'line', point: 'start' }, second: { geometryId: 'line', point: 'end' }, value: 5 },
])
const request = (requestId, requestSnapshot = snapshot) => ({ requestId, documentId: 'chrome-doc', documentVersion: 1, snapshot: requestSnapshot })
const completed = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-completed'))
const verticalSnapshot = createSketch('chrome-provider-vertical', [{ id: 'line', type: 'line', start: { x: 0, y: 0 }, end: { x: 4, y: 3 } }], [
{ id: 'vertical', type: 'vertical', geometryId: 'line' },
{ id: 'length', type: 'distance', first: { geometryId: 'line', point: 'start' }, second: { geometryId: 'line', point: 'end' }, value: 5 },
])
const verticalCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-vertical-completed', verticalSnapshot))
const parallelSnapshot = createSketch('chrome-provider-parallel', [
{ id: 'first', type: 'line', start: { x: 0, y: 0 }, end: { x: 4, y: 0 } },
{ id: 'second', type: 'line', start: { x: 0, y: 2 }, end: { x: 3, y: 6 } },
], [{ id: 'parallel', type: 'parallel', firstGeometryId: 'first', secondGeometryId: 'second' }])
const parallelCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-parallel-completed', parallelSnapshot))
const perpendicularSnapshot = createSketch('chrome-provider-perpendicular', [
{ id: 'first', type: 'line', start: { x: 0, y: 0 }, end: { x: 4, y: 0 } },
{ id: 'second', type: 'line', start: { x: 0, y: 2 }, end: { x: 3, y: 6 } },
], [{ id: 'perpendicular', type: 'perpendicular', firstGeometryId: 'first', secondGeometryId: 'second' }])
const perpendicularCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-perpendicular-completed', perpendicularSnapshot))
const coincidentSnapshot = createSketch('chrome-provider-coincident', [
{ id: 'first', type: 'line', start: { x: 0, y: 0 }, end: { x: 4, y: 0 } },
{ id: 'second', type: 'line', start: { x: 0, y: 2 }, end: { x: 3, y: 6 } },
], [{ id: 'coincident', type: 'coincident', first: { geometryId: 'first', point: 'end' }, second: { geometryId: 'second', point: 'start' } }])
const coincidentCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-coincident-completed', coincidentSnapshot))
const coincidentEndpointProviders = []
for (const firstPoint of ['start', 'end']) {
for (const secondPoint of ['start', 'end']) {
const endpointSnapshot = createSketch(`chrome-provider-coincident-${firstPoint}-${secondPoint}`, [
{ id: 'first', type: 'line', start: { x: 0, y: 0 }, end: { x: 4, y: 0 } },
{ id: 'second', type: 'line', start: { x: 0, y: 2 }, end: { x: 3, y: 6 } },
], [{ id: 'coincident', type: 'coincident', first: { geometryId: 'first', point: firstPoint }, second: { geometryId: 'second', point: secondPoint } }])
const completed = await new SketchSolverCoordinator(() => 1).solve(provider, request(`provider-coincident-${firstPoint}-${secondPoint}-completed`, endpointSnapshot))
const firstLine = completed.response?.result.snapshot.geometry[0]
const secondLine = completed.response?.result.snapshot.geometry[1]
coincidentEndpointProviders.push({
firstPoint,
secondPoint,
completed: completed.status,
first: firstLine?.type === 'line' ? firstLine[firstPoint] : null,
second: secondLine?.type === 'line' ? secondLine[secondPoint] : null,
secondLength: secondLine?.type === 'line' ? Math.hypot(secondLine.end.x - secondLine.start.x, secondLine.end.y - secondLine.start.y) : null,
})
}
}
const coincidentEndpointProvidersPassed = coincidentEndpointProviders.length === 4 && coincidentEndpointProviders.every((entry) => entry.completed === 'completed' && entry.first && entry.second && Math.abs(entry.first.x - entry.second.x) <= 1e-7 && Math.abs(entry.first.y - entry.second.y) <= 1e-7 && Math.abs(entry.secondLength - 5) <= 1e-7)
const snellsLawSnapshot = createSketch('chrome-provider-snells-law', [
{ id: 'incident', type: 'line', start: { x: 0, y: 0 }, end: { x: -4, y: 4 } },
{ id: 'refracted', type: 'line', start: { x: 0, y: 0 }, end: { x: 4, y: 3 } },
{ id: 'boundary', type: 'line', start: { x: -5, y: 0 }, end: { x: 5, y: 0 } },
], [
{ id: 'junction', type: 'coincident', first: { geometryId: 'incident', point: 'start' }, second: { geometryId: 'refracted', point: 'start' } },
{ id: 'boundary-point', type: 'pointOnObject', point: { geometryId: 'incident', point: 'start' }, geometryId: 'boundary' },
{ id: 'snells-law', type: 'snellsLaw', first: { geometryId: 'incident', point: 'start' }, second: { geometryId: 'refracted', point: 'start' }, boundaryGeometryId: 'boundary', value: 1.5 },
])
const snellsLawCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-snells-law-completed', snellsLawSnapshot))
const snellsLawProviderFirst = snellsLawCompleted.response?.result.snapshot.geometry[0]
const snellsLawProviderSecond = snellsLawCompleted.response?.result.snapshot.geometry[1]
const snellsLawProviderBoundary = snellsLawCompleted.response?.result.snapshot.geometry[2]
const snellsLawProviderPassed = snellsLawCompleted.status === 'completed' && snellsLawCompleted.response?.result.degreesOfFreedom === 8 && snellsLawProviderFirst?.type === 'line' && snellsLawProviderSecond?.type === 'line' && snellsLawProviderBoundary?.type === 'line' && Math.abs(snellsLawProviderFirst.start.x - snellsLawProviderSecond.start.x) <= 1e-7 && Math.abs(snellsLawProviderFirst.start.y - snellsLawProviderSecond.start.y) <= 1e-7 && Math.abs(Math.hypot(snellsLawProviderSecond.end.x - snellsLawProviderSecond.start.x, snellsLawProviderSecond.end.y - snellsLawProviderSecond.start.y) - 5) <= 1e-7 && Math.abs(snellsLawProviderBoundary.start.y) <= 1e-7 && Math.abs(snellsLawProviderBoundary.end.y) <= 1e-7 && Math.abs(snellsLawCompleted.response?.result.residual ?? Infinity) <= 1e-7
const ellipseInternalAlignmentSnapshot = createSketch('chrome-provider-ellipse-internal-alignment', [
{ id: 'ellipse', type: 'ellipse', center: { x: 2, y: 3 }, majorRadius: 5, minorRadius: 3, rotation: 0.25 },
], [
{ id: 'major', type: 'internalAlignment', geometryId: 'ellipse', internalGeometryIndex: 0, alignmentType: 'ellipse-major' },
{ id: 'minor', type: 'internalAlignment', geometryId: 'ellipse', internalGeometryIndex: 0, alignmentType: 'ellipse-minor' },
{ id: 'focus-1', type: 'internalAlignment', geometryId: 'ellipse', internalGeometryIndex: 0, alignmentType: 'ellipse-focus' },
{ id: 'focus-2', type: 'internalAlignment', geometryId: 'ellipse', internalGeometryIndex: 1, alignmentType: 'ellipse-focus' },
])
const ellipseInternalAlignmentCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-ellipse-internal-alignment-completed', ellipseInternalAlignmentSnapshot))
const ellipseInternalAlignmentProviderGeometry = ellipseInternalAlignmentCompleted.response?.result.snapshot.geometry[0]
const ellipseInternalAlignmentProviderPassed = ellipseInternalAlignmentCompleted.status === 'completed' && ellipseInternalAlignmentCompleted.response?.result.degreesOfFreedom === 5 && Math.abs(ellipseInternalAlignmentCompleted.response?.result.residual ?? Infinity) <= 1e-7 && ellipseInternalAlignmentProviderGeometry?.type === 'ellipse' && ellipseInternalAlignmentProviderGeometry.center.x === 2 && ellipseInternalAlignmentProviderGeometry.center.y === 3 && ellipseInternalAlignmentProviderGeometry.majorRadius === 5 && ellipseInternalAlignmentProviderGeometry.minorRadius === 3 && ellipseInternalAlignmentProviderGeometry.rotation === 0.25
const bsplineWeightSnapshot = createSketch('chrome-provider-B-spline-weight', [
{ id: 'bspline', type: 'bspline', degree: 3, controlPoints: [{ x: 0, y: 0 }, { x: 1, y: 2 }, { x: 3, y: 2 }, { x: 4, y: 0 }], weights: [1, 0.75, 1.25, 1], knots: [0, 0, 0, 0, 1, 1, 1, 1], periodic: false },
], [{ id: 'weight', type: 'weight', geometryId: 'bspline', controlPointIndex: 1, value: 1.5 }])
const bsplineWeightCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-B-spline-weight-completed', bsplineWeightSnapshot))
const bsplineWeightProviderGeometry = bsplineWeightCompleted.response?.result.snapshot.geometry[0]
const bsplineWeightProviderPassed = bsplineWeightCompleted.status === 'completed' && bsplineWeightCompleted.response?.result.degreesOfFreedom === 11 && Math.abs(bsplineWeightCompleted.response?.result.residual ?? Infinity) <= 1e-7 && bsplineWeightProviderGeometry?.type === 'bspline' && bsplineWeightProviderGeometry.weights.length === 4 && Math.abs(bsplineWeightProviderGeometry.weights[0] - 1) <= 1e-7 && Math.abs(bsplineWeightProviderGeometry.weights[1] - 1.5) <= 1e-7 && Math.abs(bsplineWeightProviderGeometry.weights[2] - 1.25) <= 1e-7 && Math.abs(bsplineWeightProviderGeometry.weights[3] - 1) <= 1e-7
const cancellationCoordinator = new SketchSolverCoordinator(() => 1)
const cancellation = cancellationCoordinator.solve(provider, request('provider-cancelled'))
cancellationCoordinator.cancel()
const cancelled = await cancellation
const recovered = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-recovered'))
const stale = await new SketchSolverCoordinator(() => 2).solve(provider, request('provider-stale'))
const recoveredLine = recovered.response?.result.snapshot.geometry[0]
const verticalProviderLine = verticalCompleted.response?.result.snapshot.geometry[0]
const parallelProviderLine = parallelCompleted.response?.result.snapshot.geometry[1]
const perpendicularProviderLine = perpendicularCompleted.response?.result.snapshot.geometry[1]
const coincidentProviderLine = coincidentCompleted.response?.result.snapshot.geometry[1]
const angleSnapshot = createSketch('chrome-provider-angle', [{ id: 'line', type: 'line', start: { x: 0, y: 0 }, end: { x: 3, y: 4 } }], [
{ id: 'angle', type: 'angle', geometryId: 'line', value: Math.PI / 4 },
{ id: 'length', type: 'distance', first: { geometryId: 'line', point: 'start' }, second: { geometryId: 'line', point: 'end' }, value: 5 },
])
const angleCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-angle-completed', angleSnapshot))
const angleProviderLine = angleCompleted.response?.result.snapshot.geometry[0]
const circleRadiusSnapshot = createSketch('chrome-provider-circle-radius', [{ id: 'circle', type: 'circle', center: { x: 2, y: 3 }, radius: 1 }], [
{ id: 'radius', type: 'radius', geometryId: 'circle', value: 4 },
])
const circleRadiusCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-circle-radius-completed', circleRadiusSnapshot))
const circleProvider = circleRadiusCompleted.response?.result.snapshot.geometry[0]
const circleDiameterSnapshot = createSketch('chrome-provider-circle-diameter', [{ id: 'circle', type: 'circle', center: { x: 2, y: 3 }, radius: 1 }], [
{ id: 'diameter', type: 'diameter', geometryId: 'circle', value: 8 },
])
const circleDiameterCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-circle-diameter-completed', circleDiameterSnapshot))
const circleDiameterProvider = circleDiameterCompleted.response?.result.snapshot.geometry[0]
const equalLinesSnapshot = createSketch('chrome-provider-equal-lines', [
{ id: 'first', type: 'line', start: { x: 0, y: 0 }, end: { x: 4, y: 0 } },
{ id: 'second', type: 'line', start: { x: 0, y: 2 }, end: { x: 3, y: 6 } },
], [{ id: 'equal', type: 'equal', firstGeometryId: 'first', secondGeometryId: 'second' }])
const equalLinesCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-equal-lines-completed', equalLinesSnapshot))
const equalLinesProviderSecond = equalLinesCompleted.response?.result.snapshot.geometry[1]
const equalCirclesSnapshot = createSketch('chrome-provider-equal-circles', [
{ id: 'first', type: 'circle', center: { x: 2, y: 3 }, radius: 4 },
{ id: 'second', type: 'circle', center: { x: 8, y: 9 }, radius: 1 },
], [{ id: 'equal', type: 'equal', firstGeometryId: 'first', secondGeometryId: 'second' }])
const equalCirclesCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-equal-circles-completed', equalCirclesSnapshot))
const equalCirclesProviderSecond = equalCirclesCompleted.response?.result.snapshot.geometry[1]
const tangentCirclesSnapshot = createSketch('chrome-provider-tangent-circles', [
{ id: 'first', type: 'circle', center: { x: 2, y: 3 }, radius: 4 },
{ id: 'second', type: 'circle', center: { x: 10, y: 3 }, radius: 1 },
], [{ id: 'tangent', type: 'tangent', firstGeometryId: 'first', secondGeometryId: 'second' }])
const tangentCirclesCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-tangent-circles-completed', tangentCirclesSnapshot))
const tangentCirclesProviderSecond = tangentCirclesCompleted.response?.result.snapshot.geometry[1]
const pointSymmetrySnapshot = createSketch('chrome-provider-point-symmetry', [
{ id: 'first', type: 'point', position: { x: 1, y: 2 } },
{ id: 'second', type: 'point', position: { x: 0, y: 0 } },
{ id: 'center', type: 'point', position: { x: 4, y: 6 } },
], [{ id: 'symmetric', type: 'symmetric', first: { geometryId: 'first', point: 'position' }, second: { geometryId: 'second', point: 'position' }, center: { geometryId: 'center', point: 'position' } }])
const pointSymmetryCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-point-symmetry-completed', pointSymmetrySnapshot))
const pointSymmetryProviderSecond = pointSymmetryCompleted.response?.result.snapshot.geometry[1]
const pointOnLineSnapshot = createSketch('chrome-provider-point-on-object', [
{ id: 'point', type: 'point', position: { x: 2, y: 3 } },
{ id: 'line', type: 'line', start: { x: 0, y: 0 }, end: { x: 4, y: 0 } },
], [{ id: 'point-on-object', type: 'pointOnObject', point: { geometryId: 'point', point: 'position' }, geometryId: 'line' }])
const pointOnLineCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-point-on-object-completed', pointOnLineSnapshot))
const pointOnLineProviderPoint = pointOnLineCompleted.response?.result.snapshot.geometry[0]
const pointOnCircleSnapshot = createSketch('chrome-provider-point-on-circle', [
{ id: 'point', type: 'point', position: { x: 4, y: 6 } },
{ id: 'circle', type: 'circle', center: { x: 2, y: 3 }, radius: 4 },
], [{ id: 'point-on-circle', type: 'pointOnObject', point: { geometryId: 'point', point: 'position' }, geometryId: 'circle' }])
const pointOnCircleCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-point-on-circle-completed', pointOnCircleSnapshot))
const pointOnCircleProviderPoint = pointOnCircleCompleted.response?.result.snapshot.geometry[0]
const pointOnCircleProviderPassed = pointOnCircleCompleted.status === 'completed' && pointOnCircleProviderPoint?.type === 'point' && Math.abs(pointOnCircleProviderPoint.position.x - 4) <= 1e-7 && Math.abs(pointOnCircleProviderPoint.position.y - 6.464101615137754) <= 1e-7
const pointOnArcSnapshot = createSketch('chrome-provider-point-on-arc', [
{ id: 'point', type: 'point', position: { x: 2, y: 6 } },
{ id: 'arc', type: 'arc', center: { x: 2, y: 3 }, radius: 4, startAngle: 0, endAngle: Math.PI },
], [{ id: 'point-on-arc', type: 'pointOnObject', point: { geometryId: 'point', point: 'position' }, geometryId: 'arc' }])
const pointOnArcCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-point-on-arc-completed', pointOnArcSnapshot))
const pointOnArcProviderPoint = pointOnArcCompleted.response?.result.snapshot.geometry[0]
const pointOnArcProviderTarget = pointOnArcCompleted.response?.result.snapshot.geometry[1]
const pointOnArcProviderPassed = pointOnArcCompleted.status === 'completed' && pointOnArcProviderPoint?.type === 'point' && Math.abs(pointOnArcProviderPoint.position.x - 2) <= 1e-7 && Math.abs(pointOnArcProviderPoint.position.y - 7) <= 1e-7 && pointOnArcProviderTarget?.type === 'arc' && Math.abs(pointOnArcProviderTarget.radius - 4) <= 1e-7 && Math.abs(pointOnArcProviderTarget.startAngle) <= 1e-7 && Math.abs(pointOnArcProviderTarget.endAngle - Math.PI) <= 1e-7
const pointOnEllipseSnapshot = createSketch('chrome-provider-point-on-ellipse', [
{ id: 'point', type: 'point', position: { x: 2, y: 7 } },
{ id: 'ellipse', type: 'ellipse', center: { x: 2, y: 3 }, majorRadius: 5, minorRadius: 3, rotation: 0 },
], [{ id: 'point-on-ellipse', type: 'pointOnObject', point: { geometryId: 'point', point: 'position' }, geometryId: 'ellipse' }])
const pointOnEllipseCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-point-on-ellipse-completed', pointOnEllipseSnapshot))
const pointOnEllipseProviderPoint = pointOnEllipseCompleted.response?.result.snapshot.geometry[0]
const pointOnEllipseProviderTarget = pointOnEllipseCompleted.response?.result.snapshot.geometry[1]
const pointOnEllipseProviderPassed = pointOnEllipseCompleted.status === 'completed' && pointOnEllipseProviderPoint?.type === 'point' && Math.abs(pointOnEllipseProviderPoint.position.x - 2) <= 1e-7 && Math.abs(pointOnEllipseProviderPoint.position.y - 6) <= 1e-7 && pointOnEllipseProviderTarget?.type === 'ellipse' && Math.abs(pointOnEllipseProviderTarget.majorRadius - 5) <= 1e-7 && Math.abs(pointOnEllipseProviderTarget.minorRadius - 3) <= 1e-7 && Math.abs(pointOnEllipseProviderTarget.rotation) <= 1e-7
const pointOnBsplineSnapshot = createSketch('chrome-provider-point-on-B-spline', [
{ id: 'point', type: 'point', position: { x: 2, y: 1 } },
{ id: 'bspline', type: 'bspline', degree: 3, controlPoints: [{ x: 0, y: 0 }, { x: 1, y: 2 }, { x: 3, y: 2 }, { x: 4, y: 0 }], weights: [1, 1, 1, 1], knots: [0, 0, 0, 0, 1, 1, 1, 1], periodic: false },
], [{ id: 'point-on-B-spline', type: 'pointOnObject', point: { geometryId: 'point', point: 'position' }, geometryId: 'bspline' }])
const pointOnBsplineCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-point-on-B-spline-completed', pointOnBsplineSnapshot))
const pointOnBsplineProviderPoint = pointOnBsplineCompleted.response?.result.snapshot.geometry[0]
const pointOnBsplineProviderTarget = pointOnBsplineCompleted.response?.result.snapshot.geometry[1]
const pointOnBsplineProviderPassed = pointOnBsplineCompleted.status === 'completed' && pointOnBsplineProviderPoint?.type === 'point' && Math.abs(pointOnBsplineProviderPoint.position.x - 2) <= 1e-7 && Math.abs(pointOnBsplineProviderPoint.position.y - 1.5) <= 1e-7 && pointOnBsplineProviderTarget?.type === 'bspline' && pointOnBsplineProviderTarget.controlPoints.length === 4
const blockSnapshot = createSketch('chrome-provider-block', [{ id: 'line', type: 'line', start: { x: 1, y: 2 }, end: { x: 4, y: 6 } }], [{ id: 'block', type: 'block', geometryId: 'line' }])
const blockCompleted = await new SketchSolverCoordinator(() => 1).solve(provider, request('provider-block-completed', blockSnapshot))
const blockProviderLine = blockCompleted.response?.result.snapshot.geometry[0]
const blockProviderPassed = blockCompleted.status === 'completed' && blockCompleted.response?.result.degreesOfFreedom === 0 && blockProviderLine?.type === 'line' && blockProviderLine.start.x === 1 && blockProviderLine.start.y === 2 && blockProviderLine.end.x === 4 && blockProviderLine.end.y === 6
const providerPassed = capabilities.availability === 'available' && capabilities.engine === 'planegcs-wasm' && capabilities.supportedGeometry.includes('point') && capabilities.supportedConstraints.includes('block') && capabilities.supportedConstraints.includes('vertical') && capabilities.supportedConstraints.includes('distanceX') && capabilities.supportedConstraints.includes('distanceY') && capabilities.supportedConstraints.includes('angle') && capabilities.supportedConstraints.includes('radius') && capabilities.supportedConstraints.includes('diameter') && capabilities.supportedConstraints.includes('equal') && capabilities.supportedConstraints.includes('tangent') && capabilities.supportedConstraints.includes('symmetric') && capabilities.supportedConstraints.includes('pointOnObject') && capabilities.supportedConstraints.includes('parallel') && capabilities.supportedConstraints.includes('perpendicular') && capabilities.supportedConstraints.includes('coincident') && completed.status === 'completed' && verticalCompleted.status === 'completed' && angleCompleted.status === 'completed' && circleRadiusCompleted.status === 'completed' && circleDiameterCompleted.status === 'completed' && equalLinesCompleted.status === 'completed' && equalCirclesCompleted.status === 'completed' && tangentCirclesCompleted.status === 'completed' && pointSymmetryCompleted.status === 'completed' && pointOnLineCompleted.status === 'completed' && parallelCompleted.status === 'completed' && perpendicularCompleted.status === 'completed' && coincidentCompleted.status === 'completed' && coincidentEndpointProvidersPassed && cancelled.status === 'cancelled' && recovered.status === 'completed' && stale.status === 'stale' && recoveredLine?.type === 'line' && Math.abs(recoveredLine.end.x - 5) <= 1e-7 && Math.abs(recoveredLine.end.y) <= 1e-7 && verticalProviderLine?.type === 'line' && Math.abs(verticalProviderLine.end.x) <= 1e-7 && Math.abs(verticalProviderLine.end.y - 5) <= 1e-7 && angleProviderLine?.type === 'line' && Math.abs(Math.hypot(angleProviderLine.end.x - angleProviderLine.start.x, angleProviderLine.end.y - angleProviderLine.start.y) - 5) <= 1e-7 && Math.abs(Math.atan2(angleProviderLine.end.y - angleProviderLine.start.y, angleProviderLine.end.x - angleProviderLine.start.x) - Math.PI / 4) <= 1e-7 && circleProvider?.type === 'circle' && Math.abs(circleProvider.center.x - 2) <= 1e-7 && Math.abs(circleProvider.center.y - 3) <= 1e-7 && Math.abs(circleProvider.radius - 4) <= 1e-7 && circleDiameterProvider?.type === 'circle' && Math.abs(circleDiameterProvider.center.x - 2) <= 1e-7 && Math.abs(circleDiameterProvider.center.y - 3) <= 1e-7 && Math.abs(circleDiameterProvider.radius * 2 - 8) <= 1e-7 && equalLinesProviderSecond?.type === 'line' && Math.abs(Math.hypot(equalLinesProviderSecond.end.x - equalLinesProviderSecond.start.x, equalLinesProviderSecond.end.y - equalLinesProviderSecond.start.y) - 4) <= 1e-7 && equalCirclesProviderSecond?.type === 'circle' && Math.abs(equalCirclesProviderSecond.radius - 4) <= 1e-7 && tangentCirclesProviderSecond?.type === 'circle' && Math.abs(tangentCirclesProviderSecond.radius - 4) <= 1e-7 && pointSymmetryProviderSecond?.type === 'point' && Math.abs(pointSymmetryProviderSecond.position.x - 7) <= 1e-7 && Math.abs(pointSymmetryProviderSecond.position.y - 10) <= 1e-7 && pointOnLineProviderPoint?.type === 'point' && Math.abs(pointOnLineProviderPoint.position.x - 2) <= 1e-7 && Math.abs(pointOnLineProviderPoint.position.y) <= 1e-7 && parallelProviderLine?.type === 'line' && Math.abs(parallelProviderLine.end.y - parallelProviderLine.start.y) <= 1e-7 && Math.abs(parallelProviderLine.end.x - 5) <= 1e-7 && perpendicularProviderLine?.type === 'line' && Math.abs(perpendicularProviderLine.end.x - perpendicularProviderLine.start.x) <= 1e-7 && Math.abs(perpendicularProviderLine.end.y - 7) <= 1e-7 && coincidentProviderLine?.type === 'line' && Math.abs(coincidentProviderLine.start.x - 4) <= 1e-7 && Math.abs(coincidentProviderLine.start.y) <= 1e-7
const snellsLawCapabilityPassed = capabilities.supportedConstraints.includes('snellsLaw')
const internalAlignmentCapabilityPassed = capabilities.supportedConstraints.includes('internalAlignment')
const weightCapabilityPassed = capabilities.supportedConstraints.includes('weight')
const arcProviderPassed = capabilities.supportedGeometry.includes('arc') && pointOnArcProviderPassed
const ellipseProviderPassed = capabilities.supportedGeometry.includes('ellipse') && pointOnEllipseProviderPassed
const bsplineProviderPassed = capabilities.supportedGeometry.includes('bspline') && pointOnBsplineProviderPassed
Object.assign(report, {
status: nativePassed && verticalPassed && distanceXPassed && distanceYPassed && anglePassed && circleRadiusPassed && circleDiameterPassed && equalLinesPassed && equalCirclesPassed && tangentCirclesPassed && pointSymmetryPassed && pointOnLinePassed && pointOnCirclePassed && pointOnCircleProviderPassed && pointOnArcPassed && arcProviderPassed && pointOnEllipsePassed && ellipseProviderPassed && pointOnBsplinePassed && bsplineProviderPassed && bsplineWeightPassed && bsplineWeightProviderPassed && weightCapabilityPassed && blockProviderPassed && parallelPassed && perpendicularPassed && coincidentPassed && coincidentEndpointSolvesPassed && snellsLawPassed && snellsLawProviderPassed && snellsLawCapabilityPassed && ellipseInternalAlignmentSolvesPassed && ellipseInternalAlignmentProviderPassed && internalAlignmentCapabilityPassed && providerPassed ? 'pass' : 'failed',
solve: { solveStatus, startX, startY, endX, endY, residual },
verticalSolve: { solveStatus: verticalSolveStatus, startX: verticalStartX, startY: verticalStartY, endX: verticalEndX, endY: verticalEndY, residual: verticalResidual },
distanceXSolve: { solveStatus: distanceXSolveStatus, startX: distanceXStartX, startY: distanceXStartY, endX: distanceXEndX, endY: distanceXEndY, residual: distanceXResidual },
distanceYSolve: { solveStatus: distanceYSolveStatus, startX: distanceYStartX, startY: distanceYStartY, endX: distanceYEndX, endY: distanceYEndY, residual: distanceYResidual },
angleSolve: { solveStatus: angleSolveStatus, startX: angleStartX, startY: angleStartY, endX: angleEndX, endY: angleEndY, lengthResidual: angleLengthResidual, residual: angleResidual, targetAngle: Math.PI / 4 },
circleRadiusSolve: { solveStatus: circleRadiusSolveStatus, centerX: circleCenterX, centerY: circleCenterY, radius: circleRadius, residual: circleRadiusResidual },
circleDiameterSolve: { solveStatus: circleDiameterSolveStatus, centerX: circleDiameterCenterX, centerY: circleDiameterCenterY, radius: circleDiameterRadius, residual: circleDiameterResidual },
equalLinesSolve: { solveStatus: equalLinesSolveStatus, firstStartX: equalLinesFirstStartX, firstStartY: equalLinesFirstStartY, firstEndX: equalLinesFirstEndX, firstEndY: equalLinesFirstEndY, secondStartX: equalLinesSecondStartX, secondStartY: equalLinesSecondStartY, secondEndX: equalLinesSecondEndX, secondEndY: equalLinesSecondEndY, residual: equalLinesResidual },
equalCirclesSolve: { solveStatus: equalCirclesSolveStatus, firstCenterX: equalCirclesFirstCenterX, firstCenterY: equalCirclesFirstCenterY, firstRadius: equalCirclesFirstRadius, secondCenterX: equalCirclesSecondCenterX, secondCenterY: equalCirclesSecondCenterY, secondRadius: equalCirclesSecondRadius, residual: equalCirclesResidual },
tangentCirclesSolve: { solveStatus: tangentCirclesSolveStatus, firstCenterX: tangentCirclesFirstCenterX, firstCenterY: tangentCirclesFirstCenterY, firstRadius: tangentCirclesFirstRadius, secondCenterX: tangentCirclesSecondCenterX, secondCenterY: tangentCirclesSecondCenterY, secondRadius: tangentCirclesSecondRadius, residual: tangentCirclesResidual },
pointSymmetrySolve: { solveStatus: pointSymmetrySolveStatus, firstX: pointSymmetryFirstX, firstY: pointSymmetryFirstY, secondX: pointSymmetrySecondX, secondY: pointSymmetrySecondY, centerX: pointSymmetryCenterX, centerY: pointSymmetryCenterY, residual: pointSymmetryResidual },
pointOnLineSolve: { solveStatus: pointOnLineSolveStatus, pointX: pointOnLineX, pointY: pointOnLineY, startX: pointOnLineStartX, startY: pointOnLineStartY, endX: pointOnLineEndX, endY: pointOnLineEndY, residual: pointOnLineResidual },
pointOnCircleSolve: { solveStatus: pointOnCircleSolveStatus, pointX: pointOnCircleX, pointY: pointOnCircleY, centerX: pointOnCircleCenterX, centerY: pointOnCircleCenterY, radius: pointOnCircleRadius, residual: pointOnCircleResidual },
pointOnArcSolve: { solveStatus: pointOnArcSolveStatus, pointX: pointOnArcX, pointY: pointOnArcY, centerX: pointOnArcCenterX, centerY: pointOnArcCenterY, radius: pointOnArcRadius, startAngle: pointOnArcStartAngle, endAngle: pointOnArcEndAngle, residual: pointOnArcResidual },
pointOnEllipseSolve: { solveStatus: pointOnEllipseSolveStatus, pointX: pointOnEllipseX, pointY: pointOnEllipseY, centerX: pointOnEllipseCenterX, centerY: pointOnEllipseCenterY, focusX: pointOnEllipseFocusX, focusY: pointOnEllipseFocusY, minorRadius: pointOnEllipseMinorRadius, residual: pointOnEllipseResidual },
pointOnBsplineSolve: { solveStatus: pointOnBsplineSolveStatus, pointX: pointOnBsplineX, pointY: pointOnBsplineY, pointParameter: pointOnBsplineParameter, residualX: pointOnBsplineResidualX, residualY: pointOnBsplineResidualY },
bsplineWeightSolve: { solveStatus: bsplineWeightSolveStatus, weights: [bsplineWeight0, bsplineWeight1, bsplineWeight2, bsplineWeight3], helper: { center: { x: bsplineWeightHelperX, y: bsplineWeightHelperY }, radius: bsplineWeightHelperRadius }, alignmentResidual: bsplineWeightAlignmentResidual, residual: bsplineWeightResidual },
parallelSolve: { solveStatus: parallelSolveStatus, firstStartX: parallelFirstStartX, firstStartY: parallelFirstStartY, firstEndX: parallelFirstEndX, firstEndY: parallelFirstEndY, secondStartX: parallelSecondStartX, secondStartY: parallelSecondStartY, secondEndX: parallelSecondEndX, secondEndY: parallelSecondEndY, residual: parallelResidual },
perpendicularSolve: { solveStatus: perpendicularSolveStatus, firstStartX: perpendicularFirstStartX, firstStartY: perpendicularFirstStartY, firstEndX: perpendicularFirstEndX, firstEndY: perpendicularFirstEndY, secondStartX: perpendicularSecondStartX, secondStartY: perpendicularSecondStartY, secondEndX: perpendicularSecondEndX, secondEndY: perpendicularSecondEndY, residual: perpendicularResidual },
coincidentSolve: { solveStatus: coincidentSolveStatus, firstEndX: coincidentFirstEndX, firstEndY: coincidentFirstEndY, secondStartX: coincidentSecondStartX, secondStartY: coincidentSecondStartY, residual: coincidentResidual },
coincidentEndpointSolves,
coincidentEndpointProviders,
snellsLawSolve: { solveStatus: snellsLawSolveStatus, firstStart: { x: snellsFirstStartX, y: snellsFirstStartY }, firstEnd: { x: snellsFirstEndX, y: snellsFirstEndY }, secondStart: { x: snellsSecondStartX, y: snellsSecondStartY }, secondEnd: { x: snellsSecondEndX, y: snellsSecondEndY }, boundaryStart: { x: snellsBoundaryStartX, y: snellsBoundaryStartY }, boundaryEnd: { x: snellsBoundaryEndX, y: snellsBoundaryEndY }, ratio: 1.5, residual: snellsLawResidual },
snellsLawProvider: { completed: snellsLawCompleted.status, degreesOfFreedom: snellsLawCompleted.response?.result.degreesOfFreedom, residual: snellsLawCompleted.response?.result.residual, first: snellsLawProviderFirst?.type === 'line' ? { start: snellsLawProviderFirst.start, end: snellsLawProviderFirst.end } : null, second: snellsLawProviderSecond?.type === 'line' ? { start: snellsLawProviderSecond.start, end: snellsLawProviderSecond.end } : null, boundary: snellsLawProviderBoundary?.type === 'line' ? { start: snellsLawProviderBoundary.start, end: snellsLawProviderBoundary.end } : null },
ellipseInternalAlignmentSolves,
ellipseInternalAlignmentProvider: { completed: ellipseInternalAlignmentCompleted.status, degreesOfFreedom: ellipseInternalAlignmentCompleted.response?.result.degreesOfFreedom, residual: ellipseInternalAlignmentCompleted.response?.result.residual, ellipse: ellipseInternalAlignmentProviderGeometry?.type === 'ellipse' ? { center: ellipseInternalAlignmentProviderGeometry.center, majorRadius: ellipseInternalAlignmentProviderGeometry.majorRadius, minorRadius: ellipseInternalAlignmentProviderGeometry.minorRadius, rotation: ellipseInternalAlignmentProviderGeometry.rotation } : null },
bsplineWeightProvider: { completed: bsplineWeightCompleted.status, degreesOfFreedom: bsplineWeightCompleted.response?.result.degreesOfFreedom, residual: bsplineWeightCompleted.response?.result.residual, bspline: bsplineWeightProviderGeometry?.type === 'bspline' ? { degree: bsplineWeightProviderGeometry.degree, controlPoints: bsplineWeightProviderGeometry.controlPoints, weights: bsplineWeightProviderGeometry.weights, knots: bsplineWeightProviderGeometry.knots, periodic: bsplineWeightProviderGeometry.periodic } : null },
pointOnCircleProvider: { completed: pointOnCircleCompleted.status, point: pointOnCircleProviderPoint?.type === 'point' ? pointOnCircleProviderPoint.position : null },
pointOnArcProvider: { completed: pointOnArcCompleted.status, point: pointOnArcProviderPoint?.type === 'point' ? pointOnArcProviderPoint.position : null, arc: pointOnArcProviderTarget?.type === 'arc' ? { center: pointOnArcProviderTarget.center, radius: pointOnArcProviderTarget.radius, startAngle: pointOnArcProviderTarget.startAngle, endAngle: pointOnArcProviderTarget.endAngle } : null },
pointOnEllipseProvider: { completed: pointOnEllipseCompleted.status, point: pointOnEllipseProviderPoint?.type === 'point' ? pointOnEllipseProviderPoint.position : null, ellipse: pointOnEllipseProviderTarget?.type === 'ellipse' ? { center: pointOnEllipseProviderTarget.center, majorRadius: pointOnEllipseProviderTarget.majorRadius, minorRadius: pointOnEllipseProviderTarget.minorRadius, rotation: pointOnEllipseProviderTarget.rotation } : null },
pointOnBsplineProvider: { completed: pointOnBsplineCompleted.status, point: pointOnBsplineProviderPoint?.type === 'point' ? pointOnBsplineProviderPoint.position : null, bspline: pointOnBsplineProviderTarget?.type === 'bspline' ? { degree: pointOnBsplineProviderTarget.degree, controlPoints: pointOnBsplineProviderTarget.controlPoints, weights: pointOnBsplineProviderTarget.weights, knots: pointOnBsplineProviderTarget.knots, periodic: pointOnBsplineProviderTarget.periodic } : null },
blockProvider: { completed: blockCompleted.status, degreesOfFreedom: blockCompleted.response?.result.degreesOfFreedom, line: blockProviderLine?.type === 'line' ? { start: blockProviderLine.start, end: blockProviderLine.end } : null },
providerLifecycle: { availability: capabilities.availability, engine: capabilities.engine, supportedGeometry: capabilities.supportedGeometry, supportedConstraints: capabilities.supportedConstraints, completed: completed.status, verticalCompleted: verticalCompleted.status, verticalEnd: verticalProviderLine?.type === 'line' ? verticalProviderLine.end : null, angleCompleted: angleCompleted.status, angleEnd: angleProviderLine?.type === 'line' ? angleProviderLine.end : null, circleRadiusCompleted: circleRadiusCompleted.status, circleRadius: circleProvider?.type === 'circle' ? { center: circleProvider.center, radius: circleProvider.radius } : null, circleDiameterCompleted: circleDiameterCompleted.status, circleDiameter: circleDiameterProvider?.type === 'circle' ? { center: circleDiameterProvider.center, radius: circleDiameterProvider.radius } : null, equalLinesCompleted: equalLinesCompleted.status, equalLinesSecond: equalLinesProviderSecond?.type === 'line' ? { start: equalLinesProviderSecond.start, end: equalLinesProviderSecond.end } : null, equalCirclesCompleted: equalCirclesCompleted.status, equalCirclesSecond: equalCirclesProviderSecond?.type === 'circle' ? { center: equalCirclesProviderSecond.center, radius: equalCirclesProviderSecond.radius } : null, tangentCirclesCompleted: tangentCirclesCompleted.status, tangentCirclesSecond: tangentCirclesProviderSecond?.type === 'circle' ? { center: tangentCirclesProviderSecond.center, radius: tangentCirclesProviderSecond.radius } : null, pointSymmetryCompleted: pointSymmetryCompleted.status, pointSymmetrySecond: pointSymmetryProviderSecond?.type === 'point' ? pointSymmetryProviderSecond.position : null, pointOnLineCompleted: pointOnLineCompleted.status, pointOnLinePoint: pointOnLineProviderPoint?.type === 'point' ? pointOnLineProviderPoint.position : null, parallelCompleted: parallelCompleted.status, parallelEnd: parallelProviderLine?.type === 'line' ? parallelProviderLine.end : null, perpendicularCompleted: perpendicularCompleted.status, perpendicularEnd: perpendicularProviderLine?.type === 'line' ? perpendicularProviderLine.end : null, coincidentCompleted: coincidentCompleted.status, coincidentStart: coincidentProviderLine?.type === 'line' ? coincidentProviderLine.start : null, cancelled: cancelled.status, recovered: recovered.status, stale: stale.status, recoveredEnd: recoveredLine?.type === 'line' ? recoveredLine.end : null },
})
} catch (error) {
report.error = error instanceof Error ? `${error.name}: ${error.message}` : String(error)
} finally {
worker.terminate()
provider.dispose()
}
document.querySelector('#status').textContent = JSON.stringify(report, null, 2)
await fetch('/__planegcs-report', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(report) })
</script>
</body>
</html>