Users should be able to select points (not just lines) (#97)
* update selection datastructure to accomodate more selection metadata * Users should be able to select points (not just lines)
This commit is contained in:
29
src/App.tsx
29
src/App.tsx
@ -45,6 +45,7 @@ function App() {
|
|||||||
errorState,
|
errorState,
|
||||||
setProgramMemory,
|
setProgramMemory,
|
||||||
resetLogs,
|
resetLogs,
|
||||||
|
selectionRangeTypeMap,
|
||||||
} = useStore((s) => ({
|
} = useStore((s) => ({
|
||||||
editorView: s.editorView,
|
editorView: s.editorView,
|
||||||
setEditorView: s.setEditorView,
|
setEditorView: s.setEditorView,
|
||||||
@ -61,6 +62,7 @@ function App() {
|
|||||||
errorState: s.errorState,
|
errorState: s.errorState,
|
||||||
setProgramMemory: s.setProgramMemory,
|
setProgramMemory: s.setProgramMemory,
|
||||||
resetLogs: s.resetLogs,
|
resetLogs: s.resetLogs,
|
||||||
|
selectionRangeTypeMap: s.selectionRangeTypeMap,
|
||||||
}))
|
}))
|
||||||
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
||||||
const onChange = (value: string, viewUpdate: ViewUpdate) => {
|
const onChange = (value: string, viewUpdate: ViewUpdate) => {
|
||||||
@ -76,13 +78,30 @@ function App() {
|
|||||||
const ranges = viewUpdate.state.selection.ranges
|
const ranges = viewUpdate.state.selection.ranges
|
||||||
|
|
||||||
const isChange =
|
const isChange =
|
||||||
ranges.length !== selectionRange.length ||
|
ranges.length !== selectionRange.codeBasedSelections.length ||
|
||||||
ranges.some(({ from, to }, i) => {
|
ranges.some(({ from, to }, i) => {
|
||||||
return from !== selectionRange[i][0] || to !== selectionRange[i][1]
|
return (
|
||||||
|
from !== selectionRange.codeBasedSelections[i].range[0] ||
|
||||||
|
to !== selectionRange.codeBasedSelections[i].range[1]
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!isChange) return
|
if (!isChange) return
|
||||||
setSelectionRanges(ranges.map(({ from, to }) => [from, to]))
|
setSelectionRanges({
|
||||||
|
otherSelections: [],
|
||||||
|
codeBasedSelections: ranges.map(({ from, to }, i) => {
|
||||||
|
if (selectionRangeTypeMap[to]) {
|
||||||
|
return {
|
||||||
|
type: selectionRangeTypeMap[to],
|
||||||
|
range: [from, to],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'default',
|
||||||
|
range: [from, to],
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const [geoArray, setGeoArray] = useState<(ExtrudeGroup | SketchGroup)[]>([])
|
const [geoArray, setGeoArray] = useState<(ExtrudeGroup | SketchGroup)[]>([])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -163,7 +182,7 @@ function App() {
|
|||||||
<div className="h-screen">
|
<div className="h-screen">
|
||||||
<ModalContainer />
|
<ModalContainer />
|
||||||
<Allotment snap={true}>
|
<Allotment snap={true}>
|
||||||
<Allotment vertical defaultSizes={[4, 1, 1]} minSize={20}>
|
<Allotment vertical defaultSizes={[400, 1, 1]} minSize={20}>
|
||||||
<div className="h-full flex flex-col items-start">
|
<div className="h-full flex flex-col items-start">
|
||||||
<PanelHeader title="Editor" />
|
<PanelHeader title="Editor" />
|
||||||
{/* <button
|
{/* <button
|
||||||
@ -190,7 +209,7 @@ function App() {
|
|||||||
<MemoryPanel />
|
<MemoryPanel />
|
||||||
<Logs />
|
<Logs />
|
||||||
</Allotment>
|
</Allotment>
|
||||||
<Allotment vertical defaultSizes={[4, 1]} minSize={20}>
|
<Allotment vertical defaultSizes={[400, 1]} minSize={20}>
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
<PanelHeader title="Drafting Board" />
|
<PanelHeader title="Drafting Board" />
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
|
@ -48,7 +48,7 @@ export const Toolbar = () => {
|
|||||||
if (!ast) return
|
if (!ast) return
|
||||||
const pathToNode = getNodePathFromSourceRange(
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
ast,
|
ast,
|
||||||
selectionRanges[0]
|
selectionRanges.codeBasedSelections[0].range
|
||||||
)
|
)
|
||||||
const { modifiedAst } = sketchOnExtrudedFace(
|
const { modifiedAst } = sketchOnExtrudedFace(
|
||||||
ast,
|
ast,
|
||||||
@ -85,7 +85,7 @@ export const Toolbar = () => {
|
|||||||
if (!ast) return
|
if (!ast) return
|
||||||
const pathToNode = getNodePathFromSourceRange(
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
ast,
|
ast,
|
||||||
selectionRanges[0]
|
selectionRanges.codeBasedSelections[0].range
|
||||||
)
|
)
|
||||||
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
|
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
|
||||||
ast,
|
ast,
|
||||||
@ -102,7 +102,7 @@ export const Toolbar = () => {
|
|||||||
if (!ast) return
|
if (!ast) return
|
||||||
const pathToNode = getNodePathFromSourceRange(
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
ast,
|
ast,
|
||||||
selectionRanges[0]
|
selectionRanges.codeBasedSelections[0].range
|
||||||
)
|
)
|
||||||
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
|
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
|
||||||
ast,
|
ast,
|
||||||
|
@ -98,7 +98,7 @@ export function useCalc({
|
|||||||
const { ast, programMemory, selectionRange } = useStore((s) => ({
|
const { ast, programMemory, selectionRange } = useStore((s) => ({
|
||||||
ast: s.ast,
|
ast: s.ast,
|
||||||
programMemory: s.programMemory,
|
programMemory: s.programMemory,
|
||||||
selectionRange: s.selectionRanges[0],
|
selectionRange: s.selectionRanges.codeBasedSelections[0].range,
|
||||||
}))
|
}))
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
const [availableVarInfo, setAvailableVarInfo] = useState<
|
const [availableVarInfo, setAvailableVarInfo] = useState<
|
||||||
|
@ -28,7 +28,7 @@ import { useSetCursor } from '../hooks/useSetCursor'
|
|||||||
import { getConstraintLevelFromSourceRange } from '../lang/std/sketchcombos'
|
import { getConstraintLevelFromSourceRange } from '../lang/std/sketchcombos'
|
||||||
import { createCallExpression, createPipeSubstitution } from '../lang/modifyAst'
|
import { createCallExpression, createPipeSubstitution } from '../lang/modifyAst'
|
||||||
|
|
||||||
function MovingSphere({
|
function LineEnd({
|
||||||
geo,
|
geo,
|
||||||
sourceRange,
|
sourceRange,
|
||||||
editorCursor,
|
editorCursor,
|
||||||
@ -51,6 +51,8 @@ function MovingSphere({
|
|||||||
const [isMouseDown, setIsMouseDown] = useState(false)
|
const [isMouseDown, setIsMouseDown] = useState(false)
|
||||||
const baseColor = useConstraintColors(sourceRange)
|
const baseColor = useConstraintColors(sourceRange)
|
||||||
|
|
||||||
|
const setCursor = useSetCursor(sourceRange, 'line-end')
|
||||||
|
|
||||||
const { setHighlightRange, guiMode, ast, updateAst, programMemory } =
|
const { setHighlightRange, guiMode, ast, updateAst, programMemory } =
|
||||||
useStore((s) => ({
|
useStore((s) => ({
|
||||||
setHighlightRange: s.setHighlightRange,
|
setHighlightRange: s.setHighlightRange,
|
||||||
@ -84,7 +86,6 @@ function MovingSphere({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
if (isMouseDown && ast) {
|
if (isMouseDown && ast) {
|
||||||
const thePath = getNodePathFromSourceRange(ast, sourceRange)
|
|
||||||
const current2d = point2DRef.current.clone()
|
const current2d = point2DRef.current.clone()
|
||||||
const inverseQuaternion = new Quaternion()
|
const inverseQuaternion = new Quaternion()
|
||||||
if (
|
if (
|
||||||
@ -107,8 +108,8 @@ function MovingSphere({
|
|||||||
guiMode,
|
guiMode,
|
||||||
from
|
from
|
||||||
)
|
)
|
||||||
|
if (!(current2d.x === 0 && current2d.y === 0 && current2d.z === 0))
|
||||||
updateAst(modifiedAst)
|
updateAst(modifiedAst)
|
||||||
ref.current.position.set(...position)
|
ref.current.position.set(...position)
|
||||||
}
|
}
|
||||||
setIsMouseDown(false)
|
setIsMouseDown(false)
|
||||||
@ -142,7 +143,10 @@ function MovingSphere({
|
|||||||
setHover(false)
|
setHover(false)
|
||||||
setHighlightRange([0, 0])
|
setHighlightRange([0, 0])
|
||||||
}}
|
}}
|
||||||
onPointerDown={() => inEditMode && setIsMouseDown(true)}
|
onPointerDown={() => {
|
||||||
|
inEditMode && setIsMouseDown(true)
|
||||||
|
setCursor()
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<primitive object={geo} scale={hovered ? 2 : 1} />
|
<primitive object={geo} scale={hovered ? 2 : 1} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
@ -313,8 +317,8 @@ function WallRender({
|
|||||||
|
|
||||||
const [editorCursor, setEditorCursor] = useState(false)
|
const [editorCursor, setEditorCursor] = useState(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldHighlight = selectionRanges.some((range) =>
|
const shouldHighlight = selectionRanges.codeBasedSelections.some(
|
||||||
isOverlap(geoInfo.__geoMeta.sourceRange, range)
|
({ range }) => isOverlap(geoInfo.__geoMeta.sourceRange, range)
|
||||||
)
|
)
|
||||||
setEditorCursor(shouldHighlight)
|
setEditorCursor(shouldHighlight)
|
||||||
}, [selectionRanges, geoInfo])
|
}, [selectionRanges, geoInfo])
|
||||||
@ -369,11 +373,17 @@ function PathRender({
|
|||||||
guiMode: s.guiMode,
|
guiMode: s.guiMode,
|
||||||
}))
|
}))
|
||||||
const [editorCursor, setEditorCursor] = useState(false)
|
const [editorCursor, setEditorCursor] = useState(false)
|
||||||
|
const [editorLineCursor, setEditorLineCursor] = useState(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldHighlight = selectionRanges.some((range) =>
|
const shouldHighlight = selectionRanges.codeBasedSelections.some(
|
||||||
isOverlap(geoInfo.__geoMeta.sourceRange, range)
|
({ range }) => isOverlap(geoInfo.__geoMeta.sourceRange, range)
|
||||||
|
)
|
||||||
|
const shouldHighlightLine = selectionRanges.codeBasedSelections.some(
|
||||||
|
({ range, type }) =>
|
||||||
|
isOverlap(geoInfo.__geoMeta.sourceRange, range) && type === 'default'
|
||||||
)
|
)
|
||||||
setEditorCursor(shouldHighlight)
|
setEditorCursor(shouldHighlight)
|
||||||
|
setEditorLineCursor(shouldHighlightLine)
|
||||||
}, [selectionRanges, geoInfo])
|
}, [selectionRanges, geoInfo])
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -384,14 +394,14 @@ function PathRender({
|
|||||||
key={i}
|
key={i}
|
||||||
geo={meta.geo}
|
geo={meta.geo}
|
||||||
sourceRange={geoInfo.__geoMeta.sourceRange}
|
sourceRange={geoInfo.__geoMeta.sourceRange}
|
||||||
forceHighlight={forceHighlight || editorCursor}
|
forceHighlight={editorLineCursor}
|
||||||
rotation={rotation}
|
rotation={rotation}
|
||||||
position={position}
|
position={position}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
if (meta.type === 'lineEnd')
|
if (meta.type === 'lineEnd')
|
||||||
return (
|
return (
|
||||||
<MovingSphere
|
<LineEnd
|
||||||
key={i}
|
key={i}
|
||||||
geo={meta.geo}
|
geo={meta.geo}
|
||||||
from={geoInfo.from}
|
from={geoInfo.from}
|
||||||
@ -407,7 +417,7 @@ function PathRender({
|
|||||||
key={i}
|
key={i}
|
||||||
geo={meta.geo}
|
geo={meta.geo}
|
||||||
sourceRange={geoInfo.__geoMeta.sourceRange}
|
sourceRange={geoInfo.__geoMeta.sourceRange}
|
||||||
forceHighlight={forceHighlight || editorCursor}
|
forceHighlight={forceHighlight || editorLineCursor}
|
||||||
rotation={rotation}
|
rotation={rotation}
|
||||||
position={position}
|
position={position}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -533,7 +543,12 @@ function useSetAppModeFromCursorLocation(artifacts: Artifact[]) {
|
|||||||
)[] = []
|
)[] = []
|
||||||
artifacts?.forEach((artifact) => {
|
artifacts?.forEach((artifact) => {
|
||||||
artifact.value.forEach((geo) => {
|
artifact.value.forEach((geo) => {
|
||||||
if (isOverlap(geo.__geoMeta.sourceRange, selectionRanges[0])) {
|
if (
|
||||||
|
isOverlap(
|
||||||
|
geo.__geoMeta.sourceRange,
|
||||||
|
selectionRanges.codeBasedSelections[0].range
|
||||||
|
)
|
||||||
|
) {
|
||||||
artifactsWithinCursorRange.push({
|
artifactsWithinCursorRange.push({
|
||||||
parentType: artifact.type,
|
parentType: artifact.type,
|
||||||
isParent: false,
|
isParent: false,
|
||||||
@ -545,7 +560,12 @@ function useSetAppModeFromCursorLocation(artifacts: Artifact[]) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
artifact.__meta.forEach((meta) => {
|
artifact.__meta.forEach((meta) => {
|
||||||
if (isOverlap(meta.sourceRange, selectionRanges[0])) {
|
if (
|
||||||
|
isOverlap(
|
||||||
|
meta.sourceRange,
|
||||||
|
selectionRanges.codeBasedSelections[0].range
|
||||||
|
)
|
||||||
|
) {
|
||||||
artifactsWithinCursorRange.push({
|
artifactsWithinCursorRange.push({
|
||||||
parentType: artifact.type,
|
parentType: artifact.type,
|
||||||
isParent: true,
|
isParent: true,
|
||||||
|
@ -21,9 +21,12 @@ export const ConvertToVariable = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
|
|
||||||
const { isSafe, value } = isNodeSafeToReplace(ast, selectionRanges[0])
|
const { isSafe, value } = isNodeSafeToReplace(
|
||||||
|
ast,
|
||||||
|
selectionRanges.codeBasedSelections[0].range
|
||||||
|
)
|
||||||
const canReplace = isSafe && value.type !== 'Identifier'
|
const canReplace = isSafe && value.type !== 'Identifier'
|
||||||
const isOnlyOneSelection = selectionRanges.length === 1
|
const isOnlyOneSelection = selectionRanges.codeBasedSelections.length === 1
|
||||||
|
|
||||||
const _enableHorz = canReplace && isOnlyOneSelection
|
const _enableHorz = canReplace && isOnlyOneSelection
|
||||||
setEnableAngLen(_enableHorz)
|
setEnableAngLen(_enableHorz)
|
||||||
@ -41,7 +44,7 @@ export const ConvertToVariable = () => {
|
|||||||
const { modifiedAst: _modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst: _modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
programMemory,
|
programMemory,
|
||||||
selectionRanges[0],
|
selectionRanges.codeBasedSelections[0].range,
|
||||||
variableName
|
variableName
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ export const EqualAngle = () => {
|
|||||||
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
const paths = selectionRanges.map((selectionRange) =>
|
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
||||||
getNodePathFromSourceRange(ast, selectionRange)
|
getNodePathFromSourceRange(ast, range)
|
||||||
)
|
)
|
||||||
const nodes = paths.map(
|
const nodes = paths.map(
|
||||||
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
||||||
@ -52,7 +52,10 @@ export const EqualAngle = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const theTransforms = getTransformInfos(
|
const theTransforms = getTransformInfos(
|
||||||
selectionRanges.slice(1),
|
{
|
||||||
|
...selectionRanges,
|
||||||
|
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
|
||||||
|
},
|
||||||
ast,
|
ast,
|
||||||
'equalAngle'
|
'equalAngle'
|
||||||
)
|
)
|
||||||
|
@ -26,8 +26,8 @@ export const EqualLength = () => {
|
|||||||
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
const paths = selectionRanges.map((selectionRange) =>
|
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
||||||
getNodePathFromSourceRange(ast, selectionRange)
|
getNodePathFromSourceRange(ast, range)
|
||||||
)
|
)
|
||||||
const nodes = paths.map(
|
const nodes = paths.map(
|
||||||
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
||||||
@ -52,7 +52,10 @@ export const EqualLength = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const theTransforms = getTransformInfos(
|
const theTransforms = getTransformInfos(
|
||||||
selectionRanges.slice(1),
|
{
|
||||||
|
...selectionRanges,
|
||||||
|
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
|
||||||
|
},
|
||||||
ast,
|
ast,
|
||||||
'equalLength'
|
'equalLength'
|
||||||
)
|
)
|
||||||
|
@ -29,8 +29,8 @@ export const HorzVert = ({
|
|||||||
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
const paths = selectionRanges.map((selectionRange) =>
|
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
||||||
getNodePathFromSourceRange(ast, selectionRange)
|
getNodePathFromSourceRange(ast, range)
|
||||||
)
|
)
|
||||||
const nodes = paths.map(
|
const nodes = paths.map(
|
||||||
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
||||||
|
@ -39,8 +39,8 @@ export const Intersect = () => {
|
|||||||
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
const paths = selectionRanges.map((selectionRange) =>
|
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
||||||
getNodePathFromSourceRange(ast, selectionRange)
|
getNodePathFromSourceRange(ast, range)
|
||||||
)
|
)
|
||||||
const nodes = paths.map(
|
const nodes = paths.map(
|
||||||
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
||||||
@ -68,7 +68,10 @@ export const Intersect = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const theTransforms = getTransformInfos(
|
const theTransforms = getTransformInfos(
|
||||||
selectionRanges.slice(1),
|
{
|
||||||
|
...selectionRanges,
|
||||||
|
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
|
||||||
|
},
|
||||||
ast,
|
ast,
|
||||||
'intersect'
|
'intersect'
|
||||||
)
|
)
|
||||||
@ -150,7 +153,7 @@ export const Intersect = () => {
|
|||||||
}`}
|
}`}
|
||||||
disabled={!enable}
|
disabled={!enable}
|
||||||
>
|
>
|
||||||
Intersect
|
perpendicularDistance
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@ export const RemoveConstrainingValues = () => {
|
|||||||
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
const paths = selectionRanges.map((selectionRange) =>
|
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
||||||
getNodePathFromSourceRange(ast, selectionRange)
|
getNodePathFromSourceRange(ast, range)
|
||||||
)
|
)
|
||||||
const nodes = paths.map(
|
const nodes = paths.map(
|
||||||
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
||||||
@ -37,15 +37,19 @@ export const RemoveConstrainingValues = () => {
|
|||||||
toolTips.includes(node.callee.name as any)
|
toolTips.includes(node.callee.name as any)
|
||||||
)
|
)
|
||||||
|
|
||||||
const theTransforms = getRemoveConstraintsTransforms(
|
try {
|
||||||
selectionRanges,
|
const theTransforms = getRemoveConstraintsTransforms(
|
||||||
ast,
|
selectionRanges,
|
||||||
'removeConstrainingValues'
|
ast,
|
||||||
)
|
'removeConstrainingValues'
|
||||||
setTransformInfos(theTransforms)
|
)
|
||||||
|
setTransformInfos(theTransforms)
|
||||||
|
|
||||||
const _enableHorz = isAllTooltips && theTransforms.every(Boolean)
|
const _enableHorz = isAllTooltips && theTransforms.every(Boolean)
|
||||||
setEnableHorz(_enableHorz)
|
setEnableHorz(_enableHorz)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
}, [guiMode, selectionRanges])
|
}, [guiMode, selectionRanges])
|
||||||
if (guiMode.mode !== 'sketch') return null
|
if (guiMode.mode !== 'sketch') return null
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ export const SetAngleLength = ({
|
|||||||
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
const paths = selectionRanges.map((selectionRange) =>
|
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
||||||
getNodePathFromSourceRange(ast, selectionRange)
|
getNodePathFromSourceRange(ast, range)
|
||||||
)
|
)
|
||||||
const nodes = paths.map(
|
const nodes = paths.map(
|
||||||
(pathToNode) =>
|
(pathToNode) =>
|
||||||
@ -60,7 +60,7 @@ export const SetAngleLength = ({
|
|||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!(transformInfos && ast)) return
|
if (!(transformInfos && ast)) return
|
||||||
const { modifiedAst, valueUsedInTransform } = transformAstSketchLines({
|
const { valueUsedInTransform } = transformAstSketchLines({
|
||||||
ast: JSON.parse(JSON.stringify(ast)),
|
ast: JSON.parse(JSON.stringify(ast)),
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
|
@ -43,8 +43,8 @@ export const SetHorzDistance = ({
|
|||||||
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
const paths = selectionRanges.map((selectionRange) =>
|
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
||||||
getNodePathFromSourceRange(ast, selectionRange)
|
getNodePathFromSourceRange(ast, range)
|
||||||
)
|
)
|
||||||
const nodes = paths.map(
|
const nodes = paths.map(
|
||||||
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
||||||
@ -72,7 +72,10 @@ export const SetHorzDistance = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const theTransforms = getTransformInfos(
|
const theTransforms = getTransformInfos(
|
||||||
selectionRanges.slice(1),
|
{
|
||||||
|
...selectionRanges,
|
||||||
|
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
|
||||||
|
},
|
||||||
ast,
|
ast,
|
||||||
horOrVert
|
horOrVert
|
||||||
)
|
)
|
||||||
@ -127,7 +130,6 @@ export const SetHorzDistance = ({
|
|||||||
sign,
|
sign,
|
||||||
variableName
|
variableName
|
||||||
)
|
)
|
||||||
console.log(finalValue)
|
|
||||||
// transform again but forcing certain values
|
// transform again but forcing certain values
|
||||||
const { modifiedAst: _modifiedAst } =
|
const { modifiedAst: _modifiedAst } =
|
||||||
transformSecondarySketchLinesTagFirst({
|
transformSecondarySketchLinesTagFirst({
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
import { useStore, Range } from '../useStore'
|
import { useStore, Selection, Selections } from '../useStore'
|
||||||
|
|
||||||
export function useSetCursor(sourceRange: Range) {
|
export function useSetCursor(
|
||||||
|
sourceRange: Selection['range'],
|
||||||
|
type: Selection['type'] = 'default'
|
||||||
|
) {
|
||||||
const { setCursor, selectionRanges, isShiftDown } = useStore((s) => ({
|
const { setCursor, selectionRanges, isShiftDown } = useStore((s) => ({
|
||||||
setCursor: s.setCursor,
|
setCursor: s.setCursor,
|
||||||
selectionRanges: s.selectionRanges,
|
selectionRanges: s.selectionRanges,
|
||||||
isShiftDown: s.isShiftDown,
|
isShiftDown: s.isShiftDown,
|
||||||
}))
|
}))
|
||||||
return () => {
|
return () => {
|
||||||
const ranges = isShiftDown
|
const selections: Selections = {
|
||||||
? [...selectionRanges, sourceRange]
|
...selectionRanges,
|
||||||
: [sourceRange]
|
codeBasedSelections: isShiftDown
|
||||||
setCursor(ranges)
|
? [...selectionRanges.codeBasedSelections, { range: sourceRange, type }]
|
||||||
|
: [{ range: sourceRange, type }],
|
||||||
|
}
|
||||||
|
setCursor(selections)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,14 +67,17 @@ export function lineGeo({
|
|||||||
sign,
|
sign,
|
||||||
} = trigCalcs({ from, to })
|
} = trigCalcs({ from, to })
|
||||||
|
|
||||||
|
const lineEndLength = 0.25
|
||||||
// create BoxGeometry with size [Hypotenuse3d, 0.1, 0.1] centered at center, with rotation of [0, ry, rz]
|
// create BoxGeometry with size [Hypotenuse3d, 0.1, 0.1] centered at center, with rotation of [0, ry, rz]
|
||||||
const lineBody = new BoxGeometry(Hypotenuse3d, 0.1, 0.1)
|
const lineBody = new BoxGeometry(Hypotenuse3d - lineEndLength, 0.1, 0.1)
|
||||||
|
const __sign = to[0] === from[0] ? -1 : 1
|
||||||
|
lineBody.translate((__sign * lineEndLength) / 2, 0, 0)
|
||||||
lineBody.rotateY(ry)
|
lineBody.rotateY(ry)
|
||||||
lineBody.rotateZ(rz)
|
lineBody.rotateZ(rz)
|
||||||
lineBody.translate(centre[0], centre[1], centre[2])
|
lineBody.translate(centre[0], centre[1], centre[2])
|
||||||
|
|
||||||
// create line end points with CylinderGeometry at `to`
|
// create line end points with CylinderGeometry at `to`
|
||||||
const lineEnd1 = new CylinderGeometry(0.05, 0.22, 0.25, 4)
|
const lineEnd1 = new CylinderGeometry(0.05, 0.22, lineEndLength + 0.05, 4)
|
||||||
lineEnd1.translate(0, -0.1, 0)
|
lineEnd1.translate(0, -0.1, 0)
|
||||||
lineEnd1.rotateY(Math.PI / 4)
|
lineEnd1.rotateY(Math.PI / 4)
|
||||||
lineEnd1.rotateZ(rz)
|
lineEnd1.rotateZ(rz)
|
||||||
|
@ -15,7 +15,7 @@ import { internalFns } from './std/std'
|
|||||||
import { BufferGeometry } from 'three'
|
import { BufferGeometry } from 'three'
|
||||||
|
|
||||||
export type SourceRange = [number, number]
|
export type SourceRange = [number, number]
|
||||||
export type PathToNode = [string | number, string][]
|
export type PathToNode = [string | number, string][] // [pathKey, nodeType][]
|
||||||
export type Metadata = {
|
export type Metadata = {
|
||||||
sourceRange: SourceRange
|
sourceRange: SourceRange
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Range, TooTip } from '../useStore'
|
import { Selection, TooTip } from '../useStore'
|
||||||
import {
|
import {
|
||||||
Program,
|
Program,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
@ -534,7 +534,7 @@ export function createBinaryExpressionWithUnary([left, right]: [
|
|||||||
|
|
||||||
export function giveSketchFnCallTag(
|
export function giveSketchFnCallTag(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
range: Range,
|
range: Selection['range'],
|
||||||
tag?: string
|
tag?: string
|
||||||
): { modifiedAst: Program; tag: string; isTagExisting: boolean } {
|
): { modifiedAst: Program; tag: string; isTagExisting: boolean } {
|
||||||
const { node: primaryCallExp } = getNodeFromPath<CallExpression>(
|
const { node: primaryCallExp } = getNodeFromPath<CallExpression>(
|
||||||
@ -563,7 +563,7 @@ export function giveSketchFnCallTag(
|
|||||||
export function moveValueIntoNewVariable(
|
export function moveValueIntoNewVariable(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
sourceRange: Range,
|
sourceRange: Selection['range'],
|
||||||
variableName: string
|
variableName: string
|
||||||
): {
|
): {
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { PathToNode, ProgramMemory } from './executor'
|
import { PathToNode, ProgramMemory } from './executor'
|
||||||
import { Range } from '../useStore'
|
import { Selection } from '../useStore'
|
||||||
import {
|
import {
|
||||||
BinaryExpression,
|
BinaryExpression,
|
||||||
Program,
|
Program,
|
||||||
@ -96,7 +96,7 @@ export function getNodeFromPathCurry(
|
|||||||
|
|
||||||
function moreNodePathFromSourceRange(
|
function moreNodePathFromSourceRange(
|
||||||
node: Value | ExpressionStatement | VariableDeclaration | ReturnStatement,
|
node: Value | ExpressionStatement | VariableDeclaration | ReturnStatement,
|
||||||
sourceRange: Range,
|
sourceRange: Selection['range'],
|
||||||
previousPath: PathToNode = [['body', '']]
|
previousPath: PathToNode = [['body', '']]
|
||||||
): PathToNode {
|
): PathToNode {
|
||||||
const [start, end] = sourceRange
|
const [start, end] = sourceRange
|
||||||
@ -239,7 +239,7 @@ function moreNodePathFromSourceRange(
|
|||||||
|
|
||||||
export function getNodePathFromSourceRange(
|
export function getNodePathFromSourceRange(
|
||||||
node: Program,
|
node: Program,
|
||||||
sourceRange: Range,
|
sourceRange: Selection['range'],
|
||||||
previousPath: PathToNode = [['body', '']]
|
previousPath: PathToNode = [['body', '']]
|
||||||
): PathToNode {
|
): PathToNode {
|
||||||
const [start, end] = sourceRange
|
const [start, end] = sourceRange
|
||||||
@ -269,7 +269,7 @@ export interface PrevVariable<T> {
|
|||||||
export function findAllPreviousVariables(
|
export function findAllPreviousVariables(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
sourceRange: Range,
|
sourceRange: Selection['range'],
|
||||||
type: 'number' | 'string' = 'number'
|
type: 'number' | 'string' = 'number'
|
||||||
): {
|
): {
|
||||||
variables: PrevVariable<typeof type extends 'number' ? number : string>[]
|
variables: PrevVariable<typeof type extends 'number' ? number : string>[]
|
||||||
|
@ -64,7 +64,7 @@ export function getCoordsFromPaths(skGroup: SketchGroup, index = 0): Coords2d {
|
|||||||
|
|
||||||
export function createFirstArg(
|
export function createFirstArg(
|
||||||
sketchFn: TooTip,
|
sketchFn: TooTip,
|
||||||
val: Value | [Value, Value],
|
val: Value | [Value, Value] | [Value, Value, Value],
|
||||||
tag?: Value
|
tag?: Value
|
||||||
): Value {
|
): Value {
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
@ -84,6 +84,13 @@ export function createFirstArg(
|
|||||||
return createObjectExpression({ angle: val[0], length: val[1], tag })
|
return createObjectExpression({ angle: val[0], length: val[1], tag })
|
||||||
if (['angledLineToX', 'angledLineToY'].includes(sketchFn))
|
if (['angledLineToX', 'angledLineToY'].includes(sketchFn))
|
||||||
return createObjectExpression({ angle: val[0], to: val[1], tag })
|
return createObjectExpression({ angle: val[0], to: val[1], tag })
|
||||||
|
if (['angledLineThatIntersects'].includes(sketchFn) && val[2])
|
||||||
|
return createObjectExpression({
|
||||||
|
angle: val[0],
|
||||||
|
offset: val[1],
|
||||||
|
intersectTag: val[2],
|
||||||
|
tag,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
if (['xLine', 'yLine'].includes(sketchFn))
|
if (['xLine', 'yLine'].includes(sketchFn))
|
||||||
return createObjectExpression({ length: val, tag })
|
return createObjectExpression({ length: val, tag })
|
||||||
@ -1678,7 +1685,7 @@ function getFirstArgValuesForXYLineFns(callExpression: CallExpression): {
|
|||||||
const getAngledLineThatIntersects = (
|
const getAngledLineThatIntersects = (
|
||||||
callExp: CallExpression
|
callExp: CallExpression
|
||||||
): {
|
): {
|
||||||
val: [Value, Value]
|
val: [Value, Value, Value]
|
||||||
tag?: Value
|
tag?: Value
|
||||||
} => {
|
} => {
|
||||||
const firstArg = callExp.arguments[0]
|
const firstArg = callExp.arguments[0]
|
||||||
@ -1688,15 +1695,18 @@ const getAngledLineThatIntersects = (
|
|||||||
const offset = firstArg.properties.find(
|
const offset = firstArg.properties.find(
|
||||||
(p) => p.key.name === 'offset'
|
(p) => p.key.name === 'offset'
|
||||||
)?.value
|
)?.value
|
||||||
if (angle && offset) {
|
const intersectTag = firstArg.properties.find(
|
||||||
return { val: [angle, offset], tag }
|
(p) => p.key.name === 'intersectTag'
|
||||||
|
)?.value
|
||||||
|
if (angle && offset && intersectTag) {
|
||||||
|
return { val: [angle, offset, intersectTag], tag }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error('expected ArrayExpression or ObjectExpression')
|
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] | [Value, Value, Value]
|
||||||
tag?: Value
|
tag?: Value
|
||||||
} {
|
} {
|
||||||
const name = callExp?.callee?.name
|
const name = callExp?.callee?.name
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
import { recast } from '../recast'
|
import { recast } from '../recast'
|
||||||
import { initPromise } from '../rust'
|
import { initPromise } from '../rust'
|
||||||
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
|
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
|
||||||
|
import { Selection } from '../../useStore'
|
||||||
|
|
||||||
beforeAll(() => initPromise)
|
beforeAll(() => initPromise)
|
||||||
|
|
||||||
@ -26,23 +27,30 @@ function testingSwapSketchFnCall({
|
|||||||
originalRange: [number, number]
|
originalRange: [number, number]
|
||||||
} {
|
} {
|
||||||
const startIndex = inputCode.indexOf(callToSwap)
|
const startIndex = inputCode.indexOf(callToSwap)
|
||||||
const range: [number, number] = [startIndex, startIndex + callToSwap.length]
|
const range: Selection = {
|
||||||
|
type: 'default',
|
||||||
|
range: [startIndex, startIndex + callToSwap.length],
|
||||||
|
}
|
||||||
const tokens = lexer(inputCode)
|
const tokens = lexer(inputCode)
|
||||||
const ast = abstractSyntaxTree(tokens)
|
const ast = abstractSyntaxTree(tokens)
|
||||||
const programMemory = executor(ast)
|
const programMemory = executor(ast)
|
||||||
const transformInfos = getTransformInfos([range], ast, constraintType)
|
const selections = {
|
||||||
|
codeBasedSelections: [range],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const transformInfos = getTransformInfos(selections, ast, constraintType)
|
||||||
|
|
||||||
if (!transformInfos) throw new Error('nope')
|
if (!transformInfos) throw new Error('nope')
|
||||||
const { modifiedAst } = transformAstSketchLines({
|
const { modifiedAst } = transformAstSketchLines({
|
||||||
ast,
|
ast,
|
||||||
programMemory,
|
programMemory,
|
||||||
selectionRanges: [range],
|
selectionRanges: selections,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
newCode: recast(modifiedAst),
|
newCode: recast(modifiedAst),
|
||||||
originalRange: range,
|
originalRange: range.range,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { getAngle } from '../../lib/utils'
|
import { getAngle } from '../../lib/utils'
|
||||||
import { Range, TooTip, toolTips } from '../../useStore'
|
import { Selection, TooTip, toolTips } from '../../useStore'
|
||||||
import {
|
import {
|
||||||
Program,
|
Program,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
} from '../abstractSyntaxTree'
|
} from '../abstractSyntaxTree'
|
||||||
import { SketchGroup } from '../executor'
|
import { SketchGroup, SourceRange } from '../executor'
|
||||||
import { InternalFn } from './stdTypes'
|
import { InternalFn } from './stdTypes'
|
||||||
|
|
||||||
export function getSketchSegmentFromSourceRange(
|
export function getSketchSegmentFromSourceRange(
|
||||||
sketchGroup: SketchGroup,
|
sketchGroup: SketchGroup,
|
||||||
[rangeStart, rangeEnd]: Range
|
[rangeStart, rangeEnd]: SourceRange
|
||||||
): SketchGroup['value'][number] {
|
): SketchGroup['value'][number] {
|
||||||
const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
|
const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
|
||||||
if (
|
if (
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
getConstraintLevelFromSourceRange,
|
getConstraintLevelFromSourceRange,
|
||||||
} from './sketchcombos'
|
} from './sketchcombos'
|
||||||
import { initPromise } from '../rust'
|
import { initPromise } from '../rust'
|
||||||
import { TooTip } from '../../useStore'
|
import { Selections, TooTip } from '../../useStore'
|
||||||
import { executor } from '../../lang/executor'
|
import { executor } from '../../lang/executor'
|
||||||
import { recast } from '../../lang/recast'
|
import { recast } from '../../lang/recast'
|
||||||
|
|
||||||
@ -80,6 +80,15 @@ function getConstraintTypeFromSourceHelper2(
|
|||||||
return getConstraintType(arg, fnName)
|
return getConstraintType(arg, fnName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeSelections(
|
||||||
|
codeBaseSelections: Selections['codeBasedSelections']
|
||||||
|
): Selections {
|
||||||
|
return {
|
||||||
|
codeBasedSelections: codeBaseSelections,
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('testing transformAstForSketchLines for equal length constraint', () => {
|
describe('testing transformAstForSketchLines for equal length constraint', () => {
|
||||||
const inputScript = `const myVar = 3
|
const inputScript = `const myVar = 3
|
||||||
const myVar2 = 5
|
const myVar2 = 5
|
||||||
@ -189,25 +198,28 @@ const part001 = startSketchAt([0, 0])
|
|||||||
show(part001)`
|
show(part001)`
|
||||||
it('It should transform the ast', () => {
|
it('It should transform the ast', () => {
|
||||||
const ast = abstractSyntaxTree(lexer(inputScript))
|
const ast = abstractSyntaxTree(lexer(inputScript))
|
||||||
const selectionRanges = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) => ln.includes('//'))
|
.filter((ln) => ln.includes('//'))
|
||||||
.map((ln) => {
|
.map((ln) => {
|
||||||
const comment = ln.split('//')[1]
|
const comment = ln.split('//')[1]
|
||||||
const start = inputScript.indexOf('//' + comment) - 7
|
const start = inputScript.indexOf('//' + comment) - 7
|
||||||
return [start, start]
|
return {
|
||||||
}) as [number, number][]
|
type: 'default',
|
||||||
|
range: [start, start],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const programMemory = executor(ast)
|
const programMemory = executor(ast)
|
||||||
const transformInfos = getTransformInfos(
|
const transformInfos = getTransformInfos(
|
||||||
selectionRanges.slice(1),
|
makeSelections(selectionRanges.slice(1)),
|
||||||
ast,
|
ast,
|
||||||
'equalLength'
|
'equalLength'
|
||||||
)
|
)
|
||||||
|
|
||||||
const newAst = transformSecondarySketchLinesTagFirst({
|
const newAst = transformSecondarySketchLinesTagFirst({
|
||||||
ast,
|
ast,
|
||||||
selectionRanges,
|
selectionRanges: makeSelections(selectionRanges),
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory,
|
programMemory,
|
||||||
})?.modifiedAst
|
})?.modifiedAst
|
||||||
@ -271,21 +283,28 @@ const part001 = startSketchAt([0, 0])
|
|||||||
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
||||||
show(part001)`
|
show(part001)`
|
||||||
const ast = abstractSyntaxTree(lexer(inputScript))
|
const ast = abstractSyntaxTree(lexer(inputScript))
|
||||||
const selectionRanges = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) => ln.includes('// select for horizontal constraint'))
|
.filter((ln) => ln.includes('// select for horizontal constraint'))
|
||||||
.map((ln) => {
|
.map((ln) => {
|
||||||
const comment = ln.split('//')[1]
|
const comment = ln.split('//')[1]
|
||||||
const start = inputScript.indexOf('//' + comment) - 7
|
const start = inputScript.indexOf('//' + comment) - 7
|
||||||
return [start, start]
|
return {
|
||||||
}) as [number, number][]
|
type: 'default',
|
||||||
|
range: [start, start],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const programMemory = executor(ast)
|
const programMemory = executor(ast)
|
||||||
const transformInfos = getTransformInfos(selectionRanges, ast, 'horizontal')
|
const transformInfos = getTransformInfos(
|
||||||
|
makeSelections(selectionRanges),
|
||||||
|
ast,
|
||||||
|
'horizontal'
|
||||||
|
)
|
||||||
|
|
||||||
const newAst = transformAstSketchLines({
|
const newAst = transformAstSketchLines({
|
||||||
ast,
|
ast,
|
||||||
selectionRanges,
|
selectionRanges: makeSelections(selectionRanges),
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory,
|
programMemory,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
@ -321,21 +340,28 @@ const part001 = startSketchAt([0, 0])
|
|||||||
|> yLineTo(myVar, %) // select for vertical constraint 10
|
|> yLineTo(myVar, %) // select for vertical constraint 10
|
||||||
show(part001)`
|
show(part001)`
|
||||||
const ast = abstractSyntaxTree(lexer(inputScript))
|
const ast = abstractSyntaxTree(lexer(inputScript))
|
||||||
const selectionRanges = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) => ln.includes('// select for vertical constraint'))
|
.filter((ln) => ln.includes('// select for vertical constraint'))
|
||||||
.map((ln) => {
|
.map((ln) => {
|
||||||
const comment = ln.split('//')[1]
|
const comment = ln.split('//')[1]
|
||||||
const start = inputScript.indexOf('//' + comment) - 7
|
const start = inputScript.indexOf('//' + comment) - 7
|
||||||
return [start, start]
|
return {
|
||||||
}) as [number, number][]
|
type: 'default',
|
||||||
|
range: [start, start],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const programMemory = executor(ast)
|
const programMemory = executor(ast)
|
||||||
const transformInfos = getTransformInfos(selectionRanges, ast, 'vertical')
|
const transformInfos = getTransformInfos(
|
||||||
|
makeSelections(selectionRanges),
|
||||||
|
ast,
|
||||||
|
'vertical'
|
||||||
|
)
|
||||||
|
|
||||||
const newAst = transformAstSketchLines({
|
const newAst = transformAstSketchLines({
|
||||||
ast,
|
ast,
|
||||||
selectionRanges,
|
selectionRanges: makeSelections(selectionRanges),
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory,
|
programMemory,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
@ -404,7 +430,7 @@ function helperThing(
|
|||||||
constraint: ConstraintType
|
constraint: ConstraintType
|
||||||
): string {
|
): string {
|
||||||
const ast = abstractSyntaxTree(lexer(inputScript))
|
const ast = abstractSyntaxTree(lexer(inputScript))
|
||||||
const selectionRanges = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) =>
|
.filter((ln) =>
|
||||||
linesOfInterest.some((lineOfInterest) => ln.includes(lineOfInterest))
|
linesOfInterest.some((lineOfInterest) => ln.includes(lineOfInterest))
|
||||||
@ -412,19 +438,22 @@ function helperThing(
|
|||||||
.map((ln) => {
|
.map((ln) => {
|
||||||
const comment = ln.split('//')[1]
|
const comment = ln.split('//')[1]
|
||||||
const start = inputScript.indexOf('//' + comment) - 7
|
const start = inputScript.indexOf('//' + comment) - 7
|
||||||
return [start, start]
|
return {
|
||||||
}) as [number, number][]
|
type: 'default',
|
||||||
|
range: [start, start],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const programMemory = executor(ast)
|
const programMemory = executor(ast)
|
||||||
const transformInfos = getTransformInfos(
|
const transformInfos = getTransformInfos(
|
||||||
selectionRanges.slice(1),
|
makeSelections(selectionRanges.slice(1)),
|
||||||
ast,
|
ast,
|
||||||
constraint
|
constraint
|
||||||
)
|
)
|
||||||
|
|
||||||
const newAst = transformSecondarySketchLinesTagFirst({
|
const newAst = transformSecondarySketchLinesTagFirst({
|
||||||
ast,
|
ast,
|
||||||
selectionRanges,
|
selectionRanges: makeSelections(selectionRanges),
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory,
|
programMemory,
|
||||||
})?.modifiedAst
|
})?.modifiedAst
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { TransformCallback } from './stdTypes'
|
import { TransformCallback } from './stdTypes'
|
||||||
import { Ranges, toolTips, TooTip, Range } from '../../useStore'
|
import { Selections, toolTips, TooTip, Selection } from '../../useStore'
|
||||||
import {
|
import {
|
||||||
BinaryPart,
|
BinaryPart,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
@ -127,7 +127,6 @@ const xyLineSetLength =
|
|||||||
: referenceSeg
|
: referenceSeg
|
||||||
? segRef
|
? segRef
|
||||||
: args[0]
|
: args[0]
|
||||||
// console.log({ lineVal, segRef, forceValueUsedInTransform, args })
|
|
||||||
return createCallWrapper(xOrY, lineVal, tag, getArgLiteralVal(args[0]))
|
return createCallWrapper(xOrY, lineVal, tag, getArgLiteralVal(args[0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +278,6 @@ const setHorzVertDistanceForAngleLineCreateNode =
|
|||||||
(forceValueUsedInTransform as BinaryPart) ||
|
(forceValueUsedInTransform as BinaryPart) ||
|
||||||
createLiteral(valueUsedInTransform),
|
createLiteral(valueUsedInTransform),
|
||||||
])
|
])
|
||||||
console.log('here or no?', binExp)
|
|
||||||
return createCallWrapper(
|
return createCallWrapper(
|
||||||
xOrY === 'x' ? 'angledLineToX' : 'angledLineToY',
|
xOrY === 'x' ? 'angledLineToX' : 'angledLineToY',
|
||||||
[varValA, binExp],
|
[varValA, binExp],
|
||||||
@ -1099,7 +1097,7 @@ export function getTransformInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getConstraintType(
|
export function getConstraintType(
|
||||||
val: Value | [Value, Value],
|
val: Value | [Value, Value] | [Value, Value, Value],
|
||||||
fnName: TooTip
|
fnName: TooTip
|
||||||
): LineInputsType | null {
|
): LineInputsType | null {
|
||||||
// this function assumes that for two val sketch functions that one arg is locked down not both
|
// this function assumes that for two val sketch functions that one arg is locked down not both
|
||||||
@ -1133,12 +1131,12 @@ export function getConstraintType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getTransformInfos(
|
export function getTransformInfos(
|
||||||
selectionRanges: Ranges,
|
selectionRanges: Selections,
|
||||||
ast: Program,
|
ast: Program,
|
||||||
constraintType: ConstraintType
|
constraintType: ConstraintType
|
||||||
): TransformInfo[] {
|
): TransformInfo[] {
|
||||||
const paths = selectionRanges.map((selectionRange) =>
|
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
||||||
getNodePathFromSourceRange(ast, selectionRange)
|
getNodePathFromSourceRange(ast, range)
|
||||||
)
|
)
|
||||||
const nodes = paths.map(
|
const nodes = paths.map(
|
||||||
(pathToNode) =>
|
(pathToNode) =>
|
||||||
@ -1160,13 +1158,13 @@ export function getTransformInfos(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getRemoveConstraintsTransforms(
|
export function getRemoveConstraintsTransforms(
|
||||||
selectionRanges: Ranges,
|
selectionRanges: Selections,
|
||||||
ast: Program,
|
ast: Program,
|
||||||
constraintType: ConstraintType
|
constraintType: ConstraintType
|
||||||
): TransformInfo[] {
|
): TransformInfo[] {
|
||||||
// return ()
|
// return ()
|
||||||
const paths = selectionRanges.map((selectionRange) =>
|
const paths = selectionRanges.codeBasedSelections.map((selectionRange) =>
|
||||||
getNodePathFromSourceRange(ast, selectionRange)
|
getNodePathFromSourceRange(ast, selectionRange.range)
|
||||||
)
|
)
|
||||||
const nodes = paths.map(
|
const nodes = paths.map(
|
||||||
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
||||||
@ -1190,7 +1188,7 @@ export function transformSecondarySketchLinesTagFirst({
|
|||||||
forceValueUsedInTransform,
|
forceValueUsedInTransform,
|
||||||
}: {
|
}: {
|
||||||
ast: Program
|
ast: Program
|
||||||
selectionRanges: Ranges
|
selectionRanges: Selections
|
||||||
transformInfos: TransformInfo[]
|
transformInfos: TransformInfo[]
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
forceSegName?: string
|
forceSegName?: string
|
||||||
@ -1204,7 +1202,7 @@ export function transformSecondarySketchLinesTagFirst({
|
|||||||
}
|
}
|
||||||
} {
|
} {
|
||||||
// let node = JSON.parse(JSON.stringify(ast))
|
// let node = JSON.parse(JSON.stringify(ast))
|
||||||
const primarySelection = selectionRanges[0]
|
const primarySelection = selectionRanges.codeBasedSelections[0].range
|
||||||
|
|
||||||
const { modifiedAst, tag, isTagExisting } = giveSketchFnCallTag(
|
const { modifiedAst, tag, isTagExisting } = giveSketchFnCallTag(
|
||||||
ast,
|
ast,
|
||||||
@ -1215,7 +1213,10 @@ export function transformSecondarySketchLinesTagFirst({
|
|||||||
return {
|
return {
|
||||||
...transformAstSketchLines({
|
...transformAstSketchLines({
|
||||||
ast: modifiedAst,
|
ast: modifiedAst,
|
||||||
selectionRanges: selectionRanges.slice(1),
|
selectionRanges: {
|
||||||
|
...selectionRanges,
|
||||||
|
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
|
||||||
|
},
|
||||||
referencedSegmentRange: primarySelection,
|
referencedSegmentRange: primarySelection,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory,
|
programMemory,
|
||||||
@ -1239,18 +1240,18 @@ export function transformAstSketchLines({
|
|||||||
referencedSegmentRange,
|
referencedSegmentRange,
|
||||||
}: {
|
}: {
|
||||||
ast: Program
|
ast: Program
|
||||||
selectionRanges: Ranges
|
selectionRanges: Selections
|
||||||
transformInfos: TransformInfo[]
|
transformInfos: TransformInfo[]
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
referenceSegName: string
|
referenceSegName: string
|
||||||
forceValueUsedInTransform?: Value
|
forceValueUsedInTransform?: Value
|
||||||
referencedSegmentRange?: Range
|
referencedSegmentRange?: Selection['range']
|
||||||
}): { modifiedAst: Program; valueUsedInTransform?: number } {
|
}): { modifiedAst: Program; valueUsedInTransform?: number } {
|
||||||
// deep clone since we are mutating in a loop, of which any could fail
|
// deep clone since we are mutating in a loop, of which any could fail
|
||||||
let node = JSON.parse(JSON.stringify(ast))
|
let node = JSON.parse(JSON.stringify(ast))
|
||||||
let _valueUsedInTransform // TODO should this be an array?
|
let _valueUsedInTransform // TODO should this be an array?
|
||||||
|
|
||||||
selectionRanges.forEach((range, index) => {
|
selectionRanges.codeBasedSelections.forEach(({ range }, index) => {
|
||||||
const callBack = transformInfos?.[index].createNode
|
const callBack = transformInfos?.[index].createNode
|
||||||
const transformTo = transformInfos?.[index].tooltip
|
const transformTo = transformInfos?.[index].tooltip
|
||||||
if (!callBack || !transformTo) throw new Error('no callback helper')
|
if (!callBack || !transformTo) throw new Error('no callback helper')
|
||||||
@ -1343,7 +1344,7 @@ function getArgLiteralVal(arg: Value): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getConstraintLevelFromSourceRange(
|
export function getConstraintLevelFromSourceRange(
|
||||||
cursorRange: Range,
|
cursorRange: Selection['range'],
|
||||||
ast: Program
|
ast: Program
|
||||||
): 'free' | 'partial' | 'full' {
|
): 'free' | 'partial' | 'full' {
|
||||||
const { node: sketchFnExp } = getNodeFromPath<CallExpression>(
|
const { node: sketchFnExp } = getNodeFromPath<CallExpression>(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { isOverlap, roundOff } from './utils'
|
import { isOverlap, roundOff } from './utils'
|
||||||
import { Range } from '../useStore'
|
import { SourceRange } from '../lang/executor'
|
||||||
|
|
||||||
describe('testing isOverlapping', () => {
|
describe('testing isOverlapping', () => {
|
||||||
testBothOrders([0, 3], [3, 10])
|
testBothOrders([0, 3], [3, 10])
|
||||||
@ -11,7 +11,7 @@ describe('testing isOverlapping', () => {
|
|||||||
testBothOrders([0, 5], [-2, -1], false)
|
testBothOrders([0, 5], [-2, -1], false)
|
||||||
})
|
})
|
||||||
|
|
||||||
function testBothOrders(a: Range, b: Range, result = true) {
|
function testBothOrders(a: SourceRange, b: SourceRange, result = true) {
|
||||||
it(`test is overlapping ${a} ${b}`, () => {
|
it(`test is overlapping ${a} ${b}`, () => {
|
||||||
expect(isOverlap(a, b)).toBe(result)
|
expect(isOverlap(a, b)).toBe(result)
|
||||||
expect(isOverlap(b, a)).toBe(result)
|
expect(isOverlap(b, a)).toBe(result)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Range } from '../useStore'
|
import { SourceRange } from '../lang/executor'
|
||||||
|
|
||||||
export function isOverlap(a: Range, b: Range) {
|
export function isOverlap(a: SourceRange, b: SourceRange) {
|
||||||
const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a]
|
const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a]
|
||||||
const [lastOfFirst, firstOfSecond] = [startingRange[1], secondRange[0]]
|
const [lastOfFirst, firstOfSecond] = [startingRange[1], secondRange[0]]
|
||||||
return lastOfFirst >= firstOfSecond
|
return lastOfFirst >= firstOfSecond
|
||||||
|
@ -3,13 +3,25 @@ import { persist } from 'zustand/middleware'
|
|||||||
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
||||||
import { Program, abstractSyntaxTree } from './lang/abstractSyntaxTree'
|
import { Program, abstractSyntaxTree } from './lang/abstractSyntaxTree'
|
||||||
import { getNodeFromPath } from './lang/queryAst'
|
import { getNodeFromPath } from './lang/queryAst'
|
||||||
import { ProgramMemory, Position, PathToNode, Rotation } from './lang/executor'
|
import {
|
||||||
|
ProgramMemory,
|
||||||
|
Position,
|
||||||
|
PathToNode,
|
||||||
|
Rotation,
|
||||||
|
SourceRange,
|
||||||
|
} from './lang/executor'
|
||||||
import { recast } from './lang/recast'
|
import { recast } from './lang/recast'
|
||||||
import { asyncLexer } from './lang/tokeniser'
|
import { asyncLexer } from './lang/tokeniser'
|
||||||
import { EditorSelection } from '@codemirror/state'
|
import { EditorSelection } from '@codemirror/state'
|
||||||
|
|
||||||
export type Range = [number, number]
|
export type Selection = {
|
||||||
export type Ranges = Range[]
|
type: 'default' | 'line-end' | 'line-mid'
|
||||||
|
range: SourceRange
|
||||||
|
}
|
||||||
|
export type Selections = {
|
||||||
|
otherSelections: ('y-axis' | 'x-axis' | 'z-axis')[]
|
||||||
|
codeBasedSelections: Selection[]
|
||||||
|
}
|
||||||
export type TooTip =
|
export type TooTip =
|
||||||
| 'lineTo'
|
| 'lineTo'
|
||||||
| 'line'
|
| 'line'
|
||||||
@ -80,10 +92,11 @@ interface StoreState {
|
|||||||
editorView: EditorView | null
|
editorView: EditorView | null
|
||||||
setEditorView: (editorView: EditorView) => void
|
setEditorView: (editorView: EditorView) => void
|
||||||
highlightRange: [number, number]
|
highlightRange: [number, number]
|
||||||
setHighlightRange: (range: Range) => void
|
setHighlightRange: (range: Selection['range']) => void
|
||||||
setCursor: (selections: Ranges) => void
|
setCursor: (selections: Selections) => void
|
||||||
selectionRanges: Ranges
|
selectionRanges: Selections
|
||||||
setSelectionRanges: (range: Ranges) => void
|
selectionRangeTypeMap: { [key: number]: Selection['type'] }
|
||||||
|
setSelectionRanges: (range: Selections) => void
|
||||||
guiMode: GuiModes
|
guiMode: GuiModes
|
||||||
lastGuiMode: GuiModes
|
lastGuiMode: GuiModes
|
||||||
setGuiMode: (guiMode: GuiModes) => void
|
setGuiMode: (guiMode: GuiModes) => void
|
||||||
@ -118,27 +131,39 @@ export const useStore = create<StoreState>()(
|
|||||||
set({ editorView })
|
set({ editorView })
|
||||||
},
|
},
|
||||||
highlightRange: [0, 0],
|
highlightRange: [0, 0],
|
||||||
setHighlightRange: (highlightRange) => {
|
setHighlightRange: (selection) => {
|
||||||
set({ highlightRange })
|
set({ highlightRange: selection })
|
||||||
const editorView = get().editorView
|
const editorView = get().editorView
|
||||||
if (editorView) {
|
if (editorView) {
|
||||||
editorView.dispatch({ effects: addLineHighlight.of(highlightRange) })
|
editorView.dispatch({ effects: addLineHighlight.of(selection) })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setCursor: (ranges: Ranges) => {
|
setCursor: (selections) => {
|
||||||
const { editorView } = get()
|
const { editorView } = get()
|
||||||
if (!editorView) return
|
if (!editorView) return
|
||||||
editorView.dispatch({
|
const ranges: ReturnType<typeof EditorSelection.cursor>[] = []
|
||||||
selection: EditorSelection.create(
|
const selectionRangeTypeMap: { [key: number]: Selection['type'] } = {}
|
||||||
[...ranges.map(([start, end]) => EditorSelection.cursor(end))],
|
set({ selectionRangeTypeMap })
|
||||||
ranges.length - 1
|
selections.codeBasedSelections.forEach(({ range, type }) => {
|
||||||
),
|
ranges.push(EditorSelection.cursor(range[1]))
|
||||||
|
selectionRangeTypeMap[range[1]] = type
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
editorView.dispatch({
|
||||||
|
selection: EditorSelection.create(
|
||||||
|
ranges,
|
||||||
|
selections.codeBasedSelections.length - 1
|
||||||
|
),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
selectionRanges: [[0, 0]],
|
selectionRangeTypeMap: {},
|
||||||
setSelectionRanges: (selectionRanges) => {
|
selectionRanges: {
|
||||||
set({ selectionRanges })
|
otherSelections: [],
|
||||||
|
codeBasedSelections: [],
|
||||||
},
|
},
|
||||||
|
setSelectionRanges: (selectionRanges) =>
|
||||||
|
set({ selectionRanges, selectionRangeTypeMap: {} }),
|
||||||
guiMode: { mode: 'default' },
|
guiMode: { mode: 'default' },
|
||||||
lastGuiMode: { mode: 'default' },
|
lastGuiMode: { mode: 'default' },
|
||||||
setGuiMode: (guiMode) => {
|
setGuiMode: (guiMode) => {
|
||||||
@ -162,7 +187,6 @@ export const useStore = create<StoreState>()(
|
|||||||
},
|
},
|
||||||
updateAst: async (ast, focusPath) => {
|
updateAst: async (ast, focusPath) => {
|
||||||
const newCode = recast(ast)
|
const newCode = recast(ast)
|
||||||
console.log('running update Ast', ast)
|
|
||||||
const astWithUpdatedSource = abstractSyntaxTree(
|
const astWithUpdatedSource = abstractSyntaxTree(
|
||||||
await asyncLexer(newCode)
|
await asyncLexer(newCode)
|
||||||
)
|
)
|
||||||
@ -173,7 +197,15 @@ export const useStore = create<StoreState>()(
|
|||||||
const { start, end } = node
|
const { start, end } = node
|
||||||
if (!start || !end) return
|
if (!start || !end) return
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
get().setCursor([[start, end]])
|
get().setCursor({
|
||||||
|
codeBasedSelections: [
|
||||||
|
{
|
||||||
|
type: 'default',
|
||||||
|
range: [start, end],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user