Add Angle & Length constraint/value, and modal for the details (#54)
* setup UI for getting length and Angle values from user * enable setLength-angle for angledLines with an exisiting constraining value
This commit is contained in:
@ -4,6 +4,7 @@ import { getNodePathFromSourceRange } from './lang/queryAst'
|
|||||||
import { HorzVert } from './components/Toolbar/HorzVert'
|
import { HorzVert } from './components/Toolbar/HorzVert'
|
||||||
import { Equal } from './components/Toolbar/Equal'
|
import { Equal } from './components/Toolbar/Equal'
|
||||||
import { SetHorzDistance } from './components/Toolbar/SetHorzDistance'
|
import { SetHorzDistance } from './components/Toolbar/SetHorzDistance'
|
||||||
|
import { SetAngleLength } from './components/Toolbar/SetAngleLength'
|
||||||
|
|
||||||
export const Toolbar = () => {
|
export const Toolbar = () => {
|
||||||
const {
|
const {
|
||||||
@ -159,6 +160,8 @@ export const Toolbar = () => {
|
|||||||
<Equal />
|
<Equal />
|
||||||
<SetHorzDistance horOrVert="setHorzDistance" />
|
<SetHorzDistance horOrVert="setHorzDistance" />
|
||||||
<SetHorzDistance horOrVert="setVertDistance" />
|
<SetHorzDistance horOrVert="setVertDistance" />
|
||||||
|
<SetAngleLength angleOrLength="setAngle" />
|
||||||
|
<SetAngleLength angleOrLength="setLength" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
272
src/components/SetAngleModal.tsx
Normal file
272
src/components/SetAngleModal.tsx
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
import { Dialog, Transition } from '@headlessui/react'
|
||||||
|
import { Fragment, useState, useRef, useEffect } from 'react'
|
||||||
|
import { abstractSyntaxTree, Value } from '../lang/abstractSyntaxTree'
|
||||||
|
import { executor } from '../lang/executor'
|
||||||
|
import { findUniqueName } from '../lang/modifyAst'
|
||||||
|
import { PrevVariable } from '../lang/queryAst'
|
||||||
|
import { lexer } from '../lang/tokeniser'
|
||||||
|
import { useStore } from '../useStore'
|
||||||
|
|
||||||
|
export const SetAngleLengthModal = ({
|
||||||
|
isOpen,
|
||||||
|
onResolve,
|
||||||
|
onReject,
|
||||||
|
prevVariables,
|
||||||
|
value: initialValue,
|
||||||
|
valueName,
|
||||||
|
}: {
|
||||||
|
isOpen: boolean
|
||||||
|
onResolve: (a: {
|
||||||
|
value: string
|
||||||
|
valueNode: Value
|
||||||
|
variableName?: string
|
||||||
|
}) => void
|
||||||
|
onReject: (a: any) => void
|
||||||
|
prevVariables: PrevVariable<number>[]
|
||||||
|
value: number
|
||||||
|
valueName: string
|
||||||
|
}) => {
|
||||||
|
const { ast, programMemory } = useStore((s) => ({
|
||||||
|
ast: s.ast,
|
||||||
|
programMemory: s.programMemory,
|
||||||
|
}))
|
||||||
|
const [value, setValue] = useState(String(initialValue))
|
||||||
|
const [calcResult, setCalcResult] = useState('NAN')
|
||||||
|
const [shouldCreateVariable, setShouldCreateVariable] = useState(false)
|
||||||
|
const [newVariableName, setNewVariableName] = useState('')
|
||||||
|
const [isNewVariableNameUnique, setIsNewVariableNameUnique] = useState(true)
|
||||||
|
const [valueNode, setValueNode] = useState<any>(null)
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
inputRef.current && inputRef.current.focus()
|
||||||
|
inputRef.current &&
|
||||||
|
inputRef.current.setSelectionRange(0, String(value).length)
|
||||||
|
}, 100)
|
||||||
|
if (ast) {
|
||||||
|
setNewVariableName(findUniqueName(ast, valueName))
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const allVarNames = Object.keys(programMemory.root)
|
||||||
|
if (allVarNames.includes(newVariableName)) {
|
||||||
|
setIsNewVariableNameUnique(false)
|
||||||
|
} else {
|
||||||
|
setIsNewVariableNameUnique(true)
|
||||||
|
}
|
||||||
|
}, [newVariableName])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
const code = `const __result__ = ${value}\nshow(__result__)`
|
||||||
|
const ast = abstractSyntaxTree(lexer(code))
|
||||||
|
const _programMem: any = { root: {} }
|
||||||
|
prevVariables.forEach(({ key, value }) => {
|
||||||
|
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
||||||
|
})
|
||||||
|
const programMemory = executor(ast, _programMem)
|
||||||
|
const resultDeclaration = ast.body.find(
|
||||||
|
(a) =>
|
||||||
|
a.type === 'VariableDeclaration' &&
|
||||||
|
a.declarations?.[0]?.id?.name === '__result__'
|
||||||
|
)
|
||||||
|
const init =
|
||||||
|
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||||
|
resultDeclaration?.declarations?.[0]?.init
|
||||||
|
console.log(init)
|
||||||
|
setCalcResult(programMemory?.root?.__result__?.value || 'NAN')
|
||||||
|
setValueNode(init)
|
||||||
|
} catch (e) {
|
||||||
|
setCalcResult('NAN')
|
||||||
|
setValueNode(null)
|
||||||
|
}
|
||||||
|
}, [value])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
|
<Dialog as="div" className="relative z-10" onClose={onReject}>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||||
|
</Transition.Child>
|
||||||
|
|
||||||
|
<div className="fixed inset-0 overflow-y-auto">
|
||||||
|
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0 scale-95"
|
||||||
|
enterTo="opacity-100 scale-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100 scale-100"
|
||||||
|
leaveTo="opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
|
||||||
|
<Dialog.Title
|
||||||
|
as="h3"
|
||||||
|
className="text-lg font-medium leading-6 text-gray-900 capitalize"
|
||||||
|
>
|
||||||
|
Set {valueName}
|
||||||
|
</Dialog.Title>
|
||||||
|
<div className="block text-sm font-medium text-gray-700 mt-3 font-mono capitalize">
|
||||||
|
Available Variables
|
||||||
|
</div>
|
||||||
|
<ul className="flex flex-col">
|
||||||
|
{prevVariables.length &&
|
||||||
|
prevVariables.map(({ key, value }) => (
|
||||||
|
<li key={key}>
|
||||||
|
<button
|
||||||
|
className="flex w-full justify-between items-center rounded-md hover:bg-gray-100 max-w-xs"
|
||||||
|
onClick={(e) => {
|
||||||
|
const selectionStart =
|
||||||
|
inputRef.current?.selectionStart
|
||||||
|
let selectionEnd = inputRef.current?.selectionEnd
|
||||||
|
let newValue = ''
|
||||||
|
if (
|
||||||
|
typeof selectionStart === 'number' &&
|
||||||
|
typeof selectionEnd === 'number'
|
||||||
|
) {
|
||||||
|
newValue = stringSplice(
|
||||||
|
inputRef.current?.value || '',
|
||||||
|
selectionStart,
|
||||||
|
selectionEnd,
|
||||||
|
key
|
||||||
|
)
|
||||||
|
selectionEnd = selectionStart + key.length
|
||||||
|
} else {
|
||||||
|
newValue = inputRef.current?.value + key
|
||||||
|
}
|
||||||
|
setValue(newValue)
|
||||||
|
inputRef.current?.focus()
|
||||||
|
setTimeout(() => {
|
||||||
|
// run in the next render cycle
|
||||||
|
const _selectionEnd =
|
||||||
|
typeof selectionEnd === 'number'
|
||||||
|
? selectionEnd
|
||||||
|
: newValue.length
|
||||||
|
inputRef.current?.setSelectionRange(
|
||||||
|
_selectionEnd,
|
||||||
|
_selectionEnd
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="font-[monospace] text-gray-800">
|
||||||
|
{key}
|
||||||
|
</span>{' '}
|
||||||
|
<span className="font-[monospace] text-gray-600 w-24 text-start font-bold">
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<label
|
||||||
|
htmlFor="val"
|
||||||
|
className="block text-sm font-medium text-gray-700 mt-3 font-mono capitalize"
|
||||||
|
>
|
||||||
|
{valueName} Value
|
||||||
|
</label>
|
||||||
|
<div className="mt-1">
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
name="val"
|
||||||
|
id="val"
|
||||||
|
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono pl-1"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => {
|
||||||
|
setValue(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="font-[monospace] pl-4 text-gray-600">
|
||||||
|
<span
|
||||||
|
className={`${
|
||||||
|
calcResult === 'NAN' ? 'bg-pink-200' : ''
|
||||||
|
} px-2 py-0.5 rounded`}
|
||||||
|
>
|
||||||
|
= {calcResult}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label
|
||||||
|
htmlFor="val"
|
||||||
|
className="block text-sm font-medium text-gray-700 mt-3 font-mono"
|
||||||
|
>
|
||||||
|
Create new variable
|
||||||
|
</label>
|
||||||
|
<div className="mt-1 flex flex-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="val"
|
||||||
|
id="val"
|
||||||
|
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono pl-1 flex-shrink"
|
||||||
|
checked={shouldCreateVariable}
|
||||||
|
onChange={(e) => {
|
||||||
|
setShouldCreateVariable(e.target.checked)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
disabled={!shouldCreateVariable}
|
||||||
|
name="val"
|
||||||
|
id="val"
|
||||||
|
className={`shadow-sm font-[monospace] focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono pl-1 flex-shrink-0 ${
|
||||||
|
!shouldCreateVariable ? 'opacity-50' : ''
|
||||||
|
}`}
|
||||||
|
value={newVariableName}
|
||||||
|
onChange={(e) => {
|
||||||
|
setNewVariableName(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{!isNewVariableNameUnique && (
|
||||||
|
<div className="bg-pink-200 rounded px-2 py-0.5 text-xs">
|
||||||
|
Sorry, that's not a unique variable name. Please try
|
||||||
|
something else
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="mt-4">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={calcResult === 'NAN' || !isNewVariableNameUnique}
|
||||||
|
className={`inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 ${
|
||||||
|
calcResult === 'NAN' || !isNewVariableNameUnique
|
||||||
|
? 'opacity-50 cursor-not-allowed'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
onClick={() =>
|
||||||
|
onResolve({
|
||||||
|
value,
|
||||||
|
valueNode,
|
||||||
|
variableName: shouldCreateVariable
|
||||||
|
? newVariableName
|
||||||
|
: undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Add constraining value
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Dialog.Panel>
|
||||||
|
</Transition.Child>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringSplice(str: string, index: number, count: number, add: string) {
|
||||||
|
return str.slice(0, index) + (add || '') + str.slice(index + count)
|
||||||
|
}
|
118
src/components/Toolbar/SetAngleLength.tsx
Normal file
118
src/components/Toolbar/SetAngleLength.tsx
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { create } from 'react-modal-promise'
|
||||||
|
import { toolTips, useStore } from '../../useStore'
|
||||||
|
import { Value } from '../../lang/abstractSyntaxTree'
|
||||||
|
import {
|
||||||
|
getNodePathFromSourceRange,
|
||||||
|
getNodeFromPath,
|
||||||
|
findAllPreviousVariables,
|
||||||
|
} from '../../lang/queryAst'
|
||||||
|
import {
|
||||||
|
TransformInfo,
|
||||||
|
getTransformInfos,
|
||||||
|
transformAstSketchLines,
|
||||||
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { SetAngleLengthModal } from '../SetAngleModal'
|
||||||
|
import {
|
||||||
|
createIdentifier,
|
||||||
|
createVariableDeclaration,
|
||||||
|
} from '../../lang/modifyAst'
|
||||||
|
|
||||||
|
const getModalInfo = create(SetAngleLengthModal as any)
|
||||||
|
|
||||||
|
export const SetAngleLength = ({
|
||||||
|
angleOrLength,
|
||||||
|
}: {
|
||||||
|
angleOrLength: 'setAngle' | 'setLength'
|
||||||
|
}) => {
|
||||||
|
const { guiMode, selectionRanges, ast, programMemory, updateAst } = useStore(
|
||||||
|
(s) => ({
|
||||||
|
guiMode: s.guiMode,
|
||||||
|
ast: s.ast,
|
||||||
|
updateAst: s.updateAst,
|
||||||
|
selectionRanges: s.selectionRanges,
|
||||||
|
programMemory: s.programMemory,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const [enableHorz, setEnableHorz] = 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 isAllTooltips = nodes.every(
|
||||||
|
(node) =>
|
||||||
|
node?.type === 'CallExpression' &&
|
||||||
|
toolTips.includes(node.callee.name as any)
|
||||||
|
)
|
||||||
|
|
||||||
|
const theTransforms = getTransformInfos(selectionRanges, ast, angleOrLength)
|
||||||
|
setTransformInfos(theTransforms)
|
||||||
|
|
||||||
|
const _enableHorz = isAllTooltips && theTransforms.every(Boolean)
|
||||||
|
setEnableHorz(_enableHorz)
|
||||||
|
}, [guiMode, selectionRanges])
|
||||||
|
if (guiMode.mode !== 'sketch') return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
if (!(transformInfos && ast)) return
|
||||||
|
const { modifiedAst, valueUsedInTransform } = transformAstSketchLines({
|
||||||
|
ast: JSON.parse(JSON.stringify(ast)),
|
||||||
|
selectionRanges,
|
||||||
|
transformInfos,
|
||||||
|
programMemory,
|
||||||
|
referenceSegName: '',
|
||||||
|
})
|
||||||
|
const availableVarInfo = findAllPreviousVariables(
|
||||||
|
modifiedAst,
|
||||||
|
programMemory,
|
||||||
|
selectionRanges[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { valueNode, variableName } = await getModalInfo({
|
||||||
|
value: valueUsedInTransform,
|
||||||
|
prevVariables: availableVarInfo.variables,
|
||||||
|
valueName: angleOrLength === 'setAngle' ? 'angle' : 'length',
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
const { modifiedAst: _modifiedAst } = transformAstSketchLines({
|
||||||
|
ast: JSON.parse(JSON.stringify(ast)),
|
||||||
|
selectionRanges,
|
||||||
|
transformInfos,
|
||||||
|
programMemory,
|
||||||
|
referenceSegName: '',
|
||||||
|
forceValueUsedInTransform: variableName
|
||||||
|
? createIdentifier(variableName)
|
||||||
|
: valueNode,
|
||||||
|
})
|
||||||
|
if (variableName) {
|
||||||
|
const newBody = [..._modifiedAst.body]
|
||||||
|
newBody.splice(
|
||||||
|
availableVarInfo.insertIndex,
|
||||||
|
0,
|
||||||
|
createVariableDeclaration(variableName, valueNode)
|
||||||
|
)
|
||||||
|
_modifiedAst.body = newBody
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAst(_modifiedAst)
|
||||||
|
} catch (e) {
|
||||||
|
console.log('e', e)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={`border m-1 px-1 rounded text-xs ${
|
||||||
|
enableHorz ? 'bg-gray-50 text-gray-800' : 'bg-gray-200 text-gray-400'
|
||||||
|
}`}
|
||||||
|
disabled={!enableHorz}
|
||||||
|
>
|
||||||
|
{angleOrLength}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
@ -160,6 +160,12 @@ export const executor = (
|
|||||||
__meta,
|
__meta,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (declaration.init.type === 'Identifier') {
|
||||||
|
_programMemory.root[variableName] = {
|
||||||
|
type: 'userVal',
|
||||||
|
value: _programMemory.root[declaration.init.name].value,
|
||||||
|
__meta,
|
||||||
|
}
|
||||||
} else if (declaration.init.type === 'Literal') {
|
} else if (declaration.init.type === 'Literal') {
|
||||||
_programMemory.root[variableName] = {
|
_programMemory.root[variableName] = {
|
||||||
type: 'userVal',
|
type: 'userVal',
|
||||||
|
@ -29,7 +29,6 @@ export function addSketchTo(
|
|||||||
name = ''
|
name = ''
|
||||||
): { modifiedAst: Program; id: string; pathToNode: (string | number)[] } {
|
): { modifiedAst: Program; id: string; pathToNode: (string | number)[] } {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const dumbyStartend = { start: 0, end: 0 }
|
|
||||||
const _name = name || findUniqueName(node, 'part')
|
const _name = name || findUniqueName(node, 'part')
|
||||||
|
|
||||||
const startSketchAt = createCallExpression('startSketchAt', [
|
const startSketchAt = createCallExpression('startSketchAt', [
|
||||||
|
@ -135,7 +135,7 @@ export function getNodePathFromSourceRange(
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PrevVariable<T> {
|
export interface PrevVariable<T> {
|
||||||
key: string
|
key: string
|
||||||
value: T
|
value: T
|
||||||
}
|
}
|
||||||
|
@ -160,12 +160,15 @@ export const lineTo: SketchLineHelper = {
|
|||||||
])
|
])
|
||||||
const callIndex = getLastIndex(pathToNode)
|
const callIndex = getLastIndex(pathToNode)
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExisting && createCallback) {
|
||||||
const boop = createCallback(newVals, referencedSegment)
|
const { callExp, valueUsedInTransform } = createCallback(
|
||||||
pipe.body[callIndex] = boop.callExp
|
newVals,
|
||||||
|
referencedSegment
|
||||||
|
)
|
||||||
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
valueUsedInTransform: boop.valueUsedInTransform,
|
valueUsedInTransform: valueUsedInTransform,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pipe.body = [...pipe.body, newLine]
|
pipe.body = [...pipe.body, newLine]
|
||||||
@ -599,16 +602,23 @@ export const angledLine: SketchLineHelper = {
|
|||||||
|
|
||||||
const newAngleVal = createLiteral(roundOff(getAngle(from, to), 0))
|
const newAngleVal = createLiteral(roundOff(getAngle(from, to), 0))
|
||||||
const newLengthVal = createLiteral(roundOff(getLength(from, to), 2))
|
const newLengthVal = createLiteral(roundOff(getLength(from, to), 2))
|
||||||
const newLine = createCallback
|
const newLine = createCallExpression('angledLine', [
|
||||||
? createCallback([newAngleVal, newLengthVal]).callExp
|
|
||||||
: createCallExpression('angledLine', [
|
|
||||||
createArrayExpression([newAngleVal, newLengthVal]),
|
createArrayExpression([newAngleVal, newLengthVal]),
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
])
|
])
|
||||||
|
|
||||||
const callIndex = getLastIndex(pathToNode)
|
const callIndex = getLastIndex(pathToNode)
|
||||||
if (replaceExisting) {
|
if (replaceExisting && createCallback) {
|
||||||
pipe.body[callIndex] = newLine
|
const { callExp, valueUsedInTransform } = createCallback([
|
||||||
|
newAngleVal,
|
||||||
|
newLengthVal,
|
||||||
|
])
|
||||||
|
pipe.body[callIndex] = callExp
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode,
|
||||||
|
valueUsedInTransform,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
pipe.body = [...pipe.body, newLine]
|
pipe.body = [...pipe.body, newLine]
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
createBinaryExpression,
|
createBinaryExpression,
|
||||||
createCallExpression,
|
createCallExpression,
|
||||||
|
createIdentifier,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
createPipeSubstitution,
|
createPipeSubstitution,
|
||||||
createUnaryExpression,
|
createUnaryExpression,
|
||||||
@ -40,6 +41,8 @@ export type ConstraintType =
|
|||||||
| 'equalangle'
|
| 'equalangle'
|
||||||
| 'setHorzDistance'
|
| 'setHorzDistance'
|
||||||
| 'setVertDistance'
|
| 'setVertDistance'
|
||||||
|
| 'setAngle'
|
||||||
|
| 'setLength'
|
||||||
|
|
||||||
function createCallWrapper(
|
function createCallWrapper(
|
||||||
a: TooTip,
|
a: TooTip,
|
||||||
@ -75,14 +78,38 @@ type TransformMap = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const basicAngledLineCreateNode: TransformInfo['createNode'] =
|
const basicAngledLineCreateNode =
|
||||||
({ referenceSegName, tag }) =>
|
(
|
||||||
(args) =>
|
referenceSeg: 'ang' | 'len' | 'none' = 'none',
|
||||||
createCallWrapper(
|
valToForce: 'ang' | 'len' | 'none' = 'none',
|
||||||
|
varValToUse: 'ang' | 'len' | 'none' = 'none'
|
||||||
|
): TransformInfo['createNode'] =>
|
||||||
|
({ referenceSegName, tag, forceValueUsedInTransform, varValA, varValB }) =>
|
||||||
|
(args) => {
|
||||||
|
const nonForcedAng =
|
||||||
|
varValToUse === 'ang'
|
||||||
|
? varValA
|
||||||
|
: referenceSeg === 'ang'
|
||||||
|
? createSegAngle(referenceSegName)
|
||||||
|
: args[0]
|
||||||
|
const nonForcedLen =
|
||||||
|
varValToUse === 'len'
|
||||||
|
? varValB
|
||||||
|
: referenceSeg === 'len'
|
||||||
|
? createSegLen(referenceSegName)
|
||||||
|
: args[1]
|
||||||
|
const shouldForceAng = valToForce === 'ang' && forceValueUsedInTransform
|
||||||
|
const shouldForceLen = valToForce === 'len' && forceValueUsedInTransform
|
||||||
|
return createCallWrapper(
|
||||||
'angledLine',
|
'angledLine',
|
||||||
[args[0], createSegLen(referenceSegName)],
|
[
|
||||||
tag
|
shouldForceAng ? forceValueUsedInTransform : nonForcedAng,
|
||||||
|
shouldForceLen ? forceValueUsedInTransform : nonForcedLen,
|
||||||
|
],
|
||||||
|
tag,
|
||||||
|
getArgLiteralVal(valToForce === 'ang' ? args[0] : args[1])
|
||||||
)
|
)
|
||||||
|
}
|
||||||
const angledLineAngleCreateNode: TransformInfo['createNode'] =
|
const angledLineAngleCreateNode: TransformInfo['createNode'] =
|
||||||
({ referenceSegName, varValA, tag }) =>
|
({ referenceSegName, varValA, tag }) =>
|
||||||
() =>
|
() =>
|
||||||
@ -133,7 +160,7 @@ const getLegAng = (arg: Value, legAngleVal: BinaryPart) => {
|
|||||||
'+',
|
'+',
|
||||||
legAngleVal,
|
legAngleVal,
|
||||||
])
|
])
|
||||||
return truncatedTo90 == 0 ? legAngleVal : binExp
|
return truncatedTo90 === 0 ? legAngleVal : binExp
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAngleLengthSign = (arg: Value, legAngleVal: BinaryPart) => {
|
const getAngleLengthSign = (arg: Value, legAngleVal: BinaryPart) => {
|
||||||
@ -258,7 +285,7 @@ const transformMap: TransformMap = {
|
|||||||
free: {
|
free: {
|
||||||
equalLength: {
|
equalLength: {
|
||||||
tooltip: 'angledLine',
|
tooltip: 'angledLine',
|
||||||
createNode: basicAngledLineCreateNode,
|
createNode: basicAngledLineCreateNode('len'),
|
||||||
},
|
},
|
||||||
horizontal: {
|
horizontal: {
|
||||||
tooltip: 'xLine',
|
tooltip: 'xLine',
|
||||||
@ -282,13 +309,21 @@ const transformMap: TransformMap = {
|
|||||||
tooltip: 'lineTo',
|
tooltip: 'lineTo',
|
||||||
createNode: setHorzVertDistanceCreateNode('y'),
|
createNode: setHorzVertDistanceCreateNode('y'),
|
||||||
},
|
},
|
||||||
|
setAngle: {
|
||||||
|
tooltip: 'angledLine',
|
||||||
|
createNode: basicAngledLineCreateNode('none', 'ang'),
|
||||||
|
},
|
||||||
|
setLength: {
|
||||||
|
tooltip: 'angledLine',
|
||||||
|
createNode: basicAngledLineCreateNode('none', 'len'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lineTo: {
|
lineTo: {
|
||||||
free: {
|
free: {
|
||||||
equalLength: {
|
equalLength: {
|
||||||
tooltip: 'angledLine',
|
tooltip: 'angledLine',
|
||||||
createNode: basicAngledLineCreateNode,
|
createNode: basicAngledLineCreateNode('len'),
|
||||||
},
|
},
|
||||||
horizontal: {
|
horizontal: {
|
||||||
tooltip: 'xLineTo',
|
tooltip: 'xLineTo',
|
||||||
@ -377,11 +412,15 @@ const transformMap: TransformMap = {
|
|||||||
tag
|
tag
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
setLength: {
|
||||||
|
tooltip: 'angledLine',
|
||||||
|
createNode: basicAngledLineCreateNode('none', 'len', 'ang'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
free: {
|
free: {
|
||||||
equalLength: {
|
equalLength: {
|
||||||
tooltip: 'angledLine',
|
tooltip: 'angledLine',
|
||||||
createNode: basicAngledLineCreateNode,
|
createNode: basicAngledLineCreateNode('len'),
|
||||||
},
|
},
|
||||||
vertical: {
|
vertical: {
|
||||||
tooltip: 'yLine',
|
tooltip: 'yLine',
|
||||||
@ -423,13 +462,17 @@ const transformMap: TransformMap = {
|
|||||||
return createCallWrapper('xLine', val, tag)
|
return createCallWrapper('xLine', val, tag)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
setAngle: {
|
||||||
|
tooltip: 'angledLine',
|
||||||
|
createNode: basicAngledLineCreateNode('len', 'ang', 'len'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
angledLineOfXLength: {
|
angledLineOfXLength: {
|
||||||
free: {
|
free: {
|
||||||
equalLength: {
|
equalLength: {
|
||||||
tooltip: 'angledLine',
|
tooltip: 'angledLine',
|
||||||
createNode: basicAngledLineCreateNode,
|
createNode: basicAngledLineCreateNode('len'),
|
||||||
},
|
},
|
||||||
horizontal: {
|
horizontal: {
|
||||||
tooltip: 'xLine',
|
tooltip: 'xLine',
|
||||||
@ -479,7 +522,7 @@ const transformMap: TransformMap = {
|
|||||||
free: {
|
free: {
|
||||||
equalLength: {
|
equalLength: {
|
||||||
tooltip: 'angledLine',
|
tooltip: 'angledLine',
|
||||||
createNode: basicAngledLineCreateNode,
|
createNode: basicAngledLineCreateNode('len'),
|
||||||
},
|
},
|
||||||
vertical: {
|
vertical: {
|
||||||
tooltip: 'yLine',
|
tooltip: 'yLine',
|
||||||
@ -530,7 +573,7 @@ const transformMap: TransformMap = {
|
|||||||
free: {
|
free: {
|
||||||
equalLength: {
|
equalLength: {
|
||||||
tooltip: 'angledLine',
|
tooltip: 'angledLine',
|
||||||
createNode: basicAngledLineCreateNode,
|
createNode: basicAngledLineCreateNode('len'),
|
||||||
},
|
},
|
||||||
horizontal: {
|
horizontal: {
|
||||||
tooltip: 'xLineTo',
|
tooltip: 'xLineTo',
|
||||||
@ -580,7 +623,7 @@ const transformMap: TransformMap = {
|
|||||||
free: {
|
free: {
|
||||||
equalLength: {
|
equalLength: {
|
||||||
tooltip: 'angledLine',
|
tooltip: 'angledLine',
|
||||||
createNode: basicAngledLineCreateNode,
|
createNode: basicAngledLineCreateNode('len'),
|
||||||
},
|
},
|
||||||
vertical: {
|
vertical: {
|
||||||
tooltip: 'yLineTo',
|
tooltip: 'yLineTo',
|
||||||
@ -924,6 +967,13 @@ function createSegLen(referenceSegName: string): Value {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createSegAngle(referenceSegName: string): Value {
|
||||||
|
return createCallExpression('segAngle', [
|
||||||
|
createLiteral(referenceSegName),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
function createSegEnd(referenceSegName: string, isX: boolean): CallExpression {
|
function createSegEnd(referenceSegName: string, isX: boolean): CallExpression {
|
||||||
return createCallExpression(isX ? 'segEndX' : 'segEndY', [
|
return createCallExpression(isX ? 'segEndX' : 'segEndY', [
|
||||||
createLiteral(referenceSegName),
|
createLiteral(referenceSegName),
|
||||||
|
Reference in New Issue
Block a user