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-modal-promise": "^1.0.2",
|
||||
"react-scripts": "5.0.1",
|
||||
"sketch-helpers": "^0.0.1",
|
||||
"sketch-helpers": "^0.0.2",
|
||||
"swr": "^2.0.4",
|
||||
"three": "^0.146.0",
|
||||
"typescript": "^4.4.2",
|
||||
|
@ -4,6 +4,7 @@ import { getNodePathFromSourceRange } from './lang/queryAst'
|
||||
import { HorzVert } from './components/Toolbar/HorzVert'
|
||||
import { EqualLength } from './components/Toolbar/EqualLength'
|
||||
import { EqualAngle } from './components/Toolbar/EqualAngle'
|
||||
import { Intersect } from './components/Toolbar/Intersect'
|
||||
import { SetHorzDistance } from './components/Toolbar/SetHorzDistance'
|
||||
import { SetAngleLength } from './components/Toolbar/SetAngleLength'
|
||||
|
||||
@ -123,38 +124,42 @@ export const Toolbar = () => {
|
||||
Exit sketch
|
||||
</button>
|
||||
)}
|
||||
{toolTips.map((sketchFnName) => {
|
||||
if (
|
||||
guiMode.mode !== 'sketch' ||
|
||||
!('isTooltip' in guiMode || guiMode.sketchMode === 'sketchEdit')
|
||||
{toolTips
|
||||
.filter(
|
||||
(sketchFnName) => !['angledLineThatIntersects'].includes(sketchFnName)
|
||||
)
|
||||
return null
|
||||
return (
|
||||
<button
|
||||
key={sketchFnName}
|
||||
className={`border m-0.5 px-0.5 rounded text-xs ${
|
||||
guiMode.sketchMode === sketchFnName && 'bg-gray-400'
|
||||
}`}
|
||||
onClick={() =>
|
||||
setGuiMode({
|
||||
...guiMode,
|
||||
...(guiMode.sketchMode === sketchFnName
|
||||
? {
|
||||
sketchMode: 'sketchEdit',
|
||||
// todo: ...guiMod is adding isTooltip: true, will probably just fix with xstate migtaion
|
||||
}
|
||||
: {
|
||||
sketchMode: sketchFnName,
|
||||
isTooltip: true,
|
||||
}),
|
||||
})
|
||||
}
|
||||
>
|
||||
{sketchFnName}
|
||||
{guiMode.sketchMode === sketchFnName && '✅'}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
.map((sketchFnName) => {
|
||||
if (
|
||||
guiMode.mode !== 'sketch' ||
|
||||
!('isTooltip' in guiMode || guiMode.sketchMode === 'sketchEdit')
|
||||
)
|
||||
return null
|
||||
return (
|
||||
<button
|
||||
key={sketchFnName}
|
||||
className={`border m-0.5 px-0.5 rounded text-xs ${
|
||||
guiMode.sketchMode === sketchFnName && 'bg-gray-400'
|
||||
}`}
|
||||
onClick={() =>
|
||||
setGuiMode({
|
||||
...guiMode,
|
||||
...(guiMode.sketchMode === sketchFnName
|
||||
? {
|
||||
sketchMode: 'sketchEdit',
|
||||
// todo: ...guiMod is adding isTooltip: true, will probably just fix with xstate migtaion
|
||||
}
|
||||
: {
|
||||
sketchMode: sketchFnName,
|
||||
isTooltip: true,
|
||||
}),
|
||||
})
|
||||
}
|
||||
>
|
||||
{sketchFnName}
|
||||
{guiMode.sketchMode === sketchFnName && '✅'}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
<br></br>
|
||||
<HorzVert horOrVert="horizontal" />
|
||||
<HorzVert horOrVert="vertical" />
|
||||
@ -164,6 +169,7 @@ export const Toolbar = () => {
|
||||
<SetHorzDistance horOrVert="setVertDistance" />
|
||||
<SetAngleLength angleOrLength="setAngle" />
|
||||
<SetAngleLength angleOrLength="setLength" />
|
||||
<Intersect />
|
||||
</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') {
|
||||
obj[property.key.name] = getBinaryExpressionResult(
|
||||
property.value,
|
||||
_programMemory
|
||||
_programMemory,
|
||||
_pipeInfo
|
||||
)
|
||||
} else if (property.value.type === 'PipeExpression') {
|
||||
obj[property.key.name] = getPipeExpressionResult(
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
VariableDeclarator,
|
||||
Value,
|
||||
Literal,
|
||||
VariableDeclaration,
|
||||
} from '../abstractSyntaxTree'
|
||||
import {
|
||||
getNodeFromPath,
|
||||
@ -41,6 +42,10 @@ import {
|
||||
} from '../modifyAst'
|
||||
import { roundOff, getLength, getAngle } from '../../lib/utils'
|
||||
import { getSketchSegmentIndexFromSourceRange } from './sketchConstraints'
|
||||
import {
|
||||
intersectionWithParallelLine,
|
||||
perpendicularDistance,
|
||||
} from 'sketch-helpers'
|
||||
|
||||
export type Coords2d = [number, number]
|
||||
|
||||
@ -312,7 +317,7 @@ export const line: SketchLineHelper = {
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
const _node = { ...node }
|
||||
const { node: callExpression, path } = getNodeFromPath<CallExpression>(
|
||||
const { node: callExpression } = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
pathToNode
|
||||
)
|
||||
@ -338,9 +343,9 @@ export const line: SketchLineHelper = {
|
||||
) {
|
||||
toProp.value = toArrExp
|
||||
}
|
||||
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
||||
} else {
|
||||
mutateArrExp(callExpression.arguments?.[0], toArrExp) ||
|
||||
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
||||
mutateArrExp(callExpression.arguments?.[0], toArrExp)
|
||||
}
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -397,7 +402,7 @@ export const xLineTo: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, to }) => {
|
||||
const _node = { ...node }
|
||||
const { node: callExpression } = getNodeFromPath<CallExpression>(
|
||||
_node,
|
||||
@ -1084,6 +1089,126 @@ export const angledLineToY: SketchLineHelper = {
|
||||
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 } = {
|
||||
line,
|
||||
lineTo,
|
||||
@ -1096,6 +1221,7 @@ export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
||||
angledLineOfYLength,
|
||||
angledLineToX,
|
||||
angledLineToY,
|
||||
angledLineThatIntersects,
|
||||
} as const
|
||||
|
||||
export function changeSketchArguments(
|
||||
@ -1116,6 +1242,7 @@ export function changeSketchArguments(
|
||||
|
||||
if (callExpression?.callee?.name in sketchLineHelperMap) {
|
||||
const { updateArgs } = sketchLineHelperMap[callExpression.callee.name]
|
||||
if (!updateArgs) throw new Error('not a sketch line helper')
|
||||
return updateArgs({
|
||||
node: _node,
|
||||
previousProgramMemory: programMemory,
|
||||
@ -1235,7 +1362,8 @@ export function replaceSketchLine({
|
||||
createCallback: TransformCallback
|
||||
referencedSegment?: Path
|
||||
}): { 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 thePath = getNodePathFromSourceRange(_node, sourceRange)
|
||||
|
||||
@ -1540,6 +1668,26 @@ function getFirstArgValuesForXYLineFns(callExpression: CallExpression): {
|
||||
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): {
|
||||
val: Value | [Value, Value]
|
||||
tag?: Value
|
||||
@ -1565,5 +1713,8 @@ export function getFirstArg(callExp: CallExpression): {
|
||||
if (['startSketchAt'].includes(name)) {
|
||||
return getFirstArgValuesForXYLineFns(callExp)
|
||||
}
|
||||
if (['angledLineThatIntersects'].includes(name)) {
|
||||
return getAngledLineThatIntersects(callExp)
|
||||
}
|
||||
throw new Error('unexpected call expression')
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
createCallExpression,
|
||||
createIdentifier,
|
||||
createLiteral,
|
||||
createObjectExpression,
|
||||
createPipeSubstitution,
|
||||
createUnaryExpression,
|
||||
giveSketchFnCallTag,
|
||||
@ -43,6 +44,7 @@ export type ConstraintType =
|
||||
| 'setVertDistance'
|
||||
| 'setAngle'
|
||||
| 'setLength'
|
||||
| 'intersect'
|
||||
|
||||
function createCallWrapper(
|
||||
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 = {
|
||||
tooltip: TooTip
|
||||
createNode: (a: {
|
||||
@ -71,7 +105,7 @@ export type TransformInfo = {
|
||||
}
|
||||
|
||||
type TransformMap = {
|
||||
[key in TooTip]: {
|
||||
[key in TooTip]?: {
|
||||
[key in LineInputsType | 'free']?: {
|
||||
[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 = {
|
||||
line: {
|
||||
xRelative: {
|
||||
@ -404,6 +476,10 @@ const transformMap: TransformMap = {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode('ang'),
|
||||
},
|
||||
intersect: {
|
||||
tooltip: 'angledLineThatIntersects',
|
||||
createNode: setAngledIntersectLineForLines,
|
||||
},
|
||||
},
|
||||
},
|
||||
lineTo: {
|
||||
@ -524,6 +600,10 @@ const transformMap: TransformMap = {
|
||||
tooltip: 'angledLineToX',
|
||||
createNode: setHorzVertDistanceForAngleLineCreateNode('x'),
|
||||
},
|
||||
intersect: {
|
||||
tooltip: 'angledLineThatIntersects',
|
||||
createNode: setAngledIntersectForAngledLines,
|
||||
},
|
||||
},
|
||||
free: {
|
||||
equalLength: {
|
||||
@ -798,6 +878,10 @@ const transformMap: TransformMap = {
|
||||
tooltip: 'xLine',
|
||||
createNode: xyLineSetLength('xLine'),
|
||||
},
|
||||
intersect: {
|
||||
tooltip: 'angledLineThatIntersects',
|
||||
createNode: setAngledIntersectLineForLines,
|
||||
},
|
||||
},
|
||||
},
|
||||
yLine: {
|
||||
@ -817,6 +901,10 @@ const transformMap: TransformMap = {
|
||||
tooltip: 'yLineTo',
|
||||
createNode: setHorVertDistanceForXYLines('y'),
|
||||
},
|
||||
intersect: {
|
||||
tooltip: 'angledLineThatIntersects',
|
||||
createNode: setAngledIntersectLineForLines,
|
||||
},
|
||||
},
|
||||
},
|
||||
xLineTo: {
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
angledLineToY,
|
||||
closee,
|
||||
startSketchAt,
|
||||
getCoordsFromPaths,
|
||||
angledLineThatIntersects,
|
||||
} from './sketch'
|
||||
import {
|
||||
segLen,
|
||||
@ -29,7 +29,6 @@ import { Quaternion, Vector3 } from 'three'
|
||||
import { SketchGroup, ExtrudeGroup, Position, Rotation } from '../executor'
|
||||
|
||||
import { InternalFn, InternalFnNames, InternalFirstArg } from './stdTypes'
|
||||
import { intersectionWithParallelLine } from 'sketch-helpers'
|
||||
|
||||
const transform: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
|
||||
{ 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 legLen: InternalFn = (_, hypotenuse: number, leg: number): number =>
|
||||
@ -158,7 +125,7 @@ export const internalFns: { [key in InternalFnNames]: InternalFn } = {
|
||||
angledLineToX: angledLineToX.fn,
|
||||
angledLineOfYLength: angledLineOfYLength.fn,
|
||||
angledLineToY: angledLineToY.fn,
|
||||
angledLineThatIntersects,
|
||||
angledLineThatIntersects: angledLineThatIntersects.fn,
|
||||
startSketchAt,
|
||||
closee,
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ export type TooTip =
|
||||
| 'yLine'
|
||||
| 'xLineTo'
|
||||
| 'yLineTo'
|
||||
| 'angledLineThatIntersects'
|
||||
|
||||
export const toolTips: TooTip[] = [
|
||||
'lineTo',
|
||||
@ -35,6 +36,7 @@ export const toolTips: TooTip[] = [
|
||||
'yLine',
|
||||
'xLineTo',
|
||||
'yLineTo',
|
||||
'angledLineThatIntersects',
|
||||
]
|
||||
|
||||
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"
|
||||
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
|
||||
|
||||
sketch-helpers@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sketch-helpers/-/sketch-helpers-0.0.1.tgz#637ead1f6e39276408d2c2e2a48dfefe13dc0cb0"
|
||||
integrity sha512-ePn4nTA5sVNR6+8JalyCPQ+K7tpuYtCrccw2QGL6H2N3JRq6bO8x9RmZpyjTe/+T0uSrd2+F41d+ibsrjHHSFg==
|
||||
sketch-helpers@^0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/sketch-helpers/-/sketch-helpers-0.0.2.tgz#7b088303a82d3b7008abfe4e20a476c6da20cc0e"
|
||||
integrity sha512-3VnjIlqg3ORxcIR9vATazvvQt6/4vzPymcorQ30vigjjZEXPHf4xT6Zh7pbZmR58RJBKEU1AtAANhqYar4CRFQ==
|
||||
|
||||
slash@^3.0.0:
|
||||
version "3.0.0"
|
||||
|
Reference in New Issue
Block a user