diff --git a/src/App/App.tsx b/src/App/App.tsx index 48ce3ad1..dcc497f9 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -23,6 +23,7 @@ import { NodeResult } from "../Viewer/objects/NodeResults"; import { EditorBar } from "../EditorBar/EditorBar"; import { Parameters, ParametersType } from "../Parameters/Parameters"; import { Login, supabase } from "../Login/Login"; +import { Axes } from "../Viewer/objects/Axes"; export const staging = localStorage.getItem("staging") ? true : false; @@ -264,6 +265,10 @@ export const analysisResults = analyzing(nodes, elements, assignments);`; + diff --git a/src/Viewer/Viewer.stories.tsx b/src/Viewer/Viewer.stories.tsx index 4b86f47e..e9efba05 100644 --- a/src/Viewer/Viewer.stories.tsx +++ b/src/Viewer/Viewer.stories.tsx @@ -8,6 +8,7 @@ import { NodeLoad } from "./objects/NodeLoad"; import { ElementResult } from "./objects/ElementResult"; import { Text } from "./objects/Text"; import { NodeResult } from "./objects/NodeResults"; +import { Axes } from "./objects/Axes"; export const Default: StoryObj = {}; @@ -18,6 +19,8 @@ export default { + + diff --git a/src/Viewer/objects/Axes.tsx b/src/Viewer/objects/Axes.tsx new file mode 100644 index 00000000..79418c71 --- /dev/null +++ b/src/Viewer/objects/Axes.tsx @@ -0,0 +1,124 @@ +import * as THREE from "three"; +import { Text } from "./Text"; +import { createEffect, createSignal, onCleanup } from "solid-js"; + +type AxesProps = { + position: any; + size: number; +}; + +export function Axes(props: AxesProps) { + const [xTextPosition, setXTextPosition] = createSignal([0, 0, 0]); + const [yTextPosition, setYTextPosition] = createSignal([0, 0, 0]); + const [zTextPosition, setZTextPosition] = createSignal([0, 0, 0]); + + if ( + !props.position || + props.position.length != 3 || + props.position.some((element: any) => typeof element !== "number") || + props.position.flat().length != props.position.length + ) + return; + + const xArrow = new THREE.ArrowHelper( + new THREE.Vector3(1, 0, 0), + new THREE.Vector3(0, 0, 0), + 1, + 0x666666, + 0.2, + 0.2 + ); + const yArrow = new THREE.ArrowHelper( + new THREE.Vector3(0, 0, -1), + new THREE.Vector3(0, 0, 0), + 1, + 0x666666, + 0.2, + 0.2 + ); + const zArrow = new THREE.ArrowHelper( + new THREE.Vector3(0, 1, 0), + new THREE.Vector3(0, 0, 0), + 1, + 0x666666, + 0.2, + 0.2 + ); + + // on position change + createEffect(() => { + const swapYZPosition: [number, number, number] = [ + props.position[0], + props.position[2], + props.position[1], + ]; + xArrow.position.copy(new THREE.Vector3(...swapYZPosition)); + yArrow.position.copy(new THREE.Vector3(...swapYZPosition)); + zArrow.position.copy(new THREE.Vector3(...swapYZPosition)); + }); + + // on size change + createEffect(() => { + xArrow.scale.set(props.size, props.size, props.size); + yArrow.scale.set(props.size, props.size, props.size); + zArrow.scale.set(props.size, props.size, props.size); + + const textOffset = 1.3; + setXTextPosition( + new THREE.Vector3(textOffset * props.size, 0, 0) + .add(new THREE.Vector3(...props.position)) + .toArray() + ); + setYTextPosition( + new THREE.Vector3(0, -textOffset * props.size, 0) + .add(new THREE.Vector3(...props.position)) + .toArray() + ); + setZTextPosition( + new THREE.Vector3(0, 0, textOffset * props.size) + .add(new THREE.Vector3(...props.position)) + .toArray() + ); + }); + + onCleanup(() => { + xArrow.dispose(); + yArrow.dispose(); + zArrow.dispose(); + }); + + return ( + <> + {xArrow} + {yArrow} + {zArrow} + { + + } + { + + } + { + + } + + ); +} diff --git a/src/Viewer/objects/Text.tsx b/src/Viewer/objects/Text.tsx index 32bc4f9f..9d665df4 100644 --- a/src/Viewer/objects/Text.tsx +++ b/src/Viewer/objects/Text.tsx @@ -5,6 +5,8 @@ type TextProps = { text: any; position: any; size: number; + color?: string; + background?: string; }; export function Text(props: TextProps) { @@ -21,7 +23,13 @@ export function Text(props: TextProps) { const resolution = 100; const material = new THREE.SpriteMaterial(); - material.map = createTexture(props.text, props.size, resolution); + material.map = createTexture( + props.text, + props.size, + resolution, + props.color, + props.background + ); material.depthTest = false; const text = new THREE.Sprite(material); @@ -35,7 +43,13 @@ export function Text(props: TextProps) { // on text change or size change createEffect(() => { material.map?.dispose(); - material.map = createTexture(props.text, props.size, resolution); + material.map = createTexture( + props.text, + props.size, + resolution, + props.color, + props.background + ); text.scale.set( material.map.image.width / resolution / devicePixelRatio, props.size, @@ -57,7 +71,13 @@ export function Text(props: TextProps) { return <>{text}; } -function createTexture(text: string, size: number, resolution: number) { +function createTexture( + text: string, + size: number, + resolution: number, + color: string | undefined, + background: string | undefined +) { const fontHeightPx = resolution * size * devicePixelRatio; const canvas = document.createElement("canvas"); @@ -68,12 +88,12 @@ function createTexture(text: string, size: number, resolution: number) { canvas.width = ctx.measureText(text).width; canvas.height = fontHeightPx; - ctx.fillStyle = "#0d0d0d"; + if (background != "transparent") ctx.fillStyle = background || "#0d0d0d"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.textAlign = "center"; ctx.textBaseline = "middle"; - ctx.fillStyle = "#ffffff"; + ctx.fillStyle = color || "#ffffff"; const toMargin = 0.9; ctx.font = `${fontHeightPx * toMargin}px Arial`; const toCenterTextV = 0.08 * canvas.height;