Add intersect constraint UI (#71)
* Add intersect constraint UI * add more combos for intersect constraint
This commit is contained in:
@ -22,7 +22,7 @@
|
|||||||
"react-json-view": "^1.21.3",
|
"react-json-view": "^1.21.3",
|
||||||
"react-modal-promise": "^1.0.2",
|
"react-modal-promise": "^1.0.2",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"sketch-helpers": "^0.0.1",
|
"sketch-helpers": "^0.0.2",
|
||||||
"swr": "^2.0.4",
|
"swr": "^2.0.4",
|
||||||
"three": "^0.146.0",
|
"three": "^0.146.0",
|
||||||
"typescript": "^4.4.2",
|
"typescript": "^4.4.2",
|
||||||
|
@ -4,6 +4,7 @@ import { getNodePathFromSourceRange } from './lang/queryAst'
|
|||||||
import { HorzVert } from './components/Toolbar/HorzVert'
|
import { HorzVert } from './components/Toolbar/HorzVert'
|
||||||
import { EqualLength } from './components/Toolbar/EqualLength'
|
import { EqualLength } from './components/Toolbar/EqualLength'
|
||||||
import { EqualAngle } from './components/Toolbar/EqualAngle'
|
import { EqualAngle } from './components/Toolbar/EqualAngle'
|
||||||
|
import { Intersect } from './components/Toolbar/Intersect'
|
||||||
import { SetHorzDistance } from './components/Toolbar/SetHorzDistance'
|
import { SetHorzDistance } from './components/Toolbar/SetHorzDistance'
|
||||||
import { SetAngleLength } from './components/Toolbar/SetAngleLength'
|
import { SetAngleLength } from './components/Toolbar/SetAngleLength'
|
||||||
|
|
||||||
@ -123,38 +124,42 @@ export const Toolbar = () => {
|
|||||||
Exit sketch
|
Exit sketch
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{toolTips.map((sketchFnName) => {
|
{toolTips
|
||||||
if (
|
.filter(
|
||||||
guiMode.mode !== 'sketch' ||
|
(sketchFnName) => !['angledLineThatIntersects'].includes(sketchFnName)
|
||||||
!('isTooltip' in guiMode || guiMode.sketchMode === 'sketchEdit')
|
|
||||||
)
|
)
|
||||||
return null
|
.map((sketchFnName) => {
|
||||||
return (
|
if (
|
||||||
<button
|
guiMode.mode !== 'sketch' ||
|
||||||
key={sketchFnName}
|
!('isTooltip' in guiMode || guiMode.sketchMode === 'sketchEdit')
|
||||||
className={`border m-0.5 px-0.5 rounded text-xs ${
|
)
|
||||||
guiMode.sketchMode === sketchFnName && 'bg-gray-400'
|
return null
|
||||||
}`}
|
return (
|
||||||
onClick={() =>
|
<button
|
||||||
setGuiMode({
|
key={sketchFnName}
|
||||||
...guiMode,
|
className={`border m-0.5 px-0.5 rounded text-xs ${
|
||||||
...(guiMode.sketchMode === sketchFnName
|
guiMode.sketchMode === sketchFnName && 'bg-gray-400'
|
||||||
? {
|
}`}
|
||||||
sketchMode: 'sketchEdit',
|
onClick={() =>
|
||||||
// todo: ...guiMod is adding isTooltip: true, will probably just fix with xstate migtaion
|
setGuiMode({
|
||||||
}
|
...guiMode,
|
||||||
: {
|
...(guiMode.sketchMode === sketchFnName
|
||||||
sketchMode: sketchFnName,
|
? {
|
||||||
isTooltip: true,
|
sketchMode: 'sketchEdit',
|
||||||
}),
|
// todo: ...guiMod is adding isTooltip: true, will probably just fix with xstate migtaion
|
||||||
})
|
}
|
||||||
}
|
: {
|
||||||
>
|
sketchMode: sketchFnName,
|
||||||
{sketchFnName}
|
isTooltip: true,
|
||||||
{guiMode.sketchMode === sketchFnName && '✅'}
|
}),
|
||||||
</button>
|
})
|
||||||
)
|
}
|
||||||
})}
|
>
|
||||||
|
{sketchFnName}
|
||||||
|
{guiMode.sketchMode === sketchFnName && '✅'}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
<br></br>
|
<br></br>
|
||||||
<HorzVert horOrVert="horizontal" />
|
<HorzVert horOrVert="horizontal" />
|
||||||
<HorzVert horOrVert="vertical" />
|
<HorzVert horOrVert="vertical" />
|
||||||
@ -164,6 +169,7 @@ export const Toolbar = () => {
|
|||||||
<SetHorzDistance horOrVert="setVertDistance" />
|
<SetHorzDistance horOrVert="setVertDistance" />
|
||||||
<SetAngleLength angleOrLength="setAngle" />
|
<SetAngleLength angleOrLength="setAngle" />
|
||||||
<SetAngleLength angleOrLength="setLength" />
|
<SetAngleLength angleOrLength="setLength" />
|
||||||
|
<Intersect />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
146
src/components/Toolbar/Intersect.tsx
Normal file
146
src/components/Toolbar/Intersect.tsx
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { create } from 'react-modal-promise'
|
||||||
|
import { toolTips, useStore } from '../../useStore'
|
||||||
|
import { Value, VariableDeclarator } from '../../lang/abstractSyntaxTree'
|
||||||
|
import {
|
||||||
|
getNodePathFromSourceRange,
|
||||||
|
getNodeFromPath,
|
||||||
|
} from '../../lang/queryAst'
|
||||||
|
import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints'
|
||||||
|
import {
|
||||||
|
TransformInfo,
|
||||||
|
transformSecondarySketchLinesTagFirst,
|
||||||
|
getTransformInfos,
|
||||||
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { GetInfoModal } from '../SetHorVertDistanceModal'
|
||||||
|
import {
|
||||||
|
createIdentifier,
|
||||||
|
createVariableDeclaration,
|
||||||
|
} from '../../lang/modifyAst'
|
||||||
|
|
||||||
|
const getModalInfo = create(GetInfoModal as any)
|
||||||
|
|
||||||
|
export const Intersect = () => {
|
||||||
|
const { guiMode, selectionRanges, ast, programMemory, updateAst } = useStore(
|
||||||
|
(s) => ({
|
||||||
|
guiMode: s.guiMode,
|
||||||
|
ast: s.ast,
|
||||||
|
updateAst: s.updateAst,
|
||||||
|
selectionRanges: s.selectionRanges,
|
||||||
|
programMemory: s.programMemory,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const [enable, setEnable] = 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,
|
||||||
|
'startSketchAt', // TODO probably a better place for this to live
|
||||||
|
].includes(node.callee.name as any)
|
||||||
|
)
|
||||||
|
|
||||||
|
const theTransforms = getTransformInfos(
|
||||||
|
selectionRanges.slice(1),
|
||||||
|
ast,
|
||||||
|
'intersect'
|
||||||
|
)
|
||||||
|
setTransformInfos(theTransforms)
|
||||||
|
|
||||||
|
const _enableEqual =
|
||||||
|
secondaryVarDecs.length === 1 &&
|
||||||
|
isAllTooltips &&
|
||||||
|
isOthersLinkedToPrimary &&
|
||||||
|
theTransforms.every(Boolean)
|
||||||
|
setEnable(_enableEqual)
|
||||||
|
}, [guiMode, selectionRanges])
|
||||||
|
if (guiMode.mode !== 'sketch') return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
if (transformInfos && ast) {
|
||||||
|
const { modifiedAst, tagInfo, valueUsedInTransform } =
|
||||||
|
transformSecondarySketchLinesTagFirst({
|
||||||
|
ast: JSON.parse(JSON.stringify(ast)),
|
||||||
|
selectionRanges,
|
||||||
|
transformInfos,
|
||||||
|
programMemory,
|
||||||
|
})
|
||||||
|
const {
|
||||||
|
segName,
|
||||||
|
value,
|
||||||
|
valueNode,
|
||||||
|
variableName,
|
||||||
|
newVariableInsertIndex,
|
||||||
|
}: {
|
||||||
|
segName: string
|
||||||
|
value: number
|
||||||
|
valueNode: Value
|
||||||
|
variableName?: string
|
||||||
|
newVariableInsertIndex: number
|
||||||
|
} = await getModalInfo({
|
||||||
|
segName: tagInfo?.tag,
|
||||||
|
isSegNameEditable: !tagInfo?.isTagExisting,
|
||||||
|
value: valueUsedInTransform,
|
||||||
|
initialVariableName: 'offset',
|
||||||
|
} as any)
|
||||||
|
if (segName === tagInfo?.tag && value === valueUsedInTransform) {
|
||||||
|
updateAst(modifiedAst)
|
||||||
|
} else {
|
||||||
|
// transform again but forcing certain values
|
||||||
|
const { modifiedAst: _modifiedAst } =
|
||||||
|
transformSecondarySketchLinesTagFirst({
|
||||||
|
ast,
|
||||||
|
selectionRanges,
|
||||||
|
transformInfos,
|
||||||
|
programMemory,
|
||||||
|
forceSegName: segName,
|
||||||
|
forceValueUsedInTransform: variableName
|
||||||
|
? createIdentifier(variableName)
|
||||||
|
: valueNode,
|
||||||
|
})
|
||||||
|
if (variableName) {
|
||||||
|
const newBody = [..._modifiedAst.body]
|
||||||
|
newBody.splice(
|
||||||
|
newVariableInsertIndex,
|
||||||
|
0,
|
||||||
|
createVariableDeclaration(variableName, valueNode)
|
||||||
|
)
|
||||||
|
_modifiedAst.body = newBody
|
||||||
|
}
|
||||||
|
updateAst(_modifiedAst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={`border m-1 px-1 rounded text-xs ${
|
||||||
|
enable ? 'bg-gray-50 text-gray-800' : 'bg-gray-200 text-gray-400'
|
||||||
|
}`}
|
||||||
|
disabled={!enable}
|
||||||
|
>
|
||||||
|
Intersect
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
@ -507,7 +507,8 @@ function executeObjectExpression(
|
|||||||
} else if (property.value.type === 'BinaryExpression') {
|
} else if (property.value.type === 'BinaryExpression') {
|
||||||
obj[property.key.name] = getBinaryExpressionResult(
|
obj[property.key.name] = getBinaryExpressionResult(
|
||||||
property.value,
|
property.value,
|
||||||
_programMemory
|
_programMemory,
|
||||||
|
_pipeInfo
|
||||||
)
|
)
|
||||||
} else if (property.value.type === 'PipeExpression') {
|
} else if (property.value.type === 'PipeExpression') {
|
||||||
obj[property.key.name] = getPipeExpressionResult(
|
obj[property.key.name] = getPipeExpressionResult(
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
Value,
|
Value,
|
||||||
Literal,
|
Literal,
|
||||||
|
VariableDeclaration,
|
||||||
} from '../abstractSyntaxTree'
|
} from '../abstractSyntaxTree'
|
||||||
import {
|
import {
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
@ -41,6 +42,10 @@ import {
|
|||||||
} from '../modifyAst'
|
} from '../modifyAst'
|
||||||
import { roundOff, getLength, getAngle } from '../../lib/utils'
|
import { roundOff, getLength, getAngle } from '../../lib/utils'
|
||||||
import { getSketchSegmentIndexFromSourceRange } from './sketchConstraints'
|
import { getSketchSegmentIndexFromSourceRange } from './sketchConstraints'
|
||||||
|
import {
|
||||||
|
intersectionWithParallelLine,
|
||||||
|
perpendicularDistance,
|
||||||
|
} from 'sketch-helpers'
|
||||||
|
|
||||||
export type Coords2d = [number, number]
|
export type Coords2d = [number, number]
|
||||||
|
|
||||||
@ -312,7 +317,7 @@ export const line: SketchLineHelper = {
|
|||||||
},
|
},
|
||||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const { node: callExpression, path } = getNodeFromPath<CallExpression>(
|
const { node: callExpression } = getNodeFromPath<CallExpression>(
|
||||||
_node,
|
_node,
|
||||||
pathToNode
|
pathToNode
|
||||||
)
|
)
|
||||||
@ -338,9 +343,9 @@ export const line: SketchLineHelper = {
|
|||||||
) {
|
) {
|
||||||
toProp.value = toArrExp
|
toProp.value = toArrExp
|
||||||
}
|
}
|
||||||
|
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
||||||
} else {
|
} else {
|
||||||
mutateArrExp(callExpression.arguments?.[0], toArrExp) ||
|
mutateArrExp(callExpression.arguments?.[0], toArrExp)
|
||||||
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -397,7 +402,7 @@ export const xLineTo: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
updateArgs: ({ node, pathToNode, to }) => {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const { node: callExpression } = getNodeFromPath<CallExpression>(
|
const { node: callExpression } = getNodeFromPath<CallExpression>(
|
||||||
_node,
|
_node,
|
||||||
@ -1084,6 +1089,126 @@ export const angledLineToY: SketchLineHelper = {
|
|||||||
addTag: addTagWithTo('angleTo'),
|
addTag: addTagWithTo('angleTo'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const angledLineThatIntersects: SketchLineHelper = {
|
||||||
|
fn: (
|
||||||
|
{ sourceRange, programMemory },
|
||||||
|
data: {
|
||||||
|
angle: number
|
||||||
|
intersectTag: string
|
||||||
|
offset?: number
|
||||||
|
tag?: string
|
||||||
|
},
|
||||||
|
previousSketch: SketchGroup
|
||||||
|
) => {
|
||||||
|
if (!previousSketch) throw new Error('lineTo must be called after lineTo')
|
||||||
|
const intersectPath = previousSketch.value.find(
|
||||||
|
({ name }) => name === data.intersectTag
|
||||||
|
)
|
||||||
|
if (!intersectPath) throw new Error('intersectTag must match a line')
|
||||||
|
const from = getCoordsFromPaths(
|
||||||
|
previousSketch,
|
||||||
|
previousSketch.value.length - 1
|
||||||
|
)
|
||||||
|
const to = intersectionWithParallelLine({
|
||||||
|
line1: [intersectPath.from, intersectPath.to],
|
||||||
|
line1Offset: data.offset || 0,
|
||||||
|
line2Point: from,
|
||||||
|
line2Angle: data.angle,
|
||||||
|
})
|
||||||
|
return lineTo.fn(
|
||||||
|
{ sourceRange, programMemory },
|
||||||
|
{ to, tag: data.tag },
|
||||||
|
previousSketch
|
||||||
|
)
|
||||||
|
},
|
||||||
|
add: ({
|
||||||
|
node,
|
||||||
|
pathToNode,
|
||||||
|
to,
|
||||||
|
from,
|
||||||
|
createCallback,
|
||||||
|
replaceExisting,
|
||||||
|
referencedSegment,
|
||||||
|
}) => {
|
||||||
|
const _node = { ...node }
|
||||||
|
const { node: pipe } = getNodeFromPath<PipeExpression>(
|
||||||
|
_node,
|
||||||
|
pathToNode,
|
||||||
|
'PipeExpression'
|
||||||
|
)
|
||||||
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||||
|
if (!referencedSegment)
|
||||||
|
throw new Error('referencedSegment must be provided')
|
||||||
|
const offset = createLiteral(
|
||||||
|
roundOff(
|
||||||
|
perpendicularDistance(
|
||||||
|
referencedSegment?.from,
|
||||||
|
referencedSegment?.to,
|
||||||
|
to
|
||||||
|
),
|
||||||
|
2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (replaceExisting && createCallback) {
|
||||||
|
const { callExp, valueUsedInTransform } = createCallback([angle, offset])
|
||||||
|
const callIndex = getLastIndex(pathToNode)
|
||||||
|
pipe.body[callIndex] = callExp
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode,
|
||||||
|
valueUsedInTransform,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('not implemented')
|
||||||
|
},
|
||||||
|
updateArgs: ({ node, pathToNode, to, from, previousProgramMemory }) => {
|
||||||
|
const _node = { ...node }
|
||||||
|
const { node: callExpression, path } = getNodeFromPath<CallExpression>(
|
||||||
|
_node,
|
||||||
|
pathToNode
|
||||||
|
)
|
||||||
|
const angle = roundOff(getAngle(from, to), 0)
|
||||||
|
|
||||||
|
const firstArg = callExpression.arguments?.[0]
|
||||||
|
const intersectTag =
|
||||||
|
firstArg.type === 'ObjectExpression'
|
||||||
|
? firstArg.properties.find((p) => p.key.name === 'intersectTag')
|
||||||
|
?.value || createLiteral('')
|
||||||
|
: createLiteral('')
|
||||||
|
const intersectTagName =
|
||||||
|
intersectTag.type === 'Literal' ? intersectTag.value : ''
|
||||||
|
const { node: varDec } = getNodeFromPath<VariableDeclaration>(
|
||||||
|
_node,
|
||||||
|
pathToNode,
|
||||||
|
'VariableDeclaration'
|
||||||
|
)
|
||||||
|
|
||||||
|
const varName = varDec.declarations[0].id.name
|
||||||
|
const sketchGroup = previousProgramMemory.root[varName] as SketchGroup
|
||||||
|
const intersectPath = sketchGroup.value.find(
|
||||||
|
({ name }) => name === intersectTagName
|
||||||
|
)
|
||||||
|
let offset = 0
|
||||||
|
if (intersectPath) {
|
||||||
|
offset = roundOff(
|
||||||
|
perpendicularDistance(intersectPath?.from, intersectPath?.to, to),
|
||||||
|
2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const angleLit = createLiteral(angle)
|
||||||
|
|
||||||
|
mutateObjExpProp(firstArg, angleLit, 'angle')
|
||||||
|
mutateObjExpProp(firstArg, createLiteral(offset), 'offset')
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addTag: addTagWithTo('angleTo'), // TODO might be wrong
|
||||||
|
}
|
||||||
|
|
||||||
export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
||||||
line,
|
line,
|
||||||
lineTo,
|
lineTo,
|
||||||
@ -1096,6 +1221,7 @@ export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
|||||||
angledLineOfYLength,
|
angledLineOfYLength,
|
||||||
angledLineToX,
|
angledLineToX,
|
||||||
angledLineToY,
|
angledLineToY,
|
||||||
|
angledLineThatIntersects,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export function changeSketchArguments(
|
export function changeSketchArguments(
|
||||||
@ -1116,6 +1242,7 @@ export function changeSketchArguments(
|
|||||||
|
|
||||||
if (callExpression?.callee?.name in sketchLineHelperMap) {
|
if (callExpression?.callee?.name in sketchLineHelperMap) {
|
||||||
const { updateArgs } = sketchLineHelperMap[callExpression.callee.name]
|
const { updateArgs } = sketchLineHelperMap[callExpression.callee.name]
|
||||||
|
if (!updateArgs) throw new Error('not a sketch line helper')
|
||||||
return updateArgs({
|
return updateArgs({
|
||||||
node: _node,
|
node: _node,
|
||||||
previousProgramMemory: programMemory,
|
previousProgramMemory: programMemory,
|
||||||
@ -1235,7 +1362,8 @@ export function replaceSketchLine({
|
|||||||
createCallback: TransformCallback
|
createCallback: TransformCallback
|
||||||
referencedSegment?: Path
|
referencedSegment?: Path
|
||||||
}): { modifiedAst: Program; valueUsedInTransform?: number } {
|
}): { modifiedAst: Program; valueUsedInTransform?: number } {
|
||||||
if (!toolTips.includes(fnName)) throw new Error('not a tooltip')
|
if (![...toolTips, 'intersect'].includes(fnName))
|
||||||
|
throw new Error('not a tooltip')
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const thePath = getNodePathFromSourceRange(_node, sourceRange)
|
const thePath = getNodePathFromSourceRange(_node, sourceRange)
|
||||||
|
|
||||||
@ -1540,6 +1668,26 @@ function getFirstArgValuesForXYLineFns(callExpression: CallExpression): {
|
|||||||
throw new Error('expected ArrayExpression or ObjectExpression')
|
throw new Error('expected ArrayExpression or ObjectExpression')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAngledLineThatIntersects = (
|
||||||
|
callExp: CallExpression
|
||||||
|
): {
|
||||||
|
val: [Value, Value]
|
||||||
|
tag?: Value
|
||||||
|
} => {
|
||||||
|
const firstArg = callExp.arguments[0]
|
||||||
|
if (firstArg.type === 'ObjectExpression') {
|
||||||
|
const tag = firstArg.properties.find((p) => p.key.name === 'tag')?.value
|
||||||
|
const angle = firstArg.properties.find((p) => p.key.name === 'angle')?.value
|
||||||
|
const offset = firstArg.properties.find(
|
||||||
|
(p) => p.key.name === 'offset'
|
||||||
|
)?.value
|
||||||
|
if (angle && offset) {
|
||||||
|
return { val: [angle, offset], tag }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('expected ArrayExpression or ObjectExpression')
|
||||||
|
}
|
||||||
|
|
||||||
export function getFirstArg(callExp: CallExpression): {
|
export function getFirstArg(callExp: CallExpression): {
|
||||||
val: Value | [Value, Value]
|
val: Value | [Value, Value]
|
||||||
tag?: Value
|
tag?: Value
|
||||||
@ -1565,5 +1713,8 @@ export function getFirstArg(callExp: CallExpression): {
|
|||||||
if (['startSketchAt'].includes(name)) {
|
if (['startSketchAt'].includes(name)) {
|
||||||
return getFirstArgValuesForXYLineFns(callExp)
|
return getFirstArgValuesForXYLineFns(callExp)
|
||||||
}
|
}
|
||||||
|
if (['angledLineThatIntersects'].includes(name)) {
|
||||||
|
return getAngledLineThatIntersects(callExp)
|
||||||
|
}
|
||||||
throw new Error('unexpected call expression')
|
throw new Error('unexpected call expression')
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
createCallExpression,
|
createCallExpression,
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
|
createObjectExpression,
|
||||||
createPipeSubstitution,
|
createPipeSubstitution,
|
||||||
createUnaryExpression,
|
createUnaryExpression,
|
||||||
giveSketchFnCallTag,
|
giveSketchFnCallTag,
|
||||||
@ -43,6 +44,7 @@ export type ConstraintType =
|
|||||||
| 'setVertDistance'
|
| 'setVertDistance'
|
||||||
| 'setAngle'
|
| 'setAngle'
|
||||||
| 'setLength'
|
| 'setLength'
|
||||||
|
| 'intersect'
|
||||||
|
|
||||||
function createCallWrapper(
|
function createCallWrapper(
|
||||||
a: TooTip,
|
a: TooTip,
|
||||||
@ -59,6 +61,38 @@ function createCallWrapper(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function intersectCallWrapper({
|
||||||
|
fnName,
|
||||||
|
angleVal,
|
||||||
|
offsetVal,
|
||||||
|
intersectTag,
|
||||||
|
tag,
|
||||||
|
valueUsedInTransform,
|
||||||
|
}: {
|
||||||
|
fnName: string
|
||||||
|
angleVal: Value
|
||||||
|
offsetVal: Value
|
||||||
|
intersectTag: Value
|
||||||
|
tag?: Value
|
||||||
|
valueUsedInTransform?: number
|
||||||
|
}): ReturnType<TransformCallback> {
|
||||||
|
const firstArg: any = {
|
||||||
|
angle: angleVal,
|
||||||
|
offset: offsetVal,
|
||||||
|
intersectTag,
|
||||||
|
}
|
||||||
|
if (tag) {
|
||||||
|
firstArg['tag'] = tag
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
callExp: createCallExpression(fnName, [
|
||||||
|
createObjectExpression(firstArg),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
]),
|
||||||
|
valueUsedInTransform,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type TransformInfo = {
|
export type TransformInfo = {
|
||||||
tooltip: TooTip
|
tooltip: TooTip
|
||||||
createNode: (a: {
|
createNode: (a: {
|
||||||
@ -71,7 +105,7 @@ export type TransformInfo = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TransformMap = {
|
type TransformMap = {
|
||||||
[key in TooTip]: {
|
[key in TooTip]?: {
|
||||||
[key in LineInputsType | 'free']?: {
|
[key in LineInputsType | 'free']?: {
|
||||||
[key in ConstraintType]?: TransformInfo
|
[key in ConstraintType]?: TransformInfo
|
||||||
}
|
}
|
||||||
@ -307,6 +341,44 @@ const setHorzVertDistanceConstraintLineCreateNode =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setAngledIntersectLineForLines: TransformInfo['createNode'] =
|
||||||
|
({ referenceSegName, tag, forceValueUsedInTransform }) =>
|
||||||
|
(args) => {
|
||||||
|
const valueUsedInTransform = roundOff(
|
||||||
|
args[1].type === 'Literal' ? Number(args[1].value) : 0,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
const angle = args[0].type === 'Literal' ? Number(args[0].value) : 0
|
||||||
|
return intersectCallWrapper({
|
||||||
|
fnName: 'angledLineThatIntersects',
|
||||||
|
angleVal: createLiteral(angle),
|
||||||
|
offsetVal:
|
||||||
|
forceValueUsedInTransform || createLiteral(valueUsedInTransform),
|
||||||
|
intersectTag: createLiteral(referenceSegName),
|
||||||
|
tag,
|
||||||
|
valueUsedInTransform,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setAngledIntersectForAngledLines: TransformInfo['createNode'] =
|
||||||
|
({ referenceSegName, tag, forceValueUsedInTransform, varValA }) =>
|
||||||
|
(args) => {
|
||||||
|
const valueUsedInTransform = roundOff(
|
||||||
|
args[1].type === 'Literal' ? Number(args[1].value) : 0,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
// const angle = args[0].type === 'Literal' ? Number(args[0].value) : 0
|
||||||
|
return intersectCallWrapper({
|
||||||
|
fnName: 'angledLineThatIntersects',
|
||||||
|
angleVal: varValA,
|
||||||
|
offsetVal:
|
||||||
|
forceValueUsedInTransform || createLiteral(valueUsedInTransform),
|
||||||
|
intersectTag: createLiteral(referenceSegName),
|
||||||
|
tag,
|
||||||
|
valueUsedInTransform,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const transformMap: TransformMap = {
|
const transformMap: TransformMap = {
|
||||||
line: {
|
line: {
|
||||||
xRelative: {
|
xRelative: {
|
||||||
@ -404,6 +476,10 @@ const transformMap: TransformMap = {
|
|||||||
tooltip: 'angledLine',
|
tooltip: 'angledLine',
|
||||||
createNode: basicAngledLineCreateNode('ang'),
|
createNode: basicAngledLineCreateNode('ang'),
|
||||||
},
|
},
|
||||||
|
intersect: {
|
||||||
|
tooltip: 'angledLineThatIntersects',
|
||||||
|
createNode: setAngledIntersectLineForLines,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lineTo: {
|
lineTo: {
|
||||||
@ -524,6 +600,10 @@ const transformMap: TransformMap = {
|
|||||||
tooltip: 'angledLineToX',
|
tooltip: 'angledLineToX',
|
||||||
createNode: setHorzVertDistanceForAngleLineCreateNode('x'),
|
createNode: setHorzVertDistanceForAngleLineCreateNode('x'),
|
||||||
},
|
},
|
||||||
|
intersect: {
|
||||||
|
tooltip: 'angledLineThatIntersects',
|
||||||
|
createNode: setAngledIntersectForAngledLines,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
free: {
|
free: {
|
||||||
equalLength: {
|
equalLength: {
|
||||||
@ -798,6 +878,10 @@ const transformMap: TransformMap = {
|
|||||||
tooltip: 'xLine',
|
tooltip: 'xLine',
|
||||||
createNode: xyLineSetLength('xLine'),
|
createNode: xyLineSetLength('xLine'),
|
||||||
},
|
},
|
||||||
|
intersect: {
|
||||||
|
tooltip: 'angledLineThatIntersects',
|
||||||
|
createNode: setAngledIntersectLineForLines,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
yLine: {
|
yLine: {
|
||||||
@ -817,6 +901,10 @@ const transformMap: TransformMap = {
|
|||||||
tooltip: 'yLineTo',
|
tooltip: 'yLineTo',
|
||||||
createNode: setHorVertDistanceForXYLines('y'),
|
createNode: setHorVertDistanceForXYLines('y'),
|
||||||
},
|
},
|
||||||
|
intersect: {
|
||||||
|
tooltip: 'angledLineThatIntersects',
|
||||||
|
createNode: setAngledIntersectLineForLines,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
xLineTo: {
|
xLineTo: {
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
angledLineToY,
|
angledLineToY,
|
||||||
closee,
|
closee,
|
||||||
startSketchAt,
|
startSketchAt,
|
||||||
getCoordsFromPaths,
|
angledLineThatIntersects,
|
||||||
} from './sketch'
|
} from './sketch'
|
||||||
import {
|
import {
|
||||||
segLen,
|
segLen,
|
||||||
@ -29,7 +29,6 @@ import { Quaternion, Vector3 } from 'three'
|
|||||||
import { SketchGroup, ExtrudeGroup, Position, Rotation } from '../executor'
|
import { SketchGroup, ExtrudeGroup, Position, Rotation } from '../executor'
|
||||||
|
|
||||||
import { InternalFn, InternalFnNames, InternalFirstArg } from './stdTypes'
|
import { InternalFn, InternalFnNames, InternalFirstArg } from './stdTypes'
|
||||||
import { intersectionWithParallelLine } from 'sketch-helpers'
|
|
||||||
|
|
||||||
const transform: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
|
const transform: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
|
||||||
{ sourceRange }: InternalFirstArg,
|
{ sourceRange }: InternalFirstArg,
|
||||||
@ -82,38 +81,6 @@ const translate: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const angledLineThatIntersects: InternalFn = (
|
|
||||||
{ sourceRange, programMemory },
|
|
||||||
data: {
|
|
||||||
angle: number
|
|
||||||
intersectTag: string
|
|
||||||
offset?: number
|
|
||||||
tag?: string
|
|
||||||
},
|
|
||||||
previousSketch: SketchGroup
|
|
||||||
) => {
|
|
||||||
if (!previousSketch) throw new Error('lineTo must be called after lineTo')
|
|
||||||
const intersectPath = previousSketch.value.find(
|
|
||||||
({ name }) => name === data.intersectTag
|
|
||||||
)
|
|
||||||
if (!intersectPath) throw new Error('intersectTag must match a line')
|
|
||||||
const from = getCoordsFromPaths(
|
|
||||||
previousSketch,
|
|
||||||
previousSketch.value.length - 1
|
|
||||||
)
|
|
||||||
const to = intersectionWithParallelLine({
|
|
||||||
line1: [intersectPath.from, intersectPath.to],
|
|
||||||
line1Offset: data.offset || 0,
|
|
||||||
line2Point: from,
|
|
||||||
line2Angle: data.angle,
|
|
||||||
})
|
|
||||||
return lineTo.fn(
|
|
||||||
{ sourceRange, programMemory },
|
|
||||||
{ to, tag: data.tag },
|
|
||||||
previousSketch
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const min: InternalFn = (_, a: number, b: number): number => Math.min(a, b)
|
const min: InternalFn = (_, a: number, b: number): number => Math.min(a, b)
|
||||||
|
|
||||||
const legLen: InternalFn = (_, hypotenuse: number, leg: number): number =>
|
const legLen: InternalFn = (_, hypotenuse: number, leg: number): number =>
|
||||||
@ -158,7 +125,7 @@ export const internalFns: { [key in InternalFnNames]: InternalFn } = {
|
|||||||
angledLineToX: angledLineToX.fn,
|
angledLineToX: angledLineToX.fn,
|
||||||
angledLineOfYLength: angledLineOfYLength.fn,
|
angledLineOfYLength: angledLineOfYLength.fn,
|
||||||
angledLineToY: angledLineToY.fn,
|
angledLineToY: angledLineToY.fn,
|
||||||
angledLineThatIntersects,
|
angledLineThatIntersects: angledLineThatIntersects.fn,
|
||||||
startSketchAt,
|
startSketchAt,
|
||||||
closee,
|
closee,
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ export type TooTip =
|
|||||||
| 'yLine'
|
| 'yLine'
|
||||||
| 'xLineTo'
|
| 'xLineTo'
|
||||||
| 'yLineTo'
|
| 'yLineTo'
|
||||||
|
| 'angledLineThatIntersects'
|
||||||
|
|
||||||
export const toolTips: TooTip[] = [
|
export const toolTips: TooTip[] = [
|
||||||
'lineTo',
|
'lineTo',
|
||||||
@ -35,6 +36,7 @@ export const toolTips: TooTip[] = [
|
|||||||
'yLine',
|
'yLine',
|
||||||
'xLineTo',
|
'xLineTo',
|
||||||
'yLineTo',
|
'yLineTo',
|
||||||
|
'angledLineThatIntersects',
|
||||||
]
|
]
|
||||||
|
|
||||||
export type GuiModes =
|
export type GuiModes =
|
||||||
|
@ -9360,10 +9360,10 @@ sisteransi@^1.0.4, sisteransi@^1.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
|
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
|
||||||
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
|
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
|
||||||
|
|
||||||
sketch-helpers@^0.0.1:
|
sketch-helpers@^0.0.2:
|
||||||
version "0.0.1"
|
version "0.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/sketch-helpers/-/sketch-helpers-0.0.1.tgz#637ead1f6e39276408d2c2e2a48dfefe13dc0cb0"
|
resolved "https://registry.yarnpkg.com/sketch-helpers/-/sketch-helpers-0.0.2.tgz#7b088303a82d3b7008abfe4e20a476c6da20cc0e"
|
||||||
integrity sha512-ePn4nTA5sVNR6+8JalyCPQ+K7tpuYtCrccw2QGL6H2N3JRq6bO8x9RmZpyjTe/+T0uSrd2+F41d+ibsrjHHSFg==
|
integrity sha512-3VnjIlqg3ORxcIR9vATazvvQt6/4vzPymcorQ30vigjjZEXPHf4xT6Zh7pbZmR58RJBKEU1AtAANhqYar4CRFQ==
|
||||||
|
|
||||||
slash@^3.0.0:
|
slash@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
|
Reference in New Issue
Block a user