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 { Equal } from './components/Toolbar/Equal'
|
||||
import { SetHorzDistance } from './components/Toolbar/SetHorzDistance'
|
||||
import { SetAngleLength } from './components/Toolbar/SetAngleLength'
|
||||
|
||||
export const Toolbar = () => {
|
||||
const {
|
||||
@ -159,6 +160,8 @@ export const Toolbar = () => {
|
||||
<Equal />
|
||||
<SetHorzDistance horOrVert="setHorzDistance" />
|
||||
<SetHorzDistance horOrVert="setVertDistance" />
|
||||
<SetAngleLength angleOrLength="setAngle" />
|
||||
<SetAngleLength angleOrLength="setLength" />
|
||||
</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,
|
||||
}
|
||||
}
|
||||
} 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') {
|
||||
_programMemory.root[variableName] = {
|
||||
type: 'userVal',
|
||||
|
@ -29,7 +29,6 @@ export function addSketchTo(
|
||||
name = ''
|
||||
): { modifiedAst: Program; id: string; pathToNode: (string | number)[] } {
|
||||
const _node = { ...node }
|
||||
const dumbyStartend = { start: 0, end: 0 }
|
||||
const _name = name || findUniqueName(node, 'part')
|
||||
|
||||
const startSketchAt = createCallExpression('startSketchAt', [
|
||||
|
@ -135,7 +135,7 @@ export function getNodePathFromSourceRange(
|
||||
return path
|
||||
}
|
||||
|
||||
interface PrevVariable<T> {
|
||||
export interface PrevVariable<T> {
|
||||
key: string
|
||||
value: T
|
||||
}
|
||||
|
@ -160,12 +160,15 @@ export const lineTo: SketchLineHelper = {
|
||||
])
|
||||
const callIndex = getLastIndex(pathToNode)
|
||||
if (replaceExisting && createCallback) {
|
||||
const boop = createCallback(newVals, referencedSegment)
|
||||
pipe.body[callIndex] = boop.callExp
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
newVals,
|
||||
referencedSegment
|
||||
)
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
pathToNode,
|
||||
valueUsedInTransform: boop.valueUsedInTransform,
|
||||
valueUsedInTransform: valueUsedInTransform,
|
||||
}
|
||||
} else {
|
||||
pipe.body = [...pipe.body, newLine]
|
||||
@ -599,16 +602,23 @@ export const angledLine: SketchLineHelper = {
|
||||
|
||||
const newAngleVal = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
const newLengthVal = createLiteral(roundOff(getLength(from, to), 2))
|
||||
const newLine = createCallback
|
||||
? createCallback([newAngleVal, newLengthVal]).callExp
|
||||
: createCallExpression('angledLine', [
|
||||
const newLine = createCallExpression('angledLine', [
|
||||
createArrayExpression([newAngleVal, newLengthVal]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
|
||||
const callIndex = getLastIndex(pathToNode)
|
||||
if (replaceExisting) {
|
||||
pipe.body[callIndex] = newLine
|
||||
if (replaceExisting && createCallback) {
|
||||
const { callExp, valueUsedInTransform } = createCallback([
|
||||
newAngleVal,
|
||||
newLengthVal,
|
||||
])
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
pathToNode,
|
||||
valueUsedInTransform,
|
||||
}
|
||||
} else {
|
||||
pipe.body = [...pipe.body, newLine]
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
import {
|
||||
createBinaryExpression,
|
||||
createCallExpression,
|
||||
createIdentifier,
|
||||
createLiteral,
|
||||
createPipeSubstitution,
|
||||
createUnaryExpression,
|
||||
@ -40,6 +41,8 @@ export type ConstraintType =
|
||||
| 'equalangle'
|
||||
| 'setHorzDistance'
|
||||
| 'setVertDistance'
|
||||
| 'setAngle'
|
||||
| 'setLength'
|
||||
|
||||
function createCallWrapper(
|
||||
a: TooTip,
|
||||
@ -75,14 +78,38 @@ type TransformMap = {
|
||||
}
|
||||
}
|
||||
|
||||
const basicAngledLineCreateNode: TransformInfo['createNode'] =
|
||||
({ referenceSegName, tag }) =>
|
||||
(args) =>
|
||||
createCallWrapper(
|
||||
const basicAngledLineCreateNode =
|
||||
(
|
||||
referenceSeg: 'ang' | 'len' | 'none' = 'none',
|
||||
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',
|
||||
[args[0], createSegLen(referenceSegName)],
|
||||
tag
|
||||
[
|
||||
shouldForceAng ? forceValueUsedInTransform : nonForcedAng,
|
||||
shouldForceLen ? forceValueUsedInTransform : nonForcedLen,
|
||||
],
|
||||
tag,
|
||||
getArgLiteralVal(valToForce === 'ang' ? args[0] : args[1])
|
||||
)
|
||||
}
|
||||
const angledLineAngleCreateNode: TransformInfo['createNode'] =
|
||||
({ referenceSegName, varValA, tag }) =>
|
||||
() =>
|
||||
@ -133,7 +160,7 @@ const getLegAng = (arg: Value, legAngleVal: BinaryPart) => {
|
||||
'+',
|
||||
legAngleVal,
|
||||
])
|
||||
return truncatedTo90 == 0 ? legAngleVal : binExp
|
||||
return truncatedTo90 === 0 ? legAngleVal : binExp
|
||||
}
|
||||
|
||||
const getAngleLengthSign = (arg: Value, legAngleVal: BinaryPart) => {
|
||||
@ -258,7 +285,7 @@ const transformMap: TransformMap = {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
createNode: basicAngledLineCreateNode('len'),
|
||||
},
|
||||
horizontal: {
|
||||
tooltip: 'xLine',
|
||||
@ -282,13 +309,21 @@ const transformMap: TransformMap = {
|
||||
tooltip: 'lineTo',
|
||||
createNode: setHorzVertDistanceCreateNode('y'),
|
||||
},
|
||||
setAngle: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode('none', 'ang'),
|
||||
},
|
||||
setLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode('none', 'len'),
|
||||
},
|
||||
},
|
||||
},
|
||||
lineTo: {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
createNode: basicAngledLineCreateNode('len'),
|
||||
},
|
||||
horizontal: {
|
||||
tooltip: 'xLineTo',
|
||||
@ -377,11 +412,15 @@ const transformMap: TransformMap = {
|
||||
tag
|
||||
),
|
||||
},
|
||||
setLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode('none', 'len', 'ang'),
|
||||
},
|
||||
},
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
createNode: basicAngledLineCreateNode('len'),
|
||||
},
|
||||
vertical: {
|
||||
tooltip: 'yLine',
|
||||
@ -423,13 +462,17 @@ const transformMap: TransformMap = {
|
||||
return createCallWrapper('xLine', val, tag)
|
||||
},
|
||||
},
|
||||
setAngle: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode('len', 'ang', 'len'),
|
||||
},
|
||||
},
|
||||
},
|
||||
angledLineOfXLength: {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
createNode: basicAngledLineCreateNode('len'),
|
||||
},
|
||||
horizontal: {
|
||||
tooltip: 'xLine',
|
||||
@ -479,7 +522,7 @@ const transformMap: TransformMap = {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
createNode: basicAngledLineCreateNode('len'),
|
||||
},
|
||||
vertical: {
|
||||
tooltip: 'yLine',
|
||||
@ -530,7 +573,7 @@ const transformMap: TransformMap = {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
createNode: basicAngledLineCreateNode('len'),
|
||||
},
|
||||
horizontal: {
|
||||
tooltip: 'xLineTo',
|
||||
@ -580,7 +623,7 @@ const transformMap: TransformMap = {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
createNode: basicAngledLineCreateNode('len'),
|
||||
},
|
||||
vertical: {
|
||||
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 {
|
||||
return createCallExpression(isX ? 'segEndX' : 'segEndY', [
|
||||
createLiteral(referenceSegName),
|
||||
|
Reference in New Issue
Block a user