sketch code to 3d scene initial connection working
This commit is contained in:
@ -26,6 +26,9 @@
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"jest": {
|
||||
"transformIgnorePatterns": ["node_modules/(?!(three)/)"]
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
@ -45,6 +48,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/three": "^0.146.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"postcss": "^8.4.19",
|
||||
"tailwindcss": "^3.2.4"
|
||||
|
138
src/App.tsx
138
src/App.tsx
@ -1,69 +1,65 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { Allotment } from "allotment";
|
||||
import { OrbitControls, OrthographicCamera } from "@react-three/drei";
|
||||
import "allotment/dist/style.css";
|
||||
import { lexer } from "./lang/tokeniser";
|
||||
import { abstractSyntaxTree } from "./lang/abstractSyntaxTree";
|
||||
import { executor } from "./lang/executor";
|
||||
import { BufferGeometry } from "three";
|
||||
// import { Box } from "./lang/engine";
|
||||
|
||||
function Box({
|
||||
from,
|
||||
to,
|
||||
}: {
|
||||
from: [number, number, number];
|
||||
to: [number, number, number];
|
||||
}) {
|
||||
// This reference will give us direct access to the mesh
|
||||
const mesh = useRef<{ rotation: { x: number } }>();
|
||||
// Set up state for the hovered and active state
|
||||
const [hovered, setHover] = useState(false);
|
||||
const [active, setActive] = useState(false);
|
||||
|
||||
const sq = (a: number): number => a * a;
|
||||
const center = [
|
||||
(from[0] + to[0]) / 2,
|
||||
(from[1] + to[1]) / 2,
|
||||
(from[2] + to[2]) / 2,
|
||||
];
|
||||
const Hypotenuse3d = Math.sqrt(
|
||||
sq(from[0] - to[0]) + sq(from[1] - to[1]) + sq(from[2] - to[2])
|
||||
);
|
||||
const ang1 = -Math.atan2(from[2] - to[2], from[0] - to[0]);
|
||||
const Hypotenuse2d = Math.sqrt(sq(from[0] - to[0]) + sq(from[2] - to[2]));
|
||||
const ang2 = -Math.atan2(to[1] - from[1], Hypotenuse2d);
|
||||
return (
|
||||
<group>
|
||||
<mesh
|
||||
position={center}
|
||||
rotation={[0, ang1, ang2]}
|
||||
ref={mesh}
|
||||
onClick={(event) => setActive(!active)}
|
||||
onPointerOver={(event) => setHover(true)}
|
||||
onPointerOut={(event) => setHover(false)}
|
||||
>
|
||||
<boxGeometry args={[Hypotenuse3d, 0.1, 0.1]} />
|
||||
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
|
||||
</mesh>
|
||||
<mesh position={to}>
|
||||
<sphereGeometry args={[0.15, 8, 8]} />
|
||||
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
|
||||
</mesh>
|
||||
<mesh position={from}>
|
||||
<sphereGeometry args={[0.15, 8, 8]} />
|
||||
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
const _code = `sketch mySketch {
|
||||
path myPath = lineTo(0,1)
|
||||
lineTo(1,5)
|
||||
path rightPath = lineTo(1,0)
|
||||
close()
|
||||
}
|
||||
show(mySketch)`;
|
||||
|
||||
const OrrthographicCamera = OrthographicCamera as any;
|
||||
|
||||
function App() {
|
||||
const cam = useRef();
|
||||
const [code, setCode] = useState(_code);
|
||||
const [geoArray, setGeoArray] = useState<
|
||||
{ geo: BufferGeometry; sourceRange: [number, number] }[]
|
||||
>([]);
|
||||
useEffect(() => {
|
||||
try {
|
||||
const tokens = lexer(code);
|
||||
const ast = abstractSyntaxTree(tokens);
|
||||
const programMemory = executor(ast);
|
||||
const geos: { geo: BufferGeometry; sourceRange: [number, number] }[] =
|
||||
programMemory.root.mySketch
|
||||
.map(
|
||||
({
|
||||
geo,
|
||||
sourceRange,
|
||||
}: {
|
||||
geo: BufferGeometry;
|
||||
sourceRange: [number, number];
|
||||
}) => ({ geo, sourceRange })
|
||||
)
|
||||
.filter((a: any) => !!a.geo);
|
||||
setGeoArray(geos);
|
||||
console.log("length", geos.length, geos);
|
||||
console.log(programMemory);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}, [code]);
|
||||
return (
|
||||
<div className="h-screen">
|
||||
<Allotment>
|
||||
<div className="bg-red h-full">
|
||||
editor
|
||||
<textarea />
|
||||
<textarea
|
||||
className="w-full p-4 h-64 font-mono"
|
||||
onChange={(a) => setCode(a.target.value)}
|
||||
value={code}
|
||||
>
|
||||
{code}
|
||||
</textarea>
|
||||
</div>
|
||||
<div className="h-full">
|
||||
viewer
|
||||
@ -84,11 +80,16 @@ function App() {
|
||||
/>
|
||||
<ambientLight />
|
||||
<pointLight position={[10, 10, 10]} />
|
||||
<Box from={[6, 6, 6]} to={[0, 1, 5]} />
|
||||
<mesh>
|
||||
<boxGeometry args={[0.1, 0.2, 1]} />
|
||||
<meshStandardMaterial color={"hotpink"} />
|
||||
</mesh>
|
||||
{geoArray.map(
|
||||
(
|
||||
{
|
||||
geo,
|
||||
sourceRange,
|
||||
}: { geo: BufferGeometry; sourceRange: [number, number] },
|
||||
index
|
||||
) => <Line key={index} geo={geo} sourceRange={sourceRange} />
|
||||
|
||||
)}
|
||||
</Canvas>
|
||||
</div>
|
||||
</Allotment>
|
||||
@ -97,3 +98,28 @@ function App() {
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
function Line({
|
||||
geo,
|
||||
sourceRange,
|
||||
}: {
|
||||
geo: BufferGeometry;
|
||||
sourceRange: [number, number];
|
||||
}) {
|
||||
// This reference will give us direct access to the mesh
|
||||
// const ref = useRef<Mesh<BufferGeometry | Material | Material[]> | undefined>();
|
||||
const ref = useRef<BufferGeometry | undefined>() as any;
|
||||
// Set up state for the hovered and active state
|
||||
const [hovered, setHover] = useState(false);
|
||||
|
||||
return (
|
||||
<mesh
|
||||
ref={ref}
|
||||
onPointerOver={(event) => setHover(true)}
|
||||
onPointerOut={(event) => setHover(false)}
|
||||
>
|
||||
<primitive object={geo} />
|
||||
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
40
src/lang/engine.tsx
Normal file
40
src/lang/engine.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { BoxGeometry, SphereGeometry, BufferGeometry } from 'three'
|
||||
import {mergeBufferGeometries} from 'three/examples/jsm/utils/BufferGeometryUtils'
|
||||
|
||||
export function lineGeo({
|
||||
from,
|
||||
to,
|
||||
}: {
|
||||
from: [number, number, number];
|
||||
to: [number, number, number];
|
||||
}): BufferGeometry {
|
||||
const sq = (a: number): number => a * a;
|
||||
const center = [
|
||||
(from[0] + to[0]) / 2,
|
||||
(from[1] + to[1]) / 2,
|
||||
(from[2] + to[2]) / 2,
|
||||
];
|
||||
const Hypotenuse3d = Math.sqrt(
|
||||
sq(from[0] - to[0]) + sq(from[1] - to[1]) + sq(from[2] - to[2])
|
||||
);
|
||||
const ang1 = Math.atan2(from[2] - to[2], from[0] - to[0]);
|
||||
const Hypotenuse2d = Math.sqrt(sq(from[0] - to[0]) + sq(from[2] - to[2]));
|
||||
const ang2 = Math.abs(Math.atan((to[1] - from[1])/ Hypotenuse2d))*Math.sign(to[1] - from[1])*(Math.sign(to[0] - from[0])||1);
|
||||
|
||||
// create BoxGeometry with size [Hypotenuse3d, 0.1, 0.1] centered at center, with rotation of [0, ang1, ang2]
|
||||
const lineBody = new BoxGeometry(Hypotenuse3d, 0.1, 0.1);
|
||||
lineBody.rotateY(ang1)
|
||||
lineBody.rotateZ(ang2)
|
||||
lineBody.translate(center[0], center[1], center[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 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, lineEnd2]);
|
||||
}
|
||||
|
@ -62,15 +62,19 @@ log(5, myVar)`;
|
||||
show(mySketch)
|
||||
`;
|
||||
const { root, return: _return } = exe(code);
|
||||
expect(root.mySketch.map(({ previousPath, ...rest }: any) => rest)).toEqual(
|
||||
[
|
||||
expect(
|
||||
root.mySketch.map(({ previousPath, geo, ...rest }: any) => rest)
|
||||
).toEqual([
|
||||
{ type: "base", from: [0, 0] },
|
||||
{ type: "toPoint", to: [0, 1], name: "myPath" },
|
||||
{ type: "toPoint", to: [1, 1] },
|
||||
{ type: "toPoint", to: [1, 0], name: "rightPath" },
|
||||
{ type: "close", firstPath: { type: "base", from: [0, 0] } },
|
||||
]
|
||||
);
|
||||
{ type: "toPoint", to: [0, 1], sourceRange: [25, 45], name: "myPath" },
|
||||
{ type: "toPoint", to: [1, 1], sourceRange: [48, 59] },
|
||||
{ type: "toPoint", to: [1, 0], sourceRange: [67, 90], name: "rightPath" },
|
||||
{
|
||||
type: "close",
|
||||
firstPath: { type: "base", from: [0, 0] },
|
||||
sourceRange: [93, 100],
|
||||
},
|
||||
]);
|
||||
expect(root.mySketch[0]).toEqual(root.mySketch[4].firstPath);
|
||||
// hmm not sure what handle the "show" function
|
||||
expect(_return).toEqual([
|
||||
|
@ -85,6 +85,7 @@ export const executor = (
|
||||
const result = sketchFns[fnName](
|
||||
_programMemory,
|
||||
variableName,
|
||||
[declaration.start, declaration.end],
|
||||
...fnArgs
|
||||
);
|
||||
_programMemory._sketch = result.programMemory._sketch;
|
||||
@ -113,7 +114,7 @@ export const executor = (
|
||||
`Cannot call ${functionName} outside of a sketch declaration`
|
||||
);
|
||||
}
|
||||
const result = sketchFns[functionName](_programMemory, "", ...args);
|
||||
const result = sketchFns[functionName](_programMemory, "", [statement.start, statement.end], ...args);
|
||||
_programMemory._sketch = [...result.programMemory._sketch];
|
||||
} else if("show" === functionName) {
|
||||
if (options.bodyType !== "root") {
|
||||
|
@ -1,39 +1,54 @@
|
||||
import { ProgramMemory } from "./executor";
|
||||
import { lineGeo } from "./engine";
|
||||
import { BufferGeometry } from 'three'
|
||||
|
||||
type Coords2d = [number, number]
|
||||
type SourceRange = [number, number]
|
||||
|
||||
export type Path =
|
||||
| {
|
||||
type: "points";
|
||||
name?: string;
|
||||
from: [number, number];
|
||||
to: [number, number];
|
||||
from: Coords2d;
|
||||
to: Coords2d;
|
||||
geo: BufferGeometry;
|
||||
sourceRange: SourceRange;
|
||||
}
|
||||
| {
|
||||
type: "horizontalLineTo";
|
||||
name?: string;
|
||||
x: number;
|
||||
previousPath: Path;
|
||||
geo: BufferGeometry;
|
||||
sourceRange: SourceRange;
|
||||
}
|
||||
| {
|
||||
type: "verticalLineTo";
|
||||
name?: string;
|
||||
y: number;
|
||||
previousPath: Path;
|
||||
geo: BufferGeometry;
|
||||
sourceRange: SourceRange;
|
||||
}
|
||||
| {
|
||||
type: "toPoint";
|
||||
name?: string;
|
||||
to: [number, number];
|
||||
to: Coords2d;
|
||||
previousPath: Path;
|
||||
geo: BufferGeometry;
|
||||
sourceRange: SourceRange;
|
||||
}
|
||||
| {
|
||||
type: "close";
|
||||
name?: string;
|
||||
firstPath: Path;
|
||||
previousPath: Path;
|
||||
geo: BufferGeometry;
|
||||
sourceRange: SourceRange;
|
||||
}
|
||||
| {
|
||||
type: "base";
|
||||
from: [number, number];
|
||||
from: Coords2d;
|
||||
};
|
||||
|
||||
function addBasePath(programMemory: ProgramMemory) {
|
||||
@ -55,19 +70,46 @@ interface PathReturn {
|
||||
currentPath: Path;
|
||||
}
|
||||
|
||||
function getCoordsFromPaths(paths: Path[], index = 0): Coords2d {
|
||||
const currentPath = paths[index]
|
||||
if(!currentPath) {
|
||||
return [0, 0]
|
||||
}
|
||||
if(currentPath.type === 'points' || currentPath.type === 'toPoint') {
|
||||
return currentPath.to
|
||||
} else if (currentPath.type === 'base') {
|
||||
return currentPath.from
|
||||
} else if (currentPath.type === 'horizontalLineTo') {
|
||||
const pathBefore = getCoordsFromPaths(paths, index - 1)
|
||||
return [currentPath.x, pathBefore[1]]
|
||||
} else if (currentPath.type === 'verticalLineTo') {
|
||||
const pathBefore = getCoordsFromPaths(paths, index - 1)
|
||||
return [pathBefore[0], currentPath.y]
|
||||
}
|
||||
return [0,0]
|
||||
}
|
||||
|
||||
export const sketchFns = {
|
||||
close: (programMemory: ProgramMemory, name: string = ""): PathReturn => {
|
||||
close: (programMemory: ProgramMemory, name: string = "", sourceRange: SourceRange): PathReturn => {
|
||||
const lastPath = programMemory?._sketch?.[
|
||||
programMemory?._sketch.length - 1
|
||||
] as Path;
|
||||
|
||||
let from = getCoordsFromPaths(programMemory?._sketch, programMemory?._sketch.length - 1)
|
||||
const firstPath = programMemory?._sketch?.[0] as Path;
|
||||
if (lastPath?.type === "base") {
|
||||
throw new Error("Cannot close a base path");
|
||||
}
|
||||
let to = getCoordsFromPaths(programMemory?._sketch, 0)
|
||||
|
||||
|
||||
|
||||
const newPath: Path = {
|
||||
type: "close",
|
||||
firstPath,
|
||||
previousPath: lastPath,
|
||||
geo: lineGeo({from: [...from, 0], to: [...to, 0]}),
|
||||
sourceRange
|
||||
};
|
||||
if (name) {
|
||||
newPath.name = name;
|
||||
@ -83,6 +125,7 @@ export const sketchFns = {
|
||||
lineTo: (
|
||||
programMemory: ProgramMemory,
|
||||
name: string = "",
|
||||
sourceRange: SourceRange,
|
||||
...args: any[]
|
||||
): PathReturn => {
|
||||
const _programMemory = addBasePath(programMemory);
|
||||
@ -92,10 +135,13 @@ export const sketchFns = {
|
||||
}
|
||||
const lastPath: Path =
|
||||
_programMemory._sketch[_programMemory._sketch.length - 1];
|
||||
let from = getCoordsFromPaths(programMemory?._sketch, programMemory?._sketch.length - 1)
|
||||
const currentPath: Path = {
|
||||
type: "toPoint",
|
||||
to: [x, y],
|
||||
previousPath: lastPath,
|
||||
geo: lineGeo({from: [...from, 0], to: [x, y, 0]}),
|
||||
sourceRange
|
||||
};
|
||||
if (name) {
|
||||
currentPath.name = name;
|
||||
|
12
yarn.lock
12
yarn.lock
@ -2492,11 +2492,23 @@
|
||||
dependencies:
|
||||
"@types/jest" "*"
|
||||
|
||||
"@types/three@^0.146.0":
|
||||
version "0.146.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/three/-/three-0.146.0.tgz#83813ba0d2fff6bdc6d7fda3a77993a932bba45f"
|
||||
integrity sha512-75AgysUrIvTCB054eQa2pDVFurfeFW8CrMQjpzjt3yHBfuuknoSvvsESd/3EhQxPrz9si3+P0wiDUVsWUlljfA==
|
||||
dependencies:
|
||||
"@types/webxr" "*"
|
||||
|
||||
"@types/trusted-types@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
|
||||
integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
|
||||
|
||||
"@types/webxr@*":
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.0.tgz#aae1cef3210d88fd4204f8c33385a0bbc4da07c9"
|
||||
integrity sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==
|
||||
|
||||
"@types/ws@^8.5.1":
|
||||
version "8.5.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
|
||||
|
Reference in New Issue
Block a user