functional sketch working (#26)
* functional sketch working With old sketch block still there * get all version of lines working with add line and update line * remove old ui state types * some clean up * rename some things * add todo for multi cursor * shorten useStore repitition * small type improvement * big overhaul to group sketch function and they ast modifying helpers together * unneeded tweak * ruthlessly rip out sketch logic * clean up path keyword * getting sketch on face working again with all the new sketch line types * add a bunch of tests and re-arrage file structure
This commit is contained in:
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
@ -14,3 +14,4 @@ jobs:
|
|||||||
node-version: '18.x'
|
node-version: '18.x'
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn test:nowatch
|
- run: yarn test:nowatch
|
||||||
|
- run: yarn test:cov
|
||||||
|
@ -29,8 +29,9 @@
|
|||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"test:nowatch": "react-scripts test --watchAll=false",
|
"test:nowatch": "react-scripts test --watchAll=false",
|
||||||
|
"test:cov": "react-scripts test --watchAll=false --coverage=true",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"fmt": "prettier --write ./src/**.{ts,tsx} && prettier --write ./src/**/*.{ts,tsx}"
|
"fmt": "prettier --write ./src/**.{ts,tsx} && prettier --write ./src/**/*.{ts,tsx} && prettier --write ./src/lang/**/*.{ts,tsx}"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"transformIgnorePatterns": [
|
"transformIgnorePatterns": [
|
||||||
|
@ -77,6 +77,8 @@ function App() {
|
|||||||
setEditorView(viewUpdate.view)
|
setEditorView(viewUpdate.view)
|
||||||
}
|
}
|
||||||
const range = viewUpdate.state.selection.ranges[0]
|
const range = viewUpdate.state.selection.ranges[0]
|
||||||
|
// console.log(viewUpdate.state.selection.ranges)
|
||||||
|
// TODO allow multiple cursors so that we can do constrain style features
|
||||||
const isNoChange =
|
const isNoChange =
|
||||||
range.from === selectionRange[0] && range.to === selectionRange[1]
|
range.from === selectionRange[0] && range.to === selectionRange[1]
|
||||||
if (isNoChange) return
|
if (isNoChange) return
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import { useStore } from './useStore'
|
import { useStore, toolTips } from './useStore'
|
||||||
import { extrudeSketch, sketchOnExtrudedFace } from './lang/modifyAst'
|
import { extrudeSketch, sketchOnExtrudedFace } from './lang/modifyAst'
|
||||||
import { getNodePathFromSourceRange } from './lang/abstractSyntaxTree'
|
import { getNodePathFromSourceRange } from './lang/abstractSyntaxTree'
|
||||||
|
|
||||||
export const Toolbar = () => {
|
export const Toolbar = () => {
|
||||||
const { setGuiMode, guiMode, selectionRange, ast, updateAst } = useStore(
|
const { setGuiMode, guiMode, selectionRange, ast, updateAst, programMemory } =
|
||||||
({ guiMode, setGuiMode, selectionRange, ast, updateAst }) => ({
|
useStore((s) => ({
|
||||||
guiMode,
|
guiMode: s.guiMode,
|
||||||
setGuiMode,
|
setGuiMode: s.setGuiMode,
|
||||||
selectionRange,
|
selectionRange: s.selectionRange,
|
||||||
ast,
|
ast: s.ast,
|
||||||
updateAst,
|
updateAst: s.updateAst,
|
||||||
})
|
programMemory: s.programMemory,
|
||||||
)
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{guiMode.mode === 'default' && (
|
{guiMode.mode === 'default' && (
|
||||||
@ -24,7 +25,7 @@ export const Toolbar = () => {
|
|||||||
}}
|
}}
|
||||||
className="border m-1 px-1 rounded"
|
className="border m-1 px-1 rounded"
|
||||||
>
|
>
|
||||||
Start sketch
|
Start Sketch
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{guiMode.mode === 'canEditExtrude' && (
|
{guiMode.mode === 'canEditExtrude' && (
|
||||||
@ -32,7 +33,11 @@ export const Toolbar = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, selectionRange)
|
const pathToNode = getNodePathFromSourceRange(ast, selectionRange)
|
||||||
const { modifiedAst } = sketchOnExtrudedFace(ast, pathToNode)
|
const { modifiedAst } = sketchOnExtrudedFace(
|
||||||
|
ast,
|
||||||
|
pathToNode,
|
||||||
|
programMemory
|
||||||
|
)
|
||||||
updateAst(modifiedAst)
|
updateAst(modifiedAst)
|
||||||
}}
|
}}
|
||||||
className="border m-1 px-1 rounded"
|
className="border m-1 px-1 rounded"
|
||||||
@ -41,7 +46,7 @@ export const Toolbar = () => {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{(guiMode.mode === 'canEditSketch' || false) && (
|
{(guiMode.mode === 'canEditSketch' || false) && (
|
||||||
/*guiMode.mode === 'canEditExtrude'*/ <button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setGuiMode({
|
setGuiMode({
|
||||||
mode: 'sketch',
|
mode: 'sketch',
|
||||||
@ -49,6 +54,7 @@ export const Toolbar = () => {
|
|||||||
pathToNode: guiMode.pathToNode,
|
pathToNode: guiMode.pathToNode,
|
||||||
rotation: guiMode.rotation,
|
rotation: guiMode.rotation,
|
||||||
position: guiMode.position,
|
position: guiMode.position,
|
||||||
|
isTooltip: true,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
className="border m-1 px-1 rounded"
|
className="border m-1 px-1 rounded"
|
||||||
@ -98,24 +104,29 @@ export const Toolbar = () => {
|
|||||||
Exit sketch
|
Exit sketch
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{guiMode.mode === 'sketch' &&
|
{toolTips.map((sketchFnName) => {
|
||||||
(guiMode.sketchMode === 'points' ||
|
if (guiMode.mode !== 'sketch' || !('isTooltip' in guiMode)) return null
|
||||||
guiMode.sketchMode === 'sketchEdit') && (
|
return (
|
||||||
<button
|
<button
|
||||||
|
key={sketchFnName}
|
||||||
className={`border m-1 px-1 rounded ${
|
className={`border m-1 px-1 rounded ${
|
||||||
guiMode.sketchMode === 'points' && 'bg-gray-400'
|
guiMode.sketchMode === sketchFnName && 'bg-gray-400'
|
||||||
}`}
|
}`}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setGuiMode({
|
setGuiMode({
|
||||||
...guiMode,
|
...guiMode,
|
||||||
sketchMode:
|
sketchMode:
|
||||||
guiMode.sketchMode === 'points' ? 'sketchEdit' : 'points',
|
guiMode.sketchMode === sketchFnName
|
||||||
|
? 'sketchEdit'
|
||||||
|
: sketchFnName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
LineTo{guiMode.sketchMode === 'points' && '✅'}
|
{sketchFnName}
|
||||||
|
{guiMode.sketchMode === sketchFnName && '✅'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ export const BasePlanes = () => {
|
|||||||
rotation: quaternion.toArray() as [number, number, number, number],
|
rotation: quaternion.toArray() as [number, number, number, number],
|
||||||
position: [0, 0, 0],
|
position: [0, 0, 0],
|
||||||
pathToNode,
|
pathToNode,
|
||||||
|
isTooltip: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
updateAst(modifiedAst)
|
updateAst(modifiedAst)
|
||||||
|
@ -11,17 +11,15 @@ const myFn = (a) => {
|
|||||||
}
|
}
|
||||||
const otherVar = myFn(5)
|
const otherVar = myFn(5)
|
||||||
|
|
||||||
sketch theExtrude {
|
const theExtrude = startSketchAt([0, 0])
|
||||||
lineTo(-2.4, myVar)
|
|> lineTo([-2.4, myVar], %)
|
||||||
lineTo(-0.76, otherVar)
|
|> lineTo([-0.76, otherVar], %)
|
||||||
}
|
|
||||||
|> extrude(4, %)
|
|> extrude(4, %)
|
||||||
|
|
||||||
sketch theSketch {
|
const theSketch = startSketchAt([0, 0])
|
||||||
lineTo(-3.35, 0.17)
|
|> lineTo([-3.35, 0.17], %)
|
||||||
lineTo(0.98, 5.16)
|
|> lineTo([0.98, 5.16], %)
|
||||||
lineTo(2.15, 4.32)
|
|> lineTo([2.15, 4.32], %)
|
||||||
}
|
|
||||||
|> rx(90, %)
|
|> rx(90, %)
|
||||||
show(theExtrude, theSketch)`
|
show(theExtrude, theSketch)`
|
||||||
const tokens = lexer(code)
|
const tokens = lexer(code)
|
||||||
|
@ -3,8 +3,9 @@ import {
|
|||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
|
ArrayExpression,
|
||||||
} from '../lang/abstractSyntaxTree'
|
} from '../lang/abstractSyntaxTree'
|
||||||
import { changeArguments } from '../lang/modifyAst'
|
import { changeSketchArguments } from '../lang/std/sketch'
|
||||||
import {
|
import {
|
||||||
ExtrudeGroup,
|
ExtrudeGroup,
|
||||||
ExtrudeSurface,
|
ExtrudeSurface,
|
||||||
@ -17,14 +18,10 @@ import {
|
|||||||
} from '../lang/executor'
|
} from '../lang/executor'
|
||||||
import { BufferGeometry } from 'three'
|
import { BufferGeometry } from 'three'
|
||||||
import { useStore } from '../useStore'
|
import { useStore } from '../useStore'
|
||||||
import { isOverlapping } from '../lib/utils'
|
import { isOverlap } from '../lib/utils'
|
||||||
import { Vector3, DoubleSide, Quaternion } from 'three'
|
import { Vector3, DoubleSide, Quaternion } from 'three'
|
||||||
import { useSetCursor } from '../hooks/useSetCursor'
|
import { useSetCursor } from '../hooks/useSetCursor'
|
||||||
|
import { roundOff } from '../lib/utils'
|
||||||
const roundOff = (num: number, places: number): number => {
|
|
||||||
const x = Math.pow(10, places)
|
|
||||||
return Math.round(num * x) / x
|
|
||||||
}
|
|
||||||
|
|
||||||
function MovingSphere({
|
function MovingSphere({
|
||||||
geo,
|
geo,
|
||||||
@ -32,12 +29,14 @@ function MovingSphere({
|
|||||||
editorCursor,
|
editorCursor,
|
||||||
rotation,
|
rotation,
|
||||||
position,
|
position,
|
||||||
|
from,
|
||||||
}: {
|
}: {
|
||||||
geo: BufferGeometry
|
geo: BufferGeometry
|
||||||
sourceRange: [number, number]
|
sourceRange: [number, number]
|
||||||
editorCursor: boolean
|
editorCursor: boolean
|
||||||
rotation: Rotation
|
rotation: Rotation
|
||||||
position: Position
|
position: Position
|
||||||
|
from: [number, number]
|
||||||
}) {
|
}) {
|
||||||
const ref = useRef<BufferGeometry | undefined>() as any
|
const ref = useRef<BufferGeometry | undefined>() as any
|
||||||
const detectionPlaneRef = useRef<BufferGeometry | undefined>() as any
|
const detectionPlaneRef = useRef<BufferGeometry | undefined>() as any
|
||||||
@ -46,11 +45,13 @@ function MovingSphere({
|
|||||||
const [hovered, setHover] = useState(false)
|
const [hovered, setHover] = useState(false)
|
||||||
const [isMouseDown, setIsMouseDown] = useState(false)
|
const [isMouseDown, setIsMouseDown] = useState(false)
|
||||||
|
|
||||||
const { setHighlightRange, guiMode, ast, updateAst } = useStore((s) => ({
|
const { setHighlightRange, guiMode, ast, updateAst, programMemory } =
|
||||||
|
useStore((s) => ({
|
||||||
setHighlightRange: s.setHighlightRange,
|
setHighlightRange: s.setHighlightRange,
|
||||||
guiMode: s.guiMode,
|
guiMode: s.guiMode,
|
||||||
ast: s.ast,
|
ast: s.ast,
|
||||||
updateAst: s.updateAst,
|
updateAst: s.updateAst,
|
||||||
|
programMemory: s.programMemory,
|
||||||
}))
|
}))
|
||||||
const { originalXY } = useMemo(() => {
|
const { originalXY } = useMemo(() => {
|
||||||
if (ast) {
|
if (ast) {
|
||||||
@ -59,7 +60,10 @@ function MovingSphere({
|
|||||||
ast,
|
ast,
|
||||||
thePath
|
thePath
|
||||||
)
|
)
|
||||||
const [xArg, yArg] = callExpression?.arguments || []
|
const [xArg, yArg] =
|
||||||
|
guiMode.mode === 'sketch'
|
||||||
|
? callExpression?.arguments || []
|
||||||
|
: (callExpression?.arguments?.[0] as ArrayExpression)?.elements || []
|
||||||
const x = xArg?.type === 'Literal' ? xArg.value : -1
|
const x = xArg?.type === 'Literal' ? xArg.value : -1
|
||||||
const y = yArg?.type === 'Literal' ? yArg.value : -1
|
const y = yArg?.type === 'Literal' ? yArg.value : -1
|
||||||
return {
|
return {
|
||||||
@ -87,7 +91,15 @@ function MovingSphere({
|
|||||||
yo.sub(new Vector3(...position).applyQuaternion(inverseQuaternion))
|
yo.sub(new Vector3(...position).applyQuaternion(inverseQuaternion))
|
||||||
let [x, y] = [roundOff(yo.x, 2), roundOff(yo.y, 2)]
|
let [x, y] = [roundOff(yo.x, 2), roundOff(yo.y, 2)]
|
||||||
let theNewPoints: [number, number] = [x, y]
|
let theNewPoints: [number, number] = [x, y]
|
||||||
const { modifiedAst } = changeArguments(ast, thePath, theNewPoints)
|
const { modifiedAst } = changeSketchArguments(
|
||||||
|
ast,
|
||||||
|
programMemory,
|
||||||
|
sourceRange,
|
||||||
|
theNewPoints,
|
||||||
|
guiMode,
|
||||||
|
from
|
||||||
|
)
|
||||||
|
|
||||||
updateAst(modifiedAst)
|
updateAst(modifiedAst)
|
||||||
ref.current.position.set(...position)
|
ref.current.position.set(...position)
|
||||||
}
|
}
|
||||||
@ -285,7 +297,7 @@ function WallRender({
|
|||||||
|
|
||||||
const [editorCursor, setEditorCursor] = useState(false)
|
const [editorCursor, setEditorCursor] = useState(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldHighlight = isOverlapping(
|
const shouldHighlight = isOverlap(
|
||||||
geoInfo.__geoMeta.sourceRange,
|
geoInfo.__geoMeta.sourceRange,
|
||||||
selectionRange
|
selectionRange
|
||||||
)
|
)
|
||||||
@ -340,7 +352,7 @@ function PathRender({
|
|||||||
}))
|
}))
|
||||||
const [editorCursor, setEditorCursor] = useState(false)
|
const [editorCursor, setEditorCursor] = useState(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldHighlight = isOverlapping(
|
const shouldHighlight = isOverlap(
|
||||||
geoInfo.__geoMeta.sourceRange,
|
geoInfo.__geoMeta.sourceRange,
|
||||||
selectionRange
|
selectionRange
|
||||||
)
|
)
|
||||||
@ -366,6 +378,7 @@ function PathRender({
|
|||||||
<MovingSphere
|
<MovingSphere
|
||||||
key={i}
|
key={i}
|
||||||
geo={meta.geo}
|
geo={meta.geo}
|
||||||
|
from={geoInfo.from}
|
||||||
sourceRange={geoInfo.__geoMeta.sourceRange}
|
sourceRange={geoInfo.__geoMeta.sourceRange}
|
||||||
editorCursor={forceHighlight || editorCursor}
|
editorCursor={forceHighlight || editorCursor}
|
||||||
rotation={rotation}
|
rotation={rotation}
|
||||||
@ -424,9 +437,9 @@ function LineRender({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Boop = ExtrudeGroup | SketchGroup
|
type Artifact = ExtrudeGroup | SketchGroup
|
||||||
|
|
||||||
function useSetAppModeFromCursorLocation(artifacts: Boop[]) {
|
function useSetAppModeFromCursorLocation(artifacts: Artifact[]) {
|
||||||
const { selectionRange, guiMode, setGuiMode, ast } = useStore(
|
const { selectionRange, guiMode, setGuiMode, ast } = useStore(
|
||||||
({ selectionRange, guiMode, setGuiMode, ast }) => ({
|
({ selectionRange, guiMode, setGuiMode, ast }) => ({
|
||||||
selectionRange,
|
selectionRange,
|
||||||
@ -438,7 +451,7 @@ function useSetAppModeFromCursorLocation(artifacts: Boop[]) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const artifactsWithinCursorRange: (
|
const artifactsWithinCursorRange: (
|
||||||
| {
|
| {
|
||||||
parentType: Boop['type']
|
parentType: Artifact['type']
|
||||||
isParent: true
|
isParent: true
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
sourceRange: SourceRange
|
sourceRange: SourceRange
|
||||||
@ -446,25 +459,29 @@ function useSetAppModeFromCursorLocation(artifacts: Boop[]) {
|
|||||||
position: Position
|
position: Position
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
parentType: Boop['type']
|
parentType: Artifact['type']
|
||||||
isParent: false
|
isParent: false
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
sourceRange: SourceRange
|
sourceRange: SourceRange
|
||||||
|
rotation: Rotation
|
||||||
|
position: Position
|
||||||
}
|
}
|
||||||
)[] = []
|
)[] = []
|
||||||
artifacts?.forEach((artifact) => {
|
artifacts?.forEach((artifact) => {
|
||||||
artifact.value.forEach((geo) => {
|
artifact.value.forEach((geo) => {
|
||||||
if (isOverlapping(geo.__geoMeta.sourceRange, selectionRange)) {
|
if (isOverlap(geo.__geoMeta.sourceRange, selectionRange)) {
|
||||||
artifactsWithinCursorRange.push({
|
artifactsWithinCursorRange.push({
|
||||||
parentType: artifact.type,
|
parentType: artifact.type,
|
||||||
isParent: false,
|
isParent: false,
|
||||||
pathToNode: geo.__geoMeta.pathToNode,
|
pathToNode: geo.__geoMeta.pathToNode,
|
||||||
sourceRange: geo.__geoMeta.sourceRange,
|
sourceRange: geo.__geoMeta.sourceRange,
|
||||||
|
rotation: artifact.rotation,
|
||||||
|
position: artifact.position,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
artifact.__meta.forEach((meta) => {
|
artifact.__meta.forEach((meta) => {
|
||||||
if (isOverlapping(meta.sourceRange, selectionRange)) {
|
if (isOverlap(meta.sourceRange, selectionRange)) {
|
||||||
artifactsWithinCursorRange.push({
|
artifactsWithinCursorRange.push({
|
||||||
parentType: artifact.type,
|
parentType: artifact.type,
|
||||||
isParent: true,
|
isParent: true,
|
||||||
@ -477,35 +494,39 @@ function useSetAppModeFromCursorLocation(artifacts: Boop[]) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
const parentArtifacts = artifactsWithinCursorRange.filter((a) => a.isParent)
|
const parentArtifacts = artifactsWithinCursorRange.filter((a) => a.isParent)
|
||||||
if (parentArtifacts.length > 1) {
|
const hasSketchArtifact = artifactsWithinCursorRange.filter(
|
||||||
console.log('multiple parents, might be an issue?', parentArtifacts)
|
({ parentType }) => parentType === 'sketchGroup'
|
||||||
}
|
)
|
||||||
|
const hasExtrudeArtifact = artifactsWithinCursorRange.filter(
|
||||||
|
({ parentType }) => parentType === 'extrudeGroup'
|
||||||
|
)
|
||||||
const artifact = parentArtifacts[0]
|
const artifact = parentArtifacts[0]
|
||||||
const shouldHighlight = !!artifact
|
const shouldHighlight = !!artifact || hasSketchArtifact.length
|
||||||
if (
|
if (
|
||||||
shouldHighlight &&
|
|
||||||
(guiMode.mode === 'default' || guiMode.mode === 'canEditSketch') &&
|
(guiMode.mode === 'default' || guiMode.mode === 'canEditSketch') &&
|
||||||
ast &&
|
ast &&
|
||||||
artifact.parentType === 'sketchGroup' &&
|
hasSketchArtifact.length
|
||||||
artifact.isParent
|
|
||||||
) {
|
) {
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, artifact.sourceRange)
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
const { rotation, position } = artifact
|
ast,
|
||||||
|
hasSketchArtifact[0].sourceRange
|
||||||
|
)
|
||||||
|
const { rotation, position } = hasSketchArtifact[0]
|
||||||
setGuiMode({ mode: 'canEditSketch', pathToNode, rotation, position })
|
setGuiMode({ mode: 'canEditSketch', pathToNode, rotation, position })
|
||||||
} else if (
|
} else if (
|
||||||
shouldHighlight &&
|
hasExtrudeArtifact.length &&
|
||||||
(guiMode.mode === 'default' || guiMode.mode === 'canEditSketch') &&
|
(guiMode.mode === 'default' || guiMode.mode === 'canEditExtrude') &&
|
||||||
ast &&
|
ast
|
||||||
artifact.parentType === 'extrudeGroup' &&
|
|
||||||
artifact.isParent
|
|
||||||
) {
|
) {
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, artifact.sourceRange)
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
const { rotation, position } = artifact
|
ast,
|
||||||
|
hasExtrudeArtifact[0].sourceRange
|
||||||
|
)
|
||||||
|
const { rotation, position } = hasExtrudeArtifact[0]
|
||||||
setGuiMode({ mode: 'canEditExtrude', pathToNode, rotation, position })
|
setGuiMode({ mode: 'canEditExtrude', pathToNode, rotation, position })
|
||||||
} else if (
|
} else if (
|
||||||
!shouldHighlight &&
|
!shouldHighlight &&
|
||||||
(guiMode.mode === 'canEditSketch' || guiMode.mode === 'canEditExtrude')
|
(guiMode.mode === 'canEditExtrude' || guiMode.mode === 'canEditSketch')
|
||||||
// (artifact.parentType === 'extrudeGroup' || artifact.type === 'extrudeGroup')
|
|
||||||
) {
|
) {
|
||||||
setGuiMode({ mode: 'default' })
|
setGuiMode({ mode: 'default' })
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { useStore } from '../useStore'
|
import { useStore } from '../useStore'
|
||||||
import { DoubleSide, Vector3, Quaternion } from 'three'
|
import { DoubleSide, Vector3, Quaternion } from 'three'
|
||||||
import { Program } from '../lang/abstractSyntaxTree'
|
import { Program } from '../lang/abstractSyntaxTree'
|
||||||
import { addLine } from '../lang/modifyAst'
|
import { toolTipModification } from '../lang/std/sketch'
|
||||||
|
import { roundOff } from '../lib/utils'
|
||||||
|
|
||||||
export const SketchPlane = () => {
|
export const SketchPlane = () => {
|
||||||
const { ast, guiMode, updateAst } = useStore(
|
const { ast, guiMode, updateAst, programMemory } = useStore((s) => ({
|
||||||
({ guiMode, ast, updateAst }) => ({
|
guiMode: s.guiMode,
|
||||||
guiMode,
|
ast: s.ast,
|
||||||
ast,
|
updateAst: s.updateAst,
|
||||||
updateAst,
|
programMemory: s.programMemory,
|
||||||
})
|
}))
|
||||||
)
|
|
||||||
if (guiMode.mode !== 'sketch') {
|
if (guiMode.mode !== 'sketch') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
if (guiMode.sketchMode !== 'points' && guiMode.sketchMode !== 'sketchEdit') {
|
if (!(guiMode.sketchMode === 'lineTo') && !('isTooltip' in guiMode)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ export const SketchPlane = () => {
|
|||||||
position={position}
|
position={position}
|
||||||
name={sketchGridName}
|
name={sketchGridName}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (guiMode.sketchMode !== 'points') {
|
if (!('isTooltip' in guiMode)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const sketchGridIntersection = e.intersections.find(
|
const sketchGridIntersection = e.intersections.find(
|
||||||
@ -65,10 +65,11 @@ export const SketchPlane = () => {
|
|||||||
nonCodeMeta: {},
|
nonCodeMeta: {},
|
||||||
}
|
}
|
||||||
const addLinePoint: [number, number] = [point.x, point.y]
|
const addLinePoint: [number, number] = [point.x, point.y]
|
||||||
const { modifiedAst } = addLine(
|
const { modifiedAst } = toolTipModification(
|
||||||
_ast,
|
_ast,
|
||||||
guiMode.pathToNode,
|
programMemory,
|
||||||
addLinePoint
|
addLinePoint,
|
||||||
|
guiMode
|
||||||
)
|
)
|
||||||
updateAst(modifiedAst)
|
updateAst(modifiedAst)
|
||||||
}}
|
}}
|
||||||
@ -91,10 +92,6 @@ export const SketchPlane = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function roundy({ x, y, z }: any) {
|
function roundy({ x, y, z }: any) {
|
||||||
const roundOff = (num: number, places: number): number => {
|
|
||||||
const x = Math.pow(10, places)
|
|
||||||
return Math.round(num * x) / x
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
x: roundOff(x, 2),
|
x: roundOff(x, 2),
|
||||||
y: roundOff(y, 2),
|
y: roundOff(y, 2),
|
||||||
|
@ -462,198 +462,6 @@ const myVar = funcN(1, 2)`
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('structures specific to this lang', () => {
|
|
||||||
test('sketch', () => {
|
|
||||||
let code = `sketch mySketch {
|
|
||||||
path myPath = lineTo(0,1)
|
|
||||||
lineTo(1,1)
|
|
||||||
path rightPath = lineTo(1,0)
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
`
|
|
||||||
const tokens = lexer(code)
|
|
||||||
const { body } = abstractSyntaxTree(tokens)
|
|
||||||
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
|
||||||
expect(body).toEqual([
|
|
||||||
{
|
|
||||||
type: 'VariableDeclaration',
|
|
||||||
start: 0,
|
|
||||||
end: 102,
|
|
||||||
kind: 'sketch',
|
|
||||||
declarations: [
|
|
||||||
{
|
|
||||||
type: 'VariableDeclarator',
|
|
||||||
start: 7,
|
|
||||||
end: 102,
|
|
||||||
id: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 7,
|
|
||||||
end: 15,
|
|
||||||
name: 'mySketch',
|
|
||||||
},
|
|
||||||
init: {
|
|
||||||
type: 'SketchExpression',
|
|
||||||
start: 16,
|
|
||||||
end: 102,
|
|
||||||
body: {
|
|
||||||
type: 'BlockStatement',
|
|
||||||
start: 16,
|
|
||||||
end: 102,
|
|
||||||
body: [
|
|
||||||
{
|
|
||||||
type: 'VariableDeclaration',
|
|
||||||
start: 20,
|
|
||||||
end: 45,
|
|
||||||
kind: 'path',
|
|
||||||
declarations: [
|
|
||||||
{
|
|
||||||
type: 'VariableDeclarator',
|
|
||||||
start: 25,
|
|
||||||
end: 45,
|
|
||||||
id: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 25,
|
|
||||||
end: 31,
|
|
||||||
name: 'myPath',
|
|
||||||
},
|
|
||||||
init: {
|
|
||||||
type: 'CallExpression',
|
|
||||||
start: 34,
|
|
||||||
end: 45,
|
|
||||||
callee: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 34,
|
|
||||||
end: 40,
|
|
||||||
name: 'lineTo',
|
|
||||||
},
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
start: 41,
|
|
||||||
end: 42,
|
|
||||||
value: 0,
|
|
||||||
raw: '0',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
start: 43,
|
|
||||||
end: 44,
|
|
||||||
value: 1,
|
|
||||||
raw: '1',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
optional: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'ExpressionStatement',
|
|
||||||
start: 48,
|
|
||||||
end: 59,
|
|
||||||
expression: {
|
|
||||||
type: 'CallExpression',
|
|
||||||
start: 48,
|
|
||||||
end: 59,
|
|
||||||
callee: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 48,
|
|
||||||
end: 54,
|
|
||||||
name: 'lineTo',
|
|
||||||
},
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
start: 55,
|
|
||||||
end: 56,
|
|
||||||
value: 1,
|
|
||||||
raw: '1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
start: 57,
|
|
||||||
end: 58,
|
|
||||||
value: 1,
|
|
||||||
raw: '1',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
optional: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'VariableDeclaration',
|
|
||||||
start: 62,
|
|
||||||
end: 90,
|
|
||||||
kind: 'path',
|
|
||||||
declarations: [
|
|
||||||
{
|
|
||||||
type: 'VariableDeclarator',
|
|
||||||
start: 67,
|
|
||||||
end: 90,
|
|
||||||
id: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 67,
|
|
||||||
end: 76,
|
|
||||||
name: 'rightPath',
|
|
||||||
},
|
|
||||||
init: {
|
|
||||||
type: 'CallExpression',
|
|
||||||
start: 79,
|
|
||||||
end: 90,
|
|
||||||
callee: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 79,
|
|
||||||
end: 85,
|
|
||||||
name: 'lineTo',
|
|
||||||
},
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
start: 86,
|
|
||||||
end: 87,
|
|
||||||
value: 1,
|
|
||||||
raw: '1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
start: 88,
|
|
||||||
end: 89,
|
|
||||||
value: 0,
|
|
||||||
raw: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
optional: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'ExpressionStatement',
|
|
||||||
start: 93,
|
|
||||||
end: 100,
|
|
||||||
expression: {
|
|
||||||
type: 'CallExpression',
|
|
||||||
start: 93,
|
|
||||||
end: 100,
|
|
||||||
callee: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 93,
|
|
||||||
end: 98,
|
|
||||||
name: 'close',
|
|
||||||
},
|
|
||||||
arguments: [],
|
|
||||||
optional: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('testing hasPipeOperator', () => {
|
describe('testing hasPipeOperator', () => {
|
||||||
test('hasPipeOperator is true', () => {
|
test('hasPipeOperator is true', () => {
|
||||||
let code = `sketch mySketch {
|
let code = `sketch mySketch {
|
||||||
@ -739,186 +547,230 @@ const yo = myFunc(9()
|
|||||||
|
|
||||||
describe('testing pipe operator special', () => {
|
describe('testing pipe operator special', () => {
|
||||||
test('pipe operator with sketch', () => {
|
test('pipe operator with sketch', () => {
|
||||||
let code = `sketch mySketch {
|
let code = `const mySketch = startSketchAt([0, 0])
|
||||||
lineTo(2, 3)
|
|> lineTo([2, 3], %)
|
||||||
path myPath = lineTo(0, 1)
|
|> lineTo({ to: [0, 1], tag: "myPath" }, %)
|
||||||
lineTo(1,1)
|
|> lineTo([1, 1], %)
|
||||||
} |> rx(45, %)
|
} |> rx(45, %)
|
||||||
`
|
`
|
||||||
const tokens = lexer(code)
|
const tokens = lexer(code)
|
||||||
const { body } = abstractSyntaxTree(tokens)
|
const { body } = abstractSyntaxTree(tokens)
|
||||||
delete (body[0] as any).declarations[0].init.nonCodeMeta
|
delete (body[0] as any).declarations[0].init.nonCodeMeta
|
||||||
delete (body[0] as any).declarations[0].init.body[0].body.nonCodeMeta
|
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 90,
|
end: 145,
|
||||||
kind: 'sketch',
|
kind: 'const',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: 'VariableDeclarator',
|
type: 'VariableDeclarator',
|
||||||
start: 7,
|
start: 6,
|
||||||
end: 90,
|
end: 145,
|
||||||
id: {
|
id: { type: 'Identifier', start: 6, end: 14, name: 'mySketch' },
|
||||||
type: 'Identifier',
|
|
||||||
start: 7,
|
|
||||||
end: 15,
|
|
||||||
name: 'mySketch',
|
|
||||||
},
|
|
||||||
init: {
|
init: {
|
||||||
type: 'PipeExpression',
|
type: 'PipeExpression',
|
||||||
start: 16,
|
start: 15,
|
||||||
end: 90,
|
end: 145,
|
||||||
body: [
|
body: [
|
||||||
{
|
{
|
||||||
type: 'SketchExpression',
|
|
||||||
start: 16,
|
|
||||||
end: 77,
|
|
||||||
body: {
|
|
||||||
type: 'BlockStatement',
|
|
||||||
start: 16,
|
|
||||||
end: 77,
|
|
||||||
body: [
|
|
||||||
{
|
|
||||||
type: 'ExpressionStatement',
|
|
||||||
start: 20,
|
|
||||||
end: 32,
|
|
||||||
expression: {
|
|
||||||
type: 'CallExpression',
|
type: 'CallExpression',
|
||||||
start: 20,
|
start: 17,
|
||||||
end: 32,
|
end: 38,
|
||||||
callee: {
|
callee: {
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
start: 20,
|
start: 17,
|
||||||
end: 26,
|
end: 30,
|
||||||
name: 'lineTo',
|
name: 'startSketchAt',
|
||||||
},
|
},
|
||||||
arguments: [
|
arguments: [
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'ArrayExpression',
|
||||||
start: 27,
|
start: 31,
|
||||||
end: 28,
|
end: 37,
|
||||||
value: 2,
|
elements: [
|
||||||
raw: '2',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 30,
|
start: 32,
|
||||||
end: 31,
|
end: 33,
|
||||||
value: 3,
|
|
||||||
raw: '3',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
optional: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'VariableDeclaration',
|
|
||||||
start: 35,
|
|
||||||
end: 61,
|
|
||||||
kind: 'path',
|
|
||||||
declarations: [
|
|
||||||
{
|
|
||||||
type: 'VariableDeclarator',
|
|
||||||
start: 40,
|
|
||||||
end: 61,
|
|
||||||
id: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 40,
|
|
||||||
end: 46,
|
|
||||||
name: 'myPath',
|
|
||||||
},
|
|
||||||
init: {
|
|
||||||
type: 'CallExpression',
|
|
||||||
start: 49,
|
|
||||||
end: 61,
|
|
||||||
callee: {
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 49,
|
|
||||||
end: 55,
|
|
||||||
name: 'lineTo',
|
|
||||||
},
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
start: 56,
|
|
||||||
end: 57,
|
|
||||||
value: 0,
|
value: 0,
|
||||||
raw: '0',
|
raw: '0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 59,
|
start: 35,
|
||||||
end: 60,
|
end: 36,
|
||||||
value: 1,
|
value: 0,
|
||||||
raw: '1',
|
raw: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
optional: false,
|
optional: false,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'ExpressionStatement',
|
|
||||||
start: 64,
|
|
||||||
end: 75,
|
|
||||||
expression: {
|
|
||||||
type: 'CallExpression',
|
type: 'CallExpression',
|
||||||
start: 64,
|
start: 44,
|
||||||
end: 75,
|
end: 61,
|
||||||
callee: {
|
callee: {
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
start: 64,
|
start: 44,
|
||||||
end: 70,
|
end: 50,
|
||||||
name: 'lineTo',
|
name: 'lineTo',
|
||||||
},
|
},
|
||||||
arguments: [
|
arguments: [
|
||||||
|
{
|
||||||
|
type: 'ArrayExpression',
|
||||||
|
start: 51,
|
||||||
|
end: 57,
|
||||||
|
elements: [
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 71,
|
start: 52,
|
||||||
end: 72,
|
end: 53,
|
||||||
value: 1,
|
value: 2,
|
||||||
raw: '1',
|
raw: '2',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 73,
|
start: 55,
|
||||||
end: 74,
|
end: 56,
|
||||||
value: 1,
|
value: 3,
|
||||||
raw: '1',
|
raw: '3',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{ type: 'PipeSubstitution', start: 59, end: 60 },
|
||||||
|
],
|
||||||
optional: false,
|
optional: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'CallExpression',
|
||||||
|
start: 67,
|
||||||
|
end: 107,
|
||||||
|
callee: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 67,
|
||||||
|
end: 73,
|
||||||
|
name: 'lineTo',
|
||||||
|
},
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
start: 74,
|
||||||
|
end: 103,
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
type: 'ObjectProperty',
|
||||||
|
start: 76,
|
||||||
|
end: 86,
|
||||||
|
key: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 76,
|
||||||
|
end: 78,
|
||||||
|
name: 'to',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'ArrayExpression',
|
||||||
|
start: 80,
|
||||||
|
end: 86,
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'Literal',
|
||||||
|
start: 81,
|
||||||
|
end: 82,
|
||||||
|
value: 0,
|
||||||
|
raw: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'Literal',
|
||||||
|
start: 84,
|
||||||
|
end: 85,
|
||||||
|
value: 1,
|
||||||
|
raw: '1',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'ObjectProperty',
|
||||||
|
start: 88,
|
||||||
|
end: 101,
|
||||||
|
key: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 88,
|
||||||
|
end: 91,
|
||||||
|
name: 'tag',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'Literal',
|
||||||
|
start: 93,
|
||||||
|
end: 101,
|
||||||
|
value: 'myPath',
|
||||||
|
raw: '"myPath"',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ type: 'PipeSubstitution', start: 105, end: 106 },
|
||||||
|
],
|
||||||
|
optional: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'CallExpression',
|
type: 'CallExpression',
|
||||||
start: 81,
|
start: 113,
|
||||||
end: 90,
|
end: 130,
|
||||||
callee: {
|
callee: {
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
start: 81,
|
start: 113,
|
||||||
end: 83,
|
end: 119,
|
||||||
|
name: 'lineTo',
|
||||||
|
},
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
type: 'ArrayExpression',
|
||||||
|
start: 120,
|
||||||
|
end: 126,
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'Literal',
|
||||||
|
start: 121,
|
||||||
|
end: 122,
|
||||||
|
value: 1,
|
||||||
|
raw: '1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'Literal',
|
||||||
|
start: 124,
|
||||||
|
end: 125,
|
||||||
|
value: 1,
|
||||||
|
raw: '1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ type: 'PipeSubstitution', start: 128, end: 129 },
|
||||||
|
],
|
||||||
|
optional: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'CallExpression',
|
||||||
|
start: 136,
|
||||||
|
end: 145,
|
||||||
|
callee: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 136,
|
||||||
|
end: 138,
|
||||||
name: 'rx',
|
name: 'rx',
|
||||||
},
|
},
|
||||||
arguments: [
|
arguments: [
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 84,
|
start: 139,
|
||||||
end: 86,
|
end: 141,
|
||||||
value: 45,
|
value: 45,
|
||||||
raw: '45',
|
raw: '45',
|
||||||
},
|
},
|
||||||
{
|
{ type: 'PipeSubstitution', start: 143, end: 144 },
|
||||||
type: 'PipeSubstitution',
|
|
||||||
start: 88,
|
|
||||||
end: 89,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
optional: false,
|
optional: false,
|
||||||
},
|
},
|
||||||
@ -1841,47 +1693,42 @@ const key = 'c'`
|
|||||||
expect(nonCodeMeta2[0].start).not.toBe(nonCodeMetaInstance.start)
|
expect(nonCodeMeta2[0].start).not.toBe(nonCodeMetaInstance.start)
|
||||||
})
|
})
|
||||||
it('comments nested within a block statement', () => {
|
it('comments nested within a block statement', () => {
|
||||||
const code = `sketch mySketch {
|
const code = `const mySketch = startSketchAt([0,0])
|
||||||
path myPath = lineTo(0,1)
|
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||||
lineTo(1,1) /* this is
|
|> lineTo([1, 1], %) /* this is
|
||||||
a comment
|
a comment
|
||||||
spanning a few lines */
|
spanning a few lines */
|
||||||
path rightPath = lineTo(1,0)
|
|> lineTo({ to: [1,0], tag: "rightPath" }, %)
|
||||||
close()
|
|> close(%)
|
||||||
}
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const { body } = abstractSyntaxTree(lexer(code))
|
const { body } = abstractSyntaxTree(lexer(code))
|
||||||
const indexOfSecondLineToExpression = 1 // 0 index so `path myPath = lineTo(0,1)` is 0
|
const indexOfSecondLineToExpression = 2
|
||||||
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.body
|
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta
|
||||||
.nonCodeMeta
|
|
||||||
expect(sketchNonCodeMeta[indexOfSecondLineToExpression]).toEqual({
|
expect(sketchNonCodeMeta[indexOfSecondLineToExpression]).toEqual({
|
||||||
type: 'NoneCodeNode',
|
type: 'NoneCodeNode',
|
||||||
start: 67,
|
start: 106,
|
||||||
end: 133,
|
end: 168,
|
||||||
value:
|
value:
|
||||||
' /* this is \n a comment \n spanning a few lines */\n ',
|
' /* this is \n a comment \n spanning a few lines */\n ',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('comments in a pipe expression', () => {
|
it('comments in a pipe expression', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'sketch mySk1 {',
|
'const mySk1 = startSketchAt([0, 0])',
|
||||||
' lineTo(1, 1)',
|
' |> lineTo([1, 1], %)',
|
||||||
' path myPath = lineTo(0, 1)',
|
' |> lineTo({to: [0, 1], tag: "myPath"}, %)',
|
||||||
' lineTo(1, 1)',
|
' |> lineTo([1, 1], %)',
|
||||||
'}',
|
|
||||||
'// a comment',
|
'// a comment',
|
||||||
' |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
|
|
||||||
const { body } = abstractSyntaxTree(lexer(code))
|
const { body } = abstractSyntaxTree(lexer(code))
|
||||||
const bing = abstractSyntaxTree(lexer(code))
|
|
||||||
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
|
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
|
||||||
expect(1).toBe(1)
|
expect(sketchNonCodeMeta[3]).toEqual({
|
||||||
expect(sketchNonCodeMeta[0]).toEqual({
|
|
||||||
type: 'NoneCodeNode',
|
type: 'NoneCodeNode',
|
||||||
start: 75,
|
start: 125,
|
||||||
end: 91,
|
end: 141,
|
||||||
value: '\n// a comment\n ',
|
value: '\n// a comment\n ',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -17,7 +17,6 @@ type syntaxType =
|
|||||||
| 'ObjectExpression'
|
| 'ObjectExpression'
|
||||||
| 'ObjectProperty'
|
| 'ObjectProperty'
|
||||||
| 'FunctionExpression'
|
| 'FunctionExpression'
|
||||||
| 'SketchExpression'
|
|
||||||
| 'PipeExpression'
|
| 'PipeExpression'
|
||||||
| 'PipeSubstitution'
|
| 'PipeSubstitution'
|
||||||
| 'Literal'
|
| 'Literal'
|
||||||
@ -321,14 +320,14 @@ function makeArguments(
|
|||||||
export interface VariableDeclaration extends GeneralStatement {
|
export interface VariableDeclaration extends GeneralStatement {
|
||||||
type: 'VariableDeclaration'
|
type: 'VariableDeclaration'
|
||||||
declarations: VariableDeclarator[]
|
declarations: VariableDeclarator[]
|
||||||
kind: 'const' | 'unknown' | 'fn' | 'sketch' | 'path' //| "solid" | "surface" | "face"
|
kind: 'const' | 'unknown' | 'fn' //| "solid" | "surface" | "face"
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeVariableDeclaration(
|
function makeVariableDeclaration(
|
||||||
tokens: Token[],
|
tokens: Token[],
|
||||||
index: number
|
index: number
|
||||||
): { declaration: VariableDeclaration; lastIndex: number } {
|
): { declaration: VariableDeclaration; lastIndex: number } {
|
||||||
// token index should point to a declaration keyword i.e. const, fn, sketch, path
|
// token index should point to a declaration keyword i.e. const, fn
|
||||||
const currentToken = tokens[index]
|
const currentToken = tokens[index]
|
||||||
const declarationStartToken = nextMeaningfulToken(tokens, index)
|
const declarationStartToken = nextMeaningfulToken(tokens, index)
|
||||||
const { declarations, lastIndex } = makeVariableDeclarators(
|
const { declarations, lastIndex } = makeVariableDeclarators(
|
||||||
@ -345,10 +344,6 @@ function makeVariableDeclaration(
|
|||||||
? 'const'
|
? 'const'
|
||||||
: currentToken.value === 'fn'
|
: currentToken.value === 'fn'
|
||||||
? 'fn'
|
? 'fn'
|
||||||
: currentToken.value === 'sketch'
|
|
||||||
? 'sketch'
|
|
||||||
: currentToken.value === 'path'
|
|
||||||
? 'path'
|
|
||||||
: 'unknown',
|
: 'unknown',
|
||||||
declarations,
|
declarations,
|
||||||
},
|
},
|
||||||
@ -362,7 +357,6 @@ export type Value =
|
|||||||
| BinaryExpression
|
| BinaryExpression
|
||||||
| FunctionExpression
|
| FunctionExpression
|
||||||
| CallExpression
|
| CallExpression
|
||||||
| SketchExpression
|
|
||||||
| PipeExpression
|
| PipeExpression
|
||||||
| PipeSubstitution
|
| PipeSubstitution
|
||||||
| ArrayExpression
|
| ArrayExpression
|
||||||
@ -486,13 +480,6 @@ function makeVariableDeclarators(
|
|||||||
)
|
)
|
||||||
init = expression
|
init = expression
|
||||||
lastIndex = pipeLastIndex
|
lastIndex = pipeLastIndex
|
||||||
} else if (
|
|
||||||
declarationToken.token.type === 'word' &&
|
|
||||||
declarationToken.token.value === 'sketch'
|
|
||||||
) {
|
|
||||||
const sketchExp = makeSketchExpression(tokens, assignmentToken.index)
|
|
||||||
init = sketchExp.expression
|
|
||||||
lastIndex = sketchExp.lastIndex
|
|
||||||
} else {
|
} else {
|
||||||
const { value, lastIndex: valueLastIndex } = makeValue(
|
const { value, lastIndex: valueLastIndex } = makeValue(
|
||||||
tokens,
|
tokens,
|
||||||
@ -544,7 +531,7 @@ function makeIdentifier(token: Token[], index: number): Identifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PipeSubstitution extends GeneralStatement {
|
export interface PipeSubstitution extends GeneralStatement {
|
||||||
type: 'PipeSubstitution'
|
type: 'PipeSubstitution'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -841,30 +828,6 @@ function makeBinaryExpression(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SketchExpression extends GeneralStatement {
|
|
||||||
type: 'SketchExpression'
|
|
||||||
body: BlockStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeSketchExpression(
|
|
||||||
tokens: Token[],
|
|
||||||
index: number
|
|
||||||
): { expression: SketchExpression; lastIndex: number } {
|
|
||||||
const currentToken = tokens[index]
|
|
||||||
const { block, lastIndex: bodyLastIndex } = makeBlockStatement(tokens, index)
|
|
||||||
const endToken = tokens[bodyLastIndex]
|
|
||||||
|
|
||||||
return {
|
|
||||||
expression: {
|
|
||||||
type: 'SketchExpression',
|
|
||||||
start: currentToken.start,
|
|
||||||
end: endToken.end,
|
|
||||||
body: block,
|
|
||||||
},
|
|
||||||
lastIndex: bodyLastIndex,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PipeExpression extends GeneralStatement {
|
export interface PipeExpression extends GeneralStatement {
|
||||||
type: 'PipeExpression'
|
type: 'PipeExpression'
|
||||||
body: Value[]
|
body: Value[]
|
||||||
@ -909,10 +872,6 @@ function makePipeBody(
|
|||||||
const val = makeValue(tokens, expressionStart.index)
|
const val = makeValue(tokens, expressionStart.index)
|
||||||
value = val.value
|
value = val.value
|
||||||
lastIndex = val.lastIndex
|
lastIndex = val.lastIndex
|
||||||
} else if (currentToken.type === 'brace' && currentToken.value === '{') {
|
|
||||||
const sketch = makeSketchExpression(tokens, index)
|
|
||||||
value = sketch.expression
|
|
||||||
lastIndex = sketch.lastIndex
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Expected a previous PipeValue if statement to match')
|
throw new Error('Expected a previous PipeValue if statement to match')
|
||||||
}
|
}
|
||||||
@ -1125,10 +1084,7 @@ function makeBody(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
token.type === 'word' &&
|
token.type === 'word' &&
|
||||||
(token.value === 'const' ||
|
(token.value === 'const' || token.value === 'fn')
|
||||||
token.value === 'fn' ||
|
|
||||||
token.value === 'sketch' ||
|
|
||||||
token.value === 'path')
|
|
||||||
) {
|
) {
|
||||||
const { declaration, lastIndex } = makeVariableDeclaration(
|
const { declaration, lastIndex } = makeVariableDeclaration(
|
||||||
tokens,
|
tokens,
|
||||||
@ -1218,10 +1174,7 @@ export function findNextDeclarationKeyword(
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
nextToken.token.type === 'word' &&
|
nextToken.token.type === 'word' &&
|
||||||
(nextToken.token.value === 'const' ||
|
(nextToken.token.value === 'const' || nextToken.token.value === 'fn')
|
||||||
nextToken.token.value === 'fn' ||
|
|
||||||
nextToken.token.value === 'sketch' ||
|
|
||||||
nextToken.token.value === 'path')
|
|
||||||
) {
|
) {
|
||||||
return nextToken
|
return nextToken
|
||||||
}
|
}
|
||||||
@ -1287,7 +1240,7 @@ export function hasPipeOperator(
|
|||||||
_limitIndex = -1
|
_limitIndex = -1
|
||||||
): ReturnType<typeof nextMeaningfulToken> | false {
|
): ReturnType<typeof nextMeaningfulToken> | false {
|
||||||
// this probably still needs some work
|
// this probably still needs some work
|
||||||
// should be called on expression statuments (i.e "lineTo" for lineTo(10, 10)) or "{" for sketch declarations
|
// should be called on expression statuments (i.e "lineTo" for lineTo(10, 10))
|
||||||
let limitIndex = _limitIndex
|
let limitIndex = _limitIndex
|
||||||
if (limitIndex === -1) {
|
if (limitIndex === -1) {
|
||||||
const callExpressionEnd = isCallExpression(tokens, index)
|
const callExpressionEnd = isCallExpression(tokens, index)
|
||||||
@ -1584,34 +1537,13 @@ export function getNodePathFromSourceRange(
|
|||||||
const init = declaration.init
|
const init = declaration.init
|
||||||
if (init.start <= start && init.end >= end) {
|
if (init.start <= start && init.end >= end) {
|
||||||
path.push('init')
|
path.push('init')
|
||||||
if (init.type === 'SketchExpression') {
|
if (init.type === 'PipeExpression') {
|
||||||
const body = init.body
|
|
||||||
if (body.start <= start && body.end >= end) {
|
|
||||||
path.push('body')
|
|
||||||
if (body.type === 'BlockStatement') {
|
|
||||||
path = getNodePathFromSourceRange(body, sourceRange, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (init.type === 'PipeExpression') {
|
|
||||||
const body = init.body
|
const body = init.body
|
||||||
for (let pipeIndex = 0; pipeIndex < body.length; pipeIndex++) {
|
for (let pipeIndex = 0; pipeIndex < body.length; pipeIndex++) {
|
||||||
const pipe = body[pipeIndex]
|
const pipe = body[pipeIndex]
|
||||||
if (pipe.start <= start && pipe.end >= end) {
|
if (pipe.start <= start && pipe.end >= end) {
|
||||||
path.push('body')
|
path.push('body')
|
||||||
path.push(pipeIndex)
|
path.push(pipeIndex)
|
||||||
if (pipe.type === 'SketchExpression') {
|
|
||||||
const body = pipe.body
|
|
||||||
if (body.start <= start && body.end >= end) {
|
|
||||||
path.push('body')
|
|
||||||
if (body.type === 'BlockStatement') {
|
|
||||||
path = getNodePathFromSourceRange(
|
|
||||||
body,
|
|
||||||
sourceRange,
|
|
||||||
path
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (init.type === 'CallExpression') {
|
} else if (init.type === 'CallExpression') {
|
||||||
|
@ -5,10 +5,9 @@ import { executor, SketchGroup, ExtrudeGroup } from './executor'
|
|||||||
describe('testing artifacts', () => {
|
describe('testing artifacts', () => {
|
||||||
test('sketch artifacts', () => {
|
test('sketch artifacts', () => {
|
||||||
const code = `
|
const code = `
|
||||||
sketch mySketch001 {
|
const mySketch001 = startSketchAt([0, 0])
|
||||||
lineTo(-1.59, -1.54)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
lineTo(0.46, -5.82)
|
|> lineTo([0.46, -5.82], %)
|
||||||
}
|
|
||||||
|> rx(45, %)
|
|> rx(45, %)
|
||||||
show(mySketch001)`
|
show(mySketch001)`
|
||||||
const programMemory = executor(abstractSyntaxTree(lexer(code)))
|
const programMemory = executor(abstractSyntaxTree(lexer(code)))
|
||||||
@ -19,13 +18,14 @@ show(mySketch001)`
|
|||||||
expect(artifactsWithoutGeos).toEqual([
|
expect(artifactsWithoutGeos).toEqual([
|
||||||
{
|
{
|
||||||
type: 'sketchGroup',
|
type: 'sketchGroup',
|
||||||
|
start: [0, 0],
|
||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
type: 'toPoint',
|
type: 'toPoint',
|
||||||
to: [-1.59, -1.54],
|
to: [-1.59, -1.54],
|
||||||
from: [0, 0],
|
from: [0, 0],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
sourceRange: [24, 44],
|
sourceRange: [48, 73],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
geos: ['line', 'lineEnd'],
|
geos: ['line', 'lineEnd'],
|
||||||
},
|
},
|
||||||
@ -35,7 +35,7 @@ show(mySketch001)`
|
|||||||
to: [0.46, -5.82],
|
to: [0.46, -5.82],
|
||||||
from: [-1.59, -1.54],
|
from: [-1.59, -1.54],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
sourceRange: [47, 66],
|
sourceRange: [79, 103],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
geos: ['line', 'lineEnd'],
|
geos: ['line', 'lineEnd'],
|
||||||
},
|
},
|
||||||
@ -44,24 +44,17 @@ show(mySketch001)`
|
|||||||
position: [0, 0, 0],
|
position: [0, 0, 0],
|
||||||
rotation: [0.3826834323650898, 0, 0, 0.9238795325112867],
|
rotation: [0.3826834323650898, 0, 0, 0.9238795325112867],
|
||||||
__meta: [
|
__meta: [
|
||||||
{
|
{ sourceRange: [21, 42], pathToNode: [] },
|
||||||
sourceRange: [20, 68],
|
{ sourceRange: [109, 118], pathToNode: [] },
|
||||||
pathToNode: ['body', 0, 'declarations', 0, 'init', 0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sourceRange: [74, 83],
|
|
||||||
pathToNode: [],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('extrude artifacts', () => {
|
test('extrude artifacts', () => {
|
||||||
const code = `
|
const code = `
|
||||||
sketch mySketch001 {
|
const mySketch001 = startSketchAt([0, 0])
|
||||||
lineTo(-1.59, -1.54)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
lineTo(0.46, -5.82)
|
|> lineTo([0.46, -5.82], %)
|
||||||
}
|
|
||||||
|> rx(45, %)
|
|> rx(45, %)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
show(mySketch001)`
|
show(mySketch001)`
|
||||||
@ -83,7 +76,7 @@ show(mySketch001)`
|
|||||||
],
|
],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
geo: 'PlaneGeometry',
|
geo: 'PlaneGeometry',
|
||||||
sourceRange: [24, 44],
|
sourceRange: [48, 73],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -98,7 +91,7 @@ show(mySketch001)`
|
|||||||
],
|
],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
geo: 'PlaneGeometry',
|
geo: 'PlaneGeometry',
|
||||||
sourceRange: [47, 66],
|
sourceRange: [79, 103],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -107,35 +100,27 @@ show(mySketch001)`
|
|||||||
position: [0, 0, 0],
|
position: [0, 0, 0],
|
||||||
rotation: [0.3826834323650898, 0, 0, 0.9238795325112867],
|
rotation: [0.3826834323650898, 0, 0, 0.9238795325112867],
|
||||||
__meta: [
|
__meta: [
|
||||||
{
|
{ sourceRange: [124, 137], pathToNode: [] },
|
||||||
sourceRange: [89, 102],
|
{ sourceRange: [21, 42], pathToNode: [] },
|
||||||
pathToNode: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sourceRange: [20, 68],
|
|
||||||
pathToNode: ['body', 0, 'declarations', 0, 'init', 0],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('sketch extrude and sketch on one of the faces', () => {
|
test('sketch extrude and sketch on one of the faces', () => {
|
||||||
const code = `
|
const code = `
|
||||||
sketch sk1 {
|
const sk1 = startSketchAt([0, 0])
|
||||||
lineTo(-2.5, 0)
|
|> lineTo([-2.5, 0], %)
|
||||||
path p = lineTo(0, 10)
|
|> lineTo({ to: [0, 10], tag: "p" }, %)
|
||||||
lineTo(2.5, 0)
|
|> lineTo([2.5, 0], %)
|
||||||
}
|
|
||||||
|> rx(45, %)
|
|> rx(45, %)
|
||||||
|> translate([1,0,1], %)
|
|> translate([1,0,1], %)
|
||||||
|> ry(5, %)
|
|> ry(5, %)
|
||||||
const theExtrude = extrude(2, sk1)
|
const theExtrude = extrude(2, sk1)
|
||||||
const theTransf = getExtrudeWallTransform('p', theExtrude)
|
const theTransf = getExtrudeWallTransform('p', theExtrude)
|
||||||
sketch sk2 {
|
const sk2 = startSketchAt([0, 0])
|
||||||
lineTo(-2.5, 0)
|
|> lineTo([-2.5, 0], %)
|
||||||
path p = lineTo(0, 3)
|
|> lineTo({ to: [0, 3], tag: "p" }, %)
|
||||||
lineTo(2.5, 0)
|
|> lineTo([2.5, 0], %)
|
||||||
}
|
|
||||||
|> transform(theTransf, %)
|
|> transform(theTransf, %)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
|
|
||||||
@ -159,7 +144,7 @@ show(theExtrude, sk2)`
|
|||||||
],
|
],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
geo: 'PlaneGeometry',
|
geo: 'PlaneGeometry',
|
||||||
sourceRange: [16, 31],
|
sourceRange: [40, 60],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -174,7 +159,7 @@ show(theExtrude, sk2)`
|
|||||||
],
|
],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
geo: 'PlaneGeometry',
|
geo: 'PlaneGeometry',
|
||||||
sourceRange: [39, 56],
|
sourceRange: [66, 102],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
},
|
},
|
||||||
name: 'p',
|
name: 'p',
|
||||||
@ -190,7 +175,7 @@ show(theExtrude, sk2)`
|
|||||||
],
|
],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
geo: 'PlaneGeometry',
|
geo: 'PlaneGeometry',
|
||||||
sourceRange: [59, 73],
|
sourceRange: [108, 127],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -202,14 +187,8 @@ show(theExtrude, sk2)`
|
|||||||
0.9230002039112792,
|
0.9230002039112792,
|
||||||
],
|
],
|
||||||
__meta: [
|
__meta: [
|
||||||
{
|
{ sourceRange: [190, 218], pathToNode: [] },
|
||||||
sourceRange: [138, 166],
|
{ sourceRange: [13, 34], pathToNode: [] },
|
||||||
pathToNode: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sourceRange: [12, 75],
|
|
||||||
pathToNode: ['body', 0, 'declarations', 0, 'init', 0],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -226,7 +205,7 @@ show(theExtrude, sk2)`
|
|||||||
],
|
],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
geo: 'PlaneGeometry',
|
geo: 'PlaneGeometry',
|
||||||
sourceRange: [241, 256],
|
sourceRange: [317, 337],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -241,7 +220,7 @@ show(theExtrude, sk2)`
|
|||||||
],
|
],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
geo: 'PlaneGeometry',
|
geo: 'PlaneGeometry',
|
||||||
sourceRange: [264, 280],
|
sourceRange: [343, 378],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
},
|
},
|
||||||
name: 'p',
|
name: 'p',
|
||||||
@ -257,7 +236,7 @@ show(theExtrude, sk2)`
|
|||||||
],
|
],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
geo: 'PlaneGeometry',
|
geo: 'PlaneGeometry',
|
||||||
sourceRange: [283, 297],
|
sourceRange: [384, 403],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -269,14 +248,8 @@ show(theExtrude, sk2)`
|
|||||||
-0.5362616571538269,
|
-0.5362616571538269,
|
||||||
],
|
],
|
||||||
__meta: [
|
__meta: [
|
||||||
{
|
{ sourceRange: [438, 451], pathToNode: [] },
|
||||||
sourceRange: [334, 347],
|
{ sourceRange: [290, 311], pathToNode: [] },
|
||||||
pathToNode: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sourceRange: [237, 299],
|
|
||||||
pathToNode: ['body', 3, 'declarations', 0, 'init', 0],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
@ -62,12 +62,11 @@ log(5, myVar)`
|
|||||||
expect(root.magicNum.value).toBe(69)
|
expect(root.magicNum.value).toBe(69)
|
||||||
})
|
})
|
||||||
it('sketch declaration', () => {
|
it('sketch declaration', () => {
|
||||||
let code = `sketch mySketch {
|
let code = `const mySketch = startSketchAt([0,0])
|
||||||
path myPath = lineTo(0,2)
|
|> lineTo({to: [0,2], tag: "myPath"}, %)
|
||||||
lineTo(2,3)
|
|> lineTo([2,3], %)
|
||||||
path rightPath = lineTo(5,-1)
|
|> lineTo({ to: [5,-1], tag: "rightPath" }, %)
|
||||||
close()
|
// |> close(%)
|
||||||
}
|
|
||||||
show(mySketch)
|
show(mySketch)
|
||||||
`
|
`
|
||||||
const { root, return: _return } = exe(code)
|
const { root, return: _return } = exe(code)
|
||||||
@ -77,9 +76,9 @@ show(mySketch)
|
|||||||
{
|
{
|
||||||
type: 'toPoint',
|
type: 'toPoint',
|
||||||
to: [0, 2],
|
to: [0, 2],
|
||||||
from: [5, -1],
|
from: [0, 0],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
sourceRange: [25, 45],
|
sourceRange: [43, 80],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
geos: ['line', 'lineEnd'],
|
geos: ['line', 'lineEnd'],
|
||||||
},
|
},
|
||||||
@ -90,7 +89,7 @@ show(mySketch)
|
|||||||
to: [2, 3],
|
to: [2, 3],
|
||||||
from: [0, 2],
|
from: [0, 2],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
sourceRange: [48, 59],
|
sourceRange: [86, 102],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
geos: ['line', 'lineEnd'],
|
geos: ['line', 'lineEnd'],
|
||||||
},
|
},
|
||||||
@ -100,29 +99,19 @@ show(mySketch)
|
|||||||
to: [5, -1],
|
to: [5, -1],
|
||||||
from: [2, 3],
|
from: [2, 3],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
sourceRange: [67, 91],
|
sourceRange: [108, 151],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
geos: ['line', 'lineEnd'],
|
geos: ['line', 'lineEnd'],
|
||||||
},
|
},
|
||||||
name: 'rightPath',
|
name: 'rightPath',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'toPoint',
|
|
||||||
from: [5, -1],
|
|
||||||
to: [0, 2],
|
|
||||||
__geoMeta: {
|
|
||||||
sourceRange: [94, 101],
|
|
||||||
pathToNode: [],
|
|
||||||
geos: ['line', 'lineEnd'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
])
|
||||||
// expect(root.mySketch.sketch[0]).toEqual(root.mySketch.sketch[4].firstPath)
|
// expect(root.mySketch.sketch[0]).toEqual(root.mySketch.sketch[4].firstPath)
|
||||||
expect(_return).toEqual([
|
expect(_return).toEqual([
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
start: 109,
|
start: 174,
|
||||||
end: 117,
|
end: 182,
|
||||||
name: 'mySketch',
|
name: 'mySketch',
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
@ -139,13 +128,11 @@ show(mySketch)
|
|||||||
|
|
||||||
it('rotated sketch', () => {
|
it('rotated sketch', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'sketch mySk1 {',
|
'const mySk1 = startSketchAt([0,0])',
|
||||||
' lineTo(1,1)',
|
' |> lineTo([1,1], %)',
|
||||||
' path myPath = lineTo(0, 1)',
|
' |> lineTo({to: [0, 1], tag: "myPath"}, %)',
|
||||||
' lineTo(1,1)',
|
' |> lineTo([1, 1], %)',
|
||||||
'}',
|
|
||||||
'const rotated = rx(90, mySk1)',
|
'const rotated = rx(90, mySk1)',
|
||||||
// 'show(mySk1)',
|
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
expect(root.mySk1.value).toHaveLength(3)
|
expect(root.mySk1.value).toHaveLength(3)
|
||||||
@ -166,23 +153,24 @@ show(mySketch)
|
|||||||
|
|
||||||
it('execute pipe sketch into call expression', () => {
|
it('execute pipe sketch into call expression', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'sketch mySk1 {',
|
'const mySk1 = startSketchAt([0,0])',
|
||||||
' lineTo(1,1)',
|
' |> lineTo([1,1], %)',
|
||||||
' path myPath = lineTo(0, 1)',
|
' |> lineTo({to: [0, 1], tag: "myPath"}, %)',
|
||||||
' lineTo(1,1)',
|
' |> lineTo([1,1], %)',
|
||||||
'} |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { root } = exe(code)
|
const { root } = exe(code)
|
||||||
const striptVersion = removeGeoFromSketch(root.mySk1 as SketchGroup)
|
const striptVersion = removeGeoFromSketch(root.mySk1 as SketchGroup)
|
||||||
expect(striptVersion).toEqual({
|
expect(striptVersion).toEqual({
|
||||||
type: 'sketchGroup',
|
type: 'sketchGroup',
|
||||||
|
start: [0, 0],
|
||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
type: 'toPoint',
|
type: 'toPoint',
|
||||||
to: [1, 1],
|
to: [1, 1],
|
||||||
from: [0, 0],
|
from: [0, 0],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
sourceRange: [17, 28],
|
sourceRange: [40, 56],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
geos: ['line', 'lineEnd'],
|
geos: ['line', 'lineEnd'],
|
||||||
},
|
},
|
||||||
@ -192,7 +180,7 @@ show(mySketch)
|
|||||||
to: [0, 1],
|
to: [0, 1],
|
||||||
from: [1, 1],
|
from: [1, 1],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
sourceRange: [36, 57],
|
sourceRange: [62, 100],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
geos: ['line', 'lineEnd'],
|
geos: ['line', 'lineEnd'],
|
||||||
},
|
},
|
||||||
@ -203,7 +191,7 @@ show(mySketch)
|
|||||||
to: [1, 1],
|
to: [1, 1],
|
||||||
from: [0, 1],
|
from: [0, 1],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
sourceRange: [60, 71],
|
sourceRange: [106, 122],
|
||||||
pathToNode: [],
|
pathToNode: [],
|
||||||
geos: ['line', 'lineEnd'],
|
geos: ['line', 'lineEnd'],
|
||||||
},
|
},
|
||||||
@ -212,14 +200,8 @@ show(mySketch)
|
|||||||
position: [0, 0, 0],
|
position: [0, 0, 0],
|
||||||
rotation: [0.7071067811865475, 0, 0, 0.7071067811865476],
|
rotation: [0.7071067811865475, 0, 0, 0.7071067811865476],
|
||||||
__meta: [
|
__meta: [
|
||||||
{
|
{ sourceRange: [14, 34], pathToNode: [] },
|
||||||
sourceRange: [13, 73],
|
{ sourceRange: [128, 137], pathToNode: [] },
|
||||||
pathToNode: ['body', 0, 'declarations', 0, 'init', 0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sourceRange: [77, 86],
|
|
||||||
pathToNode: [],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -7,13 +7,10 @@ import {
|
|||||||
MemberExpression,
|
MemberExpression,
|
||||||
Identifier,
|
Identifier,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
|
ArrayExpression,
|
||||||
} from './abstractSyntaxTree'
|
} from './abstractSyntaxTree'
|
||||||
import {
|
import { InternalFnNames } from './std/stdTypes'
|
||||||
sketchFns,
|
import { internalFns } from './std/std'
|
||||||
internalFns,
|
|
||||||
InternalFnNames,
|
|
||||||
SketchFnNames,
|
|
||||||
} from './sketch'
|
|
||||||
import { BufferGeometry } from 'three'
|
import { BufferGeometry } from 'three'
|
||||||
|
|
||||||
export type SourceRange = [number, number]
|
export type SourceRange = [number, number]
|
||||||
@ -68,6 +65,7 @@ export type Path = ToPoint | HorizontalLineTo | AngledLineTo
|
|||||||
export interface SketchGroup {
|
export interface SketchGroup {
|
||||||
type: 'sketchGroup'
|
type: 'sketchGroup'
|
||||||
value: Path[]
|
value: Path[]
|
||||||
|
start?: Path['from']
|
||||||
position: Position
|
position: Position
|
||||||
rotation: Rotation
|
rotation: Rotation
|
||||||
__meta: Metadata[]
|
__meta: Metadata[]
|
||||||
@ -220,25 +218,6 @@ export const executor = (
|
|||||||
value: executeObjectExpression(_programMemory, declaration.init),
|
value: executeObjectExpression(_programMemory, declaration.init),
|
||||||
__meta,
|
__meta,
|
||||||
}
|
}
|
||||||
} else if (declaration.init.type === 'SketchExpression') {
|
|
||||||
const sketchInit = declaration.init
|
|
||||||
const fnMemory: ProgramMemory = {
|
|
||||||
root: {
|
|
||||||
..._programMemory.root,
|
|
||||||
},
|
|
||||||
_sketch: [],
|
|
||||||
}
|
|
||||||
let { _sketch } = executor(sketchInit.body, fnMemory, {
|
|
||||||
bodyType: 'sketch',
|
|
||||||
})
|
|
||||||
const newSketch: SketchGroup = {
|
|
||||||
type: 'sketchGroup',
|
|
||||||
value: _sketch || [],
|
|
||||||
position: [0, 0, 0],
|
|
||||||
rotation: [0, 0, 0, 1], //x,y,z,w
|
|
||||||
__meta,
|
|
||||||
}
|
|
||||||
_programMemory.root[variableName] = newSketch
|
|
||||||
} else if (declaration.init.type === 'FunctionExpression') {
|
} else if (declaration.init.type === 'FunctionExpression') {
|
||||||
const fnInit = declaration.init
|
const fnInit = declaration.init
|
||||||
|
|
||||||
@ -287,33 +266,14 @@ export const executor = (
|
|||||||
return _programMemory.root[arg.name].value
|
return _programMemory.root[arg.name].value
|
||||||
} else if (arg.type === 'ObjectExpression') {
|
} else if (arg.type === 'ObjectExpression') {
|
||||||
return executeObjectExpression(_programMemory, arg)
|
return executeObjectExpression(_programMemory, arg)
|
||||||
|
} else if (arg.type === 'ArrayExpression') {
|
||||||
|
return executeArrayExpression(_programMemory, arg)
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected argument type ${arg.type} in function call`
|
`Unexpected argument type ${arg.type} in function call`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
if (functionName in sketchFns) {
|
if (functionName in internalFns) {
|
||||||
const sketchFnName = functionName as SketchFnNames
|
|
||||||
if (options.bodyType !== 'sketch') {
|
|
||||||
throw new Error(
|
|
||||||
`Cannot call ${functionName} outside of a sketch declaration`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const result = sketchFns[sketchFnName](
|
|
||||||
{
|
|
||||||
programMemory: _programMemory,
|
|
||||||
name: variableName,
|
|
||||||
sourceRange: [declaration.start, declaration.end],
|
|
||||||
},
|
|
||||||
...fnArgs
|
|
||||||
)
|
|
||||||
_programMemory._sketch = result.programMemory._sketch
|
|
||||||
_programMemory.root[variableName] = {
|
|
||||||
type: 'userVal',
|
|
||||||
value: result.currentPath,
|
|
||||||
__meta,
|
|
||||||
}
|
|
||||||
} else if (functionName in internalFns) {
|
|
||||||
const result = executeCallExpression(
|
const result = executeCallExpression(
|
||||||
_programMemory,
|
_programMemory,
|
||||||
declaration.init,
|
declaration.init,
|
||||||
@ -362,22 +322,7 @@ export const executor = (
|
|||||||
return _programMemory.root[arg.name].value
|
return _programMemory.root[arg.name].value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (functionName in sketchFns) {
|
if ('show' === functionName) {
|
||||||
if (options.bodyType !== 'sketch') {
|
|
||||||
throw new Error(
|
|
||||||
`Cannot call ${functionName} outside of a sketch declaration`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const sketchFnName = functionName as SketchFnNames
|
|
||||||
const result = sketchFns[sketchFnName](
|
|
||||||
{
|
|
||||||
programMemory: _programMemory,
|
|
||||||
sourceRange: [statement.start, statement.end],
|
|
||||||
},
|
|
||||||
...args
|
|
||||||
)
|
|
||||||
_programMemory._sketch = [...(result.programMemory._sketch || [])]
|
|
||||||
} else if ('show' === functionName) {
|
|
||||||
if (options.bodyType !== 'root') {
|
if (options.bodyType !== 'root') {
|
||||||
throw new Error(`Cannot call ${functionName} outside of a root`)
|
throw new Error(`Cannot call ${functionName} outside of a root`)
|
||||||
}
|
}
|
||||||
@ -482,36 +427,6 @@ function executePipeBody(
|
|||||||
body,
|
body,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else if (expression.type === 'SketchExpression') {
|
|
||||||
const sketchBody = expression.body
|
|
||||||
const fnMemory: ProgramMemory = {
|
|
||||||
root: {
|
|
||||||
...programMemory.root,
|
|
||||||
},
|
|
||||||
_sketch: [],
|
|
||||||
}
|
|
||||||
let { _sketch } = executor(sketchBody, fnMemory, {
|
|
||||||
bodyType: 'sketch',
|
|
||||||
})
|
|
||||||
const newSketch: SketchGroup = {
|
|
||||||
type: 'sketchGroup',
|
|
||||||
value: _sketch || [],
|
|
||||||
position: [0, 0, 0],
|
|
||||||
rotation: [0, 0, 0, 1], //x,y,z,w
|
|
||||||
__meta: [
|
|
||||||
{
|
|
||||||
sourceRange: [expression.start, expression.end],
|
|
||||||
pathToNode: [...previousPathToNode, expressionIndex],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
return executePipeBody(
|
|
||||||
body,
|
|
||||||
programMemory,
|
|
||||||
previousPathToNode,
|
|
||||||
expressionIndex + 1,
|
|
||||||
[...previousResults, newSketch]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Invalid pipe expression')
|
throw new Error('Invalid pipe expression')
|
||||||
@ -544,18 +459,8 @@ function executeObjectExpression(
|
|||||||
property.value
|
property.value
|
||||||
)
|
)
|
||||||
} else if (property.value.type === 'ArrayExpression') {
|
} else if (property.value.type === 'ArrayExpression') {
|
||||||
obj[property.key.name] = property.value.elements.map((el) => {
|
const result = executeArrayExpression(_programMemory, property.value)
|
||||||
if (el.type === 'Literal') {
|
obj[property.key.name] = result
|
||||||
return el.value
|
|
||||||
} else if (el.type === 'Identifier') {
|
|
||||||
return _programMemory.root[el.name].value
|
|
||||||
} else if (el.type === 'BinaryExpression') {
|
|
||||||
return getBinaryExpressionResult(el, _programMemory)
|
|
||||||
} else if (el.type === 'ObjectExpression') {
|
|
||||||
return executeObjectExpression(_programMemory, el)
|
|
||||||
}
|
|
||||||
throw new Error('Invalid argument type')
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected property type ${property.value.type} in object expression`
|
`Unexpected property type ${property.value.type} in object expression`
|
||||||
@ -570,6 +475,24 @@ function executeObjectExpression(
|
|||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function executeArrayExpression(
|
||||||
|
_programMemory: ProgramMemory,
|
||||||
|
arrExp: ArrayExpression
|
||||||
|
) {
|
||||||
|
return arrExp.elements.map((el) => {
|
||||||
|
if (el.type === 'Literal') {
|
||||||
|
return el.value
|
||||||
|
} else if (el.type === 'Identifier') {
|
||||||
|
return _programMemory.root?.[el.name]?.value
|
||||||
|
} else if (el.type === 'BinaryExpression') {
|
||||||
|
return getBinaryExpressionResult(el, _programMemory)
|
||||||
|
} else if (el.type === 'ObjectExpression') {
|
||||||
|
return executeObjectExpression(_programMemory, el)
|
||||||
|
}
|
||||||
|
throw new Error('Invalid argument type')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function executeCallExpression(
|
function executeCallExpression(
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
expression: CallExpression,
|
expression: CallExpression,
|
||||||
@ -604,16 +527,7 @@ function executeCallExpression(
|
|||||||
} else if (arg.type === 'PipeSubstitution') {
|
} else if (arg.type === 'PipeSubstitution') {
|
||||||
return previousResults[expressionIndex - 1]
|
return previousResults[expressionIndex - 1]
|
||||||
} else if (arg.type === 'ArrayExpression') {
|
} else if (arg.type === 'ArrayExpression') {
|
||||||
return arg.elements.map((el) => {
|
return executeArrayExpression(programMemory, arg)
|
||||||
if (el.type === 'Literal') {
|
|
||||||
return el.value
|
|
||||||
} else if (el.type === 'Identifier') {
|
|
||||||
return programMemory.root[el.name]
|
|
||||||
} else if (el.type === 'BinaryExpression') {
|
|
||||||
return getBinaryExpressionResult(el, programMemory)
|
|
||||||
}
|
|
||||||
throw new Error('Invalid argument type')
|
|
||||||
})
|
|
||||||
} else if (arg.type === 'CallExpression') {
|
} else if (arg.type === 'CallExpression') {
|
||||||
const result: any = executeCallExpression(
|
const result: any = executeCallExpression(
|
||||||
programMemory,
|
programMemory,
|
||||||
@ -621,21 +535,12 @@ function executeCallExpression(
|
|||||||
previousPathToNode
|
previousPathToNode
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
} else if (arg.type === 'ObjectExpression') {
|
||||||
|
return executeObjectExpression(programMemory, arg)
|
||||||
}
|
}
|
||||||
throw new Error('Invalid argument type')
|
throw new Error('Invalid argument type in function call')
|
||||||
})
|
})
|
||||||
if (
|
if (functionName in internalFns) {
|
||||||
functionName in internalFns &&
|
|
||||||
[
|
|
||||||
'rx',
|
|
||||||
'ry',
|
|
||||||
'rz',
|
|
||||||
'translate',
|
|
||||||
'transform',
|
|
||||||
'extrude',
|
|
||||||
'getExtrudeWallTransform',
|
|
||||||
].includes(functionName)
|
|
||||||
) {
|
|
||||||
const fnNameWithSketchOrExtrude = functionName as InternalFnNames
|
const fnNameWithSketchOrExtrude = functionName as InternalFnNames
|
||||||
const result = internalFns[fnNameWithSketchOrExtrude](
|
const result = internalFns[fnNameWithSketchOrExtrude](
|
||||||
{
|
{
|
||||||
@ -655,25 +560,6 @@ function executeCallExpression(
|
|||||||
)
|
)
|
||||||
: result
|
: result
|
||||||
}
|
}
|
||||||
if (functionName in sketchFns) {
|
|
||||||
const sketchFnName = functionName as SketchFnNames
|
|
||||||
const result = sketchFns[sketchFnName](
|
|
||||||
{
|
|
||||||
programMemory,
|
|
||||||
sourceRange: sourceRangeOverride || [expression.start, expression.end],
|
|
||||||
},
|
|
||||||
...fnArgs
|
|
||||||
)
|
|
||||||
return isInPipe
|
|
||||||
? executePipeBody(
|
|
||||||
body,
|
|
||||||
programMemory,
|
|
||||||
previousPathToNode,
|
|
||||||
expressionIndex + 1,
|
|
||||||
[...previousResults, result]
|
|
||||||
)
|
|
||||||
: result
|
|
||||||
}
|
|
||||||
const result = programMemory.root[functionName].value(...fnArgs)
|
const result = programMemory.root[functionName].value(...fnArgs)
|
||||||
return isInPipe
|
return isInPipe
|
||||||
? executePipeBody(
|
? executePipeBody(
|
||||||
|
@ -6,13 +6,12 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', () => {
|
it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', () => {
|
||||||
const code = `
|
const code = `
|
||||||
const myVar = 5
|
const myVar = 5
|
||||||
sketch sk3 {
|
const sk3 = startSketchAt([0, 0])
|
||||||
lineTo(1, 2)
|
|> lineTo([1, 2], %)
|
||||||
path yo = lineTo(3, 4)
|
|> lineTo({ to: [3, 4], tag: 'yo' }, %)
|
||||||
close()
|
|> close(%)
|
||||||
}
|
|
||||||
`
|
`
|
||||||
const subStr = 'lineTo(3, 4)'
|
const subStr = "lineTo({ to: [3, 4], tag: 'yo' }, %)"
|
||||||
const lineToSubstringIndex = code.indexOf(subStr)
|
const lineToSubstringIndex = code.indexOf(subStr)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number] = [
|
||||||
lineToSubstringIndex,
|
lineToSubstringIndex,
|
||||||
|
113
src/lang/modifyAst.test.ts
Normal file
113
src/lang/modifyAst.test.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import {
|
||||||
|
createLiteral,
|
||||||
|
createIdentifier,
|
||||||
|
createCallExpression,
|
||||||
|
createObjectExpression,
|
||||||
|
createArrayExpression,
|
||||||
|
createPipeSubstitution,
|
||||||
|
createVariableDeclaration,
|
||||||
|
createPipeExpression,
|
||||||
|
findUniqueName,
|
||||||
|
addSketchTo,
|
||||||
|
} from './modifyAst'
|
||||||
|
import { recast } from './recast'
|
||||||
|
|
||||||
|
describe('Testing createLiteral', () => {
|
||||||
|
it('should create a literal', () => {
|
||||||
|
const result = createLiteral(5)
|
||||||
|
expect(result.type).toBe('Literal')
|
||||||
|
expect(result.value).toBe(5)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Testing createIdentifier', () => {
|
||||||
|
it('should create an identifier', () => {
|
||||||
|
const result = createIdentifier('myVar')
|
||||||
|
expect(result.type).toBe('Identifier')
|
||||||
|
expect(result.name).toBe('myVar')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Testing createCallExpression', () => {
|
||||||
|
it('should create a call expression', () => {
|
||||||
|
const result = createCallExpression('myFunc', [createLiteral(5)])
|
||||||
|
expect(result.type).toBe('CallExpression')
|
||||||
|
expect(result.callee.type).toBe('Identifier')
|
||||||
|
expect(result.callee.name).toBe('myFunc')
|
||||||
|
expect(result.arguments[0].type).toBe('Literal')
|
||||||
|
expect((result.arguments[0] as any).value).toBe(5)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Testing createObjectExpression', () => {
|
||||||
|
it('should create an object expression', () => {
|
||||||
|
const result = createObjectExpression({
|
||||||
|
myProp: createLiteral(5),
|
||||||
|
})
|
||||||
|
expect(result.type).toBe('ObjectExpression')
|
||||||
|
expect(result.properties[0].type).toBe('ObjectProperty')
|
||||||
|
expect(result.properties[0].key.name).toBe('myProp')
|
||||||
|
expect(result.properties[0].value.type).toBe('Literal')
|
||||||
|
expect((result.properties[0].value as any).value).toBe(5)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Testing createArrayExpression', () => {
|
||||||
|
it('should create an array expression', () => {
|
||||||
|
const result = createArrayExpression([createLiteral(5)])
|
||||||
|
expect(result.type).toBe('ArrayExpression')
|
||||||
|
expect(result.elements[0].type).toBe('Literal')
|
||||||
|
expect((result.elements[0] as any).value).toBe(5)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Testing createPipeSubstitution', () => {
|
||||||
|
it('should create a pipe substitution', () => {
|
||||||
|
const result = createPipeSubstitution()
|
||||||
|
expect(result.type).toBe('PipeSubstitution')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Testing createVariableDeclaration', () => {
|
||||||
|
it('should create a variable declaration', () => {
|
||||||
|
const result = createVariableDeclaration('myVar', createLiteral(5))
|
||||||
|
expect(result.type).toBe('VariableDeclaration')
|
||||||
|
expect(result.declarations[0].type).toBe('VariableDeclarator')
|
||||||
|
expect(result.declarations[0].id.type).toBe('Identifier')
|
||||||
|
expect(result.declarations[0].id.name).toBe('myVar')
|
||||||
|
expect(result.declarations[0].init.type).toBe('Literal')
|
||||||
|
expect((result.declarations[0].init as any).value).toBe(5)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Testing createPipeExpression', () => {
|
||||||
|
it('should create a pipe expression', () => {
|
||||||
|
const result = createPipeExpression([createLiteral(5)])
|
||||||
|
expect(result.type).toBe('PipeExpression')
|
||||||
|
expect(result.body[0].type).toBe('Literal')
|
||||||
|
expect((result.body[0] as any).value).toBe(5)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing findUniqueName', () => {
|
||||||
|
it('should find a unique name', () => {
|
||||||
|
const result = findUniqueName(
|
||||||
|
'yo01 yo02 yo03 yo04 yo05 yo06 yo07 yo08 yo09',
|
||||||
|
'yo',
|
||||||
|
2
|
||||||
|
)
|
||||||
|
expect(result).toBe('yo10')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Testing addSketchTo', () => {
|
||||||
|
it('should add a sketch to a program', () => {
|
||||||
|
const result = addSketchTo(
|
||||||
|
{
|
||||||
|
type: 'Program',
|
||||||
|
body: [],
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
nonCodeMeta: {},
|
||||||
|
},
|
||||||
|
'yz'
|
||||||
|
)
|
||||||
|
const str = recast(result.modifiedAst)
|
||||||
|
expect(str).toBe(`const part001 = startSketchAt([0, 0])
|
||||||
|
|> ry(90, %)
|
||||||
|
|> lineTo([1, 1], %)
|
||||||
|
show(part001)`)
|
||||||
|
})
|
||||||
|
})
|
@ -1,16 +1,20 @@
|
|||||||
import {
|
import {
|
||||||
Program,
|
Program,
|
||||||
BlockStatement,
|
|
||||||
SketchExpression,
|
|
||||||
CallExpression,
|
CallExpression,
|
||||||
PipeExpression,
|
PipeExpression,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
|
VariableDeclarator,
|
||||||
ExpressionStatement,
|
ExpressionStatement,
|
||||||
Value,
|
Value,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
VariableDeclarator,
|
Literal,
|
||||||
|
PipeSubstitution,
|
||||||
|
Identifier,
|
||||||
|
ArrayExpression,
|
||||||
|
ObjectExpression,
|
||||||
} from './abstractSyntaxTree'
|
} from './abstractSyntaxTree'
|
||||||
import { PathToNode } from './executor'
|
import { PathToNode, ProgramMemory } from './executor'
|
||||||
|
import { addTagForSketchOnFace } from './std/sketch'
|
||||||
|
|
||||||
export function addSketchTo(
|
export function addSketchTo(
|
||||||
node: Program,
|
node: Program,
|
||||||
@ -20,73 +24,37 @@ export function addSketchTo(
|
|||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const dumbyStartend = { start: 0, end: 0 }
|
const dumbyStartend = { start: 0, end: 0 }
|
||||||
const _name = name || findUniqueName(node, 'part')
|
const _name = name || findUniqueName(node, 'part')
|
||||||
const sketchBody: BlockStatement = {
|
|
||||||
type: 'BlockStatement',
|
|
||||||
...dumbyStartend,
|
|
||||||
body: [],
|
|
||||||
nonCodeMeta: {},
|
|
||||||
}
|
|
||||||
const sketch: SketchExpression = {
|
|
||||||
type: 'SketchExpression',
|
|
||||||
...dumbyStartend,
|
|
||||||
body: sketchBody,
|
|
||||||
}
|
|
||||||
|
|
||||||
const rotate: CallExpression = {
|
const startSketchAt = createCallExpression('startSketchAt', [
|
||||||
type: 'CallExpression',
|
createArrayExpression([createLiteral(0), createLiteral(0)]),
|
||||||
...dumbyStartend,
|
])
|
||||||
callee: {
|
const rotate = createCallExpression(axis === 'xz' ? 'rx' : 'ry', [
|
||||||
type: 'Identifier',
|
createLiteral(90),
|
||||||
...dumbyStartend,
|
createPipeSubstitution(),
|
||||||
name: axis === 'xz' ? 'rx' : 'ry',
|
])
|
||||||
},
|
const initialLineTo = createCallExpression('lineTo', [
|
||||||
arguments: [
|
createArrayExpression([createLiteral(1), createLiteral(1)]),
|
||||||
{
|
createPipeSubstitution(),
|
||||||
type: 'Literal',
|
])
|
||||||
...dumbyStartend,
|
|
||||||
value: axis === 'yz' ? 90 : 90,
|
|
||||||
raw: axis === 'yz' ? '90' : '90',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'PipeSubstitution',
|
|
||||||
...dumbyStartend,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
optional: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
const pipChain: PipeExpression = {
|
const pipeBody =
|
||||||
type: 'PipeExpression',
|
axis !== 'xy'
|
||||||
nonCodeMeta: {},
|
? [startSketchAt, rotate, initialLineTo]
|
||||||
...dumbyStartend,
|
: [startSketchAt, initialLineTo]
|
||||||
body: [sketch, rotate],
|
|
||||||
}
|
const variableDeclaration = createVariableDeclaration(
|
||||||
|
_name,
|
||||||
|
createPipeExpression(pipeBody)
|
||||||
|
)
|
||||||
|
|
||||||
const sketchVariableDeclaration: VariableDeclaration = {
|
|
||||||
type: 'VariableDeclaration',
|
|
||||||
...dumbyStartend,
|
|
||||||
kind: 'sketch',
|
|
||||||
declarations: [
|
|
||||||
{
|
|
||||||
type: 'VariableDeclarator',
|
|
||||||
...dumbyStartend,
|
|
||||||
id: {
|
|
||||||
type: 'Identifier',
|
|
||||||
...dumbyStartend,
|
|
||||||
name: _name,
|
|
||||||
},
|
|
||||||
init: axis === 'xy' ? sketch : pipChain,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
const showCallIndex = getShowIndex(_node)
|
const showCallIndex = getShowIndex(_node)
|
||||||
let sketchIndex = showCallIndex
|
let sketchIndex = showCallIndex
|
||||||
if (showCallIndex === -1) {
|
if (showCallIndex === -1) {
|
||||||
_node.body = [...node.body, sketchVariableDeclaration]
|
_node.body = [...node.body, variableDeclaration]
|
||||||
sketchIndex = _node.body.length - 1
|
sketchIndex = _node.body.length - 1
|
||||||
} else {
|
} else {
|
||||||
const newBody = [...node.body]
|
const newBody = [...node.body]
|
||||||
newBody.splice(showCallIndex, 0, sketchVariableDeclaration)
|
newBody.splice(showCallIndex, 0, variableDeclaration)
|
||||||
_node.body = newBody
|
_node.body = newBody
|
||||||
}
|
}
|
||||||
let pathToNode: (string | number)[] = [
|
let pathToNode: (string | number)[] = [
|
||||||
@ -107,7 +75,7 @@ export function addSketchTo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function findUniqueName(
|
export function findUniqueName(
|
||||||
ast: Program | string,
|
ast: Program | string,
|
||||||
name: string,
|
name: string,
|
||||||
pad = 3,
|
pad = 3,
|
||||||
@ -197,93 +165,58 @@ function getShowIndex(node: Program): number {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addLine(
|
export function mutateArrExp(
|
||||||
node: Program,
|
node: Value,
|
||||||
pathToNode: (string | number)[],
|
updateWith: ArrayExpression
|
||||||
to: [number, number]
|
): boolean {
|
||||||
): { modifiedAst: Program; pathToNode: (string | number)[] } {
|
if (node.type === 'ArrayExpression') {
|
||||||
const _node = { ...node }
|
node.elements.forEach((element, i) => {
|
||||||
const dumbyStartend = { start: 0, end: 0 }
|
if (element.type === 'Literal') {
|
||||||
const { node: sketchExpression } = getNodeFromPath<SketchExpression>(
|
node.elements[i] = updateWith.elements[i]
|
||||||
_node,
|
|
||||||
pathToNode,
|
|
||||||
'SketchExpression'
|
|
||||||
)
|
|
||||||
const line: ExpressionStatement = {
|
|
||||||
type: 'ExpressionStatement',
|
|
||||||
...dumbyStartend,
|
|
||||||
expression: {
|
|
||||||
type: 'CallExpression',
|
|
||||||
...dumbyStartend,
|
|
||||||
callee: {
|
|
||||||
type: 'Identifier',
|
|
||||||
...dumbyStartend,
|
|
||||||
name: 'lineTo',
|
|
||||||
},
|
|
||||||
optional: false,
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
...dumbyStartend,
|
|
||||||
value: to[0],
|
|
||||||
raw: `${to[0]}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
...dumbyStartend,
|
|
||||||
value: to[1],
|
|
||||||
raw: `${to[1]}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
const newBody = [...sketchExpression.body.body, line]
|
})
|
||||||
sketchExpression.body.body = newBody
|
return true
|
||||||
return {
|
|
||||||
modifiedAst: _node,
|
|
||||||
pathToNode,
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export function changeArguments(
|
export function mutateObjExpProp(
|
||||||
node: Program,
|
node: Value,
|
||||||
pathToNode: (string | number)[],
|
updateWith: Literal | ArrayExpression,
|
||||||
args: [number, number]
|
key: string
|
||||||
): { modifiedAst: Program; pathToNode: (string | number)[] } {
|
): boolean {
|
||||||
const _node = { ...node }
|
if (node.type === 'ObjectExpression') {
|
||||||
const dumbyStartend = { start: 0, end: 0 }
|
const keyIndex = node.properties.findIndex((a) => a.key.name === key)
|
||||||
// const thePath = getNodePathFromSourceRange(_node, sourceRange)
|
if (keyIndex !== -1) {
|
||||||
const { node: callExpression } = getNodeFromPath<CallExpression>(
|
if (
|
||||||
_node,
|
updateWith.type === 'Literal' &&
|
||||||
pathToNode
|
node.properties[keyIndex].value.type === 'Literal'
|
||||||
)
|
) {
|
||||||
const newXArg: CallExpression['arguments'][number] =
|
node.properties[keyIndex].value = updateWith
|
||||||
callExpression.arguments[0].type === 'Literal'
|
return true
|
||||||
? {
|
} else if (
|
||||||
type: 'Literal',
|
node.properties[keyIndex].value.type === 'ArrayExpression' &&
|
||||||
...dumbyStartend,
|
updateWith.type === 'ArrayExpression'
|
||||||
value: args[0],
|
) {
|
||||||
raw: `${args[0]}`,
|
const arrExp = node.properties[keyIndex].value as ArrayExpression
|
||||||
|
arrExp.elements.forEach((element, i) => {
|
||||||
|
if (element.type === 'Literal') {
|
||||||
|
arrExp.elements[i] = updateWith.elements[i]
|
||||||
}
|
}
|
||||||
: {
|
})
|
||||||
...callExpression.arguments[0],
|
|
||||||
}
|
}
|
||||||
const newYArg: CallExpression['arguments'][number] =
|
return true
|
||||||
callExpression.arguments[1].type === 'Literal'
|
} else {
|
||||||
? {
|
node.properties.push({
|
||||||
type: 'Literal',
|
type: 'ObjectProperty',
|
||||||
...dumbyStartend,
|
key: createIdentifier(key),
|
||||||
value: args[1],
|
value: updateWith,
|
||||||
raw: `${args[1]}`,
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
: {
|
|
||||||
...callExpression.arguments[1],
|
|
||||||
}
|
|
||||||
callExpression.arguments = [newXArg, newYArg]
|
|
||||||
return {
|
|
||||||
modifiedAst: _node,
|
|
||||||
pathToNode,
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extrudeSketch(
|
export function extrudeSketch(
|
||||||
@ -297,10 +230,10 @@ export function extrudeSketch(
|
|||||||
} {
|
} {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const dumbyStartend = { start: 0, end: 0 }
|
const dumbyStartend = { start: 0, end: 0 }
|
||||||
const { node: sketchExpression } = getNodeFromPath<SketchExpression>(
|
const { node: sketchExpression } = getNodeFromPath(
|
||||||
_node,
|
_node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'SketchExpression'
|
'SketchExpression' // TODO fix this #25
|
||||||
)
|
)
|
||||||
|
|
||||||
// determine if sketchExpression is in a pipeExpression or not
|
// determine if sketchExpression is in a pipeExpression or not
|
||||||
@ -324,17 +257,9 @@ export function extrudeSketch(
|
|||||||
},
|
},
|
||||||
optional: false,
|
optional: false,
|
||||||
arguments: [
|
arguments: [
|
||||||
{
|
createLiteral(4),
|
||||||
type: 'Literal',
|
|
||||||
...dumbyStartend,
|
|
||||||
value: 4,
|
|
||||||
raw: '4',
|
|
||||||
},
|
|
||||||
shouldPipe
|
shouldPipe
|
||||||
? {
|
? createPipeSubstitution()
|
||||||
type: 'PipeSubstitution',
|
|
||||||
...dumbyStartend,
|
|
||||||
}
|
|
||||||
: {
|
: {
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
...dumbyStartend,
|
...dumbyStartend,
|
||||||
@ -354,7 +279,7 @@ export function extrudeSketch(
|
|||||||
type: 'PipeExpression',
|
type: 'PipeExpression',
|
||||||
nonCodeMeta: {},
|
nonCodeMeta: {},
|
||||||
...dumbyStartend,
|
...dumbyStartend,
|
||||||
body: [sketchExpression, extrudeCall],
|
body: [sketchExpression as any, extrudeCall], // TODO fix this #25
|
||||||
}
|
}
|
||||||
|
|
||||||
variableDeclorator.init = pipeChain
|
variableDeclorator.init = pipeChain
|
||||||
@ -411,137 +336,61 @@ export function extrudeSketch(
|
|||||||
|
|
||||||
export function sketchOnExtrudedFace(
|
export function sketchOnExtrudedFace(
|
||||||
node: Program,
|
node: Program,
|
||||||
pathToNode: (string | number)[]
|
pathToNode: (string | number)[],
|
||||||
|
programMemory: ProgramMemory
|
||||||
): { modifiedAst: Program; pathToNode: (string | number)[] } {
|
): { modifiedAst: Program; pathToNode: (string | number)[] } {
|
||||||
const _node = { ...node }
|
let _node = { ...node }
|
||||||
const dumbyStartend = { start: 0, end: 0 }
|
|
||||||
const newSketchName = findUniqueName(node, 'part')
|
const newSketchName = findUniqueName(node, 'part')
|
||||||
const oldSketchName = getNodeFromPath<VariableDeclarator>(
|
const { node: oldSketchNode, path: pathToOldSketch } =
|
||||||
|
getNodeFromPath<VariableDeclarator>(
|
||||||
_node,
|
_node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'VariableDeclarator',
|
'VariableDeclarator',
|
||||||
true
|
true
|
||||||
).node.id.name
|
)
|
||||||
const { node: expression } = getNodeFromPath<
|
const oldSketchName = oldSketchNode.id.name
|
||||||
VariableDeclarator | CallExpression
|
const { node: expression } = getNodeFromPath<CallExpression>(
|
||||||
>(_node, pathToNode, 'CallExpression')
|
|
||||||
|
|
||||||
const pathName =
|
|
||||||
expression.type === 'VariableDeclarator'
|
|
||||||
? expression.id.name
|
|
||||||
: findUniqueName(node, 'path', 2)
|
|
||||||
|
|
||||||
if (expression.type === 'CallExpression') {
|
|
||||||
const { node: block } = getNodeFromPath<BlockStatement>(
|
|
||||||
_node,
|
_node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'BlockStatement'
|
'CallExpression'
|
||||||
)
|
)
|
||||||
const expressionIndex = getLastIndex(pathToNode)
|
|
||||||
if (expression.callee.name !== 'lineTo')
|
|
||||||
throw new Error('expected a lineTo call')
|
|
||||||
const newExpression: VariableDeclaration = {
|
|
||||||
type: 'VariableDeclaration',
|
|
||||||
...dumbyStartend,
|
|
||||||
declarations: [
|
|
||||||
{
|
|
||||||
type: 'VariableDeclarator',
|
|
||||||
...dumbyStartend,
|
|
||||||
id: {
|
|
||||||
type: 'Identifier',
|
|
||||||
...dumbyStartend,
|
|
||||||
name: pathName,
|
|
||||||
},
|
|
||||||
init: expression,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
kind: 'path',
|
|
||||||
}
|
|
||||||
|
|
||||||
block.body.splice(expressionIndex, 1, newExpression)
|
const { modifiedAst, tag } = addTagForSketchOnFace(
|
||||||
}
|
{
|
||||||
|
previousProgramMemory: programMemory,
|
||||||
|
pathToNode,
|
||||||
|
node: _node,
|
||||||
|
},
|
||||||
|
expression.callee.name
|
||||||
|
)
|
||||||
|
_node = modifiedAst
|
||||||
|
|
||||||
// create pipe expression with a sketch block piped into a transform function
|
const newSketch = createVariableDeclaration(
|
||||||
const sketchPipe: PipeExpression = {
|
newSketchName,
|
||||||
type: 'PipeExpression',
|
createPipeExpression([
|
||||||
nonCodeMeta: {},
|
createCallExpression('startSketchAt', [
|
||||||
...dumbyStartend,
|
createArrayExpression([createLiteral(0), createLiteral(0)]),
|
||||||
body: [
|
]),
|
||||||
{
|
createCallExpression('lineTo', [
|
||||||
type: 'SketchExpression',
|
createArrayExpression([createLiteral(1), createLiteral(1)]),
|
||||||
...dumbyStartend,
|
createPipeSubstitution(),
|
||||||
body: {
|
]),
|
||||||
type: 'BlockStatement',
|
createCallExpression('transform', [
|
||||||
...dumbyStartend,
|
createCallExpression('getExtrudeWallTransform', [
|
||||||
body: [],
|
createLiteral(tag),
|
||||||
nonCodeMeta: {},
|
createIdentifier(oldSketchName),
|
||||||
},
|
]),
|
||||||
},
|
createPipeSubstitution(),
|
||||||
{
|
]),
|
||||||
type: 'CallExpression',
|
]),
|
||||||
...dumbyStartend,
|
'const'
|
||||||
callee: {
|
)
|
||||||
type: 'Identifier',
|
const expressionIndex = getLastIndex(pathToOldSketch)
|
||||||
...dumbyStartend,
|
_node.body.splice(expressionIndex + 1, 0, newSketch)
|
||||||
name: 'transform',
|
|
||||||
},
|
|
||||||
optional: false,
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
type: 'CallExpression',
|
|
||||||
...dumbyStartend,
|
|
||||||
callee: {
|
|
||||||
type: 'Identifier',
|
|
||||||
...dumbyStartend,
|
|
||||||
name: 'getExtrudeWallTransform',
|
|
||||||
},
|
|
||||||
optional: false,
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
type: 'Literal',
|
|
||||||
...dumbyStartend,
|
|
||||||
value: pathName,
|
|
||||||
raw: `'${pathName}'`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Identifier',
|
|
||||||
...dumbyStartend,
|
|
||||||
name: oldSketchName,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'PipeSubstitution',
|
|
||||||
...dumbyStartend,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
const variableDec: VariableDeclaration = {
|
|
||||||
type: 'VariableDeclaration',
|
|
||||||
...dumbyStartend,
|
|
||||||
declarations: [
|
|
||||||
{
|
|
||||||
type: 'VariableDeclarator',
|
|
||||||
...dumbyStartend,
|
|
||||||
id: {
|
|
||||||
type: 'Identifier',
|
|
||||||
...dumbyStartend,
|
|
||||||
name: newSketchName,
|
|
||||||
},
|
|
||||||
init: sketchPipe,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
kind: 'sketch',
|
|
||||||
}
|
|
||||||
|
|
||||||
const showIndex = getShowIndex(_node)
|
|
||||||
_node.body.splice(showIndex, 0, variableDec)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modifiedAst: addToShow(_node, newSketchName),
|
modifiedAst: addToShow(_node, newSketchName),
|
||||||
pathToNode,
|
pathToNode: [...pathToNode.slice(0, -1), expressionIndex],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,3 +401,111 @@ const getLastIndex = (pathToNode: PathToNode): number => {
|
|||||||
}
|
}
|
||||||
return getLastIndex(pathToNode.slice(0, -1))
|
return getLastIndex(pathToNode.slice(0, -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createLiteral(value: string | number): Literal {
|
||||||
|
return {
|
||||||
|
type: 'Literal',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
value,
|
||||||
|
raw: `${value}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createIdentifier(name: string): Identifier {
|
||||||
|
return {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPipeSubstitution(): PipeSubstitution {
|
||||||
|
return {
|
||||||
|
type: 'PipeSubstitution',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCallExpression(
|
||||||
|
name: string,
|
||||||
|
args: CallExpression['arguments']
|
||||||
|
): CallExpression {
|
||||||
|
return {
|
||||||
|
type: 'CallExpression',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
callee: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
optional: false,
|
||||||
|
arguments: args,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createArrayExpression(
|
||||||
|
elements: ArrayExpression['elements']
|
||||||
|
): ArrayExpression {
|
||||||
|
return {
|
||||||
|
type: 'ArrayExpression',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
elements,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPipeExpression(
|
||||||
|
body: PipeExpression['body']
|
||||||
|
): PipeExpression {
|
||||||
|
return {
|
||||||
|
type: 'PipeExpression',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
body,
|
||||||
|
nonCodeMeta: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createVariableDeclaration(
|
||||||
|
varName: string,
|
||||||
|
init: VariableDeclarator['init'],
|
||||||
|
kind: VariableDeclaration['kind'] = 'const'
|
||||||
|
): VariableDeclaration {
|
||||||
|
return {
|
||||||
|
type: 'VariableDeclaration',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
declarations: [
|
||||||
|
{
|
||||||
|
type: 'VariableDeclarator',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
id: createIdentifier(varName),
|
||||||
|
init,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createObjectExpression(properties: {
|
||||||
|
[key: string]: Value
|
||||||
|
}): ObjectExpression {
|
||||||
|
return {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
properties: Object.entries(properties).map(([key, value]) => ({
|
||||||
|
type: 'ObjectProperty',
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
key: createIdentifier(key),
|
||||||
|
value,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -62,12 +62,12 @@ log(5, myVar)`
|
|||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('sketch declaration', () => {
|
it('sketch declaration', () => {
|
||||||
let code = `sketch mySketch {
|
let code = `const mySketch = startSketchAt([0, 0])
|
||||||
path myPath = lineTo(0, 1)
|
|> lineTo({ to: [0, 1], tag: "myPath" }, %)
|
||||||
lineTo(1, 1)
|
|> lineTo([1, 1], %)
|
||||||
path rightPath = lineTo(1, 0)
|
|> lineTo({ to: [1, 0], tag: "rightPath" }, %)
|
||||||
close()
|
|> close(%)
|
||||||
}
|
|
||||||
show(mySketch)
|
show(mySketch)
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
@ -76,11 +76,10 @@ show(mySketch)
|
|||||||
})
|
})
|
||||||
it('sketch piped into callExpression', () => {
|
it('sketch piped into callExpression', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'sketch mySk1 {',
|
'const mySk1 = startSketchAt([0, 0])',
|
||||||
' lineTo(1, 1)',
|
' |> lineTo([1, 1], %)',
|
||||||
' path myPath = lineTo(0, 1)',
|
' |> lineTo({ to: [0, 1], tag: "myTag" }, %)',
|
||||||
' lineTo(1, 1)',
|
' |> lineTo([1, 1], %)',
|
||||||
'}',
|
|
||||||
' |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
@ -226,24 +225,6 @@ const myFn = () => {
|
|||||||
|
|
||||||
const key = 'c'
|
const key = 'c'
|
||||||
// this is also a comment
|
// this is also a comment
|
||||||
}`
|
|
||||||
const { ast } = code2ast(code)
|
|
||||||
const recasted = recast(ast)
|
|
||||||
expect(recasted).toBe(code)
|
|
||||||
})
|
|
||||||
it('comments in a sketch block', () => {
|
|
||||||
const code = `
|
|
||||||
sketch mySketch { /* comment at start */
|
|
||||||
// comment at start more
|
|
||||||
path myPath = lineTo(0, 1) /* comment here with
|
|
||||||
some whitespace below */
|
|
||||||
|
|
||||||
|
|
||||||
lineTo(1, 1)
|
|
||||||
/* comment before declaration*/path rightPath = lineTo(1, 0)
|
|
||||||
close()
|
|
||||||
// comment at end
|
|
||||||
|
|
||||||
}`
|
}`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
@ -251,11 +232,10 @@ sketch mySketch { /* comment at start */
|
|||||||
})
|
})
|
||||||
it('comments in a pipe expression', () => {
|
it('comments in a pipe expression', () => {
|
||||||
const code = [
|
const code = [
|
||||||
'sketch mySk1 {',
|
'const mySk1 = startSketchAt([0, 0])',
|
||||||
' lineTo(1, 1)',
|
' |> lineTo([1, 1], %)',
|
||||||
' path myPath = lineTo(0, 1)',
|
' |> lineTo({ to: [0, 1], tag: "myTag" }, %)',
|
||||||
' lineTo(1, 1)',
|
' |> lineTo([1, 1], %)',
|
||||||
'}',
|
|
||||||
' // a comment',
|
' // a comment',
|
||||||
' |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
@ -267,14 +247,13 @@ sketch mySketch { /* comment at start */
|
|||||||
const code = `
|
const code = `
|
||||||
/* comment at start */
|
/* comment at start */
|
||||||
|
|
||||||
sketch mySk1 {
|
const mySk1 = startSketchAt([0, 0])
|
||||||
lineTo(1, 1)
|
|> lineTo([1, 1], %)
|
||||||
// comment here
|
// comment here
|
||||||
path myPath = lineTo(0, 1)
|
|> lineTo({ to: [0, 1], tag: 'myTag' }, %)
|
||||||
lineTo(1, 1) /* and
|
|> lineTo([1, 1], %) /* and
|
||||||
here
|
here
|
||||||
*/
|
*/
|
||||||
}
|
|
||||||
// a comment between pipe expression statements
|
// a comment between pipe expression statements
|
||||||
|> rx(90, %)
|
|> rx(90, %)
|
||||||
// and another with just white space between others below
|
// and another with just white space between others below
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
CallExpression,
|
CallExpression,
|
||||||
Value,
|
Value,
|
||||||
FunctionExpression,
|
FunctionExpression,
|
||||||
SketchExpression,
|
|
||||||
ArrayExpression,
|
ArrayExpression,
|
||||||
ObjectExpression,
|
ObjectExpression,
|
||||||
MemberExpression,
|
MemberExpression,
|
||||||
@ -36,17 +35,9 @@ export function recast(
|
|||||||
} else if (statement.type === 'VariableDeclaration') {
|
} else if (statement.type === 'VariableDeclaration') {
|
||||||
return statement.declarations
|
return statement.declarations
|
||||||
.map((declaration) => {
|
.map((declaration) => {
|
||||||
const isSketchOrFirstPipeExpressionIsSketch =
|
return `${statement.kind} ${declaration.id.name} = ${recastValue(
|
||||||
declaration.init.type === 'SketchExpression' ||
|
declaration.init
|
||||||
(declaration.init.type === 'PipeExpression' &&
|
)}`
|
||||||
declaration.init.body[0].type === 'SketchExpression')
|
|
||||||
|
|
||||||
const assignmentString = isSketchOrFirstPipeExpressionIsSketch
|
|
||||||
? ' '
|
|
||||||
: ' = '
|
|
||||||
return `${statement.kind} ${
|
|
||||||
declaration.id.name
|
|
||||||
}${assignmentString}${recastValue(declaration.init)}`
|
|
||||||
})
|
})
|
||||||
.join('')
|
.join('')
|
||||||
} else if (statement.type === 'ReturnStatement') {
|
} else if (statement.type === 'ReturnStatement') {
|
||||||
@ -193,15 +184,6 @@ function recastFunction(expression: FunctionExpression): string {
|
|||||||
.join(', ')}) => {${recast(expression.body, '', '', true)}}`
|
.join(', ')}) => {${recast(expression.body, '', '', true)}}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function recastSketchExpression(
|
|
||||||
expression: SketchExpression,
|
|
||||||
indentation: string
|
|
||||||
): string {
|
|
||||||
return `{${
|
|
||||||
recast(expression.body, '', indentation + ' ', true) || '\n \n'
|
|
||||||
}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function recastMemberExpression(
|
function recastMemberExpression(
|
||||||
expression: MemberExpression,
|
expression: MemberExpression,
|
||||||
indentation: string
|
indentation: string
|
||||||
@ -236,8 +218,6 @@ function recastValue(node: Value, indentation = ''): string {
|
|||||||
return recastCallExpression(node)
|
return recastCallExpression(node)
|
||||||
} else if (node.type === 'Identifier') {
|
} else if (node.type === 'Identifier') {
|
||||||
return node.name
|
return node.name
|
||||||
} else if (node.type === 'SketchExpression') {
|
|
||||||
return recastSketchExpression(node, indentation)
|
|
||||||
} else if (node.type === 'PipeExpression') {
|
} else if (node.type === 'PipeExpression') {
|
||||||
return recastPipeExpression(node)
|
return recastPipeExpression(node)
|
||||||
}
|
}
|
||||||
|
@ -1,335 +0,0 @@
|
|||||||
import {
|
|
||||||
ProgramMemory,
|
|
||||||
Path,
|
|
||||||
SketchGroup,
|
|
||||||
ExtrudeGroup,
|
|
||||||
SourceRange,
|
|
||||||
ExtrudeSurface,
|
|
||||||
Position,
|
|
||||||
Rotation,
|
|
||||||
} from './executor'
|
|
||||||
import { lineGeo, extrudeGeo } from './engine'
|
|
||||||
import { Quaternion, Vector3 } from 'three'
|
|
||||||
|
|
||||||
type Coords2d = [number, number]
|
|
||||||
|
|
||||||
interface PathReturn {
|
|
||||||
programMemory: ProgramMemory
|
|
||||||
currentPath: Path
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCoordsFromPaths(paths: Path[], index = 0): Coords2d {
|
|
||||||
const currentPath = paths[index]
|
|
||||||
if (!currentPath) {
|
|
||||||
return [0, 0]
|
|
||||||
}
|
|
||||||
if (currentPath.type === 'horizontalLineTo') {
|
|
||||||
const pathBefore = getCoordsFromPaths(paths, index - 1)
|
|
||||||
return [currentPath.x, pathBefore[1]]
|
|
||||||
} else if (currentPath.type === 'toPoint') {
|
|
||||||
return [currentPath.to[0], currentPath.to[1]]
|
|
||||||
}
|
|
||||||
return [0, 0]
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InternalFirstArg {
|
|
||||||
programMemory: ProgramMemory
|
|
||||||
name?: string
|
|
||||||
sourceRange: SourceRange
|
|
||||||
}
|
|
||||||
|
|
||||||
type SketchFn = (internals: InternalFirstArg, ...args: any[]) => PathReturn
|
|
||||||
|
|
||||||
export type SketchFnNames = 'close' | 'lineTo'
|
|
||||||
|
|
||||||
type InternalFn = (internals: InternalFirstArg, ...args: any[]) => any
|
|
||||||
|
|
||||||
export type InternalFnNames =
|
|
||||||
| 'extrude'
|
|
||||||
| 'translate'
|
|
||||||
| 'transform'
|
|
||||||
| 'getExtrudeWallTransform'
|
|
||||||
| 'rx'
|
|
||||||
| 'ry'
|
|
||||||
| 'rz'
|
|
||||||
|
|
||||||
export const sketchFns: { [key in SketchFnNames]: SketchFn } = {
|
|
||||||
close: ({ programMemory, name = '', sourceRange }) => {
|
|
||||||
const firstPath = programMemory?._sketch?.[0] as Path
|
|
||||||
|
|
||||||
let from = getCoordsFromPaths(
|
|
||||||
programMemory?._sketch || [],
|
|
||||||
(programMemory?._sketch?.length || 1) - 1
|
|
||||||
)
|
|
||||||
|
|
||||||
let to = getCoordsFromPaths(programMemory?._sketch || [], 0)
|
|
||||||
const geo = lineGeo({ from: [...from, 0], to: [...to, 0] })
|
|
||||||
const newPath: Path = {
|
|
||||||
type: 'toPoint',
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
__geoMeta: {
|
|
||||||
sourceRange,
|
|
||||||
pathToNode: [], // TODO
|
|
||||||
geos: [
|
|
||||||
{
|
|
||||||
type: 'line',
|
|
||||||
geo: geo.line,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'lineEnd',
|
|
||||||
geo: geo.tip,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if (name) {
|
|
||||||
newPath.name = name
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
programMemory: {
|
|
||||||
...programMemory,
|
|
||||||
_sketch: [
|
|
||||||
{
|
|
||||||
...firstPath,
|
|
||||||
from,
|
|
||||||
},
|
|
||||||
...(programMemory?._sketch || []).slice(1),
|
|
||||||
newPath,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
currentPath: newPath,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
lineTo: ({ programMemory, name = '', sourceRange }, ...args) => {
|
|
||||||
const [x, y] = args
|
|
||||||
if (!programMemory._sketch) {
|
|
||||||
throw new Error('No sketch to draw on')
|
|
||||||
}
|
|
||||||
let from = getCoordsFromPaths(
|
|
||||||
programMemory?._sketch || [],
|
|
||||||
(programMemory?._sketch?.length || 1) - 1
|
|
||||||
)
|
|
||||||
const geo = lineGeo({ from: [...from, 0], to: [x, y, 0] })
|
|
||||||
const currentPath: Path = {
|
|
||||||
type: 'toPoint',
|
|
||||||
to: [x, y],
|
|
||||||
from,
|
|
||||||
__geoMeta: {
|
|
||||||
sourceRange,
|
|
||||||
pathToNode: [], // TODO
|
|
||||||
geos: [
|
|
||||||
{
|
|
||||||
type: 'line',
|
|
||||||
geo: geo.line,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'lineEnd',
|
|
||||||
geo: geo.tip,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if (name) {
|
|
||||||
currentPath.name = name
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
programMemory: {
|
|
||||||
...programMemory,
|
|
||||||
_sketch: [...(programMemory._sketch || []), currentPath],
|
|
||||||
},
|
|
||||||
currentPath,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
function rotateOnAxis<T extends SketchGroup | ExtrudeGroup>(
|
|
||||||
axisMultiplier: [number, number, number]
|
|
||||||
): InternalFn {
|
|
||||||
return ({ sourceRange }, rotationD: number, sketch: T): T => {
|
|
||||||
const rotationR = rotationD * (Math.PI / 180)
|
|
||||||
const rotateVec = new Vector3(...axisMultiplier)
|
|
||||||
const quaternion = new Quaternion()
|
|
||||||
quaternion.setFromAxisAngle(rotateVec, rotationR)
|
|
||||||
|
|
||||||
const position = new Vector3(...sketch.position)
|
|
||||||
.applyQuaternion(quaternion)
|
|
||||||
.toArray()
|
|
||||||
|
|
||||||
const existingQuat = new Quaternion(...sketch.rotation)
|
|
||||||
const rotation = quaternion.multiply(existingQuat).toArray()
|
|
||||||
return {
|
|
||||||
...sketch,
|
|
||||||
rotation,
|
|
||||||
position,
|
|
||||||
__meta: [
|
|
||||||
...sketch.__meta,
|
|
||||||
{
|
|
||||||
sourceRange,
|
|
||||||
pathToNode: [], // TODO
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const extrude: InternalFn = (
|
|
||||||
{ sourceRange },
|
|
||||||
length: number,
|
|
||||||
sketchVal: SketchGroup
|
|
||||||
): ExtrudeGroup => {
|
|
||||||
const getSketchGeo = (sketchVal: SketchGroup): SketchGroup => {
|
|
||||||
return sketchVal
|
|
||||||
}
|
|
||||||
|
|
||||||
const sketch = getSketchGeo(sketchVal)
|
|
||||||
const { position, rotation } = sketchVal
|
|
||||||
|
|
||||||
const extrudeSurfaces: ExtrudeSurface[] = []
|
|
||||||
const extrusionDirection = clockwiseSign(sketch.value.map((line) => line.to))
|
|
||||||
sketch.value.map((line, index) => {
|
|
||||||
if (line.type === 'toPoint') {
|
|
||||||
let from: [number, number] = line.from
|
|
||||||
const to = line.to
|
|
||||||
const {
|
|
||||||
geo,
|
|
||||||
position: facePosition,
|
|
||||||
rotation: faceRotation,
|
|
||||||
} = extrudeGeo({
|
|
||||||
from: [from[0], from[1], 0],
|
|
||||||
to: [to[0], to[1], 0],
|
|
||||||
length,
|
|
||||||
extrusionDirection,
|
|
||||||
})
|
|
||||||
const groupQuaternion = new Quaternion(...rotation)
|
|
||||||
const currentWallQuat = new Quaternion(...faceRotation)
|
|
||||||
const unifiedQuit = new Quaternion().multiplyQuaternions(
|
|
||||||
currentWallQuat,
|
|
||||||
groupQuaternion.clone().invert()
|
|
||||||
)
|
|
||||||
|
|
||||||
const facePositionVector = new Vector3(...facePosition)
|
|
||||||
facePositionVector.applyQuaternion(groupQuaternion.clone())
|
|
||||||
const unifiedPosition = new Vector3().addVectors(
|
|
||||||
facePositionVector,
|
|
||||||
new Vector3(...position)
|
|
||||||
)
|
|
||||||
const surface: ExtrudeSurface = {
|
|
||||||
type: 'extrudePlane',
|
|
||||||
position: unifiedPosition.toArray() as Position,
|
|
||||||
rotation: unifiedQuit.toArray() as Rotation,
|
|
||||||
__geoMeta: {
|
|
||||||
geo,
|
|
||||||
sourceRange: line.__geoMeta.sourceRange,
|
|
||||||
pathToNode: line.__geoMeta.pathToNode,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
line.name && (surface.name = line.name)
|
|
||||||
extrudeSurfaces.push(surface)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
type: 'extrudeGroup',
|
|
||||||
value: extrudeSurfaces,
|
|
||||||
height: length,
|
|
||||||
position,
|
|
||||||
rotation,
|
|
||||||
__meta: [
|
|
||||||
{
|
|
||||||
sourceRange,
|
|
||||||
pathToNode: [], // TODO
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sourceRange: sketchVal.__meta[0].sourceRange,
|
|
||||||
pathToNode: sketchVal.__meta[0].pathToNode,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const translate: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
|
|
||||||
{ sourceRange }: InternalFirstArg,
|
|
||||||
vec3: [number, number, number],
|
|
||||||
sketch: T
|
|
||||||
): T => {
|
|
||||||
const oldPosition = new Vector3(...sketch.position)
|
|
||||||
const newPosition = oldPosition.add(new Vector3(...vec3))
|
|
||||||
return {
|
|
||||||
...sketch,
|
|
||||||
position: newPosition.toArray(),
|
|
||||||
__meta: [
|
|
||||||
...sketch.__meta,
|
|
||||||
{
|
|
||||||
sourceRange,
|
|
||||||
pathToNode: [], // TODO
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const transform: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
|
|
||||||
{ sourceRange }: InternalFirstArg,
|
|
||||||
transformInfo: {
|
|
||||||
position: Position
|
|
||||||
quaternion: Rotation
|
|
||||||
},
|
|
||||||
sketch: T
|
|
||||||
): T => {
|
|
||||||
const quaternionToApply = new Quaternion(...transformInfo.quaternion)
|
|
||||||
const newQuaternion = new Quaternion(...sketch.rotation).multiply(
|
|
||||||
quaternionToApply.invert()
|
|
||||||
)
|
|
||||||
|
|
||||||
const oldPosition = new Vector3(...sketch.position)
|
|
||||||
const newPosition = oldPosition
|
|
||||||
.applyQuaternion(quaternionToApply)
|
|
||||||
.add(new Vector3(...transformInfo.position))
|
|
||||||
return {
|
|
||||||
...sketch,
|
|
||||||
position: newPosition.toArray(),
|
|
||||||
rotation: newQuaternion.toArray(),
|
|
||||||
__meta: [
|
|
||||||
...sketch.__meta,
|
|
||||||
{
|
|
||||||
sourceRange,
|
|
||||||
pathToNode: [], // TODO
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getExtrudeWallTransform: InternalFn = (
|
|
||||||
_,
|
|
||||||
pathName: string,
|
|
||||||
extrudeGroup: ExtrudeGroup
|
|
||||||
): {
|
|
||||||
position: Position
|
|
||||||
quaternion: Rotation
|
|
||||||
} => {
|
|
||||||
const path = extrudeGroup.value.find((path) => path.name === pathName)
|
|
||||||
if (!path) throw new Error(`Could not find path with name ${pathName}`)
|
|
||||||
return {
|
|
||||||
position: path.position,
|
|
||||||
quaternion: path.rotation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const internalFns: { [key in InternalFnNames]: InternalFn } = {
|
|
||||||
rx: rotateOnAxis([1, 0, 0]),
|
|
||||||
ry: rotateOnAxis([0, 1, 0]),
|
|
||||||
rz: rotateOnAxis([0, 0, 1]),
|
|
||||||
extrude,
|
|
||||||
translate,
|
|
||||||
transform,
|
|
||||||
getExtrudeWallTransform,
|
|
||||||
}
|
|
||||||
|
|
||||||
function clockwiseSign(points: [number, number][]): number {
|
|
||||||
let sum = 0
|
|
||||||
for (let i = 0; i < points.length; i++) {
|
|
||||||
const currentPoint = points[i]
|
|
||||||
const nextPoint = points[(i + 1) % points.length]
|
|
||||||
sum += (nextPoint[0] - currentPoint[0]) * (nextPoint[1] + currentPoint[1])
|
|
||||||
}
|
|
||||||
return sum >= 0 ? 1 : -1
|
|
||||||
}
|
|
6
src/lang/std/README.md
Normal file
6
src/lang/std/README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
The std is as expected, tools that are provided with the language.
|
||||||
|
|
||||||
|
For this language that means functions.
|
||||||
|
|
||||||
|
However because programatically changing the source code is a first class citizen in this lang, there needs to be helpes for adding and modifying these function calls,
|
||||||
|
So it makes sense to group some of these together.
|
101
src/lang/std/extrude.ts
Normal file
101
src/lang/std/extrude.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { InternalFn } from './stdTypes'
|
||||||
|
import {
|
||||||
|
ExtrudeGroup,
|
||||||
|
ExtrudeSurface,
|
||||||
|
SketchGroup,
|
||||||
|
Position,
|
||||||
|
Rotation,
|
||||||
|
} from '../executor'
|
||||||
|
import { Quaternion, Vector3 } from 'three'
|
||||||
|
import { clockwiseSign } from './std'
|
||||||
|
import { extrudeGeo } from '../engine'
|
||||||
|
|
||||||
|
export const extrude: InternalFn = (
|
||||||
|
{ sourceRange },
|
||||||
|
length: number,
|
||||||
|
sketchVal: SketchGroup
|
||||||
|
): ExtrudeGroup => {
|
||||||
|
const getSketchGeo = (sketchVal: SketchGroup): SketchGroup => {
|
||||||
|
return sketchVal
|
||||||
|
}
|
||||||
|
|
||||||
|
const sketch = getSketchGeo(sketchVal)
|
||||||
|
const { position, rotation } = sketchVal
|
||||||
|
|
||||||
|
const extrudeSurfaces: ExtrudeSurface[] = []
|
||||||
|
const extrusionDirection = clockwiseSign(sketch.value.map((line) => line.to))
|
||||||
|
sketch.value.map((line, index) => {
|
||||||
|
if (line.type === 'toPoint') {
|
||||||
|
let from: [number, number] = line.from
|
||||||
|
const to = line.to
|
||||||
|
const {
|
||||||
|
geo,
|
||||||
|
position: facePosition,
|
||||||
|
rotation: faceRotation,
|
||||||
|
} = extrudeGeo({
|
||||||
|
from: [from[0], from[1], 0],
|
||||||
|
to: [to[0], to[1], 0],
|
||||||
|
length,
|
||||||
|
extrusionDirection,
|
||||||
|
})
|
||||||
|
const groupQuaternion = new Quaternion(...rotation)
|
||||||
|
const currentWallQuat = new Quaternion(...faceRotation)
|
||||||
|
const unifiedQuit = new Quaternion().multiplyQuaternions(
|
||||||
|
currentWallQuat,
|
||||||
|
groupQuaternion.clone().invert()
|
||||||
|
)
|
||||||
|
|
||||||
|
const facePositionVector = new Vector3(...facePosition)
|
||||||
|
facePositionVector.applyQuaternion(groupQuaternion.clone())
|
||||||
|
const unifiedPosition = new Vector3().addVectors(
|
||||||
|
facePositionVector,
|
||||||
|
new Vector3(...position)
|
||||||
|
)
|
||||||
|
const surface: ExtrudeSurface = {
|
||||||
|
type: 'extrudePlane',
|
||||||
|
position: unifiedPosition.toArray() as Position,
|
||||||
|
rotation: unifiedQuit.toArray() as Rotation,
|
||||||
|
__geoMeta: {
|
||||||
|
geo,
|
||||||
|
sourceRange: line.__geoMeta.sourceRange,
|
||||||
|
pathToNode: line.__geoMeta.pathToNode,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
line.name && (surface.name = line.name)
|
||||||
|
extrudeSurfaces.push(surface)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
type: 'extrudeGroup',
|
||||||
|
value: extrudeSurfaces,
|
||||||
|
height: length,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
__meta: [
|
||||||
|
{
|
||||||
|
sourceRange,
|
||||||
|
pathToNode: [], // TODO
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceRange: sketchVal.__meta[0].sourceRange,
|
||||||
|
pathToNode: sketchVal.__meta[0].pathToNode,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getExtrudeWallTransform: InternalFn = (
|
||||||
|
_,
|
||||||
|
pathName: string,
|
||||||
|
extrudeGroup: ExtrudeGroup
|
||||||
|
): {
|
||||||
|
position: Position
|
||||||
|
quaternion: Rotation
|
||||||
|
} => {
|
||||||
|
const path = extrudeGroup.value.find((path) => path.name === pathName)
|
||||||
|
if (!path) throw new Error(`Could not find path with name ${pathName}`)
|
||||||
|
return {
|
||||||
|
position: path.position,
|
||||||
|
quaternion: path.rotation,
|
||||||
|
}
|
||||||
|
}
|
190
src/lang/std/sketch.test.ts
Normal file
190
src/lang/std/sketch.test.ts
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import {
|
||||||
|
changeSketchArguments,
|
||||||
|
toolTipModification,
|
||||||
|
addTagForSketchOnFace,
|
||||||
|
getYComponent,
|
||||||
|
getXComponent,
|
||||||
|
} from './sketch'
|
||||||
|
import { lexer } from '../tokeniser'
|
||||||
|
import {
|
||||||
|
abstractSyntaxTree,
|
||||||
|
getNodePathFromSourceRange,
|
||||||
|
} from '../abstractSyntaxTree'
|
||||||
|
import { recast } from '../recast'
|
||||||
|
import { executor } from '../executor'
|
||||||
|
|
||||||
|
const eachQuad: [number, [number, number]][] = [
|
||||||
|
[-315, [1, 1]],
|
||||||
|
[-225, [-1, 1]],
|
||||||
|
[-135, [-1, -1]],
|
||||||
|
[-45, [1, -1]],
|
||||||
|
[45, [1, 1]],
|
||||||
|
[135, [-1, 1]],
|
||||||
|
[225, [-1, -1]],
|
||||||
|
[315, [1, -1]],
|
||||||
|
[405, [1, 1]],
|
||||||
|
[495, [-1, 1]],
|
||||||
|
[585, [-1, -1]],
|
||||||
|
[675, [1, -1]],
|
||||||
|
]
|
||||||
|
|
||||||
|
describe('testing getYComponent', () => {
|
||||||
|
it('should return the vertical component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => {
|
||||||
|
const expected: [number, number][] = []
|
||||||
|
const results: [number, number][] = []
|
||||||
|
eachQuad.forEach(([angle, expectedResult]) => {
|
||||||
|
results.push(
|
||||||
|
getYComponent(angle, 1).map((a) => Math.round(a)) as [number, number]
|
||||||
|
)
|
||||||
|
expected.push(expectedResult)
|
||||||
|
})
|
||||||
|
expect(results).toEqual(expected)
|
||||||
|
})
|
||||||
|
it('return extreme values on the extremes', () => {
|
||||||
|
let result: [number, number]
|
||||||
|
result = getYComponent(0, 1)
|
||||||
|
expect(result[0]).toBe(1)
|
||||||
|
expect(result[1]).toBe(0)
|
||||||
|
|
||||||
|
result = getYComponent(90, 1)
|
||||||
|
expect(result[0]).toBe(1)
|
||||||
|
expect(result[1]).toBeGreaterThan(100000)
|
||||||
|
|
||||||
|
result = getYComponent(180, 1)
|
||||||
|
expect(result[0]).toBe(-1)
|
||||||
|
expect(result[1]).toBeCloseTo(0)
|
||||||
|
|
||||||
|
result = getYComponent(270, 1)
|
||||||
|
expect(result[0]).toBe(-1)
|
||||||
|
expect(result[1]).toBeLessThan(100000)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('testing getXComponent', () => {
|
||||||
|
it('should return the horizontal component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => {
|
||||||
|
const expected: [number, number][] = []
|
||||||
|
const results: [number, number][] = []
|
||||||
|
eachQuad.forEach(([angle, expectedResult]) => {
|
||||||
|
results.push(
|
||||||
|
getXComponent(angle, 1).map((a) => Math.round(a)) as [number, number]
|
||||||
|
)
|
||||||
|
expected.push(expectedResult)
|
||||||
|
})
|
||||||
|
expect(results).toEqual(expected)
|
||||||
|
})
|
||||||
|
it('return extreme values on the extremes', () => {
|
||||||
|
let result: [number, number]
|
||||||
|
result = getXComponent(0, 1)
|
||||||
|
expect(result[0]).toBeGreaterThan(100000)
|
||||||
|
expect(result[1]).toBe(1)
|
||||||
|
|
||||||
|
result = getXComponent(90, 1)
|
||||||
|
expect(result[0]).toBeCloseTo(0)
|
||||||
|
expect(result[1]).toBe(1)
|
||||||
|
|
||||||
|
result = getXComponent(180, 1)
|
||||||
|
expect(result[0]).toBeLessThan(100000)
|
||||||
|
expect(result[1]).toBe(1)
|
||||||
|
|
||||||
|
result = getXComponent(270, 1)
|
||||||
|
expect(result[0]).toBeCloseTo(0)
|
||||||
|
expect(result[1]).toBe(-1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('testing changeSketchArguments', () => {
|
||||||
|
const lineToChange = 'lineTo([-1.59, -1.54], %)'
|
||||||
|
const lineAfterChange = 'lineTo([2, 3], %)'
|
||||||
|
test('changeSketchArguments', () => {
|
||||||
|
const genCode = (line: string) => `
|
||||||
|
const mySketch001 = startSketchAt([0, 0])
|
||||||
|
|> ${line}
|
||||||
|
|> lineTo([0.46, -5.82], %)
|
||||||
|
|> rx(45, %)
|
||||||
|
show(mySketch001)`
|
||||||
|
const code = genCode(lineToChange)
|
||||||
|
const expectedCode = genCode(lineAfterChange)
|
||||||
|
const ast = abstractSyntaxTree(lexer(code))
|
||||||
|
const programMemory = executor(ast)
|
||||||
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
|
const { modifiedAst } = changeSketchArguments(
|
||||||
|
ast,
|
||||||
|
programMemory,
|
||||||
|
[sourceStart, sourceStart + lineToChange.length],
|
||||||
|
[2, 3],
|
||||||
|
{
|
||||||
|
mode: 'sketch',
|
||||||
|
sketchMode: 'sketchEdit',
|
||||||
|
isTooltip: true,
|
||||||
|
rotation: [0, 0, 0, 1],
|
||||||
|
position: [0, 0, 0],
|
||||||
|
pathToNode: ['body', 0, 'declarations', '0', 'init'],
|
||||||
|
},
|
||||||
|
[0, 0]
|
||||||
|
)
|
||||||
|
expect(recast(modifiedAst)).toBe(expectedCode)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('testing toolTipModification', () => {
|
||||||
|
const lineToChange = 'lineTo([-1.59, -1.54], %)'
|
||||||
|
const lineAfterChange = 'lineTo([2, 3], %)'
|
||||||
|
test('toolTipModification', () => {
|
||||||
|
const code = `
|
||||||
|
const mySketch001 = startSketchAt([0, 0])
|
||||||
|
|> rx(45, %)
|
||||||
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|
|> lineTo([0.46, -5.82], %)
|
||||||
|
show(mySketch001)`
|
||||||
|
const ast = abstractSyntaxTree(lexer(code))
|
||||||
|
const programMemory = executor(ast)
|
||||||
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
|
const { modifiedAst } = toolTipModification(ast, programMemory, [2, 3], {
|
||||||
|
mode: 'sketch',
|
||||||
|
sketchMode: 'lineTo',
|
||||||
|
isTooltip: true,
|
||||||
|
rotation: [0, 0, 0, 1],
|
||||||
|
position: [0, 0, 0],
|
||||||
|
pathToNode: ['body', 0, 'declarations', '0', 'init'],
|
||||||
|
})
|
||||||
|
const expectedCode = `
|
||||||
|
const mySketch001 = startSketchAt([0, 0])
|
||||||
|
|> rx(45, %)
|
||||||
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|
|> lineTo([0.46, -5.82], %)
|
||||||
|
|> lineTo([2, 3], %)
|
||||||
|
show(mySketch001)`
|
||||||
|
expect(recast(modifiedAst)).toBe(expectedCode)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('testing addTagForSketchOnFace', () => {
|
||||||
|
const originalLine = 'lineTo([-1.59, -1.54], %)'
|
||||||
|
const genCode = (line: string) => `
|
||||||
|
const mySketch001 = startSketchAt([0, 0])
|
||||||
|
|> rx(45, %)
|
||||||
|
|> ${line}
|
||||||
|
|> lineTo([0.46, -5.82], %)
|
||||||
|
show(mySketch001)`
|
||||||
|
const code = genCode(originalLine)
|
||||||
|
const ast = abstractSyntaxTree(lexer(code))
|
||||||
|
const programMemory = executor(ast)
|
||||||
|
const sourceStart = code.indexOf(originalLine)
|
||||||
|
const sourceRange: [number, number] = [
|
||||||
|
sourceStart,
|
||||||
|
sourceStart + originalLine.length,
|
||||||
|
]
|
||||||
|
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
|
const { modifiedAst } = addTagForSketchOnFace(
|
||||||
|
{
|
||||||
|
previousProgramMemory: programMemory,
|
||||||
|
pathToNode,
|
||||||
|
node: ast,
|
||||||
|
},
|
||||||
|
'lineTo'
|
||||||
|
)
|
||||||
|
const expectedCode = genCode(
|
||||||
|
"lineTo({ to: [-1.59, -1.54], tag: 'seg01' }, %)"
|
||||||
|
)
|
||||||
|
expect(recast(modifiedAst)).toBe(expectedCode)
|
||||||
|
})
|
1213
src/lang/std/sketch.ts
Normal file
1213
src/lang/std/sketch.ts
Normal file
File diff suppressed because it is too large
Load Diff
134
src/lang/std/std.ts
Normal file
134
src/lang/std/std.ts
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import {
|
||||||
|
lineTo,
|
||||||
|
xLineTo,
|
||||||
|
yLineTo,
|
||||||
|
line,
|
||||||
|
xLine,
|
||||||
|
yLine,
|
||||||
|
angledLine,
|
||||||
|
angledLineOfXLength,
|
||||||
|
angledLineToX,
|
||||||
|
angledLineOfYLength,
|
||||||
|
angledLineToY,
|
||||||
|
closee,
|
||||||
|
startSketchAt,
|
||||||
|
} from './sketch'
|
||||||
|
import { extrude, getExtrudeWallTransform } from './extrude'
|
||||||
|
import { Quaternion, Vector3 } from 'three'
|
||||||
|
import { SketchGroup, ExtrudeGroup, Position, Rotation } from '../executor'
|
||||||
|
|
||||||
|
import { InternalFn, InternalFnNames, InternalFirstArg } from './stdTypes'
|
||||||
|
|
||||||
|
const transform: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
|
||||||
|
{ sourceRange }: InternalFirstArg,
|
||||||
|
transformInfo: {
|
||||||
|
position: Position
|
||||||
|
quaternion: Rotation
|
||||||
|
},
|
||||||
|
sketch: T
|
||||||
|
): T => {
|
||||||
|
const quaternionToApply = new Quaternion(...transformInfo.quaternion)
|
||||||
|
const newQuaternion = new Quaternion(...sketch.rotation).multiply(
|
||||||
|
quaternionToApply.invert()
|
||||||
|
)
|
||||||
|
|
||||||
|
const oldPosition = new Vector3(...sketch.position)
|
||||||
|
const newPosition = oldPosition
|
||||||
|
.applyQuaternion(quaternionToApply)
|
||||||
|
.add(new Vector3(...transformInfo.position))
|
||||||
|
return {
|
||||||
|
...sketch,
|
||||||
|
position: newPosition.toArray(),
|
||||||
|
rotation: newQuaternion.toArray(),
|
||||||
|
__meta: [
|
||||||
|
...sketch.__meta,
|
||||||
|
{
|
||||||
|
sourceRange,
|
||||||
|
pathToNode: [], // TODO
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const translate: InternalFn = <T extends SketchGroup | ExtrudeGroup>(
|
||||||
|
{ sourceRange }: InternalFirstArg,
|
||||||
|
vec3: [number, number, number],
|
||||||
|
sketch: T
|
||||||
|
): T => {
|
||||||
|
const oldPosition = new Vector3(...sketch.position)
|
||||||
|
const newPosition = oldPosition.add(new Vector3(...vec3))
|
||||||
|
return {
|
||||||
|
...sketch,
|
||||||
|
position: newPosition.toArray(),
|
||||||
|
__meta: [
|
||||||
|
...sketch.__meta,
|
||||||
|
{
|
||||||
|
sourceRange,
|
||||||
|
pathToNode: [], // TODO
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const internalFns: { [key in InternalFnNames]: InternalFn } = {
|
||||||
|
rx: rotateOnAxis([1, 0, 0]),
|
||||||
|
ry: rotateOnAxis([0, 1, 0]),
|
||||||
|
rz: rotateOnAxis([0, 0, 1]),
|
||||||
|
extrude,
|
||||||
|
translate,
|
||||||
|
transform,
|
||||||
|
getExtrudeWallTransform,
|
||||||
|
lineTo: lineTo.fn,
|
||||||
|
xLineTo: xLineTo.fn,
|
||||||
|
yLineTo: yLineTo.fn,
|
||||||
|
line: line.fn,
|
||||||
|
xLine: xLine.fn,
|
||||||
|
yLine: yLine.fn,
|
||||||
|
angledLine: angledLine.fn,
|
||||||
|
angledLineOfXLength: angledLineOfXLength.fn,
|
||||||
|
angledLineToX: angledLineToX.fn,
|
||||||
|
angledLineOfYLength: angledLineOfYLength.fn,
|
||||||
|
angledLineToY: angledLineToY.fn,
|
||||||
|
startSketchAt,
|
||||||
|
closee,
|
||||||
|
}
|
||||||
|
|
||||||
|
function rotateOnAxis<T extends SketchGroup | ExtrudeGroup>(
|
||||||
|
axisMultiplier: [number, number, number]
|
||||||
|
): InternalFn {
|
||||||
|
return ({ sourceRange }, rotationD: number, sketch: T): T => {
|
||||||
|
const rotationR = rotationD * (Math.PI / 180)
|
||||||
|
const rotateVec = new Vector3(...axisMultiplier)
|
||||||
|
const quaternion = new Quaternion()
|
||||||
|
quaternion.setFromAxisAngle(rotateVec, rotationR)
|
||||||
|
|
||||||
|
const position = new Vector3(...sketch.position)
|
||||||
|
.applyQuaternion(quaternion)
|
||||||
|
.toArray()
|
||||||
|
|
||||||
|
const existingQuat = new Quaternion(...sketch.rotation)
|
||||||
|
const rotation = quaternion.multiply(existingQuat).toArray()
|
||||||
|
return {
|
||||||
|
...sketch,
|
||||||
|
rotation,
|
||||||
|
position,
|
||||||
|
__meta: [
|
||||||
|
...sketch.__meta,
|
||||||
|
{
|
||||||
|
sourceRange,
|
||||||
|
pathToNode: [], // TODO
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clockwiseSign(points: [number, number][]): number {
|
||||||
|
let sum = 0
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const currentPoint = points[i]
|
||||||
|
const nextPoint = points[(i + 1) % points.length]
|
||||||
|
sum += (nextPoint[0] - currentPoint[0]) * (nextPoint[1] + currentPoint[1])
|
||||||
|
}
|
||||||
|
return sum >= 0 ? 1 : -1
|
||||||
|
}
|
68
src/lang/std/stdTypes.ts
Normal file
68
src/lang/std/stdTypes.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { ProgramMemory, Path, SourceRange } from '../executor'
|
||||||
|
import { Program } from '../abstractSyntaxTree'
|
||||||
|
|
||||||
|
export interface InternalFirstArg {
|
||||||
|
programMemory: ProgramMemory
|
||||||
|
name?: string
|
||||||
|
sourceRange: SourceRange
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PathReturn {
|
||||||
|
programMemory: ProgramMemory
|
||||||
|
currentPath: Path
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InternalFn = (internals: InternalFirstArg, ...args: any[]) => any
|
||||||
|
|
||||||
|
export type InternalFnNames =
|
||||||
|
| 'extrude'
|
||||||
|
| 'translate'
|
||||||
|
| 'transform'
|
||||||
|
| 'getExtrudeWallTransform'
|
||||||
|
| 'rx'
|
||||||
|
| 'ry'
|
||||||
|
| 'rz'
|
||||||
|
| 'lineTo'
|
||||||
|
| 'yLineTo'
|
||||||
|
| 'xLineTo'
|
||||||
|
| 'line'
|
||||||
|
| 'yLine'
|
||||||
|
| 'xLine'
|
||||||
|
| 'angledLine'
|
||||||
|
| 'angledLineOfXLength'
|
||||||
|
| 'angledLineToX'
|
||||||
|
| 'angledLineOfYLength'
|
||||||
|
| 'angledLineToY'
|
||||||
|
| 'startSketchAt'
|
||||||
|
| 'closee'
|
||||||
|
|
||||||
|
export interface ModifyAstBase {
|
||||||
|
node: Program
|
||||||
|
previousProgramMemory: ProgramMemory
|
||||||
|
pathToNode: (string | number)[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface addCall extends ModifyAstBase {
|
||||||
|
to: [number, number]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface updateArgs extends ModifyAstBase {
|
||||||
|
from: [number, number]
|
||||||
|
to: [number, number]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SketchLineHelper {
|
||||||
|
fn: InternalFn
|
||||||
|
add: (a: addCall) => {
|
||||||
|
modifiedAst: Program
|
||||||
|
pathToNode: (string | number)[]
|
||||||
|
}
|
||||||
|
updateArgs: (a: updateArgs) => {
|
||||||
|
modifiedAst: Program
|
||||||
|
pathToNode: (string | number)[]
|
||||||
|
}
|
||||||
|
addTag: (a: ModifyAstBase) => {
|
||||||
|
modifiedAst: Program
|
||||||
|
tag: string
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import { isOverlapping } from './utils'
|
import { isOverlap, roundOff } from './utils'
|
||||||
import { Range } from '../useStore'
|
import { Range } from '../useStore'
|
||||||
|
|
||||||
describe('testing isOverlapping', () => {
|
describe('testing isOverlapping', () => {
|
||||||
testBothOrders([0, 5], [3, 10])
|
testBothOrders([0, 3], [3, 10])
|
||||||
testBothOrders([0, 5], [3, 4])
|
testBothOrders([0, 5], [3, 4])
|
||||||
testBothOrders([0, 5], [5, 10])
|
testBothOrders([0, 5], [5, 10])
|
||||||
testBothOrders([0, 5], [6, 10], false)
|
testBothOrders([0, 5], [6, 10], false)
|
||||||
@ -13,7 +13,22 @@ describe('testing isOverlapping', () => {
|
|||||||
|
|
||||||
function testBothOrders(a: Range, b: Range, result = true) {
|
function testBothOrders(a: Range, b: Range, result = true) {
|
||||||
it(`test is overlapping ${a} ${b}`, () => {
|
it(`test is overlapping ${a} ${b}`, () => {
|
||||||
expect(isOverlapping(a, b)).toBe(result)
|
expect(isOverlap(a, b)).toBe(result)
|
||||||
expect(isOverlapping(b, a)).toBe(result)
|
expect(isOverlap(b, a)).toBe(result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('testing roundOff', () => {
|
||||||
|
it('defaults to 2 decimal places', () => {
|
||||||
|
expect(roundOff(1.23456789)).toBe(1.23)
|
||||||
|
})
|
||||||
|
it('rounds off to 3 decimal places', () => {
|
||||||
|
expect(roundOff(1.23456789, 3)).toBe(1.235)
|
||||||
|
})
|
||||||
|
it('works with whole numbers', () => {
|
||||||
|
expect(roundOff(1.23456789, 0)).toBe(1)
|
||||||
|
})
|
||||||
|
it('rounds up ok', () => {
|
||||||
|
expect(roundOff(1.273456789, 1)).toBe(1.3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
import { Range } from '../useStore'
|
import { Range } from '../useStore'
|
||||||
|
|
||||||
export const isOverlapping = (a: Range, b: Range) => {
|
export function isOverlap(a: Range, b: Range) {
|
||||||
const startingRange = a[0] < b[0] ? a : b
|
const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a]
|
||||||
const secondRange = a[0] < b[0] ? b : a
|
const [lastOfFirst, firstOfSecond] = [startingRange[1], secondRange[0]]
|
||||||
return startingRange[1] >= secondRange[0]
|
return lastOfFirst >= firstOfSecond
|
||||||
|
}
|
||||||
|
|
||||||
|
export function roundOff(num: number, places: number = 2): number {
|
||||||
|
const x = Math.pow(10, places)
|
||||||
|
return Math.round(num * x) / x
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLength(a: [number, number], b: [number, number]): number {
|
||||||
|
const x = b[0] - a[0]
|
||||||
|
const y = b[1] - a[1]
|
||||||
|
return Math.sqrt(x * x + y * y)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAngle(a: [number, number], b: [number, number]): number {
|
||||||
|
const x = b[0] - a[0]
|
||||||
|
const y = b[1] - a[1]
|
||||||
|
return ((Math.atan2(y, x) * 180) / Math.PI + 360) % 360
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,41 @@ import { recast } from './lang/recast'
|
|||||||
import { lexer } from './lang/tokeniser'
|
import { lexer } from './lang/tokeniser'
|
||||||
|
|
||||||
export type Range = [number, number]
|
export type Range = [number, number]
|
||||||
|
export type TooTip =
|
||||||
|
| 'lineTo'
|
||||||
|
| 'line'
|
||||||
|
| 'angledLine'
|
||||||
|
| 'angledLineOfXLength'
|
||||||
|
| 'angledLineOfYLength'
|
||||||
|
| 'angledLineToX'
|
||||||
|
| 'angledLineToY'
|
||||||
|
| 'xLine'
|
||||||
|
| 'yLine'
|
||||||
|
| 'xLineTo'
|
||||||
|
| 'yLineTo'
|
||||||
|
|
||||||
type GuiModes =
|
export const toolTips: TooTip[] = [
|
||||||
|
'lineTo',
|
||||||
|
'line',
|
||||||
|
'angledLine',
|
||||||
|
'angledLineOfXLength',
|
||||||
|
'angledLineOfYLength',
|
||||||
|
'angledLineToX',
|
||||||
|
'angledLineToY',
|
||||||
|
'xLine',
|
||||||
|
'yLine',
|
||||||
|
'xLineTo',
|
||||||
|
'yLineTo',
|
||||||
|
]
|
||||||
|
|
||||||
|
export type GuiModes =
|
||||||
| {
|
| {
|
||||||
mode: 'default'
|
mode: 'default'
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
mode: 'sketch'
|
mode: 'sketch'
|
||||||
sketchMode: 'points'
|
sketchMode: TooTip
|
||||||
|
isTooltip: true
|
||||||
rotation: Rotation
|
rotation: Rotation
|
||||||
position: Position
|
position: Position
|
||||||
id?: string
|
id?: string
|
||||||
@ -26,6 +53,7 @@ type GuiModes =
|
|||||||
| {
|
| {
|
||||||
mode: 'sketch'
|
mode: 'sketch'
|
||||||
sketchMode: 'sketchEdit'
|
sketchMode: 'sketchEdit'
|
||||||
|
isTooltip: true
|
||||||
rotation: Rotation
|
rotation: Rotation
|
||||||
position: Position
|
position: Position
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
|
Reference in New Issue
Block a user