diff --git a/src/App.tsx b/src/App.tsx
index dc244fe92..fc020790e 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -3,14 +3,9 @@ import { Canvas } from '@react-three/fiber'
import { Allotment } from 'allotment'
import { OrbitControls, OrthographicCamera } from '@react-three/drei'
import { lexer } from './lang/tokeniser'
-import {
- abstractSyntaxTree,
- getNodePathFromSourceRange,
- getNodeFromPath
-} from './lang/abstractSyntaxTree'
+import { abstractSyntaxTree } from './lang/abstractSyntaxTree'
import { executor, processShownObjects, ViewerArtifact } from './lang/executor'
import { recast } from './lang/recast'
-import { BufferGeometry } from 'three'
import CodeMirror from '@uiw/react-codemirror'
import { javascript } from '@codemirror/lang-javascript'
import { ViewUpdate } from '@codemirror/view'
@@ -19,12 +14,12 @@ import {
addLineHighlight,
} from './editor/highlightextension'
import { useStore } from './useStore'
-import { isOverlapping } from './lib/utils'
import { Toolbar } from './Toolbar'
import { BasePlanes } from './components/BasePlanes'
import { SketchPlane } from './components/SketchPlane'
import { Logs } from './components/Logs'
import { AxisIndicator } from './components/AxisIndicator'
+import { RenderViewerArtifacts } from './components/SketchLine'
const OrrthographicCamera = OrthographicCamera as any
@@ -160,7 +155,7 @@ function App() {
@@ -199,111 +194,3 @@ function App() {
}
export default App
-
-function Line({
- geo,
- sourceRange,
- forceHighlight = false,
-}: {
- geo: BufferGeometry
- sourceRange: [number, number]
- forceHighlight?: boolean
-}) {
- const { setHighlightRange, selectionRange, guiMode, setGuiMode, ast } =
- useStore(
- ({ setHighlightRange, selectionRange, guiMode, setGuiMode, ast }) => ({
- setHighlightRange,
- selectionRange,
- guiMode,
- setGuiMode,
- ast,
- })
- )
- // This reference will give us direct access to the mesh
- const ref = useRef() as any
- const [hovered, setHover] = useState(false)
- const [editorCursor, setEditorCursor] = useState(false)
- const [didSetCanEdit, setDidSetCanEdit] = useState(false)
- useEffect(() => {
- const shouldHighlight = isOverlapping(sourceRange, selectionRange)
- setEditorCursor(shouldHighlight)
- if (shouldHighlight && guiMode.mode === 'default' && ast) {
- const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
- const piper = getNodeFromPath(ast, pathToNode, 'PipeExpression')
- const axis = piper.type !== 'PipeExpression' ? 'xy'
- : piper?.body?.[1]?.callee?.name === 'rx' ? 'xz' : 'yz'
- setGuiMode({ mode: 'canEditSketch', pathToNode, axis })
- setDidSetCanEdit(true)
- } else if (
- !shouldHighlight &&
- didSetCanEdit &&
- guiMode.mode === 'canEditSketch'
- ) {
- setGuiMode({ mode: 'default' })
- setDidSetCanEdit(false)
- }
- }, [selectionRange, sourceRange])
-
- return (
- {
- setHover(true)
- setHighlightRange(sourceRange)
- }}
- onPointerOut={(event) => {
- setHover(false)
- setHighlightRange([0, 0])
- }}
- >
-
-
-
- )
-}
-
-function RenderViewerArtifacts({
- artifact,
- forceHighlight = false,
-}: {
- artifact: ViewerArtifact
- forceHighlight?: boolean
-}) {
- const { selectionRange } = useStore(({ selectionRange }) => ({
- selectionRange,
- }))
- const [editorCursor, setEditorCursor] = useState(false)
- useEffect(() => {
- const shouldHighlight = isOverlapping(artifact.sourceRange, selectionRange)
- setEditorCursor(shouldHighlight)
- }, [selectionRange, artifact.sourceRange])
- if (artifact.type === 'sketchLine') {
- const { geo, sourceRange } = artifact
- return (
-
- )
- }
- return (
- <>
- {artifact.children.map((artifact, index) => (
-
- ))}
- >
- )
-}
diff --git a/src/components/SketchLine.tsx b/src/components/SketchLine.tsx
new file mode 100644
index 000000000..be293de41
--- /dev/null
+++ b/src/components/SketchLine.tsx
@@ -0,0 +1,274 @@
+import { useRef, useState, useEffect, useMemo } from 'react'
+import {
+ getNodePathFromSourceRange,
+ getNodeFromPath,
+ CallExpression,
+ changeArguments,
+} from '../lang/abstractSyntaxTree'
+import { ViewerArtifact } from '../lang/executor'
+import { BufferGeometry } from 'three'
+import { useStore } from '../useStore'
+import { isOverlapping } from '../lib/utils'
+import { LineGeos } from '../lang/engine'
+import { Vector3 } from 'three'
+
+function useHeightlight(sourceRange: [number, number]) {
+ const { selectionRange, guiMode, setGuiMode, ast } = useStore((s) => ({
+ setHighlightRange: s.setHighlightRange,
+ selectionRange: s.selectionRange,
+ guiMode: s.guiMode,
+ setGuiMode: s.setGuiMode,
+ ast: s.ast,
+ }))
+ // This reference will give us direct access to the mesh
+ const [editorCursor, setEditorCursor] = useState(false)
+ const [didSetCanEdit, setDidSetCanEdit] = useState(false)
+ useEffect(() => {
+ const shouldHighlight = isOverlapping(sourceRange, selectionRange)
+ setEditorCursor(shouldHighlight)
+ if (shouldHighlight && guiMode.mode === 'default' && ast) {
+ const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
+ const piper = getNodeFromPath(ast, pathToNode, 'PipeExpression')
+ const axis =
+ piper.type !== 'PipeExpression'
+ ? 'xy'
+ : piper?.body?.[1]?.callee?.name === 'rx'
+ ? 'xz'
+ : 'yz'
+ setGuiMode({ mode: 'canEditSketch', pathToNode, axis })
+ setDidSetCanEdit(true)
+ } else if (
+ !shouldHighlight &&
+ didSetCanEdit &&
+ guiMode.mode === 'canEditSketch'
+ ) {
+ setGuiMode({ mode: 'default' })
+ setDidSetCanEdit(false)
+ }
+ }, [selectionRange, sourceRange])
+ return {
+ editorCursor,
+ }
+}
+
+function SketchLine({
+ geo,
+ sourceRange,
+ forceHighlight = false,
+}: {
+ geo: LineGeos
+ sourceRange: [number, number]
+ forceHighlight?: boolean
+}) {
+ const { editorCursor } = useHeightlight(sourceRange)
+ const { setHighlightRange } = useStore(
+ ({ setHighlightRange, selectionRange, guiMode, setGuiMode, ast }) => ({
+ setHighlightRange,
+ selectionRange,
+ guiMode,
+ setGuiMode,
+ ast,
+ })
+ )
+ // This reference will give us direct access to the mesh
+ const ref = useRef() as any
+ const [hovered, setHover] = useState(false)
+
+ return (
+ <>
+ {
+ setHover(true)
+ setHighlightRange(sourceRange)
+ }}
+ onPointerOut={(event) => {
+ setHover(false)
+ setHighlightRange([0, 0])
+ }}
+ >
+
+
+
+
+ >
+ )
+}
+
+const roundOff = (num: number, places: number): number => {
+ const x = Math.pow(10, places)
+ return Math.round(num * x) / x
+}
+
+function MovingSphere({
+ geo,
+ sourceRange,
+ editorCursor,
+}: {
+ geo: BufferGeometry
+ sourceRange: [number, number]
+ editorCursor: boolean
+}) {
+ const ref = useRef() as any
+ const lastPointerRef = useRef(new Vector3())
+ const [hovered, setHover] = useState(false)
+ const [isMouseDown, setIsMouseDown] = useState(false)
+
+ const { setHighlightRange, guiMode, ast, updateAst } = useStore((s) => ({
+ setHighlightRange: s.setHighlightRange,
+ selectionRange: s.selectionRange,
+ guiMode: s.guiMode,
+ setGuiMode: s.setGuiMode,
+ ast: s.ast,
+ updateAst: s.updateAst,
+ }))
+ const { originalXY } = useMemo(() => {
+ if (ast) {
+ const thePath = getNodePathFromSourceRange(ast, sourceRange)
+ const callExpression = getNodeFromPath(ast, thePath) as CallExpression
+ const [xArg, yArg] = callExpression?.arguments || []
+ const x = xArg?.type === 'Literal' ? xArg.value : -1
+ const y = yArg?.type === 'Literal' ? yArg.value : -1
+ console.log(callExpression)
+ return {
+ originalXY: [x, y],
+ }
+ }
+ return {
+ originalXY: [-1, -1],
+ }
+ }, [ast])
+
+ useEffect(() => {
+ const handleMouseUp = () => {
+ if (isMouseDown && ast) {
+ const thePath = getNodePathFromSourceRange(ast, sourceRange)
+ const theNewPoints: [number, number] = [
+ roundOff(lastPointerRef.current.x, 2),
+ roundOff(lastPointerRef.current.y, 2),
+ ]
+ console.log('theNewPoints', theNewPoints)
+ const { modifiedAst } = changeArguments(ast, thePath, theNewPoints)
+ updateAst(modifiedAst)
+ ref.current.position.set(0, 0, 0)
+ }
+ setIsMouseDown(false)
+ }
+ window.addEventListener('mouseup', handleMouseUp)
+ return () => {
+ window.removeEventListener('mouseup', handleMouseUp)
+ }
+ }, [isMouseDown, ast])
+
+ return (
+ <>
+ {
+ setHover(true)
+ setHighlightRange(sourceRange)
+ }}
+ onPointerOut={(event) => {
+ setHover(false)
+ setHighlightRange([0, 0])
+ }}
+ onPointerDown={() => setIsMouseDown(true)}
+ >
+
+
+
+ {isMouseDown && (
+ {
+ const point = a.point
+ if (
+ lastPointerRef.current.x === 0 &&
+ lastPointerRef.current.y === 0 &&
+ lastPointerRef.current.z === 0
+ ) {
+ lastPointerRef.current.set(point.x, point.y, point.z)
+ return
+ }
+ if (guiMode.mode)
+ if (ref.current) {
+ const diff = new Vector3().subVectors(
+ point,
+ lastPointerRef.current
+ )
+ console.log(originalXY)
+ if (originalXY[0] === -1) {
+ diff.x = 0
+ }
+ if (originalXY[1] === -1) {
+ diff.y = 0
+ }
+ ref.current.position.add(diff)
+ lastPointerRef.current.set(point.x, point.y, point.z)
+ }
+ }}
+ name="my-mesh"
+ >
+
+
+
+ )}
+ >
+ )
+}
+
+export function RenderViewerArtifacts({
+ artifact,
+ forceHighlight = false,
+}: {
+ artifact: ViewerArtifact
+ forceHighlight?: boolean
+}) {
+ const { selectionRange } = useStore(({ selectionRange }) => ({
+ selectionRange,
+ }))
+ const [editorCursor, setEditorCursor] = useState(false)
+ useEffect(() => {
+ const shouldHighlight = isOverlapping(artifact.sourceRange, selectionRange)
+ setEditorCursor(shouldHighlight)
+ }, [selectionRange, artifact.sourceRange])
+ if (artifact.type === 'sketchLine') {
+ const { geo, sourceRange } = artifact
+ return (
+
+ )
+ }
+ if (artifact.type === 'sketchBase') {
+ console.log('BASE TODO')
+ return null
+ }
+ return (
+ <>
+ {artifact.children.map((artifact, index) => (
+
+ ))}
+ >
+ )
+}
diff --git a/src/lang/abstractSyntaxTree.ts b/src/lang/abstractSyntaxTree.ts
index 66442e381..47fe73af7 100644
--- a/src/lang/abstractSyntaxTree.ts
+++ b/src/lang/abstractSyntaxTree.ts
@@ -1215,6 +1215,38 @@ export function addLine(
}
}
+export function changeArguments(
+ node: Program,
+ pathToNode: (string | number)[],
+ args: [number, number]
+): { modifiedAst: Program; pathToNode: (string | number)[] }{
+ const _node = { ...node }
+ const dumbyStartend = { start: 0, end: 0 }
+ // const thePath = getNodePathFromSourceRange(_node, sourceRange)
+ const callExpression = getNodeFromPath(_node, pathToNode) as CallExpression
+ const newXArg: CallExpression['arguments'][number] = callExpression.arguments[0].type === 'Literal' ? {
+ type: 'Literal',
+ ...dumbyStartend,
+ value: args[0],
+ raw: `${args[0]}`,
+ } : {
+ ...callExpression.arguments[0]
+ }
+ const newYArg: CallExpression['arguments'][number] = callExpression.arguments[1].type === 'Literal' ? {
+ type: 'Literal',
+ ...dumbyStartend,
+ value: args[1],
+ raw: `${args[1]}`,
+ } : {
+ ...callExpression.arguments[1]
+ }
+ callExpression.arguments = [newXArg, newYArg]
+ return {
+ modifiedAst: _node,
+ pathToNode,
+ }
+}
+
function isCallExpression(tokens: Token[], index: number): number {
const currentToken = tokens[index]
const veryNextToken = tokens[index + 1] // i.e. no whitespace
diff --git a/src/lang/engine.tsx b/src/lang/engine.tsx
index 517d3e494..38bfc7c79 100644
--- a/src/lang/engine.tsx
+++ b/src/lang/engine.tsx
@@ -7,15 +7,21 @@ export function baseGeo({ from }: { from: [number, number, number] }) {
return baseSphere
}
+export interface LineGeos {
+ line: BufferGeometry
+ tip: BufferGeometry
+ centre: BufferGeometry
+}
+
export function lineGeo({
from,
to,
}: {
from: [number, number, number]
to: [number, number, number]
-}): BufferGeometry {
+}): LineGeos {
const sq = (a: number): number => a * a
- const center = [
+ const centre = [
(from[0] + to[0]) / 2,
(from[1] + to[1]) / 2,
(from[2] + to[2]) / 2,
@@ -34,15 +40,23 @@ export function lineGeo({
const lineBody = new BoxGeometry(Hypotenuse3d, 0.1, 0.1)
lineBody.rotateY(ang1)
lineBody.rotateZ(ang2)
- lineBody.translate(center[0], center[1], center[2])
+ lineBody.translate(centre[0], centre[1], centre[2])
// create line end balls with SphereGeometry at `to` and `from` with radius of 0.15
const lineEnd1 = new SphereGeometry(0.15)
lineEnd1.translate(to[0], to[1], to[2])
+
+ const centreSphere = new SphereGeometry(0.15)
+ centreSphere.translate(centre[0], centre[1], centre[2])
// const lineEnd2 = new SphereGeometry(0.15);
// lineEnd2.translate(from[0], from[1], from[2])
// group all three geometries
- return mergeBufferGeometries([lineBody, lineEnd1])
+ // return mergeBufferGeometries([lineBody, lineEnd1])
// return mergeBufferGeometries([lineBody, lineEnd1, lineEnd2]);
+ return {
+ line: lineBody,
+ tip: lineEnd1,
+ centre: centreSphere,
+ }
}
diff --git a/src/lang/executor.ts b/src/lang/executor.ts
index 6a76e87ef..aaed9f76b 100644
--- a/src/lang/executor.ts
+++ b/src/lang/executor.ts
@@ -6,6 +6,7 @@ import {
} from './abstractSyntaxTree'
import { Path, Transform, SketchGeo, sketchFns } from './sketch'
import { BufferGeometry } from 'three'
+import { LineGeos } from './engine'
export interface ProgramMemory {
root: { [key: string]: any }
@@ -296,8 +297,13 @@ export type ViewerArtifact =
| {
type: 'sketchLine'
sourceRange: SourceRange
- geo: BufferGeometry
+ geo: LineGeos
}
+ | {
+ type: 'sketchBase',
+ sourceRange: SourceRange,
+ geo: BufferGeometry
+ }
| {
type: 'parent'
sourceRange: SourceRange
@@ -315,20 +321,44 @@ export const processShownObjects = (
previousTransforms: PreviousTransforms = []
): ViewerArtifact[] => {
if (geoMeta?.type === 'sketchGeo') {
- return geoMeta.sketch.map(({ geo, sourceRange }) => {
- const newGeo = geo.clone()
- previousTransforms.forEach(({ rotation, transform }) => {
- newGeo.rotateX(rotation[0])
- newGeo.rotateY(rotation[1])
- newGeo.rotateZ(rotation[2])
- newGeo.translate(transform[0], transform[1], transform[2])
- })
-
- return {
- type: 'sketchLine',
- geo: newGeo,
- sourceRange,
+ return geoMeta.sketch.map(({ geo, sourceRange, type }) => {
+ if(type === 'toPoint') {
+ // const newGeo = geo.clone()
+ const newGeo: LineGeos = {
+ line: geo.line.clone(),
+ tip: geo.tip.clone(),
+ centre: geo.centre.clone(),
+ }
+ previousTransforms.forEach(({ rotation, transform }) => {
+ Object.values(newGeo).forEach((geoItem) => {
+ geoItem.rotateX(rotation[0])
+ geoItem.rotateY(rotation[1])
+ geoItem.rotateZ(rotation[2])
+ geoItem.translate(transform[0], transform[1], transform[2])
+ })
+ })
+ return {
+ type: 'sketchLine',
+ geo: newGeo,
+ sourceRange,
+ }
+ } else if(type === 'base') {
+ const newGeo = geo.clone()
+ previousTransforms.forEach(({ rotation, transform }) => {
+ newGeo.rotateX(rotation[0])
+ newGeo.rotateY(rotation[1])
+ newGeo.rotateZ(rotation[2])
+ newGeo.translate(transform[0], transform[1], transform[2])
+ })
+ return {
+ type: 'sketchBase',
+ geo: newGeo,
+ sourceRange,
+ }
}
+ console.log('type',type)
+
+ throw new Error('Unknown geo type')
})
} else if (geoMeta.type === 'transform') {
const referencedVar = geoMeta.sketch
diff --git a/src/lang/sketch.ts b/src/lang/sketch.ts
index d6f1e5e72..3a7869714 100644
--- a/src/lang/sketch.ts
+++ b/src/lang/sketch.ts
@@ -1,5 +1,5 @@
import { ProgramMemory } from './executor'
-import { lineGeo, baseGeo } from './engine'
+import { lineGeo, baseGeo, LineGeos } from './engine'
import { BufferGeometry } from 'three'
type Coords2d = [number, number]
@@ -37,7 +37,7 @@ export type Path =
name?: string
to: Coords2d
previousPath: Path
- geo: BufferGeometry
+ geo: LineGeos
sourceRange: SourceRange
}
| {
@@ -45,7 +45,7 @@ export type Path =
name?: string
firstPath: Path
previousPath: Path
- geo: BufferGeometry
+ geo: LineGeos
sourceRange: SourceRange
}
| {