{
  "openapi": "3.1.1",
  "info": {
    "title": "Parametric Sketch Constraint Solver API",
    "version": "1.0.0",
    "summary": "Solve a 2-D constraint sketch with parameters, report degrees of freedom and extrude it.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://kerfwisesketchconstraintsolver.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Solve a 2-D constraint sketch with parameters, report degrees of freedom and extrude it.",
        "description": "Solve points, lines, arcs and circles under geometric and dimensional constraints driven by named parameters. Get exact coordinates, the remaining degrees of freedom, redundant and conflicting constraints, and the profile extruded as a Kerfwise Form Studio solid.\n\nMethodology:\n- Variables are the coordinates of every point that is not fixed and the radius of every circle; an arc adds the equation |start − centre| = |end − centre|. Each constraint adds one or two residual equations in millimetres (angles are scaled by the sketch size), so the solution is r(x) = 0.\n- Levenberg–Marquardt with identity damping: each step solves min ‖J·δ + r‖² + μ‖δ‖² by Householder QR (Proofline’s linear algebra). With small μ this is the minimum-norm Gauss–Newton step, so geometry that the constraints leave free moves as little as possible from the stored sketch.\n- Degrees of freedom = variables − rank of the Jacobian at the solution. The rank comes from Gram–Schmidt over the normalised constraint rows in order: a row that is a combination of earlier rows is redundant if its residual is zero, or conflicting (with the rows it depends on) if it is not.\n- Dimensions are numbers or expressions in the parameter table, compiled by Proofline’s safe expression parser (no eval). Lengths are millimetres, angles degrees, tangency at a shared end point is written as radius ⊥ line.\n- The profile is the single closed loop of non-construction lines and arcs (or one circle), arcs tessellated with chords of at most 15°, at most 64 points. It becomes a Form Studio extrusion with the evaluated height.\n\nConcept geometry. A sketch can have several solutions (for example an arc bulging inward or outward); the solver finds the one nearest to the stored points. Check the drawing.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kerfwise_sketch_constraint_solver",
        "x-owner-app": "kerfwise",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KerfwiseSketchConstraintSolverInput"
              },
              "examples": {
                "example1": {
                  "summary": "Panel with a tangent fillet arc (radius R)",
                  "value": {
                    "sketch": "{\n  \"points\": [\n    {\n      \"id\": \"p1\",\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"id\": \"p2\",\n      \"x\": 600,\n      \"y\": 0\n    },\n    {\n      \"id\": \"p3\",\n      \"x\": 600,\n      \"y\": 300\n    },\n    {\n      \"id\": \"p4\",\n      \"x\": 100,\n      \"y\": 400\n    },\n    {\n      \"id\": \"p5\",\n      \"x\": 0,\n      \"y\": 400\n    },\n    {\n      \"id\": \"c\",\n      \"x\": 90,\n      \"y\": 310\n    }\n  ],\n  \"entities\": [\n    {\n      \"id\": \"bottom\",\n      \"type\": \"line\",\n      \"a\": \"p1\",\n      \"b\": \"p2\"\n    },\n    {\n      \"id\": \"right\",\n      \"type\": \"line\",\n      \"a\": \"p2\",\n      \"b\": \"p3\"\n    },\n    {\n      \"id\": \"round\",\n      \"type\": \"arc\",\n      \"c\": \"c\",\n      \"a\": \"p3\",\n      \"b\": \"p4\"\n    },\n    {\n      \"id\": \"top\",\n      \"type\": \"line\",\n      \"a\": \"p4\",\n      \"b\": \"p5\"\n    },\n    {\n      \"id\": \"left\",\n      \"type\": \"line\",\n      \"a\": \"p5\",\n      \"b\": \"p1\"\n    }\n  ],\n  \"constraints\": [\n    {\n      \"id\": \"origin\",\n      \"type\": \"fixed\",\n      \"refs\": [\n        \"p1\"\n      ]\n    },\n    {\n      \"id\": \"h1\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"bottom\"\n      ]\n    },\n    {\n      \"id\": \"h2\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"top\"\n      ]\n    },\n    {\n      \"id\": \"v1\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"left\"\n      ]\n    },\n    {\n      \"id\": \"v2\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"right\"\n      ]\n    },\n    {\n      \"id\": \"t1\",\n      \"type\": \"tangent\",\n      \"refs\": [\n        \"right\",\n        \"round\"\n      ]\n    },\n    {\n      \"id\": \"t2\",\n      \"type\": \"tangent\",\n      \"refs\": [\n        \"top\",\n        \"round\"\n      ]\n    },\n    {\n      \"id\": \"width\",\n      \"type\": \"distanceX\",\n      \"refs\": [\n        \"p1\",\n        \"p2\"\n      ],\n      \"value\": \"W\"\n    },\n    {\n      \"id\": \"depth\",\n      \"type\": \"distanceY\",\n      \"refs\": [\n        \"p1\",\n        \"p5\"\n      ],\n      \"value\": \"D\"\n    },\n    {\n      \"id\": \"fillet\",\n      \"type\": \"radius\",\n      \"refs\": [\n        \"round\"\n      ],\n      \"value\": \"R\"\n    }\n  ],\n  \"height\": \"T\"\n}",
                    "parameters": "W = 600\nD = 400\nR = 100\nT = 19",
                    "name": "Rounded panel"
                  }
                },
                "example2": {
                  "summary": "Rectangle whose depth is half its width (D = W / 2)",
                  "value": {
                    "sketch": "{\n  \"points\": [\n    {\n      \"id\": \"p1\",\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"id\": \"p2\",\n      \"x\": 500,\n      \"y\": 10\n    },\n    {\n      \"id\": \"p3\",\n      \"x\": 480,\n      \"y\": 300\n    },\n    {\n      \"id\": \"p4\",\n      \"x\": -10,\n      \"y\": 290\n    }\n  ],\n  \"entities\": [\n    {\n      \"id\": \"bottom\",\n      \"type\": \"line\",\n      \"a\": \"p1\",\n      \"b\": \"p2\"\n    },\n    {\n      \"id\": \"right\",\n      \"type\": \"line\",\n      \"a\": \"p2\",\n      \"b\": \"p3\"\n    },\n    {\n      \"id\": \"top\",\n      \"type\": \"line\",\n      \"a\": \"p3\",\n      \"b\": \"p4\"\n    },\n    {\n      \"id\": \"left\",\n      \"type\": \"line\",\n      \"a\": \"p4\",\n      \"b\": \"p1\"\n    }\n  ],\n  \"constraints\": [\n    {\n      \"id\": \"origin\",\n      \"type\": \"fixed\",\n      \"refs\": [\n        \"p1\"\n      ]\n    },\n    {\n      \"id\": \"h1\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"bottom\"\n      ]\n    },\n    {\n      \"id\": \"h2\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"top\"\n      ]\n    },\n    {\n      \"id\": \"v1\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"left\"\n      ]\n    },\n    {\n      \"id\": \"v2\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"right\"\n      ]\n    },\n    {\n      \"id\": \"width\",\n      \"type\": \"distance\",\n      \"refs\": [\n        \"bottom\"\n      ],\n      \"value\": \"W\"\n    },\n    {\n      \"id\": \"depth\",\n      \"type\": \"distance\",\n      \"refs\": [\n        \"left\"\n      ],\n      \"value\": \"D\"\n    }\n  ],\n  \"height\": \"H\"\n}",
                    "parameters": "W = 600\nD = W / 2\nH = 18",
                    "name": "Shelf blank"
                  }
                },
                "example3": {
                  "summary": "Over-constrained: a second, contradicting width",
                  "value": {
                    "sketch": "{\n  \"points\": [\n    {\n      \"id\": \"p1\",\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"id\": \"p2\",\n      \"x\": 500,\n      \"y\": 10\n    },\n    {\n      \"id\": \"p3\",\n      \"x\": 480,\n      \"y\": 300\n    },\n    {\n      \"id\": \"p4\",\n      \"x\": -10,\n      \"y\": 290\n    }\n  ],\n  \"entities\": [\n    {\n      \"id\": \"bottom\",\n      \"type\": \"line\",\n      \"a\": \"p1\",\n      \"b\": \"p2\"\n    },\n    {\n      \"id\": \"right\",\n      \"type\": \"line\",\n      \"a\": \"p2\",\n      \"b\": \"p3\"\n    },\n    {\n      \"id\": \"top\",\n      \"type\": \"line\",\n      \"a\": \"p3\",\n      \"b\": \"p4\"\n    },\n    {\n      \"id\": \"left\",\n      \"type\": \"line\",\n      \"a\": \"p4\",\n      \"b\": \"p1\"\n    }\n  ],\n  \"constraints\": [\n    {\n      \"id\": \"origin\",\n      \"type\": \"fixed\",\n      \"refs\": [\n        \"p1\"\n      ]\n    },\n    {\n      \"id\": \"h1\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"bottom\"\n      ]\n    },\n    {\n      \"id\": \"h2\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"top\"\n      ]\n    },\n    {\n      \"id\": \"v1\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"left\"\n      ]\n    },\n    {\n      \"id\": \"v2\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"right\"\n      ]\n    },\n    {\n      \"id\": \"width\",\n      \"type\": \"distance\",\n      \"refs\": [\n        \"bottom\"\n      ],\n      \"value\": \"W\"\n    },\n    {\n      \"id\": \"depth\",\n      \"type\": \"distance\",\n      \"refs\": [\n        \"left\"\n      ],\n      \"value\": \"D\"\n    },\n    {\n      \"id\": \"width2\",\n      \"type\": \"distanceX\",\n      \"refs\": [\n        \"p1\",\n        \"p2\"\n      ],\n      \"value\": \"W + 10\"\n    }\n  ],\n  \"height\": \"H\"\n}",
                    "parameters": "W = 600\nD = W / 2\nH = 18",
                    "name": "Conflict check"
                  }
                },
                "example4": {
                  "summary": "L-shaped bracket profile with equal leg thickness",
                  "value": {
                    "sketch": "{\n  \"points\": [\n    {\n      \"id\": \"a\",\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"id\": \"b\",\n      \"x\": 160,\n      \"y\": 5\n    },\n    {\n      \"id\": \"c\",\n      \"x\": 150,\n      \"y\": 25\n    },\n    {\n      \"id\": \"d\",\n      \"x\": 30,\n      \"y\": 20\n    },\n    {\n      \"id\": \"e\",\n      \"x\": 25,\n      \"y\": 110\n    },\n    {\n      \"id\": \"f\",\n      \"x\": 5,\n      \"y\": 120\n    }\n  ],\n  \"entities\": [\n    {\n      \"id\": \"base\",\n      \"type\": \"line\",\n      \"a\": \"a\",\n      \"b\": \"b\"\n    },\n    {\n      \"id\": \"end\",\n      \"type\": \"line\",\n      \"a\": \"b\",\n      \"b\": \"c\"\n    },\n    {\n      \"id\": \"inner1\",\n      \"type\": \"line\",\n      \"a\": \"c\",\n      \"b\": \"d\"\n    },\n    {\n      \"id\": \"inner2\",\n      \"type\": \"line\",\n      \"a\": \"d\",\n      \"b\": \"e\"\n    },\n    {\n      \"id\": \"tip\",\n      \"type\": \"line\",\n      \"a\": \"e\",\n      \"b\": \"f\"\n    },\n    {\n      \"id\": \"back\",\n      \"type\": \"line\",\n      \"a\": \"f\",\n      \"b\": \"a\"\n    }\n  ],\n  \"constraints\": [\n    {\n      \"id\": \"origin\",\n      \"type\": \"fixed\",\n      \"refs\": [\n        \"a\"\n      ]\n    },\n    {\n      \"id\": \"h1\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"base\"\n      ]\n    },\n    {\n      \"id\": \"h2\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"inner1\"\n      ]\n    },\n    {\n      \"id\": \"h3\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"tip\"\n      ]\n    },\n    {\n      \"id\": \"v1\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"back\"\n      ]\n    },\n    {\n      \"id\": \"v2\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"end\"\n      ]\n    },\n    {\n      \"id\": \"v3\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"inner2\"\n      ]\n    },\n    {\n      \"id\": \"leg1\",\n      \"type\": \"distance\",\n      \"refs\": [\n        \"base\"\n      ],\n      \"value\": \"A\"\n    },\n    {\n      \"id\": \"leg2\",\n      \"type\": \"distance\",\n      \"refs\": [\n        \"back\"\n      ],\n      \"value\": \"B\"\n    },\n    {\n      \"id\": \"thick1\",\n      \"type\": \"distance\",\n      \"refs\": [\n        \"end\"\n      ],\n      \"value\": \"T\"\n    },\n    {\n      \"id\": \"thick2\",\n      \"type\": \"equal\",\n      \"refs\": [\n        \"tip\",\n        \"end\"\n      ]\n    }\n  ],\n  \"height\": \"L\"\n}",
                    "parameters": "A = 160\nB = 120\nT = 20\nL = 80",
                    "name": "Bracket"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KerfwiseSketchConstraintSolverResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "KerfwiseSketchConstraintSolverInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "sketch": {
            "title": "Sketch JSON (points, entities, constraints, optional height)",
            "description": "Points {id, x, y} in mm; entities line {a, b}, arc {c, a, b} counter-clockwise, circle {c, r}; constraints {id, type, refs, value}.",
            "type": "string",
            "maxLength": 200000,
            "default": "{\n  \"points\": [\n    {\n      \"id\": \"p1\",\n      \"x\": 0,\n      \"y\": 0\n    },\n    {\n      \"id\": \"p2\",\n      \"x\": 600,\n      \"y\": 0\n    },\n    {\n      \"id\": \"p3\",\n      \"x\": 600,\n      \"y\": 300\n    },\n    {\n      \"id\": \"p4\",\n      \"x\": 100,\n      \"y\": 400\n    },\n    {\n      \"id\": \"p5\",\n      \"x\": 0,\n      \"y\": 400\n    },\n    {\n      \"id\": \"c\",\n      \"x\": 90,\n      \"y\": 310\n    }\n  ],\n  \"entities\": [\n    {\n      \"id\": \"bottom\",\n      \"type\": \"line\",\n      \"a\": \"p1\",\n      \"b\": \"p2\"\n    },\n    {\n      \"id\": \"right\",\n      \"type\": \"line\",\n      \"a\": \"p2\",\n      \"b\": \"p3\"\n    },\n    {\n      \"id\": \"round\",\n      \"type\": \"arc\",\n      \"c\": \"c\",\n      \"a\": \"p3\",\n      \"b\": \"p4\"\n    },\n    {\n      \"id\": \"top\",\n      \"type\": \"line\",\n      \"a\": \"p4\",\n      \"b\": \"p5\"\n    },\n    {\n      \"id\": \"left\",\n      \"type\": \"line\",\n      \"a\": \"p5\",\n      \"b\": \"p1\"\n    }\n  ],\n  \"constraints\": [\n    {\n      \"id\": \"origin\",\n      \"type\": \"fixed\",\n      \"refs\": [\n        \"p1\"\n      ]\n    },\n    {\n      \"id\": \"h1\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"bottom\"\n      ]\n    },\n    {\n      \"id\": \"h2\",\n      \"type\": \"horizontal\",\n      \"refs\": [\n        \"top\"\n      ]\n    },\n    {\n      \"id\": \"v1\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"left\"\n      ]\n    },\n    {\n      \"id\": \"v2\",\n      \"type\": \"vertical\",\n      \"refs\": [\n        \"right\"\n      ]\n    },\n    {\n      \"id\": \"t1\",\n      \"type\": \"tangent\",\n      \"refs\": [\n        \"right\",\n        \"round\"\n      ]\n    },\n    {\n      \"id\": \"t2\",\n      \"type\": \"tangent\",\n      \"refs\": [\n        \"top\",\n        \"round\"\n      ]\n    },\n    {\n      \"id\": \"width\",\n      \"type\": \"distanceX\",\n      \"refs\": [\n        \"p1\",\n        \"p2\"\n      ],\n      \"value\": \"W\"\n    },\n    {\n      \"id\": \"depth\",\n      \"type\": \"distanceY\",\n      \"refs\": [\n        \"p1\",\n        \"p5\"\n      ],\n      \"value\": \"D\"\n    },\n    {\n      \"id\": \"fillet\",\n      \"type\": \"radius\",\n      \"refs\": [\n        \"round\"\n      ],\n      \"value\": \"R\"\n    }\n  ],\n  \"height\": \"T\"\n}"
          },
          "parameters": {
            "title": "Parameters (one “name = expression” per line)",
            "description": "Each expression may use the parameters above it, for example D = W / 2.",
            "type": "string",
            "maxLength": 40000,
            "default": "W = 600\nD = 400\nR = 100\nT = 19"
          },
          "name": {
            "title": "Solid name",
            "type": "string",
            "maxLength": 80,
            "default": "Sketch solid"
          }
        },
        "required": []
      },
      "KerfwiseSketchConstraintSolverResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "status",
              "message",
              "converged",
              "dof",
              "rank",
              "variables",
              "equations",
              "iterations",
              "maxResidual",
              "redundant",
              "conflicting",
              "points",
              "radii",
              "constraints",
              "parameters",
              "profile",
              "profileError",
              "height",
              "sketch",
              "project",
              "openInApp"
            ],
            "properties": {
              "status": {
                "enum": [
                  "fully-constrained",
                  "under-constrained",
                  "over-constrained",
                  "conflicting",
                  "not-solved"
                ]
              },
              "message": {
                "type": "string"
              },
              "converged": {
                "type": "boolean"
              },
              "dof": {
                "type": "integer",
                "description": "Remaining degrees of freedom = variables − rank of the constraint Jacobian."
              },
              "rank": {
                "type": "integer"
              },
              "variables": {
                "type": "integer"
              },
              "equations": {
                "type": "integer"
              },
              "iterations": {
                "type": "integer"
              },
              "maxResidual": {
                "type": "number"
              },
              "redundant": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "conflicting": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "points": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "x": {
                      "type": "number"
                    },
                    "y": {
                      "type": "number"
                    },
                    "state": {
                      "enum": [
                        "fixed",
                        "defined",
                        "free"
                      ]
                    }
                  }
                }
              },
              "radii": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "r": {
                      "type": "number"
                    },
                    "state": {
                      "enum": [
                        "defined",
                        "free"
                      ]
                    }
                  }
                }
              },
              "constraints": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "state": {
                      "enum": [
                        "ok",
                        "redundant",
                        "conflicting",
                        "unsatisfied"
                      ]
                    },
                    "residual": {
                      "type": "number"
                    },
                    "value": {
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  }
                }
              },
              "parameters": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "expression": {
                      "type": "string"
                    },
                    "value": {
                      "type": "number"
                    }
                  }
                }
              },
              "profile": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "array",
                  "minItems": 2,
                  "maxItems": 2,
                  "items": {
                    "type": "number"
                  }
                },
                "description": "Counter-clockwise footprint in mm (arcs tessellated at ≤ 15°)."
              },
              "profileError": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "height": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "sketch": {
                "type": "object",
                "description": "The sketch with solved coordinates (start of the next solve)."
              },
              "project": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "required": [
                  "format",
                  "version",
                  "name",
                  "units",
                  "grid",
                  "snap",
                  "objects",
                  "scenes"
                ],
                "properties": {
                  "format": {
                    "const": "kerfwise-form"
                  },
                  "version": {
                    "const": 1
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "units": {
                    "enum": [
                      "mm",
                      "in"
                    ]
                  },
                  "grid": {
                    "type": "number",
                    "minimum": 0.1,
                    "maximum": 10000
                  },
                  "snap": {
                    "type": "boolean"
                  },
                  "objects": {
                    "type": "array",
                    "maxItems": 128,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "id",
                        "name",
                        "tag",
                        "color",
                        "profile",
                        "height",
                        "x",
                        "y",
                        "z",
                        "rotation",
                        "visible",
                        "locked"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^[-a-zA-Z0-9_]{1,64}$"
                        },
                        "name": {
                          "type": "string",
                          "maxLength": 80
                        },
                        "tag": {
                          "type": "string",
                          "maxLength": 40
                        },
                        "color": {
                          "type": "string",
                          "pattern": "^#[a-fA-F0-9]{6}$"
                        },
                        "profile": {
                          "type": "array",
                          "minItems": 3,
                          "maxItems": 64,
                          "items": {
                            "type": "array",
                            "minItems": 2,
                            "maxItems": 2,
                            "items": {
                              "type": "number",
                              "minimum": -100000,
                              "maximum": 100000
                            }
                          }
                        },
                        "height": {
                          "type": "number",
                          "minimum": 1,
                          "maximum": 100000
                        },
                        "x": {
                          "type": "number",
                          "minimum": -100000,
                          "maximum": 100000
                        },
                        "y": {
                          "type": "number",
                          "minimum": -100000,
                          "maximum": 100000
                        },
                        "z": {
                          "type": "number",
                          "minimum": -100000,
                          "maximum": 100000
                        },
                        "rotation": {
                          "type": "number"
                        },
                        "tiltX": {
                          "type": "number",
                          "minimum": -180,
                          "maximum": 180
                        },
                        "tiltY": {
                          "type": "number",
                          "minimum": -180,
                          "maximum": 180
                        },
                        "visible": {
                          "type": "boolean"
                        },
                        "locked": {
                          "type": "boolean"
                        },
                        "orthogonal": {
                          "type": "string",
                          "maxLength": 12000
                        },
                        "sketch": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "points",
                            "entities",
                            "constraints"
                          ],
                          "description": "2-D constraint sketch in millimetres (Y up). Arcs run counter-clockwise from a to b around c. Values of distance, radius and diameter constraints are millimetres, angles degrees; both may be expressions in the parameters.",
                          "properties": {
                            "points": {
                              "type": "array",
                              "maxItems": 200,
                              "items": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "id",
                                  "x",
                                  "y"
                                ],
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,31}$"
                                  },
                                  "x": {
                                    "type": "number",
                                    "minimum": -100000,
                                    "maximum": 100000
                                  },
                                  "y": {
                                    "type": "number",
                                    "minimum": -100000,
                                    "maximum": 100000
                                  }
                                }
                              }
                            },
                            "entities": {
                              "type": "array",
                              "maxItems": 200,
                              "items": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "id",
                                  "type"
                                ],
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,31}$"
                                  },
                                  "type": {
                                    "enum": [
                                      "line",
                                      "arc",
                                      "circle"
                                    ]
                                  },
                                  "a": {
                                    "type": "string",
                                    "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,31}$"
                                  },
                                  "b": {
                                    "type": "string",
                                    "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,31}$"
                                  },
                                  "c": {
                                    "type": "string",
                                    "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,31}$"
                                  },
                                  "r": {
                                    "type": "number",
                                    "exclusiveMinimum": 0,
                                    "maximum": 100000
                                  },
                                  "construction": {
                                    "type": "boolean"
                                  }
                                }
                              }
                            },
                            "constraints": {
                              "type": "array",
                              "maxItems": 400,
                              "items": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "id",
                                  "type",
                                  "refs"
                                ],
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,31}$"
                                  },
                                  "type": {
                                    "enum": [
                                      "coincident",
                                      "horizontal",
                                      "vertical",
                                      "parallel",
                                      "perpendicular",
                                      "tangent",
                                      "equal",
                                      "distance",
                                      "distanceX",
                                      "distanceY",
                                      "angle",
                                      "radius",
                                      "diameter",
                                      "pointOnLine",
                                      "pointOnCircle",
                                      "midpoint",
                                      "symmetric",
                                      "fixed"
                                    ]
                                  },
                                  "refs": {
                                    "type": "array",
                                    "minItems": 1,
                                    "maxItems": 3,
                                    "items": {
                                      "type": "string",
                                      "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,31}$"
                                    }
                                  },
                                  "value": {
                                    "type": "string",
                                    "maxLength": 500
                                  }
                                }
                              }
                            },
                            "height": {
                              "type": "string",
                              "maxLength": 500,
                              "description": "Extrusion height in mm: a number or an expression in the parameters."
                            }
                          }
                        }
                      }
                    }
                  },
                  "scenes": {
                    "type": "array",
                    "maxItems": 8,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "name",
                        "camera"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 60
                        },
                        "camera": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "azimuth",
                            "elevation",
                            "span",
                            "target"
                          ],
                          "properties": {
                            "azimuth": {
                              "type": "number"
                            },
                            "elevation": {
                              "type": "number",
                              "minimum": 5,
                              "maximum": 90
                            },
                            "span": {
                              "type": "number",
                              "minimum": 10,
                              "maximum": 1000000
                            },
                            "target": {
                              "type": "array",
                              "minItems": 3,
                              "maxItems": 3,
                              "items": {
                                "type": "number"
                              }
                            }
                          }
                        }
                      }
                    }
                  },
                  "parameters": {
                    "type": "array",
                    "maxItems": 64,
                    "description": "Named parameters in order; each expression may use the parameters above it (Proofline expression syntax).",
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "name",
                        "expression"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "pattern": "^[A-Za-z_][A-Za-z_0-9]*$"
                        },
                        "expression": {
                          "type": "string",
                          "maxLength": 500
                        }
                      }
                    }
                  }
                }
              },
              "openInApp": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}