From 88a16e72486353589dd2a8e6668a83e2071aab11 Mon Sep 17 00:00:00 2001 From: Marcus Alagar Date: Sun, 21 Sep 2025 01:56:23 -0700 Subject: [PATCH 1/2] feat: experimental 3D view Implement 3D viewer of current design in new "3D" tab by using `solid-three`, specifically its `next` branch. Used `Entity` pattern as opposed to `createT` pattern mainly as personal preference. Also added lighting using (admittedly) magic numbers to give visual contrast to 3D. Also used magic numbers to get in frame on own machine. --- package-lock.json | 90 +++++++++++++++++++++++++++++++++++- package.json | 1 + src/components/Isometric.tsx | 49 ++++++++++++++++++++ src/components/MainView.tsx | 12 ++++- 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 src/components/Isometric.tsx diff --git a/package-lock.json b/package-lock.json index b9d5f4a..279cec9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "plotly.js-basic-dist": "^2.15.1", "solid-js": "^1.9.9", "solid-record": "^0.2.1", + "solid-three": "^0.3.0-next.11", "three": "^0.149.0", "vinxi": "^0.5.8", "vite": "^6.1.1" @@ -2240,6 +2241,66 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@solid-primitives/event-listener": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@solid-primitives/event-listener/-/event-listener-2.4.3.tgz", + "integrity": "sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg==", + "license": "MIT", + "dependencies": { + "@solid-primitives/utils": "^6.3.2" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/resize-observer": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@solid-primitives/resize-observer/-/resize-observer-2.1.3.tgz", + "integrity": "sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ==", + "license": "MIT", + "dependencies": { + "@solid-primitives/event-listener": "^2.4.3", + "@solid-primitives/rootless": "^1.5.2", + "@solid-primitives/static-store": "^0.1.2", + "@solid-primitives/utils": "^6.3.2" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/rootless": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@solid-primitives/rootless/-/rootless-1.5.2.tgz", + "integrity": "sha512-9HULb0QAzL2r47CCad0M+NKFtQ+LrGGNHZfteX/ThdGvKIg2o2GYhBooZubTCd/RTu2l2+Nw4s+dEfiDGvdrrQ==", + "license": "MIT", + "dependencies": { + "@solid-primitives/utils": "^6.3.2" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/static-store": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@solid-primitives/static-store/-/static-store-0.1.2.tgz", + "integrity": "sha512-ReK+5O38lJ7fT+L6mUFvUr6igFwHBESZF+2Ug842s7fvlVeBdIVEdTCErygff6w7uR6+jrr7J8jQo+cYrEq4Iw==", + "license": "MIT", + "dependencies": { + "@solid-primitives/utils": "^6.3.2" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/utils": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/@solid-primitives/utils/-/utils-6.3.2.tgz", + "integrity": "sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ==", + "license": "MIT", + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, "node_modules/@solidjs/start": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@solidjs/start/-/start-1.2.0.tgz", @@ -2628,7 +2689,6 @@ "version": "0.149.0", "resolved": "https://registry.npmjs.org/@types/three/-/three-0.149.0.tgz", "integrity": "sha512-fgNBm9LWc65ER/W0cvoXdC0iMy7Ke9e2CONmEr6Jt8sDSY3sw4DgOubZfmdZ747dkPhbQrgRQAWwDEr2S/7IEg==", - "dev": true, "license": "MIT", "dependencies": { "@types/webxr": "*" @@ -2644,7 +2704,6 @@ "version": "0.5.23", "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.23.tgz", "integrity": "sha512-GPe4AsfOSpqWd3xA/0gwoKod13ChcfV67trvxaW2krUbgb9gxQjnCx8zGshzMl8LSHZlNH5gQ8LNScsDuc7nGQ==", - "dev": true, "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -4521,6 +4580,18 @@ } } }, + "node_modules/debounce": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz", + "integrity": "sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -10417,6 +10488,21 @@ "solid-js": "^1.3" } }, + "node_modules/solid-three": { + "version": "0.3.0-next.11", + "resolved": "https://registry.npmjs.org/solid-three/-/solid-three-0.3.0-next.11.tgz", + "integrity": "sha512-nai9Sf5zEO+v8JZGauB5PMt36qlj6wdef9OXfUHkh91pNDClr4i8hhhSjTEYxNzzD6nYk9Ov1Q+gh4zEgjKdsg==", + "license": "MIT", + "dependencies": { + "@solid-primitives/resize-observer": "^2.0.25", + "debounce": "^2.1.0" + }, + "peerDependencies": { + "@types/three": "*", + "solid-js": "*", + "three": "*" + } + }, "node_modules/solid-use": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/solid-use/-/solid-use-0.9.1.tgz", diff --git a/package.json b/package.json index 943e53f..d5f8509 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "plotly.js-basic-dist": "^2.15.1", "solid-js": "^1.9.9", "solid-record": "^0.2.1", + "solid-three": "^0.3.0-next.11", "three": "^0.149.0", "vinxi": "^0.5.8", "vite": "^6.1.1" diff --git a/src/components/Isometric.tsx b/src/components/Isometric.tsx new file mode 100644 index 0000000..85cf829 --- /dev/null +++ b/src/components/Isometric.tsx @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 + +import { For } from 'solid-js'; +import { Canvas, Entity } from 'solid-three'; +import * as THREE from 'three'; +import { layout, rectLayer, rectViaLayer } from '../model/layout'; + +export default function Isometric() { + const rects = () => layout.rects; + + return ( + + + + {(rect) => { + const layer = rectLayer(rect); + const viaLayer = rectViaLayer(layout, rect); + if (!viaLayer || !layer) { + return ; + } + + return ( + + + + + ); + }} + + + + + + ); +} diff --git a/src/components/MainView.tsx b/src/components/MainView.tsx index 0ab8f76..ab976ae 100644 --- a/src/components/MainView.tsx +++ b/src/components/MainView.tsx @@ -10,9 +10,10 @@ import CrossSection from './CrossSection'; import DRCList from './DRCList'; import Editor from './Editor'; import Layers from './Layers'; +import Isometric from './Isometric'; import SimulationParams from './SimulationParams'; -type ITabName = 'xsection' | 'simulation'; +type ITabName = 'xsection' | 'simulation' | '3d'; export default function MainView() { const [drc, setDRC] = createSignal(); @@ -43,6 +44,12 @@ export default function MainView() { +