import { toolTips } from 'lang/langHelpers' import { Program, Expr, VariableDeclarator } from '../../lang/wasm' import { Selections } from 'lib/selections' import { getNodeFromPath } from '../../lang/queryAst' import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' import { transformSecondarySketchLinesTagFirst, getTransformInfos, PathToNodeMap, isExprBinaryPart, } from '../../lang/std/sketchcombos' import { TransformInfo } from 'lang/std/stdTypes' import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' import { createVariableDeclaration } from '../../lang/modifyAst' import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { kclManager } from 'lib/singletons' import { err } from 'lib/trap' const getModalInfo = createInfoModal(GetInfoModal) export function angleBetweenInfo({ selectionRanges, }: { selectionRanges: Selections }): | { transforms: TransformInfo[] enabled: boolean } | Error { const _nodes = selectionRanges.graphSelections.map(({ codeRef }) => { const tmp = getNodeFromPath(kclManager.ast, codeRef.pathToNode) if (err(tmp)) return tmp return tmp.node }) const _err1 = _nodes.find(err) if (err(_err1)) return _err1 const nodes = _nodes as Expr[] const _varDecs = selectionRanges.graphSelections.map(({ codeRef }) => { const tmp = getNodeFromPath( kclManager.ast, codeRef.pathToNode, 'VariableDeclarator' ) if (err(tmp)) return tmp return tmp.node }) const _err2 = _varDecs.find(err) if (err(_err2)) return _err2 const varDecs = _varDecs as VariableDeclarator[] const primaryLine = varDecs[0] const secondaryVarDecs = varDecs.slice(1) const isOthersLinkedToPrimary = secondaryVarDecs.every((secondary) => isSketchVariablesLinked(secondary, primaryLine, kclManager.ast) ) const isAllTooltips = nodes.every( (node) => node?.type === 'CallExpression' && toolTips.includes(node.callee.name as any) ) const theTransforms = getTransformInfos( { ...selectionRanges, graphSelections: selectionRanges.graphSelections.slice(1), }, kclManager.ast, 'setAngleBetween' ) const _enableEqual = selectionRanges.otherSelections.length === 0 && secondaryVarDecs.length === 1 && isAllTooltips && isOthersLinkedToPrimary && theTransforms.every(Boolean) return { enabled: _enableEqual, transforms: theTransforms } } export async function applyConstraintAngleBetween({ selectionRanges, }: { selectionRanges: Selections }): Promise<{ modifiedAst: Program pathToNodeMap: PathToNodeMap }> { const info = angleBetweenInfo({ selectionRanges }) if (err(info)) return Promise.reject(info) const transformInfos = info.transforms const transformed1 = transformSecondarySketchLinesTagFirst({ ast: structuredClone(kclManager.ast), selectionRanges, transformInfos, programMemory: kclManager.programMemory, }) if (err(transformed1)) return Promise.reject(transformed1) const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } = transformed1 const { segName, value, valueNode, variableName, newVariableInsertIndex, sign, } = await getModalInfo({ segName: tagInfo?.tag, isSegNameEditable: !tagInfo?.isTagExisting, value: valueUsedInTransform, initialVariableName: 'angle', } as any) if ( segName === tagInfo?.tag && Number(value) === valueUsedInTransform && !variableName ) { return { modifiedAst, pathToNodeMap, } } if (!isExprBinaryPart(valueNode)) return Promise.reject('Invalid valueNode, is not a BinaryPart') const finalValue = removeDoubleNegatives(valueNode, sign, variableName) // transform again but forcing certain values const transformed2 = transformSecondarySketchLinesTagFirst({ ast: kclManager.ast, selectionRanges, transformInfos, programMemory: kclManager.programMemory, forceSegName: segName, forceValueUsedInTransform: finalValue, }) if (err(transformed2)) return Promise.reject(transformed2) const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } = transformed2 if (variableName) { const newBody = [..._modifiedAst.body] newBody.splice( newVariableInsertIndex, 0, createVariableDeclaration(variableName, valueNode) ) _modifiedAst.body = newBody Object.values(_pathToNodeMap).forEach((pathToNode) => { const index = pathToNode.findIndex((a) => a[0] === 'body') + 1 pathToNode[index][0] = Number(pathToNode[index][0]) + 1 }) } return { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap, } }