Add equal-length constraints & implement UnaryExpressions (#35)

* add segLen help to lang std

* adding helpers functions to sketchConstraints

* update tokeniser tests because they were annoying me not being 100%

* compare async lexer with sync lexer instead

* add helper functions

* remove unneeded nesting

* update add ast modifier function for angledLine

* initial equal ast modification

It adds a tag to the primary line, and converts any secondary lines to angledLine, but doesn't reference the taged/primary line yet

* Update fn call with refernce to previous line using segLen

* add test for giveSketchFnCallTag

* fix excutor bug, executing call expression in array expression

* fix small issue in executor

* add CallExpressions to BinaryExpressions

* add unary Expressions

* tweaks to unaryExpression logic

* add recasting for unaryExpressions and CallExpressions in BinaryExpressions

* ensure pipe substitution info is passed down to unary expressions and others

* allow binary expressions in function argumentns

* inital setup, new way of organising sketch fn transforms

Starting with equal length

* overhaul equalLength button

* add equal length support for angledLine

* line with one variable supports signed legLength

* fix indentation when recasting long arrayExpressions in a pipeExpression

* improve modifyAst consision

* further modify ast tidy

* equalLength transfroms far angledLineOfXLength

* add transforms for line-yRelative

* add equal constraint for angledLineOfYLength

* quick test fix

* add equal length constrain transforms for lineTo

* add equal length constraints for angledLineToX

* add equalLength constraints for angledLineToY

* test tidy

* setup new vertical-horizontal constraints

* Add equal Length constraints for vertical/horizontal lines

* migrate old tests, and refactor callback tag

* tweaks and refactor horzVert component

* fix leg len with small negative leg length
This commit is contained in:
Kurt Hutten
2023-03-02 21:19:11 +11:00
committed by GitHub
parent f70f0f7bc3
commit 6446601a67
21 changed files with 2280 additions and 666 deletions

View File

@ -0,0 +1,94 @@
import { useState, useEffect } from 'react'
import { toolTips, useStore } from '../../useStore'
import {
getNodePathFromSourceRange,
getNodeFromPath,
Value,
VariableDeclarator,
} from '../../lang/abstractSyntaxTree'
import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints'
import {
TransformInfo,
transformAstForSketchLines,
getTransformInfos,
} from '../../lang/std/sketchcombos'
export const Equal = () => {
const { guiMode, selectionRanges, ast, programMemory, updateAst } = useStore(
(s) => ({
guiMode: s.guiMode,
ast: s.ast,
updateAst: s.updateAst,
selectionRanges: s.selectionRanges,
programMemory: s.programMemory,
})
)
const [enableEqual, setEnableEqual] = useState(false)
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
useEffect(() => {
if (!ast) return
const paths = selectionRanges.map((selectionRange) =>
getNodePathFromSourceRange(ast, selectionRange)
)
const nodes = paths.map(
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
)
const varDecs = paths.map(
(pathToNode) =>
getNodeFromPath<VariableDeclarator>(
ast,
pathToNode,
'VariableDeclarator'
)?.node
)
const primaryLine = varDecs[0]
const secondaryVarDecs = varDecs.slice(1)
const isOthersLinkedToPrimary = secondaryVarDecs.every((secondary) =>
isSketchVariablesLinked(secondary, primaryLine, ast)
)
const isAllTooltips = nodes.every(
(node) =>
node?.type === 'CallExpression' &&
toolTips.includes(node.callee.name as any)
)
const theTransforms = getTransformInfos(
selectionRanges.slice(1),
ast,
'equalLength'
)
setTransformInfos(theTransforms)
const _enableEqual =
!!secondaryVarDecs.length &&
isAllTooltips &&
isOthersLinkedToPrimary &&
theTransforms.every(Boolean)
setEnableEqual(_enableEqual)
}, [guiMode, selectionRanges])
if (guiMode.mode !== 'sketch') return null
return (
<button
onClick={() =>
transformInfos &&
ast &&
updateAst(
transformAstForSketchLines({
ast,
selectionRanges,
transformInfos,
programMemory,
})?.modifiedAst
)
}
className={`border m-1 px-1 rounded ${
enableEqual ? 'bg-gray-50 text-gray-800' : 'bg-gray-200 text-gray-400'
}`}
disabled={!enableEqual}
title="yo dawg"
>
Equal
</button>
)
}

View File

@ -1,16 +1,21 @@
import { useState, useEffect } from 'react'
import { TooTip, useStore } from '../../useStore'
import { toolTips, useStore } from '../../useStore'
import {
getNodePathFromSourceRange,
getNodeFromPath,
Value,
CallExpression,
} from '../../lang/abstractSyntaxTree'
import { toolTips } from '../../useStore'
import { allowedTransforms } from '../../lang/std/sketch'
import { swapSketchHelper } from '../../lang/std/sketchConstraints'
import {
TransformInfo,
getTransformInfos,
transformAstForHorzVert,
} from '../../lang/std/sketchcombos'
export const HorzVert = () => {
export const HorzVert = ({
horOrVert,
}: {
horOrVert: 'vertical' | 'horizontal'
}) => {
const { guiMode, selectionRanges, ast, programMemory, updateAst } = useStore(
(s) => ({
guiMode: s.guiMode,
@ -21,116 +26,50 @@ export const HorzVert = () => {
})
)
const [enableHorz, setEnableHorz] = useState(false)
const [enableVert, setEnableVert] = useState(false)
const [allowedTransformsMap, setAllowedTransformsMap] = useState<
ReturnType<typeof allowedTransforms>[]
>([])
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
useEffect(() => {
if (!ast) return
const islineFn = (expression: Value): boolean => {
if (expression?.type !== 'CallExpression') return false
if (!toolTips.includes(expression.callee.name as any)) return false
return true
}
const paths = selectionRanges.map((selectionRange) =>
getNodePathFromSourceRange(ast, selectionRange)
)
const nodes = paths.map(
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
)
const allowedSwaps = paths.map((a) =>
allowedTransforms({
node: ast,
pathToNode: a,
previousProgramMemory: programMemory,
})
const isAllTooltips = nodes.every(
(node) =>
node?.type === 'CallExpression' &&
toolTips.includes(node.callee.name as any)
)
setAllowedTransformsMap(allowedSwaps)
const allowedSwapsnames = allowedSwaps.map((a) =>
Object.keys(a)
) as TooTip[][]
const horzAllowed = includedInAll(allowedSwapsnames, ['xLine', 'xLineTo'])
const vertAllowed = includedInAll(allowedSwapsnames, ['yLine', 'yLineTo'])
const isCursorsInLineFns = nodes.every(islineFn)
const _enableHorz =
isCursorsInLineFns &&
horzAllowed &&
guiMode.mode === 'sketch' &&
guiMode.sketchMode === 'sketchEdit'
if (enableHorz !== _enableHorz) setEnableHorz(_enableHorz)
const _enableVert =
isCursorsInLineFns &&
vertAllowed &&
guiMode.mode === 'sketch' &&
guiMode.sketchMode === 'sketchEdit'
if (enableVert !== _enableVert) setEnableVert(_enableVert)
}, [guiMode, selectionRanges, guiMode])
const theTransforms = getTransformInfos(selectionRanges, ast, horOrVert)
setTransformInfos(theTransforms)
const _enableHorz = isAllTooltips && theTransforms.every(Boolean)
setEnableHorz(_enableHorz)
}, [guiMode, selectionRanges])
if (guiMode.mode !== 'sketch') return null
const onClick = (vertOrHor: 'vert' | 'horz') => () => {
if (ast) {
// deep clone since we are mutating in a loop, of which any could fail
let node = JSON.parse(JSON.stringify(ast))
selectionRanges.forEach((range, index) => {
const { node: callExpression } = getNodeFromPath<CallExpression>(
node,
getNodePathFromSourceRange(node, range)
)
const [relLine, absLine]: [TooTip, TooTip] =
vertOrHor === 'vert' ? ['yLine', 'yLineTo'] : ['xLine', 'xLineTo']
const finalLine = [
'line',
'angledLine',
'angledLineOfXLength',
'angledLineOfYLength',
].includes(callExpression.callee.name)
? relLine
: absLine
const createCallBackHelper = allowedTransformsMap[index][finalLine]
if (!createCallBackHelper) throw new Error('no callback helper')
const { modifiedAst } = swapSketchHelper(
programMemory,
node,
range,
finalLine,
createCallBackHelper
)
node = modifiedAst
})
updateAst(node)
}
}
return (
<>
<button
onClick={onClick('horz')}
className={`border m-1 px-1 rounded ${
enableHorz ? 'bg-gray-50 text-gray-800' : 'bg-gray-200 text-gray-400'
}`}
disabled={!enableHorz}
title="yo dawg"
>
Horz
</button>
<button
onClick={onClick('vert')}
className={`border m-1 px-1 rounded ${
enableVert ? 'bg-gray-50 text-gray-800' : 'bg-gray-200 text-gray-400'
}`}
disabled={!enableVert}
>
Vert
</button>
</>
)
}
function includedInAll(
allowedsOfEach: TooTip[][],
isIncludes: TooTip[]
): boolean {
return allowedsOfEach.every((alloweds) =>
isIncludes.some((isInclude) => alloweds.includes(isInclude))
<button
onClick={() =>
transformInfos &&
ast &&
updateAst(
transformAstForHorzVert({
ast,
selectionRanges,
transformInfos,
programMemory,
})?.modifiedAst
)
}
className={`border m-1 px-1 rounded ${
enableHorz ? 'bg-gray-50 text-gray-800' : 'bg-gray-200 text-gray-400'
}`}
disabled={!enableHorz}
title="yo dawg"
>
{horOrVert === 'horizontal' ? 'Horz' : 'Vert'}
</button>
)
}