diff --git a/docs/kcl.json b/docs/kcl.json index d373cebbe..28e1df233 100644 --- a/docs/kcl.json +++ b/docs/kcl.json @@ -17017,5 +17017,1998 @@ }, "unpublished": false, "deprecated": false + }, + { + "name": "arc", + "summary": "Draw an arc.", + "description": "", + "tags": [], + "args": [ + { + "name": "data", + "type_": "ArcData", + "schema": { + "description": "Data to draw an arc.", + "anyOf": [ + { + "description": "Angles and radius with a tag.", + "type": "object", + "required": [ + "angle_end", + "angle_start", + "radius", + "tag" + ], + "properties": { + "angle_end": { + "description": "The end angle.", + "type": "number", + "format": "double" + }, + "angle_start": { + "description": "The start angle.", + "type": "number", + "format": "double" + }, + "radius": { + "description": "The radius.", + "type": "number", + "format": "double" + }, + "tag": { + "description": "The tag.", + "type": "string" + } + } + }, + { + "description": "Angles and radius.", + "type": "object", + "required": [ + "angle_end", + "angle_start", + "radius" + ], + "properties": { + "angle_end": { + "description": "The end angle.", + "type": "number", + "format": "double" + }, + "angle_start": { + "description": "The start angle.", + "type": "number", + "format": "double" + }, + "radius": { + "description": "The radius.", + "type": "number", + "format": "double" + } + } + }, + { + "description": "Center, to and radius with a tag.", + "type": "object", + "required": [ + "center", + "radius", + "tag", + "to" + ], + "properties": { + "center": { + "description": "The center.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "radius": { + "description": "The radius.", + "type": "number", + "format": "double" + }, + "tag": { + "description": "The tag.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + { + "description": "Center, to and radius.", + "type": "object", + "required": [ + "center", + "radius", + "to" + ], + "properties": { + "center": { + "description": "The center.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "radius": { + "description": "The radius.", + "type": "number", + "format": "double" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + } + } + } + ] + }, + "required": true + }, + { + "name": "sketch_group", + "type_": "SketchGroup", + "schema": { + "description": "A sketch group is a collection of paths.", + "type": "object", + "required": [ + "__meta", + "id", + "position", + "rotation", + "start", + "value" + ], + "properties": { + "__meta": { + "description": "Metadata.", + "type": "array", + "items": { + "description": "Metadata.", + "type": "object", + "required": [ + "sourceRange" + ], + "properties": { + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + } + }, + "id": { + "description": "The id of the sketch group.", + "type": "string", + "format": "uuid" + }, + "position": { + "description": "The position of the sketch group.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 3, + "minItems": 3 + }, + "rotation": { + "description": "The rotation of the sketch group.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 4, + "minItems": 4 + }, + "start": { + "description": "The starting path.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "value": { + "description": "The paths in the sketch group.", + "type": "array", + "items": { + "description": "A path.", + "oneOf": [ + { + "description": "A path that goes to a point.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "toPoint" + ] + } + } + }, + { + "description": "A path that is horizontal.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type", + "x" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "horizontal" + ] + }, + "x": { + "description": "The x coordinate.", + "type": "number", + "format": "double" + } + } + }, + { + "description": "An angled line to.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "angledLineTo" + ] + }, + "x": { + "description": "The x coordinate.", + "type": "number", + "format": "double", + "nullable": true + }, + "y": { + "description": "The y coordinate.", + "type": "number", + "format": "double", + "nullable": true + } + } + }, + { + "description": "A base path.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "base" + ] + } + } + } + ] + } + } + } + }, + "required": true + } + ], + "return_value": { + "name": "", + "type_": "SketchGroup", + "schema": { + "description": "A sketch group is a collection of paths.", + "type": "object", + "required": [ + "__meta", + "id", + "position", + "rotation", + "start", + "value" + ], + "properties": { + "__meta": { + "description": "Metadata.", + "type": "array", + "items": { + "description": "Metadata.", + "type": "object", + "required": [ + "sourceRange" + ], + "properties": { + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + } + }, + "id": { + "description": "The id of the sketch group.", + "type": "string", + "format": "uuid" + }, + "position": { + "description": "The position of the sketch group.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 3, + "minItems": 3 + }, + "rotation": { + "description": "The rotation of the sketch group.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 4, + "minItems": 4 + }, + "start": { + "description": "The starting path.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "value": { + "description": "The paths in the sketch group.", + "type": "array", + "items": { + "description": "A path.", + "oneOf": [ + { + "description": "A path that goes to a point.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "toPoint" + ] + } + } + }, + { + "description": "A path that is horizontal.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type", + "x" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "horizontal" + ] + }, + "x": { + "description": "The x coordinate.", + "type": "number", + "format": "double" + } + } + }, + { + "description": "An angled line to.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "angledLineTo" + ] + }, + "x": { + "description": "The x coordinate.", + "type": "number", + "format": "double", + "nullable": true + }, + "y": { + "description": "The y coordinate.", + "type": "number", + "format": "double", + "nullable": true + } + } + }, + { + "description": "A base path.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "base" + ] + } + } + } + ] + } + } + } + }, + "required": true + }, + "unpublished": false, + "deprecated": false + }, + { + "name": "bezierCurve", + "summary": "Draw a bezier curve.", + "description": "", + "tags": [], + "args": [ + { + "name": "data", + "type_": "BezierData", + "schema": { + "description": "Data to draw a bezier curve.", + "anyOf": [ + { + "description": "Points with a tag.", + "type": "object", + "required": [ + "control1", + "control2", + "tag", + "to" + ], + "properties": { + "control1": { + "description": "The first control point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "control2": { + "description": "The second control point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "tag": { + "description": "The tag.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + { + "description": "Points.", + "type": "object", + "required": [ + "control1", + "control2", + "to" + ], + "properties": { + "control1": { + "description": "The first control point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "control2": { + "description": "The second control point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + } + } + } + ] + }, + "required": true + }, + { + "name": "sketch_group", + "type_": "SketchGroup", + "schema": { + "description": "A sketch group is a collection of paths.", + "type": "object", + "required": [ + "__meta", + "id", + "position", + "rotation", + "start", + "value" + ], + "properties": { + "__meta": { + "description": "Metadata.", + "type": "array", + "items": { + "description": "Metadata.", + "type": "object", + "required": [ + "sourceRange" + ], + "properties": { + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + } + }, + "id": { + "description": "The id of the sketch group.", + "type": "string", + "format": "uuid" + }, + "position": { + "description": "The position of the sketch group.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 3, + "minItems": 3 + }, + "rotation": { + "description": "The rotation of the sketch group.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 4, + "minItems": 4 + }, + "start": { + "description": "The starting path.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "value": { + "description": "The paths in the sketch group.", + "type": "array", + "items": { + "description": "A path.", + "oneOf": [ + { + "description": "A path that goes to a point.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "toPoint" + ] + } + } + }, + { + "description": "A path that is horizontal.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type", + "x" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "horizontal" + ] + }, + "x": { + "description": "The x coordinate.", + "type": "number", + "format": "double" + } + } + }, + { + "description": "An angled line to.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "angledLineTo" + ] + }, + "x": { + "description": "The x coordinate.", + "type": "number", + "format": "double", + "nullable": true + }, + "y": { + "description": "The y coordinate.", + "type": "number", + "format": "double", + "nullable": true + } + } + }, + { + "description": "A base path.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "base" + ] + } + } + } + ] + } + } + } + }, + "required": true + } + ], + "return_value": { + "name": "", + "type_": "SketchGroup", + "schema": { + "description": "A sketch group is a collection of paths.", + "type": "object", + "required": [ + "__meta", + "id", + "position", + "rotation", + "start", + "value" + ], + "properties": { + "__meta": { + "description": "Metadata.", + "type": "array", + "items": { + "description": "Metadata.", + "type": "object", + "required": [ + "sourceRange" + ], + "properties": { + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + } + }, + "id": { + "description": "The id of the sketch group.", + "type": "string", + "format": "uuid" + }, + "position": { + "description": "The position of the sketch group.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 3, + "minItems": 3 + }, + "rotation": { + "description": "The rotation of the sketch group.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 4, + "minItems": 4 + }, + "start": { + "description": "The starting path.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "value": { + "description": "The paths in the sketch group.", + "type": "array", + "items": { + "description": "A path.", + "oneOf": [ + { + "description": "A path that goes to a point.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "toPoint" + ] + } + } + }, + { + "description": "A path that is horizontal.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type", + "x" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "horizontal" + ] + }, + "x": { + "description": "The x coordinate.", + "type": "number", + "format": "double" + } + } + }, + { + "description": "An angled line to.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "angledLineTo" + ] + }, + "x": { + "description": "The x coordinate.", + "type": "number", + "format": "double", + "nullable": true + }, + "y": { + "description": "The y coordinate.", + "type": "number", + "format": "double", + "nullable": true + } + } + }, + { + "description": "A base path.", + "type": "object", + "required": [ + "__geoMeta", + "from", + "name", + "to", + "type" + ], + "properties": { + "__geoMeta": { + "description": "Metadata.", + "type": "object", + "required": [ + "id", + "sourceRange" + ], + "properties": { + "id": { + "description": "The id of the geometry.", + "type": "string", + "format": "uuid" + }, + "sourceRange": { + "description": "The source range.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "maxItems": 2, + "minItems": 2 + } + } + }, + "from": { + "description": "The from point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "name": { + "description": "The name of the path.", + "type": "string" + }, + "to": { + "description": "The to point.", + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "maxItems": 2, + "minItems": 2 + }, + "type": { + "type": "string", + "enum": [ + "base" + ] + } + } + } + ] + } + } + } + }, + "required": true + }, + "unpublished": false, + "deprecated": false } ] \ No newline at end of file diff --git a/docs/kcl.md b/docs/kcl.md index 2d93ebccb..eaa64b37e 100644 --- a/docs/kcl.md +++ b/docs/kcl.md @@ -33,6 +33,8 @@ * [`angledLineThatIntersects`](#angledLineThatIntersects) * [`startSketchAt`](#startSketchAt) * [`close`](#close) + * [`arc`](#arc) + * [`bezierCurve`](#bezierCurve) ## Functions @@ -3046,3 +3048,351 @@ close(sketch_group: SketchGroup) -> SketchGroup +### arc + +Draw an arc. + + + +``` +arc(data: ArcData, sketch_group: SketchGroup) -> SketchGroup +``` + +#### Arguments + +* `data`: `ArcData` - Data to draw an arc. +``` +{ + // The end angle. + "angle_end": number, + // The start angle. + "angle_start": number, + // The radius. + "radius": number, + // The tag. + "tag": string, +} | +{ + // The end angle. + "angle_end": number, + // The start angle. + "angle_start": number, + // The radius. + "radius": number, +} | +{ + // The center. + "center": [number], + // The radius. + "radius": number, + // The tag. + "tag": string, + // The to point. + "to": [number], +} | +{ + // The center. + "center": [number], + // The radius. + "radius": number, + // The to point. + "to": [number], +} +``` +* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. +``` +{ + // The id of the sketch group. + "id": uuid, + // The position of the sketch group. + "position": [number], + // The rotation of the sketch group. + "rotation": [number], + // The starting path. + "start": { + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], +}, + // The paths in the sketch group. + "value": [{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, + // The x coordinate. + "x": number, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, + // The x coordinate. + "x": number, + // The y coordinate. + "y": number, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, +}], +} +``` + +#### Returns + +* `SketchGroup` - A sketch group is a collection of paths. +``` +{ + // The id of the sketch group. + "id": uuid, + // The position of the sketch group. + "position": [number], + // The rotation of the sketch group. + "rotation": [number], + // The starting path. + "start": { + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], +}, + // The paths in the sketch group. + "value": [{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, + // The x coordinate. + "x": number, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, + // The x coordinate. + "x": number, + // The y coordinate. + "y": number, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, +}], +} +``` + + + +### bezierCurve + +Draw a bezier curve. + + + +``` +bezierCurve(data: BezierData, sketch_group: SketchGroup) -> SketchGroup +``` + +#### Arguments + +* `data`: `BezierData` - Data to draw a bezier curve. +``` +{ + // The first control point. + "control1": [number], + // The second control point. + "control2": [number], + // The tag. + "tag": string, + // The to point. + "to": [number], +} | +{ + // The first control point. + "control1": [number], + // The second control point. + "control2": [number], + // The to point. + "to": [number], +} +``` +* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. +``` +{ + // The id of the sketch group. + "id": uuid, + // The position of the sketch group. + "position": [number], + // The rotation of the sketch group. + "rotation": [number], + // The starting path. + "start": { + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], +}, + // The paths in the sketch group. + "value": [{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, + // The x coordinate. + "x": number, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, + // The x coordinate. + "x": number, + // The y coordinate. + "y": number, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, +}], +} +``` + +#### Returns + +* `SketchGroup` - A sketch group is a collection of paths. +``` +{ + // The id of the sketch group. + "id": uuid, + // The position of the sketch group. + "position": [number], + // The rotation of the sketch group. + "rotation": [number], + // The starting path. + "start": { + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], +}, + // The paths in the sketch group. + "value": [{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, + // The x coordinate. + "x": number, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, + // The x coordinate. + "x": number, + // The y coordinate. + "y": number, +} | +{ + // The from point. + "from": [number], + // The name of the path. + "name": string, + // The to point. + "to": [number], + "type": string, +}], +} +``` + + + diff --git a/package.json b/package.json index a23b891ee..f164ea59c 100644 --- a/package.json +++ b/package.json @@ -57,13 +57,13 @@ "build:both:local": "yarn build:wasm && vite build", "test": "vitest --mode development", "test:nowatch": "vitest run --mode development", - "test:rust": "(cd src/wasm-lib && cargo test && cargo clippy)", + "test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests)", "test:cov": "vitest run --coverage --mode development", "simpleserver:ci": "http-server ./public --cors -p 3000 &", "simpleserver": "http-server ./public --cors -p 3000", "fmt": "prettier --write ./src", "fmt-check": "prettier --check ./src", - "build:wasm": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test --all) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt && yarn remove-importmeta", + "build:wasm": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt && yarn remove-importmeta", "remove-importmeta": "sed -i 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"", "wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings", "lint": "eslint --fix src", diff --git a/src/wasm-lib/Cargo.lock b/src/wasm-lib/Cargo.lock index 0340dcf51..1f6653967 100644 --- a/src/wasm-lib/Cargo.lock +++ b/src/wasm-lib/Cargo.lock @@ -948,7 +948,7 @@ dependencies = [ [[package]] name = "kcl-lib" -version = "0.1.3" +version = "0.1.10" dependencies = [ "anyhow", "bson", diff --git a/src/wasm-lib/kcl/Cargo.toml b/src/wasm-lib/kcl/Cargo.toml index d41290727..d43d0c669 100644 --- a/src/wasm-lib/kcl/Cargo.toml +++ b/src/wasm-lib/kcl/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kcl-lib" description = "KittyCAD Language" -version = "0.1.3" +version = "0.1.10" edition = "2021" license = "MIT" @@ -33,6 +33,10 @@ reqwest = { version = "0.11.20", default-features = false } tokio = { version = "1.32.0", features = ["full"] } tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] } +[features] +default = ["engine"] +engine = [] + [profile.release] panic = "abort" debug = true diff --git a/src/wasm-lib/kcl/src/abstract_syntax_tree_types.rs b/src/wasm-lib/kcl/src/abstract_syntax_tree_types.rs index 3a771d94f..d4b9931fb 100644 --- a/src/wasm-lib/kcl/src/abstract_syntax_tree_types.rs +++ b/src/wasm-lib/kcl/src/abstract_syntax_tree_types.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; +use parse_display::{Display, FromStr}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json::Map; @@ -383,11 +384,22 @@ pub struct VariableDeclaration { pub start: usize, pub end: usize, pub declarations: Vec, - pub kind: String, // Change to enum if there are specific values + pub kind: VariableKind, // Change to enum if there are specific values } impl_value_meta!(VariableDeclaration); +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display)] +#[ts(export)] +#[serde(rename_all = "snake_case")] +#[display(style = "snake_case")] +pub enum VariableKind { + Let, + Const, + Fn, + Var, +} + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[ts(export)] #[serde(tag = "type")] diff --git a/src/wasm-lib/kcl/src/docs.rs b/src/wasm-lib/kcl/src/docs.rs index 45f987f8f..9057eb6b1 100644 --- a/src/wasm-lib/kcl/src/docs.rs +++ b/src/wasm-lib/kcl/src/docs.rs @@ -44,6 +44,11 @@ impl StdLibFnArg { get_type_string_from_schema(&self.schema) } + #[allow(dead_code)] + pub fn get_autocomplete_string(&self) -> Result { + get_autocomplete_string_from_schema(&self.schema) + } + #[allow(dead_code)] pub fn description(&self) -> Option { get_description_string_from_schema(&self.schema) @@ -93,9 +98,24 @@ pub trait StdLibFn { deprecated: self.deprecated(), }) } + + fn fn_signature(&self) -> String { + let mut signature = String::new(); + signature.push_str(&format!("{}(", self.name())); + for (i, arg) in self.args().iter().enumerate() { + if i > 0 { + signature.push_str(", "); + } + signature.push_str(&format!("{}: {}", arg.name, arg.type_)); + } + signature.push_str(") -> "); + signature.push_str(&self.return_value().type_); + + signature + } } -fn get_description_string_from_schema(schema: &schemars::schema::Schema) -> Option { +pub fn get_description_string_from_schema(schema: &schemars::schema::Schema) -> Option { if let schemars::schema::Schema::Object(o) = schema { if let Some(metadata) = &o.metadata { if let Some(description) = &metadata.description { @@ -107,7 +127,7 @@ fn get_description_string_from_schema(schema: &schemars::schema::Schema) -> Opti None } -fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<(String, bool)> { +pub fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<(String, bool)> { match schema { schemars::schema::Schema::Object(o) => { if let Some(format) = &o.format { @@ -187,3 +207,78 @@ fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<(Str schemars::schema::Schema::Bool(_) => Ok((Primitive::Bool.to_string(), false)), } } + +pub fn get_autocomplete_string_from_schema(schema: &schemars::schema::Schema) -> Result { + match schema { + schemars::schema::Schema::Object(o) => { + if let Some(format) = &o.format { + if format == "uuid" { + return Ok(Primitive::Uuid.to_string()); + } else if format == "double" || format == "uint" { + return Ok(Primitive::Number.to_string()); + } else { + anyhow::bail!("unknown format: {}", format); + } + } + + if let Some(obj_val) = &o.object { + let mut fn_docs = String::new(); + fn_docs.push_str("{\n"); + // Let's print out the object's properties. + for (prop_name, prop) in obj_val.properties.iter() { + if prop_name.starts_with('_') { + continue; + } + + if let Some(description) = get_description_string_from_schema(prop) { + fn_docs.push_str(&format!("\t// {}\n", description)); + } + fn_docs.push_str(&format!( + "\t\"{}\": {},\n", + prop_name, + get_autocomplete_string_from_schema(prop)?, + )); + } + + fn_docs.push('}'); + + return Ok(fn_docs); + } + + if let Some(array_val) = &o.array { + if let Some(schemars::schema::SingleOrVec::Single(items)) = &array_val.items { + // Let's print out the object's properties. + return Ok(format!("[{}]", get_autocomplete_string_from_schema(items)?)); + } else if let Some(items) = &array_val.contains { + return Ok(format!("[{}]", get_autocomplete_string_from_schema(items)?)); + } + } + + if let Some(subschemas) = &o.subschemas { + let mut fn_docs = String::new(); + if let Some(items) = &subschemas.one_of { + if let Some(item) = items.iter().next() { + // Let's print out the object's properties. + fn_docs.push_str(&get_autocomplete_string_from_schema(item)?); + } + } else if let Some(items) = &subschemas.any_of { + if let Some(item) = items.iter().next() { + // Let's print out the object's properties. + fn_docs.push_str(&get_autocomplete_string_from_schema(item)?); + } + } else { + anyhow::bail!("unknown subschemas: {:#?}", subschemas); + } + + return Ok(fn_docs); + } + + if let Some(schemars::schema::SingleOrVec::Single(_string)) = &o.instance_type { + return Ok(Primitive::String.to_string()); + } + + anyhow::bail!("unknown type: {:#?}", o) + } + schemars::schema::Schema::Bool(_) => Ok(Primitive::Bool.to_string()), + } +} diff --git a/src/wasm-lib/kcl/src/engine/mod.rs b/src/wasm-lib/kcl/src/engine/mod.rs index 1a2473259..b5a976b04 100644 --- a/src/wasm-lib/kcl/src/engine/mod.rs +++ b/src/wasm-lib/kcl/src/engine/mod.rs @@ -4,16 +4,20 @@ use wasm_bindgen::prelude::*; #[cfg(not(target_arch = "wasm32"))] #[cfg(not(test))] +#[cfg(feature = "engine")] pub mod conn; #[cfg(not(target_arch = "wasm32"))] #[cfg(not(test))] +#[cfg(feature = "engine")] pub use conn::EngineConnection; #[cfg(target_arch = "wasm32")] #[cfg(not(test))] +#[cfg(feature = "engine")] pub mod conn_wasm; #[cfg(target_arch = "wasm32")] #[cfg(not(test))] +#[cfg(feature = "engine")] pub use conn_wasm::EngineConnection; #[cfg(test)] @@ -21,6 +25,13 @@ pub mod conn_mock; #[cfg(test)] pub use conn_mock::EngineConnection; +#[cfg(not(feature = "engine"))] +#[cfg(not(test))] +pub mod conn_mock; +#[cfg(not(feature = "engine"))] +#[cfg(not(test))] +pub use conn_mock::EngineConnection; + use crate::executor::SourceRange; #[derive(Debug)] @@ -33,6 +44,7 @@ pub struct EngineManager { impl EngineManager { #[cfg(target_arch = "wasm32")] #[cfg(not(test))] + #[cfg(feature = "engine")] #[wasm_bindgen(constructor)] pub async fn new(manager: conn_wasm::EngineCommandManager) -> EngineManager { EngineManager { diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index 9f83b11fe..359893a5a 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -298,12 +298,24 @@ impl From<[f64; 2]> for Point2d { } } +impl From<&[f64; 2]> for Point2d { + fn from(p: &[f64; 2]) -> Self { + Self { x: p[0], y: p[1] } + } +} + impl From for [f64; 2] { fn from(p: Point2d) -> Self { [p.x, p.y] } } +impl From for kittycad::types::Point2D { + fn from(p: Point2d) -> Self { + Self { x: p.x, y: p.y } + } +} + #[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)] #[ts(export)] pub struct Point3d { diff --git a/src/wasm-lib/kcl/src/lib.rs b/src/wasm-lib/kcl/src/lib.rs index ca268fda8..406482a01 100644 --- a/src/wasm-lib/kcl/src/lib.rs +++ b/src/wasm-lib/kcl/src/lib.rs @@ -1,5 +1,5 @@ pub mod abstract_syntax_tree_types; -mod docs; +pub mod docs; pub mod engine; pub mod errors; pub mod executor; diff --git a/src/wasm-lib/kcl/src/math_parser.rs b/src/wasm-lib/kcl/src/math_parser.rs index 1f36625fa..6214a02c0 100644 --- a/src/wasm-lib/kcl/src/math_parser.rs +++ b/src/wasm-lib/kcl/src/math_parser.rs @@ -315,23 +315,25 @@ fn build_tree( }))); return build_tree(&reverse_polish_notation_tokens[1..], new_stack); } else if current_token.token_type == TokenType::Word { - if reverse_polish_notation_tokens[1].token_type == TokenType::Brace - && reverse_polish_notation_tokens[1].value == "(" - { - let closing_brace = find_closing_brace(reverse_polish_notation_tokens, 1, 0, "")?; + if reverse_polish_notation_tokens.len() > 1 { + if reverse_polish_notation_tokens[1].token_type == TokenType::Brace + && reverse_polish_notation_tokens[1].value == "(" + { + let closing_brace = find_closing_brace(reverse_polish_notation_tokens, 1, 0, "")?; + let mut new_stack = stack; + new_stack.push(MathExpression::CallExpression(Box::new( + make_call_expression(reverse_polish_notation_tokens, 0)?.expression, + ))); + return build_tree(&reverse_polish_notation_tokens[closing_brace + 1..], new_stack); + } let mut new_stack = stack; - new_stack.push(MathExpression::CallExpression(Box::new( - make_call_expression(reverse_polish_notation_tokens, 0)?.expression, - ))); - return build_tree(&reverse_polish_notation_tokens[closing_brace + 1..], new_stack); + new_stack.push(MathExpression::Identifier(Box::new(Identifier { + name: current_token.value.clone(), + start: current_token.start, + end: current_token.end, + }))); + return build_tree(&reverse_polish_notation_tokens[1..], new_stack); } - let mut new_stack = stack; - new_stack.push(MathExpression::Identifier(Box::new(Identifier { - name: current_token.value.clone(), - start: current_token.start, - end: current_token.end, - }))); - return build_tree(&reverse_polish_notation_tokens[1..], new_stack); } else if current_token.token_type == TokenType::Brace && current_token.value == "(" { let mut new_stack = stack; new_stack.push(MathExpression::ParenthesisToken(Box::new(ParenthesisToken { @@ -424,6 +426,14 @@ fn build_tree( new_stack.push(expression); return build_tree(&reverse_polish_notation_tokens[1..], new_stack); } + + if stack.len() < 2 { + return Err(KclError::Syntax(KclErrorDetails { + source_ranges: vec![current_token.into()], + message: "unexpected end of expression".to_string(), + })); + } + let left: (BinaryPart, usize) = match &stack[stack.len() - 2] { MathExpression::ExtendedBinaryExpression(bin_exp) => ( BinaryPart::BinaryExpression(Box::new(BinaryExpression { diff --git a/src/wasm-lib/kcl/src/parser.rs b/src/wasm-lib/kcl/src/parser.rs index 29aa8c1d6..017d380bb 100644 --- a/src/wasm-lib/kcl/src/parser.rs +++ b/src/wasm-lib/kcl/src/parser.rs @@ -1,11 +1,11 @@ -use std::collections::HashMap; +use std::{collections::HashMap, str::FromStr}; use crate::{ abstract_syntax_tree_types::{ ArrayExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, ExpressionStatement, FunctionExpression, Identifier, Literal, LiteralIdentifier, MemberExpression, MemberObject, NoneCodeMeta, NoneCodeNode, ObjectExpression, ObjectKeyInfo, ObjectProperty, PipeExpression, PipeSubstitution, Program, - ReturnStatement, UnaryExpression, Value, VariableDeclaration, VariableDeclarator, + ReturnStatement, UnaryExpression, Value, VariableDeclaration, VariableDeclarator, VariableKind, }, errors::{KclError, KclErrorDetails}, math_parser::parse_expression, @@ -145,7 +145,12 @@ pub fn find_closing_brace( search_opening_brace: &str, ) -> Result { let closing_brace_map: HashMap<&str, &str> = [("(", ")"), ("{", "}"), ("[", "]")].iter().cloned().collect(); - let current_token = &tokens[index]; + let Some(current_token) = tokens.get(index) else { + return Err(KclError::Syntax(KclErrorDetails { + source_ranges: vec![tokens.last().unwrap().into()], + message: "unexpected end".to_string(), + })); + }; let mut search_opening_brace = search_opening_brace; let is_first_call = search_opening_brace.is_empty() && brace_count == 0; if is_first_call { @@ -966,13 +971,12 @@ fn make_variable_declaration(tokens: &[Token], index: usize) -> Result close(%)"#, ); let result = make_variable_declaration(&tokens, 0).unwrap(); - assert_eq!(result.declaration.kind, "const"); + assert_eq!(result.declaration.kind.to_string(), "const"); assert_eq!(result.declaration.declarations.len(), 1); assert_eq!(result.declaration.declarations[0].id.name, "yo"); let declaration = result.declaration.declarations[0].clone(); diff --git a/src/wasm-lib/kcl/src/std/mod.rs b/src/wasm-lib/kcl/src/std/mod.rs index 78b3b4f62..96a756513 100644 --- a/src/wasm-lib/kcl/src/std/mod.rs +++ b/src/wasm-lib/kcl/src/std/mod.rs @@ -27,8 +27,7 @@ pub type FnMap = HashMap; pub type StdFn = fn(&mut Args) -> Result; pub struct StdLib { - #[allow(dead_code)] - internal_fn_names: Vec>, + pub internal_fn_names: Vec>, pub fns: FnMap, } @@ -64,6 +63,8 @@ impl StdLib { Box::new(crate::std::sketch::AngledLineThatIntersects), Box::new(crate::std::sketch::StartSketchAt), Box::new(crate::std::sketch::Close), + Box::new(crate::std::sketch::Arc), + Box::new(crate::std::sketch::BezierCurve), ]; let mut fns = HashMap::new(); @@ -536,15 +537,8 @@ mod tests { fn_docs.push_str(&format!("{}\n\n", internal_fn.description())); fn_docs.push_str("```\n"); - fn_docs.push_str(&format!("{}(", internal_fn.name())); - for (i, arg) in internal_fn.args().iter().enumerate() { - if i > 0 { - fn_docs.push_str(", "); - } - fn_docs.push_str(&format!("{}: {}", arg.name, arg.type_)); - } - fn_docs.push_str(") -> "); - fn_docs.push_str(&internal_fn.return_value().type_); + let signature = internal_fn.fn_signature(); + fn_docs.push_str(&signature); fn_docs.push_str("\n```\n\n"); fn_docs.push_str("#### Arguments\n\n"); diff --git a/src/wasm-lib/kcl/src/std/sketch.rs b/src/wasm-lib/kcl/src/std/sketch.rs index 7da891465..b2f0b7460 100644 --- a/src/wasm-lib/kcl/src/std/sketch.rs +++ b/src/wasm-lib/kcl/src/std/sketch.rs @@ -10,7 +10,7 @@ use crate::{ errors::{KclError, KclErrorDetails}, executor::{BasePath, GeoMeta, MemoryItem, Path, Point2d, Position, Rotation, SketchGroup}, std::{ - utils::{get_x_component, get_y_component, intersection_with_parallel_line}, + utils::{arc_angles, arc_center_and_end, get_x_component, get_y_component, intersection_with_parallel_line}, Args, }, }; @@ -43,7 +43,7 @@ pub fn line_to(args: &mut Args) -> Result { #[stdlib { name = "lineTo", }] -fn inner_line_to(data: LineToData, sketch_group: SketchGroup, args: &Args) -> Result { +fn inner_line_to(data: LineToData, sketch_group: SketchGroup, args: &mut Args) -> Result { let from = sketch_group.get_coords_from_paths()?; let to = match data { LineToData::PointWithTag { to, .. } => to, @@ -51,6 +51,21 @@ fn inner_line_to(data: LineToData, sketch_group: SketchGroup, args: &Args) -> Re }; let id = uuid::Uuid::new_v4(); + + args.send_modeling_cmd( + id, + ModelingCmd::ExtendPath { + path: sketch_group.id, + segment: kittycad::types::PathSegment::Line { + end: Point3D { + x: to[0], + y: to[1], + z: 0.0, + }, + }, + }, + )?; + let current_path = Path::ToPoint { base: BasePath { from: from.into(), @@ -101,7 +116,7 @@ pub fn x_line_to(args: &mut Args) -> Result { #[stdlib { name = "xLineTo", }] -fn inner_x_line_to(data: AxisLineToData, sketch_group: SketchGroup, args: &Args) -> Result { +fn inner_x_line_to(data: AxisLineToData, sketch_group: SketchGroup, args: &mut Args) -> Result { let from = sketch_group.get_coords_from_paths()?; let line_to_data = match data { @@ -126,7 +141,7 @@ pub fn y_line_to(args: &mut Args) -> Result { #[stdlib { name = "yLineTo", }] -fn inner_y_line_to(data: AxisLineToData, sketch_group: SketchGroup, args: &Args) -> Result { +fn inner_y_line_to(data: AxisLineToData, sketch_group: SketchGroup, args: &mut Args) -> Result { let from = sketch_group.get_coords_from_paths()?; let line_to_data = match data { @@ -716,6 +731,248 @@ fn inner_close(sketch_group: SketchGroup, args: &mut Args) -> Result Result { + let (data, sketch_group): (ArcData, SketchGroup) = args.get_data_and_sketch_group()?; + + let new_sketch_group = inner_arc(data, sketch_group, args)?; + Ok(MemoryItem::SketchGroup(new_sketch_group)) +} + +/// Draw an arc. +#[stdlib { + name = "arc", +}] +fn inner_arc(data: ArcData, sketch_group: SketchGroup, args: &mut Args) -> Result { + let from = sketch_group.get_coords_from_paths()?; + + let (center, angle_start, angle_end, radius, end) = match &data { + ArcData::AnglesAndRadiusWithTag { + angle_start, + angle_end, + radius, + .. + } => { + let (center, end) = arc_center_and_end(&from, *angle_start, *angle_end, *radius); + (center, *angle_start, *angle_end, *radius, end) + } + ArcData::AnglesAndRadius { + angle_start, + angle_end, + radius, + } => { + let (center, end) = arc_center_and_end(&from, *angle_start, *angle_end, *radius); + (center, *angle_start, *angle_end, *radius, end) + } + ArcData::CenterToRadiusWithTag { center, to, radius, .. } => { + let (angle_start, angle_end) = arc_angles(&from, ¢er.into(), &to.into(), *radius, args.source_range)?; + (center.into(), angle_start, angle_end, *radius, to.into()) + } + ArcData::CenterToRadius { center, to, radius } => { + let (angle_start, angle_end) = arc_angles(&from, ¢er.into(), &to.into(), *radius, args.source_range)?; + (center.into(), angle_start, angle_end, *radius, to.into()) + } + }; + + let id = uuid::Uuid::new_v4(); + + args.send_modeling_cmd( + id, + ModelingCmd::ExtendPath { + path: sketch_group.id, + segment: kittycad::types::PathSegment::Arc { + angle_start, + angle_end, + center: center.into(), + radius, + }, + }, + )?; + // Move the path pen to the end of the arc. + // Since that is where we want to draw the next path. + // TODO: the engine should automatically move the pen to the end of the arc. + // This just seems inefficient. + args.send_modeling_cmd( + id, + ModelingCmd::MovePathPen { + path: sketch_group.id, + to: Point3D { + x: end.x, + y: end.y, + z: 0.0, + }, + }, + )?; + + let current_path = Path::ToPoint { + base: BasePath { + from: from.into(), + to: end.into(), + name: match data { + ArcData::AnglesAndRadiusWithTag { tag, .. } => tag.to_string(), + ArcData::AnglesAndRadius { .. } => "".to_string(), + ArcData::CenterToRadiusWithTag { tag, .. } => tag.to_string(), + ArcData::CenterToRadius { .. } => "".to_string(), + }, + geo_meta: GeoMeta { + id, + metadata: args.source_range.into(), + }, + }, + }; + + let mut new_sketch_group = sketch_group.clone(); + new_sketch_group.value.push(current_path); + + Ok(new_sketch_group) +} + +/// Data to draw a bezier curve. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] +#[ts(export)] +#[serde(rename_all = "camelCase", untagged)] +pub enum BezierData { + /// Points with a tag. + PointsWithTag { + /// The to point. + to: [f64; 2], + /// The first control point. + control1: [f64; 2], + /// The second control point. + control2: [f64; 2], + /// The tag. + tag: String, + }, + /// Points. + Points { + /// The to point. + to: [f64; 2], + /// The first control point. + control1: [f64; 2], + /// The second control point. + control2: [f64; 2], + }, +} + +/// Draw a bezier curve. +pub fn bezier_curve(args: &mut Args) -> Result { + let (data, sketch_group): (BezierData, SketchGroup) = args.get_data_and_sketch_group()?; + + let new_sketch_group = inner_bezier_curve(data, sketch_group, args)?; + Ok(MemoryItem::SketchGroup(new_sketch_group)) +} + +/// Draw a bezier curve. +#[stdlib { + name = "bezierCurve", +}] +fn inner_bezier_curve(data: BezierData, sketch_group: SketchGroup, args: &mut Args) -> Result { + let from = sketch_group.get_coords_from_paths()?; + + let (to, control1, control2) = match &data { + BezierData::PointsWithTag { + to, control1, control2, .. + } => (to, control1, control2), + BezierData::Points { to, control1, control2 } => (to, control1, control2), + }; + + let to = [from.x + to[0], from.y + to[1]]; + + let id = uuid::Uuid::new_v4(); + + args.send_modeling_cmd( + id, + ModelingCmd::ExtendPath { + path: sketch_group.id, + segment: kittycad::types::PathSegment::Bezier { + control1: Point3D { + x: from.x + control1[0], + y: from.y + control1[1], + z: 0.0, + }, + control2: Point3D { + x: from.x + control2[0], + y: from.y + control2[1], + z: 0.0, + }, + end: Point3D { + x: to[0], + y: to[1], + z: 0.0, + }, + }, + }, + )?; + + let current_path = Path::ToPoint { + base: BasePath { + from: from.into(), + to, + name: if let BezierData::PointsWithTag { tag, .. } = data { + tag.to_string() + } else { + "".to_string() + }, + geo_meta: GeoMeta { + id, + metadata: args.source_range.into(), + }, + }, + }; + + let mut new_sketch_group = sketch_group.clone(); + new_sketch_group.value.push(current_path); + + Ok(new_sketch_group) +} + #[cfg(test)] mod tests { diff --git a/src/wasm-lib/kcl/src/std/utils.rs b/src/wasm-lib/kcl/src/std/utils.rs index 803dd4921..257ab8e24 100644 --- a/src/wasm-lib/kcl/src/std/utils.rs +++ b/src/wasm-lib/kcl/src/std/utils.rs @@ -1,3 +1,8 @@ +use crate::{ + errors::{KclError, KclErrorDetails}, + executor::{Point2d, SourceRange}, +}; + pub fn get_angle(a: &[f64; 2], b: &[f64; 2]) -> f64 { let x = b[0] - a[0]; let y = b[1] - a[1]; @@ -160,12 +165,80 @@ pub fn get_x_component(angle_degree: f64, y_component: f64) -> [f64; 2] { [sign * x_component, sign * y_component] } +pub fn arc_center_and_end(from: &Point2d, start_angle_deg: f64, end_angle_deg: f64, radius: f64) -> (Point2d, Point2d) { + let start_angle = start_angle_deg * (std::f64::consts::PI / 180.0); + let end_angle = end_angle_deg * (std::f64::consts::PI / 180.0); + + let center = Point2d { + x: -1.0 * (radius * start_angle.cos() - from.x), + y: -1.0 * (radius * start_angle.sin() - from.y), + }; + + let end = Point2d { + x: center.x + radius * end_angle.cos(), + y: center.y + radius * end_angle.sin(), + }; + + (center, end) +} + +pub fn arc_angles( + from: &Point2d, + to: &Point2d, + center: &Point2d, + radius: f64, + source_range: SourceRange, +) -> Result<(f64, f64), KclError> { + // First make sure that the points are on the circumference of the circle. + // If not, we'll return an error. + if !is_on_circumference(center, from, radius) { + return Err(KclError::Semantic(KclErrorDetails { + message: format!( + "Point {:?} is not on the circumference of the circle with center {:?} and radius {}.", + from, center, radius + ), + source_ranges: vec![source_range], + })); + } + + if !is_on_circumference(center, to, radius) { + return Err(KclError::Semantic(KclErrorDetails { + message: format!( + "Point {:?} is not on the circumference of the circle with center {:?} and radius {}.", + to, center, radius + ), + source_ranges: vec![source_range], + })); + } + + let start_angle = (from.y - center.y).atan2(from.x - center.x); + let end_angle = (to.y - center.y).atan2(to.x - center.x); + + let start_angle_deg = start_angle * (180.0 / std::f64::consts::PI); + let end_angle_deg = end_angle * (180.0 / std::f64::consts::PI); + + Ok((start_angle_deg, end_angle_deg)) +} + +pub fn is_on_circumference(center: &Point2d, point: &Point2d, radius: f64) -> bool { + let dx = point.x - center.x; + let dy = point.y - center.y; + + let distance_squared = dx.powi(2) + dy.powi(2); + + // We'll check if the distance squared is approximately equal to radius squared. + // Due to potential floating point inaccuracies, we'll check if the difference + // is very small (e.g., 1e-9) rather than checking for strict equality. + (distance_squared - radius.powi(2)).abs() < 1e-9 +} + #[cfg(test)] mod tests { // Here you can bring your functions into scope use pretty_assertions::assert_eq; use super::{get_x_component, get_y_component}; + use crate::executor::SourceRange; static EACH_QUAD: [(i32, [i32; 2]); 12] = [ (-315, [1, 1]), @@ -241,4 +314,77 @@ mod tests { assert!((result[0] - 0.0).abs() < f64::EPSILON); assert_eq!(result[1] as i32, -1); } + + #[test] + fn test_arc_center_and_end() { + let (center, end) = super::arc_center_and_end(&super::Point2d { x: 0.0, y: 0.0 }, 0.0, 90.0, 1.0); + assert_eq!(center.x.round(), -1.0); + assert_eq!(center.y, 0.0); + assert_eq!(end.x.round(), -1.0); + assert_eq!(end.y, 1.0); + + let (center, end) = super::arc_center_and_end(&super::Point2d { x: 0.0, y: 0.0 }, 0.0, 180.0, 1.0); + assert_eq!(center.x.round(), -1.0); + assert_eq!(center.y, 0.0); + assert_eq!(end.x.round(), -2.0); + assert_eq!(end.y.round(), 0.0); + + let (center, end) = super::arc_center_and_end(&super::Point2d { x: 0.0, y: 0.0 }, 0.0, 180.0, 10.0); + assert_eq!(center.x.round(), -10.0); + assert_eq!(center.y, 0.0); + assert_eq!(end.x.round(), -20.0); + assert_eq!(end.y.round(), 0.0); + } + + #[test] + fn test_arc_angles() { + let (angle_start, angle_end) = super::arc_angles( + &super::Point2d { x: 0.0, y: 0.0 }, + &super::Point2d { x: -1.0, y: 1.0 }, + &super::Point2d { x: -1.0, y: 0.0 }, + 1.0, + SourceRange(Default::default()), + ) + .unwrap(); + assert_eq!(angle_start.round(), 0.0); + assert_eq!(angle_end.round(), 90.0); + + let (angle_start, angle_end) = super::arc_angles( + &super::Point2d { x: 0.0, y: 0.0 }, + &super::Point2d { x: -2.0, y: 0.0 }, + &super::Point2d { x: -1.0, y: 0.0 }, + 1.0, + SourceRange(Default::default()), + ) + .unwrap(); + assert_eq!(angle_start.round(), 0.0); + assert_eq!(angle_end.round(), 180.0); + + let (angle_start, angle_end) = super::arc_angles( + &super::Point2d { x: 0.0, y: 0.0 }, + &super::Point2d { x: -20.0, y: 0.0 }, + &super::Point2d { x: -10.0, y: 0.0 }, + 10.0, + SourceRange(Default::default()), + ) + .unwrap(); + assert_eq!(angle_start.round(), 0.0); + assert_eq!(angle_end.round(), 180.0); + + let result = super::arc_angles( + &super::Point2d { x: 0.0, y: 5.0 }, + &super::Point2d { x: 5.0, y: 5.0 }, + &super::Point2d { x: 10.0, y: -10.0 }, + 10.0, + SourceRange(Default::default()), + ); + + if let Err(err) = result { + assert!(err.to_string().contains( "Point Point2d { x: 0.0, y: 5.0 } is not on the circumference of the circle with center Point2d { x: 10.0, y: -10.0 } and radius 10.")); + } else { + panic!("Expected error"); + } + assert_eq!(angle_start.round(), 0.0); + assert_eq!(angle_end.round(), 180.0); + } }