From c6c05e01b9572fbede4fea00fd1b4ba06dbd1703 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 21 Jun 2023 15:49:12 +0100 Subject: [PATCH 001/132] wip getting blocks in a worker --- app/api/trpc/[trpc]/route.ts | 3 -- app/design/state/transients/transforms.ts | 2 +- app/design/ui-3d/grouped/GroupedApp.tsx | 2 +- app/design/ui/menu/interactions/Exporters.tsx | 2 +- .../{state => workers}/exporters/index.ts | 2 +- .../{state => workers}/exporters/worker.ts | 0 app/layout.tsx | 32 +++++++------ app/ui/TrpcProvider.tsx | 2 +- app/workers/systems/index.ts | 20 ++++++++ app/workers/systems/worker.ts | 47 +++++++++++++++++++ app/workers/utils/index.ts | 46 ++++++++++++++++++ client/trpc.ts | 19 ++++++++ 12 files changed, 155 insertions(+), 22 deletions(-) rename app/design/{state => workers}/exporters/index.ts (98%) rename app/design/{state => workers}/exporters/worker.ts (100%) create mode 100644 app/workers/systems/index.ts create mode 100644 app/workers/systems/worker.ts create mode 100644 app/workers/utils/index.ts diff --git a/app/api/trpc/[trpc]/route.ts b/app/api/trpc/[trpc]/route.ts index 9e46f510..6d482890 100644 --- a/app/api/trpc/[trpc]/route.ts +++ b/app/api/trpc/[trpc]/route.ts @@ -1,9 +1,6 @@ -import { initTRPC } from "@trpc/server" import { fetchRequestHandler } from "@trpc/server/adapters/fetch" import { appRouter } from "@/server/trpc/router" -const t = initTRPC.create() - export type AppRouter = typeof appRouter const handler = async (request: Request) => { diff --git a/app/design/state/transients/transforms.ts b/app/design/state/transients/transforms.ts index da9b92f5..69a3042e 100644 --- a/app/design/state/transients/transforms.ts +++ b/app/design/state/transients/transforms.ts @@ -6,7 +6,7 @@ import { proxy } from "valtio" import { useSubscribeKey } from "~/utils/hooks" import { yAxis } from "~/utils/three" import dimensions, { collideOBB, useComputeDimensions } from "../dimensions" -import { dispatchUpdateExportModelsEvent } from "../exporters" +import { dispatchUpdateExportModelsEvent } from "../../workers/exporters" import houses from "../houses" export type Transforms = { diff --git a/app/design/ui-3d/grouped/GroupedApp.tsx b/app/design/ui-3d/grouped/GroupedApp.tsx index e5b50af4..f6a7be5c 100644 --- a/app/design/ui-3d/grouped/GroupedApp.tsx +++ b/app/design/ui-3d/grouped/GroupedApp.tsx @@ -4,7 +4,7 @@ import { Fragment, Suspense } from "react" import { usePreviews } from "~/design/state/previews" import { useRouting } from "~/design/state/routing" import { RA } from "~/utils/functions" -import { useExportersWorker } from "../../state/exporters" +import { useExportersWorker } from "../../workers/exporters" import { useDragHandler, useGestures } from "../../state/gestures" import { useHouseKeys } from "../../state/houses" import XZPlane from "../XZPlane" diff --git a/app/design/ui/menu/interactions/Exporters.tsx b/app/design/ui/menu/interactions/Exporters.tsx index 0a1886c8..e651457d 100644 --- a/app/design/ui/menu/interactions/Exporters.tsx +++ b/app/design/ui/menu/interactions/Exporters.tsx @@ -1,5 +1,5 @@ import { ArrowDown } from "@carbon/icons-react" -import { dispatchGetModelEvent } from "../../../state/exporters" +import { dispatchGetModelEvent } from "../../../workers/exporters" import ContextMenuButton from "../ContextMenuButton" import ContextMenuNested from "../ContextMenuNested" diff --git a/app/design/state/exporters/index.ts b/app/design/workers/exporters/index.ts similarity index 98% rename from app/design/state/exporters/index.ts rename to app/design/workers/exporters/index.ts index fd69f2e8..a20839ad 100644 --- a/app/design/state/exporters/index.ts +++ b/app/design/workers/exporters/index.ts @@ -1,7 +1,7 @@ import { Remote, wrap } from "comlink" import { useEffect, useRef } from "react" import { useEvent } from "react-use" -import houses from "../houses" +import houses from "../../state/houses" import { ExportersWorkerAPI } from "./worker" export const UPDATE_EXPORT_MODELS_EVENT = "UpdateExportModels" diff --git a/app/design/state/exporters/worker.ts b/app/design/workers/exporters/worker.ts similarity index 100% rename from app/design/state/exporters/worker.ts rename to app/design/workers/exporters/worker.ts diff --git a/app/layout.tsx b/app/layout.tsx index bca59b3c..70f9fcb5 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,9 +1,10 @@ import clsx from "clsx" import { Inter } from "next/font/google" -import { PropsWithChildren } from "react" +import { Fragment, PropsWithChildren } from "react" import "~/styles/globals.css" import Footer from "./ui/Footer" import Nav from "./ui/Nav" +import { SystemsWorker } from "./workers/systems" const inter = Inter({ subsets: ["latin"], @@ -12,19 +13,22 @@ const inter = Inter({ const Layout = ({ children }: PropsWithChildren<{}>) => { return ( - - -
-
-
- {children} -
-
-
-
- - + + + +
+
+
+ {children} +
+
+
+
+ + + +
) } diff --git a/app/ui/TrpcProvider.tsx b/app/ui/TrpcProvider.tsx index d3a44152..351acc3f 100644 --- a/app/ui/TrpcProvider.tsx +++ b/app/ui/TrpcProvider.tsx @@ -1,5 +1,4 @@ "use client" - import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { httpBatchLink } from "@trpc/client" import { useState } from "react" @@ -17,6 +16,7 @@ export const TrpcProvider: React.FC<{ children: React.ReactNode }> = (p) => { ], }) ) + return ( diff --git a/app/workers/systems/index.ts b/app/workers/systems/index.ts new file mode 100644 index 00000000..13ec6022 --- /dev/null +++ b/app/workers/systems/index.ts @@ -0,0 +1,20 @@ +"use client" +import { releaseProxy, Remote, wrap } from "comlink" +import { useEffect, useRef } from "react" +import { SystemsAPI } from "./worker" + +export const SystemsWorker = () => { + const ref = useRef | null>(null) + + useEffect(() => { + const worker = new Worker(new URL("./worker.ts", import.meta.url)) + ref.current = wrap(worker) + + return () => { + ref.current?.[releaseProxy]() + worker.terminate() + } + }, []) + + return null +} diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts new file mode 100644 index 00000000..9df345d5 --- /dev/null +++ b/app/workers/systems/worker.ts @@ -0,0 +1,47 @@ +import { TRPCContext } from "@trpc/react-query/shared" +import Airtable from "airtable" +import { expose } from "comlink" +import Dexie from "dexie" +import { trpc, vanillaTrpc } from "../../../client/trpc" +import { Block, blocksQuery } from "../../../server/data/blocks" + +console.log("HI IM A WORKER") + +class SystemsDatabase extends Dexie { + blocks: Dexie.Table // "string" is the type of the primary key + + constructor() { + super("SystemsDatabase") + this.version(1).stores({ + blocks: "id,systemId,name", + }) + this.blocks = this.table("blocks") + } +} + +// Create Dexie database +const db = new SystemsDatabase() + +const init = async () => { + const blocks = await vanillaTrpc.blocks.query() + console.log(blocks) +} + +init() + +const getBlocks = async (systemIds: string[] = []) => { + console.log("getBlocks") + if (systemIds.length === 0) { + return [] + } else { + return db.blocks.toArray() + } +} + +const api = { + getBlocks, +} + +export type SystemsAPI = typeof api + +expose(api) diff --git a/app/workers/utils/index.ts b/app/workers/utils/index.ts new file mode 100644 index 00000000..757f5cdd --- /dev/null +++ b/app/workers/utils/index.ts @@ -0,0 +1,46 @@ +import { useEffect, useRef } from "react" +import { Remote, wrap, releaseProxy } from "comlink" + +export const useWorker = (workerURL: URL) => { + const ref = useRef | null>(null) + + useEffect(() => { + const worker = new Worker(workerURL) + ref.current = wrap(worker) + + return () => { + ref.current?.[releaseProxy]() + worker.terminate() + } + }, [workerURL]) + + return ref +} + +export const useSharedWorker = (workerURL: URL) => { + const ref = useRef | null>(null) + + useEffect(() => { + let worker: SharedWorker | Worker + try { + worker = new SharedWorker(workerURL) + ref.current = wrap(worker.port) + worker.port.start() + } catch (error) { + // Fallback to a regular Worker if SharedWorker is not supported + worker = new Worker(workerURL) + ref.current = wrap(worker) + } + + return () => { + ref.current?.[releaseProxy]() + if (worker instanceof SharedWorker) { + worker.port.close() + } else { + worker.terminate() + } + } + }, [workerURL]) + + return ref +} diff --git a/client/trpc.ts b/client/trpc.ts index ad1bc54d..49e116c9 100644 --- a/client/trpc.ts +++ b/client/trpc.ts @@ -1,5 +1,24 @@ "use client" import { createTRPCReact } from "@trpc/react-query" +import { createTRPCProxyClient, httpBatchLink } from "@trpc/client" import type { AppRouter } from "@/server/trpc/router" +import { getBaseUrl } from "../app/utils/next" export const trpc = createTRPCReact() + +export const vanillaTrpc = createTRPCProxyClient({ + links: [ + httpBatchLink({ + url: `${getBaseUrl()}/api/trpc`, + }), + // httpBatchLink({ + // url: "http://localhost:3000/trpc", + // // // You can pass any HTTP headers you wish here + // // async headers() { + // // return { + // // authorization: getAuthCookie(), + // // }; + // // }, + // }), + ], +}) From 7d2cb4d5ca72c0bc99bf2433242b781586e1b80d Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 22 Jun 2023 12:09:54 +0100 Subject: [PATCH 002/132] wip --- app/workers/systems/worker.ts | 24 ++++++++++++-------- package.json | 8 +++---- yarn.lock | 41 ++++++++++------------------------- 3 files changed, 30 insertions(+), 43 deletions(-) diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index 9df345d5..909ad1fb 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -1,21 +1,21 @@ -import { TRPCContext } from "@trpc/react-query/shared" -import Airtable from "airtable" import { expose } from "comlink" -import Dexie from "dexie" -import { trpc, vanillaTrpc } from "../../../client/trpc" -import { Block, blocksQuery } from "../../../server/data/blocks" - -console.log("HI IM A WORKER") +import Dexie, { liveQuery } from "dexie" +import { vanillaTrpc } from "../../../client/trpc" +import { Block } from "../../../server/data/blocks" +import { Module } from "../../../server/data/modules" class SystemsDatabase extends Dexie { blocks: Dexie.Table // "string" is the type of the primary key + modules: Dexie.Table // "string" is the type of the primary key constructor() { super("SystemsDatabase") this.version(1).stores({ blocks: "id,systemId,name", + modules: "id,systemId,dna", }) this.blocks = this.table("blocks") + this.modules = this.table("modules") } } @@ -23,8 +23,11 @@ class SystemsDatabase extends Dexie { const db = new SystemsDatabase() const init = async () => { - const blocks = await vanillaTrpc.blocks.query() - console.log(blocks) + const blocks: Block[] = await vanillaTrpc.blocks.query() + const modules = (await vanillaTrpc.modules.query()) as Module[] + + db.blocks.bulkAdd(blocks) + db.modules.bulkAdd(modules) } init() @@ -38,8 +41,11 @@ const getBlocks = async (systemIds: string[] = []) => { } } +const modulesObservable = liveQuery(() => db.modules.toArray()) + const api = { getBlocks, + modulesObservable, } export type SystemsAPI = typeof api diff --git a/package.json b/package.json index 6f92ac6f..8f187473 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,9 @@ "@speckle/objectloader": "^2.13.3", "@tanstack/react-query": "^4.20.9", "@tanstack/react-table": "^8.7.9", - "@trpc/client": "^10.7.0", - "@trpc/next": "^10.7.0", - "@trpc/react": "^10.0.0-proxy-alpha.63", - "@trpc/react-query": "^10.7.0", - "@trpc/server": "^10.7.0", + "@trpc/client": "^10.31.0", + "@trpc/react-query": "^10.31.0", + "@trpc/server": "^10.31.0", "@turf/turf": "^6.5.0", "@use-gesture/react": "^10.2.20", "airtable": "^0.11.4", diff --git a/yarn.lock b/yarn.lock index 8ea7c65b..67c30f70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -945,32 +945,20 @@ resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.7.9.tgz#0e975f8a5079972f1827a569079943d43257c42f" integrity sha512-4RkayPMV1oS2SKDXfQbFoct1w5k+pvGpmX18tCXMofK/VDRdA2hhxfsQlMvsJ4oTX8b0CI4Y3GDKn5T425jBCw== -"@trpc/client@^10.7.0": - version "10.7.0" - resolved "https://registry.yarnpkg.com/@trpc/client/-/client-10.7.0.tgz#73793dfdfd1a0b8d0484c69bfc558dfd28843fe6" - integrity sha512-xCPc2hp37REJst6TXMkJvdc85CHvxpVY6YVdZIjl7uCYoQcrstwy8AWi7ElB3x7e19Cweke+9CAaUXKwAE1AKQ== +"@trpc/client@^10.31.0": + version "10.31.0" + resolved "https://registry.yarnpkg.com/@trpc/client/-/client-10.31.0.tgz#64f6198741a0b96f21671e0c3c0e2e211bae3cc9" + integrity sha512-VCqbJEvFJb8C4hQFw7AD+dkQTjgEdV/QAzO4D+/cX5e93u5NpfNXI+PKS0QFXwG/zqgwQwVV6OkYc/D/MFwA6g== -"@trpc/next@^10.7.0": - version "10.7.0" - resolved "https://registry.yarnpkg.com/@trpc/next/-/next-10.7.0.tgz#33ec7d134f9d3093e59cba1170dcb75bc9b914c0" - integrity sha512-ZLY4J9IgEHN2op0UM14NCwCDyodRUw8Pmxgnpr62+UMIhDll6uggb2an8Op87l9EeYdKhKe2VavDWphI8vFGgw== - dependencies: - react-ssr-prepass "^1.5.0" - -"@trpc/react-query@^10.7.0": - version "10.7.0" - resolved "https://registry.yarnpkg.com/@trpc/react-query/-/react-query-10.7.0.tgz#37cd9a3ab5db0e9bcf012cfec3669ce0ecc055e1" - integrity sha512-dSSmYid6NWocU32QMsH6qWEqnW4mbp7hX01hxsBfAA2mGYD8b4Kgc9ripw4MpPNbsJElJFyWDHfb/UHnhywWbA== +"@trpc/react-query@^10.31.0": + version "10.31.0" + resolved "https://registry.yarnpkg.com/@trpc/react-query/-/react-query-10.31.0.tgz#d12042b73138eafafd9efffc6b215a2712595731" + integrity sha512-+M8sIsbf6e4H5XYvHlzDqhaf+ybfUigA/9OL3wXRp2vXhCedEiIERCnwNuHWFDRASl9vjOcM33AuJ4sbOOINEA== -"@trpc/react@^10.0.0-proxy-alpha.63": - version "10.0.0-proxy-alpha.63" - resolved "https://registry.yarnpkg.com/@trpc/react/-/react-10.0.0-proxy-alpha.63.tgz#a39dd34b4efb0e8c59bad880cb4267e8ee4e9fe6" - integrity sha512-D+tIg9a7CVoUOmd8hZTiTuC2hDcpB7surC0eFdcooCbWb2pERE/5xPdiyB6Kze5H5BvzxiRt9ttrKbVQnhvQYw== - -"@trpc/server@^10.7.0": - version "10.7.0" - resolved "https://registry.yarnpkg.com/@trpc/server/-/server-10.7.0.tgz#97c451cb5963c70fba68f4813c56111331baec3c" - integrity sha512-kFhlqhzZt5PwVRRDci8oJKMMFGjjq9189stT6bvS9xuuLjRzzwhMShRi99sm/jqhdjWmlXyO3Ncx0rnghUvH+w== +"@trpc/server@^10.31.0": + version "10.31.0" + resolved "https://registry.yarnpkg.com/@trpc/server/-/server-10.31.0.tgz#2a757e814d9a779d6faa8b4fd6bda80691179a19" + integrity sha512-9EnRTSDE9nF11LZsvSOqNKqkRYzHqFX4ch5AJ6VIu8uta2vxVTN4FxxsNRSOluTzVYZDeaCISbwmOJ5iihCCIg== "@turf/along@^6.5.0": version "6.5.0" @@ -5108,11 +5096,6 @@ react-reconciler@^0.27.0: loose-envify "^1.1.0" scheduler "^0.21.0" -react-ssr-prepass@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/react-ssr-prepass/-/react-ssr-prepass-1.5.0.tgz#bc4ca7fcb52365e6aea11cc254a3d1bdcbd030c5" - integrity sha512-yFNHrlVEReVYKsLI5lF05tZoHveA5pGzjFbFJY/3pOqqjGOmMmqx83N4hIjN2n6E1AOa+eQEUxs3CgRnPmT0RQ== - react-universal-interface@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" From 50bb7b7273cd09858855aa5a513d180caa588ca9 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 22 Jun 2023 13:44:56 +0100 Subject: [PATCH 003/132] wip init modules with time comparison --- app/design/state/houses.ts | 5 +++-- app/workers/systems/worker.ts | 30 +++++++++++++++++++++++++----- package.json | 2 +- server/data/modules.ts | 15 ++++++++++++++- yarn.lock | 7 +------ 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/app/design/state/houses.ts b/app/design/state/houses.ts index 43be8b4d..375540d3 100644 --- a/app/design/state/houses.ts +++ b/app/design/state/houses.ts @@ -11,7 +11,7 @@ import { proxy, subscribe, useSnapshot } from "valtio" import { BUILDX_LOCAL_STORAGE_HOUSES_KEY } from "./constants" import { getHousesFromLocalStorage, House, Houses } from "../../data/houses" import { Module } from "@/server/data/modules" -import { A, R, RA, RNEA, RR, S } from "~/utils/functions" +import { A, pipeLog, R, RA, RNEA, RR, S } from "~/utils/functions" import { useModules, useSystemModules } from "../../data/modules" import { useHouseTypes } from "../../data/houseTypes" @@ -68,7 +68,8 @@ export const useHouseModules = (houseId: string) => { systemModule.systemId === systemId && systemModule.dna === dna ) ) - ) + ), + pipeLog ), [dna, systemId, systemModules] ) diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index 909ad1fb..dd38dd63 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -1,5 +1,6 @@ import { expose } from "comlink" import Dexie, { liveQuery } from "dexie" +import { pipe } from "fp-ts/lib/function" import { vanillaTrpc } from "../../../client/trpc" import { Block } from "../../../server/data/blocks" import { Module } from "../../../server/data/modules" @@ -22,12 +23,31 @@ class SystemsDatabase extends Dexie { // Create Dexie database const db = new SystemsDatabase() -const init = async () => { - const blocks: Block[] = await vanillaTrpc.blocks.query() - const modules = (await vanillaTrpc.modules.query()) as Module[] +const initModules = async () => { + const remoteModules = await vanillaTrpc.modules.query() + + const promises = remoteModules.map(async (remoteModule) => { + const remoteDate = new Date(remoteModule.lastModified) + const localModule = await db.modules.get(remoteModule.id) + + if (!localModule) { + await db.modules.put(remoteModule as Module) + return + } + + const localDate = new Date(localModule.lastModified) - db.blocks.bulkAdd(blocks) - db.modules.bulkAdd(modules) + if (remoteDate > localDate) { + await db.modules.put(remoteModule as Module) + return + } + }) + + await Promise.all(promises) +} + +const init = async () => { + await initModules() } init() diff --git a/package.json b/package.json index 8f187473..0c0b0c5e 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "usehooks-ts": "^2.9.1", "valtio": "^1.7.0", "wouter": "^2.8.0-alpha.2", - "zod": "^3.20.2" + "zod": "^3.21.4" }, "devDependencies": { "@types/carbon__icons-react": "^11.12.0", diff --git a/server/data/modules.ts b/server/data/modules.ts index ed301f22..9af17fdc 100644 --- a/server/data/modules.ts +++ b/server/data/modules.ts @@ -83,7 +83,18 @@ export const moduleParser = z visual_reference: z .array(z.object({ url: z.string().optional() }).optional()) .optional(), - description: z.string().optional(), + description: z.string().default(""), + last_modified: z.string().refine( + (value) => { + // Attempt to parse the value as a date and check that it's valid + const date = new Date(value) + return !isNaN(date.getTime()) + }, + { + // Custom error message + message: "Invalid date string", + } + ), }), }) .transform( @@ -112,6 +123,7 @@ export const moduleParser = z embodied_carbon, visual_reference, description, + last_modified, }, }) => ({ id, @@ -138,6 +150,7 @@ export const moduleParser = z embodiedCarbon: embodied_carbon ?? -400, description, visualReference: visual_reference?.[0]?.url, + lastModified: last_modified, }) ) diff --git a/yarn.lock b/yarn.lock index 67c30f70..2bb7a9d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6086,16 +6086,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zod@3.21.4: +zod@3.21.4, zod@^3.21.4: version "3.21.4" resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw== -zod@^3.20.2: - version "3.20.2" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.2.tgz#068606642c8f51b3333981f91c0a8ab37dfc2807" - integrity sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ== - zstddec@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.0.2.tgz#57e2f28dd1ff56b750e07d158a43f0611ad9eeb4" From 02684267481440205f376b8342f9a152377d62f1 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 22 Jun 2023 15:12:57 +0100 Subject: [PATCH 004/132] wip models in idb --- app/workers/systems/worker.ts | 80 ++++++++++++++++++++++++++++------- server/data/speckleModel.ts | 2 +- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index dd38dd63..1002f076 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -1,22 +1,41 @@ import { expose } from "comlink" import Dexie, { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" +import produce from "immer" +import { BufferGeometry } from "three" +import { mergeBufferGeometries } from "three-stdlib" import { vanillaTrpc } from "../../../client/trpc" import { Block } from "../../../server/data/blocks" import { Module } from "../../../server/data/modules" +import { getSpeckleObject } from "../../../server/data/speckleModel" +import { A, R } from "../../utils/functions" +import speckleIfcParser from "../../utils/speckle/speckleIfcParser" + +type IndexedModule = Module & { + lastFetched: string +} + +type IndexedModel = { + speckleBranchUrl: string + lastFetched: string + geometries: any +} class SystemsDatabase extends Dexie { blocks: Dexie.Table // "string" is the type of the primary key - modules: Dexie.Table // "string" is the type of the primary key + modules: Dexie.Table // "string" is the type of the primary key + models: Dexie.Table constructor() { super("SystemsDatabase") this.version(1).stores({ blocks: "id,systemId,name", modules: "id,systemId,dna", + models: "speckleBranchUrl", }) this.blocks = this.table("blocks") this.modules = this.table("modules") + this.models = this.table("models") } } @@ -30,15 +49,20 @@ const initModules = async () => { const remoteDate = new Date(remoteModule.lastModified) const localModule = await db.modules.get(remoteModule.id) + const indexedModule: IndexedModule = { + ...remoteModule, + lastFetched: new Date().toISOString(), + } as any + if (!localModule) { - await db.modules.put(remoteModule as Module) + await db.modules.put(indexedModule) return } const localDate = new Date(localModule.lastModified) if (remoteDate > localDate) { - await db.modules.put(remoteModule as Module) + await db.modules.put(indexedModule) return } }) @@ -52,21 +76,45 @@ const init = async () => { init() -const getBlocks = async (systemIds: string[] = []) => { - console.log("getBlocks") - if (systemIds.length === 0) { - return [] - } else { - return db.blocks.toArray() - } -} - const modulesObservable = liveQuery(() => db.modules.toArray()) -const api = { - getBlocks, - modulesObservable, -} +modulesObservable.subscribe((modules) => { + modules.map(async (nextModule) => { + const { speckleBranchUrl, lastFetched } = nextModule + const maybeModel = await db.models.get(speckleBranchUrl) + + if ( + maybeModel && + new Date(maybeModel.lastFetched) === new Date(lastFetched) + ) { + return + } + + const speckleObjectData = await getSpeckleObject(speckleBranchUrl) + const speckleObject = speckleIfcParser.parse(speckleObjectData) + const geometries = pipe( + speckleObject, + A.reduce( + {}, + (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { + return produce(acc, (draft) => { + if (ifcTag in draft) draft[ifcTag].push(geometry) + else draft[ifcTag] = [geometry] + }) + } + ), + R.map((geoms) => mergeBufferGeometries(geoms)), + R.filter((bg: BufferGeometry | null): bg is BufferGeometry => + Boolean(bg) + ), + R.map((x) => x.toJSON()) + ) + + db.models.put({ speckleBranchUrl, lastFetched, geometries }) + }) +}) + +const api = {} export type SystemsAPI = typeof api diff --git a/server/data/speckleModel.ts b/server/data/speckleModel.ts index 9f15c21c..f3fcb4fe 100644 --- a/server/data/speckleModel.ts +++ b/server/data/speckleModel.ts @@ -38,7 +38,7 @@ const extractStreamId = (urlString: string) => { return pathParts[streamIdIndex] } -const getSpeckleObject = async (speckleBranchUrl: string) => { +export const getSpeckleObject = async (speckleBranchUrl: string) => { const streamId = extractStreamId(speckleBranchUrl) const data: any = await request("https://speckle.xyz/graphql", document, { From 118ac0743b3b0952662fe2dd0d3c0eb0fd52438e Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 22 Jun 2023 15:29:36 +0100 Subject: [PATCH 005/132] wip new useSpeckleObject --- app/build/layout.tsx | 3 --- app/data/elements.ts | 26 ++++++++++++++++--- app/db/index.ts | 36 +++++++++++++++++++++++++++ app/design/state/houses.ts | 3 +-- app/utils/speckle/useSpeckleObject.ts | 6 ----- app/workers/systems/worker.ts | 36 ++------------------------- package.json | 1 + yarn.lock | 5 ++++ 8 files changed, 68 insertions(+), 48 deletions(-) create mode 100644 app/db/index.ts diff --git a/app/build/layout.tsx b/app/build/layout.tsx index 6988b90d..fa356710 100644 --- a/app/build/layout.tsx +++ b/app/build/layout.tsx @@ -1,8 +1,6 @@ import dynamic from "next/dynamic" import { PropsWithChildren } from "react" -import Footer from "../ui/Footer" import { TrpcProvider } from "../ui/TrpcProvider" -import { PreloadSpeckleObjects } from "../utils/speckle/useSpeckleObject" import BuildNav from "./common/BuildNav" const HousesPillsSelector = dynamic( @@ -26,7 +24,6 @@ const BuildLayout = ({ children }: PropsWithChildren<{}>) => {
{children}
- ) } diff --git a/app/data/elements.ts b/app/data/elements.ts index d0a2f2c4..5991dd1d 100644 --- a/app/data/elements.ts +++ b/app/data/elements.ts @@ -1,11 +1,13 @@ import { trpc } from "@/client/trpc" +import { useLiveQuery } from "dexie-react-hooks" import { pipe } from "fp-ts/lib/function" import { useMemo } from "react" -import { BufferGeometry } from "three" +import { BufferGeometry, BufferGeometryLoader } from "three" import { O, R, RA, S } from "~/utils/functions" import { Element } from "../../server/data/elements" import { Module } from "../../server/data/modules" -import useSpeckleObject from "../utils/speckle/useSpeckleObject" +import db from "../db" +// import useSpeckleObject from "../utils/speckle/useSpeckleObject" export const useElements = (): Element[] => { const { data = [] } = trpc.elements.useQuery() @@ -55,6 +57,24 @@ export const invertModuleElementGeometriesKey = (input: string) => { return { systemId, dna } } +const useSpeckleObject = (speckleBranchUrl: string) => { + const loader = useMemo(() => new BufferGeometryLoader(), []) + + const geometries = useLiveQuery(async () => { + const model = await db.models.get(speckleBranchUrl) + return model?.geometries + }) + + return useMemo( + () => + pipe( + geometries, + R.map((x) => loader.parse(x) as BufferGeometry) + ), + [geometries, loader] + ) +} + export const useModuleElements = ({ systemId, speckleBranchUrl, @@ -76,6 +96,6 @@ export const useModuleElements = ({ }) ), // eslint-disable-next-line react-hooks/exhaustive-deps - [speckleBranchUrl] + [speckleBranchUrl, speckleObject] ) } diff --git a/app/db/index.ts b/app/db/index.ts new file mode 100644 index 00000000..d78380b7 --- /dev/null +++ b/app/db/index.ts @@ -0,0 +1,36 @@ +import Dexie from "dexie" +import { Module } from "@/server/data/modules" +import { Block } from "@/server/data/blocks" + +export type IndexedModule = Module & { + lastFetched: string +} + +export type IndexedModel = { + speckleBranchUrl: string + lastFetched: string + geometries: any +} + +class SystemsDatabase extends Dexie { + blocks: Dexie.Table // "string" is the type of the primary key + modules: Dexie.Table // "string" is the type of the primary key + models: Dexie.Table + + constructor() { + super("SystemsDatabase") + this.version(1).stores({ + blocks: "id,systemId,name", + modules: "id,systemId,dna", + models: "speckleBranchUrl", + }) + this.blocks = this.table("blocks") + this.modules = this.table("modules") + this.models = this.table("models") + } +} + +// Create Dexie database +const db = new SystemsDatabase() + +export default db diff --git a/app/design/state/houses.ts b/app/design/state/houses.ts index 375540d3..1513e249 100644 --- a/app/design/state/houses.ts +++ b/app/design/state/houses.ts @@ -68,8 +68,7 @@ export const useHouseModules = (houseId: string) => { systemModule.systemId === systemId && systemModule.dna === dna ) ) - ), - pipeLog + ) ), [dna, systemId, systemModules] ) diff --git a/app/utils/speckle/useSpeckleObject.ts b/app/utils/speckle/useSpeckleObject.ts index 9e7b81de..197be000 100644 --- a/app/utils/speckle/useSpeckleObject.ts +++ b/app/utils/speckle/useSpeckleObject.ts @@ -116,10 +116,4 @@ export const useSpeckleObjects = ( }) } -export const PreloadSpeckleObjects = () => { - const modules = useModules() - useSpeckleObjects(modules.map((module) => module.speckleBranchUrl)) - return null -} - export default useSpeckleObject diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index 1002f076..01a7b3d7 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -1,47 +1,15 @@ import { expose } from "comlink" -import Dexie, { liveQuery } from "dexie" +import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" import produce from "immer" import { BufferGeometry } from "three" import { mergeBufferGeometries } from "three-stdlib" import { vanillaTrpc } from "../../../client/trpc" -import { Block } from "../../../server/data/blocks" -import { Module } from "../../../server/data/modules" import { getSpeckleObject } from "../../../server/data/speckleModel" +import db, { IndexedModule } from "../../db" import { A, R } from "../../utils/functions" import speckleIfcParser from "../../utils/speckle/speckleIfcParser" -type IndexedModule = Module & { - lastFetched: string -} - -type IndexedModel = { - speckleBranchUrl: string - lastFetched: string - geometries: any -} - -class SystemsDatabase extends Dexie { - blocks: Dexie.Table // "string" is the type of the primary key - modules: Dexie.Table // "string" is the type of the primary key - models: Dexie.Table - - constructor() { - super("SystemsDatabase") - this.version(1).stores({ - blocks: "id,systemId,name", - modules: "id,systemId,dna", - models: "speckleBranchUrl", - }) - this.blocks = this.table("blocks") - this.modules = this.table("modules") - this.models = this.table("models") - } -} - -// Create Dexie database -const db = new SystemsDatabase() - const initModules = async () => { const remoteModules = await vanillaTrpc.modules.query() diff --git a/package.json b/package.json index 0c0b0c5e..4e88e4d4 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "crypto-js": "^4.1.1", "d3-dsv": "^3.0.1", "dexie": "^3.2.3", + "dexie-react-hooks": "^1.1.6", "encoding": "^0.1.13", "fp-ts": "^2.12.3", "fp-ts-std": "^0.15.0", diff --git a/yarn.lock b/yarn.lock index 2bb7a9d8..47df41ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3068,6 +3068,11 @@ detective@^5.2.1: defined "^1.0.0" minimist "^1.2.6" +dexie-react-hooks@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/dexie-react-hooks/-/dexie-react-hooks-1.1.6.tgz#42d4d4d314b049c23e2abe20c8543107ed85b0bd" + integrity sha512-xSblWtmPwhafWNWMECsW7zMMmBu8goH3QqTxEfwBNoNG1mgsM0oFclippev7ss9HhKICqBwTjgqpscci5Ed4mA== + dexie@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/dexie/-/dexie-3.2.3.tgz#f35c91ca797599df8e771b998e9ae9669c877f8c" From b3120181b33816f84004bb662cf7027d2623fa75 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Sun, 25 Jun 2023 16:01:18 +0100 Subject: [PATCH 006/132] wip propped tree start --- app/data/elements.ts | 4 +- app/db/{index.ts => system.ts} | 4 +- app/db/user.ts | 35 ++++++++++ app/design/page.tsx | 3 +- app/design/state/houses.ts | 8 +-- app/design/ui-3d/propped/ProppedApp.tsx | 76 ++++++++++++++++++++++ app/design/ui-3d/propped/ProppedHouse.tsx | 17 +++++ app/design/ui-3d/propped/ProppedSystem.tsx | 41 ++++++++++++ app/design/ui/SiteSidebar.tsx | 11 ++-- app/workers/systems/worker.ts | 14 ++-- 10 files changed, 191 insertions(+), 22 deletions(-) rename app/db/{index.ts => system.ts} (93%) create mode 100644 app/db/user.ts create mode 100644 app/design/ui-3d/propped/ProppedApp.tsx create mode 100644 app/design/ui-3d/propped/ProppedHouse.tsx create mode 100644 app/design/ui-3d/propped/ProppedSystem.tsx diff --git a/app/data/elements.ts b/app/data/elements.ts index 5991dd1d..e309b9ca 100644 --- a/app/data/elements.ts +++ b/app/data/elements.ts @@ -6,7 +6,7 @@ import { BufferGeometry, BufferGeometryLoader } from "three" import { O, R, RA, S } from "~/utils/functions" import { Element } from "../../server/data/elements" import { Module } from "../../server/data/modules" -import db from "../db" +import systemsDB from "../db/system" // import useSpeckleObject from "../utils/speckle/useSpeckleObject" export const useElements = (): Element[] => { @@ -61,7 +61,7 @@ const useSpeckleObject = (speckleBranchUrl: string) => { const loader = useMemo(() => new BufferGeometryLoader(), []) const geometries = useLiveQuery(async () => { - const model = await db.models.get(speckleBranchUrl) + const model = await systemsDB.models.get(speckleBranchUrl) return model?.geometries }) diff --git a/app/db/index.ts b/app/db/system.ts similarity index 93% rename from app/db/index.ts rename to app/db/system.ts index d78380b7..6d1d47f6 100644 --- a/app/db/index.ts +++ b/app/db/system.ts @@ -31,6 +31,6 @@ class SystemsDatabase extends Dexie { } // Create Dexie database -const db = new SystemsDatabase() +const systemsDB = new SystemsDatabase() -export default db +export default systemsDB diff --git a/app/db/user.ts b/app/db/user.ts new file mode 100644 index 00000000..20511366 --- /dev/null +++ b/app/db/user.ts @@ -0,0 +1,35 @@ +import Dexie from "dexie" +import { z } from "zod" + +export const houseParser = z.object({ + id: z.string().min(1), + houseTypeId: z.string().min(1), + systemId: z.string().min(1), + dnas: z.array(z.string().min(1)), + modifiedMaterials: z.record(z.string().min(1)), + friendlyName: z.string().min(1), + position: z.object({ + x: z.number(), + y: z.number(), + z: z.number(), + }), + rotation: z.number(), +}) + +export type House = z.infer + +class UserDatabase extends Dexie { + houses: Dexie.Table + + constructor() { + super("UserDatabase") + this.version(1).stores({ + houses: "id,&friendlyName", + }) + this.houses = this.table("houses") + } +} + +const userDB = new UserDatabase() + +export default userDB diff --git a/app/design/page.tsx b/app/design/page.tsx index 6f4f5848..e88d03a7 100644 --- a/app/design/page.tsx +++ b/app/design/page.tsx @@ -2,13 +2,14 @@ import DataInit from "~/data/DataInit" import { TrpcProvider } from "../ui/TrpcProvider" import GroupedApp from "./ui-3d/grouped/GroupedApp" import AppInit from "./ui-3d/init/AppInit" +import ProppedApp from "./ui-3d/propped/ProppedApp" const IndexPage = () => { return ( - + diff --git a/app/design/state/houses.ts b/app/design/state/houses.ts index 1513e249..61a9c0f6 100644 --- a/app/design/state/houses.ts +++ b/app/design/state/houses.ts @@ -1,3 +1,4 @@ +import { Module } from "@/server/data/modules" import { values } from "fp-ts-std/Record" import { pipe } from "fp-ts/lib/function" import { none, some } from "fp-ts/lib/Option" @@ -8,12 +9,11 @@ import { useEffect, useMemo } from "react" import { useKey } from "react-use" import { Vector3 } from "three" import { proxy, subscribe, useSnapshot } from "valtio" -import { BUILDX_LOCAL_STORAGE_HOUSES_KEY } from "./constants" +import { A, R, RA, RR, S } from "~/utils/functions" import { getHousesFromLocalStorage, House, Houses } from "../../data/houses" -import { Module } from "@/server/data/modules" -import { A, pipeLog, R, RA, RNEA, RR, S } from "~/utils/functions" -import { useModules, useSystemModules } from "../../data/modules" import { useHouseTypes } from "../../data/houseTypes" +import { useModules, useSystemModules } from "../../data/modules" +import { BUILDX_LOCAL_STORAGE_HOUSES_KEY } from "./constants" const houses = proxy(getHousesFromLocalStorage()) diff --git a/app/design/ui-3d/propped/ProppedApp.tsx b/app/design/ui-3d/propped/ProppedApp.tsx new file mode 100644 index 00000000..c256c006 --- /dev/null +++ b/app/design/ui-3d/propped/ProppedApp.tsx @@ -0,0 +1,76 @@ +"use client" +import { pipe } from "fp-ts/lib/function" +import { Fragment, Suspense } from "react" +import { usePreviews } from "~/design/state/previews" +import { useRouting } from "~/design/state/routing" +import { A, NEA, RA } from "~/utils/functions" +import { useExportersWorker } from "../../workers/exporters" +import { useDragHandler, useGestures } from "../../state/gestures" +import { useHouseKeys } from "../../state/houses" +import XZPlane from "../XZPlane" +import YPlane from "../YPlane" +import { useLiveQuery } from "dexie-react-hooks" +import userDB from "../../../db/user" +import { systems } from "../../../../server/data/system" +import systemsDB from "../../../db/system" +import Loader from "../../../ui/Loader" +import ProppedSystem from "./ProppedSystem" +// import GroupedHouse from "./GroupedHouse" + +const ProppedApp = () => { + // const houses = useLiveQuery(() => userDB.houses.toArray()) ?? [] + // const houseKeys = useHouseKeys() + // usePreviews() + // const bindAll = useGestures() + // useDragHandler() + // useRouting() + + // useExportersWorker() + + const result = useLiveQuery(async () => ({ + modules: pipe( + await systemsDB.modules.toArray(), + NEA.groupBy((x) => x.systemId) + ), + models: pipe( + await systemsDB.models.toArray(), + NEA.groupBy((x) => x.speckleBranchUrl) + ), + })) + + if (!result) return null + + const { modules, models } = result + + return ( + + + {pipe( + systems, + A.map((system) => ( + + )) + )} + {/* {pipe( + houses, + RA.map((house) => ( + + + + )) + )} */} + + + + + ) +} + +export default ProppedApp diff --git a/app/design/ui-3d/propped/ProppedHouse.tsx b/app/design/ui-3d/propped/ProppedHouse.tsx new file mode 100644 index 00000000..fe65cfa4 --- /dev/null +++ b/app/design/ui-3d/propped/ProppedHouse.tsx @@ -0,0 +1,17 @@ +import React from "react" +import { IndexedModel, IndexedModule } from "../../../db/system" +import { House } from "../../../db/user" + +type Props = { + house: House + modules: IndexedModule[] + models: Record +} + +const ProppedHouse = (props: Props) => { + const { house } = props + console.log(house) + return +} + +export default ProppedHouse diff --git a/app/design/ui-3d/propped/ProppedSystem.tsx b/app/design/ui-3d/propped/ProppedSystem.tsx new file mode 100644 index 00000000..127484ef --- /dev/null +++ b/app/design/ui-3d/propped/ProppedSystem.tsx @@ -0,0 +1,41 @@ +import { useLiveQuery } from "dexie-react-hooks" +import { pipe } from "fp-ts/lib/function" +import { IndexedModel, IndexedModule } from "../../../db/system" +import userDB from "../../../db/user" +import { A } from "../../../utils/functions" +import ProppedHouse from "./ProppedHouse" + +type Props = { + systemId: string + modules: IndexedModule[] + models: Record +} + +const ProppedSystem = (props: Props) => { + const { systemId, modules, models } = props + + const houses = + useLiveQuery(() => + userDB.houses.filter((x) => x.systemId === systemId).toArray() + ) ?? [] + + console.log(houses) + + return ( + + {pipe( + houses, + A.map((house) => ( + + )) + )} + + ) +} + +export default ProppedSystem diff --git a/app/design/ui/SiteSidebar.tsx b/app/design/ui/SiteSidebar.tsx index a7f4be63..a276ca3d 100644 --- a/app/design/ui/SiteSidebar.tsx +++ b/app/design/ui/SiteSidebar.tsx @@ -1,7 +1,5 @@ -import { useCameraGroundRaycast } from "../state/camera" import { System, systems } from "@/server/data/system" -// import houses from "@/src/stores/houses" -import Sidebar from "~/ui//Sidebar" +import { useCameraGroundRaycast } from "../state/camera" import { pipe } from "fp-ts/lib/function" import { mapWithIndex } from "fp-ts/lib/ReadonlyArray" import { keys } from "fp-ts/lib/ReadonlyRecord" @@ -10,8 +8,9 @@ import { Fragment, useMemo, useState } from "react" import { Vector3 } from "three" import { useHouseTypes } from "~/data/houseTypes" import houses from "~/design/state/houses" +import Sidebar from "~/ui//Sidebar" +import userDB from "../../db/user" import HouseThumbnail from "./HouseThumbnail" -// import HouseThumbnail from "./HouseThumbnail" type Props = { open: boolean @@ -78,7 +77,7 @@ const SiteSidebar = ({ open, close }: Props) => { const position = cameraGroundRaycast() ?? new Vector3(0, 0, 0) - houses[id] = { + userDB.houses.add({ id, houseTypeId: houseType.id, systemId: houseType.systemId, @@ -87,7 +86,7 @@ const SiteSidebar = ({ open, close }: Props) => { dnas: houseType.dnas as string[], modifiedMaterials: {}, friendlyName: `Building ${keys(houses).length + 1}`, - } + }) close() }} diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index 01a7b3d7..f2381f54 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -6,7 +6,7 @@ import { BufferGeometry } from "three" import { mergeBufferGeometries } from "three-stdlib" import { vanillaTrpc } from "../../../client/trpc" import { getSpeckleObject } from "../../../server/data/speckleModel" -import db, { IndexedModule } from "../../db" +import systemsDB, { IndexedModule } from "../../db/system" import { A, R } from "../../utils/functions" import speckleIfcParser from "../../utils/speckle/speckleIfcParser" @@ -15,7 +15,7 @@ const initModules = async () => { const promises = remoteModules.map(async (remoteModule) => { const remoteDate = new Date(remoteModule.lastModified) - const localModule = await db.modules.get(remoteModule.id) + const localModule = await systemsDB.modules.get(remoteModule.id) const indexedModule: IndexedModule = { ...remoteModule, @@ -23,14 +23,14 @@ const initModules = async () => { } as any if (!localModule) { - await db.modules.put(indexedModule) + await systemsDB.modules.put(indexedModule) return } const localDate = new Date(localModule.lastModified) if (remoteDate > localDate) { - await db.modules.put(indexedModule) + await systemsDB.modules.put(indexedModule) return } }) @@ -44,12 +44,12 @@ const init = async () => { init() -const modulesObservable = liveQuery(() => db.modules.toArray()) +const modulesObservable = liveQuery(() => systemsDB.modules.toArray()) modulesObservable.subscribe((modules) => { modules.map(async (nextModule) => { const { speckleBranchUrl, lastFetched } = nextModule - const maybeModel = await db.models.get(speckleBranchUrl) + const maybeModel = await systemsDB.models.get(speckleBranchUrl) if ( maybeModel && @@ -78,7 +78,7 @@ modulesObservable.subscribe((modules) => { R.map((x) => x.toJSON()) ) - db.models.put({ speckleBranchUrl, lastFetched, geometries }) + systemsDB.models.put({ speckleBranchUrl, lastFetched, geometries }) }) }) From 3d769d41187079cd4c9dd28d0ece77cc53d8d548 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Sun, 25 Jun 2023 17:40:59 +0100 Subject: [PATCH 007/132] wip getting messy --- app/data/elements.ts | 2 +- app/db/{system.ts => systems.ts} | 0 app/db/user.ts | 3 ++ app/design/page.tsx | 1 - app/design/ui-3d/propped/ProppedApp.tsx | 3 +- app/design/ui-3d/propped/ProppedHouse.tsx | 2 +- app/design/ui-3d/propped/ProppedSystem.tsx | 4 +- app/design/ui/SiteSidebar.tsx | 7 +-- app/workers/house/useHouseWorker.ts | 17 ++++++++ app/workers/house/worker.ts | 7 +++ app/workers/systems/worker.ts | 51 ++++++++++------------ 11 files changed, 61 insertions(+), 36 deletions(-) rename app/db/{system.ts => systems.ts} (100%) create mode 100644 app/workers/house/useHouseWorker.ts create mode 100644 app/workers/house/worker.ts diff --git a/app/data/elements.ts b/app/data/elements.ts index e309b9ca..3f476d50 100644 --- a/app/data/elements.ts +++ b/app/data/elements.ts @@ -6,7 +6,7 @@ import { BufferGeometry, BufferGeometryLoader } from "three" import { O, R, RA, S } from "~/utils/functions" import { Element } from "../../server/data/elements" import { Module } from "../../server/data/modules" -import systemsDB from "../db/system" +import systemsDB from "../db/systems" // import useSpeckleObject from "../utils/speckle/useSpeckleObject" export const useElements = (): Element[] => { diff --git a/app/db/system.ts b/app/db/systems.ts similarity index 100% rename from app/db/system.ts rename to app/db/systems.ts diff --git a/app/db/user.ts b/app/db/user.ts index 20511366..826764ab 100644 --- a/app/db/user.ts +++ b/app/db/user.ts @@ -1,4 +1,5 @@ import Dexie from "dexie" +import { useLiveQuery } from "dexie-react-hooks" import { z } from "zod" export const houseParser = z.object({ @@ -32,4 +33,6 @@ class UserDatabase extends Dexie { const userDB = new UserDatabase() +export const useHouses = () => useLiveQuery(() => userDB.houses.toArray()) ?? [] + export default userDB diff --git a/app/design/page.tsx b/app/design/page.tsx index e88d03a7..e5ea94b2 100644 --- a/app/design/page.tsx +++ b/app/design/page.tsx @@ -1,6 +1,5 @@ import DataInit from "~/data/DataInit" import { TrpcProvider } from "../ui/TrpcProvider" -import GroupedApp from "./ui-3d/grouped/GroupedApp" import AppInit from "./ui-3d/init/AppInit" import ProppedApp from "./ui-3d/propped/ProppedApp" diff --git a/app/design/ui-3d/propped/ProppedApp.tsx b/app/design/ui-3d/propped/ProppedApp.tsx index c256c006..fd178442 100644 --- a/app/design/ui-3d/propped/ProppedApp.tsx +++ b/app/design/ui-3d/propped/ProppedApp.tsx @@ -12,9 +12,10 @@ import YPlane from "../YPlane" import { useLiveQuery } from "dexie-react-hooks" import userDB from "../../../db/user" import { systems } from "../../../../server/data/system" -import systemsDB from "../../../db/system" +import systemsDB from "../../../db/systems" import Loader from "../../../ui/Loader" import ProppedSystem from "./ProppedSystem" +import { useKey } from "react-use" // import GroupedHouse from "./GroupedHouse" const ProppedApp = () => { diff --git a/app/design/ui-3d/propped/ProppedHouse.tsx b/app/design/ui-3d/propped/ProppedHouse.tsx index fe65cfa4..09f8386d 100644 --- a/app/design/ui-3d/propped/ProppedHouse.tsx +++ b/app/design/ui-3d/propped/ProppedHouse.tsx @@ -1,5 +1,5 @@ import React from "react" -import { IndexedModel, IndexedModule } from "../../../db/system" +import { IndexedModel, IndexedModule } from "../../../db/systems" import { House } from "../../../db/user" type Props = { diff --git a/app/design/ui-3d/propped/ProppedSystem.tsx b/app/design/ui-3d/propped/ProppedSystem.tsx index 127484ef..3e2955f4 100644 --- a/app/design/ui-3d/propped/ProppedSystem.tsx +++ b/app/design/ui-3d/propped/ProppedSystem.tsx @@ -1,6 +1,6 @@ import { useLiveQuery } from "dexie-react-hooks" import { pipe } from "fp-ts/lib/function" -import { IndexedModel, IndexedModule } from "../../../db/system" +import { IndexedModel, IndexedModule } from "../../../db/systems" import userDB from "../../../db/user" import { A } from "../../../utils/functions" import ProppedHouse from "./ProppedHouse" @@ -19,7 +19,7 @@ const ProppedSystem = (props: Props) => { userDB.houses.filter((x) => x.systemId === systemId).toArray() ) ?? [] - console.log(houses) + console.log(`new houses ${systemId}`) return ( diff --git a/app/design/ui/SiteSidebar.tsx b/app/design/ui/SiteSidebar.tsx index a276ca3d..dbe605db 100644 --- a/app/design/ui/SiteSidebar.tsx +++ b/app/design/ui/SiteSidebar.tsx @@ -7,9 +7,8 @@ import { nanoid } from "nanoid" import { Fragment, useMemo, useState } from "react" import { Vector3 } from "three" import { useHouseTypes } from "~/data/houseTypes" -import houses from "~/design/state/houses" import Sidebar from "~/ui//Sidebar" -import userDB from "../../db/user" +import userDB, { useHouses } from "../../db/user" import HouseThumbnail from "./HouseThumbnail" type Props = { @@ -33,6 +32,8 @@ const SiteSidebar = ({ open, close }: Props) => { const cameraGroundRaycast = useCameraGroundRaycast() + const houses = useHouses() + return ( {manySystems && !selectedSystem ? ( @@ -85,7 +86,7 @@ const SiteSidebar = ({ open, close }: Props) => { rotation: 0, dnas: houseType.dnas as string[], modifiedMaterials: {}, - friendlyName: `Building ${keys(houses).length + 1}`, + friendlyName: `Building ${houses.length + 1}`, }) close() diff --git a/app/workers/house/useHouseWorker.ts b/app/workers/house/useHouseWorker.ts new file mode 100644 index 00000000..883eb094 --- /dev/null +++ b/app/workers/house/useHouseWorker.ts @@ -0,0 +1,17 @@ +import { releaseProxy, Remote, wrap } from "comlink" +import { useEffect, useRef } from "react" +import { HouseWorker } from "./worker" + +export const useHouseWorker = () => { + const ref = useRef | null>(null) + + useEffect(() => { + const worker = new Worker(new URL("./worker.ts", import.meta.url)) + ref.current = wrap(worker) + + return () => { + ref.current?.[releaseProxy]() + worker.terminate() + } + }, []) +} diff --git a/app/workers/house/worker.ts b/app/workers/house/worker.ts new file mode 100644 index 00000000..21b71de2 --- /dev/null +++ b/app/workers/house/worker.ts @@ -0,0 +1,7 @@ +import { expose } from "comlink" + +const api = {} + +export type HouseWorker = typeof api + +expose(api) diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index f2381f54..2342c28b 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -1,14 +1,7 @@ import { expose } from "comlink" import { liveQuery } from "dexie" -import { pipe } from "fp-ts/lib/function" -import produce from "immer" -import { BufferGeometry } from "three" -import { mergeBufferGeometries } from "three-stdlib" import { vanillaTrpc } from "../../../client/trpc" -import { getSpeckleObject } from "../../../server/data/speckleModel" -import systemsDB, { IndexedModule } from "../../db/system" -import { A, R } from "../../utils/functions" -import speckleIfcParser from "../../utils/speckle/speckleIfcParser" +import systemsDB, { IndexedModule } from "../../db/systems" const initModules = async () => { const remoteModules = await vanillaTrpc.modules.query() @@ -58,25 +51,29 @@ modulesObservable.subscribe((modules) => { return } - const speckleObjectData = await getSpeckleObject(speckleBranchUrl) - const speckleObject = speckleIfcParser.parse(speckleObjectData) - const geometries = pipe( - speckleObject, - A.reduce( - {}, - (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { - return produce(acc, (draft) => { - if (ifcTag in draft) draft[ifcTag].push(geometry) - else draft[ifcTag] = [geometry] - }) - } - ), - R.map((geoms) => mergeBufferGeometries(geoms)), - R.filter((bg: BufferGeometry | null): bg is BufferGeometry => - Boolean(bg) - ), - R.map((x) => x.toJSON()) - ) + const geometries = await vanillaTrpc.speckleModel.query({ + speckleBranchUrl, + }) + + // const speckleObjectData = await getSpeckleObject(speckleBranchUrl) + // const speckleObject = speckleIfcParser.parse(speckleObjectData) + // const geometries = pipe( + // speckleObject, + // A.reduce( + // {}, + // (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { + // return produce(acc, (draft) => { + // if (ifcTag in draft) draft[ifcTag].push(geometry) + // else draft[ifcTag] = [geometry] + // }) + // } + // ), + // R.map((geoms) => mergeBufferGeometries(geoms)), + // R.filter((bg: BufferGeometry | null): bg is BufferGeometry => + // Boolean(bg) + // ), + // R.map((x) => x.toJSON()) + // ) systemsDB.models.put({ speckleBranchUrl, lastFetched, geometries }) }) From 745cffee3f4b8fdb22846ac35f6b70cc5f3dc655 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Sun, 25 Jun 2023 19:19:06 +0100 Subject: [PATCH 008/132] wip layout computer --- app/design/state/layouts.ts | 2 +- app/design/ui-3d/propped/ProppedHouse.tsx | 22 ++++++-- app/design/ui-3d/propped/ProppedSystem.tsx | 2 - app/workers/systems/index.ts | 66 ++++++++++++++++++++++ app/workers/systems/worker.ts | 35 +++++++++++- 5 files changed, 119 insertions(+), 8 deletions(-) diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index a01b3f8a..4d9c3ff0 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -192,7 +192,7 @@ const columnify = return pipe(acc, transposeRA) } -const modulesToColumnLayout = (modules: Module[]) => { +export const modulesToColumnLayout = (modules: Module[]) => { const columns = pipe( modules, modulesToRows, diff --git a/app/design/ui-3d/propped/ProppedHouse.tsx b/app/design/ui-3d/propped/ProppedHouse.tsx index 09f8386d..24eaf74f 100644 --- a/app/design/ui-3d/propped/ProppedHouse.tsx +++ b/app/design/ui-3d/propped/ProppedHouse.tsx @@ -1,6 +1,10 @@ -import React from "react" +import React, { useEffect } from "react" import { IndexedModel, IndexedModule } from "../../../db/systems" import { House } from "../../../db/user" +import { + dispatchComputeLayoutEvent, + useHouseLayoutEvents, +} from "../../../workers/systems" type Props = { house: House @@ -9,9 +13,19 @@ type Props = { } const ProppedHouse = (props: Props) => { - const { house } = props - console.log(house) - return + const { + house: { id: houseId, systemId, dnas }, + } = props + + useEffect(() => { + dispatchComputeLayoutEvent({ houseId, systemId, dnas }) + }, [dnas, houseId, systemId]) + + useHouseLayoutEvents(houseId, (layout) => { + console.log(JSON.stringify(layout)) + }) + + return } export default ProppedHouse diff --git a/app/design/ui-3d/propped/ProppedSystem.tsx b/app/design/ui-3d/propped/ProppedSystem.tsx index 3e2955f4..4522d511 100644 --- a/app/design/ui-3d/propped/ProppedSystem.tsx +++ b/app/design/ui-3d/propped/ProppedSystem.tsx @@ -19,8 +19,6 @@ const ProppedSystem = (props: Props) => { userDB.houses.filter((x) => x.systemId === systemId).toArray() ) ?? [] - console.log(`new houses ${systemId}`) - return ( {pipe( diff --git a/app/workers/systems/index.ts b/app/workers/systems/index.ts index 13ec6022..044bf985 100644 --- a/app/workers/systems/index.ts +++ b/app/workers/systems/index.ts @@ -1,8 +1,62 @@ "use client" import { releaseProxy, Remote, wrap } from "comlink" import { useEffect, useRef } from "react" +import { useEvent } from "react-use" +import { ColumnLayout } from "../../design/state/layouts" import { SystemsAPI } from "./worker" +const COMPUTE_LAYOUT_EVENT = "ComputeLayoutEvent" +const COMPUTED_LAYOUT_EVENT = "ComputedLayoutEvent" + +export type ComputeLayoutEventDetail = { + houseId: string + systemId: string + dnas: string[] +} + +type ComputeLayoutEvent = { + type: typeof COMPUTE_LAYOUT_EVENT + detail: ComputeLayoutEventDetail +} + +export const dispatchComputeLayoutEvent = ( + detail: ComputeLayoutEventDetail +) => { + dispatchEvent( + new CustomEvent(COMPUTE_LAYOUT_EVENT, { + detail, + }) + ) +} + +type ComputedLayoutEventDetail = { + houseId: string + layout: ColumnLayout +} + +type ComputedLayoutEvent = { + type: typeof COMPUTED_LAYOUT_EVENT + detail: ComputedLayoutEventDetail +} + +const dispatchComputedLayoutEvent = (detail: ComputedLayoutEventDetail) => { + dispatchEvent( + new CustomEvent(COMPUTED_LAYOUT_EVENT, { + detail, + }) + ) +} + +export const useHouseLayoutEvents = ( + houseId: string, + cb: (layout: ColumnLayout) => void +) => { + useEvent(COMPUTED_LAYOUT_EVENT, ({ detail }: ComputedLayoutEvent) => { + if (houseId !== detail.houseId) return + cb(detail.layout) + }) +} + export const SystemsWorker = () => { const ref = useRef | null>(null) @@ -16,5 +70,17 @@ export const SystemsWorker = () => { } }, []) + useEvent( + COMPUTE_LAYOUT_EVENT, + async ({ detail: { houseId, systemId, dnas } }: ComputeLayoutEvent) => { + const layout = await ref.current?.computeLayout({ + houseId, + systemId, + dnas, + }) + if (layout) dispatchComputedLayoutEvent({ houseId, layout }) + } + ) + return null } diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index 2342c28b..93732061 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -1,7 +1,12 @@ import { expose } from "comlink" import { liveQuery } from "dexie" +import { pipe } from "fp-ts/lib/function" +import { ComputeLayoutEventDetail } from "." import { vanillaTrpc } from "../../../client/trpc" +import { Module } from "../../../server/data/modules" import systemsDB, { IndexedModule } from "../../db/systems" +import { modulesToColumnLayout } from "../../design/state/layouts" +import { A, pipeLog, RA } from "../../utils/functions" const initModules = async () => { const remoteModules = await vanillaTrpc.modules.query() @@ -39,7 +44,11 @@ init() const modulesObservable = liveQuery(() => systemsDB.modules.toArray()) +let allSystemsModules: Module[] = [] + modulesObservable.subscribe((modules) => { + allSystemsModules = modules + modules.map(async (nextModule) => { const { speckleBranchUrl, lastFetched } = nextModule const maybeModel = await systemsDB.models.get(speckleBranchUrl) @@ -79,7 +88,31 @@ modulesObservable.subscribe((modules) => { }) }) -const api = {} +export const computeLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { + // dnas to modules array + const modules = pipe( + dnas, + A.filterMap((dna) => + pipe( + allSystemsModules, + pipeLog, + A.findFirst( + (systemModule: Module) => + systemModule.systemId === systemId && systemModule.dna === dna + ) + ) + ) + ) + + // modules to rows + const columnLayout = modulesToColumnLayout(modules) + + return columnLayout +} + +const api = { + computeLayout, +} export type SystemsAPI = typeof api From 4e881a64195afdcbc4c2517cbd5cd8da65e27e0a Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Sun, 25 Jun 2023 19:41:23 +0100 Subject: [PATCH 009/132] wip minor --- app/design/state/layouts.ts | 1 + app/design/ui-3d/propped/ProppedHouse.tsx | 22 ++++++- app/workers/systems/worker.ts | 3 +- t | 73 +++++++++++++++++++++++ 4 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 t diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 4d9c3ff0..32fcb568 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -1,3 +1,4 @@ +import { Module } from "@/server/data/modules" import { transpose as transposeA } from "fp-ts-std/Array" import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" import * as A from "fp-ts/Array" diff --git a/app/design/ui-3d/propped/ProppedHouse.tsx b/app/design/ui-3d/propped/ProppedHouse.tsx index 24eaf74f..1d3e0de9 100644 --- a/app/design/ui-3d/propped/ProppedHouse.tsx +++ b/app/design/ui-3d/propped/ProppedHouse.tsx @@ -22,7 +22,27 @@ const ProppedHouse = (props: Props) => { }, [dnas, houseId, systemId]) useHouseLayoutEvents(houseId, (layout) => { - console.log(JSON.stringify(layout)) + // console.log( + // JSON.stringify( + // layout.map(({ columnIndex, gridGroups, length, z }) => ({ + // columnIndex, + // length, + // z, + // gridGroups: gridGroups.map( + // ({ length, levelIndex, levelType, modules, y }) => ({ + // length, + // levelIndex, + // levelType, + // y, + // modules: modules.map(({ gridGroupIndex, z }) => ({ + // gridGroupIndex, + // z, + // })), + // }) + // ), + // })) + // ) + // ) }) return diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index 93732061..758bf36a 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -6,7 +6,7 @@ import { vanillaTrpc } from "../../../client/trpc" import { Module } from "../../../server/data/modules" import systemsDB, { IndexedModule } from "../../db/systems" import { modulesToColumnLayout } from "../../design/state/layouts" -import { A, pipeLog, RA } from "../../utils/functions" +import { A } from "../../utils/functions" const initModules = async () => { const remoteModules = await vanillaTrpc.modules.query() @@ -95,7 +95,6 @@ export const computeLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { A.filterMap((dna) => pipe( allSystemsModules, - pipeLog, A.findFirst( (systemModule: Module) => systemModule.systemId === systemId && systemModule.dna === dna diff --git a/t b/t new file mode 100644 index 00000000..5c2db95f --- /dev/null +++ b/t @@ -0,0 +1,73 @@ +diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts +index 059800a..e8c9a1c 100644 +--- a/app/design/state/layouts.ts ++++ b/app/design/state/layouts.ts +@@ -1,3 +1,4 @@ ++import { Module } from "@/server/data/modules" + import { transpose as transposeA } from "fp-ts-std/Array" + import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" + import * as A from "fp-ts/Array" +@@ -6,9 +7,7 @@ import * as RA from "fp-ts/ReadonlyArray" + import produce from "immer" + import { proxy, ref } from "valtio" + import { usePadColumn } from "../../data/modules" +-import { Module } from "@/server/data/modules" + import { modulesToRows, useDnaModules, useHouseModules } from "./houses" +-import { pipeLog } from "../../utils/functions" +  + export type PositionedModule = { + module: Module +diff --git a/app/design/ui-3d/propped/ProppedHouse.tsx b/app/design/ui-3d/propped/ProppedHouse.tsx +index 24eaf74..1a556e6 100644 +--- a/app/design/ui-3d/propped/ProppedHouse.tsx ++++ b/app/design/ui-3d/propped/ProppedHouse.tsx +@@ -22,7 +22,27 @@ const ProppedHouse = (props: Props) => { + }, [dnas, houseId, systemId]) +  + useHouseLayoutEvents(houseId, (layout) => { +- console.log(JSON.stringify(layout)) ++ console.log( ++ JSON.stringify( ++ layout.map(({ columnIndex, gridGroups, length, z }) => ({ ++ columnIndex, ++ length, ++ z, ++ gridGroups: gridGroups.map( ++ ({ length, levelIndex, levelType, modules, y }) => ({ ++ length, ++ levelIndex, ++ levelType, ++ y, ++ modules: modules.map(({ gridGroupIndex, z }) => ({ ++ gridGroupIndex, ++ z, ++ })), ++ }) ++ ), ++ })) ++ ) ++ ) + }) +  + return  +diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts +index 9373206..758bf36 100644 +--- a/app/workers/systems/worker.ts ++++ b/app/workers/systems/worker.ts +@@ -6,7 +6,7 @@ import { vanillaTrpc } from "../../../client/trpc" + import { Module } from "../../../server/data/modules" + import systemsDB, { IndexedModule } from "../../db/systems" + import { modulesToColumnLayout } from "../../design/state/layouts" +-import { A, pipeLog, RA } from "../../utils/functions" ++import { A } from "../../utils/functions" +  + const initModules = async () => { + const remoteModules = await vanillaTrpc.modules.query() +@@ -95,7 +95,6 @@ export const computeLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { + A.filterMap((dna) => + pipe( + allSystemsModules, +- pipeLog, + A.findFirst( + (systemModule: Module) => + systemModule.systemId === systemId && systemModule.dna === dna From 628c2a018c7ee63a659e3406306c7f123a965ade Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 26 Jun 2023 10:43:46 +0100 Subject: [PATCH 010/132] wip fix double import --- app/design/state/layouts.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 32fcb568..692a479e 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -7,7 +7,6 @@ import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" import { proxy, ref } from "valtio" import { usePadColumn } from "../../data/modules" -import { Module } from "@/server/data/modules" import { modulesToRows, useDnasModules, useHouseModules } from "./houses" export type PositionedModule = { From d91ac00fccd25cc065023e2193d15fa3dd35b638 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 27 Jun 2023 08:09:36 +0100 Subject: [PATCH 011/132] wip quieten speckle ObjectLoader --- server/data/speckleModel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/data/speckleModel.ts b/server/data/speckleModel.ts index f3fcb4fe..5ae89c23 100644 --- a/server/data/speckleModel.ts +++ b/server/data/speckleModel.ts @@ -54,8 +54,8 @@ export const getSpeckleObject = async (speckleBranchUrl: string) => { options: { enableCaching: false, excludeProps: [], - customLogger: undefined, - customWarner: undefined, + customLogger: () => {}, + customWarner: () => {}, fullyTraverseArrays: undefined, }, }) From 921fdfc1c14540bb853293d6fd34022a838924bc Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 27 Jun 2023 08:10:34 +0100 Subject: [PATCH 012/132] wip delete rogue file --- t | 73 --------------------------------------------------------------- 1 file changed, 73 deletions(-) delete mode 100644 t diff --git a/t b/t deleted file mode 100644 index 5c2db95f..00000000 --- a/t +++ /dev/null @@ -1,73 +0,0 @@ -diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts -index 059800a..e8c9a1c 100644 ---- a/app/design/state/layouts.ts -+++ b/app/design/state/layouts.ts -@@ -1,3 +1,4 @@ -+import { Module } from "@/server/data/modules" - import { transpose as transposeA } from "fp-ts-std/Array" - import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" - import * as A from "fp-ts/Array" -@@ -6,9 +7,7 @@ import * as RA from "fp-ts/ReadonlyArray" - import produce from "immer" - import { proxy, ref } from "valtio" - import { usePadColumn } from "../../data/modules" --import { Module } from "@/server/data/modules" - import { modulesToRows, useDnaModules, useHouseModules } from "./houses" --import { pipeLog } from "../../utils/functions" -  - export type PositionedModule = { - module: Module -diff --git a/app/design/ui-3d/propped/ProppedHouse.tsx b/app/design/ui-3d/propped/ProppedHouse.tsx -index 24eaf74..1a556e6 100644 ---- a/app/design/ui-3d/propped/ProppedHouse.tsx -+++ b/app/design/ui-3d/propped/ProppedHouse.tsx -@@ -22,7 +22,27 @@ const ProppedHouse = (props: Props) => { - }, [dnas, houseId, systemId]) -  - useHouseLayoutEvents(houseId, (layout) => { -- console.log(JSON.stringify(layout)) -+ console.log( -+ JSON.stringify( -+ layout.map(({ columnIndex, gridGroups, length, z }) => ({ -+ columnIndex, -+ length, -+ z, -+ gridGroups: gridGroups.map( -+ ({ length, levelIndex, levelType, modules, y }) => ({ -+ length, -+ levelIndex, -+ levelType, -+ y, -+ modules: modules.map(({ gridGroupIndex, z }) => ({ -+ gridGroupIndex, -+ z, -+ })), -+ }) -+ ), -+ })) -+ ) -+ ) - }) -  - return  -diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts -index 9373206..758bf36 100644 ---- a/app/workers/systems/worker.ts -+++ b/app/workers/systems/worker.ts -@@ -6,7 +6,7 @@ import { vanillaTrpc } from "../../../client/trpc" - import { Module } from "../../../server/data/modules" - import systemsDB, { IndexedModule } from "../../db/systems" - import { modulesToColumnLayout } from "../../design/state/layouts" --import { A, pipeLog, RA } from "../../utils/functions" -+import { A } from "../../utils/functions" -  - const initModules = async () => { - const remoteModules = await vanillaTrpc.modules.query() -@@ -95,7 +95,6 @@ export const computeLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { - A.filterMap((dna) => - pipe( - allSystemsModules, -- pipeLog, - A.findFirst( - (systemModule: Module) => - systemModule.systemId === systemId && systemModule.dna === dna From cd1eb71f9fa0adf5cab22038393081499b2777cd Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 27 Jun 2023 08:11:52 +0100 Subject: [PATCH 013/132] wip global singleton systems worker --- app/design/page.tsx | 8 ++++++-- app/layout.tsx | 32 ++++++++++++++------------------ app/workers/systems/global.ts | 15 +++++++++++++++ app/workers/systems/index.ts | 2 +- app/workers/systems/worker.ts | 6 ++++-- 5 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 app/workers/systems/global.ts diff --git a/app/design/page.tsx b/app/design/page.tsx index e5ea94b2..fdbc6ac5 100644 --- a/app/design/page.tsx +++ b/app/design/page.tsx @@ -1,14 +1,18 @@ +"use client" import DataInit from "~/data/DataInit" import { TrpcProvider } from "../ui/TrpcProvider" +import getSystemsWorker from "../workers/systems/global" +import GroupedApp from "./ui-3d/grouped/GroupedApp" import AppInit from "./ui-3d/init/AppInit" -import ProppedApp from "./ui-3d/propped/ProppedApp" +// import ProppedApp from "./ui-3d/propped/ProppedApp" const IndexPage = () => { + getSystemsWorker() return ( - + diff --git a/app/layout.tsx b/app/layout.tsx index 70f9fcb5..bca59b3c 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,10 +1,9 @@ import clsx from "clsx" import { Inter } from "next/font/google" -import { Fragment, PropsWithChildren } from "react" +import { PropsWithChildren } from "react" import "~/styles/globals.css" import Footer from "./ui/Footer" import Nav from "./ui/Nav" -import { SystemsWorker } from "./workers/systems" const inter = Inter({ subsets: ["latin"], @@ -13,22 +12,19 @@ const inter = Inter({ const Layout = ({ children }: PropsWithChildren<{}>) => { return ( - - - -
-
-
- {children} -
-
-
-
- - - -
+ + +
+
+
+ {children} +
+
+
+
+ + ) } diff --git a/app/workers/systems/global.ts b/app/workers/systems/global.ts new file mode 100644 index 00000000..43c5a1da --- /dev/null +++ b/app/workers/systems/global.ts @@ -0,0 +1,15 @@ +import { Remote, wrap } from "comlink" +import { SystemsAPI } from "./worker" + +let worker: Remote | null = null + +const getSystemsWorker = () => { + if (worker === null) { + worker = wrap( + new Worker(new URL("./worker.ts", import.meta.url)) + ) + } + return worker +} + +export default getSystemsWorker diff --git a/app/workers/systems/index.ts b/app/workers/systems/index.ts index 044bf985..1fc308e5 100644 --- a/app/workers/systems/index.ts +++ b/app/workers/systems/index.ts @@ -73,7 +73,7 @@ export const SystemsWorker = () => { useEvent( COMPUTE_LAYOUT_EVENT, async ({ detail: { houseId, systemId, dnas } }: ComputeLayoutEvent) => { - const layout = await ref.current?.computeLayout({ + const layout = await ref.current?.getLayout({ houseId, systemId, dnas, diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index 758bf36a..237fe300 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -88,7 +88,9 @@ modulesObservable.subscribe((modules) => { }) }) -export const computeLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { +export const getLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { + // TODO: cache in the db! + // dnas to modules array const modules = pipe( dnas, @@ -110,7 +112,7 @@ export const computeLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { } const api = { - computeLayout, + getLayout: getLayout, } export type SystemsAPI = typeof api From b3e65fe549048b5c45705652d098d2f2b0ed6722 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 27 Jun 2023 10:30:36 +0100 Subject: [PATCH 014/132] wip idb houses --- app/data/elements.ts | 2 +- app/debug/system/ui-3d/DebugSpeckleModule.tsx | 2 +- app/design/state/houses.ts | 56 ++++++--- app/utils/functions.ts | 8 ++ app/utils/speckle/useSpeckleObject.ts | 119 ------------------ app/workers/systems/global.ts | 4 +- app/workers/systems/worker.ts | 52 ++++---- 7 files changed, 77 insertions(+), 166 deletions(-) delete mode 100644 app/utils/speckle/useSpeckleObject.ts diff --git a/app/data/elements.ts b/app/data/elements.ts index 3f476d50..cff9caf6 100644 --- a/app/data/elements.ts +++ b/app/data/elements.ts @@ -57,7 +57,7 @@ export const invertModuleElementGeometriesKey = (input: string) => { return { systemId, dna } } -const useSpeckleObject = (speckleBranchUrl: string) => { +export const useSpeckleObject = (speckleBranchUrl: string) => { const loader = useMemo(() => new BufferGeometryLoader(), []) const geometries = useLiveQuery(async () => { diff --git a/app/debug/system/ui-3d/DebugSpeckleModule.tsx b/app/debug/system/ui-3d/DebugSpeckleModule.tsx index ecbfc3da..875300ee 100644 --- a/app/debug/system/ui-3d/DebugSpeckleModule.tsx +++ b/app/debug/system/ui-3d/DebugSpeckleModule.tsx @@ -3,9 +3,9 @@ import { pipe } from "fp-ts/lib/function" import { useMemo, useRef } from "react" import { Group, Material, MeshBasicMaterial } from "three" import { Module } from "../../../../server/data/modules" +import { useSpeckleObject } from "../../../data/elements" import { useGetDefaultElementMaterial } from "../../../design/state/hashedMaterials" import { O, R, S } from "../../../utils/functions" -import useSpeckleObject from "../../../utils/speckle/useSpeckleObject" import DebugSpeckleElement from "./DebugSpeckleElement" const DebugSpeckleModule = ({ module }: { module: Module }) => { diff --git a/app/design/state/houses.ts b/app/design/state/houses.ts index 61a9c0f6..31843ae1 100644 --- a/app/design/state/houses.ts +++ b/app/design/state/houses.ts @@ -5,37 +5,55 @@ import { none, some } from "fp-ts/lib/Option" import { keys } from "fp-ts/lib/ReadonlyRecord" import produce from "immer" import { nanoid } from "nanoid" -import { useEffect, useMemo } from "react" +import { useMemo } from "react" import { useKey } from "react-use" import { Vector3 } from "three" -import { proxy, subscribe, useSnapshot } from "valtio" -import { A, R, RA, RR, S } from "~/utils/functions" -import { getHousesFromLocalStorage, House, Houses } from "../../data/houses" +import { proxy, snapshot, subscribe, useSnapshot } from "valtio" +import { A, clearRecord, R, RA, RR, S } from "~/utils/functions" +import { House, Houses } from "../../data/houses" import { useHouseTypes } from "../../data/houseTypes" import { useModules, useSystemModules } from "../../data/modules" -import { BUILDX_LOCAL_STORAGE_HOUSES_KEY } from "./constants" +import userDB from "../../db/user" +import { isSSR } from "../../utils/next" -const houses = proxy(getHousesFromLocalStorage()) +const houses = proxy({}) -export const useLocallyStoredHouses = () => - useEffect( - () => - subscribe(houses, () => { - localStorage.setItem( - BUILDX_LOCAL_STORAGE_HOUSES_KEY, - JSON.stringify(houses) - ) - }), - [] - ) +const initHouses = async () => { + if (isSSR()) return + + const housesArray = await userDB.houses.toArray() + if (!A.isNonEmpty(housesArray)) { + clearRecord(houses) + } + + housesArray.forEach((house) => { + houses[house.id] = house + }) +} + +initHouses().then(() => { + const unsubscribe = subscribe(houses, () => { + // This will run every time `houses` changes + Object.values(houses).forEach(async (house) => { + const snapshotHouse = snapshot(house) as typeof house + // Check if house exists in the DB + const existingHouse = await userDB.houses.get(house.id) + if (existingHouse) { + // If it exists, update it + userDB.houses.update(house.id, snapshotHouse) + } else { + // If it doesn't exist, add it + userDB.houses.add(snapshotHouse) + } + }) + }) +}) export const useHouses = () => { - useLocallyStoredHouses() return useSnapshot(houses) as typeof houses } export const useHouseKeys = () => { - useLocallyStoredHouses() return pipe(useSnapshot(houses) as typeof houses, RR.keys) } diff --git a/app/utils/functions.ts b/app/utils/functions.ts index 51a517fa..464aa23d 100644 --- a/app/utils/functions.ts +++ b/app/utils/functions.ts @@ -102,3 +102,11 @@ export const capitalizeFirstLetters = (str: string): string => .split(" ") .map((x) => x.charAt(0).toUpperCase()) .join("") + +export const clearRecord = (obj: Record) => { + for (let key in obj) { + if (obj.hasOwnProperty(key)) { + delete obj[key] + } + } +} diff --git a/app/utils/speckle/useSpeckleObject.ts b/app/utils/speckle/useSpeckleObject.ts deleted file mode 100644 index 197be000..00000000 --- a/app/utils/speckle/useSpeckleObject.ts +++ /dev/null @@ -1,119 +0,0 @@ -"use client" -import CryptoJS from "crypto-js" -import Dexie from "dexie" -import { pipe } from "fp-ts/lib/function" -import { useMemo } from "react" -import { suspend } from "suspend-react" -import { BufferGeometry, BufferGeometryLoader } from "three" -import { trpc } from "../../../client/trpc" -import { useModules } from "../../data/modules" -import { R } from "../functions" - -// Define a TypeScript interface for the data stored in the database -interface GeometryRecord { - speckleBranchUrl: string - geometries: any - timestamp: number - hash: string -} - -// Extend Dexie to create a database with the given structure -class SpeckleGeometryDatabase extends Dexie { - geometries: Dexie.Table // "string" is the type of the primary key - - constructor() { - super("SpeckleGeometryDatabase") - this.version(1).stores({ - geometries: "speckleBranchUrl,timestamp,hash", - }) - this.geometries = this.table("geometries") - } -} - -// Create Dexie database -const db = new SpeckleGeometryDatabase() - -// Extract the core logic to a new function -const useFetchGeometry = () => { - const { speckleModel } = trpc.useContext() - - const fetchGeometry = async ( - speckleBranchUrl: string - ): Promise> => { - // Try to get geometry from IndexedDB - const cachedJsonGeometries = await db.geometries.get(speckleBranchUrl) - - if (cachedJsonGeometries) { - // Here you can add additional conditions to check if the cached data is still valid - // e.g., compare the current timestamp with the cached timestamp and decide if you need to fetch new data - return cachedJsonGeometries.geometries - } else { - // If not found in IndexedDB, fetch from server - // Replace the following line with your data fetching logic - - const ifcJsonGeometries = await speckleModel.fetch({ - speckleBranchUrl, - }) - - // Hash the geometries - const geometriesHash = CryptoJS.MD5( - JSON.stringify(ifcJsonGeometries) - ).toString() - - // Cache the result in IndexedDB along with the current timestamp and hash - await db.geometries.put({ - speckleBranchUrl, - geometries: ifcJsonGeometries, - timestamp: new Date().getTime(), - hash: geometriesHash, - }) - - return ifcJsonGeometries - } - } - return fetchGeometry -} - -const useSpeckleObject = ( - speckleBranchUrl: string -): Record => { - const fetchGeometry = useFetchGeometry() - const ifcJsonGeometries = suspend(fetchGeometry, [speckleBranchUrl], { - lifespan: 3600000, - }) // cache for 1 hour - - const loader = useMemo(() => new BufferGeometryLoader(), []) - - return useMemo( - () => - pipe( - ifcJsonGeometries, - R.map((x) => loader.parse(x) as BufferGeometry) - ), - [ifcJsonGeometries, loader] - ) -} - -export const useSpeckleObjects = ( - speckleBranchUrls: string[] -): Record[] => { - const fetchGeometry = useFetchGeometry() - - // Use suspend-react to fetch the geometries - const geometries = speckleBranchUrls.map((url) => { - return suspend(fetchGeometry, [url], { lifespan: 3600000 }) // cache for 1 hour - }) - - const loader = useMemo(() => new BufferGeometryLoader(), []) - - // Transform the JSON geometries into BufferGeometry objects - return geometries.map((geometry, i) => { - const bufferGeometries: Record = {} - for (const key in geometry) { - bufferGeometries[key] = loader.parse(geometry[key]) as BufferGeometry - } - return bufferGeometries - }) -} - -export default useSpeckleObject diff --git a/app/workers/systems/global.ts b/app/workers/systems/global.ts index 43c5a1da..eda8842b 100644 --- a/app/workers/systems/global.ts +++ b/app/workers/systems/global.ts @@ -1,10 +1,12 @@ +"use client" import { Remote, wrap } from "comlink" +import { isSSR } from "../../utils/next" import { SystemsAPI } from "./worker" let worker: Remote | null = null const getSystemsWorker = () => { - if (worker === null) { + if (!isSSR() && worker === null) { worker = wrap( new Worker(new URL("./worker.ts", import.meta.url)) ) diff --git a/app/workers/systems/worker.ts b/app/workers/systems/worker.ts index 237fe300..7c3ba735 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems/worker.ts @@ -1,12 +1,17 @@ import { expose } from "comlink" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" +import produce from "immer" +import { BufferGeometry } from "three" +import { mergeBufferGeometries } from "three-stdlib" import { ComputeLayoutEventDetail } from "." import { vanillaTrpc } from "../../../client/trpc" import { Module } from "../../../server/data/modules" +import { getSpeckleObject } from "../../../server/data/speckleModel" import systemsDB, { IndexedModule } from "../../db/systems" import { modulesToColumnLayout } from "../../design/state/layouts" -import { A } from "../../utils/functions" +import { A, R } from "../../utils/functions" +import speckleIfcParser from "../../utils/speckle/speckleIfcParser" const initModules = async () => { const remoteModules = await vanillaTrpc.modules.query() @@ -55,34 +60,31 @@ modulesObservable.subscribe((modules) => { if ( maybeModel && - new Date(maybeModel.lastFetched) === new Date(lastFetched) + new Date(maybeModel.lastFetched).getTime() === + new Date(lastFetched).getTime() ) { return } - const geometries = await vanillaTrpc.speckleModel.query({ - speckleBranchUrl, - }) - - // const speckleObjectData = await getSpeckleObject(speckleBranchUrl) - // const speckleObject = speckleIfcParser.parse(speckleObjectData) - // const geometries = pipe( - // speckleObject, - // A.reduce( - // {}, - // (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { - // return produce(acc, (draft) => { - // if (ifcTag in draft) draft[ifcTag].push(geometry) - // else draft[ifcTag] = [geometry] - // }) - // } - // ), - // R.map((geoms) => mergeBufferGeometries(geoms)), - // R.filter((bg: BufferGeometry | null): bg is BufferGeometry => - // Boolean(bg) - // ), - // R.map((x) => x.toJSON()) - // ) + const speckleObjectData = await getSpeckleObject(speckleBranchUrl) + const speckleObject = speckleIfcParser.parse(speckleObjectData) + const geometries = pipe( + speckleObject, + A.reduce( + {}, + (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { + return produce(acc, (draft) => { + if (ifcTag in draft) draft[ifcTag].push(geometry) + else draft[ifcTag] = [geometry] + }) + } + ), + R.map((geoms) => mergeBufferGeometries(geoms)), + R.filter((bg: BufferGeometry | null): bg is BufferGeometry => + Boolean(bg) + ), + R.map((x) => x.toJSON()) + ) systemsDB.models.put({ speckleBranchUrl, lastFetched, geometries }) }) From c21c84efb509788f26e76cfcacaaf65e3928012d Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 27 Jun 2023 17:21:20 +0100 Subject: [PATCH 015/132] wip --- app/design/state/layouts.ts | 2 +- app/workers/house/useHouseWorker.ts | 17 ----------- app/workers/house/worker.ts | 7 ----- app/workers/utils/index.ts | 46 ----------------------------- 4 files changed, 1 insertion(+), 71 deletions(-) delete mode 100644 app/workers/house/useHouseWorker.ts delete mode 100644 app/workers/house/worker.ts delete mode 100644 app/workers/utils/index.ts diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 692a479e..9976396e 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -60,7 +60,7 @@ export type SystemHouseModuleIdentifier = HouseModuleIdentifier & { export type ColumnLayout = Array export const layouts = proxy< - Record // houseId + Record // systemId:dnas : Layout >({}) export const useRowLayout = (houseId: string): RowLayout => diff --git a/app/workers/house/useHouseWorker.ts b/app/workers/house/useHouseWorker.ts deleted file mode 100644 index 883eb094..00000000 --- a/app/workers/house/useHouseWorker.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { releaseProxy, Remote, wrap } from "comlink" -import { useEffect, useRef } from "react" -import { HouseWorker } from "./worker" - -export const useHouseWorker = () => { - const ref = useRef | null>(null) - - useEffect(() => { - const worker = new Worker(new URL("./worker.ts", import.meta.url)) - ref.current = wrap(worker) - - return () => { - ref.current?.[releaseProxy]() - worker.terminate() - } - }, []) -} diff --git a/app/workers/house/worker.ts b/app/workers/house/worker.ts deleted file mode 100644 index 21b71de2..00000000 --- a/app/workers/house/worker.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { expose } from "comlink" - -const api = {} - -export type HouseWorker = typeof api - -expose(api) diff --git a/app/workers/utils/index.ts b/app/workers/utils/index.ts deleted file mode 100644 index 757f5cdd..00000000 --- a/app/workers/utils/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { useEffect, useRef } from "react" -import { Remote, wrap, releaseProxy } from "comlink" - -export const useWorker = (workerURL: URL) => { - const ref = useRef | null>(null) - - useEffect(() => { - const worker = new Worker(workerURL) - ref.current = wrap(worker) - - return () => { - ref.current?.[releaseProxy]() - worker.terminate() - } - }, [workerURL]) - - return ref -} - -export const useSharedWorker = (workerURL: URL) => { - const ref = useRef | null>(null) - - useEffect(() => { - let worker: SharedWorker | Worker - try { - worker = new SharedWorker(workerURL) - ref.current = wrap(worker.port) - worker.port.start() - } catch (error) { - // Fallback to a regular Worker if SharedWorker is not supported - worker = new Worker(workerURL) - ref.current = wrap(worker) - } - - return () => { - ref.current?.[releaseProxy]() - if (worker instanceof SharedWorker) { - worker.port.close() - } else { - worker.terminate() - } - } - }, [workerURL]) - - return ref -} From 2834e83f7f98b27ae53a5257a9cb5126d4b4f5f7 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 27 Jun 2023 17:52:35 +0100 Subject: [PATCH 016/132] wip restructure --- app/data/elements.ts | 3 +- app/db/layouts.ts | 28 ++++++ app/db/systems.ts | 13 +-- app/db/user.ts | 4 +- app/design/page.tsx | 3 +- app/design/ui-3d/grouped/GroupedHouse.tsx | 2 + app/design/ui-3d/propped/ProppedApp.tsx | 77 ----------------- app/design/ui-3d/propped/ProppedHouse.tsx | 51 ----------- app/design/ui-3d/propped/ProppedSystem.tsx | 39 --------- app/design/ui/SiteSidebar.tsx | 11 +-- app/workers/{systems/global.ts => index.ts} | 6 +- app/workers/layouts.ts | 11 +++ app/workers/{systems/worker.ts => systems.ts} | 65 +++++++------- app/workers/systems/index.ts | 86 ------------------- 14 files changed, 90 insertions(+), 309 deletions(-) create mode 100644 app/db/layouts.ts delete mode 100644 app/design/ui-3d/propped/ProppedApp.tsx delete mode 100644 app/design/ui-3d/propped/ProppedHouse.tsx delete mode 100644 app/design/ui-3d/propped/ProppedSystem.tsx rename app/workers/{systems/global.ts => index.ts} (65%) create mode 100644 app/workers/layouts.ts rename app/workers/{systems/worker.ts => systems.ts} (66%) delete mode 100644 app/workers/systems/index.ts diff --git a/app/data/elements.ts b/app/data/elements.ts index cff9caf6..936069c7 100644 --- a/app/data/elements.ts +++ b/app/data/elements.ts @@ -6,6 +6,7 @@ import { BufferGeometry, BufferGeometryLoader } from "three" import { O, R, RA, S } from "~/utils/functions" import { Element } from "../../server/data/elements" import { Module } from "../../server/data/modules" +import layoutsDB from "../db/layouts" import systemsDB from "../db/systems" // import useSpeckleObject from "../utils/speckle/useSpeckleObject" @@ -61,7 +62,7 @@ export const useSpeckleObject = (speckleBranchUrl: string) => { const loader = useMemo(() => new BufferGeometryLoader(), []) const geometries = useLiveQuery(async () => { - const model = await systemsDB.models.get(speckleBranchUrl) + const model = await layoutsDB.models.get(speckleBranchUrl) return model?.geometries }) diff --git a/app/db/layouts.ts b/app/db/layouts.ts new file mode 100644 index 00000000..8d31baf4 --- /dev/null +++ b/app/db/layouts.ts @@ -0,0 +1,28 @@ +import Dexie from "dexie" +import { ColumnLayout } from "../design/state/layouts" + +export type IndexedModel = { + speckleBranchUrl: string + lastFetched: string + geometries: any +} + +class LayoutsDatabase extends Dexie { + models: Dexie.Table + layouts: Dexie.Table + + constructor() { + super("LayoutsDatabase") + this.version(1).stores({ + models: "speckleBranchUrl", + layouts: "systemIdDnas", + }) + this.layouts = this.table("layouts") + this.models = this.table("models") + } +} + +// Create Dexie database +const layoutsDB = new LayoutsDatabase() + +export default layoutsDB diff --git a/app/db/systems.ts b/app/db/systems.ts index 6d1d47f6..8d351b7c 100644 --- a/app/db/systems.ts +++ b/app/db/systems.ts @@ -1,32 +1,23 @@ -import Dexie from "dexie" -import { Module } from "@/server/data/modules" import { Block } from "@/server/data/blocks" +import { Module } from "@/server/data/modules" +import Dexie from "dexie" export type IndexedModule = Module & { lastFetched: string } -export type IndexedModel = { - speckleBranchUrl: string - lastFetched: string - geometries: any -} - class SystemsDatabase extends Dexie { blocks: Dexie.Table // "string" is the type of the primary key modules: Dexie.Table // "string" is the type of the primary key - models: Dexie.Table constructor() { super("SystemsDatabase") this.version(1).stores({ blocks: "id,systemId,name", modules: "id,systemId,dna", - models: "speckleBranchUrl", }) this.blocks = this.table("blocks") this.modules = this.table("modules") - this.models = this.table("models") } } diff --git a/app/db/user.ts b/app/db/user.ts index 826764ab..48ec28a2 100644 --- a/app/db/user.ts +++ b/app/db/user.ts @@ -1,6 +1,8 @@ import Dexie from "dexie" import { useLiveQuery } from "dexie-react-hooks" +import { useSnapshot } from "valtio" import { z } from "zod" +import houses from "../design/state/houses" export const houseParser = z.object({ id: z.string().min(1), @@ -33,6 +35,6 @@ class UserDatabase extends Dexie { const userDB = new UserDatabase() -export const useHouses = () => useLiveQuery(() => userDB.houses.toArray()) ?? [] +export const useHouses = () => useSnapshot(houses) export default userDB diff --git a/app/design/page.tsx b/app/design/page.tsx index fdbc6ac5..da150ae6 100644 --- a/app/design/page.tsx +++ b/app/design/page.tsx @@ -1,10 +1,9 @@ "use client" import DataInit from "~/data/DataInit" import { TrpcProvider } from "../ui/TrpcProvider" -import getSystemsWorker from "../workers/systems/global" +import getSystemsWorker from "../workers" import GroupedApp from "./ui-3d/grouped/GroupedApp" import AppInit from "./ui-3d/init/AppInit" -// import ProppedApp from "./ui-3d/propped/ProppedApp" const IndexPage = () => { getSystemsWorker() diff --git a/app/design/ui-3d/grouped/GroupedHouse.tsx b/app/design/ui-3d/grouped/GroupedHouse.tsx index e3ed70d7..3539c64a 100644 --- a/app/design/ui-3d/grouped/GroupedHouse.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse.tsx @@ -74,6 +74,8 @@ const GroupedHouse = (props: Props) => { useHouseMaterialOps(houseId, houseGroupRef) + console.log("hi") + return ( diff --git a/app/design/ui-3d/propped/ProppedApp.tsx b/app/design/ui-3d/propped/ProppedApp.tsx deleted file mode 100644 index fd178442..00000000 --- a/app/design/ui-3d/propped/ProppedApp.tsx +++ /dev/null @@ -1,77 +0,0 @@ -"use client" -import { pipe } from "fp-ts/lib/function" -import { Fragment, Suspense } from "react" -import { usePreviews } from "~/design/state/previews" -import { useRouting } from "~/design/state/routing" -import { A, NEA, RA } from "~/utils/functions" -import { useExportersWorker } from "../../workers/exporters" -import { useDragHandler, useGestures } from "../../state/gestures" -import { useHouseKeys } from "../../state/houses" -import XZPlane from "../XZPlane" -import YPlane from "../YPlane" -import { useLiveQuery } from "dexie-react-hooks" -import userDB from "../../../db/user" -import { systems } from "../../../../server/data/system" -import systemsDB from "../../../db/systems" -import Loader from "../../../ui/Loader" -import ProppedSystem from "./ProppedSystem" -import { useKey } from "react-use" -// import GroupedHouse from "./GroupedHouse" - -const ProppedApp = () => { - // const houses = useLiveQuery(() => userDB.houses.toArray()) ?? [] - // const houseKeys = useHouseKeys() - // usePreviews() - // const bindAll = useGestures() - // useDragHandler() - // useRouting() - - // useExportersWorker() - - const result = useLiveQuery(async () => ({ - modules: pipe( - await systemsDB.modules.toArray(), - NEA.groupBy((x) => x.systemId) - ), - models: pipe( - await systemsDB.models.toArray(), - NEA.groupBy((x) => x.speckleBranchUrl) - ), - })) - - if (!result) return null - - const { modules, models } = result - - return ( - - - {pipe( - systems, - A.map((system) => ( - - )) - )} - {/* {pipe( - houses, - RA.map((house) => ( - - - - )) - )} */} - - - - - ) -} - -export default ProppedApp diff --git a/app/design/ui-3d/propped/ProppedHouse.tsx b/app/design/ui-3d/propped/ProppedHouse.tsx deleted file mode 100644 index 1d3e0de9..00000000 --- a/app/design/ui-3d/propped/ProppedHouse.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useEffect } from "react" -import { IndexedModel, IndexedModule } from "../../../db/systems" -import { House } from "../../../db/user" -import { - dispatchComputeLayoutEvent, - useHouseLayoutEvents, -} from "../../../workers/systems" - -type Props = { - house: House - modules: IndexedModule[] - models: Record -} - -const ProppedHouse = (props: Props) => { - const { - house: { id: houseId, systemId, dnas }, - } = props - - useEffect(() => { - dispatchComputeLayoutEvent({ houseId, systemId, dnas }) - }, [dnas, houseId, systemId]) - - useHouseLayoutEvents(houseId, (layout) => { - // console.log( - // JSON.stringify( - // layout.map(({ columnIndex, gridGroups, length, z }) => ({ - // columnIndex, - // length, - // z, - // gridGroups: gridGroups.map( - // ({ length, levelIndex, levelType, modules, y }) => ({ - // length, - // levelIndex, - // levelType, - // y, - // modules: modules.map(({ gridGroupIndex, z }) => ({ - // gridGroupIndex, - // z, - // })), - // }) - // ), - // })) - // ) - // ) - }) - - return -} - -export default ProppedHouse diff --git a/app/design/ui-3d/propped/ProppedSystem.tsx b/app/design/ui-3d/propped/ProppedSystem.tsx deleted file mode 100644 index 4522d511..00000000 --- a/app/design/ui-3d/propped/ProppedSystem.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useLiveQuery } from "dexie-react-hooks" -import { pipe } from "fp-ts/lib/function" -import { IndexedModel, IndexedModule } from "../../../db/systems" -import userDB from "../../../db/user" -import { A } from "../../../utils/functions" -import ProppedHouse from "./ProppedHouse" - -type Props = { - systemId: string - modules: IndexedModule[] - models: Record -} - -const ProppedSystem = (props: Props) => { - const { systemId, modules, models } = props - - const houses = - useLiveQuery(() => - userDB.houses.filter((x) => x.systemId === systemId).toArray() - ) ?? [] - - return ( - - {pipe( - houses, - A.map((house) => ( - - )) - )} - - ) -} - -export default ProppedSystem diff --git a/app/design/ui/SiteSidebar.tsx b/app/design/ui/SiteSidebar.tsx index dbe605db..a6e74aaf 100644 --- a/app/design/ui/SiteSidebar.tsx +++ b/app/design/ui/SiteSidebar.tsx @@ -10,6 +10,7 @@ import { useHouseTypes } from "~/data/houseTypes" import Sidebar from "~/ui//Sidebar" import userDB, { useHouses } from "../../db/user" import HouseThumbnail from "./HouseThumbnail" +import houses from "../state/houses" type Props = { open: boolean @@ -32,8 +33,6 @@ const SiteSidebar = ({ open, close }: Props) => { const cameraGroundRaycast = useCameraGroundRaycast() - const houses = useHouses() - return ( {manySystems && !selectedSystem ? ( @@ -78,7 +77,7 @@ const SiteSidebar = ({ open, close }: Props) => { const position = cameraGroundRaycast() ?? new Vector3(0, 0, 0) - userDB.houses.add({ + houses[id] = { id, houseTypeId: houseType.id, systemId: houseType.systemId, @@ -86,8 +85,10 @@ const SiteSidebar = ({ open, close }: Props) => { rotation: 0, dnas: houseType.dnas as string[], modifiedMaterials: {}, - friendlyName: `Building ${houses.length + 1}`, - }) + friendlyName: `Building ${ + Object.keys(houses).length + 1 + }`, + } close() }} diff --git a/app/workers/systems/global.ts b/app/workers/index.ts similarity index 65% rename from app/workers/systems/global.ts rename to app/workers/index.ts index eda8842b..d5ae7eab 100644 --- a/app/workers/systems/global.ts +++ b/app/workers/index.ts @@ -1,14 +1,14 @@ "use client" import { Remote, wrap } from "comlink" -import { isSSR } from "../../utils/next" -import { SystemsAPI } from "./worker" +import { isSSR } from "../utils/next" +import { SystemsAPI } from "./systems" let worker: Remote | null = null const getSystemsWorker = () => { if (!isSSR() && worker === null) { worker = wrap( - new Worker(new URL("./worker.ts", import.meta.url)) + new Worker(new URL("./systems.ts", import.meta.url)) ) } return worker diff --git a/app/workers/layouts.ts b/app/workers/layouts.ts new file mode 100644 index 00000000..cd36a907 --- /dev/null +++ b/app/workers/layouts.ts @@ -0,0 +1,11 @@ +// populate the layouts database by subscribing to +// or listening for +// system id dnas events + +// init across houseTypes for those dnas + +// an individual column can be a systemIdDnas entry +// so we can use this for stretch maybe +// this is a VANILLA COLUMN + +export {} diff --git a/app/workers/systems/worker.ts b/app/workers/systems.ts similarity index 66% rename from app/workers/systems/worker.ts rename to app/workers/systems.ts index 7c3ba735..bb3af904 100644 --- a/app/workers/systems/worker.ts +++ b/app/workers/systems.ts @@ -4,14 +4,13 @@ import { pipe } from "fp-ts/lib/function" import produce from "immer" import { BufferGeometry } from "three" import { mergeBufferGeometries } from "three-stdlib" -import { ComputeLayoutEventDetail } from "." -import { vanillaTrpc } from "../../../client/trpc" -import { Module } from "../../../server/data/modules" -import { getSpeckleObject } from "../../../server/data/speckleModel" -import systemsDB, { IndexedModule } from "../../db/systems" -import { modulesToColumnLayout } from "../../design/state/layouts" -import { A, R } from "../../utils/functions" -import speckleIfcParser from "../../utils/speckle/speckleIfcParser" +import { vanillaTrpc } from "../../client/trpc" +import { Module } from "../../server/data/modules" +import { getSpeckleObject } from "../../server/data/speckleModel" +import layoutsDB from "../db/layouts" +import systemsDB, { IndexedModule } from "../db/systems" +import { A, R } from "../utils/functions" +import speckleIfcParser from "../utils/speckle/speckleIfcParser" const initModules = async () => { const remoteModules = await vanillaTrpc.modules.query() @@ -56,7 +55,7 @@ modulesObservable.subscribe((modules) => { modules.map(async (nextModule) => { const { speckleBranchUrl, lastFetched } = nextModule - const maybeModel = await systemsDB.models.get(speckleBranchUrl) + const maybeModel = await layoutsDB.models.get(speckleBranchUrl) if ( maybeModel && @@ -86,35 +85,35 @@ modulesObservable.subscribe((modules) => { R.map((x) => x.toJSON()) ) - systemsDB.models.put({ speckleBranchUrl, lastFetched, geometries }) + layoutsDB.models.put({ speckleBranchUrl, lastFetched, geometries }) }) }) -export const getLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { - // TODO: cache in the db! - - // dnas to modules array - const modules = pipe( - dnas, - A.filterMap((dna) => - pipe( - allSystemsModules, - A.findFirst( - (systemModule: Module) => - systemModule.systemId === systemId && systemModule.dna === dna - ) - ) - ) - ) - - // modules to rows - const columnLayout = modulesToColumnLayout(modules) - - return columnLayout -} +// export const getLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { +// // TODO: cache in the db! + +// // dnas to modules array +// const modules = pipe( +// dnas, +// A.filterMap((dna) => +// pipe( +// allSystemsModules, +// A.findFirst( +// (systemModule: Module) => +// systemModule.systemId === systemId && systemModule.dna === dna +// ) +// ) +// ) +// ) + +// // modules to rows +// const columnLayout = modulesToColumnLayout(modules) + +// return columnLayout +// } const api = { - getLayout: getLayout, + // getLayout: getLayout, } export type SystemsAPI = typeof api diff --git a/app/workers/systems/index.ts b/app/workers/systems/index.ts deleted file mode 100644 index 1fc308e5..00000000 --- a/app/workers/systems/index.ts +++ /dev/null @@ -1,86 +0,0 @@ -"use client" -import { releaseProxy, Remote, wrap } from "comlink" -import { useEffect, useRef } from "react" -import { useEvent } from "react-use" -import { ColumnLayout } from "../../design/state/layouts" -import { SystemsAPI } from "./worker" - -const COMPUTE_LAYOUT_EVENT = "ComputeLayoutEvent" -const COMPUTED_LAYOUT_EVENT = "ComputedLayoutEvent" - -export type ComputeLayoutEventDetail = { - houseId: string - systemId: string - dnas: string[] -} - -type ComputeLayoutEvent = { - type: typeof COMPUTE_LAYOUT_EVENT - detail: ComputeLayoutEventDetail -} - -export const dispatchComputeLayoutEvent = ( - detail: ComputeLayoutEventDetail -) => { - dispatchEvent( - new CustomEvent(COMPUTE_LAYOUT_EVENT, { - detail, - }) - ) -} - -type ComputedLayoutEventDetail = { - houseId: string - layout: ColumnLayout -} - -type ComputedLayoutEvent = { - type: typeof COMPUTED_LAYOUT_EVENT - detail: ComputedLayoutEventDetail -} - -const dispatchComputedLayoutEvent = (detail: ComputedLayoutEventDetail) => { - dispatchEvent( - new CustomEvent(COMPUTED_LAYOUT_EVENT, { - detail, - }) - ) -} - -export const useHouseLayoutEvents = ( - houseId: string, - cb: (layout: ColumnLayout) => void -) => { - useEvent(COMPUTED_LAYOUT_EVENT, ({ detail }: ComputedLayoutEvent) => { - if (houseId !== detail.houseId) return - cb(detail.layout) - }) -} - -export const SystemsWorker = () => { - const ref = useRef | null>(null) - - useEffect(() => { - const worker = new Worker(new URL("./worker.ts", import.meta.url)) - ref.current = wrap(worker) - - return () => { - ref.current?.[releaseProxy]() - worker.terminate() - } - }, []) - - useEvent( - COMPUTE_LAYOUT_EVENT, - async ({ detail: { houseId, systemId, dnas } }: ComputeLayoutEvent) => { - const layout = await ref.current?.getLayout({ - houseId, - systemId, - dnas, - }) - if (layout) dispatchComputedLayoutEvent({ houseId, layout }) - } - ) - - return null -} From 6e5216c04ddde721191aeeaab2bbbb938a554048 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 28 Jun 2023 11:05:48 +0100 Subject: [PATCH 017/132] wip idb caching layouts --- app/analyse/state/data.ts | 2 +- app/data/houses.ts | 40 -------- app/db/layouts.ts | 23 ++++- app/db/systems.ts | 14 ++- app/db/user.ts | 5 - app/design/page.tsx | 3 +- app/design/state/houses.ts | 7 +- app/design/ui-3d/grouped/GroupedHouse.tsx | 2 - app/design/ui/SiteSidebar.tsx | 6 +- app/design/ui/menu/ContextMenuEntry.tsx | 3 +- app/workers/index.ts | 22 +++-- app/workers/layouts.ts | 114 ++++++++++++++++++++-- app/workers/systems.ts | 110 +++++---------------- server/data/houseTypes.ts | 13 +++ server/data/modules.ts | 2 +- 15 files changed, 194 insertions(+), 172 deletions(-) delete mode 100644 app/data/houses.ts diff --git a/app/analyse/state/data.ts b/app/analyse/state/data.ts index 3fea5c71..3ac58c21 100644 --- a/app/analyse/state/data.ts +++ b/app/analyse/state/data.ts @@ -15,11 +15,11 @@ import { type WindowType } from "@/server/data/windowTypes" import { useSelectedHouseIds } from "../ui/HousesPillsSelector" import { useElements } from "../../data/elements" import { useEnergyInfos } from "../../data/energyInfos" -import { type House } from "../../data/houses" import { useMaterials } from "../../data/materials" import { useSpaceTypes } from "../../data/spaceTypes" import { useWindowTypes } from "../../data/windowTypes" import { useGetHouseModules, useHouses } from "../../design/state/houses" +import { type House } from "../../db/user" export type SystemsData = { houseTypes: Array diff --git a/app/data/houses.ts b/app/data/houses.ts deleted file mode 100644 index 3e44e763..00000000 --- a/app/data/houses.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { pipe } from "fp-ts/lib/function" -import * as z from "zod" -import { BUILDX_LOCAL_STORAGE_HOUSES_KEY } from "~/design/state/constants" - -export const houseParser = z.object({ - id: z.string().min(1), - houseTypeId: z.string().min(1), - systemId: z.string().min(1), - dnas: z.array(z.string().min(1)), - modifiedMaterials: z.record(z.string().min(1)), - friendlyName: z.string().min(1), - position: z.object({ - x: z.number(), - y: z.number(), - z: z.number(), - }), - rotation: z.number(), -}) - -export type House = z.infer - -export const housesParser = z.record(houseParser) - -export type Houses = z.infer - -export const getHousesFromLocalStorage = (): Houses => { - try { - return pipe( - localStorage.getItem(BUILDX_LOCAL_STORAGE_HOUSES_KEY) ?? "{}", - JSON.parse, - housesParser.parse - ) - } catch (err) { - return {} - } -} - -export const saveHouses = (houses: Houses) => { - localStorage.setItem(BUILDX_LOCAL_STORAGE_HOUSES_KEY, JSON.stringify(houses)) -} diff --git a/app/db/layouts.ts b/app/db/layouts.ts index 8d31baf4..4730f153 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -1,21 +1,34 @@ import Dexie from "dexie" import { ColumnLayout } from "../design/state/layouts" +import { LastFetchStamped } from "./systems" -export type IndexedModel = { +export type ParsedModel = { speckleBranchUrl: string - lastFetched: string geometries: any } +export type IndexedLayout = { + layoutsKey: string + layout: ColumnLayout +} + +export type LayoutsKey = { + systemId: string + dnas: string[] +} + +export const serializeLayoutsKey = ({ systemId, dnas }: LayoutsKey) => + `${systemId}:${dnas}` + class LayoutsDatabase extends Dexie { - models: Dexie.Table - layouts: Dexie.Table + models: Dexie.Table, string> + layouts: Dexie.Table constructor() { super("LayoutsDatabase") this.version(1).stores({ models: "speckleBranchUrl", - layouts: "systemIdDnas", + layouts: "layoutsKey", }) this.layouts = this.table("layouts") this.models = this.table("models") diff --git a/app/db/systems.ts b/app/db/systems.ts index 8d351b7c..d76262d3 100644 --- a/app/db/systems.ts +++ b/app/db/systems.ts @@ -1,23 +1,27 @@ import { Block } from "@/server/data/blocks" import { Module } from "@/server/data/modules" import Dexie from "dexie" +import { HouseType } from "../../server/data/houseTypes" -export type IndexedModule = Module & { - lastFetched: string +export type LastFetchStamped = T & { + lastFetched: number } class SystemsDatabase extends Dexie { - blocks: Dexie.Table // "string" is the type of the primary key - modules: Dexie.Table // "string" is the type of the primary key + modules: Dexie.Table, string> + houseTypes: Dexie.Table, string> + blocks: Dexie.Table constructor() { super("SystemsDatabase") this.version(1).stores({ blocks: "id,systemId,name", modules: "id,systemId,dna", + houseTypes: "id,systemId", }) - this.blocks = this.table("blocks") this.modules = this.table("modules") + this.houseTypes = this.table("houseTypes") + this.blocks = this.table("blocks") } } diff --git a/app/db/user.ts b/app/db/user.ts index 48ec28a2..20511366 100644 --- a/app/db/user.ts +++ b/app/db/user.ts @@ -1,8 +1,5 @@ import Dexie from "dexie" -import { useLiveQuery } from "dexie-react-hooks" -import { useSnapshot } from "valtio" import { z } from "zod" -import houses from "../design/state/houses" export const houseParser = z.object({ id: z.string().min(1), @@ -35,6 +32,4 @@ class UserDatabase extends Dexie { const userDB = new UserDatabase() -export const useHouses = () => useSnapshot(houses) - export default userDB diff --git a/app/design/page.tsx b/app/design/page.tsx index da150ae6..70e9f8a4 100644 --- a/app/design/page.tsx +++ b/app/design/page.tsx @@ -1,12 +1,13 @@ "use client" import DataInit from "~/data/DataInit" import { TrpcProvider } from "../ui/TrpcProvider" -import getSystemsWorker from "../workers" +import { getLayoutsWorker, getSystemsWorker } from "../workers" import GroupedApp from "./ui-3d/grouped/GroupedApp" import AppInit from "./ui-3d/init/AppInit" const IndexPage = () => { getSystemsWorker() + getLayoutsWorker() return ( diff --git a/app/design/state/houses.ts b/app/design/state/houses.ts index 31843ae1..8524669d 100644 --- a/app/design/state/houses.ts +++ b/app/design/state/houses.ts @@ -10,13 +10,12 @@ import { useKey } from "react-use" import { Vector3 } from "three" import { proxy, snapshot, subscribe, useSnapshot } from "valtio" import { A, clearRecord, R, RA, RR, S } from "~/utils/functions" -import { House, Houses } from "../../data/houses" import { useHouseTypes } from "../../data/houseTypes" import { useModules, useSystemModules } from "../../data/modules" -import userDB from "../../db/user" +import userDB, { House } from "../../db/user" import { isSSR } from "../../utils/next" -const houses = proxy({}) +const houses = proxy>({}) const initHouses = async () => { if (isSSR()) return @@ -32,7 +31,7 @@ const initHouses = async () => { } initHouses().then(() => { - const unsubscribe = subscribe(houses, () => { + subscribe(houses, () => { // This will run every time `houses` changes Object.values(houses).forEach(async (house) => { const snapshotHouse = snapshot(house) as typeof house diff --git a/app/design/ui-3d/grouped/GroupedHouse.tsx b/app/design/ui-3d/grouped/GroupedHouse.tsx index 3539c64a..e3ed70d7 100644 --- a/app/design/ui-3d/grouped/GroupedHouse.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse.tsx @@ -74,8 +74,6 @@ const GroupedHouse = (props: Props) => { useHouseMaterialOps(houseId, houseGroupRef) - console.log("hi") - return ( diff --git a/app/design/ui/SiteSidebar.tsx b/app/design/ui/SiteSidebar.tsx index a6e74aaf..def17cd2 100644 --- a/app/design/ui/SiteSidebar.tsx +++ b/app/design/ui/SiteSidebar.tsx @@ -1,16 +1,14 @@ import { System, systems } from "@/server/data/system" -import { useCameraGroundRaycast } from "../state/camera" import { pipe } from "fp-ts/lib/function" import { mapWithIndex } from "fp-ts/lib/ReadonlyArray" -import { keys } from "fp-ts/lib/ReadonlyRecord" import { nanoid } from "nanoid" import { Fragment, useMemo, useState } from "react" import { Vector3 } from "three" import { useHouseTypes } from "~/data/houseTypes" import Sidebar from "~/ui//Sidebar" -import userDB, { useHouses } from "../../db/user" -import HouseThumbnail from "./HouseThumbnail" +import { useCameraGroundRaycast } from "../state/camera" import houses from "../state/houses" +import HouseThumbnail from "./HouseThumbnail" type Props = { open: boolean diff --git a/app/design/ui/menu/ContextMenuEntry.tsx b/app/design/ui/menu/ContextMenuEntry.tsx index 26201a75..f620ab84 100644 --- a/app/design/ui/menu/ContextMenuEntry.tsx +++ b/app/design/ui/menu/ContextMenuEntry.tsx @@ -1,7 +1,6 @@ import { Reset, TrashCan } from "@carbon/icons-react" import { invalidate } from "@react-three/fiber" import { Fragment, useState } from "react" -import { House } from "~/data/houses" import { useHouseTypes } from "~/data/houseTypes" import scope, { useScope } from "~/design/state/scope" import { Pencil, TextCursor } from "~/ui/icons" @@ -42,7 +41,7 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { const { houseId, columnIndex, levelIndex, gridGroupIndex, elementName } = selected - const house = useHouse(houseId) as House + const house = useHouse(houseId) const editBuilding = () => { downMode(selected) diff --git a/app/workers/index.ts b/app/workers/index.ts index d5ae7eab..10be0fa2 100644 --- a/app/workers/index.ts +++ b/app/workers/index.ts @@ -1,17 +1,21 @@ "use client" import { Remote, wrap } from "comlink" import { isSSR } from "../utils/next" -import { SystemsAPI } from "./systems" +import { LayoutsAPI } from "./layouts" -let worker: Remote | null = null +let systemsWorker: Worker | null = null +let layoutsWorker: Remote | null = null -const getSystemsWorker = () => { - if (!isSSR() && worker === null) { - worker = wrap( - new Worker(new URL("./systems.ts", import.meta.url)) - ) +export const getSystemsWorker = () => { + if (!isSSR() && systemsWorker === null) { + systemsWorker = new Worker(new URL("./systems.ts", import.meta.url)) } - return worker + return systemsWorker } -export default getSystemsWorker +export const getLayoutsWorker = () => { + if (!isSSR() && layoutsWorker === null) { + layoutsWorker = wrap(new Worker(new URL("./layouts.ts", import.meta.url))) + } + return layoutsWorker +} diff --git a/app/workers/layouts.ts b/app/workers/layouts.ts index cd36a907..763266c6 100644 --- a/app/workers/layouts.ts +++ b/app/workers/layouts.ts @@ -1,11 +1,109 @@ -// populate the layouts database by subscribing to -// or listening for -// system id dnas events +import { expose } from "comlink" +import { liveQuery } from "dexie" +import { pipe } from "fp-ts/lib/function" +import produce from "immer" +import { BufferGeometry } from "three" +import { mergeBufferGeometries } from "three-stdlib" +import { Module } from "../../server/data/modules" +import { getSpeckleObject } from "../../server/data/speckleModel" +import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../db/layouts" +import systemsDB, { LastFetchStamped } from "../db/systems" +import { modulesToColumnLayout } from "../design/state/layouts" +import { A, R } from "../utils/functions" +import speckleIfcParser from "../utils/speckle/speckleIfcParser" -// init across houseTypes for those dnas +const syncModels = (modules: LastFetchStamped[]) => { + modules.map(async (nextModule) => { + const { speckleBranchUrl, lastFetched } = nextModule + const maybeModel = await layoutsDB.models.get(speckleBranchUrl) -// an individual column can be a systemIdDnas entry -// so we can use this for stretch maybe -// this is a VANILLA COLUMN + if (maybeModel && maybeModel.lastFetched === lastFetched) { + return + } -export {} + const speckleObjectData = await getSpeckleObject(speckleBranchUrl) + const speckleObject = speckleIfcParser.parse(speckleObjectData) + const geometries = pipe( + speckleObject, + A.reduce( + {}, + (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { + return produce(acc, (draft) => { + if (ifcTag in draft) draft[ifcTag].push(geometry) + else draft[ifcTag] = [geometry] + }) + } + ), + R.map((geoms) => mergeBufferGeometries(geoms)), + R.filter((bg: BufferGeometry | null): bg is BufferGeometry => + Boolean(bg) + ), + R.map((x) => x.toJSON()) + ) + + layoutsDB.models.put({ speckleBranchUrl, lastFetched, geometries }) + }) +} + +let modulesCache: LastFetchStamped[] = [] +let layoutsQueue: LayoutsKey[] = [] + +const processLayout = async ({ systemId, dnas }: LayoutsKey) => { + const modules = pipe( + dnas, + A.filterMap((dna) => + pipe( + modulesCache, + A.findFirst( + (systemModule: Module) => + systemModule.systemId === systemId && systemModule.dna === dna + ) + ) + ) + ) + + // modules to rows + const layout = modulesToColumnLayout(modules) + const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + + layoutsDB.layouts.put({ + layout, + layoutsKey, + }) +} +const processLayoutsQueue = async () => { + if (modulesCache.length === 0) { + return + } + + // Process queue one item at a time + while (layoutsQueue.length > 0) { + const layoutsKey = layoutsQueue.shift() + if (layoutsKey) { + await processLayout(layoutsKey) + } + } +} + +liveQuery(() => systemsDB.modules.toArray()).subscribe((modules) => { + syncModels(modules) + modulesCache = modules + processLayoutsQueue() +}) + +const postLayout = (key: LayoutsKey) => layoutsQueue.push(key) +const postLayouts = (keys: LayoutsKey[]) => keys.map(postLayout) + +liveQuery(() => systemsDB.houseTypes.toArray()).subscribe((houseTypes) => { + postLayouts(houseTypes) + processLayoutsQueue() +}) + +const api = { + postLayout, + postLayouts, +} + +export type LayoutsAPI = typeof api + +expose(api) diff --git a/app/workers/systems.ts b/app/workers/systems.ts index bb3af904..e6556022 100644 --- a/app/workers/systems.ts +++ b/app/workers/systems.ts @@ -1,27 +1,17 @@ -import { expose } from "comlink" -import { liveQuery } from "dexie" -import { pipe } from "fp-ts/lib/function" -import produce from "immer" -import { BufferGeometry } from "three" -import { mergeBufferGeometries } from "three-stdlib" import { vanillaTrpc } from "../../client/trpc" +import { HouseType } from "../../server/data/houseTypes" import { Module } from "../../server/data/modules" -import { getSpeckleObject } from "../../server/data/speckleModel" -import layoutsDB from "../db/layouts" -import systemsDB, { IndexedModule } from "../db/systems" -import { A, R } from "../utils/functions" -import speckleIfcParser from "../utils/speckle/speckleIfcParser" +import systemsDB, { LastFetchStamped } from "../db/systems" const initModules = async () => { const remoteModules = await vanillaTrpc.modules.query() const promises = remoteModules.map(async (remoteModule) => { - const remoteDate = new Date(remoteModule.lastModified) const localModule = await systemsDB.modules.get(remoteModule.id) - const indexedModule: IndexedModule = { + const indexedModule: LastFetchStamped = { ...remoteModule, - lastFetched: new Date().toISOString(), + lastFetched: new Date().getTime(), } as any if (!localModule) { @@ -29,9 +19,7 @@ const initModules = async () => { return } - const localDate = new Date(localModule.lastModified) - - if (remoteDate > localDate) { + if (remoteModule.lastModified > localModule.lastModified) { await systemsDB.modules.put(indexedModule) return } @@ -40,82 +28,34 @@ const initModules = async () => { await Promise.all(promises) } -const init = async () => { - await initModules() -} - -init() - -const modulesObservable = liveQuery(() => systemsDB.modules.toArray()) +const initHouseTypes = async () => { + const remoteHouseTypes = await vanillaTrpc.houseTypes.query() -let allSystemsModules: Module[] = [] + const promises = remoteHouseTypes.map(async (remoteHouseType) => { + const localHouseType = await systemsDB.houseTypes.get(remoteHouseType.id) -modulesObservable.subscribe((modules) => { - allSystemsModules = modules - - modules.map(async (nextModule) => { - const { speckleBranchUrl, lastFetched } = nextModule - const maybeModel = await layoutsDB.models.get(speckleBranchUrl) + const indexedHouseType: LastFetchStamped = { + ...remoteHouseType, + lastFetched: new Date().getTime(), + } - if ( - maybeModel && - new Date(maybeModel.lastFetched).getTime() === - new Date(lastFetched).getTime() - ) { + if (!localHouseType) { + await systemsDB.houseTypes.put(indexedHouseType) return } - const speckleObjectData = await getSpeckleObject(speckleBranchUrl) - const speckleObject = speckleIfcParser.parse(speckleObjectData) - const geometries = pipe( - speckleObject, - A.reduce( - {}, - (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { - return produce(acc, (draft) => { - if (ifcTag in draft) draft[ifcTag].push(geometry) - else draft[ifcTag] = [geometry] - }) - } - ), - R.map((geoms) => mergeBufferGeometries(geoms)), - R.filter((bg: BufferGeometry | null): bg is BufferGeometry => - Boolean(bg) - ), - R.map((x) => x.toJSON()) - ) - - layoutsDB.models.put({ speckleBranchUrl, lastFetched, geometries }) + if (remoteHouseType.lastModified > localHouseType.lastModified) { + await systemsDB.houseTypes.put(indexedHouseType) + return + } }) -}) - -// export const getLayout = ({ systemId, dnas }: ComputeLayoutEventDetail) => { -// // TODO: cache in the db! - -// // dnas to modules array -// const modules = pipe( -// dnas, -// A.filterMap((dna) => -// pipe( -// allSystemsModules, -// A.findFirst( -// (systemModule: Module) => -// systemModule.systemId === systemId && systemModule.dna === dna -// ) -// ) -// ) -// ) - -// // modules to rows -// const columnLayout = modulesToColumnLayout(modules) -// return columnLayout -// } - -const api = { - // getLayout: getLayout, + await Promise.all(promises) } -export type SystemsAPI = typeof api +const init = () => { + initModules() + initHouseTypes() +} -expose(api) +init() diff --git a/server/data/houseTypes.ts b/server/data/houseTypes.ts index ecba1cf6..9720a546 100644 --- a/server/data/houseTypes.ts +++ b/server/data/houseTypes.ts @@ -33,6 +33,17 @@ export const houseTypeParser = z description: z.string().min(1), cost: z.number(), embodied_carbon: z.number(), + last_modified: z.string().refine( + (value) => { + // Attempt to parse the value as a date and check that it's valid + const date = new Date(value) + return !isNaN(date.getTime()) + }, + { + // Custom error message + message: "Invalid date string", + } + ), }) .passthrough(), id: z.string().min(1), @@ -47,6 +58,7 @@ export const houseTypeParser = z description, cost, embodied_carbon, + last_modified, }, }) => ({ id, @@ -56,6 +68,7 @@ export const houseTypeParser = z description, cost, carbon: embodied_carbon, + lastModified: new Date(last_modified).getTime(), }) ) diff --git a/server/data/modules.ts b/server/data/modules.ts index 9af17fdc..0aa97b61 100644 --- a/server/data/modules.ts +++ b/server/data/modules.ts @@ -150,7 +150,7 @@ export const moduleParser = z embodiedCarbon: embodied_carbon ?? -400, description, visualReference: visual_reference?.[0]?.url, - lastModified: last_modified, + lastModified: new Date(last_modified).getTime(), }) ) From f21416e11d1fa143bc81fd92e0c499098b2792df Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 28 Jun 2023 13:41:42 +0100 Subject: [PATCH 018/132] wip fix-up fetcher code --- app/workers/systems.ts | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/app/workers/systems.ts b/app/workers/systems.ts index e6556022..a83a4887 100644 --- a/app/workers/systems.ts +++ b/app/workers/systems.ts @@ -14,14 +14,8 @@ const initModules = async () => { lastFetched: new Date().getTime(), } as any - if (!localModule) { + if (!localModule || remoteModule.lastModified > localModule.lastModified) { await systemsDB.modules.put(indexedModule) - return - } - - if (remoteModule.lastModified > localModule.lastModified) { - await systemsDB.modules.put(indexedModule) - return } }) @@ -39,14 +33,11 @@ const initHouseTypes = async () => { lastFetched: new Date().getTime(), } - if (!localHouseType) { - await systemsDB.houseTypes.put(indexedHouseType) - return - } - - if (remoteHouseType.lastModified > localHouseType.lastModified) { + if ( + !localHouseType || + remoteHouseType.lastModified > localHouseType.lastModified + ) { await systemsDB.houseTypes.put(indexedHouseType) - return } }) From e059be59a1687a55b1ca876e6c0e6c168b572e72 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 28 Jun 2023 14:10:29 +0100 Subject: [PATCH 019/132] wip using cached layout --- app/design/state/dimensions.tsx | 13 +++++++++++-- app/design/state/layouts.ts | 17 ++++++++++++++++- app/design/ui-3d/grouped/GroupedHouse.tsx | 15 ++++++--------- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/app/design/state/dimensions.tsx b/app/design/state/dimensions.tsx index 70f6c81e..c24166d0 100644 --- a/app/design/state/dimensions.tsx +++ b/app/design/state/dimensions.tsx @@ -5,7 +5,8 @@ import { OBB } from "three-stdlib" import { proxy, ref, useSnapshot } from "valtio" import { useSubscribeKey } from "~/utils/hooks" import { yAxis } from "~/utils/three" -import houses from "./houses" +import { serializeLayoutsKey } from "../../db/layouts" +import houses, { useHouse } from "./houses" import { layouts } from "./layouts" import { postTransformsTransients, Transforms } from "./transients/transforms" @@ -68,17 +69,25 @@ export const usePostTransMatrix = (houseId: string) => { } } +export const useLayoutsKey = (houseId: string) => { + const { systemId, dnas } = useHouse(houseId) + + return serializeLayoutsKey({ systemId, dnas }) +} + export const useComputeDimensions = (houseId: string) => { const translationMatrix = useRef(new Matrix4()) const rotationMatrix = useRef(new Matrix4()) + const layoutsKey = useLayoutsKey(houseId) + return (transients: Transforms = {}): Dimensions => { const { rotation: dr = 0, position: { dx, dy, dz } = { dx: 0, dy: 0, dz: 0 }, } = transients - const columns = layouts[houseId] + const columns = layouts[layoutsKey] const width = columns[0].gridGroups[0].modules[0].module.width const height = columns[0].gridGroups.reduce( diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 9976396e..fed62dfa 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -1,12 +1,14 @@ import { Module } from "@/server/data/modules" +import { liveQuery } from "dexie" import { transpose as transposeA } from "fp-ts-std/Array" import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" import * as A from "fp-ts/Array" import { pipe } from "fp-ts/lib/function" import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" -import { proxy, ref } from "valtio" +import { proxy, ref, snapshot, useSnapshot } from "valtio" import { usePadColumn } from "../../data/modules" +import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../../db/layouts" import { modulesToRows, useDnasModules, useHouseModules } from "./houses" export type PositionedModule = { @@ -63,6 +65,14 @@ export const layouts = proxy< Record // systemId:dnas : Layout >({}) +liveQuery(() => layoutsDB.layouts.toArray()).subscribe((dbLayouts) => { + for (let { layoutsKey, layout } of dbLayouts) { + if (!(layoutsKey in layouts)) { + layouts[layoutsKey] = ref(layout) + } + } +}) + export const useRowLayout = (houseId: string): RowLayout => pipe( useHouseModules(houseId), @@ -345,6 +355,11 @@ export const modulesToColumnLayout = (modules: Module[]) => { ) } +export const useDnasLayout = (layoutsKey: LayoutsKey) => { + const snap = useSnapshot(layouts) as typeof layouts + return snap[serializeLayoutsKey(layoutsKey)] +} + export const useHouseColumnLayout = (houseId: string) => { const houseModules = useHouseModules(houseId) const columnLayout = modulesToColumnLayout(houseModules) diff --git a/app/design/ui-3d/grouped/GroupedHouse.tsx b/app/design/ui-3d/grouped/GroupedHouse.tsx index e3ed70d7..d971a437 100644 --- a/app/design/ui-3d/grouped/GroupedHouse.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useRef } from "react" import { Group } from "three" import { useHouseMaterialOps } from "~/design/state/hashedMaterials" import { useHouseElementOutline } from "~/design/state/highlights" -import { useHouseColumnLayout } from "~/design/state/layouts" +import { useDnasLayout, useHouseColumnLayout } from "~/design/state/layouts" import { useStretchLength } from "~/design/state/transients/stretchLength" import { usePostTransformsTransients, @@ -30,8 +30,12 @@ const GroupedHouse = (props: Props) => { const endRef = useRef(null!) const systemId = useHouseSystemId(houseId) + const house = useHouse(houseId) + const houseDnasKey = JSON.stringify(house.dnas) + // eslint-disable-next-line react-hooks/exhaustive-deps + const dnas = useMemo(() => house.dnas, [houseDnasKey]) - const layout = useHouseColumnLayout(houseId) + const layout = useDnasLayout({ systemId, dnas }) const { startColumn, midColumns, endColumn, columnsUp, columnsDown } = useStretchLength({ houseId, layout, startRef, endRef }) @@ -54,13 +58,6 @@ const GroupedHouse = (props: Props) => { [houseRefs] ) - const house = useHouse(houseId) - - const houseDnasKey = JSON.stringify(house.dnas) - - // eslint-disable-next-line react-hooks/exhaustive-deps - const dnas = useMemo(() => house.dnas, [houseDnasKey]) - // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => setHouseVisible(true), [dnas, setHouseVisible]) From 0bf79ca296b41c47c526de109ceb3c66674a6257 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 28 Jun 2023 16:51:51 +0100 Subject: [PATCH 020/132] big wip --- app/build/layout.tsx | 4 + app/data/elements.ts | 95 +++++++++++++------ app/db/layouts.ts | 3 +- app/db/systems.ts | 4 + app/design/state/hashedMaterials.ts | 18 ++-- app/design/state/interactions/layouts.ts | 10 +- app/design/state/interactions/windows.ts | 14 ++- app/design/state/layouts.ts | 42 ++++---- app/design/state/scope.ts | 12 ++- app/design/state/transients/stretchLength.tsx | 5 +- app/design/ui-3d/box/BoxApp.tsx | 19 ---- app/design/ui-3d/box/BoxColumn.tsx | 62 ------------ app/design/ui-3d/box/BoxHouse.tsx | 49 ---------- app/design/ui-3d/box/BoxModule.tsx | 39 -------- app/design/ui-3d/grouped/GroupedHouse.tsx | 9 +- app/workers/layouts.ts | 30 ++++-- app/workers/systems.ts | 24 +++++ server/data/elements.ts | 23 ++++- 18 files changed, 217 insertions(+), 245 deletions(-) delete mode 100644 app/design/ui-3d/box/BoxApp.tsx delete mode 100644 app/design/ui-3d/box/BoxColumn.tsx delete mode 100644 app/design/ui-3d/box/BoxHouse.tsx delete mode 100644 app/design/ui-3d/box/BoxModule.tsx diff --git a/app/build/layout.tsx b/app/build/layout.tsx index fa356710..b94f9526 100644 --- a/app/build/layout.tsx +++ b/app/build/layout.tsx @@ -1,6 +1,8 @@ +"use client" import dynamic from "next/dynamic" import { PropsWithChildren } from "react" import { TrpcProvider } from "../ui/TrpcProvider" +import { getLayoutsWorker, getSystemsWorker } from "../workers" import BuildNav from "./common/BuildNav" const HousesPillsSelector = dynamic( @@ -11,6 +13,8 @@ const HousesPillsSelector = dynamic( ) const BuildLayout = ({ children }: PropsWithChildren<{}>) => { + getSystemsWorker() + getLayoutsWorker() return (
diff --git a/app/data/elements.ts b/app/data/elements.ts index 936069c7..34724feb 100644 --- a/app/data/elements.ts +++ b/app/data/elements.ts @@ -1,14 +1,15 @@ import { trpc } from "@/client/trpc" -import { useLiveQuery } from "dexie-react-hooks" +import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" import { useMemo } from "react" import { BufferGeometry, BufferGeometryLoader } from "three" +import { proxy, ref, useSnapshot } from "valtio" import { O, R, RA, S } from "~/utils/functions" import { Element } from "../../server/data/elements" import { Module } from "../../server/data/modules" import layoutsDB from "../db/layouts" import systemsDB from "../db/systems" -// import useSpeckleObject from "../utils/speckle/useSpeckleObject" +import { isSSR } from "../utils/next" export const useElements = (): Element[] => { const { data = [] } = trpc.elements.useQuery() @@ -23,6 +24,34 @@ export const useSystemElements = ({ return useElements().filter((x) => x.systemId === systemId) } +export const ifcTagToElement = ({ + systemId, + elements, + ifcTag, +}: { + elements: Element[] + ifcTag: string + systemId: string +}) => { + const result = pipe( + elements, + RA.findFirst((el) => { + return ( + el.ifc4Variable.toUpperCase() === ifcTag && el.systemId === systemId + ) + }), + O.toUndefined + ) + + if (result === undefined) { + console.log({ + unmatchedIfcTag: { ifcTag }, + }) + } + + return result +} + export const useIfcTagToElement = (systemId: string) => { const elements = useSystemElements({ systemId }) @@ -58,44 +87,54 @@ export const invertModuleElementGeometriesKey = (input: string) => { return { systemId, dna } } -export const useSpeckleObject = (speckleBranchUrl: string) => { - const loader = useMemo(() => new BufferGeometryLoader(), []) +const models = proxy>>({}) + +const loader = new BufferGeometryLoader() - const geometries = useLiveQuery(async () => { - const model = await layoutsDB.models.get(speckleBranchUrl) - return model?.geometries +if (!isSSR()) { + liveQuery(async () => { + const models = await layoutsDB.models.toArray() + const elements = await systemsDB.elements.toArray() + return { models, elements } + }).subscribe(({ models: dbModels, elements: dbElements }) => { + for (let { speckleBranchUrl, geometries, systemId } of dbModels) { + if (!(speckleBranchUrl in models)) { + const loadedModels: Record = pipe( + geometries, + R.map((x) => loader.parse(x) as BufferGeometry), + R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { + const el = ifcTagToElement({ + systemId, + elements: dbElements, + ifcTag, + }) + if (!el) return acc + return { + ...acc, + [el.name]: geometry, + } + }) + ) + models[speckleBranchUrl] = ref(loadedModels) + } + } }) +} - return useMemo( - () => - pipe( - geometries, - R.map((x) => loader.parse(x) as BufferGeometry) - ), - [geometries, loader] - ) +export const useSpeckleObject = (speckleBranchUrl: string) => { + const snap = useSnapshot(models) as typeof models + + return snap[speckleBranchUrl] } export const useModuleElements = ({ systemId, speckleBranchUrl, }: Module): Record => { - const ifcTagToElement = useIfcTagToElement(systemId) const speckleObject = useSpeckleObject(speckleBranchUrl) return useMemo( - () => - pipe( - speckleObject, - R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { - const el = ifcTagToElement(ifcTag) - if (!el) return acc - return { - ...acc, - [el.name]: geometry, - } - }) - ), + () => pipe(speckleObject), // eslint-disable-next-line react-hooks/exhaustive-deps [speckleBranchUrl, speckleObject] ) diff --git a/app/db/layouts.ts b/app/db/layouts.ts index 4730f153..1335852b 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -5,6 +5,7 @@ import { LastFetchStamped } from "./systems" export type ParsedModel = { speckleBranchUrl: string geometries: any + systemId: string } export type IndexedLayout = { @@ -27,7 +28,7 @@ class LayoutsDatabase extends Dexie { constructor() { super("LayoutsDatabase") this.version(1).stores({ - models: "speckleBranchUrl", + models: "speckleBranchUrl,systemId", layouts: "layoutsKey", }) this.layouts = this.table("layouts") diff --git a/app/db/systems.ts b/app/db/systems.ts index d76262d3..eb8a01eb 100644 --- a/app/db/systems.ts +++ b/app/db/systems.ts @@ -1,6 +1,7 @@ import { Block } from "@/server/data/blocks" import { Module } from "@/server/data/modules" import Dexie from "dexie" +import { Element } from "../../server/data/elements" import { HouseType } from "../../server/data/houseTypes" export type LastFetchStamped = T & { @@ -10,6 +11,7 @@ export type LastFetchStamped = T & { class SystemsDatabase extends Dexie { modules: Dexie.Table, string> houseTypes: Dexie.Table, string> + elements: Dexie.Table, string> blocks: Dexie.Table constructor() { @@ -18,10 +20,12 @@ class SystemsDatabase extends Dexie { blocks: "id,systemId,name", modules: "id,systemId,dna", houseTypes: "id,systemId", + elements: "id,systemId", }) this.modules = this.table("modules") this.houseTypes = this.table("houseTypes") this.blocks = this.table("blocks") + this.elements = this.table("elements") } } diff --git a/app/design/state/hashedMaterials.ts b/app/design/state/hashedMaterials.ts index a567f1cc..6fa22264 100644 --- a/app/design/state/hashedMaterials.ts +++ b/app/design/state/hashedMaterials.ts @@ -15,6 +15,7 @@ import { useHousePreviews } from "./previews" import settings from "./settings" import siteCtx from "./siteCtx" import { postTransformsTransients } from "./transients/transforms" +import { LayoutsKey } from "../../db/layouts" const getMaterialHash = ({ systemId, @@ -232,10 +233,15 @@ const usePlaneMatrix = (houseId: string) => { } } -export const useHouseMaterialOps = ( - houseId: string, +export const useHouseMaterialOps = ({ + houseGroupRef, + houseId, + serializedLayoutsKey, +}: { + houseId: string houseGroupRef: MutableRefObject -) => { + serializedLayoutsKey: string +}) => { const systemId = houses[houseId].systemId const elementMaterials = useRef>({}) const categoryElements = useRef>({}) @@ -254,9 +260,9 @@ export const useHouseMaterialOps = ( const levelHeight = siteCtx.levelIndex === null ? Infinity - : layouts[houseId][0].gridGroups[siteCtx.levelIndex].y + - layouts[houseId][0].gridGroups[siteCtx.levelIndex].modules[0].module - .height / + : layouts[serializedLayoutsKey][0].gridGroups[siteCtx.levelIndex].y + + layouts[serializedLayoutsKey][0].gridGroups[siteCtx.levelIndex] + .modules[0].module.height / 2 const updateMatrices = () => { diff --git a/app/design/state/interactions/layouts.ts b/app/design/state/interactions/layouts.ts index eb514402..d00acbe1 100644 --- a/app/design/state/interactions/layouts.ts +++ b/app/design/state/interactions/layouts.ts @@ -22,6 +22,7 @@ import { import { StairType } from "@/server/data/stairTypes" import { Module } from "@/server/data/modules" import { useSystemStairTypes } from "~/data/stairTypes" +import { serializeLayoutsKey } from "../../../db/layouts" export const useChangeModuleLayout = ({ houseId, @@ -32,7 +33,8 @@ export const useChangeModuleLayout = ({ const systemId = houses[houseId].systemId const getVanillaModule = useGetVanillaModule(systemId) - const columnLayout = layouts[houseId] + const columnLayout = + layouts[serializeLayoutsKey({ systemId, dnas: houses[houseId].dnas })] const oldModule = columnLayout[columnIndex].gridGroups[levelIndex].modules[gridGroupIndex] @@ -144,7 +146,8 @@ export const useLayoutOptions = ({ selected: LayoutOpt["value"] } => { const systemId = houses[houseId].systemId - const layout = layouts[houseId] + const layout = + layouts[serializeLayoutsKey({ systemId, dnas: houses[houseId].dnas })] const m = layout[columnIndex].gridGroups[levelIndex].modules[gridGroupIndex].module @@ -207,7 +210,8 @@ export const useStairsOptions = ({ selected: StairsOpt["value"] } => { const systemId = houses[houseId].systemId - const layout = layouts[houseId] + const layout = + layouts[serializeLayoutsKey({ systemId, dnas: houses[houseId].dnas })] const stairTypes = useSystemStairTypes({ systemId }) diff --git a/app/design/state/interactions/windows.ts b/app/design/state/interactions/windows.ts index 51debac5..9ea54ec9 100644 --- a/app/design/state/interactions/windows.ts +++ b/app/design/state/interactions/windows.ts @@ -13,6 +13,8 @@ import { } from "~/design/state/layouts" import siteCtx from "~/design/state/siteCtx" import { useChangeModuleLayout } from "./layouts" +import { serializeLayoutsKey } from "../../../db/layouts" +import houses from "../houses" export type WindowTypeOption = { label: string @@ -29,9 +31,15 @@ export const useWindowOptions = ({ options: WindowTypeOption[] selected: WindowTypeOption["value"] } => { + const layoutsKey = serializeLayoutsKey({ + systemId: houses[houseId].systemId, + dnas: houses[houseId].dnas, + }) + const m = - layouts[houseId][columnIndex].gridGroups[levelIndex].modules[gridGroupIndex] - .module + layouts[layoutsKey][columnIndex].gridGroups[levelIndex].modules[ + gridGroupIndex + ].module const { systemId } = m @@ -106,7 +114,7 @@ export const useWindowOptions = ({ const selected = pipe( options, A.findFirstMap(({ value }) => { - const houseDna = columnLayoutToDNA(layouts[houseId]) + const houseDna = columnLayoutToDNA(layouts[layoutsKey]) return eq.equals(value.houseDna, houseDna) ? O.some(value) : O.none }), O.getOrElse(() => { diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index fed62dfa..f89e9f9f 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -9,7 +9,13 @@ import produce from "immer" import { proxy, ref, snapshot, useSnapshot } from "valtio" import { usePadColumn } from "../../data/modules" import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../../db/layouts" -import { modulesToRows, useDnasModules, useHouseModules } from "./houses" +import { isSSR } from "../../utils/next" +import houses, { + modulesToRows, + useDnasModules, + useHouse, + useHouseModules, +} from "./houses" export type PositionedModule = { module: Module @@ -65,13 +71,15 @@ export const layouts = proxy< Record // systemId:dnas : Layout >({}) -liveQuery(() => layoutsDB.layouts.toArray()).subscribe((dbLayouts) => { - for (let { layoutsKey, layout } of dbLayouts) { - if (!(layoutsKey in layouts)) { - layouts[layoutsKey] = ref(layout) +if (!isSSR()) { + liveQuery(() => layoutsDB.layouts.toArray()).subscribe((dbLayouts) => { + for (let { layoutsKey, layout } of dbLayouts) { + if (!(layoutsKey in layouts)) { + layouts[layoutsKey] = ref(layout) + } } - } -}) + }) +} export const useRowLayout = (houseId: string): RowLayout => pipe( @@ -360,13 +368,6 @@ export const useDnasLayout = (layoutsKey: LayoutsKey) => { return snap[serializeLayoutsKey(layoutsKey)] } -export const useHouseColumnLayout = (houseId: string) => { - const houseModules = useHouseModules(houseId) - const columnLayout = modulesToColumnLayout(houseModules) - layouts[houseId] = ref(columnLayout) - return columnLayout -} - export const useDnasColumnLayout = (systemId: string, dnas: string[]) => { const houseModules = useDnasModules(systemId, dnas) const columnLayout = modulesToColumnLayout(houseModules) @@ -411,6 +412,14 @@ export const columnLayoutToMatrix = (columnLayout: ColumnLayout) => { ) } +export const useColumnMatrix = (houseId: string) => { + const { systemId, dnas } = useHouse(houseId) + const layoutsSnap = useSnapshot(layouts) as typeof layouts + return columnLayoutToMatrix( + layoutsSnap[serializeLayoutsKey({ systemId, dnas })] + ) +} + export const columnMatrixToDna = (columnMatrix: Module[][][]) => pipe( columnMatrix, @@ -420,11 +429,6 @@ export const columnMatrixToDna = (columnMatrix: Module[][][]) => A.flatten ) -export const useColumnMatrix = (houseId: string) => { - const columnLayout = useHouseColumnLayout(houseId) - return columnLayoutToMatrix(columnLayout) -} - export const rowLayoutToMatrix = (rowLayout: RowLayout) => pipe( rowLayout, diff --git a/app/design/state/scope.ts b/app/design/state/scope.ts index bceba366..5ebbc4b4 100644 --- a/app/design/state/scope.ts +++ b/app/design/state/scope.ts @@ -3,6 +3,8 @@ import { pipe } from "fp-ts/lib/function" import { proxy, useSnapshot } from "valtio" import { A, O } from "~/utils/functions" import { columnLayoutToMatrix, layouts } from "~/design/state/layouts" +import { serializeLayoutsKey } from "../../db/layouts" +import houses from "./houses" export type ScopeItem = { elementName: string @@ -28,7 +30,11 @@ export const getSelectedModule = () => { if (scope.selected === null) return null const { houseId, columnIndex, levelIndex, gridGroupIndex } = scope.selected - return layouts[houseId][columnIndex].gridGroups[levelIndex].modules[ + const { systemId, dnas } = houses[houseId] + + const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + + return layouts[layoutsKey][columnIndex].gridGroups[levelIndex].modules[ gridGroupIndex ].module } @@ -39,7 +45,9 @@ export const getSelectedLevelType = () => export const getSelectedColumnMatrix = () => { if (scope.selected === null) return null const { houseId } = scope.selected - return columnLayoutToMatrix(layouts[houseId]) + const { systemId, dnas } = houses[houseId] + const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + return columnLayoutToMatrix(layouts[layoutsKey]) } export const getSelectedLevelModules = () => { if (scope.selected === null) return null diff --git a/app/design/state/transients/stretchLength.tsx b/app/design/state/transients/stretchLength.tsx index 52b8cebf..78be111d 100644 --- a/app/design/state/transients/stretchLength.tsx +++ b/app/design/state/transients/stretchLength.tsx @@ -26,6 +26,7 @@ import { useGetVanillaModule, vanillaColumns, } from "../vanilla" +import { serializeLayoutsKey } from "../../../db/layouts" export type StretchLength = { direction: 1 | -1 @@ -300,7 +301,9 @@ export const useStretchLength = ({ export const setStretchLength = () => { for (let houseId of Object.keys(stretchLengthClamped)) { - const layout = layouts[houseId] + const { systemId, dnas } = houses[houseId] + const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + const layout = layouts[layoutsKey] const { startColumn, midColumns, endColumn } = splitColumns(layout) const vanillaColumn = vanillaColumns[houseId] const vanillaColumnLength = getVanillaColumnLength(vanillaColumn) diff --git a/app/design/ui-3d/box/BoxApp.tsx b/app/design/ui-3d/box/BoxApp.tsx deleted file mode 100644 index 6577698c..00000000 --- a/app/design/ui-3d/box/BoxApp.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { pipe } from "fp-ts/lib/function" -import { Fragment } from "react" -import { useHouses } from "../../state/houses" -import { RA, RR } from "~/utils/functions" -import BoxHouse from "./BoxHouse" - -const BoxApp = () => { - const houses = useHouses() - - const housesChildren = pipe( - houses, - RR.keys, - RA.map((id) => ) - ) - - return {housesChildren} -} - -export default BoxApp diff --git a/app/design/ui-3d/box/BoxColumn.tsx b/app/design/ui-3d/box/BoxColumn.tsx deleted file mode 100644 index 6ceac670..00000000 --- a/app/design/ui-3d/box/BoxColumn.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { PositionedRow } from "~/design/state/layouts" -import { pipe } from "fp-ts/lib/function" -import * as RA from "fp-ts/ReadonlyArray" -import { Plane } from "three" -import BoxModule from "./BoxModule" - -type Props = { - houseId: string - columnZ: number - columnIndex: number - mirror?: boolean - gridGroups: readonly PositionedRow[] - verticalCutPlanes: Plane[] -} - -const BoxColumn = (props: Props) => { - const { - houseId, - columnIndex, - columnZ, - gridGroups, - mirror = false, - verticalCutPlanes, - ...groupProps - } = props - const levels = pipe( - gridGroups, - RA.map(({ levelIndex, modules, y }) => - pipe( - modules, - RA.mapWithIndex((groupIndex, { module, z }) => { - const key = `${houseId}:${columnIndex},${levelIndex},${groupIndex}` - - return ( - - ) - }) - ) - ) - ) - return ( - - {levels} - - ) -} -export default BoxColumn diff --git a/app/design/ui-3d/box/BoxHouse.tsx b/app/design/ui-3d/box/BoxHouse.tsx deleted file mode 100644 index feda0c66..00000000 --- a/app/design/ui-3d/box/BoxHouse.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useHouseColumnLayout } from "~/design/state/layouts" -import { Fragment, useRef } from "react" -import { Group } from "three" -import { useHouseDimensionsUpdates } from "../../state/dimensions" -import VerticalHandle from "../VerticalHandle" -import BoxColumn from "./BoxColumn" - -type Props = { - id: string -} - -const BoxHouse = (props: Props) => { - const groupRef = useRef(null!) - const { id } = props - const columns = useHouseColumnLayout(id) - - useHouseDimensionsUpdates(id) - // useMoveRotateSubscription(id, groupRef) - - // const bind = useHouseEventHandlers(id) - - return ( - - - - {columns.map(({ columnIndex, z, gridGroups }) => { - return ( - - ) - })} - - {/* */} - - - - ) -} - -export default BoxHouse diff --git a/app/design/ui-3d/box/BoxModule.tsx b/app/design/ui-3d/box/BoxModule.tsx deleted file mode 100644 index 44acb479..00000000 --- a/app/design/ui-3d/box/BoxModule.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { GroupProps } from "@react-three/fiber" -import { Plane } from "three" -import { Module } from "@/server/data/modules" - -type Props = GroupProps & { - module: Module - columnIndex: number - levelIndex: number - groupIndex: number - houseId: string - levelY: number - verticalCutPlanes: Plane[] -} - -const BoxModule = (props: Props) => { - const { - houseId, - columnIndex, - levelIndex, - groupIndex, - module, - levelY, - verticalCutPlanes, - ...groupProps - } = props - - const key = `${houseId}:${columnIndex},${levelIndex},${groupIndex}` - - return ( - - - - - - - ) -} - -export default BoxModule diff --git a/app/design/ui-3d/grouped/GroupedHouse.tsx b/app/design/ui-3d/grouped/GroupedHouse.tsx index d971a437..324199cb 100644 --- a/app/design/ui-3d/grouped/GroupedHouse.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse.tsx @@ -3,13 +3,14 @@ import { useCallback, useEffect, useMemo, useRef } from "react" import { Group } from "three" import { useHouseMaterialOps } from "~/design/state/hashedMaterials" import { useHouseElementOutline } from "~/design/state/highlights" -import { useDnasLayout, useHouseColumnLayout } from "~/design/state/layouts" +import { useDnasLayout } from "~/design/state/layouts" import { useStretchLength } from "~/design/state/transients/stretchLength" import { usePostTransformsTransients, usePreTransformsTransients, } from "~/design/state/transients/transforms" import { RA } from "~/utils/functions" +import { serializeLayoutsKey } from "../../../db/layouts" import { useHouse, useHouseSystemId } from "../../state/houses" import { useIsMoveRotateable, useIsStretchable } from "../../state/siteCtx" import RotateHandles from "../handles/RotateHandles" @@ -69,7 +70,11 @@ const GroupedHouse = (props: Props) => { const isStretchable = useIsStretchable(houseId) const isMoveRotateable = useIsMoveRotateable(houseId) - useHouseMaterialOps(houseId, houseGroupRef) + useHouseMaterialOps({ + houseId, + houseGroupRef, + serializedLayoutsKey: serializeLayoutsKey({ systemId, dnas }), + }) return ( diff --git a/app/workers/layouts.ts b/app/workers/layouts.ts index 763266c6..a555cde2 100644 --- a/app/workers/layouts.ts +++ b/app/workers/layouts.ts @@ -10,6 +10,7 @@ import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../db/layouts" import systemsDB, { LastFetchStamped } from "../db/systems" import { modulesToColumnLayout } from "../design/state/layouts" import { A, R } from "../utils/functions" +import { isSSR } from "../utils/next" import speckleIfcParser from "../utils/speckle/speckleIfcParser" const syncModels = (modules: LastFetchStamped[]) => { @@ -41,7 +42,12 @@ const syncModels = (modules: LastFetchStamped[]) => { R.map((x) => x.toJSON()) ) - layoutsDB.models.put({ speckleBranchUrl, lastFetched, geometries }) + layoutsDB.models.put({ + speckleBranchUrl, + lastFetched, + geometries, + systemId: nextModule.systemId, + }) }) } @@ -85,19 +91,23 @@ const processLayoutsQueue = async () => { } } -liveQuery(() => systemsDB.modules.toArray()).subscribe((modules) => { - syncModels(modules) - modulesCache = modules - processLayoutsQueue() -}) +if (!isSSR()) { + liveQuery(() => systemsDB.modules.toArray()).subscribe((modules) => { + syncModels(modules) + modulesCache = modules + processLayoutsQueue() + }) +} const postLayout = (key: LayoutsKey) => layoutsQueue.push(key) const postLayouts = (keys: LayoutsKey[]) => keys.map(postLayout) -liveQuery(() => systemsDB.houseTypes.toArray()).subscribe((houseTypes) => { - postLayouts(houseTypes) - processLayoutsQueue() -}) +if (!isSSR()) { + liveQuery(() => systemsDB.houseTypes.toArray()).subscribe((houseTypes) => { + postLayouts(houseTypes) + processLayoutsQueue() + }) +} const api = { postLayout, diff --git a/app/workers/systems.ts b/app/workers/systems.ts index a83a4887..dd3f87f9 100644 --- a/app/workers/systems.ts +++ b/app/workers/systems.ts @@ -1,4 +1,5 @@ import { vanillaTrpc } from "../../client/trpc" +import { Element } from "../../server/data/elements" import { HouseType } from "../../server/data/houseTypes" import { Module } from "../../server/data/modules" import systemsDB, { LastFetchStamped } from "../db/systems" @@ -44,9 +45,32 @@ const initHouseTypes = async () => { await Promise.all(promises) } +const initElements = async () => { + const remoteElements = await vanillaTrpc.elements.query() + + const promises = remoteElements.map(async (remoteElement) => { + const localElement = await systemsDB.elements.get(remoteElement.id) + + const indexedElement: LastFetchStamped = { + ...remoteElement, + lastFetched: new Date().getTime(), + } + + if ( + !localElement || + remoteElement.lastModified > localElement.lastModified + ) { + await systemsDB.elements.put(indexedElement) + } + }) + + await Promise.all(promises) +} + const init = () => { initModules() initHouseTypes() + initElements() } init() diff --git a/server/data/elements.ts b/server/data/elements.ts index 60299eb1..bec46f07 100644 --- a/server/data/elements.ts +++ b/server/data/elements.ts @@ -13,6 +13,7 @@ export type Element = { defaultMaterial: string materialOptions: Array category: string + lastModified: number } export const elementParser = z.object({ @@ -25,6 +26,20 @@ export const elementParser = z.object({ ifc4_variable: z.string().min(1), default_material: z.array(z.string().min(1)).optional(), element_category: z.string().min(1), + last_modified: z + .string() + .refine( + (value) => { + // Attempt to parse the value as a date and check that it's valid + const date = new Date(value) + return !isNaN(date.getTime()) + }, + { + // Custom error message + message: "Invalid date string", + } + ) + .transform((x) => new Date(x).getTime()), }), }) @@ -47,7 +62,12 @@ export const elementsQuery: QueryFn = elementParser.transform( ({ id, - fields: { element_code, ifc4_variable, element_category }, + fields: { + element_code, + ifc4_variable, + element_category, + last_modified, + }, }) => { const defaultMaterials = materials.filter( ({ defaultFor }) => defaultFor.includes(id) @@ -71,6 +91,7 @@ export const elementsQuery: QueryFn = defaultMaterial, materialOptions, category: element_category, + lastModified: last_modified, } } ) From 98cb5ed29fe56a8850a9b990e484eb2ae2e653b0 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 29 Jun 2023 11:45:21 +0100 Subject: [PATCH 021/132] wip --- app/design/state/hashedMaterials.ts | 18 +-- app/design/state/siteCtx.ts | 26 ++-- app/design/ui-3d/grouped/GroupedApp.tsx | 21 ++-- app/design/ui-3d/grouped/GroupedHouse.tsx | 20 +-- app/design/ui-3d/grouped/GroupedHouse2.tsx | 117 ++++++++++++++++++ .../ui-3d/grouped/preview/PreviewHouses.tsx | 2 + .../grouped/stretchWidth/StretchWidth.tsx | 6 +- 7 files changed, 162 insertions(+), 48 deletions(-) create mode 100644 app/design/ui-3d/grouped/GroupedHouse2.tsx diff --git a/app/design/state/hashedMaterials.ts b/app/design/state/hashedMaterials.ts index 6fa22264..cfe37a1a 100644 --- a/app/design/state/hashedMaterials.ts +++ b/app/design/state/hashedMaterials.ts @@ -234,13 +234,13 @@ const usePlaneMatrix = (houseId: string) => { } export const useHouseMaterialOps = ({ - houseGroupRef, + ref, houseId, - serializedLayoutsKey, + layoutsKey, }: { houseId: string - houseGroupRef: MutableRefObject - serializedLayoutsKey: string + ref: MutableRefObject + layoutsKey: string }) => { const systemId = houses[houseId].systemId const elementMaterials = useRef>({}) @@ -260,9 +260,9 @@ export const useHouseMaterialOps = ({ const levelHeight = siteCtx.levelIndex === null ? Infinity - : layouts[serializedLayoutsKey][0].gridGroups[siteCtx.levelIndex].y + - layouts[serializedLayoutsKey][0].gridGroups[siteCtx.levelIndex] - .modules[0].module.height / + : layouts[layoutsKey][0].gridGroups[siteCtx.levelIndex].y + + layouts[layoutsKey][0].gridGroups[siteCtx.levelIndex].modules[0].module + .height / 2 const updateMatrices = () => { @@ -280,7 +280,7 @@ export const useHouseMaterialOps = ({ useSubscribeKey(postTransformsTransients, houseId, updateMatrices, true) useEffect(() => { - houseGroupRef.current?.traverse((o3) => { + ref.current?.traverse((o3) => { if ( !isMesh(o3) || Array.isArray(o3.material) || @@ -313,7 +313,7 @@ export const useHouseMaterialOps = ({ elementMaterials.current = {} categoryElements.current = {} } - }, [elements, houseGroupRef]) + }, [elements, ref]) useSubscribe( elementCategories, diff --git a/app/design/state/siteCtx.ts b/app/design/state/siteCtx.ts index 14d9eae6..02773b5c 100644 --- a/app/design/state/siteCtx.ts +++ b/app/design/state/siteCtx.ts @@ -47,23 +47,19 @@ export const useIsBuilding = (houseId: string) => { return houseId === buildingHouseId } -export const useIsStretchable = (houseId: string) => { - const { mode, editMode, houseId: ctxHouseId } = useSiteCtx() - return ( - mode === SiteCtxModeEnum.Enum.BUILDING && - editMode === EditModeEnum.Enum.STRETCH && - houseId === ctxHouseId - ) -} - -export const useIsMoveRotateable = (houseId: string) => { +export const useTransformabilityBooleans = (houseId: string) => { const { mode, editMode, houseId: ctxHouseId } = useSiteCtx() - return ( - mode === SiteCtxModeEnum.Enum.SITE && - editMode === EditModeEnum.Enum.MOVE_ROTATE && - houseId === ctxHouseId - ) + return { + stretchEnabled: + mode === SiteCtxModeEnum.Enum.BUILDING && + editMode === EditModeEnum.Enum.STRETCH && + houseId === ctxHouseId, + moveRotateEnabled: + mode === SiteCtxModeEnum.Enum.SITE && + editMode === EditModeEnum.Enum.MOVE_ROTATE && + houseId === ctxHouseId, + } } export const useLocallyStoredSiteCtx = () => diff --git a/app/design/ui-3d/grouped/GroupedApp.tsx b/app/design/ui-3d/grouped/GroupedApp.tsx index f6a7be5c..7e533d1a 100644 --- a/app/design/ui-3d/grouped/GroupedApp.tsx +++ b/app/design/ui-3d/grouped/GroupedApp.tsx @@ -1,18 +1,18 @@ "use client" import { pipe } from "fp-ts/lib/function" -import { Fragment, Suspense } from "react" +import { Fragment } from "react" import { usePreviews } from "~/design/state/previews" import { useRouting } from "~/design/state/routing" -import { RA } from "~/utils/functions" -import { useExportersWorker } from "../../workers/exporters" +import { A, R } from "~/utils/functions" import { useDragHandler, useGestures } from "../../state/gestures" -import { useHouseKeys } from "../../state/houses" +import { useHouses } from "../../state/houses" +import { useExportersWorker } from "../../workers/exporters" import XZPlane from "../XZPlane" import YPlane from "../YPlane" -import GroupedHouse from "./GroupedHouse" +import GroupedHouse2 from "./GroupedHouse2" const GroupedApp = () => { - const houseKeys = useHouseKeys() + const houses = useHouses() usePreviews() const bindAll = useGestures() useDragHandler() @@ -24,11 +24,10 @@ const GroupedApp = () => { {pipe( - houseKeys, - RA.map((houseId) => ( - - - + houses, + R.toArray, + A.map(([houseId, house]) => ( + )) )} diff --git a/app/design/ui-3d/grouped/GroupedHouse.tsx b/app/design/ui-3d/grouped/GroupedHouse.tsx index 324199cb..4314c9ab 100644 --- a/app/design/ui-3d/grouped/GroupedHouse.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse.tsx @@ -12,7 +12,7 @@ import { import { RA } from "~/utils/functions" import { serializeLayoutsKey } from "../../../db/layouts" import { useHouse, useHouseSystemId } from "../../state/houses" -import { useIsMoveRotateable, useIsStretchable } from "../../state/siteCtx" +import { useTransformabilityBooleans } from "../../state/siteCtx" import RotateHandles from "../handles/RotateHandles" import StretchHandle from "../handles/StretchHandle" import GroupedColumn from "./GroupedColumn" @@ -67,23 +67,23 @@ const GroupedHouse = (props: Props) => { useHouseElementOutline(houseId, houseGroupRef) - const isStretchable = useIsStretchable(houseId) - const isMoveRotateable = useIsMoveRotateable(houseId) + const { stretchEnabled, moveRotateEnabled } = + useTransformabilityBooleans(houseId) useHouseMaterialOps({ houseId, - houseGroupRef, - serializedLayoutsKey: serializeLayoutsKey({ systemId, dnas }), + ref: houseGroupRef, + layoutsKey: serializeLayoutsKey({ systemId, dnas }), }) return ( - + { houseId={houseId} axis="z" direction={1} - disable={!isStretchable} + disable={!stretchEnabled} /> @@ -127,10 +127,10 @@ const GroupedHouse = (props: Props) => { - + {columnsUp} {columnsDown} diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx new file mode 100644 index 00000000..d0adc95e --- /dev/null +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -0,0 +1,117 @@ +import { pipe } from "fp-ts/lib/function" +import React, { useRef } from "react" +import { Group } from "three" +import { serializeLayoutsKey } from "../../../db/layouts" +import { House } from "../../../db/user" +import { RA } from "../../../utils/functions" +import { useHouseMaterialOps } from "../../state/hashedMaterials" +import { useDnasLayout } from "../../state/layouts" +import { useTransformabilityBooleans } from "../../state/siteCtx" +import { useStretchLength } from "../../state/transients/stretchLength" +import RotateHandles from "../handles/RotateHandles" +import StretchHandle from "../handles/StretchHandle" +import GroupedColumn from "./GroupedColumn" +import PreviewHouses from "./preview/PreviewHouses" +import StretchWidth from "./stretchWidth/StretchWidth" + +type Props = { + house: House +} + +const GroupedHouse2 = (props: Props) => { + const { house } = props + const { systemId, id: houseId, dnas, position, rotation } = house + + const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + + const rootRef = useRef(null!) + const startRef = useRef(null!) + const endRef = useRef(null!) + + const { stretchEnabled, moveRotateEnabled } = + useTransformabilityBooleans(houseId) + + const layout = useDnasLayout(house) + + const { startColumn, midColumns, endColumn, columnsUp, columnsDown } = + useStretchLength({ houseId, layout, startRef, endRef }) + + useHouseMaterialOps({ + houseId, + ref: rootRef, + layoutsKey, + }) + + const startColumnRef = useRef(null!) + const midColumnsRef = useRef(null!) + const endColumnRef = useRef(null!) + + const setHouseVisible = (b: boolean) => + [startColumnRef, midColumnsRef, endColumnRef].forEach((ref) => { + ref.current.visible = b + }) + + return ( + + + + + + + {pipe( + midColumns, + RA.map((column) => ( + + )) + )} + + + + + + + + + + + + {columnsUp} + {columnsDown} + + + + ) +} + +export default GroupedHouse2 diff --git a/app/design/ui-3d/grouped/preview/PreviewHouses.tsx b/app/design/ui-3d/grouped/preview/PreviewHouses.tsx index a748c93c..07cd8c2f 100644 --- a/app/design/ui-3d/grouped/preview/PreviewHouses.tsx +++ b/app/design/ui-3d/grouped/preview/PreviewHouses.tsx @@ -21,8 +21,10 @@ const PreviewHouses = (props: Props) => { {pipe( dnaPreviews, R.collect(S.Ord)((k, { value }) => { + console.log({ houseId, k }) return ( ((props, rootRef) => { const rightHandleRef = useRef(null!) const leftHandleRef = useRef(null!) - const isStretchable = useIsStretchable(houseId) + const { stretchEnabled } = useTransformabilityBooleans(houseId) const systemId = houses[houseId].systemId @@ -347,7 +347,7 @@ const StretchWidth = forwardRef((props, rootRef) => { return ( Date: Sun, 2 Jul 2023 14:23:59 +0100 Subject: [PATCH 022/132] wip circular deps --- app/design/state/layouts.ts | 20 +++++-- app/design/state/transients/transforms.ts | 2 +- app/design/ui-3d/grouped/GroupedApp.tsx | 11 ++-- app/design/ui-3d/grouped/GroupedHouse2.tsx | 6 ++- app/design/ui/SiteSidebar.tsx | 3 +- app/design/ui/menu/interactions/Exporters.tsx | 2 +- app/workers/exporters/events.ts | 42 +++++++++++++++ .../index.ts => workers/exporters/hook.ts} | 53 +++---------------- app/{design => }/workers/exporters/worker.ts | 2 +- app/workers/index.ts | 2 +- app/workers/layouts.ts | 15 +++++- 11 files changed, 99 insertions(+), 59 deletions(-) create mode 100644 app/workers/exporters/events.ts rename app/{design/workers/exporters/index.ts => workers/exporters/hook.ts} (66%) rename app/{design => }/workers/exporters/worker.ts (97%) diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index f89e9f9f..67f99a0e 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -6,11 +6,13 @@ import * as A from "fp-ts/Array" import { pipe } from "fp-ts/lib/function" import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" +import { suspend } from "suspend-react" import { proxy, ref, snapshot, useSnapshot } from "valtio" import { usePadColumn } from "../../data/modules" import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../../db/layouts" import { isSSR } from "../../utils/next" -import houses, { +import { getLayoutsWorker } from "../../workers" +import { modulesToRows, useDnasModules, useHouse, @@ -363,9 +365,21 @@ export const modulesToColumnLayout = (modules: Module[]) => { ) } -export const useDnasLayout = (layoutsKey: LayoutsKey) => { +export const useDnasLayout = (layoutsKey: LayoutsKey): ColumnLayout => { const snap = useSnapshot(layouts) as typeof layouts - return snap[serializeLayoutsKey(layoutsKey)] + const serialKey = serializeLayoutsKey(layoutsKey) + const maybeLayout: ColumnLayout | undefined = snap?.[serialKey] + + return suspend(async () => { + if (maybeLayout) return maybeLayout + + const layoutsWorker = getLayoutsWorker() + + if (layoutsWorker === null) + throw new Error(`layoutsWorker null in useDnasLayout`) + + return await layoutsWorker.processLayout(layoutsKey) + }, [maybeLayout]) } export const useDnasColumnLayout = (systemId: string, dnas: string[]) => { diff --git a/app/design/state/transients/transforms.ts b/app/design/state/transients/transforms.ts index 69a3042e..5de45c23 100644 --- a/app/design/state/transients/transforms.ts +++ b/app/design/state/transients/transforms.ts @@ -5,8 +5,8 @@ import { useDebouncedCallback } from "use-debounce" import { proxy } from "valtio" import { useSubscribeKey } from "~/utils/hooks" import { yAxis } from "~/utils/three" +import { dispatchUpdateExportModelsEvent } from "../../../workers/exporters/events" import dimensions, { collideOBB, useComputeDimensions } from "../dimensions" -import { dispatchUpdateExportModelsEvent } from "../../workers/exporters" import houses from "../houses" export type Transforms = { diff --git a/app/design/ui-3d/grouped/GroupedApp.tsx b/app/design/ui-3d/grouped/GroupedApp.tsx index 7e533d1a..93ec8177 100644 --- a/app/design/ui-3d/grouped/GroupedApp.tsx +++ b/app/design/ui-3d/grouped/GroupedApp.tsx @@ -1,15 +1,16 @@ "use client" import { pipe } from "fp-ts/lib/function" -import { Fragment } from "react" +import { Fragment, Suspense } from "react" import { usePreviews } from "~/design/state/previews" import { useRouting } from "~/design/state/routing" import { A, R } from "~/utils/functions" import { useDragHandler, useGestures } from "../../state/gestures" import { useHouses } from "../../state/houses" -import { useExportersWorker } from "../../workers/exporters" +import { useExportersWorker } from "../../../workers/exporters/hook" import XZPlane from "../XZPlane" import YPlane from "../YPlane" import GroupedHouse2 from "./GroupedHouse2" +import GroupedHouse from "./GroupedHouse" const GroupedApp = () => { const houses = useHouses() @@ -20,6 +21,8 @@ const GroupedApp = () => { useExportersWorker() + // + return ( @@ -27,7 +30,9 @@ const GroupedApp = () => { houses, R.toArray, A.map(([houseId, house]) => ( - + + + )) )} diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index d0adc95e..080d78a9 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -1,6 +1,7 @@ import { pipe } from "fp-ts/lib/function" import React, { useRef } from "react" import { Group } from "three" +import { snapshot } from "valtio" import { serializeLayoutsKey } from "../../../db/layouts" import { House } from "../../../db/user" import { RA } from "../../../utils/functions" @@ -31,7 +32,10 @@ const GroupedHouse2 = (props: Props) => { const { stretchEnabled, moveRotateEnabled } = useTransformabilityBooleans(houseId) - const layout = useDnasLayout(house) + const layout = useDnasLayout({ + systemId, + dnas: [...dnas], + }) const { startColumn, midColumns, endColumn, columnsUp, columnsDown } = useStretchLength({ houseId, layout, startRef, endRef }) diff --git a/app/design/ui/SiteSidebar.tsx b/app/design/ui/SiteSidebar.tsx index def17cd2..7e37d24e 100644 --- a/app/design/ui/SiteSidebar.tsx +++ b/app/design/ui/SiteSidebar.tsx @@ -4,6 +4,7 @@ import { mapWithIndex } from "fp-ts/lib/ReadonlyArray" import { nanoid } from "nanoid" import { Fragment, useMemo, useState } from "react" import { Vector3 } from "three" +import { ref } from "valtio" import { useHouseTypes } from "~/data/houseTypes" import Sidebar from "~/ui//Sidebar" import { useCameraGroundRaycast } from "../state/camera" @@ -81,7 +82,7 @@ const SiteSidebar = ({ open, close }: Props) => { systemId: houseType.systemId, position, rotation: 0, - dnas: houseType.dnas as string[], + dnas: ref(houseType.dnas), modifiedMaterials: {}, friendlyName: `Building ${ Object.keys(houses).length + 1 diff --git a/app/design/ui/menu/interactions/Exporters.tsx b/app/design/ui/menu/interactions/Exporters.tsx index e651457d..4de03898 100644 --- a/app/design/ui/menu/interactions/Exporters.tsx +++ b/app/design/ui/menu/interactions/Exporters.tsx @@ -1,5 +1,5 @@ import { ArrowDown } from "@carbon/icons-react" -import { dispatchGetModelEvent } from "../../../workers/exporters" +import { dispatchGetModelEvent } from "../../../../workers/exporters/events" import ContextMenuButton from "../ContextMenuButton" import ContextMenuNested from "../ContextMenuNested" diff --git a/app/workers/exporters/events.ts b/app/workers/exporters/events.ts new file mode 100644 index 00000000..ef3dec8e --- /dev/null +++ b/app/workers/exporters/events.ts @@ -0,0 +1,42 @@ +export const UPDATE_EXPORT_MODELS_EVENT = "UpdateExportModels" +export const GET_EXPORT_MODEL_EVENT = "GetExportModel" + +export type UpdateWorkerGroupEventDetail = { + houseId: string + payload: any +} + +export type UpdateWorkerGroupEvent = { + type: typeof UPDATE_EXPORT_MODELS_EVENT + detail: UpdateWorkerGroupEventDetail +} + +export type GetModelEventDetail = { + houseId: string + format: "OBJ" | "GLB" +} + +export type GetModelEvent = { + type: typeof GET_EXPORT_MODEL_EVENT + detail: GetModelEventDetail +} + +export const dispatchUpdateExportModelsEvent = ({ + houseId, + payload, +}: UpdateWorkerGroupEventDetail) => { + dispatchEvent( + new CustomEvent(UPDATE_EXPORT_MODELS_EVENT, { + detail: { houseId, payload }, + }) + ) +} + +export const dispatchGetModelEvent = ({ + houseId, + format, +}: GetModelEventDetail) => { + dispatchEvent( + new CustomEvent(GET_EXPORT_MODEL_EVENT, { detail: { houseId, format } }) + ) +} diff --git a/app/design/workers/exporters/index.ts b/app/workers/exporters/hook.ts similarity index 66% rename from app/design/workers/exporters/index.ts rename to app/workers/exporters/hook.ts index a20839ad..862195af 100644 --- a/app/design/workers/exporters/index.ts +++ b/app/workers/exporters/hook.ts @@ -1,51 +1,14 @@ import { Remote, wrap } from "comlink" import { useEffect, useRef } from "react" import { useEvent } from "react-use" -import houses from "../../state/houses" -import { ExportersWorkerAPI } from "./worker" - -export const UPDATE_EXPORT_MODELS_EVENT = "UpdateExportModels" -export const GET_EXPORT_MODEL_EVENT = "GetExportModel" - -export type UpdateWorkerGroupEventDetail = { - houseId: string - payload: any -} - -export type UpdateWorkerGroupEvent = { - type: typeof UPDATE_EXPORT_MODELS_EVENT - detail: UpdateWorkerGroupEventDetail -} - -export type GetModelEventDetail = { - houseId: string - format: "OBJ" | "GLB" -} - -export type GetModelEvent = { - type: typeof GET_EXPORT_MODEL_EVENT - detail: GetModelEventDetail -} - -export const dispatchUpdateExportModelsEvent = ({ - houseId, - payload, -}: UpdateWorkerGroupEventDetail) => { - dispatchEvent( - new CustomEvent(UPDATE_EXPORT_MODELS_EVENT, { - detail: { houseId, payload }, - }) - ) -} - -export const dispatchGetModelEvent = ({ - houseId, - format, -}: GetModelEventDetail) => { - dispatchEvent( - new CustomEvent(GET_EXPORT_MODEL_EVENT, { detail: { houseId, format } }) - ) -} +import houses from "../../design/state/houses" +import { + GetModelEvent, + UpdateWorkerGroupEvent, + GET_EXPORT_MODEL_EVENT, + UPDATE_EXPORT_MODELS_EVENT, +} from "./events" +import type { ExportersWorkerAPI } from "./worker" export const useExportersWorker = () => { const ref = useRef | null>(null) diff --git a/app/design/workers/exporters/worker.ts b/app/workers/exporters/worker.ts similarity index 97% rename from app/design/workers/exporters/worker.ts rename to app/workers/exporters/worker.ts index 01ae77fd..b9d02291 100644 --- a/app/design/workers/exporters/worker.ts +++ b/app/workers/exporters/worker.ts @@ -1,7 +1,7 @@ import { expose } from "comlink" import { Group, Matrix4, Mesh, Object3D, ObjectLoader } from "three" import { GLTFExporter, OBJExporter } from "three-stdlib" -import { UpdateWorkerGroupEventDetail } from "." +import { UpdateWorkerGroupEventDetail } from "./events" function flattenObject(root: Object3D): Group { const flatGroup = new Group() diff --git a/app/workers/index.ts b/app/workers/index.ts index 10be0fa2..a73e53f5 100644 --- a/app/workers/index.ts +++ b/app/workers/index.ts @@ -1,7 +1,7 @@ "use client" import { Remote, wrap } from "comlink" import { isSSR } from "../utils/next" -import { LayoutsAPI } from "./layouts" +import type { LayoutsAPI } from "./layouts" let systemsWorker: Worker | null = null let layoutsWorker: Remote | null = null diff --git a/app/workers/layouts.ts b/app/workers/layouts.ts index a555cde2..0fbc58ef 100644 --- a/app/workers/layouts.ts +++ b/app/workers/layouts.ts @@ -76,7 +76,12 @@ const processLayout = async ({ systemId, dnas }: LayoutsKey) => { layout, layoutsKey, }) + + console.log({ layout }) + + return layout } + const processLayoutsQueue = async () => { if (modulesCache.length === 0) { return @@ -99,8 +104,13 @@ if (!isSSR()) { }) } -const postLayout = (key: LayoutsKey) => layoutsQueue.push(key) -const postLayouts = (keys: LayoutsKey[]) => keys.map(postLayout) +const postLayout = (key: LayoutsKey) => { + layoutsQueue.push(key) +} + +const postLayouts = (keys: LayoutsKey[]) => { + keys.map(postLayout) +} if (!isSSR()) { liveQuery(() => systemsDB.houseTypes.toArray()).subscribe((houseTypes) => { @@ -112,6 +122,7 @@ if (!isSSR()) { const api = { postLayout, postLayouts, + processLayout, } export type LayoutsAPI = typeof api From a40db8d46b7b84ce4a46721d9a4e1f1bc534a947 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 3 Jul 2023 12:27:49 +0100 Subject: [PATCH 023/132] wip transform matrices --- app/design/state/dimensions.tsx | 95 ++++++++-------- app/design/state/events/index.ts | 46 ++++++++ app/design/state/gestures/index.ts | 24 +++- app/design/state/hashedMaterials.ts | 4 +- app/design/state/layouts.ts | 5 +- app/design/ui-3d/grouped/GroupedHouse2.tsx | 121 ++++++++++++++++++--- 6 files changed, 228 insertions(+), 67 deletions(-) create mode 100644 app/design/state/events/index.ts diff --git a/app/design/state/dimensions.tsx b/app/design/state/dimensions.tsx index c24166d0..0e472f77 100644 --- a/app/design/state/dimensions.tsx +++ b/app/design/state/dimensions.tsx @@ -10,7 +10,7 @@ import houses, { useHouse } from "./houses" import { layouts } from "./layouts" import { postTransformsTransients, Transforms } from "./transients/transforms" -type Dimensions = { +export type Dimensions = { obb: OBB width: number height: number @@ -81,50 +81,55 @@ export const useComputeDimensions = (houseId: string) => { const layoutsKey = useLayoutsKey(houseId) - return (transients: Transforms = {}): Dimensions => { - const { - rotation: dr = 0, - position: { dx, dy, dz } = { dx: 0, dy: 0, dz: 0 }, - } = transients - - const columns = layouts[layoutsKey] - - const width = columns[0].gridGroups[0].modules[0].module.width - const height = columns[0].gridGroups.reduce( - (acc, gg) => acc + gg.modules[0].module.height, - 0 - ) - const z0 = columns[0].gridGroups[0].modules[0].z - const lastColumn = columns[columns.length - 1] - const lastGridGroup = - lastColumn.gridGroups[lastColumn.gridGroups.length - 1] - const lastModule = lastGridGroup.modules[lastGridGroup.modules.length - 1] - const z1 = lastColumn.z + lastModule.z + lastModule.module.length - - const length = z1 - z0 - - const { x: px, y: py, z: pz } = houses[houseId].position - - const halfSize = new Vector3(width / 2, height / 2, length / 2) - const center = new Vector3(0, 0, 0) - const obb = new OBB(center, halfSize) - - rotationMatrix.current.makeRotationY(houses[houseId].rotation + dr) - translationMatrix.current.makeTranslation( - px + dx, - py + dy + height / 2, - pz + dz - ) - - obb.applyMatrix4(translationMatrix.current.multiply(rotationMatrix.current)) - - return { - width, - height, - length, - obb, - } - } + return useCallback( + (transients: Transforms = {}): Dimensions => { + const { + rotation: dr = 0, + position: { dx, dy, dz } = { dx: 0, dy: 0, dz: 0 }, + } = transients + + const columns = layouts[layoutsKey] + + const width = columns[0].gridGroups[0].modules[0].module.width + const height = columns[0].gridGroups.reduce( + (acc, gg) => acc + gg.modules[0].module.height, + 0 + ) + const z0 = columns[0].gridGroups[0].modules[0].z + const lastColumn = columns[columns.length - 1] + const lastGridGroup = + lastColumn.gridGroups[lastColumn.gridGroups.length - 1] + const lastModule = lastGridGroup.modules[lastGridGroup.modules.length - 1] + const z1 = lastColumn.z + lastModule.z + lastModule.module.length + + const length = z1 - z0 + + const { x: px, y: py, z: pz } = houses[houseId].position + + const halfSize = new Vector3(width / 2, height / 2, length / 2) + const center = new Vector3(0, 0, 0) + const obb = new OBB(center, halfSize) + + rotationMatrix.current.makeRotationY(houses[houseId].rotation + dr) + translationMatrix.current.makeTranslation( + px + dx, + py + dy + height / 2, + pz + dz + ) + + obb.applyMatrix4( + translationMatrix.current.multiply(rotationMatrix.current) + ) + + return { + width, + height, + length, + obb, + } + }, + [houseId, layoutsKey] + ) } export const useHouseDimensionsUpdates = (houseId: string) => { diff --git a/app/design/state/events/index.ts b/app/design/state/events/index.ts new file mode 100644 index 00000000..e62d67b2 --- /dev/null +++ b/app/design/state/events/index.ts @@ -0,0 +1,46 @@ +import { useEvent } from "react-use" + +const MOVE_HOUSE_INTENT_EVENT = "MoveHouseIntentEvent" +const MOVE_HOUSE_EVENT = "MoveHouseEvent" +const STRETCH_HOUSE_INTENT_EVENT = "StretchHouseIntentEvent" + +type MoveHouseDetail = { + delta: V3 + houseId: string +} + +type MoveHouseIntentDetail = MoveHouseDetail + +type StretchHouseIntentDetail = { + houseId: string + // look at your stretch proxies to figure this +} + +export const dispatchMoveHouse = (detail: MoveHouseIntentDetail) => + dispatchEvent( + new CustomEvent(MOVE_HOUSE_EVENT, { + detail, + }) + ) + +export const dispatchMoveHouseIntent = (detail: MoveHouseIntentDetail) => + dispatchEvent( + new CustomEvent(MOVE_HOUSE_INTENT_EVENT, { + detail, + }) + ) + +export const useMoveHouseIntentListener = ( + f: (eventDetail: MoveHouseIntentDetail) => void +) => useEvent(MOVE_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) + +export const useMoveHouseListener = ( + f: (eventDetail: MoveHouseIntentDetail) => void +) => useEvent(MOVE_HOUSE_EVENT, ({ detail }) => f(detail)) + +export const dispatchStretchHouseIntent = (detail: StretchHouseIntentDetail) => + dispatchEvent(new CustomEvent(STRETCH_HOUSE_INTENT_EVENT, { detail })) + +export const useStretchHouseIntentListener = ( + f: (eventDetail: StretchHouseIntentDetail) => void +) => useEvent(STRETCH_HOUSE_INTENT_EVENT, f) diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index c82a0ef5..77d5b999 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -22,6 +22,7 @@ import { openMenu } from "../menu" import pointer from "../pointer" import siteCtx, { downMode, EditModeEnum } from "../siteCtx" import dragProxy, { Drag, StretchHandleIdentifier } from "./drag" +import { dispatchMoveHouseIntent } from "../events" export const useDragHandler = () => { const { rotateV2, unrotateV2 } = useRotations() @@ -40,16 +41,26 @@ export const useDragHandler = () => { } = dragProxy switch (identifierType) { case "HOUSE_ELEMENT": { - preTransformsTransients[houseId] = { - position: { - dx: x1 - x0, - dy: 0, - dz: z1 - z0, + dispatchMoveHouseIntent({ + houseId, + delta: { + x: x1 - x0, + y: 0, + z: z1 - z0, }, - } + }) + // dispatch event move house intent + // preTransformsTransients[houseId] = { + // position: { + // dx: x1 - x0, + // dy: 0, + // dz: z1 - z0, + // }, + // } return } case "ROTATE_HANDLE": { + // dispatch event rotate house intent const { x: cx, z: cz } = getHouseCenter(houseId) const angle0 = Math.atan2(cz - z0, cx - x0) const angle = Math.atan2(cz - z1, cx - x1) @@ -59,6 +70,7 @@ export const useDragHandler = () => { return } case "STRETCH_HANDLE": { + // dispatch event stretch house intent const [distanceX, distanceZ] = unrotateV2(houseId, [x1 - x0, z1 - z0]) const [dx, dz] = rotateV2(houseId, [0, distanceZ]) diff --git a/app/design/state/hashedMaterials.ts b/app/design/state/hashedMaterials.ts index cfe37a1a..c51250be 100644 --- a/app/design/state/hashedMaterials.ts +++ b/app/design/state/hashedMaterials.ts @@ -3,7 +3,7 @@ import { useSubscribe, useSubscribeKey } from "~/utils/hooks" import { createMaterial, isMesh } from "~/utils/three" import { invalidate } from "@react-three/fiber" import { identity, pipe } from "fp-ts/lib/function" -import { MutableRefObject, useEffect, useMemo, useRef } from "react" +import { MutableRefObject, RefObject, useEffect, useMemo, useRef } from "react" import { Group, Material, Matrix4, Plane, Vector3 } from "three" import { proxy, ref } from "valtio" import { useElements, useSystemElements } from "~/data/elements" @@ -239,7 +239,7 @@ export const useHouseMaterialOps = ({ layoutsKey, }: { houseId: string - ref: MutableRefObject + ref: RefObject layoutsKey: string }) => { const systemId = houses[houseId].systemId diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 67f99a0e..f8a6d373 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -378,7 +378,10 @@ export const useDnasLayout = (layoutsKey: LayoutsKey): ColumnLayout => { if (layoutsWorker === null) throw new Error(`layoutsWorker null in useDnasLayout`) - return await layoutsWorker.processLayout(layoutsKey) + const layout = await layoutsWorker.processLayout(layoutsKey) + layouts[serialKey] = layout + + return layout }, [maybeLayout]) } diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 080d78a9..9cf423d7 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -1,14 +1,25 @@ +import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" -import React, { useRef } from "react" -import { Group } from "three" +import React, { useEffect, useMemo, useRef } from "react" +import { Group, Matrix4, Vector3 } from "three" +import { OBB } from "three-stdlib" import { snapshot } from "valtio" import { serializeLayoutsKey } from "../../../db/layouts" import { House } from "../../../db/user" import { RA } from "../../../utils/functions" +import dimensions, { collideOBB, Dimensions } from "../../state/dimensions" +import { + dispatchMoveHouse, + useMoveHouseIntentListener, + useMoveHouseListener, +} from "../../state/events" import { useHouseMaterialOps } from "../../state/hashedMaterials" import { useDnasLayout } from "../../state/layouts" import { useTransformabilityBooleans } from "../../state/siteCtx" -import { useStretchLength } from "../../state/transients/stretchLength" +import { + splitColumns, + useStretchLength, +} from "../../state/transients/stretchLength" import RotateHandles from "../handles/RotateHandles" import StretchHandle from "../handles/StretchHandle" import GroupedColumn from "./GroupedColumn" @@ -21,11 +32,25 @@ type Props = { const GroupedHouse2 = (props: Props) => { const { house } = props - const { systemId, id: houseId, dnas, position, rotation } = house + const { systemId, id: houseId, position, rotation } = house + const dnas = [...house.dnas] - const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + const translationMatrix = useMemo(() => { + const m = new Matrix4() + const { x, y, z } = position + m.makeTranslation(x, y, z) + return m + }, [position]) - const rootRef = useRef(null!) + const rotationMatrix = useMemo(() => { + const m = new Matrix4() + m.makeRotationY(rotation) + return m + }, [rotation]) + + const reactHouseMatrix = translationMatrix.multiply(rotationMatrix) + + const rootRef = useRef(null) const startRef = useRef(null!) const endRef = useRef(null!) @@ -34,16 +59,86 @@ const GroupedHouse2 = (props: Props) => { const layout = useDnasLayout({ systemId, - dnas: [...dnas], + dnas, }) - const { startColumn, midColumns, endColumn, columnsUp, columnsDown } = - useStretchLength({ houseId, layout, startRef, endRef }) + const { width, length, height, obb }: Dimensions = useMemo(() => { + const width = layout[0].gridGroups[0].modules[0].module.width + const height = layout[0].gridGroups.reduce( + (acc, gg) => acc + gg.modules[0].module.height, + 0 + ) + const z0 = layout[0].gridGroups[0].modules[0].z + const lastColumn = layout[layout.length - 1] + const lastGridGroup = + lastColumn.gridGroups[lastColumn.gridGroups.length - 1] + const lastModule = lastGridGroup.modules[lastGridGroup.modules.length - 1] + const z1 = lastColumn.z + lastModule.z + lastModule.module.length + + const length = z1 - z0 + + const halfSize = new Vector3(width / 2, height / 2, length / 2) + const center = new Vector3(0, 0, 0) + const obb = new OBB(center, halfSize) + + obb.applyMatrix4(reactHouseMatrix) + + dimensions[houseId] = { + height, + length, + width, + obb, + } + + return { + height, + length, + width, + obb, + } + }, [layout, reactHouseMatrix, houseId]) + + useEffect(() => { + if (!rootRef.current) return + rootRef.current.matrixAutoUpdate = false + rootRef.current.matrix.copy(reactHouseMatrix) + }, [reactHouseMatrix]) + + const frameOBB = useRef(new OBB()) + const frameHouseMatrix = useRef(new Matrix4()) + + useMoveHouseIntentListener((detail) => { + if (detail.houseId !== houseId) return + + const { x, z } = detail.delta + + const deltaMatrix = new Matrix4().makeTranslation(x, 0, z) + frameHouseMatrix.current = reactHouseMatrix.clone() + frameHouseMatrix.current.multiply(deltaMatrix) + + frameOBB.current.copy(obb) + frameOBB.current.applyMatrix4(frameHouseMatrix.current) + + const collision = collideOBB(frameOBB.current, [houseId]) + if (collision) return + + dispatchMoveHouse({ houseId, delta: detail.delta }) + }) + + useMoveHouseListener((detail) => { + if (!rootRef.current || houseId !== detail.houseId) return + + rootRef.current.matrix.copy(frameHouseMatrix.current) + rootRef.current.updateMatrixWorld() + invalidate() + }) + + const { startColumn, endColumn, midColumns } = splitColumns(layout) useHouseMaterialOps({ houseId, ref: rootRef, - layoutsKey, + layoutsKey: serializeLayoutsKey({ systemId, dnas }), }) const startColumnRef = useRef(null!) @@ -109,11 +204,11 @@ const GroupedHouse2 = (props: Props) => { scale={moveRotateEnabled ? [1, 1, 1] : [0, 0, 0]} /> - + {/* {columnsUp} {columnsDown} - - + */} + {/* */} ) } From 1a6b589bccbfe798c9f08a492970336daa8c3953 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 4 Jul 2023 13:10:03 +0100 Subject: [PATCH 024/132] wip perfect move --- app/build/layout.tsx | 1 + app/design/page.tsx | 2 + app/design/state/events/index.ts | 44 ++++++++++++++---- app/design/state/gestures/index.ts | 54 +++++++++++++++++++--- app/design/state/layouts.ts | 2 +- app/design/state/routing.ts | 10 +++- app/design/ui-3d/grouped/GroupedApp.tsx | 6 +-- app/design/ui-3d/grouped/GroupedHouse2.tsx | 21 ++++++++- app/design/ui/Breadcrumbs.tsx | 7 ++- app/workers/layouts.ts | 4 +- 10 files changed, 121 insertions(+), 30 deletions(-) diff --git a/app/build/layout.tsx b/app/build/layout.tsx index b94f9526..dbbe9ff2 100644 --- a/app/build/layout.tsx +++ b/app/build/layout.tsx @@ -15,6 +15,7 @@ const HousesPillsSelector = dynamic( const BuildLayout = ({ children }: PropsWithChildren<{}>) => { getSystemsWorker() getLayoutsWorker() + return (
diff --git a/app/design/page.tsx b/app/design/page.tsx index 70e9f8a4..0c4ce5cc 100644 --- a/app/design/page.tsx +++ b/app/design/page.tsx @@ -2,6 +2,7 @@ import DataInit from "~/data/DataInit" import { TrpcProvider } from "../ui/TrpcProvider" import { getLayoutsWorker, getSystemsWorker } from "../workers" +import { Routing } from "./state/routing" import GroupedApp from "./ui-3d/grouped/GroupedApp" import AppInit from "./ui-3d/init/AppInit" @@ -12,6 +13,7 @@ const IndexPage = () => { + diff --git a/app/design/state/events/index.ts b/app/design/state/events/index.ts index e62d67b2..baa7594d 100644 --- a/app/design/state/events/index.ts +++ b/app/design/state/events/index.ts @@ -2,45 +2,69 @@ import { useEvent } from "react-use" const MOVE_HOUSE_INTENT_EVENT = "MoveHouseIntentEvent" const MOVE_HOUSE_EVENT = "MoveHouseEvent" +const ROTATE_HOUSE_EVENT = "RotateHouseEvent" +const ROTATE_HOUSE_INTENT_EVENT = "RotateHouseIntentEvent" const STRETCH_HOUSE_INTENT_EVENT = "StretchHouseIntentEvent" type MoveHouseDetail = { delta: V3 houseId: string + last: boolean } -type MoveHouseIntentDetail = MoveHouseDetail +type RotateHouseDetail = { + rotation: number + houseId: string +} -type StretchHouseIntentDetail = { +type StretchHouseDetail = { houseId: string // look at your stretch proxies to figure this } -export const dispatchMoveHouse = (detail: MoveHouseIntentDetail) => +export const dispatchMoveHouseIntent = (detail: MoveHouseDetail) => dispatchEvent( - new CustomEvent(MOVE_HOUSE_EVENT, { + new CustomEvent(MOVE_HOUSE_INTENT_EVENT, { detail, }) ) -export const dispatchMoveHouseIntent = (detail: MoveHouseIntentDetail) => +export const dispatchMoveHouse = (detail: MoveHouseDetail) => dispatchEvent( - new CustomEvent(MOVE_HOUSE_INTENT_EVENT, { + new CustomEvent(MOVE_HOUSE_EVENT, { detail, }) ) export const useMoveHouseIntentListener = ( - f: (eventDetail: MoveHouseIntentDetail) => void + f: (eventDetail: MoveHouseDetail) => void ) => useEvent(MOVE_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) export const useMoveHouseListener = ( - f: (eventDetail: MoveHouseIntentDetail) => void + f: (eventDetail: MoveHouseDetail) => void ) => useEvent(MOVE_HOUSE_EVENT, ({ detail }) => f(detail)) -export const dispatchStretchHouseIntent = (detail: StretchHouseIntentDetail) => +export const dispatchRotateHouseIntent = (detail: RotateHouseDetail) => + dispatchEvent(new CustomEvent(ROTATE_HOUSE_INTENT_EVENT, { detail })) + +export const dispatchRotateHouse = (detail: RotateHouseDetail) => + dispatchEvent( + new CustomEvent(ROTATE_HOUSE_EVENT, { + detail, + }) + ) + +export const useRotateHouseIntentListener = ( + f: (eventDetail: RotateHouseDetail) => void +) => useEvent(ROTATE_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) + +export const useRotateHouseListener = ( + f: (eventDetail: RotateHouseDetail) => void +) => useEvent(ROTATE_HOUSE_EVENT, ({ detail }) => f(detail)) + +export const dispatchStretchHouseIntent = (detail: StretchHouseDetail) => dispatchEvent(new CustomEvent(STRETCH_HOUSE_INTENT_EVENT, { detail })) export const useStretchHouseIntentListener = ( - f: (eventDetail: StretchHouseIntentDetail) => void + f: (eventDetail: StretchHouseDetail) => void ) => useEvent(STRETCH_HOUSE_INTENT_EVENT, f) diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index 77d5b999..f2ede24e 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -48,6 +48,7 @@ export const useDragHandler = () => { y: 0, z: z1 - z0, }, + last: false, }) // dispatch event move house intent // preTransformsTransients[houseId] = { @@ -98,13 +99,52 @@ export const useDragHandler = () => { }) useSubscribeKey(dragProxy, "end", () => { - if (dragProxy.end) { - setTransforms() - setStretchLength() - setPreviews() + if (!dragProxy.end) return + const cleanup = () => { dragProxy.start = null dragProxy.drag = null + dragProxy.end = false + } + + if (dragProxy.drag === null || dragProxy.start === null) { + cleanup() + return + } + + const { + start: { + identifier: { houseId, identifierType }, + point: { x: x0, y: y0, z: z0 }, + }, + drag: { + point: { x: x1, y: y1, z: z1 }, + }, + } = dragProxy + + const delta = { + x: x1 - x0, + y: 0, + z: z1 - z0, } + + if (dragProxy.end) { + switch (siteCtx.editMode) { + case EditModeEnum.Enum.MOVE_ROTATE: + dispatchMoveHouseIntent({ + houseId, + delta, + last: true, + }) + // setTransforms() + break + case EditModeEnum.Enum.STRETCH: + // setStretchLength() + // setPreviews() + break + } + } + + cleanup() }) } @@ -151,10 +191,12 @@ export const useGestures = (): any => ...identifier, } - if (siteCtx.editMode === null) + if (siteCtx.editMode === null) { siteCtx.editMode = EditModeEnum.Enum.MOVE_ROTATE - if (siteCtx.houseId !== identifier.houseId) + } + if (siteCtx.houseId !== identifier.houseId) { siteCtx.houseId = identifier.houseId + } dragProxy.start = { identifier, diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index f8a6d373..6d4e454d 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -7,7 +7,7 @@ import { pipe } from "fp-ts/lib/function" import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" import { suspend } from "suspend-react" -import { proxy, ref, snapshot, useSnapshot } from "valtio" +import { proxy, ref, useSnapshot } from "valtio" import { usePadColumn } from "../../data/modules" import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../../db/layouts" import { isSSR } from "../../utils/next" diff --git a/app/design/state/routing.ts b/app/design/state/routing.ts index 766544e6..0dc7ac8f 100644 --- a/app/design/state/routing.ts +++ b/app/design/state/routing.ts @@ -1,3 +1,4 @@ +"use client" import { useRoute } from "~/utils/wouter" import { useEffect, useRef } from "react" import { useLocation } from "wouter" @@ -51,10 +52,17 @@ export const useRouting = () => { enterLevelMode(levelIndex) break } - case !("houseId" in params): + case !("houseId" in params): { exitBuildingMode() + break + } } urlChangingLock.current = false }, [params]) } + +export const Routing = () => { + useRouting() + return null +} diff --git a/app/design/ui-3d/grouped/GroupedApp.tsx b/app/design/ui-3d/grouped/GroupedApp.tsx index 93ec8177..ed435241 100644 --- a/app/design/ui-3d/grouped/GroupedApp.tsx +++ b/app/design/ui-3d/grouped/GroupedApp.tsx @@ -2,23 +2,19 @@ import { pipe } from "fp-ts/lib/function" import { Fragment, Suspense } from "react" import { usePreviews } from "~/design/state/previews" -import { useRouting } from "~/design/state/routing" import { A, R } from "~/utils/functions" +import { useExportersWorker } from "../../../workers/exporters/hook" import { useDragHandler, useGestures } from "../../state/gestures" import { useHouses } from "../../state/houses" -import { useExportersWorker } from "../../../workers/exporters/hook" import XZPlane from "../XZPlane" import YPlane from "../YPlane" import GroupedHouse2 from "./GroupedHouse2" -import GroupedHouse from "./GroupedHouse" const GroupedApp = () => { const houses = useHouses() usePreviews() const bindAll = useGestures() useDragHandler() - useRouting() - useExportersWorker() // diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 9cf423d7..761aa89d 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -14,6 +14,7 @@ import { useMoveHouseListener, } from "../../state/events" import { useHouseMaterialOps } from "../../state/hashedMaterials" +import houses from "../../state/houses" import { useDnasLayout } from "../../state/layouts" import { useTransformabilityBooleans } from "../../state/siteCtx" import { @@ -122,7 +123,7 @@ const GroupedHouse2 = (props: Props) => { const collision = collideOBB(frameOBB.current, [houseId]) if (collision) return - dispatchMoveHouse({ houseId, delta: detail.delta }) + dispatchMoveHouse(detail) }) useMoveHouseListener((detail) => { @@ -131,6 +132,16 @@ const GroupedHouse2 = (props: Props) => { rootRef.current.matrix.copy(frameHouseMatrix.current) rootRef.current.updateMatrixWorld() invalidate() + + if (detail.last) { + const { x, y, z } = detail.delta + + houses[houseId].position = { + x: position.x + x, + y: position.y + y, + z: position.z + z, + } + } }) const { startColumn, endColumn, midColumns } = splitColumns(layout) @@ -151,7 +162,13 @@ const GroupedHouse2 = (props: Props) => { }) return ( - + { : projectName } onClick={() => { - if (mode !== SiteCtxModeEnum.Enum.SITE) exitBuildingMode() - else if (!renamingProject) setRenamingProject(true) + if (mode !== SiteCtxModeEnum.Enum.SITE) { + exitBuildingMode() + } else if (!renamingProject) { + setRenamingProject(true) + } }} /> {renamingProject && ( diff --git a/app/workers/layouts.ts b/app/workers/layouts.ts index 0fbc58ef..28aa2413 100644 --- a/app/workers/layouts.ts +++ b/app/workers/layouts.ts @@ -68,8 +68,8 @@ const processLayout = async ({ systemId, dnas }: LayoutsKey) => { ) ) - // modules to rows const layout = modulesToColumnLayout(modules) + const layoutsKey = serializeLayoutsKey({ systemId, dnas }) layoutsDB.layouts.put({ @@ -77,8 +77,6 @@ const processLayout = async ({ systemId, dnas }: LayoutsKey) => { layoutsKey, }) - console.log({ layout }) - return layout } From 138b8df8cb929393a3611c3a31fe2a99e5b982f8 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 4 Jul 2023 14:54:28 +0100 Subject: [PATCH 025/132] wip fix collisions for move --- app/design/ui-3d/grouped/GroupedApp.tsx | 2 - app/design/ui-3d/grouped/GroupedHouse2.tsx | 149 ++++++++++-------- .../ui-3d/grouped/preview/PreviewHouses.tsx | 2 +- 3 files changed, 83 insertions(+), 70 deletions(-) diff --git a/app/design/ui-3d/grouped/GroupedApp.tsx b/app/design/ui-3d/grouped/GroupedApp.tsx index ed435241..7157ea2d 100644 --- a/app/design/ui-3d/grouped/GroupedApp.tsx +++ b/app/design/ui-3d/grouped/GroupedApp.tsx @@ -17,8 +17,6 @@ const GroupedApp = () => { useDragHandler() useExportersWorker() - // - return ( diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 761aa89d..a0058189 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -1,9 +1,8 @@ import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" -import React, { useEffect, useMemo, useRef } from "react" -import { Group, Matrix4, Vector3 } from "three" +import { Fragment, useEffect, useMemo, useRef } from "react" +import { Box3, Group, Matrix4, Vector3 } from "three" import { OBB } from "three-stdlib" -import { snapshot } from "valtio" import { serializeLayoutsKey } from "../../../db/layouts" import { House } from "../../../db/user" import { RA } from "../../../utils/functions" @@ -17,14 +16,10 @@ import { useHouseMaterialOps } from "../../state/hashedMaterials" import houses from "../../state/houses" import { useDnasLayout } from "../../state/layouts" import { useTransformabilityBooleans } from "../../state/siteCtx" -import { - splitColumns, - useStretchLength, -} from "../../state/transients/stretchLength" +import { splitColumns } from "../../state/transients/stretchLength" import RotateHandles from "../handles/RotateHandles" import StretchHandle from "../handles/StretchHandle" import GroupedColumn from "./GroupedColumn" -import PreviewHouses from "./preview/PreviewHouses" import StretchWidth from "./stretchWidth/StretchWidth" type Props = { @@ -79,7 +74,7 @@ const GroupedHouse2 = (props: Props) => { const length = z1 - z0 const halfSize = new Vector3(width / 2, height / 2, length / 2) - const center = new Vector3(0, 0, 0) + const center = new Vector3(0, height / 2, length / 2) const obb = new OBB(center, halfSize) obb.applyMatrix4(reactHouseMatrix) @@ -99,6 +94,22 @@ const GroupedHouse2 = (props: Props) => { } }, [layout, reactHouseMatrix, houseId]) + const obbBox = useMemo(() => { + const box3 = new Box3().setFromCenterAndSize( + obb.center, + obb.halfSize.clone().multiplyScalar(2) + ) + box3.applyMatrix4(rotationMatrix) + return box3 + }, [obb.center, obb.halfSize, rotationMatrix]) + + // const box3HelperRef = useRef(null) + + // useEffect(() => { + // box3HelperRef.current?.layers.disableAll() + // box3HelperRef.current?.layers.enable(CameraLayer.VISIBLE) + // }, []) + useEffect(() => { if (!rootRef.current) return rootRef.current.matrixAutoUpdate = false @@ -118,7 +129,7 @@ const GroupedHouse2 = (props: Props) => { frameHouseMatrix.current.multiply(deltaMatrix) frameOBB.current.copy(obb) - frameOBB.current.applyMatrix4(frameHouseMatrix.current) + frameOBB.current.applyMatrix4(deltaMatrix) const collision = collideOBB(frameOBB.current, [houseId]) if (collision) return @@ -162,71 +173,75 @@ const GroupedHouse2 = (props: Props) => { }) return ( - - - + + + + + + + {pipe( + midColumns, + RA.map((column) => ( + + )) + )} + + + + + + + - - - - {pipe( - midColumns, - RA.map((column) => ( - - )) - )} - - - - - - - - - - {/* + {/* {columnsUp} {columnsDown} */} - {/* */} - + {/* */} + + + {/* */} + ) } diff --git a/app/design/ui-3d/grouped/preview/PreviewHouses.tsx b/app/design/ui-3d/grouped/preview/PreviewHouses.tsx index 07cd8c2f..3d5465a7 100644 --- a/app/design/ui-3d/grouped/preview/PreviewHouses.tsx +++ b/app/design/ui-3d/grouped/preview/PreviewHouses.tsx @@ -21,7 +21,7 @@ const PreviewHouses = (props: Props) => { {pipe( dnaPreviews, R.collect(S.Ord)((k, { value }) => { - console.log({ houseId, k }) + // console.log({ houseId, k }) return ( Date: Wed, 5 Jul 2023 13:27:59 +0100 Subject: [PATCH 026/132] wip builds --- app/build/overview/HousesView.tsx | 7 +- app/build/overview/page.tsx | 4 +- app/db/layouts.ts | 10 +- app/design/app.tsx | 24 ++ app/design/page.tsx | 23 +- app/design/state/houses.ts | 27 -- app/design/state/interactions/layouts.ts | 8 +- app/design/state/interactions/windows.ts | 7 +- app/design/state/layouts.ts | 356 +-------------- app/design/state/transients/stretchLength.tsx | 29 +- app/design/state/vanilla.ts | 10 +- app/design/state/verticalCutPlanes.ts | 2 +- app/design/ui-3d/grouped/GroupedColumn.tsx | 3 +- app/design/ui-3d/grouped/GroupedHouse2.tsx | 12 +- app/design/ui-3d/grouped/GroupedModule.tsx | 6 +- .../ui-3d/grouped/preview/PreviewColumn.tsx | 3 +- .../ui-3d/grouped/preview/PreviewHouse.tsx | 6 +- .../ui-3d/grouped/preview/PreviewModule.tsx | 6 +- .../stretchLength/GroupedStretchColumn.tsx | 2 +- .../stretchLength/GroupedStretchModule.tsx | 2 +- .../grouped/stretchWidth/StretchWidth.tsx | 14 +- .../ui/menu/interactions/AddRemoveLevels.tsx | 2 +- .../ui/menu/interactions/ChangeLayouts.tsx | 2 +- .../ui/menu/interactions/ChangeWindows.tsx | 2 +- app/workers/layouts.ts | 408 +++++++++++++++++- 25 files changed, 509 insertions(+), 466 deletions(-) create mode 100644 app/design/app.tsx diff --git a/app/build/overview/HousesView.tsx b/app/build/overview/HousesView.tsx index ceb854ec..29dc699a 100644 --- a/app/build/overview/HousesView.tsx +++ b/app/build/overview/HousesView.tsx @@ -1,12 +1,7 @@ "use client" import DataInit from "~/data/DataInit" import GroupedApp from "~/design/ui-3d/grouped/GroupedApp" -import React from "react" -import dynamic from "next/dynamic" - -const AppInit = dynamic(() => import("~/design/ui-3d/init/AppInit"), { - ssr: false, -}) +import AppInit from "../../design/ui-3d/init/AppInit" const HousesView = () => { return ( diff --git a/app/build/overview/page.tsx b/app/build/overview/page.tsx index 976b6258..ffb53df2 100644 --- a/app/build/overview/page.tsx +++ b/app/build/overview/page.tsx @@ -1,13 +1,15 @@ "use client" import { ArrowDown } from "@carbon/icons-react" import { pipe } from "fp-ts/lib/function" +import dynamic from "next/dynamic" import { Fragment } from "react" import { A } from "~/utils/functions" import { useAnalyseData } from "../../analyse/state/data" import { useSiteCurrency } from "../../design/state/siteCtx" -import HousesView from "./HousesView" import css from "./page.module.css" +const HousesView = dynamic(() => import("./HousesView"), { ssr: false }) + const OverviewIndex = () => { const { formatWithSymbol } = useSiteCurrency() diff --git a/app/db/layouts.ts b/app/db/layouts.ts index 1335852b..041442ba 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -1,5 +1,5 @@ import Dexie from "dexie" -import { ColumnLayout } from "../design/state/layouts" +import { ColumnLayout, PositionedColumn } from "../workers/layouts" import { LastFetchStamped } from "./systems" export type ParsedModel = { @@ -18,21 +18,29 @@ export type LayoutsKey = { dnas: string[] } +export type VanillaColumn = { + houseId: string + vanillaColumn: Omit +} + export const serializeLayoutsKey = ({ systemId, dnas }: LayoutsKey) => `${systemId}:${dnas}` class LayoutsDatabase extends Dexie { models: Dexie.Table, string> layouts: Dexie.Table + vanillaColumns: Dexie.Table constructor() { super("LayoutsDatabase") this.version(1).stores({ models: "speckleBranchUrl,systemId", layouts: "layoutsKey", + vanillaColumns: "houseId", }) this.layouts = this.table("layouts") this.models = this.table("models") + this.vanillaColumns = this.table("vanillaColumns") } } diff --git a/app/design/app.tsx b/app/design/app.tsx new file mode 100644 index 00000000..9005bf66 --- /dev/null +++ b/app/design/app.tsx @@ -0,0 +1,24 @@ +"use client" +import DataInit from "~/data/DataInit" +import { TrpcProvider } from "../ui/TrpcProvider" +import { getLayoutsWorker, getSystemsWorker } from "../workers" +import { Routing } from "./state/routing" +import GroupedApp from "./ui-3d/grouped/GroupedApp" +import AppInit from "./ui-3d/init/AppInit" + +const App = () => { + getSystemsWorker() + getLayoutsWorker() + return ( + + + + + + + + + ) +} + +export default App diff --git a/app/design/page.tsx b/app/design/page.tsx index 0c4ce5cc..7b4f2d42 100644 --- a/app/design/page.tsx +++ b/app/design/page.tsx @@ -1,24 +1,9 @@ -"use client" -import DataInit from "~/data/DataInit" -import { TrpcProvider } from "../ui/TrpcProvider" -import { getLayoutsWorker, getSystemsWorker } from "../workers" -import { Routing } from "./state/routing" -import GroupedApp from "./ui-3d/grouped/GroupedApp" -import AppInit from "./ui-3d/init/AppInit" +import dynamic from "next/dynamic" + +const App = dynamic(() => import("./app"), { ssr: false }) const IndexPage = () => { - getSystemsWorker() - getLayoutsWorker() - return ( - - - - - - - - - ) + return } export default IndexPage diff --git a/app/design/state/houses.ts b/app/design/state/houses.ts index 8524669d..2b941169 100644 --- a/app/design/state/houses.ts +++ b/app/design/state/houses.ts @@ -130,33 +130,6 @@ export const useGetHouseModules = () => { } } -export const modulesToRows = ( - modules: readonly Module[] -): readonly Module[][] => { - const jumpIndices = pipe( - modules, - RA.filterMapWithIndex((i, m) => - m.structuredDna.positionType === "END" ? some(i) : none - ), - RA.filterWithIndex((i) => i % 2 === 0) - ) - - return pipe( - modules, - RA.reduceWithIndex( - [], - (moduleIndex, modules: Module[][], module: Module) => { - return jumpIndices.includes(moduleIndex) - ? [...modules, [{ ...module, moduleIndex }]] - : produce( - (draft) => - void draft[draft.length - 1].push({ ...module, moduleIndex }) - )(modules) - } - ) - ) -} - export const useHousesSystems = () => { const snap = useSnapshot(houses) as typeof houses return pipe( diff --git a/app/design/state/interactions/layouts.ts b/app/design/state/interactions/layouts.ts index d00acbe1..cc3002f5 100644 --- a/app/design/state/interactions/layouts.ts +++ b/app/design/state/interactions/layouts.ts @@ -11,10 +11,7 @@ import { NEA, O, RA, S, upperFirst } from "~/utils/functions" import houses from "~/design/state/houses" import { useGetVanillaModule } from "~/design/state/vanilla" import { - HouseModuleIdentifier, layouts, - ColumnLayout, - PositionedModule, columnLayoutToDNA, columnLayoutToMatrix, columnMatrixToDna, @@ -23,6 +20,11 @@ import { StairType } from "@/server/data/stairTypes" import { Module } from "@/server/data/modules" import { useSystemStairTypes } from "~/data/stairTypes" import { serializeLayoutsKey } from "../../../db/layouts" +import { + ColumnLayout, + HouseModuleIdentifier, + PositionedModule, +} from "../../../workers/layouts" export const useChangeModuleLayout = ({ houseId, diff --git a/app/design/state/interactions/windows.ts b/app/design/state/interactions/windows.ts index 9ea54ec9..e953cffc 100644 --- a/app/design/state/interactions/windows.ts +++ b/app/design/state/interactions/windows.ts @@ -6,15 +6,12 @@ import { WindowType } from "@/server/data/windowTypes" import { useSystemWindowTypes } from "~/data/windowTypes" import { A, O, S } from "~/utils/functions" import { getSide, Side } from "~/design/state/camera" -import { - columnLayoutToDNA, - HouseModuleIdentifier, - layouts, -} from "~/design/state/layouts" +import { columnLayoutToDNA, layouts } from "~/design/state/layouts" import siteCtx from "~/design/state/siteCtx" import { useChangeModuleLayout } from "./layouts" import { serializeLayoutsKey } from "../../../db/layouts" import houses from "../houses" +import { HouseModuleIdentifier } from "../../../workers/layouts" export type WindowTypeOption = { label: string diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 6d4e454d..32e9b84e 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -5,7 +5,6 @@ import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" import * as A from "fp-ts/Array" import { pipe } from "fp-ts/lib/function" import * as RA from "fp-ts/ReadonlyArray" -import produce from "immer" import { suspend } from "suspend-react" import { proxy, ref, useSnapshot } from "valtio" import { usePadColumn } from "../../data/modules" @@ -13,61 +12,12 @@ import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../../db/layouts" import { isSSR } from "../../utils/next" import { getLayoutsWorker } from "../../workers" import { - modulesToRows, - useDnasModules, - useHouse, - useHouseModules, -} from "./houses" - -export type PositionedModule = { - module: Module - z: number - gridGroupIndex: number -} - -export type PositionedInstancedModule = { - module: Module - y: number - z: number - columnIndex: number - levelIndex: number - gridGroupIndex: number -} - -export type PositionedRow = { - levelIndex: number - levelType: string - y: number - modules: Array - length: number -} - -export type GridGroup = PositionedRow - -export type RowLayout = Array - -export type PositionedColumn = { - gridGroups: Array - z: number - columnIndex: number - length: number -} - -export type ModuleIdentifier = { - columnIndex: number - levelIndex: number - gridGroupIndex: number -} - -export type HouseModuleIdentifier = ModuleIdentifier & { - houseId: string -} - -export type SystemHouseModuleIdentifier = HouseModuleIdentifier & { - systemId: string -} - -export type ColumnLayout = Array + ColumnLayout, + HouseModuleIdentifier, + PositionedColumn, + RowLayout, +} from "../../workers/layouts" +import { useHouse } from "./houses" export const layouts = proxy< Record // systemId:dnas : Layout @@ -83,288 +33,6 @@ if (!isSSR()) { }) } -export const useRowLayout = (houseId: string): RowLayout => - pipe( - useHouseModules(houseId), - modulesToRows, - RA.map((row) => - pipe( - row, - RA.reduceWithIndex( - [], - (i, prevs: Array, module: Module) => { - const isFirst: boolean = i === 0 - - const z = isFirst - ? module.length / 2 - : prevs[i - 1].z + - prevs[i - 1].module.length / 2 + - module.length / 2 - - return [ - ...prevs, - { - module, - z, - gridGroupIndex: i, - }, - ] - } - ) - ) - ), - RA.reduceWithIndex( - [], - ( - i, - b: { - modules: Array - y: number - levelType: string - levelIndex: number - length: number - }[], - row - ) => { - const levelType = row[0].module.structuredDna.levelType - const levelLetter = levelType[0] - const y = levelLetter === "F" ? 0 : b[i - 1].y + row[0].module.height - - return [ - ...b, - { - modules: row, - y, - levelIndex: i, - levelType: row[0].module.structuredDna.levelType, - length: row.reduce((acc, m) => acc + m.module.length, 0), - }, - ] - } - ) - ) - -const analyzeColumn = - (toLength: (a: A) => number) => - (as: readonly A[][]) => { - return pipe( - as, - RA.reduceWithIndex( - { legit: true, target: -1, rows: [] }, - ( - index, - { - rows, - legit, - target, - }: { - rows: { units: number; index: number }[] - legit: boolean - target: number - }, - row: A[] - ) => { - const units = row.reduce((acc, a) => acc + toLength(a), 0) - return { - rows: [...rows, { units, index }], - legit: legit && (target === -1 || target === units), - target: target === -1 ? units : Math.max(target, units), - } - } - ) - ) - } - -const columnify = - (toLength: (a: A) => number) => - (input: readonly A[][]) => { - let slices = new Array<[number, number]>(input.length).fill([0, 1]) - const lengths = input.map((v) => v.length) - - let acc: (readonly A[][])[] = [] - - const slicesRemaining = () => - !pipe( - RA.zip(slices)(lengths), - RA.reduce(true, (acc, [length, [start]]) => acc && start > length - 1) - ) - - while (slicesRemaining()) { - pipe( - slices, - RA.mapWithIndex((rowIndex, [start, end]) => - input[rowIndex].slice(start, end) - ), - (column) => - pipe(column, analyzeColumn(toLength), ({ rows, legit, target }) => { - if (legit) { - acc = [...acc, column] - slices = slices.map(([, end]) => [end, end + 1]) - } else { - slices = slices.map(([start, end], i) => - rows[i].units === target ? [start, end] : [start, end + 1] - ) - } - }) - ) - } - - return pipe(acc, transposeRA) - } - -export const modulesToColumnLayout = (modules: Module[]) => { - const columns = pipe( - modules, - modulesToRows, - RA.map((row) => - pipe( - row, - // group by grid type - RA.reduce( - { prev: null, acc: [] }, - ( - { prev, acc }: { prev: Module | null; acc: Module[][] }, - module - ) => ({ - acc: - module.structuredDna.positionType === - prev?.structuredDna.positionType && - module.structuredDna.gridType === prev?.structuredDna.gridType - ? produce(acc, (draft) => { - draft[draft.length - 1].push(module) - }) - : produce(acc, (draft) => { - draft[draft.length] = [module] - }), - prev: module, - }) - ), - ({ acc }) => acc - ) - ), - transposeRA - ) - - const sameLengthColumns = pipe( - columns, - RA.map((column) => - pipe( - column, - RA.map((module) => - pipe( - module, - RA.reduce(0, (b, v) => b + v.structuredDna.gridUnits) - ) - ), - RA.reduce( - { acc: true, prev: null }, - ({ prev }: { prev: number | null }, a: number) => ({ - acc: prev === null || prev === a, - prev: a as number | null, - }) - ), - ({ acc }) => acc - ) - ), - RA.reduce(true, (b, a) => b && a) - ) - - if (!sameLengthColumns) throw new Error("not sameLengthColumns") - - const columnifiedFurther = pipe( - columns, - RA.map((column) => - pipe( - column, - columnify((a) => a.structuredDna.gridUnits), - transposeRA - ) - ), - RA.flatten - ) - - return pipe( - columnifiedFurther, - RA.reduceWithIndex( - [], - (columnIndex, positionedCols: PositionedColumn[], loadedModules) => { - const last = - columnIndex === 0 ? null : positionedCols[positionedCols.length - 1] - const z = !last - ? 0 - : last.z + - last.gridGroups[0].modules.reduce( - (modulesLength, module) => modulesLength + module.module.length, - 0 - ) - - const gridGroups = pipe( - loadedModules, - RA.reduceWithIndex( - [], - (levelIndex, positionedRows: PositionedRow[], modules) => { - const levelType = modules[0].structuredDna.levelType - const levelLetter = levelType[0] - const y = - levelLetter === "F" - ? 0 - : positionedRows[levelIndex - 1].y + - positionedRows[levelIndex - 1].modules[0].module.height - - return [ - ...positionedRows, - { - modules: pipe( - modules, - RA.reduceWithIndex( - [], - ( - i, - positionedModules: PositionedModule[], - module: Module - ) => { - const isFirst: boolean = i === 0 - - const z = isFirst - ? module.length / 2 - : positionedModules[i - 1].z + - positionedModules[i - 1].module.length / 2 + - module.length / 2 - - return [ - ...positionedModules, - { - module, - gridGroupIndex: i, - z, - }, - ] - } - ) - ), - levelIndex, - levelType, - y, - length: modules.reduce((acc, m) => acc + m.length, 0), - }, - ] - } - ) - ) - return [ - ...positionedCols, - { - columnIndex, - gridGroups, - z, - length: gridGroups[0].length, - }, - ] - } - ) - ) -} - export const useDnasLayout = (layoutsKey: LayoutsKey): ColumnLayout => { const snap = useSnapshot(layouts) as typeof layouts const serialKey = serializeLayoutsKey(layoutsKey) @@ -385,12 +53,6 @@ export const useDnasLayout = (layoutsKey: LayoutsKey): ColumnLayout => { }, [maybeLayout]) } -export const useDnasColumnLayout = (systemId: string, dnas: string[]) => { - const houseModules = useDnasModules(systemId, dnas) - const columnLayout = modulesToColumnLayout(houseModules) - return columnLayout -} - export const columnLayoutToDNA = ( columnLayout: Omit[] ) => @@ -469,12 +131,6 @@ export const usePadColumnMatrix = (systemId: string) => { return (columnMatrix: Module[][][]) => pipe(columnMatrix, A.map(padColumn)) } -type SystemHouseLayouts = Map - -export const useHouseLayouts = (): SystemHouseLayouts => { - return undefined as any -} - export const indicesToKey = ({ houseId, columnIndex, diff --git a/app/design/state/transients/stretchLength.tsx b/app/design/state/transients/stretchLength.tsx index 78be111d..4e911222 100644 --- a/app/design/state/transients/stretchLength.tsx +++ b/app/design/state/transients/stretchLength.tsx @@ -15,18 +15,19 @@ import dimensions, { usePostTransMatrix, } from "~/design/state/dimensions" import houses, { useHouse } from "~/design/state/houses" -import { - ColumnLayout, - columnLayoutToDNA, - layouts, - PositionedRow, -} from "~/design/state/layouts" import { getVanillaColumnLength, useGetVanillaModule, vanillaColumns, } from "../vanilla" -import { serializeLayoutsKey } from "../../../db/layouts" +import layoutsDB, { serializeLayoutsKey } from "../../../db/layouts" +import { suspend } from "suspend-react" +import { + ColumnLayout, + PositionedRow, + splitColumns, +} from "../../../workers/layouts" +import { columnLayoutToDNA, layouts } from "../layouts" export type StretchLength = { direction: 1 | -1 @@ -38,20 +39,6 @@ export type StretchLength = { export const stretchLengthRaw = proxy>({}) export const stretchLengthClamped = proxy>({}) -export const splitColumns = (layout: ColumnLayout) => - pipe( - layout, - RA.partition( - ({ columnIndex }) => - columnIndex === 0 || columnIndex === layout.length - 1 - ), - ({ left: midColumns, right: [startColumn, endColumn] }) => ({ - startColumn, - endColumn, - midColumns, - }) - ) - export const useStretchLength = ({ houseId, layout, diff --git a/app/design/state/vanilla.ts b/app/design/state/vanilla.ts index 1680d078..ace4a0ea 100644 --- a/app/design/state/vanilla.ts +++ b/app/design/state/vanilla.ts @@ -3,7 +3,9 @@ import { proxy } from "valtio" import { useSystemModules } from "../../data/modules" import { Module } from "@/server/data/modules" import { A, all, O, Ord, RA, S, someOrError } from "~/utils/functions" -import { PositionedRow } from "./layouts" +import { suspend } from "suspend-react" +import layoutsDB from "../../db/layouts" +import { PositionedRow } from "../../workers/layouts" export const vanillaColumns = proxy>({}) @@ -72,3 +74,9 @@ export const useGetVanillaModule = (systemId: string) => { return vanillaModule } } + +export const useVanillaColumn = (houseId: string) => { + return suspend(() => { + return layoutsDB.vanillaColumns.get(houseId) + }, [houseId]) +} diff --git a/app/design/state/verticalCutPlanes.ts b/app/design/state/verticalCutPlanes.ts index d1106b86..5bbc2217 100644 --- a/app/design/state/verticalCutPlanes.ts +++ b/app/design/state/verticalCutPlanes.ts @@ -6,8 +6,8 @@ import { Matrix4, Plane, Vector3 } from "three" import { useSnapshot } from "valtio" import { R } from "~/utils/functions" import houses from "~/design/state/houses" -import { ColumnLayout } from "~/design/state/layouts" import { useVerticalCuts } from "~/design/state/settings" +import { ColumnLayout } from "../../workers/layouts" export const useVerticalCutPlanes = ( columnLayout: ColumnLayout, diff --git a/app/design/ui-3d/grouped/GroupedColumn.tsx b/app/design/ui-3d/grouped/GroupedColumn.tsx index 3948cddc..ecda5584 100644 --- a/app/design/ui-3d/grouped/GroupedColumn.tsx +++ b/app/design/ui-3d/grouped/GroupedColumn.tsx @@ -1,4 +1,4 @@ -import { indicesToKey, PositionedColumn } from "~/design/state/layouts" +import { indicesToKey } from "~/design/state/layouts" import { GroupProps } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" import { forwardRef, useRef } from "react" @@ -7,6 +7,7 @@ import { Group } from "three" import { useStretchLengthStartEndColumn } from "~/design/state/transients/stretchLength" import { RA } from "~/utils/functions" import GroupedModule from "./GroupedModule" +import { PositionedColumn } from "../../../workers/layouts" type Props = GroupProps & { systemId: string diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index a0058189..2a4f4ee7 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -6,6 +6,7 @@ import { OBB } from "three-stdlib" import { serializeLayoutsKey } from "../../../db/layouts" import { House } from "../../../db/user" import { RA } from "../../../utils/functions" +import { splitColumns } from "../../../workers/layouts" import dimensions, { collideOBB, Dimensions } from "../../state/dimensions" import { dispatchMoveHouse, @@ -16,7 +17,7 @@ import { useHouseMaterialOps } from "../../state/hashedMaterials" import houses from "../../state/houses" import { useDnasLayout } from "../../state/layouts" import { useTransformabilityBooleans } from "../../state/siteCtx" -import { splitColumns } from "../../state/transients/stretchLength" +import { useVanillaColumn } from "../../state/vanilla" import RotateHandles from "../handles/RotateHandles" import StretchHandle from "../handles/StretchHandle" import GroupedColumn from "./GroupedColumn" @@ -157,6 +158,8 @@ const GroupedHouse2 = (props: Props) => { const { startColumn, endColumn, midColumns } = splitColumns(layout) + const vanillaColumn = useVanillaColumn(houseId) + useHouseMaterialOps({ houseId, ref: rootRef, @@ -234,9 +237,10 @@ const GroupedHouse2 = (props: Props) => { /> {/* - {columnsUp} - {columnsDown} - */} + {columnsUp} + {columnsDown} + */} + {/* */} diff --git a/app/design/ui-3d/grouped/GroupedModule.tsx b/app/design/ui-3d/grouped/GroupedModule.tsx index 95da0a92..a2633c2d 100644 --- a/app/design/ui-3d/grouped/GroupedModule.tsx +++ b/app/design/ui-3d/grouped/GroupedModule.tsx @@ -1,11 +1,9 @@ import { Module } from "@/server/data/modules" import { pipe } from "fp-ts/lib/function" import { useModuleElements } from "~/data/elements" -import { - indicesToKey, - SystemHouseModuleIdentifier, -} from "~/design/state/layouts" +import { indicesToKey } from "~/design/state/layouts" import { R, S } from "~/utils/functions" +import { SystemHouseModuleIdentifier } from "../../../workers/layouts" import GroupedElement from "./GroupedElement" export type ModuleProps = SystemHouseModuleIdentifier & { diff --git a/app/design/ui-3d/grouped/preview/PreviewColumn.tsx b/app/design/ui-3d/grouped/preview/PreviewColumn.tsx index de7ef81d..ae827290 100644 --- a/app/design/ui-3d/grouped/preview/PreviewColumn.tsx +++ b/app/design/ui-3d/grouped/preview/PreviewColumn.tsx @@ -1,4 +1,4 @@ -import { indicesToKey, PositionedColumn } from "~/design/state/layouts" +import { indicesToKey } from "~/design/state/layouts" import { RA } from "~/utils/functions" import { GroupProps } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" @@ -6,6 +6,7 @@ import { forwardRef, useRef } from "react" import mergeRefs from "react-merge-refs" import { Group } from "three" import PreviewModule from "./PreviewModule" +import { PositionedColumn } from "../../../../workers/layouts" type Props = GroupProps & { systemId: string diff --git a/app/design/ui-3d/grouped/preview/PreviewHouse.tsx b/app/design/ui-3d/grouped/preview/PreviewHouse.tsx index 117a2b3e..4ee12c80 100644 --- a/app/design/ui-3d/grouped/preview/PreviewHouse.tsx +++ b/app/design/ui-3d/grouped/preview/PreviewHouse.tsx @@ -1,10 +1,10 @@ -import { useDnasColumnLayout } from "~/design/state/layouts" -import { A } from "~/utils/functions" import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Group } from "three" +import { useDnasLayout } from "~/design/state/layouts" import previews from "~/design/state/previews" +import { A } from "~/utils/functions" import { useSubscribeKey } from "~/utils/hooks" import PreviewColumn from "./PreviewColumn" @@ -20,7 +20,7 @@ const PreviewHouse = (props: Props) => { const { houseId, systemId, dnas, setHouseVisible, ...restProps } = props - const layout = useDnasColumnLayout(systemId, dnas) + const layout = useDnasLayout({ systemId, dnas }) const key = dnas.toString() diff --git a/app/design/ui-3d/grouped/preview/PreviewModule.tsx b/app/design/ui-3d/grouped/preview/PreviewModule.tsx index 7c9cab53..22c164c8 100644 --- a/app/design/ui-3d/grouped/preview/PreviewModule.tsx +++ b/app/design/ui-3d/grouped/preview/PreviewModule.tsx @@ -1,11 +1,9 @@ import { Module } from "@/server/data/modules" import { pipe } from "fp-ts/lib/function" import { useModuleElements } from "~/data/elements" -import { - indicesToKey, - SystemHouseModuleIdentifier, -} from "~/design/state/layouts" +import { indicesToKey } from "~/design/state/layouts" import { R, S } from "~/utils/functions" +import { SystemHouseModuleIdentifier } from "../../../../workers/layouts" import PreviewElement from "./PreviewElement" export type PreviewModuleProps = SystemHouseModuleIdentifier & { diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx index 76e56e05..40a677c3 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx @@ -1,4 +1,3 @@ -import { GridGroup } from "~/design/state/layouts" import { RA } from "~/utils/functions" import { pipe } from "fp-ts/lib/function" import { useRef } from "react" @@ -6,6 +5,7 @@ import { Group } from "three" import { stretchLengthClamped } from "~/design/state/transients/stretchLength" import { useSubscribeKey } from "~/utils/hooks" import GroupedStretchModule from "./GroupedStretchModule" +import { GridGroup } from "../../../../workers/layouts" type Props = { systemId: string diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx index a9e00a53..29052c71 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx @@ -2,8 +2,8 @@ import { Module } from "@/server/data/modules" import { pipe } from "fp-ts/lib/function" import { Fragment } from "react" import { useModuleElements } from "~/data/elements" -import { SystemHouseModuleIdentifier } from "~/design/state/layouts" import { R, S } from "~/utils/functions" +import { SystemHouseModuleIdentifier } from "../../../../workers/layouts" import GroupedStretchElement from "./GroupedStretchElement" export type StretchModuleProps = Omit< diff --git a/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx b/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx index a9510382..8ea35eb5 100644 --- a/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx +++ b/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx @@ -9,13 +9,7 @@ import { useSystemSectionTypes } from "~/data/sectionTypes" import dimensions, { useHouseDimensionsUpdates, } from "../../../state/dimensions" -import { - ColumnLayout, - columnLayoutToDNA, - GridGroup, - PositionedColumn, - PositionedModule, -} from "~/design/state/layouts" +import { columnLayoutToDNA } from "~/design/state/layouts" import { useGetVanillaModule } from "~/design/state/vanilla" import { A, @@ -39,6 +33,12 @@ import houses, { useHouse } from "../../../state/houses" import previews from "~/design/state/previews" import StretchHandle from "../../handles/StretchHandle" import { useTransformabilityBooleans } from "../../../state/siteCtx" +import { + ColumnLayout, + GridGroup, + PositionedColumn, + PositionedModule, +} from "../../../../workers/layouts" export type StretchWidthRaw = { direction: 1 | -1 diff --git a/app/design/ui/menu/interactions/AddRemoveLevels.tsx b/app/design/ui/menu/interactions/AddRemoveLevels.tsx index af3c10a8..0ee0b5b6 100644 --- a/app/design/ui/menu/interactions/AddRemoveLevels.tsx +++ b/app/design/ui/menu/interactions/AddRemoveLevels.tsx @@ -6,7 +6,6 @@ import { useGetStairsModule, usePadColumn } from "~/data/modules" import houses from "~/design/state/houses" import { columnMatrixToDna, - HouseModuleIdentifier, rowMatrixToDna, useColumnMatrix, } from "~/design/state/layouts" @@ -15,6 +14,7 @@ import { useGetVanillaModule } from "~/design/state/vanilla" import { A, errorThrower, O } from "~/utils/functions" import { AddLevel, RemoveLevel } from "~/ui/icons" import ContextMenuButton from "../ContextMenuButton" +import { HouseModuleIdentifier } from "../../../../workers/layouts" type Props = HouseModuleIdentifier & { onComplete?: () => void diff --git a/app/design/ui/menu/interactions/ChangeLayouts.tsx b/app/design/ui/menu/interactions/ChangeLayouts.tsx index 92570ca5..c4e48a62 100644 --- a/app/design/ui/menu/interactions/ChangeLayouts.tsx +++ b/app/design/ui/menu/interactions/ChangeLayouts.tsx @@ -4,11 +4,11 @@ import { useLayoutOptions, useStairsOptions, } from "~/design/state/interactions/layouts" -import { HouseModuleIdentifier } from "~/design/state/layouts" import { Menu, Pencil } from "~/ui/icons" import Radio from "~/ui//Radio" import ContextMenuNested from "../ContextMenuNested" import houses from "../../../state/houses" +import { HouseModuleIdentifier } from "../../../../workers/layouts" type Props = HouseModuleIdentifier & { onComplete?: () => void diff --git a/app/design/ui/menu/interactions/ChangeWindows.tsx b/app/design/ui/menu/interactions/ChangeWindows.tsx index 8212bd47..e1e53b36 100644 --- a/app/design/ui/menu/interactions/ChangeWindows.tsx +++ b/app/design/ui/menu/interactions/ChangeWindows.tsx @@ -6,12 +6,12 @@ import { useWindowOptions, WindowTypeOption, } from "~/design/state/interactions/windows" -import { HouseModuleIdentifier } from "~/design/state/layouts" import previews from "~/design/state/previews" import { A } from "~/utils/functions" import { Opening } from "~/ui/icons" import Radio from "~/ui//Radio" import ContextMenuNested from "../ContextMenuNested" +import { HouseModuleIdentifier } from "../../../../workers/layouts" type Props = HouseModuleIdentifier & { onComplete?: () => void diff --git a/app/workers/layouts.ts b/app/workers/layouts.ts index 28aa2413..71153341 100644 --- a/app/workers/layouts.ts +++ b/app/workers/layouts.ts @@ -1,6 +1,10 @@ import { expose } from "comlink" import { liveQuery } from "dexie" +import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" +import { sequenceS } from "fp-ts/lib/Apply" +import { sequence } from "fp-ts/lib/Array" import { pipe } from "fp-ts/lib/function" +import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" import { BufferGeometry } from "three" import { mergeBufferGeometries } from "three-stdlib" @@ -8,11 +12,60 @@ import { Module } from "../../server/data/modules" import { getSpeckleObject } from "../../server/data/speckleModel" import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../db/layouts" import systemsDB, { LastFetchStamped } from "../db/systems" -import { modulesToColumnLayout } from "../design/state/layouts" -import { A, R } from "../utils/functions" +import userDB from "../db/user" +import { A, all, O, Ord, R, S } from "../utils/functions" import { isSSR } from "../utils/next" import speckleIfcParser from "../utils/speckle/speckleIfcParser" +export type PositionedModule = { + module: Module + z: number + gridGroupIndex: number +} + +export type PositionedInstancedModule = { + module: Module + y: number + z: number + columnIndex: number + levelIndex: number + gridGroupIndex: number +} + +export type PositionedRow = { + levelIndex: number + levelType: string + y: number + modules: Array + length: number +} + +export type GridGroup = PositionedRow + +export type RowLayout = Array + +export type PositionedColumn = { + gridGroups: Array + z: number + columnIndex: number + length: number +} + +export type ModuleIdentifier = { + columnIndex: number + levelIndex: number + gridGroupIndex: number +} + +export type HouseModuleIdentifier = ModuleIdentifier & { + houseId: string +} + +export type SystemHouseModuleIdentifier = HouseModuleIdentifier & { + systemId: string +} + +export type ColumnLayout = Array const syncModels = (modules: LastFetchStamped[]) => { modules.map(async (nextModule) => { const { speckleBranchUrl, lastFetched } = nextModule @@ -54,6 +107,311 @@ const syncModels = (modules: LastFetchStamped[]) => { let modulesCache: LastFetchStamped[] = [] let layoutsQueue: LayoutsKey[] = [] +const modulesToRows = (modules: Module[]): Module[][] => { + const jumpIndices = pipe( + modules, + RA.filterMapWithIndex((i, m) => + m.structuredDna.positionType === "END" ? O.some(i) : O.none + ), + RA.filterWithIndex((i) => i % 2 === 0) + ) + + return pipe( + modules, + RA.reduceWithIndex( + [], + (moduleIndex, modules: Module[][], module: Module) => { + return jumpIndices.includes(moduleIndex) + ? [...modules, [{ ...module, moduleIndex }]] + : produce( + (draft) => + void draft[draft.length - 1].push({ ...module, moduleIndex }) + )(modules) + } + ) + ) +} + +const analyzeColumn = + (toLength: (a: A) => number) => + (as: readonly A[][]) => { + return pipe( + as, + RA.reduceWithIndex( + { legit: true, target: -1, rows: [] }, + ( + index, + { + rows, + legit, + target, + }: { + rows: { units: number; index: number }[] + legit: boolean + target: number + }, + row: A[] + ) => { + const units = row.reduce((acc, a) => acc + toLength(a), 0) + return { + rows: [...rows, { units, index }], + legit: legit && (target === -1 || target === units), + target: target === -1 ? units : Math.max(target, units), + } + } + ) + ) + } +const columnify = + (toLength: (a: A) => number) => + (input: readonly A[][]) => { + let slices = new Array<[number, number]>(input.length).fill([0, 1]) + const lengths = input.map((v) => v.length) + + let acc: (readonly A[][])[] = [] + + const slicesRemaining = () => + !pipe( + RA.zip(slices)(lengths), + RA.reduce(true, (acc, [length, [start]]) => acc && start > length - 1) + ) + + while (slicesRemaining()) { + pipe( + slices, + RA.mapWithIndex((rowIndex, [start, end]) => + input[rowIndex].slice(start, end) + ), + (column) => + pipe(column, analyzeColumn(toLength), ({ rows, legit, target }) => { + if (legit) { + acc = [...acc, column] + slices = slices.map(([, end]) => [end, end + 1]) + } else { + slices = slices.map(([start, end], i) => + rows[i].units === target ? [start, end] : [start, end + 1] + ) + } + }) + ) + } + + return pipe(acc, transposeRA) + } + +const modulesToColumnLayout = (modules: Module[]) => { + const columns = pipe( + modules, + modulesToRows, + RA.map((row) => + pipe( + row, + // group by grid type + RA.reduce( + { prev: null, acc: [] }, + ( + { prev, acc }: { prev: Module | null; acc: Module[][] }, + module + ) => ({ + acc: + module.structuredDna.positionType === + prev?.structuredDna.positionType && + module.structuredDna.gridType === prev?.structuredDna.gridType + ? produce(acc, (draft) => { + draft[draft.length - 1].push(module) + }) + : produce(acc, (draft) => { + draft[draft.length] = [module] + }), + prev: module, + }) + ), + ({ acc }) => acc + ) + ), + transposeRA + ) + + const sameLengthColumns = pipe( + columns, + RA.map((column) => + pipe( + column, + RA.map((module) => + pipe( + module, + RA.reduce(0, (b, v) => b + v.structuredDna.gridUnits) + ) + ), + RA.reduce( + { acc: true, prev: null }, + ({ prev }: { prev: number | null }, a: number) => ({ + acc: prev === null || prev === a, + prev: a as number | null, + }) + ), + ({ acc }) => acc + ) + ), + RA.reduce(true, (b, a) => b && a) + ) + + if (!sameLengthColumns) throw new Error("not sameLengthColumns") + + const columnifiedFurther = pipe( + columns, + RA.map((column) => + pipe( + column, + columnify((a) => a.structuredDna.gridUnits), + transposeRA + ) + ), + RA.flatten + ) + + return pipe( + columnifiedFurther, + RA.reduceWithIndex( + [], + (columnIndex, positionedCols: PositionedColumn[], loadedModules) => { + const last = + columnIndex === 0 ? null : positionedCols[positionedCols.length - 1] + const z = !last + ? 0 + : last.z + + last.gridGroups[0].modules.reduce( + (modulesLength, module) => modulesLength + module.module.length, + 0 + ) + + const gridGroups = pipe( + loadedModules, + RA.reduceWithIndex( + [], + (levelIndex, positionedRows: PositionedRow[], modules) => { + const levelType = modules[0].structuredDna.levelType + const levelLetter = levelType[0] + const y = + levelLetter === "F" + ? 0 + : positionedRows[levelIndex - 1].y + + positionedRows[levelIndex - 1].modules[0].module.height + + return [ + ...positionedRows, + { + modules: pipe( + modules, + RA.reduceWithIndex( + [], + ( + i, + positionedModules: PositionedModule[], + module: Module + ) => { + const isFirst: boolean = i === 0 + + const z = isFirst + ? module.length / 2 + : positionedModules[i - 1].z + + positionedModules[i - 1].module.length / 2 + + module.length / 2 + + return [ + ...positionedModules, + { + module, + gridGroupIndex: i, + z, + }, + ] + } + ) + ), + levelIndex, + levelType, + y, + length: modules.reduce((acc, m) => acc + m.length, 0), + }, + ] + } + ) + ) + return [ + ...positionedCols, + { + columnIndex, + gridGroups, + z, + length: gridGroups[0].length, + }, + ] + } + ) + ) +} + +export const splitColumns = (layout: ColumnLayout) => + pipe( + layout, + RA.partition( + ({ columnIndex }) => + columnIndex === 0 || columnIndex === layout.length - 1 + ), + ({ left: midColumns, right: [startColumn, endColumn] }) => ({ + startColumn, + endColumn, + midColumns, + }) + ) + +const getVanillaModule = ( + module: Module, + opts: { + positionType?: string + levelType?: string + constrainGridType?: boolean + sectionType?: string + } = {} +): O.Option => { + const { + sectionType, + positionType, + levelType, + constrainGridType = true, + } = opts + + return pipe( + modulesCache, + A.filter((sysModule) => + all( + sectionType + ? sysModule.structuredDna.sectionType === sectionType + : sysModule.structuredDna.sectionType === + module.structuredDna.sectionType, + positionType + ? sysModule.structuredDna.positionType === positionType + : sysModule.structuredDna.positionType === + module.structuredDna.positionType, + levelType + ? sysModule.structuredDna.levelType === levelType + : sysModule.structuredDna.levelType === + module.structuredDna.levelType, + !constrainGridType || + sysModule.structuredDna.gridType === module.structuredDna.gridType + ) + ), + A.sort( + pipe( + S.Ord, + Ord.contramap((m: Module) => m.dna) + ) + ), + A.head + ) +} + const processLayout = async ({ systemId, dnas }: LayoutsKey) => { const modules = pipe( dnas, @@ -77,6 +435,44 @@ const processLayout = async ({ systemId, dnas }: LayoutsKey) => { layoutsKey, }) + const { + startColumn: { gridGroups }, + } = splitColumns(layout) + + pipe( + gridGroups, + A.traverse(O.Applicative)( + ({ + levelIndex, + levelType, + y, + modules: [{ module }], + }): O.Option => + pipe( + getVanillaModule(module, { + constrainGridType: false, + positionType: "MID", + }), + O.map((vanillaModule) => ({ + modules: [ + { + module: vanillaModule, + gridGroupIndex: 0, + z: 0, + }, + ], + length: vanillaModule.length, + y, + levelIndex, + levelType, + })) + ) + ), + O.map((vanillaColumn) => { + // layoutsDB.vanillaColumns.put + }) + ) + return layout } @@ -117,6 +513,14 @@ if (!isSSR()) { }) } +// liveQuery(() => userDB.houses.toArray()).subscribe(houses => { +// pipe(houses, A.map(house => { +// const layout = +// const vanillaColumn = getVanillaColumn(house.) +// })) +// // layoutsDB.vanillaColumns +// }) + const api = { postLayout, postLayouts, From 33e1ec9317895b8ce8f0836c917fb8e442f334f9 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 5 Jul 2023 21:17:09 +0100 Subject: [PATCH 027/132] wip put vanilla columns in db --- app/db/layouts.ts | 8 ++++---- app/workers/layouts.ts | 21 ++++++++------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/app/db/layouts.ts b/app/db/layouts.ts index 041442ba..98dcc046 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -18,8 +18,8 @@ export type LayoutsKey = { dnas: string[] } -export type VanillaColumn = { - houseId: string +export type IndexedVanillaColumn = { + layoutsKey: string vanillaColumn: Omit } @@ -29,14 +29,14 @@ export const serializeLayoutsKey = ({ systemId, dnas }: LayoutsKey) => class LayoutsDatabase extends Dexie { models: Dexie.Table, string> layouts: Dexie.Table - vanillaColumns: Dexie.Table + vanillaColumns: Dexie.Table constructor() { super("LayoutsDatabase") this.version(1).stores({ models: "speckleBranchUrl,systemId", layouts: "layoutsKey", - vanillaColumns: "houseId", + vanillaColumns: "layoutsKey", }) this.layouts = this.table("layouts") this.models = this.table("models") diff --git a/app/workers/layouts.ts b/app/workers/layouts.ts index 71153341..ebb1ed98 100644 --- a/app/workers/layouts.ts +++ b/app/workers/layouts.ts @@ -1,8 +1,6 @@ import { expose } from "comlink" import { liveQuery } from "dexie" import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" -import { sequenceS } from "fp-ts/lib/Apply" -import { sequence } from "fp-ts/lib/Array" import { pipe } from "fp-ts/lib/function" import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" @@ -12,7 +10,6 @@ import { Module } from "../../server/data/modules" import { getSpeckleObject } from "../../server/data/speckleModel" import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../db/layouts" import systemsDB, { LastFetchStamped } from "../db/systems" -import userDB from "../db/user" import { A, all, O, Ord, R, S } from "../utils/functions" import { isSSR } from "../utils/next" import speckleIfcParser from "../utils/speckle/speckleIfcParser" @@ -468,8 +465,14 @@ const processLayout = async ({ systemId, dnas }: LayoutsKey) => { })) ) ), - O.map((vanillaColumn) => { - // layoutsDB.vanillaColumns.put + O.map((gridGroups) => { + layoutsDB.vanillaColumns.put({ + layoutsKey, + vanillaColumn: { + gridGroups, + length: gridGroups[0].length, + }, + }) }) ) @@ -513,14 +516,6 @@ if (!isSSR()) { }) } -// liveQuery(() => userDB.houses.toArray()).subscribe(houses => { -// pipe(houses, A.map(house => { -// const layout = -// const vanillaColumn = getVanillaColumn(house.) -// })) -// // layoutsDB.vanillaColumns -// }) - const api = { postLayout, postLayouts, From 391a845943fb6fe1e7107102e939bf204d58ae36 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 6 Jul 2023 09:37:05 +0100 Subject: [PATCH 028/132] wip refactor stretch length 1 --- app/db/layouts.ts | 8 +- app/design/state/dimensions.tsx | 4 +- app/design/state/hashedMaterials.ts | 2 +- app/design/state/interactions/layouts.ts | 8 +- .../state/interactions/stretchLength.tsx | 185 ++++++++++++++++++ app/design/state/interactions/windows.ts | 4 +- app/design/state/layouts.ts | 8 +- app/design/state/scope.ts | 6 +- app/design/state/transients/stretchLength.tsx | 7 +- app/design/state/vanilla.ts | 31 +-- app/design/ui-3d/grouped/GroupedHouse.tsx | 4 +- app/design/ui-3d/grouped/GroupedHouse2.tsx | 45 +++-- app/workers/layouts.ts | 12 +- 13 files changed, 266 insertions(+), 58 deletions(-) create mode 100644 app/design/state/interactions/stretchLength.tsx diff --git a/app/db/layouts.ts b/app/db/layouts.ts index 98dcc046..6e06d9cc 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -13,17 +13,19 @@ export type IndexedLayout = { layout: ColumnLayout } -export type LayoutsKey = { +export type LayoutKey = { systemId: string dnas: string[] } +export type VanillaColumn = Omit + export type IndexedVanillaColumn = { layoutsKey: string - vanillaColumn: Omit + vanillaColumn: VanillaColumn } -export const serializeLayoutsKey = ({ systemId, dnas }: LayoutsKey) => +export const serializeLayoutKey = ({ systemId, dnas }: LayoutKey) => `${systemId}:${dnas}` class LayoutsDatabase extends Dexie { diff --git a/app/design/state/dimensions.tsx b/app/design/state/dimensions.tsx index 0e472f77..58efb0c1 100644 --- a/app/design/state/dimensions.tsx +++ b/app/design/state/dimensions.tsx @@ -5,7 +5,7 @@ import { OBB } from "three-stdlib" import { proxy, ref, useSnapshot } from "valtio" import { useSubscribeKey } from "~/utils/hooks" import { yAxis } from "~/utils/three" -import { serializeLayoutsKey } from "../../db/layouts" +import { serializeLayoutKey } from "../../db/layouts" import houses, { useHouse } from "./houses" import { layouts } from "./layouts" import { postTransformsTransients, Transforms } from "./transients/transforms" @@ -72,7 +72,7 @@ export const usePostTransMatrix = (houseId: string) => { export const useLayoutsKey = (houseId: string) => { const { systemId, dnas } = useHouse(houseId) - return serializeLayoutsKey({ systemId, dnas }) + return serializeLayoutKey({ systemId, dnas }) } export const useComputeDimensions = (houseId: string) => { diff --git a/app/design/state/hashedMaterials.ts b/app/design/state/hashedMaterials.ts index c51250be..94e3a929 100644 --- a/app/design/state/hashedMaterials.ts +++ b/app/design/state/hashedMaterials.ts @@ -15,7 +15,7 @@ import { useHousePreviews } from "./previews" import settings from "./settings" import siteCtx from "./siteCtx" import { postTransformsTransients } from "./transients/transforms" -import { LayoutsKey } from "../../db/layouts" +import { LayoutKey } from "../../db/layouts" const getMaterialHash = ({ systemId, diff --git a/app/design/state/interactions/layouts.ts b/app/design/state/interactions/layouts.ts index cc3002f5..913944ac 100644 --- a/app/design/state/interactions/layouts.ts +++ b/app/design/state/interactions/layouts.ts @@ -19,7 +19,7 @@ import { import { StairType } from "@/server/data/stairTypes" import { Module } from "@/server/data/modules" import { useSystemStairTypes } from "~/data/stairTypes" -import { serializeLayoutsKey } from "../../../db/layouts" +import { serializeLayoutKey } from "../../../db/layouts" import { ColumnLayout, HouseModuleIdentifier, @@ -36,7 +36,7 @@ export const useChangeModuleLayout = ({ const getVanillaModule = useGetVanillaModule(systemId) const columnLayout = - layouts[serializeLayoutsKey({ systemId, dnas: houses[houseId].dnas })] + layouts[serializeLayoutKey({ systemId, dnas: houses[houseId].dnas })] const oldModule = columnLayout[columnIndex].gridGroups[levelIndex].modules[gridGroupIndex] @@ -149,7 +149,7 @@ export const useLayoutOptions = ({ } => { const systemId = houses[houseId].systemId const layout = - layouts[serializeLayoutsKey({ systemId, dnas: houses[houseId].dnas })] + layouts[serializeLayoutKey({ systemId, dnas: houses[houseId].dnas })] const m = layout[columnIndex].gridGroups[levelIndex].modules[gridGroupIndex].module @@ -213,7 +213,7 @@ export const useStairsOptions = ({ } => { const systemId = houses[houseId].systemId const layout = - layouts[serializeLayoutsKey({ systemId, dnas: houses[houseId].dnas })] + layouts[serializeLayoutKey({ systemId, dnas: houses[houseId].dnas })] const stairTypes = useSystemStairTypes({ systemId }) diff --git a/app/design/state/interactions/stretchLength.tsx b/app/design/state/interactions/stretchLength.tsx new file mode 100644 index 00000000..46fb58a6 --- /dev/null +++ b/app/design/state/interactions/stretchLength.tsx @@ -0,0 +1,185 @@ +import { pipe } from "fp-ts/lib/function" +import { useMemo } from "react" +import { suspend } from "suspend-react" +import { Matrix4, Vector3 } from "three" +import { OBB } from "three-stdlib" +import layoutsDB, { LayoutKey, serializeLayoutKey } from "../../../db/layouts" +import { A, NEA } from "../../../utils/functions" +import { floor, max } from "../../../utils/math" +import { getLayoutsWorker } from "../../../workers" +import { PositionedColumn } from "../../../workers/layouts" +import GroupedStretchColumn from "../../ui-3d/grouped/stretchLength/GroupedStretchColumn" +import { collideOBB } from "../dimensions" +import { vanillaColumns } from "../vanilla" + +export const useStretchLength2 = ({ + houseId, + layoutKey, + reactHouseMatrix, + width, + height, + length, + startColumn, + endColumn, +}: { + houseId: string + layoutKey: LayoutKey + reactHouseMatrix: Matrix4 + width: number + height: number + length: number + startColumn: PositionedColumn + endColumn: PositionedColumn +}) => { + const { systemId } = layoutKey + const strLayoutKey = serializeLayoutKey(layoutKey) + + const vanillaColumn = suspend(async () => { + if (strLayoutKey in vanillaColumns) { + return vanillaColumns[strLayoutKey] + } else { + const layoutsWorker = getLayoutsWorker() + if (!layoutsWorker) throw new Error("no layouts worker") + await layoutsWorker.processLayout(layoutKey) + const vanillaColumn = await layoutsDB.vanillaColumns.get(strLayoutKey) + if (!vanillaColumn) + throw new Error(`no vanilla column for ${strLayoutKey}`) + return vanillaColumn.vanillaColumn + } + }, []) + + const maxLength = 25 + const maxCount = floor(max(0, maxLength - length) / vanillaColumn.length) + const maxColumnZs = pipe( + NEA.range(0, maxCount - 1), + A.map((i) => i * vanillaColumn.length) + ) + + const columnZsUp = useMemo( + () => + pipe( + maxColumnZs, + A.takeLeftWhile((columnZ) => { + const center = new Vector3(0, 0, 0) + + const halfSize = new Vector3( + width / 2, + height / 2, + vanillaColumn.length / 2 + ) + + const obb = new OBB(center, halfSize) + + const mat = reactHouseMatrix + .clone() + .multiply(new Matrix4().makeTranslation(0, 0, length / 2 + columnZ)) + + obb.applyMatrix4(mat) + + const collision = collideOBB(obb, [houseId]) + + return !collision + }) + ), + [ + height, + houseId, + length, + maxColumnZs, + reactHouseMatrix, + vanillaColumn.length, + width, + ] + ) + const maxStretchLengthUp = columnZsUp?.[columnZsUp.length - 1] ?? 0 + + const columnZsDown = useMemo( + () => + pipe( + maxColumnZs, + A.map((x) => -1 * x), + A.takeLeftWhile((columnZ) => { + const center = new Vector3(0, 0, 0) + + const halfSize = new Vector3( + width / 2, + height / 2, + vanillaColumn.length / 2 + ) + + const obb = new OBB(center, halfSize) + + const mat = reactHouseMatrix + .clone() + .multiply( + new Matrix4().makeTranslation(0, 0, -(length / 2) + columnZ) + ) + + obb.applyMatrix4(mat) + + const collision = collideOBB(obb, [houseId]) + + return !collision + }) + ), + [ + maxColumnZs, + width, + height, + vanillaColumn.length, + reactHouseMatrix, + length, + houseId, + ] + ) + const maxStretchLengthDown = columnZsDown?.[columnZsDown.length - 1] ?? 0 + + const columnsUp = pipe( + columnZsUp, + A.map((columnZ) => ( + + + + )), + (columns) => ( + {columns} + ) + ) + + const columnsDown = pipe( + columnZsDown, + A.map((columnZ) => ( + + + + )), + (columns) => {columns} + ) + + return { + columnsUp, + columnsDown, + maxStretchLengthDown, + maxStretchLengthUp, + } +} diff --git a/app/design/state/interactions/windows.ts b/app/design/state/interactions/windows.ts index e953cffc..87d451b8 100644 --- a/app/design/state/interactions/windows.ts +++ b/app/design/state/interactions/windows.ts @@ -9,7 +9,7 @@ import { getSide, Side } from "~/design/state/camera" import { columnLayoutToDNA, layouts } from "~/design/state/layouts" import siteCtx from "~/design/state/siteCtx" import { useChangeModuleLayout } from "./layouts" -import { serializeLayoutsKey } from "../../../db/layouts" +import { serializeLayoutKey } from "../../../db/layouts" import houses from "../houses" import { HouseModuleIdentifier } from "../../../workers/layouts" @@ -28,7 +28,7 @@ export const useWindowOptions = ({ options: WindowTypeOption[] selected: WindowTypeOption["value"] } => { - const layoutsKey = serializeLayoutsKey({ + const layoutsKey = serializeLayoutKey({ systemId: houses[houseId].systemId, dnas: houses[houseId].dnas, }) diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 32e9b84e..7077a9cd 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -8,7 +8,7 @@ import * as RA from "fp-ts/ReadonlyArray" import { suspend } from "suspend-react" import { proxy, ref, useSnapshot } from "valtio" import { usePadColumn } from "../../data/modules" -import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../../db/layouts" +import layoutsDB, { LayoutKey, serializeLayoutKey } from "../../db/layouts" import { isSSR } from "../../utils/next" import { getLayoutsWorker } from "../../workers" import { @@ -33,9 +33,9 @@ if (!isSSR()) { }) } -export const useDnasLayout = (layoutsKey: LayoutsKey): ColumnLayout => { +export const useDnasLayout = (layoutsKey: LayoutKey): ColumnLayout => { const snap = useSnapshot(layouts) as typeof layouts - const serialKey = serializeLayoutsKey(layoutsKey) + const serialKey = serializeLayoutKey(layoutsKey) const maybeLayout: ColumnLayout | undefined = snap?.[serialKey] return suspend(async () => { @@ -95,7 +95,7 @@ export const useColumnMatrix = (houseId: string) => { const { systemId, dnas } = useHouse(houseId) const layoutsSnap = useSnapshot(layouts) as typeof layouts return columnLayoutToMatrix( - layoutsSnap[serializeLayoutsKey({ systemId, dnas })] + layoutsSnap[serializeLayoutKey({ systemId, dnas })] ) } diff --git a/app/design/state/scope.ts b/app/design/state/scope.ts index 5ebbc4b4..1bbfa4af 100644 --- a/app/design/state/scope.ts +++ b/app/design/state/scope.ts @@ -3,7 +3,7 @@ import { pipe } from "fp-ts/lib/function" import { proxy, useSnapshot } from "valtio" import { A, O } from "~/utils/functions" import { columnLayoutToMatrix, layouts } from "~/design/state/layouts" -import { serializeLayoutsKey } from "../../db/layouts" +import { serializeLayoutKey } from "../../db/layouts" import houses from "./houses" export type ScopeItem = { @@ -32,7 +32,7 @@ export const getSelectedModule = () => { const { systemId, dnas } = houses[houseId] - const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + const layoutsKey = serializeLayoutKey({ systemId, dnas }) return layouts[layoutsKey][columnIndex].gridGroups[levelIndex].modules[ gridGroupIndex @@ -46,7 +46,7 @@ export const getSelectedColumnMatrix = () => { if (scope.selected === null) return null const { houseId } = scope.selected const { systemId, dnas } = houses[houseId] - const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + const layoutsKey = serializeLayoutKey({ systemId, dnas }) return columnLayoutToMatrix(layouts[layoutsKey]) } export const getSelectedLevelModules = () => { diff --git a/app/design/state/transients/stretchLength.tsx b/app/design/state/transients/stretchLength.tsx index 4e911222..3fd15d4a 100644 --- a/app/design/state/transients/stretchLength.tsx +++ b/app/design/state/transients/stretchLength.tsx @@ -20,7 +20,7 @@ import { useGetVanillaModule, vanillaColumns, } from "../vanilla" -import layoutsDB, { serializeLayoutsKey } from "../../../db/layouts" +import layoutsDB, { serializeLayoutKey } from "../../../db/layouts" import { suspend } from "suspend-react" import { ColumnLayout, @@ -173,7 +173,6 @@ export const useStretchLength = ({ return !collision }) ), - // eslint-disable-next-line react-hooks/exhaustive-deps [ computeMatrix, houseHeight, @@ -182,8 +181,6 @@ export const useStretchLength = ({ houseWidth, maxColumnZs, vanillaColumnLength, - position, - rotation, ] ) const maxStretchLengthDown = columnZsDown?.[columnZsDown.length - 1] ?? 0 @@ -289,7 +286,7 @@ export const useStretchLength = ({ export const setStretchLength = () => { for (let houseId of Object.keys(stretchLengthClamped)) { const { systemId, dnas } = houses[houseId] - const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + const layoutsKey = serializeLayoutKey({ systemId, dnas }) const layout = layouts[layoutsKey] const { startColumn, midColumns, endColumn } = splitColumns(layout) const vanillaColumn = vanillaColumns[houseId] diff --git a/app/design/state/vanilla.ts b/app/design/state/vanilla.ts index ace4a0ea..de859ab1 100644 --- a/app/design/state/vanilla.ts +++ b/app/design/state/vanilla.ts @@ -1,13 +1,26 @@ -import { pipe } from "fp-ts/lib/function" -import { proxy } from "valtio" -import { useSystemModules } from "../../data/modules" import { Module } from "@/server/data/modules" +import { liveQuery } from "dexie" +import { pipe } from "fp-ts/lib/function" +import { proxy, ref } from "valtio" import { A, all, O, Ord, RA, S, someOrError } from "~/utils/functions" -import { suspend } from "suspend-react" -import layoutsDB from "../../db/layouts" +import { useSystemModules } from "../../data/modules" +import layoutsDB, { VanillaColumn } from "../../db/layouts" +import { isSSR } from "../../utils/next" import { PositionedRow } from "../../workers/layouts" -export const vanillaColumns = proxy>({}) +export const vanillaColumns = proxy>({}) + +if (!isSSR()) { + liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( + (dbVanillaColumns) => { + for (let { layoutsKey, vanillaColumn } of dbVanillaColumns) { + if (!(layoutsKey in dbVanillaColumns)) { + vanillaColumns[layoutsKey] = ref(vanillaColumn) + } + } + } + ) +} export const getVanillaColumnLength = (column: PositionedRow[]) => pipe( @@ -74,9 +87,3 @@ export const useGetVanillaModule = (systemId: string) => { return vanillaModule } } - -export const useVanillaColumn = (houseId: string) => { - return suspend(() => { - return layoutsDB.vanillaColumns.get(houseId) - }, [houseId]) -} diff --git a/app/design/ui-3d/grouped/GroupedHouse.tsx b/app/design/ui-3d/grouped/GroupedHouse.tsx index 4314c9ab..f0050843 100644 --- a/app/design/ui-3d/grouped/GroupedHouse.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse.tsx @@ -10,7 +10,7 @@ import { usePreTransformsTransients, } from "~/design/state/transients/transforms" import { RA } from "~/utils/functions" -import { serializeLayoutsKey } from "../../../db/layouts" +import { serializeLayoutKey } from "../../../db/layouts" import { useHouse, useHouseSystemId } from "../../state/houses" import { useTransformabilityBooleans } from "../../state/siteCtx" import RotateHandles from "../handles/RotateHandles" @@ -73,7 +73,7 @@ const GroupedHouse = (props: Props) => { useHouseMaterialOps({ houseId, ref: houseGroupRef, - layoutsKey: serializeLayoutsKey({ systemId, dnas }), + layoutsKey: serializeLayoutKey({ systemId, dnas }), }) return ( diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 2a4f4ee7..97590489 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -3,7 +3,7 @@ import { pipe } from "fp-ts/lib/function" import { Fragment, useEffect, useMemo, useRef } from "react" import { Box3, Group, Matrix4, Vector3 } from "three" import { OBB } from "three-stdlib" -import { serializeLayoutsKey } from "../../../db/layouts" +import { LayoutKey, serializeLayoutKey } from "../../../db/layouts" import { House } from "../../../db/user" import { RA } from "../../../utils/functions" import { splitColumns } from "../../../workers/layouts" @@ -15,9 +15,10 @@ import { } from "../../state/events" import { useHouseMaterialOps } from "../../state/hashedMaterials" import houses from "../../state/houses" +import { useStretchLength2 } from "../../state/interactions/stretchLength" import { useDnasLayout } from "../../state/layouts" import { useTransformabilityBooleans } from "../../state/siteCtx" -import { useVanillaColumn } from "../../state/vanilla" +import { useStretchLength } from "../../state/transients/stretchLength" import RotateHandles from "../handles/RotateHandles" import StretchHandle from "../handles/StretchHandle" import GroupedColumn from "./GroupedColumn" @@ -32,6 +33,10 @@ const GroupedHouse2 = (props: Props) => { const { systemId, id: houseId, position, rotation } = house const dnas = [...house.dnas] + const layoutKey: LayoutKey = { systemId, dnas } + + const strLayoutKey = serializeLayoutKey(layoutKey) + const translationMatrix = useMemo(() => { const m = new Matrix4() const { x, y, z } = position @@ -54,10 +59,7 @@ const GroupedHouse2 = (props: Props) => { const { stretchEnabled, moveRotateEnabled } = useTransformabilityBooleans(houseId) - const layout = useDnasLayout({ - systemId, - dnas, - }) + const layout = useDnasLayout(layoutKey) const { width, length, height, obb }: Dimensions = useMemo(() => { const width = layout[0].gridGroups[0].modules[0].module.width @@ -95,6 +97,19 @@ const GroupedHouse2 = (props: Props) => { } }, [layout, reactHouseMatrix, houseId]) + const { startColumn, endColumn, midColumns } = splitColumns(layout) + + const stretchLength = useStretchLength2({ + houseId, + layoutKey, + reactHouseMatrix, + width, + height, + length, + startColumn, + endColumn, + }) + const obbBox = useMemo(() => { const box3 = new Box3().setFromCenterAndSize( obb.center, @@ -156,14 +171,12 @@ const GroupedHouse2 = (props: Props) => { } }) - const { startColumn, endColumn, midColumns } = splitColumns(layout) - - const vanillaColumn = useVanillaColumn(houseId) + // const { columnsUp, columnsDown } = useStretchLength({ startColumn, endColumn, midColumns }) useHouseMaterialOps({ houseId, ref: rootRef, - layoutsKey: serializeLayoutsKey({ systemId, dnas }), + layoutsKey: serializeLayoutKey({ systemId, dnas }), }) const startColumnRef = useRef(null!) @@ -236,15 +249,19 @@ const GroupedHouse2 = (props: Props) => { scale={moveRotateEnabled ? [1, 1, 1] : [0, 0, 0]} /> - {/* - {columnsUp} - {columnsDown} - */} + + {stretchLength.columnsUp} + {stretchLength.columnsDown} + {/* */} {/* */} + + + + ) } diff --git a/app/workers/layouts.ts b/app/workers/layouts.ts index ebb1ed98..c329c7db 100644 --- a/app/workers/layouts.ts +++ b/app/workers/layouts.ts @@ -8,7 +8,7 @@ import { BufferGeometry } from "three" import { mergeBufferGeometries } from "three-stdlib" import { Module } from "../../server/data/modules" import { getSpeckleObject } from "../../server/data/speckleModel" -import layoutsDB, { LayoutsKey, serializeLayoutsKey } from "../db/layouts" +import layoutsDB, { LayoutKey, serializeLayoutKey } from "../db/layouts" import systemsDB, { LastFetchStamped } from "../db/systems" import { A, all, O, Ord, R, S } from "../utils/functions" import { isSSR } from "../utils/next" @@ -102,7 +102,7 @@ const syncModels = (modules: LastFetchStamped[]) => { } let modulesCache: LastFetchStamped[] = [] -let layoutsQueue: LayoutsKey[] = [] +let layoutsQueue: LayoutKey[] = [] const modulesToRows = (modules: Module[]): Module[][] => { const jumpIndices = pipe( @@ -409,7 +409,7 @@ const getVanillaModule = ( ) } -const processLayout = async ({ systemId, dnas }: LayoutsKey) => { +const processLayout = async ({ systemId, dnas }: LayoutKey) => { const modules = pipe( dnas, A.filterMap((dna) => @@ -425,7 +425,7 @@ const processLayout = async ({ systemId, dnas }: LayoutsKey) => { const layout = modulesToColumnLayout(modules) - const layoutsKey = serializeLayoutsKey({ systemId, dnas }) + const layoutsKey = serializeLayoutKey({ systemId, dnas }) layoutsDB.layouts.put({ layout, @@ -501,11 +501,11 @@ if (!isSSR()) { }) } -const postLayout = (key: LayoutsKey) => { +const postLayout = (key: LayoutKey) => { layoutsQueue.push(key) } -const postLayouts = (keys: LayoutsKey[]) => { +const postLayouts = (keys: LayoutKey[]) => { keys.map(postLayout) } From 91cca4df8890acb523f5b9fb2173f9b5f06a68ae Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 6 Jul 2023 11:13:40 +0100 Subject: [PATCH 029/132] wip intent stretch working --- app/design/state/events/index.ts | 44 ++++- app/design/state/gestures/index.ts | 174 +++++++++++------- .../{stretchLength.tsx => ZStretch.tsx} | 82 +++++++-- app/design/ui-3d/grouped/GroupedHouse2.tsx | 31 ++-- .../stretchLength/GroupedStretchColumn.tsx | 15 +- 5 files changed, 231 insertions(+), 115 deletions(-) rename app/design/state/interactions/{stretchLength.tsx => ZStretch.tsx} (78%) diff --git a/app/design/state/events/index.ts b/app/design/state/events/index.ts index baa7594d..20a9b4c9 100644 --- a/app/design/state/events/index.ts +++ b/app/design/state/events/index.ts @@ -2,9 +2,15 @@ import { useEvent } from "react-use" const MOVE_HOUSE_INTENT_EVENT = "MoveHouseIntentEvent" const MOVE_HOUSE_EVENT = "MoveHouseEvent" + const ROTATE_HOUSE_EVENT = "RotateHouseEvent" const ROTATE_HOUSE_INTENT_EVENT = "RotateHouseIntentEvent" -const STRETCH_HOUSE_INTENT_EVENT = "StretchHouseIntentEvent" + +const Z_STRETCH_HOUSE_INTENT_EVENT = "ZStretchHouseIntentEvent" +const Z_STRETCH_HOUSE_EVENT = "ZStretchHouseEvent" + +const X_STRETCH_HOUSE_INTENT_EVENT = "XStretchHouseIntentEvent" +const X_STRETCH_HOUSE_EVENT = "XStretchHouseEvent" type MoveHouseDetail = { delta: V3 @@ -15,11 +21,16 @@ type MoveHouseDetail = { type RotateHouseDetail = { rotation: number houseId: string + last: boolean } type StretchHouseDetail = { houseId: string - // look at your stretch proxies to figure this + direction: 1 | -1 + dx: number + dz: number + distance: number + last: boolean } export const dispatchMoveHouseIntent = (detail: MoveHouseDetail) => @@ -62,9 +73,30 @@ export const useRotateHouseListener = ( f: (eventDetail: RotateHouseDetail) => void ) => useEvent(ROTATE_HOUSE_EVENT, ({ detail }) => f(detail)) -export const dispatchStretchHouseIntent = (detail: StretchHouseDetail) => - dispatchEvent(new CustomEvent(STRETCH_HOUSE_INTENT_EVENT, { detail })) +export const dispatchZStretchHouseIntent = (detail: StretchHouseDetail) => + dispatchEvent(new CustomEvent(Z_STRETCH_HOUSE_INTENT_EVENT, { detail })) + +export const dispatchZStretchHouse = (detail: StretchHouseDetail) => + dispatchEvent(new CustomEvent(Z_STRETCH_HOUSE_EVENT, { detail })) + +export const useZStretchHouseIntentListener = ( + f: (eventDetail: StretchHouseDetail) => void +) => useEvent(Z_STRETCH_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) + +export const useZStretchHouseListener = ( + f: (eventDetail: StretchHouseDetail) => void +) => useEvent(Z_STRETCH_HOUSE_EVENT, ({ detail }) => f(detail)) + +export const dispatchXStretchHouseIntent = (detail: StretchHouseDetail) => + dispatchEvent(new CustomEvent(X_STRETCH_HOUSE_INTENT_EVENT, { detail })) + +export const dispatchXStretchHouse = (detail: StretchHouseDetail) => + dispatchEvent(new CustomEvent(X_STRETCH_HOUSE_EVENT, { detail })) + +export const useXStretchHouseIntentListener = ( + f: (eventDetail: StretchHouseDetail) => void +) => useEvent(X_STRETCH_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) -export const useStretchHouseIntentListener = ( +export const useXStretchHouseListener = ( f: (eventDetail: StretchHouseDetail) => void -) => useEvent(STRETCH_HOUSE_INTENT_EVENT, f) +) => useEvent(X_STRETCH_HOUSE_EVENT, ({ detail }) => f(detail)) diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index f2ede24e..e8502d32 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -22,84 +22,92 @@ import { openMenu } from "../menu" import pointer from "../pointer" import siteCtx, { downMode, EditModeEnum } from "../siteCtx" import dragProxy, { Drag, StretchHandleIdentifier } from "./drag" -import { dispatchMoveHouseIntent } from "../events" +import { + dispatchMoveHouseIntent, + dispatchXStretchHouseIntent, + dispatchZStretchHouseIntent, +} from "../events" export const useDragHandler = () => { const { rotateV2, unrotateV2 } = useRotations() - useSubscribeKey(dragProxy, "drag", () => { - if (dragProxy.drag === null || dragProxy.start === null) return - const { - start: { - identifier, - identifier: { houseId, identifierType }, - point: { x: x0, y: y0, z: z0 }, - }, - drag: { - point: { x: x1, y: y1, z: z1 }, - }, - } = dragProxy - switch (identifierType) { - case "HOUSE_ELEMENT": { - dispatchMoveHouseIntent({ - houseId, - delta: { - x: x1 - x0, - y: 0, - z: z1 - z0, - }, - last: false, - }) - // dispatch event move house intent - // preTransformsTransients[houseId] = { - // position: { - // dx: x1 - x0, - // dy: 0, - // dz: z1 - z0, - // }, - // } - return - } - case "ROTATE_HANDLE": { - // dispatch event rotate house intent - const { x: cx, z: cz } = getHouseCenter(houseId) - const angle0 = Math.atan2(cz - z0, cx - x0) - const angle = Math.atan2(cz - z1, cx - x1) - preTransformsTransients[houseId] = { - rotation: -(angle - angle0), - } - return - } - case "STRETCH_HANDLE": { - // dispatch event stretch house intent - const [distanceX, distanceZ] = unrotateV2(houseId, [x1 - x0, z1 - z0]) - const [dx, dz] = rotateV2(houseId, [0, distanceZ]) + useSubscribeKey( + dragProxy, + "drag", + () => { + if (dragProxy.drag === null || dragProxy.start === null) return - const { direction = 1, axis } = identifier as StretchHandleIdentifier + const { + start: { + identifier, + identifier: { houseId, identifierType }, + point: { x: x0, y: y0, z: z0 }, + }, + drag: { + point: { x: x1, y: y1, z: z1 }, + }, + } = dragProxy - if (axis === "z") { - stretchLengthRaw[houseId] = { - direction, - distance: distanceZ, - dx, - dz, + switch (identifierType) { + case "HOUSE_ELEMENT": { + dispatchMoveHouseIntent({ + houseId, + delta: { + x: x1 - x0, + y: 0, + z: z1 - z0, + }, + last: false, + }) + return + } + case "ROTATE_HANDLE": { + // dispatch event rotate house intent + const { x: cx, z: cz } = getHouseCenter(houseId) + const angle0 = Math.atan2(cz - z0, cx - x0) + const angle = Math.atan2(cz - z1, cx - x1) + preTransformsTransients[houseId] = { + rotation: -(angle - angle0), } + return } + case "STRETCH_HANDLE": { + const [distanceX, distanceZ] = unrotateV2(houseId, [x1 - x0, z1 - z0]) + const [dx, dz] = rotateV2(houseId, [0, distanceZ]) - if (axis === "x") { - stretchWidthRaw[houseId] = { - direction, - distance: distanceX, - dx, - dz, + const { direction = 1, axis } = identifier as StretchHandleIdentifier + + if (axis === "z") { + dispatchZStretchHouseIntent({ + houseId, + direction, + distance: distanceZ, + dx, + dz, + last: false, + }) } + + if (axis === "x") { + dispatchXStretchHouseIntent({ + houseId, + direction, + distance: distanceX, + dx, + dz, + last: false, + }) + } + return } } - } - }) + }, + false + ) useSubscribeKey(dragProxy, "end", () => { if (!dragProxy.end) return + const cleanup = () => { dragProxy.start = null dragProxy.drag = null @@ -113,11 +121,12 @@ export const useDragHandler = () => { const { start: { - identifier: { houseId, identifierType }, - point: { x: x0, y: y0, z: z0 }, + identifier, + identifier: { houseId }, + point: { x: x0, z: z0 }, }, drag: { - point: { x: x1, y: y1, z: z1 }, + point: { x: x1, z: z1 }, }, } = dragProxy @@ -135,12 +144,37 @@ export const useDragHandler = () => { delta, last: true, }) - // setTransforms() break case EditModeEnum.Enum.STRETCH: - // setStretchLength() - // setPreviews() - break + const [distanceX, distanceZ] = unrotateV2(houseId, [x1 - x0, z1 - z0]) + const [dx, dz] = rotateV2(houseId, [0, distanceZ]) + + const { direction = 1, axis } = identifier as StretchHandleIdentifier + + if (axis === "z") { + dispatchZStretchHouseIntent({ + houseId, + direction, + distance: distanceZ, + dx, + dz, + last: true, + }) + } + + if (axis === "x") { + dispatchXStretchHouseIntent({ + houseId, + direction, + distance: distanceX, + dx, + dz, + last: true, + }) + } + return + // setStretchLength() + // setPreviews() } } diff --git a/app/design/state/interactions/stretchLength.tsx b/app/design/state/interactions/ZStretch.tsx similarity index 78% rename from app/design/state/interactions/stretchLength.tsx rename to app/design/state/interactions/ZStretch.tsx index 46fb58a6..ba2e3cc6 100644 --- a/app/design/state/interactions/stretchLength.tsx +++ b/app/design/state/interactions/ZStretch.tsx @@ -1,7 +1,9 @@ +"use client" +import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" -import { useMemo } from "react" +import { Fragment, memo, MutableRefObject, useMemo } from "react" import { suspend } from "suspend-react" -import { Matrix4, Vector3 } from "three" +import { Group, Matrix4, Vector3 } from "three" import { OBB } from "three-stdlib" import layoutsDB, { LayoutKey, serializeLayoutKey } from "../../../db/layouts" import { A, NEA } from "../../../utils/functions" @@ -10,18 +12,13 @@ import { getLayoutsWorker } from "../../../workers" import { PositionedColumn } from "../../../workers/layouts" import GroupedStretchColumn from "../../ui-3d/grouped/stretchLength/GroupedStretchColumn" import { collideOBB } from "../dimensions" +import { + dispatchZStretchHouse, + useZStretchHouseIntentListener, +} from "../events" import { vanillaColumns } from "../vanilla" -export const useStretchLength2 = ({ - houseId, - layoutKey, - reactHouseMatrix, - width, - height, - length, - startColumn, - endColumn, -}: { +type Props = { houseId: string layoutKey: LayoutKey reactHouseMatrix: Matrix4 @@ -30,10 +27,27 @@ export const useStretchLength2 = ({ length: number startColumn: PositionedColumn endColumn: PositionedColumn -}) => { + startRef: MutableRefObject + endRef: MutableRefObject +} + +const ZStretch = ({ + houseId, + layoutKey, + reactHouseMatrix, + width, + height, + length, + startColumn, + endColumn, + startRef, + endRef, +}: Props) => { const { systemId } = layoutKey const strLayoutKey = serializeLayoutKey(layoutKey) + console.log(`stretchLength ${houseId}`) + const vanillaColumn = suspend(async () => { if (strLayoutKey in vanillaColumns) { return vanillaColumns[strLayoutKey] @@ -176,10 +190,40 @@ export const useStretchLength2 = ({ (columns) => {columns} ) - return { - columnsUp, - columnsDown, - maxStretchLengthDown, - maxStretchLengthUp, - } + useZStretchHouseIntentListener((detail) => { + if (detail.houseId !== houseId) return + + const { distance, direction, dx, dz, last } = detail + + switch (direction) { + case 1: { + const clamped = -distance > length || distance > maxStretchLengthUp + if (!clamped) { + endRef.current.position.set(0, 0, distance) + dispatchZStretchHouse(detail) + } + break + } + + case -1: { + const clamped = distance > length || distance < maxStretchLengthDown + if (!clamped) { + startRef.current.position.set(0, 0, distance) + dispatchZStretchHouse(detail) + } + break + } + } + + invalidate() + }) + + return ( + + {columnsUp} + {columnsDown} + + ) } + +export default memo(ZStretch) diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 97590489..5cf8e4b3 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -15,10 +15,9 @@ import { } from "../../state/events" import { useHouseMaterialOps } from "../../state/hashedMaterials" import houses from "../../state/houses" -import { useStretchLength2 } from "../../state/interactions/stretchLength" +import ZStretch from "../../state/interactions/ZStretch" import { useDnasLayout } from "../../state/layouts" import { useTransformabilityBooleans } from "../../state/siteCtx" -import { useStretchLength } from "../../state/transients/stretchLength" import RotateHandles from "../handles/RotateHandles" import StretchHandle from "../handles/StretchHandle" import GroupedColumn from "./GroupedColumn" @@ -99,16 +98,8 @@ const GroupedHouse2 = (props: Props) => { const { startColumn, endColumn, midColumns } = splitColumns(layout) - const stretchLength = useStretchLength2({ - houseId, - layoutKey, - reactHouseMatrix, - width, - height, - length, - startColumn, - endColumn, - }) + // const ZStretch = ZStretch({ + // }) const obbBox = useMemo(() => { const box3 = new Box3().setFromCenterAndSize( @@ -250,8 +241,20 @@ const GroupedHouse2 = (props: Props) => { /> - {stretchLength.columnsUp} - {stretchLength.columnsDown} + {/* */} diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx index 40a677c3..744dc75f 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx @@ -6,6 +6,7 @@ import { stretchLengthClamped } from "~/design/state/transients/stretchLength" import { useSubscribeKey } from "~/utils/hooks" import GroupedStretchModule from "./GroupedStretchModule" import { GridGroup } from "../../../../workers/layouts" +import { useZStretchHouseListener } from "../../../state/events" type Props = { systemId: string @@ -21,13 +22,15 @@ const GroupedStretchColumn = (props: Props) => { const groupRef = useRef(null) - useSubscribeKey(stretchLengthClamped, houseId, () => { - if (!stretchLengthClamped[houseId]) { - groupRef.current?.scale.set(0, 0, 0) - return - } + useZStretchHouseListener((detail) => { + if (houseId !== detail.houseId) return + + const { distance, direction, dx, dz, last } = detail - const { distance, direction } = stretchLengthClamped[houseId] + // if (!stretchLengthClamped[houseId]) { + // groupRef.current?.scale.set(0, 0, 0) + // return + // } if (direction !== props.direction) return From 44c4f5accbc90b203861abecd66b4880810c5c78 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 6 Jul 2023 15:33:41 +0100 Subject: [PATCH 030/132] wip messy --- app/data/elements.ts | 22 +- app/debug/system/ui-3d/DebugSpeckleModule.tsx | 1 + app/design/state/gestures/index.ts | 173 +++---- app/design/state/houses.ts | 30 +- app/design/state/interactions/ZStretch.tsx | 116 ++++- app/design/state/transients/stretchLength.tsx | 421 ------------------ app/design/ui-3d/grouped/GroupedApp.tsx | 2 +- app/design/ui-3d/grouped/GroupedColumn.tsx | 23 +- app/design/ui-3d/grouped/GroupedHouse.tsx | 132 +++--- app/design/ui-3d/grouped/GroupedHouse2.tsx | 41 +- app/design/ui-3d/grouped/GroupedModule.tsx | 8 +- .../ui-3d/grouped/preview/PreviewModule.tsx | 8 +- .../stretchLength/GroupedStretchColumn.tsx | 9 +- .../stretchLength/GroupedStretchModule.tsx | 18 +- app/design/ui/SiteSidebar.tsx | 7 +- app/workers/layouts.ts | 54 ++- 16 files changed, 389 insertions(+), 676 deletions(-) delete mode 100644 app/design/state/transients/stretchLength.tsx diff --git a/app/data/elements.ts b/app/data/elements.ts index 34724feb..fd075e9e 100644 --- a/app/data/elements.ts +++ b/app/data/elements.ts @@ -2,6 +2,7 @@ import { trpc } from "@/client/trpc" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" import { useMemo } from "react" +import { suspend } from "suspend-react" import { BufferGeometry, BufferGeometryLoader } from "three" import { proxy, ref, useSnapshot } from "valtio" import { O, R, RA, S } from "~/utils/functions" @@ -10,6 +11,7 @@ import { Module } from "../../server/data/modules" import layoutsDB from "../db/layouts" import systemsDB from "../db/systems" import { isSSR } from "../utils/next" +import { getLayoutsWorker } from "../workers" export const useElements = (): Element[] => { const { data = [] } = trpc.elements.useQuery() @@ -121,21 +123,9 @@ if (!isSSR()) { }) } -export const useSpeckleObject = (speckleBranchUrl: string) => { +export const useSpeckleObject = ( + speckleBranchUrl: string +): O.Option> => { const snap = useSnapshot(models) as typeof models - - return snap[speckleBranchUrl] -} - -export const useModuleElements = ({ - systemId, - speckleBranchUrl, -}: Module): Record => { - const speckleObject = useSpeckleObject(speckleBranchUrl) - - return useMemo( - () => pipe(speckleObject), - // eslint-disable-next-line react-hooks/exhaustive-deps - [speckleBranchUrl, speckleObject] - ) + return R.lookup(speckleBranchUrl)(snap) } diff --git a/app/debug/system/ui-3d/DebugSpeckleModule.tsx b/app/debug/system/ui-3d/DebugSpeckleModule.tsx index 875300ee..32853b77 100644 --- a/app/debug/system/ui-3d/DebugSpeckleModule.tsx +++ b/app/debug/system/ui-3d/DebugSpeckleModule.tsx @@ -23,6 +23,7 @@ const DebugSpeckleModule = ({ module }: { module: Module }) => { {pipe( ifcGeometries, + O.getOrElse(() => ({})), // A.takeLeft(1), R.collect(S.Ord)((ifcTag, geometry) => { const material = pipe( diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index e8502d32..c91dd292 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -1,32 +1,24 @@ -import { setPreviews } from "~/design/state/previews" +import { invalidate, ThreeEvent } from "@react-three/fiber" +import { useGesture } from "@use-gesture/react" +import { pipe } from "fp-ts/lib/function" +import { useDebouncedCallback } from "use-debounce" +import { useSnapshot } from "valtio" import scope from "~/design/state/scope" -import { - setStretchLength, - stretchLengthRaw, -} from "~/design/state/transients/stretchLength" -import { - preTransformsTransients, - setTransforms, -} from "~/design/state/transients/transforms" +import { preTransformsTransients } from "~/design/state/transients/transforms" import { A, O } from "~/utils/functions" import { useSubscribeKey } from "~/utils/hooks" import { isMesh, useRotations } from "~/utils/three" -import { ThreeEvent } from "@react-three/fiber" -import { useGesture } from "@use-gesture/react" -import { pipe } from "fp-ts/lib/function" -import { useSnapshot } from "valtio" -import { stretchWidthRaw } from "../../ui-3d/grouped/stretchWidth/StretchWidth" import { setCameraEnabled } from "../camera" import { getHouseCenter } from "../dimensions" -import { openMenu } from "../menu" -import pointer from "../pointer" -import siteCtx, { downMode, EditModeEnum } from "../siteCtx" -import dragProxy, { Drag, StretchHandleIdentifier } from "./drag" import { dispatchMoveHouseIntent, dispatchXStretchHouseIntent, dispatchZStretchHouseIntent, } from "../events" +import { openMenu } from "../menu" +import pointer from "../pointer" +import siteCtx, { downMode, EditModeEnum } from "../siteCtx" +import dragProxy, { Drag, StretchHandleIdentifier } from "./drag" export const useDragHandler = () => { const { rotateV2, unrotateV2 } = useRotations() @@ -78,6 +70,7 @@ export const useDragHandler = () => { const { direction = 1, axis } = identifier as StretchHandleIdentifier if (axis === "z") { + console.log("dispatching NORMAL") dispatchZStretchHouseIntent({ houseId, direction, @@ -86,9 +79,7 @@ export const useDragHandler = () => { dz, last: false, }) - } - - if (axis === "x") { + } else if (axis === "x") { dispatchXStretchHouseIntent({ houseId, direction, @@ -105,81 +96,90 @@ export const useDragHandler = () => { false ) - useSubscribeKey(dragProxy, "end", () => { - if (!dragProxy.end) return - - const cleanup = () => { - dragProxy.start = null - dragProxy.drag = null - dragProxy.end = false - } - - if (dragProxy.drag === null || dragProxy.start === null) { - cleanup() - return - } + useSubscribeKey( + dragProxy, + "end", + () => { + if (!dragProxy.end) return - const { - start: { - identifier, - identifier: { houseId }, - point: { x: x0, z: z0 }, - }, - drag: { - point: { x: x1, z: z1 }, - }, - } = dragProxy + const cleanup = () => { + console.log("cleaning up") + dragProxy.start = null + dragProxy.drag = null + dragProxy.end = false + } - const delta = { - x: x1 - x0, - y: 0, - z: z1 - z0, - } + if (dragProxy.drag === null || dragProxy.start === null) { + cleanup() + return + } - if (dragProxy.end) { - switch (siteCtx.editMode) { - case EditModeEnum.Enum.MOVE_ROTATE: - dispatchMoveHouseIntent({ - houseId, - delta, - last: true, - }) - break - case EditModeEnum.Enum.STRETCH: - const [distanceX, distanceZ] = unrotateV2(houseId, [x1 - x0, z1 - z0]) - const [dx, dz] = rotateV2(houseId, [0, distanceZ]) + const { + start: { + identifier, + identifier: { houseId }, + point: { x: x0, z: z0 }, + }, + drag: { + point: { x: x1, z: z1 }, + }, + } = dragProxy - const { direction = 1, axis } = identifier as StretchHandleIdentifier + const delta = { + x: x1 - x0, + y: 0, + z: z1 - z0, + } - if (axis === "z") { - dispatchZStretchHouseIntent({ + if (dragProxy.end) { + switch (siteCtx.editMode) { + case EditModeEnum.Enum.MOVE_ROTATE: + dispatchMoveHouseIntent({ houseId, - direction, - distance: distanceZ, - dx, - dz, + delta, last: true, }) - } + break + case EditModeEnum.Enum.STRETCH: + const [distanceX, distanceZ] = unrotateV2(houseId, [ + x1 - x0, + z1 - z0, + ]) + const [dx, dz] = rotateV2(houseId, [0, distanceZ]) - if (axis === "x") { - dispatchXStretchHouseIntent({ - houseId, - direction, - distance: distanceX, - dx, - dz, - last: true, - }) - } - return - // setStretchLength() - // setPreviews() + const { direction = 1, axis } = + identifier as StretchHandleIdentifier + + if (axis === "z") { + console.log("dispatching LAST") + dispatchZStretchHouseIntent({ + houseId, + direction, + distance: distanceZ, + dx, + dz, + last: true, + }) + } else if (axis === "x") { + dispatchXStretchHouseIntent({ + houseId, + direction, + distance: distanceX, + dx, + dz, + last: true, + }) + } + return + // setStretchLength() + // setPreviews() + } } - } - cleanup() - }) + cleanup() + }, + false + ) } export const useGestures = (): any => @@ -280,6 +280,7 @@ export const useGestures = (): any => if (hovering) { document.body.style.cursor = "grab" } + invalidate() }, onDoubleClick: ({ event, event: { intersections } }) => { event.stopPropagation() @@ -294,6 +295,8 @@ export const useGestures = (): any => downMode({ ...userData.identifier }) } } + + invalidate() }, }) diff --git a/app/design/state/houses.ts b/app/design/state/houses.ts index 2b941169..40c8c11d 100644 --- a/app/design/state/houses.ts +++ b/app/design/state/houses.ts @@ -8,12 +8,14 @@ import { nanoid } from "nanoid" import { useMemo } from "react" import { useKey } from "react-use" import { Vector3 } from "three" -import { proxy, snapshot, subscribe, useSnapshot } from "valtio" +import { useDebouncedCallback } from "use-debounce" +import { proxy, ref, snapshot, subscribe, useSnapshot } from "valtio" import { A, clearRecord, R, RA, RR, S } from "~/utils/functions" import { useHouseTypes } from "../../data/houseTypes" import { useModules, useSystemModules } from "../../data/modules" import userDB, { House } from "../../db/user" import { isSSR } from "../../utils/next" +import Dexie from "dexie" const houses = proxy>({}) @@ -26,28 +28,34 @@ const initHouses = async () => { } housesArray.forEach((house) => { - houses[house.id] = house + houses[house.id] = ref(house) }) } initHouses().then(() => { subscribe(houses, () => { - // This will run every time `houses` changes Object.values(houses).forEach(async (house) => { - const snapshotHouse = snapshot(house) as typeof house - // Check if house exists in the DB const existingHouse = await userDB.houses.get(house.id) + if (existingHouse) { - // If it exists, update it - userDB.houses.update(house.id, snapshotHouse) + userDB.houses.update(house.id, Dexie.deepClone(house)) } else { - // If it doesn't exist, add it - userDB.houses.add(snapshotHouse) + userDB.houses.add(Dexie.deepClone(house)) } }) }) }) +export const useSetHouse = (houseId: string) => { + return useDebouncedCallback((nextHouse: House) => { + houses[houseId] = ref({ + ...nextHouse, + position: ref(nextHouse.position), + dnas: ref(nextHouse.dnas), + }) + }, 300) +} + export const useHouses = () => { return useSnapshot(houses) as typeof houses } @@ -181,7 +189,7 @@ export const useInsert1000Skylarks = () => { for (let x = startX; x < incX * count; x += incX) { for (let z = startZ; z < incZ * count; z += incZ) { const id = nanoid() - houses[id] = { + houses[id] = ref({ id, houseTypeId, systemId: houseType.systemId, @@ -190,7 +198,7 @@ export const useInsert1000Skylarks = () => { dnas: houseType.dnas as string[], modifiedMaterials: {}, friendlyName: `Building ${keys(houses).length + 1}`, - } + }) } } }, diff --git a/app/design/state/interactions/ZStretch.tsx b/app/design/state/interactions/ZStretch.tsx index ba2e3cc6..b1d5536d 100644 --- a/app/design/state/interactions/ZStretch.tsx +++ b/app/design/state/interactions/ZStretch.tsx @@ -1,13 +1,14 @@ "use client" import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" -import { Fragment, memo, MutableRefObject, useMemo } from "react" +import { Fragment, memo, RefObject, useMemo } from "react" import { suspend } from "suspend-react" import { Group, Matrix4, Vector3 } from "three" import { OBB } from "three-stdlib" import layoutsDB, { LayoutKey, serializeLayoutKey } from "../../../db/layouts" import { A, NEA } from "../../../utils/functions" -import { floor, max } from "../../../utils/math" +import { floor, max, round, sign } from "../../../utils/math" +import { yAxis } from "../../../utils/three" import { getLayoutsWorker } from "../../../workers" import { PositionedColumn } from "../../../workers/layouts" import GroupedStretchColumn from "../../ui-3d/grouped/stretchLength/GroupedStretchColumn" @@ -15,7 +16,10 @@ import { collideOBB } from "../dimensions" import { dispatchZStretchHouse, useZStretchHouseIntentListener, + useZStretchHouseListener, } from "../events" +import houses, { useSetHouse } from "../houses" +import { columnLayoutToDNA } from "../layouts" import { vanillaColumns } from "../vanilla" type Props = { @@ -27,8 +31,9 @@ type Props = { length: number startColumn: PositionedColumn endColumn: PositionedColumn - startRef: MutableRefObject - endRef: MutableRefObject + midColumns: PositionedColumn[] + startRef: RefObject + endRef: RefObject } const ZStretch = ({ @@ -40,14 +45,13 @@ const ZStretch = ({ length, startColumn, endColumn, + midColumns, startRef, endRef, }: Props) => { const { systemId } = layoutKey const strLayoutKey = serializeLayoutKey(layoutKey) - console.log(`stretchLength ${houseId}`) - const vanillaColumn = suspend(async () => { if (strLayoutKey in vanillaColumns) { return vanillaColumns[strLayoutKey] @@ -193,13 +197,15 @@ const ZStretch = ({ useZStretchHouseIntentListener((detail) => { if (detail.houseId !== houseId) return + // if (!startRef.current || !endRef.current) return + const { distance, direction, dx, dz, last } = detail switch (direction) { case 1: { const clamped = -distance > length || distance > maxStretchLengthUp if (!clamped) { - endRef.current.position.set(0, 0, distance) + endRef.current?.position.set(0, 0, distance) dispatchZStretchHouse(detail) } break @@ -208,7 +214,7 @@ const ZStretch = ({ case -1: { const clamped = distance > length || distance < maxStretchLengthDown if (!clamped) { - startRef.current.position.set(0, 0, distance) + startRef.current?.position.set(0, 0, distance) dispatchZStretchHouse(detail) } break @@ -218,6 +224,100 @@ const ZStretch = ({ invalidate() }) + const setHouse = useSetHouse(houseId) + + useZStretchHouseListener((detail) => { + if (detail.houseId !== houseId || !detail.last) return + + const { direction, distance } = detail + + const delta = round(distance / vanillaColumn.length) + + const dxdz = new Vector3(0, 0, delta * vanillaColumn.length) + + dxdz.applyAxisAngle(yAxis, houses[houseId].rotation) + + const { x: dx, z: dz } = dxdz + + const { x, y, z } = houses[houseId].position + + switch (direction) { + case 1: { + if (sign(delta) === 1) { + setHouse({ + ...houses[houseId], + dnas: columnLayoutToDNA([ + startColumn, + ...midColumns, + ...A.replicate(delta, { + gridGroups: vanillaColumn.gridGroups, + }), + endColumn, + ]), + position: { + x: x + dx / 2, + y, + z: z + dz / 2, + }, + }) + } else if (sign(delta) === -1) { + setHouse({ + ...houses[houseId], + dnas: columnLayoutToDNA([ + startColumn, + ...midColumns.slice(0, midColumns.length + delta), + endColumn, + ]), + position: { + x: x + dx / 2, + y, + z: z + dz / 2, + }, + }) + } + break + } + case -1: { + if (sign(delta) === -1) { + const { x, y, z } = houses[houseId].position + houses[houseId] = { + ...houses[houseId], + dnas: columnLayoutToDNA([ + startColumn, + ...A.replicate(-delta, { + gridGroups: vanillaColumn.gridGroups, + }), + ...midColumns, + endColumn, + ]), + position: { + x: x + dx / 2, + y, + z: z + dz / 2, + }, + } + } else if (sign(delta) === 1) { + houses[houseId] = { + ...houses[houseId], + dnas: columnLayoutToDNA([ + startColumn, + ...midColumns.slice(0, midColumns.length - delta), + endColumn, + ]), + position: { + x: x + dx / 2, + y, + z: z + dz / 2, + }, + } + } + break + } + } + + invalidate() + }) + return ( {columnsUp} diff --git a/app/design/state/transients/stretchLength.tsx b/app/design/state/transients/stretchLength.tsx deleted file mode 100644 index 3fd15d4a..00000000 --- a/app/design/state/transients/stretchLength.tsx +++ /dev/null @@ -1,421 +0,0 @@ -import { invalidate } from "@react-three/fiber" -import { pipe } from "fp-ts/lib/function" -import { MutableRefObject, useMemo } from "react" -import { Group, Vector3 } from "three" -import { OBB } from "three-stdlib" -import { proxy } from "valtio" -import GroupedStretchColumn from "~/design/ui-3d/grouped/stretchLength/GroupedStretchColumn" -import { A, NEA, RA } from "~/utils/functions" -import { useSubscribeKey } from "~/utils/hooks" -import { floor, max, round, sign } from "~/utils/math" -import { yAxis } from "~/utils/three" -import dimensions, { - collideOBB, - useHouseDimensionsUpdates, - usePostTransMatrix, -} from "~/design/state/dimensions" -import houses, { useHouse } from "~/design/state/houses" -import { - getVanillaColumnLength, - useGetVanillaModule, - vanillaColumns, -} from "../vanilla" -import layoutsDB, { serializeLayoutKey } from "../../../db/layouts" -import { suspend } from "suspend-react" -import { - ColumnLayout, - PositionedRow, - splitColumns, -} from "../../../workers/layouts" -import { columnLayoutToDNA, layouts } from "../layouts" - -export type StretchLength = { - direction: 1 | -1 - dx: number - dz: number - distance: number -} - -export const stretchLengthRaw = proxy>({}) -export const stretchLengthClamped = proxy>({}) - -export const useStretchLength = ({ - houseId, - layout, - startRef, - endRef, -}: { - houseId: string - layout: ColumnLayout - startRef: MutableRefObject - endRef: MutableRefObject -}) => { - const systemId = houses[houseId].systemId - - const { startColumn, endColumn, midColumns } = splitColumns(layout) - - const getVanillaModule = useGetVanillaModule(houses[houseId].systemId) - - const { position, rotation } = useHouse(houseId) - - const vanillaColumn = pipe( - startColumn.gridGroups, - A.map( - ({ levelIndex, levelType, y, modules: [{ module }] }): PositionedRow => { - const vanillaModule = getVanillaModule(module, { - constrainGridType: false, - positionType: "MID", - }) - return { - modules: [ - { - module: vanillaModule, - gridGroupIndex: 0, - z: 0, - }, - ], - length: vanillaModule.length, - y, - levelIndex, - levelType, - } - } - ) - ) - - vanillaColumns[houseId] = vanillaColumn - - const vanillaColumnLength = getVanillaColumnLength(vanillaColumn) - - const { - length: houseLength, - width: houseWidth, - height: houseHeight, - } = useHouseDimensionsUpdates(houseId) - - const computeMatrix = usePostTransMatrix(houseId) - - const maxLength = 25 - const maxCount = floor(max(0, maxLength - houseLength) / vanillaColumnLength) - const maxColumnZs = pipe( - NEA.range(0, maxCount - 1), - RA.map((i) => i * vanillaColumnLength) - ) - - const columnZsUp = useMemo( - () => - pipe( - maxColumnZs, - RA.takeLeftWhile((columnZ) => { - const center = new Vector3(0, 0, 0) - - const halfSize = new Vector3( - houseWidth / 2, - houseHeight / 2, - vanillaColumnLength / 2 - ) - - const obb = new OBB(center, halfSize) - - const houseMatrix = computeMatrix({ - y: houseHeight / 2, - z: houseLength / 2 + columnZ, - }) - - obb.applyMatrix4(houseMatrix) - - const collision = collideOBB(obb, [houseId]) - - return !collision - }) - ), - // eslint-disable-next-line react-hooks/exhaustive-deps - [ - computeMatrix, - houseHeight, - houseId, - houseLength, - houseWidth, - maxColumnZs, - vanillaColumnLength, - position, - rotation, - ] - ) - const maxStretchLengthUp = columnZsUp?.[columnZsUp.length - 1] ?? 0 - - const columnZsDown = useMemo( - () => - pipe( - maxColumnZs, - RA.map((x) => -1 * x), - RA.takeLeftWhile((columnZ) => { - const center = new Vector3(0, 0, 0) - - const halfSize = new Vector3( - houseWidth / 2, - houseHeight / 2, - vanillaColumnLength / 2 - ) - - const obb = new OBB(center, halfSize) - - const houseMatrix = computeMatrix({ - y: houseHeight / 2, - z: -(houseLength / 2) + columnZ, - }) - - obb.applyMatrix4(houseMatrix) - - // renderOBB(obb, houseMatrix) - const collision = collideOBB(obb, [houseId]) - - return !collision - }) - ), - [ - computeMatrix, - houseHeight, - houseId, - houseLength, - houseWidth, - maxColumnZs, - vanillaColumnLength, - ] - ) - const maxStretchLengthDown = columnZsDown?.[columnZsDown.length - 1] ?? 0 - - const columnsUp = pipe( - columnZsUp, - RA.map((columnZ) => ( - - - - )), - (columns) => ( - {columns} - ) - ) - - const columnsDown = pipe( - columnZsDown, - RA.map((columnZ) => ( - - - - )), - (columns) => {columns} - ) - - useSubscribeKey(stretchLengthRaw, houseId, () => { - if (stretchLengthRaw[houseId]) { - const { distance, direction, dx, dz } = stretchLengthRaw[houseId] - - const { length: houseLength, width: houseWidth } = dimensions[houseId] - - switch (direction) { - case 1: { - const clamped = - -distance > houseLength || distance > maxStretchLengthUp - if (!clamped) { - endRef.current.position.set(0, 0, distance) - stretchLengthClamped[houseId] = { - direction, - distance, - dx, - dz, - } - } - break - } - - case -1: { - const clamped = - distance > houseLength || distance < maxStretchLengthDown - if (!clamped) { - startRef.current.position.set(0, 0, distance) - stretchLengthClamped[houseId] = { - direction, - distance, - dx, - dz, - } - } - break - } - } - } else { - startRef.current.position.set(0, 0, 0) - endRef.current.position.set(0, 0, 0) - } - invalidate() - }) - - return { - startColumn, - endColumn, - vanillaColumn, - midColumns, - columnsUp, - columnsDown, - maxStretchLengthDown, - maxStretchLengthUp, - } -} - -export const setStretchLength = () => { - for (let houseId of Object.keys(stretchLengthClamped)) { - const { systemId, dnas } = houses[houseId] - const layoutsKey = serializeLayoutKey({ systemId, dnas }) - const layout = layouts[layoutsKey] - const { startColumn, midColumns, endColumn } = splitColumns(layout) - const vanillaColumn = vanillaColumns[houseId] - const vanillaColumnLength = getVanillaColumnLength(vanillaColumn) - const { direction, distance } = stretchLengthClamped[houseId] - - const delta = round(distance / vanillaColumnLength) - - const dxdz = new Vector3(0, 0, delta * vanillaColumnLength) - dxdz.applyAxisAngle(yAxis, houses[houseId].rotation) - const { x: dx, z: dz } = dxdz - - const { x, y, z } = houses[houseId].position - - switch (direction) { - case 1: { - if (sign(delta) === 1) { - houses[houseId] = { - ...houses[houseId], - dnas: columnLayoutToDNA([ - startColumn, - ...midColumns, - ...A.replicate(delta, { - gridGroups: vanillaColumn, - }), - endColumn, - ]), - position: { - x: x + dx / 2, - y, - z: z + dz / 2, - }, - } - } else if (sign(delta) === -1) { - houses[houseId] = { - ...houses[houseId], - dnas: columnLayoutToDNA([ - startColumn, - ...midColumns.slice(0, midColumns.length + delta), - endColumn, - ]), - position: { - x: x + dx / 2, - y, - z: z + dz / 2, - }, - } - } - break - } - case -1: { - if (sign(delta) === -1) { - const { x, y, z } = houses[houseId].position - houses[houseId] = { - ...houses[houseId], - dnas: columnLayoutToDNA([ - startColumn, - ...A.replicate(-delta, { - gridGroups: vanillaColumn, - }), - ...midColumns, - endColumn, - ]), - position: { - x: x + dx / 2, - y, - z: z + dz / 2, - }, - } - } else if (sign(delta) === 1) { - houses[houseId] = { - ...houses[houseId], - dnas: columnLayoutToDNA([ - startColumn, - ...midColumns.slice(0, midColumns.length - delta), - endColumn, - ]), - position: { - x: x + dx / 2, - y, - z: z + dz / 2, - }, - } - } - break - } - } - - delete stretchLengthRaw[houseId] - delete stretchLengthClamped[houseId] - } -} - -export const useStretchLengthStartEndColumn = ({ - houseId, - columnGroupRef, - start, - end, - columnZ, - columnLength, -}: { - houseId: string - columnGroupRef: MutableRefObject - start: boolean - end: boolean - columnZ: number - columnLength: number -}) => { - useSubscribeKey( - stretchLengthClamped, - houseId, - () => { - if (!stretchLengthClamped[houseId] || start || end) { - columnGroupRef.current?.scale.set(1, 1, 1) - return - } - - const { distance, direction } = stretchLengthClamped[houseId] - - const { length: houseLength } = dimensions[houseId] - - if (direction === 1 && houseLength + distance < columnZ) { - columnGroupRef.current?.scale.set(0, 0, 0) - } else if (direction === -1 && distance + columnLength > columnZ) { - columnGroupRef.current?.scale.set(0, 0, 0) - } else { - columnGroupRef.current?.scale.set(1, 1, 1) - } - }, - true - ) -} diff --git a/app/design/ui-3d/grouped/GroupedApp.tsx b/app/design/ui-3d/grouped/GroupedApp.tsx index 7157ea2d..282539d8 100644 --- a/app/design/ui-3d/grouped/GroupedApp.tsx +++ b/app/design/ui-3d/grouped/GroupedApp.tsx @@ -25,7 +25,7 @@ const GroupedApp = () => { R.toArray, A.map(([houseId, house]) => ( - + )) )} diff --git a/app/design/ui-3d/grouped/GroupedColumn.tsx b/app/design/ui-3d/grouped/GroupedColumn.tsx index ecda5584..afe3867c 100644 --- a/app/design/ui-3d/grouped/GroupedColumn.tsx +++ b/app/design/ui-3d/grouped/GroupedColumn.tsx @@ -1,10 +1,9 @@ import { indicesToKey } from "~/design/state/layouts" import { GroupProps } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" -import { forwardRef, useRef } from "react" +import { forwardRef, memo, useRef } from "react" import mergeRefs from "react-merge-refs" import { Group } from "three" -import { useStretchLengthStartEndColumn } from "~/design/state/transients/stretchLength" import { RA } from "~/utils/functions" import GroupedModule from "./GroupedModule" import { PositionedColumn } from "../../../workers/layouts" @@ -21,21 +20,21 @@ const GroupedColumn = forwardRef((props, ref) => { const { systemId, houseId, - column: { gridGroups, columnIndex, z: columnZ, length: columnLength }, + column: { gridGroups, columnIndex, z: columnZ }, start = false, end = false, } = props const columnGroupRef = useRef(null!) - useStretchLengthStartEndColumn({ - houseId, - columnGroupRef, - columnZ, - columnLength, - start, - end, - }) + // useStretchLengthStartEndColumn({ + // houseId, + // columnGroupRef, + // columnZ, + // columnLength, + // start, + // end, + // }) const mergedRef = mergeRefs([ref, columnGroupRef]) @@ -83,4 +82,4 @@ const GroupedColumn = forwardRef((props, ref) => { ) }) -export default GroupedColumn +export default memo(GroupedColumn) diff --git a/app/design/ui-3d/grouped/GroupedHouse.tsx b/app/design/ui-3d/grouped/GroupedHouse.tsx index f0050843..9d80e746 100644 --- a/app/design/ui-3d/grouped/GroupedHouse.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse.tsx @@ -4,7 +4,7 @@ import { Group } from "three" import { useHouseMaterialOps } from "~/design/state/hashedMaterials" import { useHouseElementOutline } from "~/design/state/highlights" import { useDnasLayout } from "~/design/state/layouts" -import { useStretchLength } from "~/design/state/transients/stretchLength" +// import { useStretchLength } from "~/design/state/transients/stretchLength" import { usePostTransformsTransients, usePreTransformsTransients, @@ -38,8 +38,8 @@ const GroupedHouse = (props: Props) => { const layout = useDnasLayout({ systemId, dnas }) - const { startColumn, midColumns, endColumn, columnsUp, columnsDown } = - useStretchLength({ houseId, layout, startRef, endRef }) + // const { startColumn, midColumns, endColumn, columnsUp, columnsDown } = + // useStretchLength({ houseId, layout, startRef, endRef }) const startColumnRef = useRef(null) const midColumnsRef = useRef(null) @@ -76,68 +76,70 @@ const GroupedHouse = (props: Props) => { layoutsKey: serializeLayoutKey({ systemId, dnas }), }) - return ( - - - - - - - {pipe( - midColumns, - RA.map((column) => ( - - )) - )} - - - - - - - - - - - - {columnsUp} - {columnsDown} - - - - - ) + return null + + // return ( + // + // + // + // + // + // + // {pipe( + // midColumns, + // RA.map((column) => ( + // + // )) + // )} + // + // + // + // + // + + // + + // + + // + // {columnsUp} + // {columnsDown} + // + + // + // + // ) } export default GroupedHouse diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 5cf8e4b3..fb70d618 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -14,14 +14,13 @@ import { useMoveHouseListener, } from "../../state/events" import { useHouseMaterialOps } from "../../state/hashedMaterials" -import houses from "../../state/houses" +import houses, { useSetHouse } from "../../state/houses" import ZStretch from "../../state/interactions/ZStretch" import { useDnasLayout } from "../../state/layouts" import { useTransformabilityBooleans } from "../../state/siteCtx" import RotateHandles from "../handles/RotateHandles" import StretchHandle from "../handles/StretchHandle" import GroupedColumn from "./GroupedColumn" -import StretchWidth from "./stretchWidth/StretchWidth" type Props = { house: House @@ -52,8 +51,8 @@ const GroupedHouse2 = (props: Props) => { const reactHouseMatrix = translationMatrix.multiply(rotationMatrix) const rootRef = useRef(null) - const startRef = useRef(null!) - const endRef = useRef(null!) + const startRef = useRef(null) + const endRef = useRef(null) const { stretchEnabled, moveRotateEnabled } = useTransformabilityBooleans(houseId) @@ -98,9 +97,6 @@ const GroupedHouse2 = (props: Props) => { const { startColumn, endColumn, midColumns } = splitColumns(layout) - // const ZStretch = ZStretch({ - // }) - const obbBox = useMemo(() => { const box3 = new Box3().setFromCenterAndSize( obb.center, @@ -121,6 +117,7 @@ const GroupedHouse2 = (props: Props) => { if (!rootRef.current) return rootRef.current.matrixAutoUpdate = false rootRef.current.matrix.copy(reactHouseMatrix) + invalidate() }, [reactHouseMatrix]) const frameOBB = useRef(new OBB()) @@ -144,25 +141,29 @@ const GroupedHouse2 = (props: Props) => { dispatchMoveHouse(detail) }) + const setHouse = useSetHouse(houseId) + useMoveHouseListener((detail) => { if (!rootRef.current || houseId !== detail.houseId) return rootRef.current.matrix.copy(frameHouseMatrix.current) rootRef.current.updateMatrixWorld() - invalidate() if (detail.last) { const { x, y, z } = detail.delta - houses[houseId].position = { - x: position.x + x, - y: position.y + y, - z: position.z + z, - } + setHouse({ + ...house, + position: { + x: position.x + x, + y: position.y + y, + z: position.z + z, + }, + }) } - }) - // const { columnsUp, columnsDown } = useStretchLength({ startColumn, endColumn, midColumns }) + invalidate() + }) useHouseMaterialOps({ houseId, @@ -229,11 +230,11 @@ const GroupedHouse2 = (props: Props) => { /> - + /> */} { length, startColumn, endColumn, + midColumns, startRef, endRef, }} @@ -261,10 +263,11 @@ const GroupedHouse2 = (props: Props) => { {/* */} - + + {/* - + */} ) } diff --git a/app/design/ui-3d/grouped/GroupedModule.tsx b/app/design/ui-3d/grouped/GroupedModule.tsx index a2633c2d..7ad2e2ce 100644 --- a/app/design/ui-3d/grouped/GroupedModule.tsx +++ b/app/design/ui-3d/grouped/GroupedModule.tsx @@ -1,8 +1,8 @@ import { Module } from "@/server/data/modules" import { pipe } from "fp-ts/lib/function" -import { useModuleElements } from "~/data/elements" +import { useSpeckleObject } from "~/data/elements" import { indicesToKey } from "~/design/state/layouts" -import { R, S } from "~/utils/functions" +import { O, R, S } from "~/utils/functions" import { SystemHouseModuleIdentifier } from "../../../workers/layouts" import GroupedElement from "./GroupedElement" @@ -22,16 +22,18 @@ const GroupedModule = (props: ModuleProps) => { levelIndex, gridGroupIndex, module, + module: { speckleBranchUrl }, levelY, moduleZ, startColumn, endColumn, } = props - const elements = useModuleElements(module) + const elements = useSpeckleObject(speckleBranchUrl) const children = pipe( elements, + O.getOrElse(() => ({})), R.collect(S.Ord)((elementName, geometry) => { const key = indicesToKey({ houseId, diff --git a/app/design/ui-3d/grouped/preview/PreviewModule.tsx b/app/design/ui-3d/grouped/preview/PreviewModule.tsx index 22c164c8..618766bf 100644 --- a/app/design/ui-3d/grouped/preview/PreviewModule.tsx +++ b/app/design/ui-3d/grouped/preview/PreviewModule.tsx @@ -1,8 +1,8 @@ import { Module } from "@/server/data/modules" import { pipe } from "fp-ts/lib/function" -import { useModuleElements } from "~/data/elements" +import { useSpeckleObject } from "~/data/elements" import { indicesToKey } from "~/design/state/layouts" -import { R, S } from "~/utils/functions" +import { O, R, S } from "~/utils/functions" import { SystemHouseModuleIdentifier } from "../../../../workers/layouts" import PreviewElement from "./PreviewElement" @@ -22,16 +22,18 @@ const PreviewModule = (props: PreviewModuleProps) => { levelIndex, gridGroupIndex, module, + module: { speckleBranchUrl }, levelY, moduleZ, startColumn, endColumn, } = props - const elements = useModuleElements(module) + const elements = useSpeckleObject(speckleBranchUrl) const children = pipe( elements, + O.getOrElse(() => ({})), R.collect(S.Ord)((elementName, geometry) => { const key = indicesToKey({ houseId, diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx index 744dc75f..a4b7606c 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx @@ -1,12 +1,11 @@ -import { RA } from "~/utils/functions" +import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Group } from "three" -import { stretchLengthClamped } from "~/design/state/transients/stretchLength" -import { useSubscribeKey } from "~/utils/hooks" -import GroupedStretchModule from "./GroupedStretchModule" +import { RA } from "~/utils/functions" import { GridGroup } from "../../../../workers/layouts" import { useZStretchHouseListener } from "../../../state/events" +import GroupedStretchModule from "./GroupedStretchModule" type Props = { systemId: string @@ -41,6 +40,8 @@ const GroupedStretchColumn = (props: Props) => { } else { groupRef.current?.scale.set(0, 0, 0) } + + invalidate() }) return ( diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx index 29052c71..2dcd5d28 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx @@ -1,8 +1,8 @@ import { Module } from "@/server/data/modules" import { pipe } from "fp-ts/lib/function" import { Fragment } from "react" -import { useModuleElements } from "~/data/elements" -import { R, S } from "~/utils/functions" +import { useSpeckleObject } from "~/data/elements" +import { O, R, S } from "~/utils/functions" import { SystemHouseModuleIdentifier } from "../../../../workers/layouts" import GroupedStretchElement from "./GroupedStretchElement" @@ -15,13 +15,21 @@ export type StretchModuleProps = Omit< } const GroupedStretchModule = (props: StretchModuleProps) => { - const { systemId, houseId, levelIndex, gridGroupIndex, module, levelY } = - props + const { + systemId, + houseId, + levelIndex, + gridGroupIndex, + module, + module: { speckleBranchUrl }, + levelY, + } = props - const elements = useModuleElements(module) + const elements = useSpeckleObject(speckleBranchUrl) const children = pipe( elements, + O.getOrElse(() => ({})), R.collect(S.Ord)((elementName, geometry) => { return ( { houseType={houseType} onAdd={() => { const id = nanoid() - const position = + const position = ref( cameraGroundRaycast() ?? new Vector3(0, 0, 0) + ) - houses[id] = { + houses[id] = ref({ id, houseTypeId: houseType.id, systemId: houseType.systemId, @@ -87,7 +88,7 @@ const SiteSidebar = ({ open, close }: Props) => { friendlyName: `Building ${ Object.keys(houses).length + 1 }`, - } + }) close() }} diff --git a/app/workers/layouts.ts b/app/workers/layouts.ts index c329c7db..27347d7f 100644 --- a/app/workers/layouts.ts +++ b/app/workers/layouts.ts @@ -63,6 +63,28 @@ export type SystemHouseModuleIdentifier = HouseModuleIdentifier & { } export type ColumnLayout = Array + +const getSpeckleModelGeometries = async (speckleBranchUrl: string) => { + const speckleObjectData = await getSpeckleObject(speckleBranchUrl) + const speckleObject = speckleIfcParser.parse(speckleObjectData) + + return pipe( + speckleObject, + A.reduce( + {}, + (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { + return produce(acc, (draft) => { + if (ifcTag in draft) draft[ifcTag].push(geometry) + else draft[ifcTag] = [geometry] + }) + } + ), + R.map((geoms) => mergeBufferGeometries(geoms)), + R.filter((bg: BufferGeometry | null): bg is BufferGeometry => Boolean(bg)), + R.map((x) => x.toJSON()) + ) +} + const syncModels = (modules: LastFetchStamped[]) => { modules.map(async (nextModule) => { const { speckleBranchUrl, lastFetched } = nextModule @@ -72,25 +94,7 @@ const syncModels = (modules: LastFetchStamped[]) => { return } - const speckleObjectData = await getSpeckleObject(speckleBranchUrl) - const speckleObject = speckleIfcParser.parse(speckleObjectData) - const geometries = pipe( - speckleObject, - A.reduce( - {}, - (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { - return produce(acc, (draft) => { - if (ifcTag in draft) draft[ifcTag].push(geometry) - else draft[ifcTag] = [geometry] - }) - } - ), - R.map((geoms) => mergeBufferGeometries(geoms)), - R.filter((bg: BufferGeometry | null): bg is BufferGeometry => - Boolean(bg) - ), - R.map((x) => x.toJSON()) - ) + const geometries = await getSpeckleModelGeometries(speckleBranchUrl) layoutsDB.models.put({ speckleBranchUrl, @@ -104,6 +108,14 @@ const syncModels = (modules: LastFetchStamped[]) => { let modulesCache: LastFetchStamped[] = [] let layoutsQueue: LayoutKey[] = [] +// const getSpeckleModel = async (module: Module) => { +// const { speckleBranchUrl } = module +// const maybeModel = await layoutsDB.models.get(speckleBranchUrl) +// if (maybeModel && maybeModel.lastFetched === lastFetched) { +// return +// } +// } + const modulesToRows = (modules: Module[]): Module[][] => { const jumpIndices = pipe( modules, @@ -352,7 +364,7 @@ const modulesToColumnLayout = (modules: Module[]) => { export const splitColumns = (layout: ColumnLayout) => pipe( layout, - RA.partition( + A.partition( ({ columnIndex }) => columnIndex === 0 || columnIndex === layout.length - 1 ), @@ -383,6 +395,7 @@ const getVanillaModule = ( modulesCache, A.filter((sysModule) => all( + sysModule.systemId === module.systemId, sectionType ? sysModule.structuredDna.sectionType === sectionType : sysModule.structuredDna.sectionType === @@ -520,6 +533,7 @@ const api = { postLayout, postLayouts, processLayout, + syncModels, } export type LayoutsAPI = typeof api From edf1e4d33c4c83db82b73afcd07785af1f734b7c Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Sun, 9 Jul 2023 15:05:20 +0100 Subject: [PATCH 031/132] wip messier but builds --- app/build/materials/page.tsx | 6 +- app/db/layouts.ts | 64 ++++- app/db/systems.ts | 8 + app/debug/system/app.tsx | 21 ++ app/debug/system/page.tsx | 19 +- app/design/state/interactions/ZStretch.tsx | 29 ++- app/design/state/interactions/layouts.ts | 14 +- app/design/state/interactions/windows.ts | 8 +- app/design/state/layouts.ts | 45 +--- app/design/state/vanilla.ts | 3 +- app/design/state/verticalCutPlanes.ts | 2 +- app/design/ui-3d/grouped/GroupedColumn.tsx | 2 +- app/design/ui-3d/grouped/GroupedHouse2.tsx | 2 +- app/design/ui-3d/grouped/GroupedModule.tsx | 2 +- .../ui-3d/grouped/preview/PreviewColumn.tsx | 2 +- .../ui-3d/grouped/preview/PreviewModule.tsx | 2 +- .../stretchLength/GroupedStretchColumn.tsx | 27 +- .../stretchLength/GroupedStretchModule.tsx | 2 +- .../grouped/stretchWidth/StretchWidth.tsx | 6 +- .../ui/menu/interactions/AddRemoveLevels.tsx | 2 +- .../ui/menu/interactions/ChangeLayouts.tsx | 2 +- .../ui/menu/interactions/ChangeWindows.tsx | 2 +- app/workers/index.ts | 6 +- app/workers/layouts/models.ts | 51 ++++ app/workers/layouts/vanilla.ts | 114 +++++++++ app/workers/{layouts.ts => layouts/worker.ts} | 242 ++++++------------ app/workers/systems.ts | 50 ++++ server/data/levelTypes.ts | 18 +- server/data/sectionTypes.ts | 20 +- 29 files changed, 512 insertions(+), 259 deletions(-) create mode 100644 app/debug/system/app.tsx create mode 100644 app/workers/layouts/models.ts create mode 100644 app/workers/layouts/vanilla.ts rename app/workers/{layouts.ts => layouts/worker.ts} (69%) diff --git a/app/build/materials/page.tsx b/app/build/materials/page.tsx index 62c0469c..7469f108 100644 --- a/app/build/materials/page.tsx +++ b/app/build/materials/page.tsx @@ -1,7 +1,11 @@ "use client" import { Download } from "@carbon/icons-react" +import dynamic from "next/dynamic" import { useState } from "react" -import MaterialsListTable from "./MaterialsListTable" + +const MaterialsListTable = dynamic(() => import("./MaterialsListTable"), { + ssr: false, +}) const MaterialsListIndexPage = () => { const [csvDownloadUrl, setCsvDownloadUrl] = useState(null) diff --git a/app/db/layouts.ts b/app/db/layouts.ts index 6e06d9cc..f1a8910f 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -1,7 +1,57 @@ import Dexie from "dexie" -import { ColumnLayout, PositionedColumn } from "../workers/layouts" +import { Module } from "../../server/data/modules" import { LastFetchStamped } from "./systems" +export type PositionedModule = { + module: Module + z: number + gridGroupIndex: number +} + +export type PositionedInstancedModule = { + module: Module + y: number + z: number + columnIndex: number + levelIndex: number + gridGroupIndex: number +} + +export type PositionedRow = { + levelIndex: number + levelType: string + y: number + modules: Array + length: number +} + +export type GridGroup = PositionedRow + +export type RowLayout = Array + +export type PositionedColumn = { + gridGroups: Array + z: number + columnIndex: number + length: number +} + +export type ModuleIdentifier = { + columnIndex: number + levelIndex: number + gridGroupIndex: number +} + +export type HouseModuleIdentifier = ModuleIdentifier & { + houseId: string +} + +export type SystemHouseModuleIdentifier = HouseModuleIdentifier & { + systemId: string +} + +export type ColumnLayout = Array + export type ParsedModel = { speckleBranchUrl: string geometries: any @@ -18,6 +68,15 @@ export type LayoutKey = { dnas: string[] } +export type IndexedVanillaModule = { + systemId: string + sectionType: string + positionType: string + levelType: string + gridType: string + moduleDna: string +} + export type VanillaColumn = Omit export type IndexedVanillaColumn = { @@ -31,6 +90,7 @@ export const serializeLayoutKey = ({ systemId, dnas }: LayoutKey) => class LayoutsDatabase extends Dexie { models: Dexie.Table, string> layouts: Dexie.Table + vanillaModules: Dexie.Table vanillaColumns: Dexie.Table constructor() { @@ -38,10 +98,12 @@ class LayoutsDatabase extends Dexie { this.version(1).stores({ models: "speckleBranchUrl,systemId", layouts: "layoutsKey", + vanillaModules: "[systemId+sectionType+positionType+levelType+gridType]", vanillaColumns: "layoutsKey", }) this.layouts = this.table("layouts") this.models = this.table("models") + this.vanillaModules = this.table("vanillaModules") this.vanillaColumns = this.table("vanillaColumns") } } diff --git a/app/db/systems.ts b/app/db/systems.ts index eb8a01eb..8490b4ce 100644 --- a/app/db/systems.ts +++ b/app/db/systems.ts @@ -3,6 +3,8 @@ import { Module } from "@/server/data/modules" import Dexie from "dexie" import { Element } from "../../server/data/elements" import { HouseType } from "../../server/data/houseTypes" +import { LevelType } from "../../server/data/levelTypes" +import { SectionType } from "../../server/data/sectionTypes" export type LastFetchStamped = T & { lastFetched: number @@ -12,6 +14,8 @@ class SystemsDatabase extends Dexie { modules: Dexie.Table, string> houseTypes: Dexie.Table, string> elements: Dexie.Table, string> + sectionTypes: Dexie.Table, string> + levelTypes: Dexie.Table, string> blocks: Dexie.Table constructor() { @@ -21,11 +25,15 @@ class SystemsDatabase extends Dexie { modules: "id,systemId,dna", houseTypes: "id,systemId", elements: "id,systemId", + sectionTypes: "id,systemId", + levelTypes: "id,systemId", }) this.modules = this.table("modules") this.houseTypes = this.table("houseTypes") this.blocks = this.table("blocks") this.elements = this.table("elements") + this.sectionTypes = this.table("sectionTypes") + this.levelTypes = this.table("levelTypes") } } diff --git a/app/debug/system/app.tsx b/app/debug/system/app.tsx new file mode 100644 index 00000000..905a5fb0 --- /dev/null +++ b/app/debug/system/app.tsx @@ -0,0 +1,21 @@ +"use client" +import DataInit from "~/data/DataInit" +import AppInit from "~/design/ui-3d/init/AppInit" +import { TrpcProvider } from "../../ui/TrpcProvider" +import Leva from "../ui/Leva" +import DebugSystemApp from "./ui-3d/DebugSystemApp" + +const DebugApp = () => { + return ( + + + + + + + + + ) +} + +export default DebugApp diff --git a/app/debug/system/page.tsx b/app/debug/system/page.tsx index a94dca90..5de640e9 100644 --- a/app/debug/system/page.tsx +++ b/app/debug/system/page.tsx @@ -1,25 +1,10 @@ "use client" import dynamic from "next/dynamic" -import DataInit from "~/data/DataInit" -import AppInit from "~/design/ui-3d/init/AppInit" -import { TrpcProvider } from "../../ui/TrpcProvider" -import Leva from "../ui/Leva" -const DebugSystemApp = dynamic(() => import("./ui-3d/DebugSystemApp"), { - ssr: false, -}) +const DebugApp = dynamic(() => import("./app"), { ssr: false }) const IndexPage = () => { - return ( - - - - - - - - - ) + return } export default IndexPage diff --git a/app/design/state/interactions/ZStretch.tsx b/app/design/state/interactions/ZStretch.tsx index b1d5536d..a8eb1e90 100644 --- a/app/design/state/interactions/ZStretch.tsx +++ b/app/design/state/interactions/ZStretch.tsx @@ -5,12 +5,16 @@ import { Fragment, memo, RefObject, useMemo } from "react" import { suspend } from "suspend-react" import { Group, Matrix4, Vector3 } from "three" import { OBB } from "three-stdlib" -import layoutsDB, { LayoutKey, serializeLayoutKey } from "../../../db/layouts" +import layoutsDB, { + LayoutKey, + PositionedColumn, + serializeLayoutKey, +} from "../../../db/layouts" import { A, NEA } from "../../../utils/functions" import { floor, max, round, sign } from "../../../utils/math" import { yAxis } from "../../../utils/three" import { getLayoutsWorker } from "../../../workers" -import { PositionedColumn } from "../../../workers/layouts" +import { columnLayoutToDnas } from "../../../workers/layouts/worker" import GroupedStretchColumn from "../../ui-3d/grouped/stretchLength/GroupedStretchColumn" import { collideOBB } from "../dimensions" import { @@ -19,7 +23,6 @@ import { useZStretchHouseListener, } from "../events" import houses, { useSetHouse } from "../houses" -import { columnLayoutToDNA } from "../layouts" import { vanillaColumns } from "../vanilla" type Props = { @@ -152,9 +155,11 @@ const ZStretch = ({ ) const maxStretchLengthDown = columnZsDown?.[columnZsDown.length - 1] ?? 0 + console.log([...columnZsUp, ...columnZsUp]) + const columnsUp = pipe( columnZsUp, - A.map((columnZ) => ( + A.mapWithIndex((i, columnZ) => ( @@ -176,7 +183,7 @@ const ZStretch = ({ const columnsDown = pipe( columnZsDown, - A.map((columnZ) => ( + A.mapWithIndex((i, columnZ) => ( @@ -201,6 +210,8 @@ const ZStretch = ({ const { distance, direction, dx, dz, last } = detail + console.log(distance) + switch (direction) { case 1: { const clamped = -distance > length || distance > maxStretchLengthUp @@ -246,7 +257,7 @@ const ZStretch = ({ if (sign(delta) === 1) { setHouse({ ...houses[houseId], - dnas: columnLayoutToDNA([ + dnas: columnLayoutToDnas([ startColumn, ...midColumns, ...A.replicate(delta, { @@ -263,7 +274,7 @@ const ZStretch = ({ } else if (sign(delta) === -1) { setHouse({ ...houses[houseId], - dnas: columnLayoutToDNA([ + dnas: columnLayoutToDnas([ startColumn, ...midColumns.slice(0, midColumns.length + delta), endColumn, @@ -282,7 +293,7 @@ const ZStretch = ({ const { x, y, z } = houses[houseId].position houses[houseId] = { ...houses[houseId], - dnas: columnLayoutToDNA([ + dnas: columnLayoutToDnas([ startColumn, ...A.replicate(-delta, { gridGroups: vanillaColumn.gridGroups, @@ -299,7 +310,7 @@ const ZStretch = ({ } else if (sign(delta) === 1) { houses[houseId] = { ...houses[houseId], - dnas: columnLayoutToDNA([ + dnas: columnLayoutToDnas([ startColumn, ...midColumns.slice(0, midColumns.length - delta), endColumn, diff --git a/app/design/state/interactions/layouts.ts b/app/design/state/interactions/layouts.ts index 913944ac..bff20c6d 100644 --- a/app/design/state/interactions/layouts.ts +++ b/app/design/state/interactions/layouts.ts @@ -12,19 +12,19 @@ import houses from "~/design/state/houses" import { useGetVanillaModule } from "~/design/state/vanilla" import { layouts, - columnLayoutToDNA, columnLayoutToMatrix, columnMatrixToDna, } from "~/design/state/layouts" import { StairType } from "@/server/data/stairTypes" import { Module } from "@/server/data/modules" import { useSystemStairTypes } from "~/data/stairTypes" -import { serializeLayoutKey } from "../../../db/layouts" import { ColumnLayout, HouseModuleIdentifier, PositionedModule, -} from "../../../workers/layouts" + serializeLayoutKey, +} from "../../../db/layouts" +import { columnLayoutToDnas } from "../../../workers/layouts/worker" export const useChangeModuleLayout = ({ houseId, @@ -76,7 +76,7 @@ export const useChangeModuleLayout = ({ ), ] }), - columnLayoutToDNA + columnLayoutToDnas ) ) } @@ -112,7 +112,7 @@ export const useChangeModuleLayout = ({ }) }) }), - columnLayoutToDNA + columnLayoutToDnas ) } @@ -126,7 +126,7 @@ export const useChangeModuleLayout = ({ gridGroupIndex ].module.dna = newModule.dna }), - columnLayoutToDNA + columnLayoutToDnas ) as string[] } } @@ -225,7 +225,7 @@ export const useStairsOptions = ({ const selected: StairsOpt["value"] = { stairType: m.structuredDna.stairsType, - houseDna: columnLayoutToDNA(layout), + houseDna: columnLayoutToDnas(layout), } const columnMatrix = columnLayoutToMatrix(layout) diff --git a/app/design/state/interactions/windows.ts b/app/design/state/interactions/windows.ts index 87d451b8..db605a79 100644 --- a/app/design/state/interactions/windows.ts +++ b/app/design/state/interactions/windows.ts @@ -6,12 +6,12 @@ import { WindowType } from "@/server/data/windowTypes" import { useSystemWindowTypes } from "~/data/windowTypes" import { A, O, S } from "~/utils/functions" import { getSide, Side } from "~/design/state/camera" -import { columnLayoutToDNA, layouts } from "~/design/state/layouts" +import { layouts } from "~/design/state/layouts" import siteCtx from "~/design/state/siteCtx" import { useChangeModuleLayout } from "./layouts" -import { serializeLayoutKey } from "../../../db/layouts" +import { HouseModuleIdentifier, serializeLayoutKey } from "../../../db/layouts" import houses from "../houses" -import { HouseModuleIdentifier } from "../../../workers/layouts" +import { columnLayoutToDnas } from "../../../workers/layouts/worker" export type WindowTypeOption = { label: string @@ -111,7 +111,7 @@ export const useWindowOptions = ({ const selected = pipe( options, A.findFirstMap(({ value }) => { - const houseDna = columnLayoutToDNA(layouts[layoutsKey]) + const houseDna = columnLayoutToDnas(layouts[layoutsKey]) return eq.equals(value.houseDna, houseDna) ? O.some(value) : O.none }), O.getOrElse(() => { diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 7077a9cd..99e3e610 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -8,15 +8,14 @@ import * as RA from "fp-ts/ReadonlyArray" import { suspend } from "suspend-react" import { proxy, ref, useSnapshot } from "valtio" import { usePadColumn } from "../../data/modules" -import layoutsDB, { LayoutKey, serializeLayoutKey } from "../../db/layouts" -import { isSSR } from "../../utils/next" -import { getLayoutsWorker } from "../../workers" -import { +import layoutsDB, { ColumnLayout, HouseModuleIdentifier, - PositionedColumn, - RowLayout, -} from "../../workers/layouts" + LayoutKey, + serializeLayoutKey, +} from "../../db/layouts" +import { isSSR } from "../../utils/next" +import { getLayoutsWorker } from "../../workers" import { useHouse } from "./houses" export const layouts = proxy< @@ -53,27 +52,6 @@ export const useDnasLayout = (layoutsKey: LayoutKey): ColumnLayout => { }, [maybeLayout]) } -export const columnLayoutToDNA = ( - columnLayout: Omit[] -) => - pipe( - columnLayout, - RA.map(({ gridGroups }) => - pipe( - gridGroups, - RA.map(({ modules }) => - pipe( - modules, - RA.map(({ module }) => module.dna) - ) - ) - ) - ), - transposeRA, - RA.flatten, - RA.flatten - ) as string[] - export const columnLayoutToMatrix = (columnLayout: ColumnLayout) => { return pipe( columnLayout, @@ -108,17 +86,6 @@ export const columnMatrixToDna = (columnMatrix: Module[][][]) => A.flatten ) -export const rowLayoutToMatrix = (rowLayout: RowLayout) => - pipe( - rowLayout, - A.map(({ modules }) => - pipe( - modules, - RA.map(({ module }) => module) - ) - ) - ) - export const rowMatrixToDna = (rowMatrix: Module[][]) => pipe( rowMatrix, diff --git a/app/design/state/vanilla.ts b/app/design/state/vanilla.ts index de859ab1..751abcea 100644 --- a/app/design/state/vanilla.ts +++ b/app/design/state/vanilla.ts @@ -4,9 +4,8 @@ import { pipe } from "fp-ts/lib/function" import { proxy, ref } from "valtio" import { A, all, O, Ord, RA, S, someOrError } from "~/utils/functions" import { useSystemModules } from "../../data/modules" -import layoutsDB, { VanillaColumn } from "../../db/layouts" +import layoutsDB, { PositionedRow, VanillaColumn } from "../../db/layouts" import { isSSR } from "../../utils/next" -import { PositionedRow } from "../../workers/layouts" export const vanillaColumns = proxy>({}) diff --git a/app/design/state/verticalCutPlanes.ts b/app/design/state/verticalCutPlanes.ts index 5bbc2217..b1a82d4c 100644 --- a/app/design/state/verticalCutPlanes.ts +++ b/app/design/state/verticalCutPlanes.ts @@ -7,7 +7,7 @@ import { useSnapshot } from "valtio" import { R } from "~/utils/functions" import houses from "~/design/state/houses" import { useVerticalCuts } from "~/design/state/settings" -import { ColumnLayout } from "../../workers/layouts" +import { ColumnLayout } from "../../db/layouts" export const useVerticalCutPlanes = ( columnLayout: ColumnLayout, diff --git a/app/design/ui-3d/grouped/GroupedColumn.tsx b/app/design/ui-3d/grouped/GroupedColumn.tsx index afe3867c..fb4e0ade 100644 --- a/app/design/ui-3d/grouped/GroupedColumn.tsx +++ b/app/design/ui-3d/grouped/GroupedColumn.tsx @@ -6,7 +6,7 @@ import mergeRefs from "react-merge-refs" import { Group } from "three" import { RA } from "~/utils/functions" import GroupedModule from "./GroupedModule" -import { PositionedColumn } from "../../../workers/layouts" +import { PositionedColumn } from "../../../db/layouts" type Props = GroupProps & { systemId: string diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index fb70d618..51cf0e7e 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -6,7 +6,7 @@ import { OBB } from "three-stdlib" import { LayoutKey, serializeLayoutKey } from "../../../db/layouts" import { House } from "../../../db/user" import { RA } from "../../../utils/functions" -import { splitColumns } from "../../../workers/layouts" +import { splitColumns } from "../../../workers/layouts/worker" import dimensions, { collideOBB, Dimensions } from "../../state/dimensions" import { dispatchMoveHouse, diff --git a/app/design/ui-3d/grouped/GroupedModule.tsx b/app/design/ui-3d/grouped/GroupedModule.tsx index 7ad2e2ce..ab73da75 100644 --- a/app/design/ui-3d/grouped/GroupedModule.tsx +++ b/app/design/ui-3d/grouped/GroupedModule.tsx @@ -3,7 +3,7 @@ import { pipe } from "fp-ts/lib/function" import { useSpeckleObject } from "~/data/elements" import { indicesToKey } from "~/design/state/layouts" import { O, R, S } from "~/utils/functions" -import { SystemHouseModuleIdentifier } from "../../../workers/layouts" +import { SystemHouseModuleIdentifier } from "../../../db/layouts" import GroupedElement from "./GroupedElement" export type ModuleProps = SystemHouseModuleIdentifier & { diff --git a/app/design/ui-3d/grouped/preview/PreviewColumn.tsx b/app/design/ui-3d/grouped/preview/PreviewColumn.tsx index ae827290..c483cd23 100644 --- a/app/design/ui-3d/grouped/preview/PreviewColumn.tsx +++ b/app/design/ui-3d/grouped/preview/PreviewColumn.tsx @@ -6,7 +6,7 @@ import { forwardRef, useRef } from "react" import mergeRefs from "react-merge-refs" import { Group } from "three" import PreviewModule from "./PreviewModule" -import { PositionedColumn } from "../../../../workers/layouts" +import { PositionedColumn } from "../../../../db/layouts" type Props = GroupProps & { systemId: string diff --git a/app/design/ui-3d/grouped/preview/PreviewModule.tsx b/app/design/ui-3d/grouped/preview/PreviewModule.tsx index 618766bf..3b6c4478 100644 --- a/app/design/ui-3d/grouped/preview/PreviewModule.tsx +++ b/app/design/ui-3d/grouped/preview/PreviewModule.tsx @@ -3,7 +3,7 @@ import { pipe } from "fp-ts/lib/function" import { useSpeckleObject } from "~/data/elements" import { indicesToKey } from "~/design/state/layouts" import { O, R, S } from "~/utils/functions" -import { SystemHouseModuleIdentifier } from "../../../../workers/layouts" +import { SystemHouseModuleIdentifier } from "../../../../db/layouts" import PreviewElement from "./PreviewElement" export type PreviewModuleProps = SystemHouseModuleIdentifier & { diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx index a4b7606c..24bacd69 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx @@ -1,9 +1,10 @@ import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" -import { useRef } from "react" +import { useEffect, useRef } from "react" import { Group } from "three" import { RA } from "~/utils/functions" -import { GridGroup } from "../../../../workers/layouts" +import { GridGroup, LayoutKey } from "../../../../db/layouts" +import { getLayoutsWorker } from "../../../../workers" import { useZStretchHouseListener } from "../../../state/events" import GroupedStretchModule from "./GroupedStretchModule" @@ -14,13 +15,33 @@ type Props = { direction: number columnZ: number columnLength: number + i: number + layoutKey: LayoutKey } const GroupedStretchColumn = (props: Props) => { - const { systemId, houseId, gridGroups: rows, columnZ, columnLength } = props + const { + systemId, + houseId, + gridGroups: rows, + columnZ, + columnLength, + i, + direction, + layoutKey, + } = props const groupRef = useRef(null) + useEffect(() => { + const layoutsWorker = getLayoutsWorker() + if (!layoutsWorker) { + console.log("no layouts workers") + return + } + layoutsWorker.processZStretchLayout({ direction, i, layoutKey }) + }, [direction, houseId, i, layoutKey]) + useZStretchHouseListener((detail) => { if (houseId !== detail.houseId) return diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx index 2dcd5d28..6c41c868 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchModule.tsx @@ -3,7 +3,7 @@ import { pipe } from "fp-ts/lib/function" import { Fragment } from "react" import { useSpeckleObject } from "~/data/elements" import { O, R, S } from "~/utils/functions" -import { SystemHouseModuleIdentifier } from "../../../../workers/layouts" +import { SystemHouseModuleIdentifier } from "../../../../db/layouts" import GroupedStretchElement from "./GroupedStretchElement" export type StretchModuleProps = Omit< diff --git a/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx b/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx index 8ea35eb5..4e7ce948 100644 --- a/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx +++ b/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx @@ -9,7 +9,6 @@ import { useSystemSectionTypes } from "~/data/sectionTypes" import dimensions, { useHouseDimensionsUpdates, } from "../../../state/dimensions" -import { columnLayoutToDNA } from "~/design/state/layouts" import { useGetVanillaModule } from "~/design/state/vanilla" import { A, @@ -38,7 +37,8 @@ import { GridGroup, PositionedColumn, PositionedModule, -} from "../../../../workers/layouts" +} from "../../../../db/layouts" +import { columnLayoutToDnas } from "../../../../workers/layouts/worker" export type StretchWidthRaw = { direction: 1 | -1 @@ -192,7 +192,7 @@ const StretchWidth = forwardRef((props, rootRef) => { ) ), O.map((columnLayout): AugSectionType => { - const houseDna = columnLayoutToDNA(columnLayout) + const houseDna = columnLayoutToDnas(columnLayout) return { ...st, dx: (st.width - houseWidth) / 2, diff --git a/app/design/ui/menu/interactions/AddRemoveLevels.tsx b/app/design/ui/menu/interactions/AddRemoveLevels.tsx index 0ee0b5b6..e751b136 100644 --- a/app/design/ui/menu/interactions/AddRemoveLevels.tsx +++ b/app/design/ui/menu/interactions/AddRemoveLevels.tsx @@ -14,7 +14,7 @@ import { useGetVanillaModule } from "~/design/state/vanilla" import { A, errorThrower, O } from "~/utils/functions" import { AddLevel, RemoveLevel } from "~/ui/icons" import ContextMenuButton from "../ContextMenuButton" -import { HouseModuleIdentifier } from "../../../../workers/layouts" +import { HouseModuleIdentifier } from "../../../../db/layouts" type Props = HouseModuleIdentifier & { onComplete?: () => void diff --git a/app/design/ui/menu/interactions/ChangeLayouts.tsx b/app/design/ui/menu/interactions/ChangeLayouts.tsx index c4e48a62..55c8ae18 100644 --- a/app/design/ui/menu/interactions/ChangeLayouts.tsx +++ b/app/design/ui/menu/interactions/ChangeLayouts.tsx @@ -8,7 +8,7 @@ import { Menu, Pencil } from "~/ui/icons" import Radio from "~/ui//Radio" import ContextMenuNested from "../ContextMenuNested" import houses from "../../../state/houses" -import { HouseModuleIdentifier } from "../../../../workers/layouts" +import { HouseModuleIdentifier } from "../../../../db/layouts" type Props = HouseModuleIdentifier & { onComplete?: () => void diff --git a/app/design/ui/menu/interactions/ChangeWindows.tsx b/app/design/ui/menu/interactions/ChangeWindows.tsx index e1e53b36..0c115e85 100644 --- a/app/design/ui/menu/interactions/ChangeWindows.tsx +++ b/app/design/ui/menu/interactions/ChangeWindows.tsx @@ -11,7 +11,7 @@ import { A } from "~/utils/functions" import { Opening } from "~/ui/icons" import Radio from "~/ui//Radio" import ContextMenuNested from "../ContextMenuNested" -import { HouseModuleIdentifier } from "../../../../workers/layouts" +import { HouseModuleIdentifier } from "../../../../db/layouts" type Props = HouseModuleIdentifier & { onComplete?: () => void diff --git a/app/workers/index.ts b/app/workers/index.ts index a73e53f5..1546964d 100644 --- a/app/workers/index.ts +++ b/app/workers/index.ts @@ -1,7 +1,7 @@ "use client" import { Remote, wrap } from "comlink" import { isSSR } from "../utils/next" -import type { LayoutsAPI } from "./layouts" +import type { LayoutsAPI } from "./layouts/worker" let systemsWorker: Worker | null = null let layoutsWorker: Remote | null = null @@ -15,7 +15,9 @@ export const getSystemsWorker = () => { export const getLayoutsWorker = () => { if (!isSSR() && layoutsWorker === null) { - layoutsWorker = wrap(new Worker(new URL("./layouts.ts", import.meta.url))) + layoutsWorker = wrap( + new Worker(new URL("./layouts/worker.ts", import.meta.url)) + ) } return layoutsWorker } diff --git a/app/workers/layouts/models.ts b/app/workers/layouts/models.ts new file mode 100644 index 00000000..a641bc2b --- /dev/null +++ b/app/workers/layouts/models.ts @@ -0,0 +1,51 @@ +import { Module } from "../../../server/data/modules" +import layoutsDB from "../../db/layouts" +import { LastFetchStamped } from "../../db/systems" +import { pipe } from "fp-ts/lib/function" +import produce from "immer" +import { BufferGeometry } from "three" +import { mergeBufferGeometries } from "three-stdlib" +import { getSpeckleObject } from "../../../server/data/speckleModel" +import { A, R } from "../../utils/functions" +import speckleIfcParser from "../../utils/speckle/speckleIfcParser" + +const getSpeckleModelGeometries = async (speckleBranchUrl: string) => { + const speckleObjectData = await getSpeckleObject(speckleBranchUrl) + const speckleObject = speckleIfcParser.parse(speckleObjectData) + + return pipe( + speckleObject, + A.reduce( + {}, + (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { + return produce(acc, (draft) => { + if (ifcTag in draft) draft[ifcTag].push(geometry) + else draft[ifcTag] = [geometry] + }) + } + ), + R.map((geoms) => mergeBufferGeometries(geoms)), + R.filter((bg: BufferGeometry | null): bg is BufferGeometry => Boolean(bg)), + R.map((x) => x.toJSON()) + ) +} + +export const syncModels = (modules: LastFetchStamped[]) => { + modules.map(async (nextModule) => { + const { speckleBranchUrl, lastFetched } = nextModule + const maybeModel = await layoutsDB.models.get(speckleBranchUrl) + + if (maybeModel && maybeModel.lastFetched === lastFetched) { + return + } + + const geometries = await getSpeckleModelGeometries(speckleBranchUrl) + + layoutsDB.models.put({ + speckleBranchUrl, + lastFetched, + geometries, + systemId: nextModule.systemId, + }) + }) +} diff --git a/app/workers/layouts/vanilla.ts b/app/workers/layouts/vanilla.ts new file mode 100644 index 00000000..18aab0af --- /dev/null +++ b/app/workers/layouts/vanilla.ts @@ -0,0 +1,114 @@ +import { liveQuery } from "dexie" +import { flow, identity, pipe } from "fp-ts/lib/function" +import { Module } from "../../../server/data/modules" +import layoutsDB from "../../db/layouts" +import systemsDB, { LastFetchStamped } from "../../db/systems" +import { A, all, O, Ord, S } from "../../utils/functions" + +export const createVanillaModuleGetter = + (modulesCache: LastFetchStamped[]) => + ( + opts: { + positionType?: string + levelType?: string + constrainGridType?: boolean + sectionType?: string + } = {} + ) => + (module: Module): O.Option> => { + const { + sectionType, + positionType, + levelType, + constrainGridType = true, + } = opts + + return pipe( + modulesCache, + A.filter((sysModule) => + all( + sysModule.systemId === module.systemId, + sectionType + ? sysModule.structuredDna.sectionType === sectionType + : sysModule.structuredDna.sectionType === + module.structuredDna.sectionType, + positionType + ? sysModule.structuredDna.positionType === positionType + : sysModule.structuredDna.positionType === + module.structuredDna.positionType, + levelType + ? sysModule.structuredDna.levelType === levelType + : sysModule.structuredDna.levelType === + module.structuredDna.levelType, + !constrainGridType || + sysModule.structuredDna.gridType === module.structuredDna.gridType + ) + ), + A.sort( + pipe( + S.Ord, + Ord.contramap((m: Module) => m.dna) + ) + ), + A.head + ) + } + +liveQuery(() => systemsDB.modules.toArray()).subscribe( + async (modules: LastFetchStamped[]) => { + const getVanillaModule = createVanillaModuleGetter(modules)() + + pipe( + modules, + A.map(getVanillaModule), + A.traverse(O.Applicative)(identity), + O.map( + flow( + A.uniq({ equals: (x, y) => x.dna === y.dna }), + A.map( + async ({ + systemId, + id, + structuredDna: { sectionType, positionType, levelType, gridType }, + dna, + }: LastFetchStamped) => { + return await layoutsDB.vanillaModules.put({ + systemId, + sectionType, + positionType, + levelType, + gridType, + moduleDna: dna, + }) + } + ) + ) + ) + ) + } +) + +// ideas +// ----- +// get each level type and iter that? +// vanilla modules by level type (with grid type 1)? +// ----- +// positionType: MID +// gridType: {{ the lowest }} +// systemId: { for each } +// levelType: { this is what we group by } +// ----- +// so Record> +// ----- +// then a getter function to get the vanilla module of some height + +// liveQuery(() => layoutsDB.vanillaModules.toArray()).subscribe( +// (dbVanillaModules) => { +// for (const systemId of allSystemIds) { +// pipe(dbVanillaModules, A.reduce({}, (acc,{systemId, levelType}) => { + +// return acc +// })) +// } +// } +// ) diff --git a/app/workers/layouts.ts b/app/workers/layouts/worker.ts similarity index 69% rename from app/workers/layouts.ts rename to app/workers/layouts/worker.ts index 27347d7f..7feb92c6 100644 --- a/app/workers/layouts.ts +++ b/app/workers/layouts/worker.ts @@ -4,118 +4,24 @@ import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" import { pipe } from "fp-ts/lib/function" import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" -import { BufferGeometry } from "three" -import { mergeBufferGeometries } from "three-stdlib" -import { Module } from "../../server/data/modules" -import { getSpeckleObject } from "../../server/data/speckleModel" -import layoutsDB, { LayoutKey, serializeLayoutKey } from "../db/layouts" -import systemsDB, { LastFetchStamped } from "../db/systems" -import { A, all, O, Ord, R, S } from "../utils/functions" -import { isSSR } from "../utils/next" -import speckleIfcParser from "../utils/speckle/speckleIfcParser" - -export type PositionedModule = { - module: Module - z: number - gridGroupIndex: number -} - -export type PositionedInstancedModule = { - module: Module - y: number - z: number - columnIndex: number - levelIndex: number - gridGroupIndex: number -} - -export type PositionedRow = { - levelIndex: number - levelType: string - y: number - modules: Array - length: number -} - -export type GridGroup = PositionedRow - -export type RowLayout = Array - -export type PositionedColumn = { - gridGroups: Array - z: number - columnIndex: number - length: number -} - -export type ModuleIdentifier = { - columnIndex: number - levelIndex: number - gridGroupIndex: number -} - -export type HouseModuleIdentifier = ModuleIdentifier & { - houseId: string -} - -export type SystemHouseModuleIdentifier = HouseModuleIdentifier & { - systemId: string -} - -export type ColumnLayout = Array - -const getSpeckleModelGeometries = async (speckleBranchUrl: string) => { - const speckleObjectData = await getSpeckleObject(speckleBranchUrl) - const speckleObject = speckleIfcParser.parse(speckleObjectData) - - return pipe( - speckleObject, - A.reduce( - {}, - (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { - return produce(acc, (draft) => { - if (ifcTag in draft) draft[ifcTag].push(geometry) - else draft[ifcTag] = [geometry] - }) - } - ), - R.map((geoms) => mergeBufferGeometries(geoms)), - R.filter((bg: BufferGeometry | null): bg is BufferGeometry => Boolean(bg)), - R.map((x) => x.toJSON()) - ) -} - -const syncModels = (modules: LastFetchStamped[]) => { - modules.map(async (nextModule) => { - const { speckleBranchUrl, lastFetched } = nextModule - const maybeModel = await layoutsDB.models.get(speckleBranchUrl) - - if (maybeModel && maybeModel.lastFetched === lastFetched) { - return - } - - const geometries = await getSpeckleModelGeometries(speckleBranchUrl) - - layoutsDB.models.put({ - speckleBranchUrl, - lastFetched, - geometries, - systemId: nextModule.systemId, - }) - }) -} +import { Module } from "../../../server/data/modules" +import layoutsDB, { + ColumnLayout, + LayoutKey, + PositionedColumn, + PositionedModule, + PositionedRow, + serializeLayoutKey, +} from "../../db/layouts" +import systemsDB, { LastFetchStamped } from "../../db/systems" +import { A, O } from "../../utils/functions" +import { isSSR } from "../../utils/next" +import { syncModels } from "./models" +import { createVanillaModuleGetter } from "./vanilla" let modulesCache: LastFetchStamped[] = [] let layoutsQueue: LayoutKey[] = [] -// const getSpeckleModel = async (module: Module) => { -// const { speckleBranchUrl } = module -// const maybeModel = await layoutsDB.models.get(speckleBranchUrl) -// if (maybeModel && maybeModel.lastFetched === lastFetched) { -// return -// } -// } - const modulesToRows = (modules: Module[]): Module[][] => { const jumpIndices = pipe( modules, @@ -171,6 +77,7 @@ const analyzeColumn = ) ) } + const columnify = (toLength: (a: A) => number) => (input: readonly A[][]) => { @@ -375,53 +282,6 @@ export const splitColumns = (layout: ColumnLayout) => }) ) -const getVanillaModule = ( - module: Module, - opts: { - positionType?: string - levelType?: string - constrainGridType?: boolean - sectionType?: string - } = {} -): O.Option => { - const { - sectionType, - positionType, - levelType, - constrainGridType = true, - } = opts - - return pipe( - modulesCache, - A.filter((sysModule) => - all( - sysModule.systemId === module.systemId, - sectionType - ? sysModule.structuredDna.sectionType === sectionType - : sysModule.structuredDna.sectionType === - module.structuredDna.sectionType, - positionType - ? sysModule.structuredDna.positionType === positionType - : sysModule.structuredDna.positionType === - module.structuredDna.positionType, - levelType - ? sysModule.structuredDna.levelType === levelType - : sysModule.structuredDna.levelType === - module.structuredDna.levelType, - !constrainGridType || - sysModule.structuredDna.gridType === module.structuredDna.gridType - ) - ), - A.sort( - pipe( - S.Ord, - Ord.contramap((m: Module) => m.dna) - ) - ), - A.head - ) -} - const processLayout = async ({ systemId, dnas }: LayoutKey) => { const modules = pipe( dnas, @@ -449,6 +309,11 @@ const processLayout = async ({ systemId, dnas }: LayoutKey) => { startColumn: { gridGroups }, } = splitColumns(layout) + const getVanillaModule = createVanillaModuleGetter(modulesCache)({ + constrainGridType: false, + positionType: "MID", + }) + pipe( gridGroups, A.traverse(O.Applicative)( @@ -459,10 +324,8 @@ const processLayout = async ({ systemId, dnas }: LayoutKey) => { modules: [{ module }], }): O.Option => pipe( - getVanillaModule(module, { - constrainGridType: false, - positionType: "MID", - }), + module, + getVanillaModule, O.map((vanillaModule) => ({ modules: [ { @@ -529,10 +392,71 @@ if (!isSSR()) { }) } +export const columnLayoutToDnas = ( + columnLayout: Omit[] +) => + pipe( + columnLayout, + RA.map(({ gridGroups }) => + pipe( + gridGroups, + RA.map(({ modules }) => + pipe( + modules, + RA.map(({ module }) => module.dna) + ) + ) + ) + ), + transposeRA, + RA.flatten, + RA.flatten + ) as string[] + +const processZStretchLayout = async ({ + layoutKey, + direction, + i, +}: { + layoutKey: LayoutKey + direction: number + i: number +}) => { + const { systemId } = layoutKey + const strLayoutKey = serializeLayoutKey(layoutKey) + const layout = await layoutsDB.layouts.get(strLayoutKey) + if (!layout) { + console.log(`no layout for ${strLayoutKey}`) + return + } + const vanillaColumn = await layoutsDB.vanillaColumns.get(strLayoutKey) + if (!vanillaColumn) { + console.log(`no layout for ${strLayoutKey}`) + return + } + + const { startColumn, midColumns, endColumn } = splitColumns(layout.layout) + + const nextDnas = columnLayoutToDnas([ + startColumn, + ...A.replicate(i, vanillaColumn.vanillaColumn), + ...midColumns, + endColumn, + ]) + + console.log({ nextDnas }) + + processLayout({ + systemId, + dnas: nextDnas, + }) +} + const api = { postLayout, postLayouts, processLayout, + processZStretchLayout, syncModels, } diff --git a/app/workers/systems.ts b/app/workers/systems.ts index dd3f87f9..ea8938dd 100644 --- a/app/workers/systems.ts +++ b/app/workers/systems.ts @@ -1,7 +1,9 @@ import { vanillaTrpc } from "../../client/trpc" import { Element } from "../../server/data/elements" import { HouseType } from "../../server/data/houseTypes" +import { LevelType } from "../../server/data/levelTypes" import { Module } from "../../server/data/modules" +import { SectionType } from "../../server/data/sectionTypes" import systemsDB, { LastFetchStamped } from "../db/systems" const initModules = async () => { @@ -67,10 +69,58 @@ const initElements = async () => { await Promise.all(promises) } +const initSectionTypes = async () => { + const remoteSectionTypes = await vanillaTrpc.sectionTypes.query() + + const promises = remoteSectionTypes.map(async (remoteSectionType) => { + const localSectionType = await systemsDB.sectionTypes.get( + remoteSectionType.id + ) + + const indexedSectionType: LastFetchStamped = { + ...remoteSectionType, + lastFetched: new Date().getTime(), + } + + if ( + !localSectionType || + remoteSectionType.lastModified > localSectionType.lastModified + ) { + await systemsDB.sectionTypes.put(indexedSectionType) + } + }) + + await Promise.all(promises) +} + +const initLevelTypes = async () => { + const remoteLevelTypes = await vanillaTrpc.levelTypes.query() + + const promises = remoteLevelTypes.map(async (remoteLevelType) => { + const localLevelType = await systemsDB.levelTypes.get(remoteLevelType.id) + + const indexedLevelType: LastFetchStamped = { + ...remoteLevelType, + lastFetched: new Date().getTime(), + } + + if ( + !localLevelType || + remoteLevelType.lastModified > localLevelType.lastModified + ) { + await systemsDB.levelTypes.put(indexedLevelType) + } + }) + + await Promise.all(promises) +} + const init = () => { initModules() initHouseTypes() initElements() + initSectionTypes() + initLevelTypes() } init() diff --git a/server/data/levelTypes.ts b/server/data/levelTypes.ts index fb9589dc..1bf32196 100644 --- a/server/data/levelTypes.ts +++ b/server/data/levelTypes.ts @@ -9,6 +9,7 @@ export interface LevelType { systemId: string code: string description: string + lastModified: number } export const levelTypeParser = z.object({ @@ -16,6 +17,17 @@ export const levelTypeParser = z.object({ fields: z.object({ level_code: z.string().min(1), description: z.string().min(1), + last_modified: z.string().refine( + (value) => { + // Attempt to parse the value as a date and check that it's valid + const date = new Date(value) + return !isNaN(date.getTime()) + }, + { + // Custom error message + message: "Invalid date string", + } + ), }), }) @@ -33,11 +45,15 @@ export const levelTypesQuery: QueryFn = .then( z.array( levelTypeParser.transform( - ({ id, fields: { level_code, description } }): LevelType => ({ + ({ + id, + fields: { level_code, description, last_modified }, + }): LevelType => ({ id, systemId, code: level_code, description, + lastModified: new Date(last_modified).getTime(), }) ) ).parse diff --git a/server/data/sectionTypes.ts b/server/data/sectionTypes.ts index b0904680..f98a109d 100644 --- a/server/data/sectionTypes.ts +++ b/server/data/sectionTypes.ts @@ -10,6 +10,7 @@ export type SectionType = { code: string description: string width: number + lastModified: number } export const sectionTypeParser = z.object({ @@ -18,6 +19,17 @@ export const sectionTypeParser = z.object({ section_code: z.string().min(1), description: z.string().default(""), section_width: z.number(), + last_modified: z.string().refine( + (value) => { + // Attempt to parse the value as a date and check that it's valid + const date = new Date(value) + return !isNaN(date.getTime()) + }, + { + // Custom error message + message: "Invalid date string", + } + ), }), }) @@ -37,13 +49,19 @@ export const sectionTypesQuery: QueryFn = sectionTypeParser.transform( ({ id, - fields: { section_code, description, section_width }, + fields: { + section_code, + description, + section_width, + last_modified, + }, }) => ({ id, systemId, code: section_code, description, width: section_width, + lastModified: new Date(last_modified).getTime(), }) ) ).parse From 53379a2b933fbc3781f02209f8b080cdb7b9a098 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Sun, 9 Jul 2023 18:54:28 +0100 Subject: [PATCH 032/132] wip pre compound layouts index --- app/design/state/interactions/ZStretch.tsx | 24 +++---- app/design/state/vanilla.ts | 30 ++++++--- app/workers/layouts/vanilla.ts | 73 +++++++++++++++------- 3 files changed, 83 insertions(+), 44 deletions(-) diff --git a/app/design/state/interactions/ZStretch.tsx b/app/design/state/interactions/ZStretch.tsx index a8eb1e90..958b6d6a 100644 --- a/app/design/state/interactions/ZStretch.tsx +++ b/app/design/state/interactions/ZStretch.tsx @@ -23,7 +23,6 @@ import { useZStretchHouseListener, } from "../events" import houses, { useSetHouse } from "../houses" -import { vanillaColumns } from "../vanilla" type Props = { houseId: string @@ -56,17 +55,18 @@ const ZStretch = ({ const strLayoutKey = serializeLayoutKey(layoutKey) const vanillaColumn = suspend(async () => { - if (strLayoutKey in vanillaColumns) { - return vanillaColumns[strLayoutKey] - } else { - const layoutsWorker = getLayoutsWorker() - if (!layoutsWorker) throw new Error("no layouts worker") - await layoutsWorker.processLayout(layoutKey) - const vanillaColumn = await layoutsDB.vanillaColumns.get(strLayoutKey) - if (!vanillaColumn) - throw new Error(`no vanilla column for ${strLayoutKey}`) - return vanillaColumn.vanillaColumn - } + return undefined as any + // if (strLayoutKey in vanillaColumns) { + // return vanillaColumns[strLayoutKey] + // } else { + // const layoutsWorker = getLayoutsWorker() + // if (!layoutsWorker) throw new Error("no layouts worker") + // await layoutsWorker.processLayout(layoutKey) + // const vanillaColumn = await layoutsDB.vanillaColumns.get(strLayoutKey) + // if (!vanillaColumn) + // throw new Error(`no vanilla column for ${strLayoutKey}`) + // return vanillaColumn.vanillaColumn + // } }, []) const maxLength = 25 diff --git a/app/design/state/vanilla.ts b/app/design/state/vanilla.ts index 751abcea..d3d3d680 100644 --- a/app/design/state/vanilla.ts +++ b/app/design/state/vanilla.ts @@ -1,21 +1,33 @@ import { Module } from "@/server/data/modules" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" -import { proxy, ref } from "valtio" +import { proxy } from "valtio" import { A, all, O, Ord, RA, S, someOrError } from "~/utils/functions" import { useSystemModules } from "../../data/modules" -import layoutsDB, { PositionedRow, VanillaColumn } from "../../db/layouts" +import layoutsDB, { PositionedRow } from "../../db/layouts" import { isSSR } from "../../utils/next" -export const vanillaColumns = proxy>({}) +export const vanillaModules = proxy>({}) + +export const getVanillaModulesKey = ({ + systemId, + sectionType, + positionType, + levelType, + gridType, +}: { + systemId: string + sectionType: string + positionType: string + levelType: string + gridType: string +}) => [systemId, sectionType, positionType, levelType, gridType].toString() if (!isSSR()) { - liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( - (dbVanillaColumns) => { - for (let { layoutsKey, vanillaColumn } of dbVanillaColumns) { - if (!(layoutsKey in dbVanillaColumns)) { - vanillaColumns[layoutsKey] = ref(vanillaColumn) - } + liveQuery(() => layoutsDB.vanillaModules.toArray()).subscribe( + (dbVanillaModules) => { + for (let { moduleDna, ...dbVanillaModule } of dbVanillaModules) { + vanillaModules[getVanillaModulesKey(dbVanillaModule)] = moduleDna } } ) diff --git a/app/workers/layouts/vanilla.ts b/app/workers/layouts/vanilla.ts index 18aab0af..056cc297 100644 --- a/app/workers/layouts/vanilla.ts +++ b/app/workers/layouts/vanilla.ts @@ -55,7 +55,7 @@ export const createVanillaModuleGetter = } liveQuery(() => systemsDB.modules.toArray()).subscribe( - async (modules: LastFetchStamped[]) => { + (modules: LastFetchStamped[]) => { const getVanillaModule = createVanillaModuleGetter(modules)() pipe( @@ -68,11 +68,10 @@ liveQuery(() => systemsDB.modules.toArray()).subscribe( A.map( async ({ systemId, - id, structuredDna: { sectionType, positionType, levelType, gridType }, dna, - }: LastFetchStamped) => { - return await layoutsDB.vanillaModules.put({ + }: LastFetchStamped) => + layoutsDB.vanillaModules.put({ systemId, sectionType, positionType, @@ -80,7 +79,6 @@ liveQuery(() => systemsDB.modules.toArray()).subscribe( gridType, moduleDna: dna, }) - } ) ) ) @@ -88,27 +86,56 @@ liveQuery(() => systemsDB.modules.toArray()).subscribe( } ) -// ideas -// ----- -// get each level type and iter that? -// vanilla modules by level type (with grid type 1)? -// ----- -// positionType: MID -// gridType: {{ the lowest }} -// systemId: { for each } -// levelType: { this is what we group by } -// ----- -// so Record> -// ----- -// then a getter function to get the vanilla module of some height +export const getIndexedVanillaModule = ({ + systemId, + sectionType, + positionType, + levelType, + gridType, +}: { + systemId: string + sectionType: string + positionType: string + levelType: string + gridType: string +}) => + layoutsDB.vanillaModules.get([ + systemId, + sectionType, + positionType, + levelType, + gridType, + ]) +liveQuery(() => layoutsDB.layouts.toArray()).subscribe((dbLayouts) => { + for (let dbLayout of dbLayouts) { + const { layoutsKey } = dbLayout + } +}) + +// this actually needs to SUBSCRIBE TO HOUSES +// get their `dnas` +// get their column layout (maybe we're subscribing to layouts actually) +// layoutKey -> vanillaColumn we post +// +// --------------------------------------------------------------------- +// // liveQuery(() => layoutsDB.vanillaModules.toArray()).subscribe( -// (dbVanillaModules) => { -// for (const systemId of allSystemIds) { -// pipe(dbVanillaModules, A.reduce({}, (acc,{systemId, levelType}) => { +// async (dbVanillaMods) => { +// if (dbVanillaMods.length > 0) { +// const { +// systemId, +// sectionType, +// positionType, +// levelType, +// gridType, +// moduleDna, +// } = dbVanillaMods[0] -// return acc -// })) +// console.log( +// [systemId, sectionType, positionType, levelType, gridType].toString() +// ) // } + // } // ) From 21ec01f3ca04b9263eb48ca2efd3ff4eec9b594b Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 10 Jul 2023 10:27:35 +0100 Subject: [PATCH 033/132] wip --- app/design/state/interactions/ZStretch.tsx | 25 +++++++++++----------- app/design/state/vanilla.ts | 14 +++++++++++- app/design/ui-3d/grouped/GroupedHouse2.tsx | 1 + app/workers/layouts/worker.ts | 2 -- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/app/design/state/interactions/ZStretch.tsx b/app/design/state/interactions/ZStretch.tsx index 958b6d6a..d6615650 100644 --- a/app/design/state/interactions/ZStretch.tsx +++ b/app/design/state/interactions/ZStretch.tsx @@ -23,6 +23,7 @@ import { useZStretchHouseListener, } from "../events" import houses, { useSetHouse } from "../houses" +import { vanillaColumns } from "../vanilla" type Props = { houseId: string @@ -51,22 +52,22 @@ const ZStretch = ({ startRef, endRef, }: Props) => { + console.log("ZStretch") const { systemId } = layoutKey const strLayoutKey = serializeLayoutKey(layoutKey) const vanillaColumn = suspend(async () => { - return undefined as any - // if (strLayoutKey in vanillaColumns) { - // return vanillaColumns[strLayoutKey] - // } else { - // const layoutsWorker = getLayoutsWorker() - // if (!layoutsWorker) throw new Error("no layouts worker") - // await layoutsWorker.processLayout(layoutKey) - // const vanillaColumn = await layoutsDB.vanillaColumns.get(strLayoutKey) - // if (!vanillaColumn) - // throw new Error(`no vanilla column for ${strLayoutKey}`) - // return vanillaColumn.vanillaColumn - // } + if (strLayoutKey in vanillaColumns) { + return vanillaColumns[strLayoutKey] + } else { + const layoutsWorker = getLayoutsWorker() + if (!layoutsWorker) throw new Error("no layouts worker") + await layoutsWorker.processLayout(layoutKey) + const vanillaColumn = await layoutsDB.vanillaColumns.get(strLayoutKey) + if (!vanillaColumn) + throw new Error(`no vanilla column for ${strLayoutKey}`) + return vanillaColumn.vanillaColumn + } }, []) const maxLength = 25 diff --git a/app/design/state/vanilla.ts b/app/design/state/vanilla.ts index d3d3d680..e4eb8a2f 100644 --- a/app/design/state/vanilla.ts +++ b/app/design/state/vanilla.ts @@ -4,11 +4,13 @@ import { pipe } from "fp-ts/lib/function" import { proxy } from "valtio" import { A, all, O, Ord, RA, S, someOrError } from "~/utils/functions" import { useSystemModules } from "../../data/modules" -import layoutsDB, { PositionedRow } from "../../db/layouts" +import layoutsDB, { PositionedRow, VanillaColumn } from "../../db/layouts" import { isSSR } from "../../utils/next" export const vanillaModules = proxy>({}) +export const vanillaColumns = proxy>({}) + export const getVanillaModulesKey = ({ systemId, sectionType, @@ -33,6 +35,16 @@ if (!isSSR()) { ) } +if (!isSSR()) { + liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( + (dbVanillaColumns) => { + for (let { layoutsKey, vanillaColumn } of dbVanillaColumns) { + vanillaColumns[layoutsKey] = vanillaColumn + } + } + ) +} + export const getVanillaColumnLength = (column: PositionedRow[]) => pipe( column, diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 51cf0e7e..2b61fade 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -27,6 +27,7 @@ type Props = { } const GroupedHouse2 = (props: Props) => { + console.log("GroupedHouse2") const { house } = props const { systemId, id: houseId, position, rotation } = house const dnas = [...house.dnas] diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 7feb92c6..69bc8c06 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -444,8 +444,6 @@ const processZStretchLayout = async ({ endColumn, ]) - console.log({ nextDnas }) - processLayout({ systemId, dnas: nextDnas, From 63f2a692fc2dd4b7f6587b4ee9b6db5ec6a6e673 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 10 Jul 2023 14:15:07 +0100 Subject: [PATCH 034/132] wip key test --- app/design/state/interactions/ZStretch.tsx | 6 +- app/design/state/layouts.ts | 6 +- app/design/ui-3d/grouped/GroupedHouse2.tsx | 57 ++++++++++++++----- .../stretchLength/GroupedStretchColumn.tsx | 6 +- 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/app/design/state/interactions/ZStretch.tsx b/app/design/state/interactions/ZStretch.tsx index d6615650..038e06ad 100644 --- a/app/design/state/interactions/ZStretch.tsx +++ b/app/design/state/interactions/ZStretch.tsx @@ -58,19 +58,21 @@ const ZStretch = ({ const vanillaColumn = suspend(async () => { if (strLayoutKey in vanillaColumns) { + console.log("strLayoutKey in vanillaColumns") return vanillaColumns[strLayoutKey] } else { const layoutsWorker = getLayoutsWorker() if (!layoutsWorker) throw new Error("no layouts worker") await layoutsWorker.processLayout(layoutKey) const vanillaColumn = await layoutsDB.vanillaColumns.get(strLayoutKey) + console.log("awaited everything") if (!vanillaColumn) throw new Error(`no vanilla column for ${strLayoutKey}`) return vanillaColumn.vanillaColumn } }, []) - const maxLength = 25 + const maxLength = 10 const maxCount = floor(max(0, maxLength - length) / vanillaColumn.length) const maxColumnZs = pipe( NEA.range(0, maxCount - 1), @@ -156,8 +158,6 @@ const ZStretch = ({ ) const maxStretchLengthDown = columnZsDown?.[columnZsDown.length - 1] ?? 0 - console.log([...columnZsUp, ...columnZsUp]) - const columnsUp = pipe( columnZsUp, A.mapWithIndex((i, columnZ) => ( diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 99e3e610..8224ae23 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -38,7 +38,10 @@ export const useDnasLayout = (layoutsKey: LayoutKey): ColumnLayout => { const maybeLayout: ColumnLayout | undefined = snap?.[serialKey] return suspend(async () => { - if (maybeLayout) return maybeLayout + if (maybeLayout) { + console.log("maybeLayout") + return maybeLayout + } const layoutsWorker = getLayoutsWorker() @@ -48,6 +51,7 @@ export const useDnasLayout = (layoutsKey: LayoutKey): ColumnLayout => { const layout = await layoutsWorker.processLayout(layoutsKey) layouts[serialKey] = layout + console.log("awaited layout") return layout }, [maybeLayout]) } diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 2b61fade..5e71042d 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -1,12 +1,16 @@ import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" import { Fragment, useEffect, useMemo, useRef } from "react" +import { useKey } from "react-use" import { Box3, Group, Matrix4, Vector3 } from "three" import { OBB } from "three-stdlib" import { LayoutKey, serializeLayoutKey } from "../../../db/layouts" import { House } from "../../../db/user" -import { RA } from "../../../utils/functions" -import { splitColumns } from "../../../workers/layouts/worker" +import { A, RA } from "../../../utils/functions" +import { + columnLayoutToDnas, + splitColumns, +} from "../../../workers/layouts/worker" import dimensions, { collideOBB, Dimensions } from "../../state/dimensions" import { dispatchMoveHouse, @@ -18,6 +22,7 @@ import houses, { useSetHouse } from "../../state/houses" import ZStretch from "../../state/interactions/ZStretch" import { useDnasLayout } from "../../state/layouts" import { useTransformabilityBooleans } from "../../state/siteCtx" +import { vanillaColumns } from "../../state/vanilla" import RotateHandles from "../handles/RotateHandles" import StretchHandle from "../handles/StretchHandle" import GroupedColumn from "./GroupedColumn" @@ -34,8 +39,6 @@ const GroupedHouse2 = (props: Props) => { const layoutKey: LayoutKey = { systemId, dnas } - const strLayoutKey = serializeLayoutKey(layoutKey) - const translationMatrix = useMemo(() => { const m = new Matrix4() const { x, y, z } = position @@ -118,7 +121,6 @@ const GroupedHouse2 = (props: Props) => { if (!rootRef.current) return rootRef.current.matrixAutoUpdate = false rootRef.current.matrix.copy(reactHouseMatrix) - invalidate() }, [reactHouseMatrix]) const frameOBB = useRef(new OBB()) @@ -166,6 +168,8 @@ const GroupedHouse2 = (props: Props) => { invalidate() }) + useEffect(invalidate, [dnas]) + useHouseMaterialOps({ houseId, ref: rootRef, @@ -181,6 +185,27 @@ const GroupedHouse2 = (props: Props) => { ref.current.visible = b }) + useKey( + "l", + () => { + const vanillaColumn = vanillaColumns[serializeLayoutKey(layoutKey)] + + setHouse({ + ...house, + dnas: columnLayoutToDnas([ + startColumn, + ...midColumns, + ...A.replicate(1, { + gridGroups: vanillaColumn.gridGroups, + }), + endColumn, + ]), + }) + }, + undefined, + [dnas] + ) + return ( { rotation-y={rotation} > - + /> */} @@ -209,7 +234,11 @@ const GroupedHouse2 = (props: Props) => { midColumns, RA.map((column) => ( + x.modules.map((m) => m.module.dna) + ) + )}`} column={column} {...{ systemId, houseId }} /> @@ -219,16 +248,16 @@ const GroupedHouse2 = (props: Props) => { - + /> */} {/* { scale={moveRotateEnabled ? [1, 1, 1] : [0, 0, 0]} /> - + {/* { endRef, }} /> - + */} {/* */} diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx index 24bacd69..faa713f4 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx @@ -59,12 +59,16 @@ const GroupedStretchColumn = (props: Props) => { } else if (direction === -1 && distance + columnLength / 2 < columnZ) { groupRef.current?.scale.set(1, 1, 1) } else { - groupRef.current?.scale.set(0, 0, 0) + // groupRef.current?.scale.set(0, 0, 0) } invalidate() }) + useEffect(() => { + groupRef.current?.scale.set(1, 1, 1) + }, []) + return ( {pipe( From 55840fbcfba112549dfb073d856f6bbad944a196 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 10 Jul 2023 15:29:30 +0100 Subject: [PATCH 035/132] wip rendering vanilla geoms --- app/design/app.tsx | 4 +- app/design/ui-3d/fresh/FreshApp.tsx | 132 ++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 app/design/ui-3d/fresh/FreshApp.tsx diff --git a/app/design/app.tsx b/app/design/app.tsx index 9005bf66..58a54c74 100644 --- a/app/design/app.tsx +++ b/app/design/app.tsx @@ -3,7 +3,7 @@ import DataInit from "~/data/DataInit" import { TrpcProvider } from "../ui/TrpcProvider" import { getLayoutsWorker, getSystemsWorker } from "../workers" import { Routing } from "./state/routing" -import GroupedApp from "./ui-3d/grouped/GroupedApp" +import FreshApp from "./ui-3d/fresh/FreshApp" import AppInit from "./ui-3d/init/AppInit" const App = () => { @@ -14,7 +14,7 @@ const App = () => { - + diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx new file mode 100644 index 00000000..c5d15795 --- /dev/null +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -0,0 +1,132 @@ +import { useGesture } from "@use-gesture/react" +import { liveQuery } from "dexie" +import { pipe } from "fp-ts/lib/function" +import { memo, useEffect, useRef } from "react" +import { useKey } from "react-use" +import { + BufferGeometry, + BufferGeometryLoader, + Group, + Material, + Mesh, + MeshBasicMaterial, +} from "three" +import { Module } from "../../../../server/data/modules" +import { ifcTagToElement } from "../../../data/elements" +import layoutsDB, { VanillaColumn } from "../../../db/layouts" +import systemsDB from "../../../db/systems" +import { R, S } from "../../../utils/functions" + +// serialized layout key : column +let vanillaColumns: Record = {} +liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( + (dbVanillaColumns) => { + for (let dbVanillaColumn of dbVanillaColumns) { + const { layoutsKey, vanillaColumn } = dbVanillaColumn + vanillaColumns[layoutsKey] = vanillaColumn + } + } +) + +const loader = new BufferGeometryLoader() + +// speckle branch url : geometry by ifc tag +let models: Record> = {} + +liveQuery(async () => { + const models = await layoutsDB.models.toArray() + const elements = await systemsDB.elements.toArray() + return { models, elements } +}).subscribe(({ models: dbModels, elements: dbElements }) => { + for (let { speckleBranchUrl, geometries, systemId } of dbModels) { + if (!(speckleBranchUrl in models)) { + const loadedModels: Record = pipe( + geometries, + R.map((x) => loader.parse(x) as BufferGeometry), + R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { + const el = ifcTagToElement({ + systemId, + elements: dbElements, + ifcTag, + }) + if (!el) return acc + return { + ...acc, + [el.name]: geometry, + } + }) + ) + models[speckleBranchUrl] = loadedModels + } + } +}) + +const basicMaterial = new MeshBasicMaterial({ color: "tomato" }) + +const getGeometry = ({ + speckleBranchUrl, + ifcTag, +}: { + speckleBranchUrl: string + ifcTag: string +}) => models[speckleBranchUrl][ifcTag] + +const getMaterial = ({}: { + systemId: string + houseId: string + elementName: string +}): Material => basicMaterial + +const FreshApp = () => { + const rootRef = useRef(null) + + // forget this for now, just useKey + const bindAll = useGesture({ + onClick: console.log, + }) as any + + const insert1VanillaColumn = () => { + console.log("insert 1 vanilla") + } + + const moduleToGroup = (module: Module) => {} + + const vanillaColumnToGroup = ({ gridGroups }: VanillaColumn): Group => { + const columnGroup = new Group() + + gridGroups.forEach(({ modules, y }) => { + modules.forEach(({ z, module: { dna, speckleBranchUrl } }) => { + const moduleGroup = new Group() + moduleGroup.position.set(0, y, z) + const taggedModelGeometries = models[speckleBranchUrl] + for (let ifcTag of Object.keys(taggedModelGeometries)) { + const mesh = new Mesh( + getGeometry({ speckleBranchUrl, ifcTag }), + getMaterial({ systemId: "", houseId: "", elementName: "" }) + ) + moduleGroup.add(mesh) + } + columnGroup.add(moduleGroup) + }) + }) + + return columnGroup + } + + const init = () => { + if (!rootRef.current) return + + rootRef.current.clear() + + Object.values(vanillaColumns).forEach((vanillaColumn) => { + rootRef.current!.add(vanillaColumnToGroup(vanillaColumn)) + }) + } + + useEffect(init, []) + useKey("l", insert1VanillaColumn) + + return +} + +export default memo(FreshApp) From d7ecd1868af23af40efdf515eb4ed90712e25511 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 11 Jul 2023 13:15:22 +0100 Subject: [PATCH 036/132] wip house layout first pass --- app/db/layouts.ts | 4 +- app/design/state/layouts.ts | 2 +- app/design/ui-3d/fresh/FreshApp.tsx | 133 +++++------------------- app/design/ui-3d/fresh/helpers.ts | 151 ++++++++++++++++++++++++++++ app/workers/layouts/vanilla.ts | 2 +- app/workers/layouts/worker.ts | 4 +- 6 files changed, 182 insertions(+), 114 deletions(-) create mode 100644 app/design/ui-3d/fresh/helpers.ts diff --git a/app/db/layouts.ts b/app/db/layouts.ts index f1a8910f..ffabd874 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -89,7 +89,7 @@ export const serializeLayoutKey = ({ systemId, dnas }: LayoutKey) => class LayoutsDatabase extends Dexie { models: Dexie.Table, string> - layouts: Dexie.Table + houseLayouts: Dexie.Table vanillaModules: Dexie.Table vanillaColumns: Dexie.Table @@ -101,7 +101,7 @@ class LayoutsDatabase extends Dexie { vanillaModules: "[systemId+sectionType+positionType+levelType+gridType]", vanillaColumns: "layoutsKey", }) - this.layouts = this.table("layouts") + this.houseLayouts = this.table("layouts") this.models = this.table("models") this.vanillaModules = this.table("vanillaModules") this.vanillaColumns = this.table("vanillaColumns") diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 8224ae23..b387424b 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -23,7 +23,7 @@ export const layouts = proxy< >({}) if (!isSSR()) { - liveQuery(() => layoutsDB.layouts.toArray()).subscribe((dbLayouts) => { + liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe((dbLayouts) => { for (let { layoutsKey, layout } of dbLayouts) { if (!(layoutsKey in layouts)) { layouts[layoutsKey] = ref(layout) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index c5d15795..15345888 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,81 +1,9 @@ import { useGesture } from "@use-gesture/react" -import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" -import { memo, useEffect, useRef } from "react" -import { useKey } from "react-use" -import { - BufferGeometry, - BufferGeometryLoader, - Group, - Material, - Mesh, - MeshBasicMaterial, -} from "three" -import { Module } from "../../../../server/data/modules" -import { ifcTagToElement } from "../../../data/elements" -import layoutsDB, { VanillaColumn } from "../../../db/layouts" -import systemsDB from "../../../db/systems" -import { R, S } from "../../../utils/functions" - -// serialized layout key : column -let vanillaColumns: Record = {} -liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( - (dbVanillaColumns) => { - for (let dbVanillaColumn of dbVanillaColumns) { - const { layoutsKey, vanillaColumn } = dbVanillaColumn - vanillaColumns[layoutsKey] = vanillaColumn - } - } -) - -const loader = new BufferGeometryLoader() - -// speckle branch url : geometry by ifc tag -let models: Record> = {} - -liveQuery(async () => { - const models = await layoutsDB.models.toArray() - const elements = await systemsDB.elements.toArray() - return { models, elements } -}).subscribe(({ models: dbModels, elements: dbElements }) => { - for (let { speckleBranchUrl, geometries, systemId } of dbModels) { - if (!(speckleBranchUrl in models)) { - const loadedModels: Record = pipe( - geometries, - R.map((x) => loader.parse(x) as BufferGeometry), - R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { - const el = ifcTagToElement({ - systemId, - elements: dbElements, - ifcTag, - }) - if (!el) return acc - return { - ...acc, - [el.name]: geometry, - } - }) - ) - models[speckleBranchUrl] = loadedModels - } - } -}) - -const basicMaterial = new MeshBasicMaterial({ color: "tomato" }) - -const getGeometry = ({ - speckleBranchUrl, - ifcTag, -}: { - speckleBranchUrl: string - ifcTag: string -}) => models[speckleBranchUrl][ifcTag] - -const getMaterial = ({}: { - systemId: string - houseId: string - elementName: string -}): Material => basicMaterial +import { useEffect, useRef } from "react" +import { Group } from "three" +import { A, O } from "../../../utils/functions" +import { getFirstHouseLayout, layoutToColumns } from "./helpers" const FreshApp = () => { const rootRef = useRef(null) @@ -85,48 +13,37 @@ const FreshApp = () => { onClick: console.log, }) as any - const insert1VanillaColumn = () => { - console.log("insert 1 vanilla") - } - - const moduleToGroup = (module: Module) => {} - - const vanillaColumnToGroup = ({ gridGroups }: VanillaColumn): Group => { - const columnGroup = new Group() - - gridGroups.forEach(({ modules, y }) => { - modules.forEach(({ z, module: { dna, speckleBranchUrl } }) => { - const moduleGroup = new Group() - moduleGroup.position.set(0, y, z) - const taggedModelGeometries = models[speckleBranchUrl] - for (let ifcTag of Object.keys(taggedModelGeometries)) { - const mesh = new Mesh( - getGeometry({ speckleBranchUrl, ifcTag }), - getMaterial({ systemId: "", houseId: "", elementName: "" }) - ) - moduleGroup.add(mesh) - } - columnGroup.add(moduleGroup) - }) - }) - - return columnGroup + const cleanup = () => { + rootRef.current?.clear() } const init = () => { if (!rootRef.current) return - rootRef.current.clear() + // Object.values(vanillaColumns).forEach((vanillaColumn) => { + // rootRef.current!.add(vanillaColumnToGroup(vanillaColumn)) + // }) + + pipe( + getFirstHouseLayout(), + O.map((layout) => + pipe( + layout, + layoutToColumns, + A.map((columnGroup) => { + rootRef.current!.add(columnGroup) + }) + ) + ) + ) - Object.values(vanillaColumns).forEach((vanillaColumn) => { - rootRef.current!.add(vanillaColumnToGroup(vanillaColumn)) - }) + return cleanup } useEffect(init, []) - useKey("l", insert1VanillaColumn) + // useKey("l", insert1VanillaColumn) return } -export default memo(FreshApp) +export default FreshApp diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts new file mode 100644 index 00000000..e59dbc58 --- /dev/null +++ b/app/design/ui-3d/fresh/helpers.ts @@ -0,0 +1,151 @@ +import { createColumn } from "@tanstack/react-table" +import { liveQuery } from "dexie" +import { pipe } from "fp-ts/lib/function" +import { + BufferGeometry, + BufferGeometryLoader, + DoubleSide, + Group, + Material, + Mesh, + MeshBasicMaterial, +} from "three" +import { Module } from "../../../../server/data/modules" +import { ifcTagToElement } from "../../../data/elements" +import layoutsDB, { + ColumnLayout, + GridGroup, + PositionedColumn, + VanillaColumn, +} from "../../../db/layouts" +import systemsDB from "../../../db/systems" +import { House } from "../../../db/user" +import { A, O, R, S } from "../../../utils/functions" + +// serialized layout key : column +export let vanillaColumns: Record = {} + +liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( + (dbVanillaColumns) => { + for (let dbVanillaColumn of dbVanillaColumns) { + const { layoutsKey, vanillaColumn } = dbVanillaColumn + vanillaColumns[layoutsKey] = vanillaColumn + } + } +) + +const loader = new BufferGeometryLoader() + +// speckle branch url : geometry by ifc tag +export let models: Record> = {} + +liveQuery(async () => { + const models = await layoutsDB.models.toArray() + const elements = await systemsDB.elements.toArray() + return { models, elements } +}).subscribe(({ models: dbModels, elements: dbElements }) => { + for (let { speckleBranchUrl, geometries, systemId } of dbModels) { + if (!(speckleBranchUrl in models)) { + const loadedModels: Record = pipe( + geometries, + R.map((x) => loader.parse(x) as BufferGeometry), + R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { + const el = ifcTagToElement({ + systemId, + elements: dbElements, + ifcTag, + }) + if (!el) return acc + return { + ...acc, + [el.name]: geometry, + } + }) + ) + models[speckleBranchUrl] = loadedModels + } + } +}) + +const basicMaterial = new MeshBasicMaterial({ + color: "tomato", + side: DoubleSide, +}) + +export const getGeometry = ({ + speckleBranchUrl, + ifcTag, +}: { + speckleBranchUrl: string + ifcTag: string +}) => models[speckleBranchUrl][ifcTag] + +export const getMaterial = ({}: { + systemId?: string + houseId?: string + elementName?: string +} = {}): Material => basicMaterial + +export const moduleToGroup = ({ speckleBranchUrl }: Module) => { + const moduleGroup = new Group() + const taggedModelGeometries = models[speckleBranchUrl] + for (let ifcTag of Object.keys(taggedModelGeometries)) { + const mesh = new Mesh( + getGeometry({ speckleBranchUrl, ifcTag }), + getMaterial() + ) + moduleGroup.add(mesh) + } + + return moduleGroup +} + +export const insert1VanillaColumn = () => { + console.log("insert 1 vanilla") +} + +export const createColumnGroup = (gridGroups: GridGroup[]): Group => { + const columnGroup = new Group() + + gridGroups.forEach(({ modules, y }) => { + modules.forEach(({ z, module }) => { + const moduleGroup = moduleToGroup(module) + moduleGroup.position.set(0, y, z) + columnGroup.add(moduleGroup) + }) + }) + + return columnGroup +} + +export const layoutToColumns = (layout: ColumnLayout): Group[] => + pipe( + layout, + A.map(({ gridGroups, z }) => { + const group = createColumnGroup(gridGroups) + group.position.set(0, 0, z) + return group + }) + ) + +export let houseLayouts: Record = {} + +liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( + (dbHouseLayouts) => { + for (let { layoutsKey, layout } of dbHouseLayouts) + houseLayouts[layoutsKey] = layout + } +) + +export const getFirstHouseLayout = () => + pipe( + houseLayouts, + R.keys, + A.head, + O.chain((k) => pipe(houseLayouts, R.lookup(k))) + ) + +// export const houseToLayout = (house: House): ColumnLayout => { + +// return undefined as any +// } diff --git a/app/workers/layouts/vanilla.ts b/app/workers/layouts/vanilla.ts index 056cc297..02ff6969 100644 --- a/app/workers/layouts/vanilla.ts +++ b/app/workers/layouts/vanilla.ts @@ -107,7 +107,7 @@ export const getIndexedVanillaModule = ({ gridType, ]) -liveQuery(() => layoutsDB.layouts.toArray()).subscribe((dbLayouts) => { +liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe((dbLayouts) => { for (let dbLayout of dbLayouts) { const { layoutsKey } = dbLayout } diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 69bc8c06..36ed51ad 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -300,7 +300,7 @@ const processLayout = async ({ systemId, dnas }: LayoutKey) => { const layoutsKey = serializeLayoutKey({ systemId, dnas }) - layoutsDB.layouts.put({ + layoutsDB.houseLayouts.put({ layout, layoutsKey, }) @@ -424,7 +424,7 @@ const processZStretchLayout = async ({ }) => { const { systemId } = layoutKey const strLayoutKey = serializeLayoutKey(layoutKey) - const layout = await layoutsDB.layouts.get(strLayoutKey) + const layout = await layoutsDB.houseLayouts.get(strLayoutKey) if (!layout) { console.log(`no layout for ${strLayoutKey}`) return From 3384f3d9e9ff4c81fe06bc60c89ffdea94456848 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 11 Jul 2023 15:02:18 +0100 Subject: [PATCH 037/132] wip house render --- app/db/systems.ts | 4 ++ app/design/ui-3d/fresh/FreshApp.tsx | 6 +- app/design/ui-3d/fresh/helpers.ts | 94 ++++++++++++++++------------- app/design/ui-3d/fresh/systems.ts | 89 +++++++++++++++++++++++++++ app/workers/systems.ts | 24 ++++++++ server/data/elements.ts | 2 +- server/data/materials.ts | 23 +++++-- 7 files changed, 190 insertions(+), 52 deletions(-) create mode 100644 app/design/ui-3d/fresh/systems.ts diff --git a/app/db/systems.ts b/app/db/systems.ts index 8490b4ce..10acee4c 100644 --- a/app/db/systems.ts +++ b/app/db/systems.ts @@ -4,6 +4,7 @@ import Dexie from "dexie" import { Element } from "../../server/data/elements" import { HouseType } from "../../server/data/houseTypes" import { LevelType } from "../../server/data/levelTypes" +import { Material } from "../../server/data/materials" import { SectionType } from "../../server/data/sectionTypes" export type LastFetchStamped = T & { @@ -14,6 +15,7 @@ class SystemsDatabase extends Dexie { modules: Dexie.Table, string> houseTypes: Dexie.Table, string> elements: Dexie.Table, string> + materials: Dexie.Table, string> sectionTypes: Dexie.Table, string> levelTypes: Dexie.Table, string> blocks: Dexie.Table @@ -25,6 +27,7 @@ class SystemsDatabase extends Dexie { modules: "id,systemId,dna", houseTypes: "id,systemId", elements: "id,systemId", + materials: "id,systemId", sectionTypes: "id,systemId", levelTypes: "id,systemId", }) @@ -32,6 +35,7 @@ class SystemsDatabase extends Dexie { this.houseTypes = this.table("houseTypes") this.blocks = this.table("blocks") this.elements = this.table("elements") + this.materials = this.table("materials") this.sectionTypes = this.table("sectionTypes") this.levelTypes = this.table("levelTypes") } diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 15345888..ccc7976f 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -20,10 +20,6 @@ const FreshApp = () => { const init = () => { if (!rootRef.current) return - // Object.values(vanillaColumns).forEach((vanillaColumn) => { - // rootRef.current!.add(vanillaColumnToGroup(vanillaColumn)) - // }) - pipe( getFirstHouseLayout(), O.map((layout) => @@ -37,6 +33,8 @@ const FreshApp = () => { ) ) + console.log(rootRef.current) + return cleanup } diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index e59dbc58..601f6fc6 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -1,26 +1,16 @@ -import { createColumn } from "@tanstack/react-table" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" -import { - BufferGeometry, - BufferGeometryLoader, - DoubleSide, - Group, - Material, - Mesh, - MeshBasicMaterial, -} from "three" +import { BufferGeometry, BufferGeometryLoader, Group, Mesh } from "three" import { Module } from "../../../../server/data/modules" import { ifcTagToElement } from "../../../data/elements" import layoutsDB, { ColumnLayout, GridGroup, - PositionedColumn, VanillaColumn, } from "../../../db/layouts" import systemsDB from "../../../db/systems" -import { House } from "../../../db/user" import { A, O, R, S } from "../../../utils/functions" +import { getMaterial } from "./systems" // serialized layout key : column export let vanillaColumns: Record = {} @@ -39,26 +29,17 @@ const loader = new BufferGeometryLoader() // speckle branch url : geometry by ifc tag export let models: Record> = {} -liveQuery(async () => { - const models = await layoutsDB.models.toArray() - const elements = await systemsDB.elements.toArray() - return { models, elements } -}).subscribe(({ models: dbModels, elements: dbElements }) => { - for (let { speckleBranchUrl, geometries, systemId } of dbModels) { +liveQuery(() => layoutsDB.models.toArray()).subscribe((dbModels) => { + for (let { speckleBranchUrl, geometries } of dbModels) { if (!(speckleBranchUrl in models)) { const loadedModels: Record = pipe( geometries, R.map((x) => loader.parse(x) as BufferGeometry), R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { - const el = ifcTagToElement({ - systemId, - elements: dbElements, - ifcTag, - }) - if (!el) return acc + geometry.computeVertexNormals() return { ...acc, - [el.name]: geometry, + [ifcTag]: geometry, } }) ) @@ -67,11 +48,6 @@ liveQuery(async () => { } }) -const basicMaterial = new MeshBasicMaterial({ - color: "tomato", - side: DoubleSide, -}) - export const getGeometry = ({ speckleBranchUrl, ifcTag, @@ -80,20 +56,21 @@ export const getGeometry = ({ ifcTag: string }) => models[speckleBranchUrl][ifcTag] -export const getMaterial = ({}: { - systemId?: string - houseId?: string - elementName?: string -} = {}): Material => basicMaterial - -export const moduleToGroup = ({ speckleBranchUrl }: Module) => { +export const moduleToGroup = ({ + module: { speckleBranchUrl, systemId }, + endColumn = false, +}: { + module: Module + endColumn?: boolean +}) => { const moduleGroup = new Group() const taggedModelGeometries = models[speckleBranchUrl] for (let ifcTag of Object.keys(taggedModelGeometries)) { const mesh = new Mesh( getGeometry({ speckleBranchUrl, ifcTag }), - getMaterial() + getMaterial({ systemId, ifcTag, houseId: "" }) ) + mesh.castShadow = true moduleGroup.add(mesh) } @@ -104,13 +81,24 @@ export const insert1VanillaColumn = () => { console.log("insert 1 vanilla") } -export const createColumnGroup = (gridGroups: GridGroup[]): Group => { +export const createColumnGroup = ({ + gridGroups, + endColumn = false, +}: { + gridGroups: GridGroup[] + endColumn?: boolean +}): Group => { const columnGroup = new Group() gridGroups.forEach(({ modules, y }) => { modules.forEach(({ z, module }) => { - const moduleGroup = moduleToGroup(module) - moduleGroup.position.set(0, y, z) + const moduleGroup = moduleToGroup({ module, endColumn }) + moduleGroup.scale.set(1, 1, endColumn ? 1 : -1) + moduleGroup.position.set( + 0, + y, + endColumn ? z + module.length / 2 : z - module.length / 2 + ) columnGroup.add(moduleGroup) }) }) @@ -121,8 +109,11 @@ export const createColumnGroup = (gridGroups: GridGroup[]): Group => { export const layoutToColumns = (layout: ColumnLayout): Group[] => pipe( layout, - A.map(({ gridGroups, z }) => { - const group = createColumnGroup(gridGroups) + A.mapWithIndex((i, { gridGroups, z }) => { + const group = createColumnGroup({ + gridGroups, + endColumn: i === layout.length - 1, + }) group.position.set(0, 0, z) return group }) @@ -145,6 +136,23 @@ export const getFirstHouseLayout = () => O.chain((k) => pipe(houseLayouts, R.lookup(k))) ) +export const insertVanillaColumn = (houseGroup: Group) => { + const { children } = houseGroup + + const { startColumnGroup, midColumnGroups, endColumnGroup } = pipe( + children, + A.partitionWithIndex((i) => i === 0 || i === children.length - 1), + ({ left: midColumnGroups, right: [startColumnGroup, endColumnGroup] }) => ({ + startColumnGroup, + endColumnGroup, + midColumnGroups, + }) + ) + + // to start with, how can I simply update the houseGroup + // to put a clone of the startColumnGroup +} + // export const houseToLayout = (house: House): ColumnLayout => { // return undefined as any diff --git a/app/design/ui-3d/fresh/systems.ts b/app/design/ui-3d/fresh/systems.ts new file mode 100644 index 00000000..f6e73c6b --- /dev/null +++ b/app/design/ui-3d/fresh/systems.ts @@ -0,0 +1,89 @@ +import { liveQuery } from "dexie" +import { Element } from "../../../../server/data/elements" +import { Material } from "../../../../server/data/materials" +import { DoubleSide, Material as ThreeMaterial, MeshBasicMaterial } from "three" +import systemsDB, { LastFetchStamped } from "../../../db/systems" +import { identity, pipe } from "fp-ts/lib/function" +import { O, R } from "../../../utils/functions" +import { createMaterial } from "../../../utils/three" + +export let elements: Record> = {} + +liveQuery(() => systemsDB.elements.toArray()).subscribe((dbElements) => { + for (let dbElement of dbElements) { + elements[dbElement.ifc4Variable] = dbElement + } +}) + +// specification : airtable material +export let materials: Record> = {} + +liveQuery(() => systemsDB.materials.toArray()).subscribe((dbMaterials) => { + for (let dbMaterial of dbMaterials) { + materials[dbMaterial.specification] = dbMaterial + } +}) + +const basicMaterial: ThreeMaterial = new MeshBasicMaterial({ + color: "tomato", + side: DoubleSide, +}) + +// hash(systemId, houseId, material specification) : ThreeMaterial +const threeMaterials: Record = {} + +const getMaterialHash = ({ + systemId, + houseId, + specification, +}: { + systemId: string + houseId: string + specification: string +}) => `${systemId}:${houseId}:${specification}` + +const getThreeMaterial = ({ + systemId, + houseId, + material, + material: { specification }, +}: { + systemId: string + houseId: string + material: Material +}): ThreeMaterial => { + const materialHash = getMaterialHash({ systemId, houseId, specification }) + return pipe( + threeMaterials, + R.lookup(materialHash), + O.match(() => { + threeMaterials[materialHash] = createMaterial(material) + return threeMaterials[materialHash] + }, identity) + ) +} + +export const getMaterial = ( + input: { + systemId: string + houseId: string + ifcTag: string + } | null = null +): ThreeMaterial => { + if (input === null) return basicMaterial + + const { systemId, houseId, ifcTag } = input + + return pipe( + elements, + R.lookup(ifcTag), + O.chain(({ defaultMaterial }) => + pipe( + materials, + R.lookup(defaultMaterial), + O.map((material) => getThreeMaterial({ systemId, houseId, material })) + ) + ), + O.getOrElse(() => basicMaterial) + ) +} diff --git a/app/workers/systems.ts b/app/workers/systems.ts index ea8938dd..f97bfc7c 100644 --- a/app/workers/systems.ts +++ b/app/workers/systems.ts @@ -2,6 +2,7 @@ import { vanillaTrpc } from "../../client/trpc" import { Element } from "../../server/data/elements" import { HouseType } from "../../server/data/houseTypes" import { LevelType } from "../../server/data/levelTypes" +import { Material } from "../../server/data/materials" import { Module } from "../../server/data/modules" import { SectionType } from "../../server/data/sectionTypes" import systemsDB, { LastFetchStamped } from "../db/systems" @@ -69,6 +70,28 @@ const initElements = async () => { await Promise.all(promises) } +const initMaterials = async () => { + const remoteMaterials = await vanillaTrpc.materials.query() + + const promises = remoteMaterials.map(async (remoteMaterial) => { + const localElement = await systemsDB.materials.get(remoteMaterial.id) + + const indexedMaterial: LastFetchStamped = { + ...remoteMaterial, + lastFetched: new Date().getTime(), + } + + if ( + !localElement || + remoteMaterial.lastModified > localElement.lastModified + ) { + await systemsDB.materials.put(indexedMaterial) + } + }) + + await Promise.all(promises) +} + const initSectionTypes = async () => { const remoteSectionTypes = await vanillaTrpc.sectionTypes.query() @@ -119,6 +142,7 @@ const init = () => { initModules() initHouseTypes() initElements() + initMaterials() initSectionTypes() initLevelTypes() } diff --git a/server/data/elements.ts b/server/data/elements.ts index bec46f07..f0a4e2c7 100644 --- a/server/data/elements.ts +++ b/server/data/elements.ts @@ -87,7 +87,7 @@ export const elementsQuery: QueryFn = id, systemId, name: element_code, - ifc4Variable: ifc4_variable, + ifc4Variable: ifc4_variable.toUpperCase(), defaultMaterial, materialOptions, category: element_category, diff --git a/server/data/materials.ts b/server/data/materials.ts index d683716d..9025c7e4 100644 --- a/server/data/materials.ts +++ b/server/data/materials.ts @@ -1,10 +1,9 @@ +import { systemFromId } from "@/server/data/system" +import { QueryParams } from "airtable/lib/query_params" import { pipe } from "fp-ts/lib/function" -import { MeshStandardMaterial } from "three" import * as z from "zod" -import { systemFromId } from "@/server/data/system" import { A } from "~/utils/functions" import { QueryFn } from "./types" -import { QueryParams } from "airtable/lib/query_params" export interface Material { id: string @@ -18,7 +17,7 @@ export interface Material { costPerUnit: number embodiedCarbonPerUnit: number // kg unit: string | null - threeMaterial?: MeshStandardMaterial + lastModified: number } export const materialSelector: QueryParams = { @@ -45,6 +44,20 @@ export const materialParser = z embodied_carbon_cost_per_unit: z.number().default(0), link_url: z.string().optional(), unit: z.string().nullable().default(null), + last_modified: z + .string() + .refine( + (value) => { + // Attempt to parse the value as a date and check that it's valid + const date = new Date(value) + return !isNaN(date.getTime()) + }, + { + // Custom error message + message: "Invalid date string", + } + ) + .transform((x) => new Date(x).getTime()), }), }) .transform( @@ -60,6 +73,7 @@ export const materialParser = z embodied_carbon_cost_per_unit, link_url, unit, + last_modified, }, }) => ({ id, @@ -72,6 +86,7 @@ export const materialParser = z embodiedCarbonPerUnit: embodied_carbon_cost_per_unit, linkUrl: link_url, unit, + lastModified: last_modified, }) ) From 7b50b1e6a28ce64d243f86a9000b2bf0ebacc46d Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 11 Jul 2023 21:31:52 +0100 Subject: [PATCH 038/132] wip pre houseLayouts updates --- app/design/ui-3d/fresh/helpers.ts | 36 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 601f6fc6..8502a5d8 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -77,10 +77,6 @@ export const moduleToGroup = ({ return moduleGroup } -export const insert1VanillaColumn = () => { - console.log("insert 1 vanilla") -} - export const createColumnGroup = ({ gridGroups, endColumn = false, @@ -109,12 +105,21 @@ export const createColumnGroup = ({ export const layoutToColumns = (layout: ColumnLayout): Group[] => pipe( layout, - A.mapWithIndex((i, { gridGroups, z }) => { + A.mapWithIndex((i, { gridGroups, z, columnIndex, length }) => { + const startColumn = i === 0 + const endColumn = i === layout.length - 1 + const group = createColumnGroup({ gridGroups, - endColumn: i === layout.length - 1, + endColumn, }) group.position.set(0, 0, z) + group.userData = { + columnIndex, + length, + endColumn, + startColumn, + } return group }) ) @@ -136,21 +141,18 @@ export const getFirstHouseLayout = () => O.chain((k) => pipe(houseLayouts, R.lookup(k))) ) -export const insertVanillaColumn = (houseGroup: Group) => { +export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { const { children } = houseGroup - const { startColumnGroup, midColumnGroups, endColumnGroup } = pipe( + const vanillaColumn = undefined as any // get the vanilla column ready + + pipe( children, - A.partitionWithIndex((i) => i === 0 || i === children.length - 1), - ({ left: midColumnGroups, right: [startColumnGroup, endColumnGroup] }) => ({ - startColumnGroup, - endColumnGroup, - midColumnGroups, - }) + A.findFirst((x) => + direction === 1 ? x.userData.endColumn : x.userData.startColumn + ), + O.map((x) => {}) ) - - // to start with, how can I simply update the houseGroup - // to put a clone of the startColumnGroup } // export const houseToLayout = (house: House): ColumnLayout => { From 7dcef52412e6aabbfc65bd2cae1597e3a90a3d02 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 12 Jul 2023 08:54:15 +0100 Subject: [PATCH 039/132] wippy wip --- app/db/layouts.ts | 48 ++++++++++++++----- app/design/state/dimensions.tsx | 4 +- app/design/state/hashedMaterials.ts | 2 +- app/design/state/interactions/ZStretch.tsx | 8 ++-- app/design/state/interactions/layouts.ts | 8 ++-- app/design/state/interactions/windows.ts | 4 +- app/design/state/layouts.ts | 13 ++--- app/design/state/scope.ts | 6 +-- app/design/state/vanilla.ts | 11 +++-- app/design/ui-3d/fresh/helpers.ts | 27 +++++++++-- app/design/ui-3d/grouped/GroupedHouse.tsx | 4 +- app/design/ui-3d/grouped/GroupedHouse2.tsx | 8 ++-- .../stretchLength/GroupedStretchColumn.tsx | 4 +- app/workers/layouts/vanilla.ts | 2 +- app/workers/layouts/worker.ts | 29 ++++++----- 15 files changed, 115 insertions(+), 63 deletions(-) diff --git a/app/db/layouts.ts b/app/db/layouts.ts index ffabd874..c61abf76 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -52,20 +52,30 @@ export type SystemHouseModuleIdentifier = HouseModuleIdentifier & { export type ColumnLayout = Array -export type ParsedModel = { +export type IndexedModel = LastFetchStamped<{ speckleBranchUrl: string geometries: any systemId: string +}> + +export type HouseLayoutsKey = { + systemId: string + dnas: string[] } -export type IndexedLayout = { - layoutsKey: string - layout: ColumnLayout +export const getHouseLayoutsKey = ({ systemId, dnas }: HouseLayoutsKey) => + `${systemId}:${dnas}` + +export const invertHouseLayoutsKey = (key: string): HouseLayoutsKey => { + const [systemId, dnasString] = key.split(":") + const dnas = dnasString.split(",") + return { systemId, dnas } } -export type LayoutKey = { +export type IndexedLayout = { systemId: string dnas: string[] + layout: ColumnLayout } export type IndexedVanillaModule = { @@ -80,15 +90,29 @@ export type IndexedVanillaModule = { export type VanillaColumn = Omit export type IndexedVanillaColumn = { - layoutsKey: string + systemId: string + levelTypes: string[] vanillaColumn: VanillaColumn } -export const serializeLayoutKey = ({ systemId, dnas }: LayoutKey) => - `${systemId}:${dnas}` +export type VanillaColumnsKey = { + systemId: string + levelTypes: string[] +} + +export const getVanillaColumnsKey = ({ + systemId, + levelTypes, +}: VanillaColumnsKey): string => `${systemId}:${levelTypes}` + +export const invertVanillaColumnsKey = (key: string): VanillaColumnsKey => { + const [systemId, levelTypesString] = key.split(":") + const levelTypes = levelTypesString.split(",") + return { systemId, levelTypes } +} class LayoutsDatabase extends Dexie { - models: Dexie.Table, string> + models: Dexie.Table houseLayouts: Dexie.Table vanillaModules: Dexie.Table vanillaColumns: Dexie.Table @@ -97,11 +121,11 @@ class LayoutsDatabase extends Dexie { super("LayoutsDatabase") this.version(1).stores({ models: "speckleBranchUrl,systemId", - layouts: "layoutsKey", + houseLayouts: "[systemId+dnas]", vanillaModules: "[systemId+sectionType+positionType+levelType+gridType]", - vanillaColumns: "layoutsKey", + vanillaColumns: "[systemId+levelTypes]", }) - this.houseLayouts = this.table("layouts") + this.houseLayouts = this.table("houseLayouts") this.models = this.table("models") this.vanillaModules = this.table("vanillaModules") this.vanillaColumns = this.table("vanillaColumns") diff --git a/app/design/state/dimensions.tsx b/app/design/state/dimensions.tsx index 58efb0c1..4bcdaa39 100644 --- a/app/design/state/dimensions.tsx +++ b/app/design/state/dimensions.tsx @@ -5,7 +5,7 @@ import { OBB } from "three-stdlib" import { proxy, ref, useSnapshot } from "valtio" import { useSubscribeKey } from "~/utils/hooks" import { yAxis } from "~/utils/three" -import { serializeLayoutKey } from "../../db/layouts" +import { getHouseLayoutsKey } from "../../db/layouts" import houses, { useHouse } from "./houses" import { layouts } from "./layouts" import { postTransformsTransients, Transforms } from "./transients/transforms" @@ -72,7 +72,7 @@ export const usePostTransMatrix = (houseId: string) => { export const useLayoutsKey = (houseId: string) => { const { systemId, dnas } = useHouse(houseId) - return serializeLayoutKey({ systemId, dnas }) + return getHouseLayoutsKey({ systemId, dnas }) } export const useComputeDimensions = (houseId: string) => { diff --git a/app/design/state/hashedMaterials.ts b/app/design/state/hashedMaterials.ts index 94e3a929..c7ff5556 100644 --- a/app/design/state/hashedMaterials.ts +++ b/app/design/state/hashedMaterials.ts @@ -15,7 +15,7 @@ import { useHousePreviews } from "./previews" import settings from "./settings" import siteCtx from "./siteCtx" import { postTransformsTransients } from "./transients/transforms" -import { LayoutKey } from "../../db/layouts" +import { HouseLayoutsKey } from "../../db/layouts" const getMaterialHash = ({ systemId, diff --git a/app/design/state/interactions/ZStretch.tsx b/app/design/state/interactions/ZStretch.tsx index 038e06ad..20e5ca78 100644 --- a/app/design/state/interactions/ZStretch.tsx +++ b/app/design/state/interactions/ZStretch.tsx @@ -6,9 +6,9 @@ import { suspend } from "suspend-react" import { Group, Matrix4, Vector3 } from "three" import { OBB } from "three-stdlib" import layoutsDB, { - LayoutKey, + HouseLayoutsKey, PositionedColumn, - serializeLayoutKey, + getHouseLayoutsKey, } from "../../../db/layouts" import { A, NEA } from "../../../utils/functions" import { floor, max, round, sign } from "../../../utils/math" @@ -27,7 +27,7 @@ import { vanillaColumns } from "../vanilla" type Props = { houseId: string - layoutKey: LayoutKey + layoutKey: HouseLayoutsKey reactHouseMatrix: Matrix4 width: number height: number @@ -54,7 +54,7 @@ const ZStretch = ({ }: Props) => { console.log("ZStretch") const { systemId } = layoutKey - const strLayoutKey = serializeLayoutKey(layoutKey) + const strLayoutKey = getHouseLayoutsKey(layoutKey) const vanillaColumn = suspend(async () => { if (strLayoutKey in vanillaColumns) { diff --git a/app/design/state/interactions/layouts.ts b/app/design/state/interactions/layouts.ts index bff20c6d..9c7c55fd 100644 --- a/app/design/state/interactions/layouts.ts +++ b/app/design/state/interactions/layouts.ts @@ -22,7 +22,7 @@ import { ColumnLayout, HouseModuleIdentifier, PositionedModule, - serializeLayoutKey, + getHouseLayoutsKey, } from "../../../db/layouts" import { columnLayoutToDnas } from "../../../workers/layouts/worker" @@ -36,7 +36,7 @@ export const useChangeModuleLayout = ({ const getVanillaModule = useGetVanillaModule(systemId) const columnLayout = - layouts[serializeLayoutKey({ systemId, dnas: houses[houseId].dnas })] + layouts[getHouseLayoutsKey({ systemId, dnas: houses[houseId].dnas })] const oldModule = columnLayout[columnIndex].gridGroups[levelIndex].modules[gridGroupIndex] @@ -149,7 +149,7 @@ export const useLayoutOptions = ({ } => { const systemId = houses[houseId].systemId const layout = - layouts[serializeLayoutKey({ systemId, dnas: houses[houseId].dnas })] + layouts[getHouseLayoutsKey({ systemId, dnas: houses[houseId].dnas })] const m = layout[columnIndex].gridGroups[levelIndex].modules[gridGroupIndex].module @@ -213,7 +213,7 @@ export const useStairsOptions = ({ } => { const systemId = houses[houseId].systemId const layout = - layouts[serializeLayoutKey({ systemId, dnas: houses[houseId].dnas })] + layouts[getHouseLayoutsKey({ systemId, dnas: houses[houseId].dnas })] const stairTypes = useSystemStairTypes({ systemId }) diff --git a/app/design/state/interactions/windows.ts b/app/design/state/interactions/windows.ts index db605a79..28a4193c 100644 --- a/app/design/state/interactions/windows.ts +++ b/app/design/state/interactions/windows.ts @@ -9,7 +9,7 @@ import { getSide, Side } from "~/design/state/camera" import { layouts } from "~/design/state/layouts" import siteCtx from "~/design/state/siteCtx" import { useChangeModuleLayout } from "./layouts" -import { HouseModuleIdentifier, serializeLayoutKey } from "../../../db/layouts" +import { HouseModuleIdentifier, getHouseLayoutsKey } from "../../../db/layouts" import houses from "../houses" import { columnLayoutToDnas } from "../../../workers/layouts/worker" @@ -28,7 +28,7 @@ export const useWindowOptions = ({ options: WindowTypeOption[] selected: WindowTypeOption["value"] } => { - const layoutsKey = serializeLayoutKey({ + const layoutsKey = getHouseLayoutsKey({ systemId: houses[houseId].systemId, dnas: houses[houseId].dnas, }) diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index b387424b..9678b022 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -11,8 +11,8 @@ import { usePadColumn } from "../../data/modules" import layoutsDB, { ColumnLayout, HouseModuleIdentifier, - LayoutKey, - serializeLayoutKey, + HouseLayoutsKey, + getHouseLayoutsKey, } from "../../db/layouts" import { isSSR } from "../../utils/next" import { getLayoutsWorker } from "../../workers" @@ -24,7 +24,8 @@ export const layouts = proxy< if (!isSSR()) { liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe((dbLayouts) => { - for (let { layoutsKey, layout } of dbLayouts) { + for (let { systemId, dnas, layout } of dbLayouts) { + const layoutsKey = getHouseLayoutsKey({ systemId, dnas }) if (!(layoutsKey in layouts)) { layouts[layoutsKey] = ref(layout) } @@ -32,9 +33,9 @@ if (!isSSR()) { }) } -export const useDnasLayout = (layoutsKey: LayoutKey): ColumnLayout => { +export const useDnasLayout = (layoutsKey: HouseLayoutsKey): ColumnLayout => { const snap = useSnapshot(layouts) as typeof layouts - const serialKey = serializeLayoutKey(layoutsKey) + const serialKey = getHouseLayoutsKey(layoutsKey) const maybeLayout: ColumnLayout | undefined = snap?.[serialKey] return suspend(async () => { @@ -77,7 +78,7 @@ export const useColumnMatrix = (houseId: string) => { const { systemId, dnas } = useHouse(houseId) const layoutsSnap = useSnapshot(layouts) as typeof layouts return columnLayoutToMatrix( - layoutsSnap[serializeLayoutKey({ systemId, dnas })] + layoutsSnap[getHouseLayoutsKey({ systemId, dnas })] ) } diff --git a/app/design/state/scope.ts b/app/design/state/scope.ts index 1bbfa4af..b759c7f6 100644 --- a/app/design/state/scope.ts +++ b/app/design/state/scope.ts @@ -3,7 +3,7 @@ import { pipe } from "fp-ts/lib/function" import { proxy, useSnapshot } from "valtio" import { A, O } from "~/utils/functions" import { columnLayoutToMatrix, layouts } from "~/design/state/layouts" -import { serializeLayoutKey } from "../../db/layouts" +import { getHouseLayoutsKey } from "../../db/layouts" import houses from "./houses" export type ScopeItem = { @@ -32,7 +32,7 @@ export const getSelectedModule = () => { const { systemId, dnas } = houses[houseId] - const layoutsKey = serializeLayoutKey({ systemId, dnas }) + const layoutsKey = getHouseLayoutsKey({ systemId, dnas }) return layouts[layoutsKey][columnIndex].gridGroups[levelIndex].modules[ gridGroupIndex @@ -46,7 +46,7 @@ export const getSelectedColumnMatrix = () => { if (scope.selected === null) return null const { houseId } = scope.selected const { systemId, dnas } = houses[houseId] - const layoutsKey = serializeLayoutKey({ systemId, dnas }) + const layoutsKey = getHouseLayoutsKey({ systemId, dnas }) return columnLayoutToMatrix(layouts[layoutsKey]) } export const getSelectedLevelModules = () => { diff --git a/app/design/state/vanilla.ts b/app/design/state/vanilla.ts index e4eb8a2f..8537f68a 100644 --- a/app/design/state/vanilla.ts +++ b/app/design/state/vanilla.ts @@ -4,7 +4,11 @@ import { pipe } from "fp-ts/lib/function" import { proxy } from "valtio" import { A, all, O, Ord, RA, S, someOrError } from "~/utils/functions" import { useSystemModules } from "../../data/modules" -import layoutsDB, { PositionedRow, VanillaColumn } from "../../db/layouts" +import layoutsDB, { + getVanillaColumnsKey, + PositionedRow, + VanillaColumn, +} from "../../db/layouts" import { isSSR } from "../../utils/next" export const vanillaModules = proxy>({}) @@ -38,8 +42,9 @@ if (!isSSR()) { if (!isSSR()) { liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( (dbVanillaColumns) => { - for (let { layoutsKey, vanillaColumn } of dbVanillaColumns) { - vanillaColumns[layoutsKey] = vanillaColumn + for (let { systemId, levelTypes, vanillaColumn } of dbVanillaColumns) { + const vanillaColumnsKey = getVanillaColumnsKey({ systemId, levelTypes }) + vanillaColumns[vanillaColumnsKey] = vanillaColumn } } ) diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 8502a5d8..8f5c9f2e 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -6,7 +6,11 @@ import { ifcTagToElement } from "../../../data/elements" import layoutsDB, { ColumnLayout, GridGroup, + getHouseLayoutsKey, VanillaColumn, + getVanillaColumnsKey, + VanillaColumnsKey, + invertVanillaColumnsKey, } from "../../../db/layouts" import systemsDB from "../../../db/systems" import { A, O, R, S } from "../../../utils/functions" @@ -15,11 +19,17 @@ import { getMaterial } from "./systems" // serialized layout key : column export let vanillaColumns: Record = {} +const getVanillaColumn = ({ systemId, levelTypes }: VanillaColumnsKey) => { + const key = getVanillaColumnsKey({ systemId, levelTypes }) + return vanillaColumns[key] +} + liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( (dbVanillaColumns) => { for (let dbVanillaColumn of dbVanillaColumns) { - const { layoutsKey, vanillaColumn } = dbVanillaColumn - vanillaColumns[layoutsKey] = vanillaColumn + const { systemId, levelTypes, vanillaColumn } = dbVanillaColumn + vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] = + vanillaColumn } } ) @@ -128,8 +138,11 @@ export let houseLayouts: Record = {} liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( (dbHouseLayouts) => { - for (let { layoutsKey, layout } of dbHouseLayouts) - houseLayouts[layoutsKey] = layout + for (let { systemId, dnas, layout } of dbHouseLayouts) { + houseLayouts[getHouseLayoutsKey({ systemId, dnas })] = layout + } + + console.log(houseLayouts) } ) @@ -144,7 +157,11 @@ export const getFirstHouseLayout = () => export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { const { children } = houseGroup - const vanillaColumn = undefined as any // get the vanilla column ready + const levelTypes = pipe( + children + // write me... + // or maybe we put levelTypes on the house group's userData? + ) pipe( children, diff --git a/app/design/ui-3d/grouped/GroupedHouse.tsx b/app/design/ui-3d/grouped/GroupedHouse.tsx index 9d80e746..8734d42d 100644 --- a/app/design/ui-3d/grouped/GroupedHouse.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse.tsx @@ -10,7 +10,7 @@ import { usePreTransformsTransients, } from "~/design/state/transients/transforms" import { RA } from "~/utils/functions" -import { serializeLayoutKey } from "../../../db/layouts" +import { getHouseLayoutsKey } from "../../../db/layouts" import { useHouse, useHouseSystemId } from "../../state/houses" import { useTransformabilityBooleans } from "../../state/siteCtx" import RotateHandles from "../handles/RotateHandles" @@ -73,7 +73,7 @@ const GroupedHouse = (props: Props) => { useHouseMaterialOps({ houseId, ref: houseGroupRef, - layoutsKey: serializeLayoutKey({ systemId, dnas }), + layoutsKey: getHouseLayoutsKey({ systemId, dnas }), }) return null diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 5e71042d..24323e7e 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -4,7 +4,7 @@ import { Fragment, useEffect, useMemo, useRef } from "react" import { useKey } from "react-use" import { Box3, Group, Matrix4, Vector3 } from "three" import { OBB } from "three-stdlib" -import { LayoutKey, serializeLayoutKey } from "../../../db/layouts" +import { HouseLayoutsKey, getHouseLayoutsKey } from "../../../db/layouts" import { House } from "../../../db/user" import { A, RA } from "../../../utils/functions" import { @@ -37,7 +37,7 @@ const GroupedHouse2 = (props: Props) => { const { systemId, id: houseId, position, rotation } = house const dnas = [...house.dnas] - const layoutKey: LayoutKey = { systemId, dnas } + const layoutKey: HouseLayoutsKey = { systemId, dnas } const translationMatrix = useMemo(() => { const m = new Matrix4() @@ -173,7 +173,7 @@ const GroupedHouse2 = (props: Props) => { useHouseMaterialOps({ houseId, ref: rootRef, - layoutsKey: serializeLayoutKey({ systemId, dnas }), + layoutsKey: getHouseLayoutsKey({ systemId, dnas }), }) const startColumnRef = useRef(null!) @@ -188,7 +188,7 @@ const GroupedHouse2 = (props: Props) => { useKey( "l", () => { - const vanillaColumn = vanillaColumns[serializeLayoutKey(layoutKey)] + const vanillaColumn = vanillaColumns[getHouseLayoutsKey(layoutKey)] setHouse({ ...house, diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx index faa713f4..f703150f 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx @@ -3,7 +3,7 @@ import { pipe } from "fp-ts/lib/function" import { useEffect, useRef } from "react" import { Group } from "three" import { RA } from "~/utils/functions" -import { GridGroup, LayoutKey } from "../../../../db/layouts" +import { GridGroup, HouseLayoutsKey } from "../../../../db/layouts" import { getLayoutsWorker } from "../../../../workers" import { useZStretchHouseListener } from "../../../state/events" import GroupedStretchModule from "./GroupedStretchModule" @@ -16,7 +16,7 @@ type Props = { columnZ: number columnLength: number i: number - layoutKey: LayoutKey + layoutKey: HouseLayoutsKey } const GroupedStretchColumn = (props: Props) => { diff --git a/app/workers/layouts/vanilla.ts b/app/workers/layouts/vanilla.ts index 02ff6969..eda29b9e 100644 --- a/app/workers/layouts/vanilla.ts +++ b/app/workers/layouts/vanilla.ts @@ -109,7 +109,7 @@ export const getIndexedVanillaModule = ({ liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe((dbLayouts) => { for (let dbLayout of dbLayouts) { - const { layoutsKey } = dbLayout + // const { layoutsKey } = dbLayout } }) diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 36ed51ad..38543289 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -7,11 +7,11 @@ import produce from "immer" import { Module } from "../../../server/data/modules" import layoutsDB, { ColumnLayout, - LayoutKey, + HouseLayoutsKey, PositionedColumn, PositionedModule, PositionedRow, - serializeLayoutKey, + getHouseLayoutsKey, } from "../../db/layouts" import systemsDB, { LastFetchStamped } from "../../db/systems" import { A, O } from "../../utils/functions" @@ -20,7 +20,7 @@ import { syncModels } from "./models" import { createVanillaModuleGetter } from "./vanilla" let modulesCache: LastFetchStamped[] = [] -let layoutsQueue: LayoutKey[] = [] +let layoutsQueue: HouseLayoutsKey[] = [] const modulesToRows = (modules: Module[]): Module[][] => { const jumpIndices = pipe( @@ -282,7 +282,7 @@ export const splitColumns = (layout: ColumnLayout) => }) ) -const processLayout = async ({ systemId, dnas }: LayoutKey) => { +const processLayout = async ({ systemId, dnas }: HouseLayoutsKey) => { const modules = pipe( dnas, A.filterMap((dna) => @@ -298,11 +298,10 @@ const processLayout = async ({ systemId, dnas }: LayoutKey) => { const layout = modulesToColumnLayout(modules) - const layoutsKey = serializeLayoutKey({ systemId, dnas }) - layoutsDB.houseLayouts.put({ layout, - layoutsKey, + systemId, + dnas, }) const { @@ -342,8 +341,14 @@ const processLayout = async ({ systemId, dnas }: LayoutKey) => { ) ), O.map((gridGroups) => { + const levelTypes = pipe( + gridGroups, + A.map((gridGroup) => gridGroup.levelType) + ) + layoutsDB.vanillaColumns.put({ - layoutsKey, + systemId, + levelTypes, vanillaColumn: { gridGroups, length: gridGroups[0].length, @@ -377,11 +382,11 @@ if (!isSSR()) { }) } -const postLayout = (key: LayoutKey) => { +const postLayout = (key: HouseLayoutsKey) => { layoutsQueue.push(key) } -const postLayouts = (keys: LayoutKey[]) => { +const postLayouts = (keys: HouseLayoutsKey[]) => { keys.map(postLayout) } @@ -418,12 +423,12 @@ const processZStretchLayout = async ({ direction, i, }: { - layoutKey: LayoutKey + layoutKey: HouseLayoutsKey direction: number i: number }) => { const { systemId } = layoutKey - const strLayoutKey = serializeLayoutKey(layoutKey) + const strLayoutKey = getHouseLayoutsKey(layoutKey) const layout = await layoutsDB.houseLayouts.get(strLayoutKey) if (!layout) { console.log(`no layout for ${strLayoutKey}`) From 337f7746f8c7b4e1aaacace3a97cdbc0f1f5b4bf Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 12 Jul 2023 09:40:50 +0100 Subject: [PATCH 040/132] wip adding house --- app/design/state/events/houses.ts | 27 ++++++ app/design/state/events/moveRotate.ts | 59 ++++++++++++ .../state/events/{index.ts => stretch.ts} | 58 ------------ app/design/state/gestures/index.ts | 4 +- app/design/state/interactions/ZStretch.tsx | 2 +- app/design/ui-3d/fresh/FreshApp.tsx | 90 +++++++++++++++---- app/design/ui-3d/fresh/helpers.ts | 45 +++++++++- app/design/ui-3d/grouped/GroupedHouse2.tsx | 8 +- .../stretchLength/GroupedStretchColumn.tsx | 2 +- app/design/ui/SiteSidebar.tsx | 30 +++---- 10 files changed, 224 insertions(+), 101 deletions(-) create mode 100644 app/design/state/events/houses.ts create mode 100644 app/design/state/events/moveRotate.ts rename app/design/state/events/{index.ts => stretch.ts} (50%) diff --git a/app/design/state/events/houses.ts b/app/design/state/events/houses.ts new file mode 100644 index 00000000..3a000190 --- /dev/null +++ b/app/design/state/events/houses.ts @@ -0,0 +1,27 @@ +import { useEvent } from "react-use" +import { HouseType } from "../../../../server/data/houseTypes" +import { House } from "../../../db/user" + +const ADD_HOUSE_INTENT_EVENT = "AddHouseIntentEvent" +const ADD_HOUSE_EVENT = "AddHouseEvent" + +export const dispatchAddHouseIntent = (detail: HouseType) => + dispatchEvent( + new CustomEvent(ADD_HOUSE_INTENT_EVENT, { + detail, + }) + ) + +export const useAddHouseIntentListener = ( + f: (eventDetail: HouseType) => void +) => useEvent(ADD_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) + +export const dispatchAddHouse = (detail: House) => + dispatchEvent( + new CustomEvent(ADD_HOUSE_EVENT, { + detail, + }) + ) + +export const useAddHouseListener = (f: (eventDetail: House) => void) => + useEvent(ADD_HOUSE_EVENT, ({ detail }) => f(detail)) diff --git a/app/design/state/events/moveRotate.ts b/app/design/state/events/moveRotate.ts new file mode 100644 index 00000000..cf4aac30 --- /dev/null +++ b/app/design/state/events/moveRotate.ts @@ -0,0 +1,59 @@ +import { useEvent } from "react-use" + +const MOVE_HOUSE_INTENT_EVENT = "MoveHouseIntentEvent" +const MOVE_HOUSE_EVENT = "MoveHouseEvent" + +const ROTATE_HOUSE_EVENT = "RotateHouseEvent" +const ROTATE_HOUSE_INTENT_EVENT = "RotateHouseIntentEvent" + +type MoveHouseDetail = { + delta: V3 + houseId: string + last: boolean +} + +type RotateHouseDetail = { + rotation: number + houseId: string + last: boolean +} + +export const dispatchMoveHouseIntent = (detail: MoveHouseDetail) => + dispatchEvent( + new CustomEvent(MOVE_HOUSE_INTENT_EVENT, { + detail, + }) + ) + +export const dispatchMoveHouse = (detail: MoveHouseDetail) => + dispatchEvent( + new CustomEvent(MOVE_HOUSE_EVENT, { + detail, + }) + ) + +export const useMoveHouseIntentListener = ( + f: (eventDetail: MoveHouseDetail) => void +) => useEvent(MOVE_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) + +export const useMoveHouseListener = ( + f: (eventDetail: MoveHouseDetail) => void +) => useEvent(MOVE_HOUSE_EVENT, ({ detail }) => f(detail)) + +export const dispatchRotateHouseIntent = (detail: RotateHouseDetail) => + dispatchEvent(new CustomEvent(ROTATE_HOUSE_INTENT_EVENT, { detail })) + +export const dispatchRotateHouse = (detail: RotateHouseDetail) => + dispatchEvent( + new CustomEvent(ROTATE_HOUSE_EVENT, { + detail, + }) + ) + +export const useRotateHouseIntentListener = ( + f: (eventDetail: RotateHouseDetail) => void +) => useEvent(ROTATE_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) + +export const useRotateHouseListener = ( + f: (eventDetail: RotateHouseDetail) => void +) => useEvent(ROTATE_HOUSE_EVENT, ({ detail }) => f(detail)) diff --git a/app/design/state/events/index.ts b/app/design/state/events/stretch.ts similarity index 50% rename from app/design/state/events/index.ts rename to app/design/state/events/stretch.ts index 20a9b4c9..0a948b15 100644 --- a/app/design/state/events/index.ts +++ b/app/design/state/events/stretch.ts @@ -1,29 +1,11 @@ import { useEvent } from "react-use" -const MOVE_HOUSE_INTENT_EVENT = "MoveHouseIntentEvent" -const MOVE_HOUSE_EVENT = "MoveHouseEvent" - -const ROTATE_HOUSE_EVENT = "RotateHouseEvent" -const ROTATE_HOUSE_INTENT_EVENT = "RotateHouseIntentEvent" - const Z_STRETCH_HOUSE_INTENT_EVENT = "ZStretchHouseIntentEvent" const Z_STRETCH_HOUSE_EVENT = "ZStretchHouseEvent" const X_STRETCH_HOUSE_INTENT_EVENT = "XStretchHouseIntentEvent" const X_STRETCH_HOUSE_EVENT = "XStretchHouseEvent" -type MoveHouseDetail = { - delta: V3 - houseId: string - last: boolean -} - -type RotateHouseDetail = { - rotation: number - houseId: string - last: boolean -} - type StretchHouseDetail = { houseId: string direction: 1 | -1 @@ -33,46 +15,6 @@ type StretchHouseDetail = { last: boolean } -export const dispatchMoveHouseIntent = (detail: MoveHouseDetail) => - dispatchEvent( - new CustomEvent(MOVE_HOUSE_INTENT_EVENT, { - detail, - }) - ) - -export const dispatchMoveHouse = (detail: MoveHouseDetail) => - dispatchEvent( - new CustomEvent(MOVE_HOUSE_EVENT, { - detail, - }) - ) - -export const useMoveHouseIntentListener = ( - f: (eventDetail: MoveHouseDetail) => void -) => useEvent(MOVE_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) - -export const useMoveHouseListener = ( - f: (eventDetail: MoveHouseDetail) => void -) => useEvent(MOVE_HOUSE_EVENT, ({ detail }) => f(detail)) - -export const dispatchRotateHouseIntent = (detail: RotateHouseDetail) => - dispatchEvent(new CustomEvent(ROTATE_HOUSE_INTENT_EVENT, { detail })) - -export const dispatchRotateHouse = (detail: RotateHouseDetail) => - dispatchEvent( - new CustomEvent(ROTATE_HOUSE_EVENT, { - detail, - }) - ) - -export const useRotateHouseIntentListener = ( - f: (eventDetail: RotateHouseDetail) => void -) => useEvent(ROTATE_HOUSE_INTENT_EVENT, ({ detail }) => f(detail)) - -export const useRotateHouseListener = ( - f: (eventDetail: RotateHouseDetail) => void -) => useEvent(ROTATE_HOUSE_EVENT, ({ detail }) => f(detail)) - export const dispatchZStretchHouseIntent = (detail: StretchHouseDetail) => dispatchEvent(new CustomEvent(Z_STRETCH_HOUSE_INTENT_EVENT, { detail })) diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index c91dd292..6f8ada93 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -10,11 +10,11 @@ import { useSubscribeKey } from "~/utils/hooks" import { isMesh, useRotations } from "~/utils/three" import { setCameraEnabled } from "../camera" import { getHouseCenter } from "../dimensions" +import { dispatchMoveHouseIntent } from "../events/moveRotate" import { - dispatchMoveHouseIntent, dispatchXStretchHouseIntent, dispatchZStretchHouseIntent, -} from "../events" +} from "../events/stretch" import { openMenu } from "../menu" import pointer from "../pointer" import siteCtx, { downMode, EditModeEnum } from "../siteCtx" diff --git a/app/design/state/interactions/ZStretch.tsx b/app/design/state/interactions/ZStretch.tsx index 20e5ca78..74ae9998 100644 --- a/app/design/state/interactions/ZStretch.tsx +++ b/app/design/state/interactions/ZStretch.tsx @@ -21,7 +21,7 @@ import { dispatchZStretchHouse, useZStretchHouseIntentListener, useZStretchHouseListener, -} from "../events" +} from "../events/stretch" import houses, { useSetHouse } from "../houses" import { vanillaColumns } from "../vanilla" diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index ccc7976f..f8bdba14 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,13 +1,28 @@ import { useGesture } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { useEffect, useRef } from "react" -import { Group } from "three" -import { A, O } from "../../../utils/functions" -import { getFirstHouseLayout, layoutToColumns } from "./helpers" +import { Group, Vector3 } from "three" +import { getHouseLayoutsKey } from "../../../db/layouts" +import userDB from "../../../db/user" +import { A, O, R } from "../../../utils/functions" +import { + dispatchAddHouse, + useAddHouseIntentListener, + useAddHouseListener, +} from "../../state/events/houses" +import { + createHouseGroup, + getFirstHouseLayout, + houseLayouts, + layoutToColumns, +} from "./helpers" +import { nanoid } from "nanoid" const FreshApp = () => { const rootRef = useRef(null) + const houseGroupRefs = useRef([]) + // forget this for now, just useKey const bindAll = useGesture({ onClick: console.log, @@ -20,20 +35,33 @@ const FreshApp = () => { const init = () => { if (!rootRef.current) return - pipe( - getFirstHouseLayout(), - O.map((layout) => - pipe( - layout, - layoutToColumns, - A.map((columnGroup) => { - rootRef.current!.add(columnGroup) - }) - ) - ) - ) + // get houses instead - console.log(rootRef.current) + userDB.houses.toArray().then((houses) => { + pipe( + houses, + A.map(async (house) => { + const houseGroup = await createHouseGroup(house) + rootRef.current!.add(houseGroup) + console.log(`added house ${house.id}`) + }) + // A.map(({ systemId, dnas }) => + // pipe( + // houseLayouts, + // R.lookup(getHouseLayoutsKey({ systemId, dnas })), + // O.map((layout) => + // pipe( + // layout, + // layoutToColumns, + // A.map((columnGroup) => { + // rootRef.current!.add(columnGroup) + // }) + // ) + // ) + // ) + // ) + ) + }) return cleanup } @@ -41,6 +69,36 @@ const FreshApp = () => { useEffect(init, []) // useKey("l", insert1VanillaColumn) + useAddHouseIntentListener(({ dnas, id: houseTypeId, systemId }) => { + // maybe cameraGroundRaycast + // maybe collisions + + const id = nanoid() + const position = new Vector3(0, 0, 0) + + const getFriendlyName = () => "yo" // Object.keys(houses).length + 1 + + const friendlyName = getFriendlyName() + + dispatchAddHouse({ + id, + systemId, + houseTypeId, + dnas, + position, + friendlyName, + modifiedMaterials: {}, + rotation: 0, + }) + }) + + useAddHouseListener(async (house) => { + if (!rootRef.current) return + const houseGroup = await createHouseGroup(house) + rootRef.current.add(houseGroup) + console.log(`added house ${house.id}`) + }) + return } diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 8f5c9f2e..5520bf34 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -13,7 +13,9 @@ import layoutsDB, { invertVanillaColumnsKey, } from "../../../db/layouts" import systemsDB from "../../../db/systems" +import { House } from "../../../db/user" import { A, O, R, S } from "../../../utils/functions" +import { getLayoutsWorker } from "../../../workers" import { getMaterial } from "./systems" // serialized layout key : column @@ -141,8 +143,6 @@ liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( for (let { systemId, dnas, layout } of dbHouseLayouts) { houseLayouts[getHouseLayoutsKey({ systemId, dnas })] = layout } - - console.log(houseLayouts) } ) @@ -172,6 +172,47 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { ) } +export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => + pipe( + columnLayout, + A.head, + O.map(({ gridGroups }) => + pipe( + gridGroups, + A.map(({ levelType }) => levelType) + ) + ), + O.getOrElse((): string[] => []) + ) + +export const createHouseGroup = async (house: House) => { + const { systemId, dnas } = house + + const houseLayoutToHouseGroup = async (houseLayout: ColumnLayout) => { + const columnGroups = layoutToColumns(houseLayout) + const houseGroup = new Group() + houseGroup.userData = { + ...house, + levelTypes: houseLayoutToLevelTypes(houseLayout), + } + houseGroup.add(...columnGroups) + return houseGroup + } + + const houseGroup = pipe( + houseLayouts, + R.lookup(getHouseLayoutsKey({ systemId, dnas })), + O.match(async () => { + const layoutsWorker = getLayoutsWorker() + if (!layoutsWorker) throw new Error(`no layouts worker`) + const houseLayout = await layoutsWorker.processLayout({ systemId, dnas }) + return houseLayoutToHouseGroup(houseLayout) + }, houseLayoutToHouseGroup) + ) + + return houseGroup +} + // export const houseToLayout = (house: House): ColumnLayout => { // return undefined as any diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 24323e7e..c52e8bdc 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -4,7 +4,7 @@ import { Fragment, useEffect, useMemo, useRef } from "react" import { useKey } from "react-use" import { Box3, Group, Matrix4, Vector3 } from "three" import { OBB } from "three-stdlib" -import { HouseLayoutsKey, getHouseLayoutsKey } from "../../../db/layouts" +import { getHouseLayoutsKey, HouseLayoutsKey } from "../../../db/layouts" import { House } from "../../../db/user" import { A, RA } from "../../../utils/functions" import { @@ -16,15 +16,13 @@ import { dispatchMoveHouse, useMoveHouseIntentListener, useMoveHouseListener, -} from "../../state/events" +} from "../../state/events/moveRotate" import { useHouseMaterialOps } from "../../state/hashedMaterials" -import houses, { useSetHouse } from "../../state/houses" -import ZStretch from "../../state/interactions/ZStretch" +import { useSetHouse } from "../../state/houses" import { useDnasLayout } from "../../state/layouts" import { useTransformabilityBooleans } from "../../state/siteCtx" import { vanillaColumns } from "../../state/vanilla" import RotateHandles from "../handles/RotateHandles" -import StretchHandle from "../handles/StretchHandle" import GroupedColumn from "./GroupedColumn" type Props = { diff --git a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx index f703150f..b314722a 100644 --- a/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx +++ b/app/design/ui-3d/grouped/stretchLength/GroupedStretchColumn.tsx @@ -5,7 +5,7 @@ import { Group } from "three" import { RA } from "~/utils/functions" import { GridGroup, HouseLayoutsKey } from "../../../../db/layouts" import { getLayoutsWorker } from "../../../../workers" -import { useZStretchHouseListener } from "../../../state/events" +import { useZStretchHouseListener } from "../../../state/events/stretch" import GroupedStretchModule from "./GroupedStretchModule" type Props = { diff --git a/app/design/ui/SiteSidebar.tsx b/app/design/ui/SiteSidebar.tsx index 32eb5419..f3282776 100644 --- a/app/design/ui/SiteSidebar.tsx +++ b/app/design/ui/SiteSidebar.tsx @@ -8,6 +8,7 @@ import { ref } from "valtio" import { useHouseTypes } from "~/data/houseTypes" import Sidebar from "~/ui//Sidebar" import { useCameraGroundRaycast } from "../state/camera" +import { dispatchAddHouseIntent } from "../state/events/houses" import houses from "../state/houses" import HouseThumbnail from "./HouseThumbnail" @@ -72,23 +73,20 @@ const SiteSidebar = ({ open, close }: Props) => { key={index} houseType={houseType} onAdd={() => { - const id = nanoid() - const position = ref( - cameraGroundRaycast() ?? new Vector3(0, 0, 0) - ) + dispatchAddHouseIntent(houseType) - houses[id] = ref({ - id, - houseTypeId: houseType.id, - systemId: houseType.systemId, - position, - rotation: 0, - dnas: ref(houseType.dnas), - modifiedMaterials: {}, - friendlyName: `Building ${ - Object.keys(houses).length + 1 - }`, - }) + // dispatchAddHouseIntent({ + // id, + // houseTypeId: houseType.id, + // systemId: houseType.systemId, + // position, + // rotation: 0, + // dnas: houseType.dnas, + // modifiedMaterials: {}, + // friendlyName: `Building ${ + // Object.keys(houses).length + 1 + // }`, + // }) close() }} From ad39926348dc0f976cfea340e8649dd70f8402a1 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 12 Jul 2023 10:11:52 +0100 Subject: [PATCH 041/132] wip put houses in the db --- app/design/ui-3d/fresh/FreshApp.tsx | 57 ++++++++--------------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index f8bdba14..5d562e07 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,22 +1,17 @@ +import { invalidate } from "@react-three/fiber" import { useGesture } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" +import { nanoid } from "nanoid" import { useEffect, useRef } from "react" import { Group, Vector3 } from "three" -import { getHouseLayoutsKey } from "../../../db/layouts" -import userDB from "../../../db/user" -import { A, O, R } from "../../../utils/functions" +import userDB, { House } from "../../../db/user" +import { A } from "../../../utils/functions" import { dispatchAddHouse, useAddHouseIntentListener, useAddHouseListener, } from "../../state/events/houses" -import { - createHouseGroup, - getFirstHouseLayout, - houseLayouts, - layoutToColumns, -} from "./helpers" -import { nanoid } from "nanoid" +import { createHouseGroup } from "./helpers" const FreshApp = () => { const rootRef = useRef(null) @@ -32,37 +27,22 @@ const FreshApp = () => { rootRef.current?.clear() } - const init = () => { + const addHouse = async (house: House) => { if (!rootRef.current) return + const houseGroup = await createHouseGroup(house) + rootRef.current.add(houseGroup) + invalidate() - // get houses instead + userDB.houses.put(house) + } + const init = () => { userDB.houses.toArray().then((houses) => { - pipe( - houses, - A.map(async (house) => { - const houseGroup = await createHouseGroup(house) - rootRef.current!.add(houseGroup) - console.log(`added house ${house.id}`) - }) - // A.map(({ systemId, dnas }) => - // pipe( - // houseLayouts, - // R.lookup(getHouseLayoutsKey({ systemId, dnas })), - // O.map((layout) => - // pipe( - // layout, - // layoutToColumns, - // A.map((columnGroup) => { - // rootRef.current!.add(columnGroup) - // }) - // ) - // ) - // ) - // ) - ) + pipe(houses, A.map(addHouse)) }) + invalidate() + return cleanup } @@ -92,12 +72,7 @@ const FreshApp = () => { }) }) - useAddHouseListener(async (house) => { - if (!rootRef.current) return - const houseGroup = await createHouseGroup(house) - rootRef.current.add(houseGroup) - console.log(`added house ${house.id}`) - }) + useAddHouseListener(addHouse) return } From 43c693111bed1fb92fd8cc8c69276d49dbb49ed8 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 12 Jul 2023 13:35:32 +0100 Subject: [PATCH 042/132] wip updating start/end cols --- app/design/state/gestures/index.ts | 1 - app/design/ui-3d/fresh/FreshApp.tsx | 32 +++++++---- app/design/ui-3d/fresh/helpers.ts | 85 ++++++++++++++++++++++++----- 3 files changed, 94 insertions(+), 24 deletions(-) diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index 6f8ada93..7abbee3e 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -1,7 +1,6 @@ import { invalidate, ThreeEvent } from "@react-three/fiber" import { useGesture } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" -import { useDebouncedCallback } from "use-debounce" import { useSnapshot } from "valtio" import scope from "~/design/state/scope" import { preTransformsTransients } from "~/design/state/transients/transforms" diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 5d562e07..1ac0011e 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,8 +1,8 @@ import { invalidate } from "@react-three/fiber" -import { useGesture } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { nanoid } from "nanoid" import { useEffect, useRef } from "react" +import { useKey } from "react-use" import { Group, Vector3 } from "three" import userDB, { House } from "../../../db/user" import { A } from "../../../utils/functions" @@ -11,18 +11,15 @@ import { useAddHouseIntentListener, useAddHouseListener, } from "../../state/events/houses" -import { createHouseGroup } from "./helpers" +import XZPlane from "../XZPlane" +import YPlane from "../YPlane" +import { createHouseGroup, insertVanillaColumn } from "./helpers" + +let houseGroups: Record = {} const FreshApp = () => { const rootRef = useRef(null) - const houseGroupRefs = useRef([]) - - // forget this for now, just useKey - const bindAll = useGesture({ - onClick: console.log, - }) as any - const cleanup = () => { rootRef.current?.clear() } @@ -34,6 +31,7 @@ const FreshApp = () => { invalidate() userDB.houses.put(house) + houseGroups[house.id] = houseGroup } const init = () => { @@ -74,7 +72,21 @@ const FreshApp = () => { useAddHouseListener(addHouse) - return + const bindAll = () => undefined as any + + useKey("v", () => { + for (let houseGroup of Object.values(houseGroups)) { + insertVanillaColumn(houseGroup, 1) + insertVanillaColumn(houseGroup, -1) + } + }) + + return ( + + + + + ) } export default FreshApp diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 5520bf34..1c23fff2 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -1,6 +1,14 @@ +import { invalidate } from "@react-three/fiber" import { liveQuery } from "dexie" +import { findFirst } from "fp-ts/lib/Array" import { pipe } from "fp-ts/lib/function" -import { BufferGeometry, BufferGeometryLoader, Group, Mesh } from "three" +import { + BufferGeometry, + BufferGeometryLoader, + Group, + Mesh, + Vector3, +} from "three" import { Module } from "../../../../server/data/modules" import { ifcTagToElement } from "../../../data/elements" import layoutsDB, { @@ -111,6 +119,11 @@ export const createColumnGroup = ({ }) }) + columnGroup.userData.length = gridGroups[0].modules.reduce( + (acc, v) => acc + v.module.length, + 0 + ) + return columnGroup } @@ -127,6 +140,7 @@ export const layoutToColumns = (layout: ColumnLayout): Group[] => }) group.position.set(0, 0, z) group.userData = { + ...group.userData, columnIndex, length, endColumn, @@ -157,19 +171,63 @@ export const getFirstHouseLayout = () => export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { const { children } = houseGroup - const levelTypes = pipe( - children - // write me... - // or maybe we put levelTypes on the house group's userData? - ) + const levelTypes: string[] = houseGroup.userData.levelTypes + const systemId: string = houseGroup.userData.systemId + const columnGroupCount = houseGroup.userData.columnGroupCount - pipe( - children, - A.findFirst((x) => - direction === 1 ? x.userData.endColumn : x.userData.startColumn - ), - O.map((x) => {}) - ) + const vanillaColumn = + vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] + + const vanillaColumnGroup = createColumnGroup(vanillaColumn) + + const vanillaColumnLength = vanillaColumnGroup.userData.length + + if (direction === 1) { + pipe( + children, + findFirst((x) => x.userData.columnIndex === columnGroupCount - 1), + O.map((endColumn) => { + const z0 = endColumn.position.z + + endColumn.position.setZ(z0 + vanillaColumnLength) + // endColumn.updateMatrix() + + invalidate() + }) + ) + } else if (direction === -1) { + pipe( + children, + findFirst((x) => x.userData.columnIndex === 1), + O.map((secondColumn) => secondColumn.position.z), + O.chain((_z0_uhh_wat) => + pipe( + children, + findFirst((x) => x.userData.columnIndex === 0), + O.map((startColumn) => { + const z0 = startColumn.position.z + startColumn.position.setZ(z0 - vanillaColumnLength) + }) + ) + ) + ) + } + + // vanillaColumnGroup.position.set( + // 0, + // 0, + // direction === 1 ? endColumnStartZ.value : startColumnEndZ.value + // ) + + // houseGroup.add(vanillaColumnGroup) + + // pipe( + // children, + // A.findFirst((x) => + // direction === 1 ? x.userData.endColumn : x.userData.startColumn + // ), + // O.map((x) => {}) + // ) } export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => @@ -194,6 +252,7 @@ export const createHouseGroup = async (house: House) => { houseGroup.userData = { ...house, levelTypes: houseLayoutToLevelTypes(houseLayout), + columnGroupCount: columnGroups.length, } houseGroup.add(...columnGroups) return houseGroup From 6576a36083ca959880164fa377bd346af9f034c1 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 13 Jul 2023 09:06:40 +0100 Subject: [PATCH 043/132] wip nice key stretch --- app/design/ui-3d/fresh/FreshApp.tsx | 9 ++- app/design/ui-3d/fresh/helpers.ts | 110 +++++++++++++++------------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 1ac0011e..398c0f2e 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -77,14 +77,19 @@ const FreshApp = () => { useKey("v", () => { for (let houseGroup of Object.values(houseGroups)) { insertVanillaColumn(houseGroup, 1) + } + }) + + useKey("V", () => { + for (let houseGroup of Object.values(houseGroups)) { insertVanillaColumn(houseGroup, -1) } }) return ( - - + {/* + */} ) } diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 1c23fff2..1c2e7d60 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -1,35 +1,36 @@ import { invalidate } from "@react-three/fiber" import { liveQuery } from "dexie" -import { findFirst } from "fp-ts/lib/Array" import { pipe } from "fp-ts/lib/function" import { BufferGeometry, BufferGeometryLoader, Group, Mesh, + MeshStandardMaterial, + Object3D, Vector3, } from "three" import { Module } from "../../../../server/data/modules" -import { ifcTagToElement } from "../../../data/elements" import layoutsDB, { ColumnLayout, - GridGroup, getHouseLayoutsKey, - VanillaColumn, getVanillaColumnsKey, + GridGroup, + VanillaColumn, VanillaColumnsKey, - invertVanillaColumnsKey, } from "../../../db/layouts" -import systemsDB from "../../../db/systems" import { House } from "../../../db/user" -import { A, O, R, S } from "../../../utils/functions" +import { A, Num, O, Ord, R, S } from "../../../utils/functions" import { getLayoutsWorker } from "../../../workers" import { getMaterial } from "./systems" // serialized layout key : column export let vanillaColumns: Record = {} -const getVanillaColumn = ({ systemId, levelTypes }: VanillaColumnsKey) => { +export const getVanillaColumn = ({ + systemId, + levelTypes, +}: VanillaColumnsKey) => { const key = getVanillaColumnsKey({ systemId, levelTypes }) return vanillaColumns[key] } @@ -77,7 +78,7 @@ export const getGeometry = ({ }) => models[speckleBranchUrl][ifcTag] export const moduleToGroup = ({ - module: { speckleBranchUrl, systemId }, + module: { speckleBranchUrl, systemId, length, dna }, endColumn = false, }: { module: Module @@ -86,14 +87,21 @@ export const moduleToGroup = ({ const moduleGroup = new Group() const taggedModelGeometries = models[speckleBranchUrl] for (let ifcTag of Object.keys(taggedModelGeometries)) { - const mesh = new Mesh( - getGeometry({ speckleBranchUrl, ifcTag }), - getMaterial({ systemId, ifcTag, houseId: "" }) - ) + const geometry = getGeometry({ speckleBranchUrl, ifcTag }) + const material = getMaterial({ + systemId, + ifcTag, + houseId: "", + }) as MeshStandardMaterial + const mesh = new Mesh(geometry, material) mesh.castShadow = true moduleGroup.add(mesh) } + moduleGroup.userData.length = length + moduleGroup.userData.systemId = systemId + moduleGroup.userData.dna = dna + return moduleGroup } @@ -123,6 +131,8 @@ export const createColumnGroup = ({ (acc, v) => acc + v.module.length, 0 ) + columnGroup.userData.startColumn = false + columnGroup.userData.endColumn = false return columnGroup } @@ -142,7 +152,6 @@ export const layoutToColumns = (layout: ColumnLayout): Group[] => group.userData = { ...group.userData, columnIndex, - length, endColumn, startColumn, } @@ -185,12 +194,21 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { if (direction === 1) { pipe( children, - findFirst((x) => x.userData.columnIndex === columnGroupCount - 1), - O.map((endColumn) => { - const z0 = endColumn.position.z + A.findFirst((x) => x.userData.columnIndex === columnGroupCount - 1), + O.map((endColumnGroup) => { + vanillaColumnGroup.position.setZ( + endColumnGroup.position.z + vanillaColumnLength / 2 + ) + houseGroup.add(vanillaColumnGroup) + + endColumnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) + + vanillaColumnGroup.userData.columnIndex = + endColumnGroup.userData.columnIndex + + endColumnGroup.userData.columnIndex++ - endColumn.position.setZ(z0 + vanillaColumnLength) - // endColumn.updateMatrix() + houseGroup.userData.columnGroupCount = columnGroupCount + 1 invalidate() }) @@ -198,36 +216,33 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { } else if (direction === -1) { pipe( children, - findFirst((x) => x.userData.columnIndex === 1), - O.map((secondColumn) => secondColumn.position.z), - O.chain((_z0_uhh_wat) => + A.sort( pipe( - children, - findFirst((x) => x.userData.columnIndex === 0), - O.map((startColumn) => { - const z0 = startColumn.position.z - startColumn.position.setZ(z0 - vanillaColumnLength) - }) + Num.Ord, + Ord.contramap((o: Object3D) => o.userData.columnIndex) ) - ) + ), + ([startColumnGroup, ...otherColumnGroups]) => { + startColumnGroup.position.add(new Vector3(0, 0, -vanillaColumnLength)) + + for (let otherColumnGroup of otherColumnGroups) { + otherColumnGroup.userData.columnIndex++ + } + + vanillaColumnGroup.userData.columnIndex = 1 + vanillaColumnGroup.position.setZ( + startColumnGroup.position.z + + startColumnGroup.userData.length + + vanillaColumnLength / 2 + ) + houseGroup.add(vanillaColumnGroup) + + houseGroup.userData.columnGroupCount = columnGroupCount + 1 + + invalidate() + } ) } - - // vanillaColumnGroup.position.set( - // 0, - // 0, - // direction === 1 ? endColumnStartZ.value : startColumnEndZ.value - // ) - - // houseGroup.add(vanillaColumnGroup) - - // pipe( - // children, - // A.findFirst((x) => - // direction === 1 ? x.userData.endColumn : x.userData.startColumn - // ), - // O.map((x) => {}) - // ) } export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => @@ -271,8 +286,3 @@ export const createHouseGroup = async (house: House) => { return houseGroup } - -// export const houseToLayout = (house: House): ColumnLayout => { - -// return undefined as any -// } From cd63f65094a921f10008be725a23298a6fabb470 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 13 Jul 2023 09:08:40 +0100 Subject: [PATCH 044/132] wip partition --- app/design/ui-3d/fresh/helpers.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 1c2e7d60..37e4b936 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -216,13 +216,8 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { } else if (direction === -1) { pipe( children, - A.sort( - pipe( - Num.Ord, - Ord.contramap((o: Object3D) => o.userData.columnIndex) - ) - ), - ([startColumnGroup, ...otherColumnGroups]) => { + A.partition((x) => x.userData.columnIndex !== 0), + ({ left: [startColumnGroup], right: otherColumnGroups }) => { startColumnGroup.position.add(new Vector3(0, 0, -vanillaColumnLength)) for (let otherColumnGroup of otherColumnGroups) { From 2d933aaf3156f4fe0fdddd5c7dc198870c383e0c Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 13 Jul 2023 09:32:10 +0100 Subject: [PATCH 045/132] wip subtractors --- app/design/ui-3d/fresh/FreshApp.tsx | 21 +++++++-- app/design/ui-3d/fresh/helpers.ts | 67 +++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 398c0f2e..c7055e67 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -13,7 +13,11 @@ import { } from "../../state/events/houses" import XZPlane from "../XZPlane" import YPlane from "../YPlane" -import { createHouseGroup, insertVanillaColumn } from "./helpers" +import { + createHouseGroup, + insertVanillaColumn, + subtractPenultimateColumn, +} from "./helpers" let houseGroups: Record = {} @@ -74,18 +78,29 @@ const FreshApp = () => { const bindAll = () => undefined as any - useKey("v", () => { + useKey("z", () => { for (let houseGroup of Object.values(houseGroups)) { insertVanillaColumn(houseGroup, 1) } }) - useKey("V", () => { + useKey("Z", () => { for (let houseGroup of Object.values(houseGroups)) { insertVanillaColumn(houseGroup, -1) } }) + useKey("d", () => { + for (let houseGroup of Object.values(houseGroups)) { + subtractPenultimateColumn(houseGroup, 1) + } + }) + + useKey("D", () => { + for (let houseGroup of Object.values(houseGroups)) { + subtractPenultimateColumn(houseGroup, -1) + } + }) return ( {/* diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 37e4b936..0e811f35 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -1,3 +1,4 @@ +import { Dodecahedron } from "@react-three/drei" import { invalidate } from "@react-three/fiber" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" @@ -240,6 +241,72 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { } } +export const subtractPenultimateColumn = ( + houseGroup: Group, + direction: 1 | -1 +) => { + const { children } = houseGroup + + const columnGroupCount: number = houseGroup.userData.columnGroupCount + + if (columnGroupCount <= 3) return + + if (direction === 1) { + pipe( + children, + A.filter((x) => x.userData.columnIndex >= columnGroupCount - 2), + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ), + ([penultimateColumnGroup, endColumnGroup]) => { + endColumnGroup.position.add( + new Vector3(0, 0, -penultimateColumnGroup.userData.length) + ) + + houseGroup.remove(penultimateColumnGroup) + + houseGroup.userData.columnGroupCount = columnGroupCount - 1 + endColumnGroup.userData.columnIndex-- + + invalidate() + } + ) + } else if (direction === -1) { + pipe( + children, + A.partition((x) => x.userData.columnIndex >= 2), + ({ left: startColumnGroups, right: otherColumnGroups }) => + pipe( + startColumnGroups, + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ), + ([startColumnGroup, secondColumnGroup]) => { + startColumnGroup.position.add( + new Vector3(0, 0, secondColumnGroup.userData.length) + ) + + houseGroup.remove(secondColumnGroup) + + houseGroup.userData.columnGroupCount = columnGroupCount - 1 + + for (let otherColumnGroup of otherColumnGroups) { + otherColumnGroup.userData.columnIndex-- + } + + invalidate() + } + ) + ) + } +} + export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => pipe( columnLayout, From 2ddaef1c635490c11373f44169d4cb7a12276a39 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 13 Jul 2023 10:07:37 +0100 Subject: [PATCH 046/132] wip update house dimensions --- app/design/ui-3d/fresh/FreshApp.tsx | 22 ++++++++++++++++++++++ app/design/ui-3d/fresh/helpers.ts | 17 ++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index c7055e67..8256bc10 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -6,6 +6,8 @@ import { useKey } from "react-use" import { Group, Vector3 } from "three" import userDB, { House } from "../../../db/user" import { A } from "../../../utils/functions" +import { PI } from "../../../utils/math" +import { yAxis } from "../../../utils/three" import { dispatchAddHouse, useAddHouseIntentListener, @@ -17,6 +19,7 @@ import { createHouseGroup, insertVanillaColumn, subtractPenultimateColumn, + updateHouseDimensions, } from "./helpers" let houseGroups: Record = {} @@ -81,26 +84,45 @@ const FreshApp = () => { useKey("z", () => { for (let houseGroup of Object.values(houseGroups)) { insertVanillaColumn(houseGroup, 1) + updateHouseDimensions(houseGroup) } }) useKey("Z", () => { for (let houseGroup of Object.values(houseGroups)) { insertVanillaColumn(houseGroup, -1) + updateHouseDimensions(houseGroup) } }) useKey("d", () => { for (let houseGroup of Object.values(houseGroups)) { subtractPenultimateColumn(houseGroup, 1) + updateHouseDimensions(houseGroup) } }) useKey("D", () => { for (let houseGroup of Object.values(houseGroups)) { subtractPenultimateColumn(houseGroup, -1) + updateHouseDimensions(houseGroup) } }) + + useKey("t", () => { + for (let houseGroup of Object.values(houseGroups)) { + houseGroup.position.add(new Vector3(1, 0, 1)) + invalidate() + } + }) + + useKey("r", () => { + for (let houseGroup of Object.values(houseGroups)) { + houseGroup.rotateOnAxis(yAxis, PI / 8) + invalidate() + } + }) + return ( {/* diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 0e811f35..703813b3 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -1,4 +1,3 @@ -import { Dodecahedron } from "@react-three/drei" import { invalidate } from "@react-three/fiber" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" @@ -11,6 +10,7 @@ import { Object3D, Vector3, } from "three" +import { OBB } from "three-stdlib" import { Module } from "../../../../server/data/modules" import layoutsDB, { ColumnLayout, @@ -320,6 +320,19 @@ export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => O.getOrElse((): string[] => []) ) +export const createHouseDimensions = (houseGroup: Group) => { + houseGroup.userData.obb = new OBB() +} + +export const updateHouseDimensions = (houseGroup: Group) => { + const { children } = houseGroup + houseGroup.userData.length = pipe( + children, + A.reduce(0, (acc, v) => acc + v.userData.length) + ) + console.log(houseGroup.userData) +} + export const createHouseGroup = async (house: House) => { const { systemId, dnas } = house @@ -332,6 +345,8 @@ export const createHouseGroup = async (house: House) => { columnGroupCount: columnGroups.length, } houseGroup.add(...columnGroups) + createHouseDimensions(houseGroup) + updateHouseDimensions(houseGroup) return houseGroup } From 84df44471626fc91a62b3cfea35b7ee234bc7a63 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Fri, 14 Jul 2023 08:59:51 +0100 Subject: [PATCH 047/132] wip somewhere --- app/design/state/highlights.ts | 19 +- app/design/state/scope.ts | 2 +- app/design/state/settings.ts | 6 +- app/design/ui-3d/fresh/FreshApp.tsx | 134 +++++++++++-- .../{state => ui-3d/fresh}/events/houses.ts | 16 +- app/design/ui-3d/fresh/events/outlines.ts | 14 ++ app/design/ui-3d/fresh/helpers.ts | 179 ++++++++++++------ app/design/ui-3d/init/Effects.tsx | 18 +- app/design/ui/SiteSidebar.tsx | 2 +- app/design/ui/menu/ContextMenuEntry.tsx | 17 +- 10 files changed, 306 insertions(+), 101 deletions(-) rename app/design/{state => ui-3d/fresh}/events/houses.ts (58%) create mode 100644 app/design/ui-3d/fresh/events/outlines.ts diff --git a/app/design/state/highlights.ts b/app/design/state/highlights.ts index a96e1105..40c48ed6 100644 --- a/app/design/state/highlights.ts +++ b/app/design/state/highlights.ts @@ -1,13 +1,12 @@ -import { invalidate } from "@react-three/fiber" import { MutableRefObject, useEffect, useRef } from "react" import { Group, Object3D } from "three" import { proxy, ref } from "valtio" -import { useSubscribe } from "~/utils/hooks" -import { isMesh } from "~/utils/three" import { HouseElementIdentifier } from "~/design/state/gestures/drag" import { useHouse } from "~/design/state/houses" -import scope from "./scope" import siteCtx, { SiteCtxModeEnum } from "~/design/state/siteCtx" +import { useSubscribe } from "~/utils/hooks" +import { isMesh } from "~/utils/three" +import scope from "./scope" type Highights = { outlined: Array @@ -120,19 +119,19 @@ export const useHouseElementOutline = ( if (scope.hovered !== null) { if ( scope.hovered.houseId === houseId && - scope.hovered.elementName in elementObjects.current && - elementObjects.current[scope.hovered.elementName].length > 0 + scope.hovered.ifcTag in elementObjects.current && + elementObjects.current[scope.hovered.ifcTag].length > 0 ) { - o3s.push(...elementObjects.current[scope.hovered.elementName]) + o3s.push(...elementObjects.current[scope.hovered.ifcTag]) } } if (scope.selected !== null) { if ( scope.selected.houseId === houseId && - scope.selected.elementName in elementObjects.current && - elementObjects.current[scope.selected.elementName].length > 0 + scope.selected.ifcTag in elementObjects.current && + elementObjects.current[scope.selected.ifcTag].length > 0 ) { - o3s.push(...elementObjects.current[scope.selected.elementName]) + o3s.push(...elementObjects.current[scope.selected.ifcTag]) } } break diff --git a/app/design/state/scope.ts b/app/design/state/scope.ts index b759c7f6..48ba6fbf 100644 --- a/app/design/state/scope.ts +++ b/app/design/state/scope.ts @@ -7,7 +7,7 @@ import { getHouseLayoutsKey } from "../../db/layouts" import houses from "./houses" export type ScopeItem = { - elementName: string + ifcTag: string gridGroupIndex: number levelIndex: number columnIndex: number diff --git a/app/design/state/settings.ts b/app/design/state/settings.ts index 7c415e61..69212882 100644 --- a/app/design/state/settings.ts +++ b/app/design/state/settings.ts @@ -1,6 +1,6 @@ import { proxy, useSnapshot } from "valtio" -const settings = proxy<{ +type AppSettings = { mapEnabled: boolean sidebar: boolean groundPlaneEnabled: boolean @@ -9,7 +9,9 @@ const settings = proxy<{ length: boolean } debug: boolean -}>({ +} + +const settings = proxy({ mapEnabled: false, sidebar: false, groundPlaneEnabled: true, diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 8256bc10..ec8fa359 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,18 +1,22 @@ -import { invalidate } from "@react-three/fiber" +import { invalidate, ThreeEvent } from "@react-three/fiber" +import { useGesture } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { nanoid } from "nanoid" import { useEffect, useRef } from "react" import { useKey } from "react-use" import { Group, Vector3 } from "three" import userDB, { House } from "../../../db/user" -import { A } from "../../../utils/functions" -import { PI } from "../../../utils/math" -import { yAxis } from "../../../utils/three" +import { A, O } from "../../../utils/functions" +import { floor, PI } from "../../../utils/math" +import { isMesh, yAxis } from "../../../utils/three" import { dispatchAddHouse, useAddHouseIntentListener, useAddHouseListener, -} from "../../state/events/houses" + useDeleteHouseListener, +} from "./events/houses" +import { openMenu } from "../../state/menu" +import scope, { ScopeItem } from "../../state/scope" import XZPlane from "../XZPlane" import YPlane from "../YPlane" import { @@ -21,8 +25,10 @@ import { subtractPenultimateColumn, updateHouseDimensions, } from "./helpers" +import { dispatchOutline } from "./events/outlines" +import siteCtx, { SiteCtxModeEnum } from "../../state/siteCtx" -let houseGroups: Record = {} +// let houseGroups: Record = {} const FreshApp = () => { const rootRef = useRef(null) @@ -38,9 +44,10 @@ const FreshApp = () => { invalidate() userDB.houses.put(house) - houseGroups[house.id] = houseGroup } + useAddHouseListener(addHouse) + const init = () => { userDB.houses.toArray().then((houses) => { pipe(houses, A.map(addHouse)) @@ -61,7 +68,9 @@ const FreshApp = () => { const id = nanoid() const position = new Vector3(0, 0, 0) - const getFriendlyName = () => "yo" // Object.keys(houses).length + 1 + const getFriendlyName = () => { + return `yo+${floor(Math.random() * 99999)}` // Object.keys(houses).length + 1 + } const friendlyName = getFriendlyName() @@ -77,52 +86,145 @@ const FreshApp = () => { }) }) - useAddHouseListener(addHouse) + useDeleteHouseListener(({ id }) => { + if (!rootRef.current) return + + console.log(`deleting id ${id}`) + + const target = rootRef.current.children.find((x) => x.userData.id === id) - const bindAll = () => undefined as any + if (target) { + rootRef.current.remove(target) + userDB.houses.delete(id) + } + }) + + const getHouseGroups = () => (rootRef.current?.children ?? []) as Group[] useKey("z", () => { - for (let houseGroup of Object.values(houseGroups)) { + for (let houseGroup of getHouseGroups()) { insertVanillaColumn(houseGroup, 1) updateHouseDimensions(houseGroup) } }) useKey("Z", () => { - for (let houseGroup of Object.values(houseGroups)) { + for (let houseGroup of getHouseGroups()) { insertVanillaColumn(houseGroup, -1) updateHouseDimensions(houseGroup) } }) useKey("d", () => { - for (let houseGroup of Object.values(houseGroups)) { + for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, 1) updateHouseDimensions(houseGroup) } }) useKey("D", () => { - for (let houseGroup of Object.values(houseGroups)) { + for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, -1) updateHouseDimensions(houseGroup) } }) useKey("t", () => { - for (let houseGroup of Object.values(houseGroups)) { + for (let houseGroup of getHouseGroups()) { houseGroup.position.add(new Vector3(1, 0, 1)) invalidate() } }) useKey("r", () => { - for (let houseGroup of Object.values(houseGroups)) { + for (let houseGroup of getHouseGroups()) { houseGroup.rotateOnAxis(yAxis, PI / 8) invalidate() + console.log(rootRef.current) } }) + const bindAll: any = useGesture<{ + drag: ThreeEvent + hover: ThreeEvent + onContextMenu: ThreeEvent & + React.MouseEvent + onDoubleClick: ThreeEvent & + React.MouseEvent + }>({ + onHover: ({ event, event: { intersections }, hovering }) => { + event.stopPropagation() + if (intersections.length === 0) { + document.body.style.cursor = "" + dispatchOutline({ + objects: [], + }) + invalidate() + // scope.hovered = null + return + } + const { + object, + eventObject, + // object: { userData }, + } = intersections[0] + + if (object.parent?.parent) { + const objects = object.parent.parent.children.flatMap((x) => x.children) + dispatchOutline({ + objects, + }) + } + switch (siteCtx.mode) { + case SiteCtxModeEnum.Enum.SITE: + // if (object.parent?.parent?.parent) { + // const objects = object.parent.parent.parent.children.flatMap((x) => + // x.children.flatMap((y) => y.children) + // ) + // dispatchOutline({ + // objects, + // }) + // } + break + case SiteCtxModeEnum.Enum.BUILDING: + // OUTLINE COLUMN ?! + break + case SiteCtxModeEnum.Enum.LEVEL: + if (object.parent) { + dispatchOutline({ objects: object.parent.children }) + } + break + } + + // scope.hovered = { + // ...userData.identifier, + // } + + if (hovering) { + document.body.style.cursor = "grab" + } + + invalidate() + }, + onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { + event.stopPropagation() + pipe( + intersections, + A.findFirst((x) => { + return ( + isMesh(x.object) && + !Array.isArray(x.object.material) && + x.object.material.visible + ) + }), + O.map(({ object: { userData } }) => { + scope.selected = userData as ScopeItem + openMenu(pageX, pageY) + }) + ) + }, + }) + return ( {/* diff --git a/app/design/state/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts similarity index 58% rename from app/design/state/events/houses.ts rename to app/design/ui-3d/fresh/events/houses.ts index 3a000190..ca427085 100644 --- a/app/design/state/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -1,9 +1,10 @@ import { useEvent } from "react-use" -import { HouseType } from "../../../../server/data/houseTypes" -import { House } from "../../../db/user" +import { HouseType } from "../../../../../server/data/houseTypes" +import { House } from "../../../../db/user" const ADD_HOUSE_INTENT_EVENT = "AddHouseIntentEvent" const ADD_HOUSE_EVENT = "AddHouseEvent" +const DELETE_HOUSE_EVENT = "DeleteHouseEvent" export const dispatchAddHouseIntent = (detail: HouseType) => dispatchEvent( @@ -25,3 +26,14 @@ export const dispatchAddHouse = (detail: House) => export const useAddHouseListener = (f: (eventDetail: House) => void) => useEvent(ADD_HOUSE_EVENT, ({ detail }) => f(detail)) + +type DeleteHouseDetail = { + id: string +} + +export const dispatchDeleteHouse = (detail: DeleteHouseDetail) => + dispatchEvent(new CustomEvent(DELETE_HOUSE_EVENT, { detail })) + +export const useDeleteHouseListener = ( + f: (eventDetail: DeleteHouseDetail) => void +) => useEvent(DELETE_HOUSE_EVENT, ({ detail }) => f(detail)) diff --git a/app/design/ui-3d/fresh/events/outlines.ts b/app/design/ui-3d/fresh/events/outlines.ts new file mode 100644 index 00000000..0f8e1c93 --- /dev/null +++ b/app/design/ui-3d/fresh/events/outlines.ts @@ -0,0 +1,14 @@ +import { useEvent } from "react-use" +import { Object3D } from "three" + +const OUTLINE_EVENT = "OutlineEvent" + +type OutlineEventDetail = { + objects: Object3D[] +} + +export const dispatchOutline = (detail: OutlineEventDetail) => + dispatchEvent(new CustomEvent(OUTLINE_EVENT, { detail })) + +export const useOutlineEvent = (f: (eventDetail: OutlineEventDetail) => void) => + useEvent(OUTLINE_EVENT, ({ detail }) => f(detail)) diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 703813b3..68db18ee 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -11,6 +11,7 @@ import { Vector3, } from "three" import { OBB } from "three-stdlib" +import { z } from "zod" import { Module } from "../../../../server/data/modules" import layoutsDB, { ColumnLayout, @@ -78,10 +79,22 @@ export const getGeometry = ({ ifcTag: string }) => models[speckleBranchUrl][ifcTag] +export const UserDataTypeEnum = z.enum(["HouseElementMesh", "HouseModuleGroup"]) +export type UserDataTypeEnum = z.infer + export const moduleToGroup = ({ - module: { speckleBranchUrl, systemId, length, dna }, - endColumn = false, + systemId, + houseId, + columnIndex, + levelIndex, + gridGroupIndex, + module: { speckleBranchUrl, length, dna }, }: { + systemId: string + houseId: string + columnIndex: number + levelIndex: number + gridGroupIndex: number module: Module endColumn?: boolean }) => { @@ -96,9 +109,21 @@ export const moduleToGroup = ({ }) as MeshStandardMaterial const mesh = new Mesh(geometry, material) mesh.castShadow = true + mesh.userData = { + type: UserDataTypeEnum.Enum.HouseElementMesh, + systemId, + houseId, + moduleDna: dna, + ifcTag, + columnIndex, + gridGroupIndex, + levelIndex, + } + moduleGroup.add(mesh) } + moduleGroup.userData.type = UserDataTypeEnum.Enum.HouseModuleGroup moduleGroup.userData.length = length moduleGroup.userData.systemId = systemId moduleGroup.userData.dna = dna @@ -107,17 +132,31 @@ export const moduleToGroup = ({ } export const createColumnGroup = ({ + systemId, + houseId, gridGroups, + columnIndex, endColumn = false, }: { + systemId: string + houseId: string gridGroups: GridGroup[] + columnIndex: number endColumn?: boolean }): Group => { const columnGroup = new Group() - gridGroups.forEach(({ modules, y }) => { - modules.forEach(({ z, module }) => { - const moduleGroup = moduleToGroup({ module, endColumn }) + gridGroups.forEach(({ modules, y, levelIndex }) => { + modules.forEach(({ z, module, gridGroupIndex }) => { + const moduleGroup = moduleToGroup({ + systemId, + houseId, + module, + endColumn, + columnIndex, + levelIndex, + gridGroupIndex, + }) moduleGroup.scale.set(1, 1, endColumn ? 1 : -1) moduleGroup.position.set( 0, @@ -138,16 +177,27 @@ export const createColumnGroup = ({ return columnGroup } -export const layoutToColumns = (layout: ColumnLayout): Group[] => +export const houseLayoutToColumns = ({ + systemId, + houseId, + houseLayout, +}: { + systemId: string + houseId: string + houseLayout: ColumnLayout +}): Group[] => pipe( - layout, + houseLayout, A.mapWithIndex((i, { gridGroups, z, columnIndex, length }) => { const startColumn = i === 0 - const endColumn = i === layout.length - 1 + const endColumn = i === houseLayout.length - 1 const group = createColumnGroup({ + systemId, + houseId, gridGroups, endColumn, + columnIndex, }) group.position.set(0, 0, z) group.userData = { @@ -160,6 +210,18 @@ export const layoutToColumns = (layout: ColumnLayout): Group[] => }) ) +export const createHouseDimensions = (houseGroup: Group) => { + houseGroup.userData.obb = new OBB() +} + +export const updateHouseDimensions = (houseGroup: Group) => { + const { children } = houseGroup + houseGroup.userData.length = pipe( + children, + A.reduce(0, (acc, v) => acc + v.userData.length) + ) +} + export let houseLayouts: Record = {} liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( @@ -170,6 +232,56 @@ liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( } ) +export const createHouseGroup = async (house: House) => { + const { systemId, id: houseId, dnas } = house + + const houseLayoutToHouseGroup = async ({ + systemId, + houseId, + houseLayout, + }: { + systemId: string + houseId: string + houseLayout: ColumnLayout + }) => { + const columnGroups = houseLayoutToColumns({ + systemId, + houseId, + houseLayout, + }) + const houseGroup = new Group() + houseGroup.userData = { + ...house, + levelTypes: houseLayoutToLevelTypes(houseLayout), + columnGroupCount: columnGroups.length, + } + houseGroup.add(...columnGroups) + createHouseDimensions(houseGroup) + updateHouseDimensions(houseGroup) + return houseGroup + } + + const houseGroup = pipe( + houseLayouts, + R.lookup(getHouseLayoutsKey({ systemId, dnas })), + O.match( + async () => { + const layoutsWorker = getLayoutsWorker() + if (!layoutsWorker) throw new Error(`no layouts worker`) + const houseLayout = await layoutsWorker.processLayout({ + systemId, + dnas, + }) + return houseLayoutToHouseGroup({ systemId, houseId, houseLayout }) + }, + (houseLayout) => + houseLayoutToHouseGroup({ systemId, houseId, houseLayout }) + ) + ) + + return houseGroup +} + export const getFirstHouseLayout = () => pipe( houseLayouts, @@ -188,7 +300,12 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { const vanillaColumn = vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] - const vanillaColumnGroup = createColumnGroup(vanillaColumn) + const vanillaColumnGroup = createColumnGroup({ + systemId, + houseId: houseGroup.userData.houseId, + gridGroups: vanillaColumn.gridGroups, + columnIndex: -1, + }) const vanillaColumnLength = vanillaColumnGroup.userData.length @@ -319,47 +436,3 @@ export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => ), O.getOrElse((): string[] => []) ) - -export const createHouseDimensions = (houseGroup: Group) => { - houseGroup.userData.obb = new OBB() -} - -export const updateHouseDimensions = (houseGroup: Group) => { - const { children } = houseGroup - houseGroup.userData.length = pipe( - children, - A.reduce(0, (acc, v) => acc + v.userData.length) - ) - console.log(houseGroup.userData) -} - -export const createHouseGroup = async (house: House) => { - const { systemId, dnas } = house - - const houseLayoutToHouseGroup = async (houseLayout: ColumnLayout) => { - const columnGroups = layoutToColumns(houseLayout) - const houseGroup = new Group() - houseGroup.userData = { - ...house, - levelTypes: houseLayoutToLevelTypes(houseLayout), - columnGroupCount: columnGroups.length, - } - houseGroup.add(...columnGroups) - createHouseDimensions(houseGroup) - updateHouseDimensions(houseGroup) - return houseGroup - } - - const houseGroup = pipe( - houseLayouts, - R.lookup(getHouseLayoutsKey({ systemId, dnas })), - O.match(async () => { - const layoutsWorker = getLayoutsWorker() - if (!layoutsWorker) throw new Error(`no layouts worker`) - const houseLayout = await layoutsWorker.processLayout({ systemId, dnas }) - return houseLayoutToHouseGroup(houseLayout) - }, houseLayoutToHouseGroup) - ) - - return houseGroup -} diff --git a/app/design/ui-3d/init/Effects.tsx b/app/design/ui-3d/init/Effects.tsx index 78cfdc76..31fe1a6c 100644 --- a/app/design/ui-3d/init/Effects.tsx +++ b/app/design/ui-3d/init/Effects.tsx @@ -1,6 +1,5 @@ -import highlights from "~/design/state/highlights" import { useFBO } from "@react-three/drei" -import { invalidate, useFrame, useThree } from "@react-three/fiber" +import { useFrame, useThree } from "@react-three/fiber" import { EffectComposer, EffectPass, @@ -8,7 +7,7 @@ import { RenderPass, } from "postprocessing" import { useEffect, useMemo } from "react" -import { subscribeKey } from "valtio/utils" +import { useOutlineEvent } from "../fresh/events/outlines" export type UseOutlineEffectParams = ConstructorParameters< typeof OutlineEffectRaw @@ -78,16 +77,9 @@ const Effects = () => { // selectiveBloomEffect ]) - const outline = () => { - if (highlights.outlined.length > 0) { - outlineEffect.selection.set(highlights.outlined) - } else { - outlineEffect.selection.clear() - } - invalidate() - } - - subscribeKey(highlights, "outlined", outline, true) + useOutlineEvent(({ objects }) => { + outlineEffect.selection.set(objects) + }) // subscribeKey(highlights, "illuminated", () => { // if (highlights.illuminated.length > 0) { diff --git a/app/design/ui/SiteSidebar.tsx b/app/design/ui/SiteSidebar.tsx index f3282776..336da46e 100644 --- a/app/design/ui/SiteSidebar.tsx +++ b/app/design/ui/SiteSidebar.tsx @@ -8,7 +8,7 @@ import { ref } from "valtio" import { useHouseTypes } from "~/data/houseTypes" import Sidebar from "~/ui//Sidebar" import { useCameraGroundRaycast } from "../state/camera" -import { dispatchAddHouseIntent } from "../state/events/houses" +import { dispatchAddHouseIntent } from "../ui-3d/fresh/events/houses" import houses from "../state/houses" import HouseThumbnail from "./HouseThumbnail" diff --git a/app/design/ui/menu/ContextMenuEntry.tsx b/app/design/ui/menu/ContextMenuEntry.tsx index f620ab84..78fdb5e8 100644 --- a/app/design/ui/menu/ContextMenuEntry.tsx +++ b/app/design/ui/menu/ContextMenuEntry.tsx @@ -12,6 +12,7 @@ import { getModeBools, useSiteCtx, } from "../../state/siteCtx" +import { dispatchDeleteHouse } from "../../ui-3d/fresh/events/houses" import RenameForm from "../../ui/RenameForm" import ContextMenu, { ContextMenuProps } from "./ContextMenu" import ContextMenuButton from "./ContextMenuButton" @@ -28,6 +29,8 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { if (!selected) throw new Error("null selected") + console.log({ selected }) + const props: ContextMenuProps = { pageX, pageY, @@ -38,8 +41,13 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { selected, } - const { houseId, columnIndex, levelIndex, gridGroupIndex, elementName } = - selected + const { + houseId, + columnIndex, + levelIndex, + gridGroupIndex, + ifcTag: elementName, + } = selected const house = useHouse(houseId) @@ -69,7 +77,10 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { } const deleteBuilding = () => { - delete houses[houseId] + dispatchDeleteHouse({ + id: house.id, + }) + // delete houses[houseId] scope.selected = null if (Object.keys(houses).length === 0) { From 59465bc47650633fa4569aa65ef9ced5909175bb Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 17 Jul 2023 08:48:23 +0100 Subject: [PATCH 048/132] wip amess --- app/design/ui-3d/fresh/FreshApp.tsx | 134 +++++++++++---- app/design/ui-3d/fresh/clippingPlanes.ts | 145 ++++++++++++++++ app/design/ui-3d/fresh/dimensions.ts | 67 ++++++++ app/design/ui-3d/fresh/events/menu.ts | 3 + app/design/ui-3d/fresh/helpers.ts | 203 +++++++++++++---------- app/design/ui-3d/fresh/userData.ts | 101 +++++++++++ app/design/ui/menu/ContextMenu.tsx | 1 - app/design/ui/menu/ContextMenuEntry.tsx | 1 - app/design/ui/menu/FreshContextMenu.tsx | 142 ++++++++++++++++ 9 files changed, 679 insertions(+), 118 deletions(-) create mode 100644 app/design/ui-3d/fresh/clippingPlanes.ts create mode 100644 app/design/ui-3d/fresh/dimensions.ts create mode 100644 app/design/ui-3d/fresh/events/menu.ts create mode 100644 app/design/ui-3d/fresh/userData.ts create mode 100644 app/design/ui/menu/FreshContextMenu.tsx diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index ec8fa359..21d6419e 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -7,26 +7,27 @@ import { useKey } from "react-use" import { Group, Vector3 } from "three" import userDB, { House } from "../../../db/user" import { A, O } from "../../../utils/functions" +import { useSubscribe } from "../../../utils/hooks" import { floor, PI } from "../../../utils/math" import { isMesh, yAxis } from "../../../utils/three" +import { openMenu } from "../../state/menu" +import scope, { ScopeItem } from "../../state/scope" +import settings from "../../state/settings" +import siteCtx, { downMode, SiteCtxModeEnum } from "../../state/siteCtx" +import { updateHouseOBB } from "./dimensions" import { dispatchAddHouse, useAddHouseIntentListener, useAddHouseListener, useDeleteHouseListener, } from "./events/houses" -import { openMenu } from "../../state/menu" -import scope, { ScopeItem } from "../../state/scope" -import XZPlane from "../XZPlane" -import YPlane from "../YPlane" +import { dispatchOutline } from "./events/outlines" import { createHouseGroup, insertVanillaColumn, subtractPenultimateColumn, - updateHouseDimensions, } from "./helpers" -import { dispatchOutline } from "./events/outlines" -import siteCtx, { SiteCtxModeEnum } from "../../state/siteCtx" +import { UserData, UserDataTypeEnum } from "./userData" // let houseGroups: Record = {} @@ -38,8 +39,14 @@ const FreshApp = () => { } const addHouse = async (house: House) => { + const { id: houseId, systemId, dnas, friendlyName } = house if (!rootRef.current) return - const houseGroup = await createHouseGroup(house) + const houseGroup = await createHouseGroup({ + systemId, + houseId, + dnas, + friendlyName, + }) rootRef.current.add(houseGroup) invalidate() @@ -99,48 +106,59 @@ const FreshApp = () => { } }) - const getHouseGroups = () => (rootRef.current?.children ?? []) as Group[] + const getHouseGroups = () => + (rootRef.current?.children ?? []).filter( + (x) => x.userData.type === UserDataTypeEnum.Enum.HouseGroup + ) as Group[] useKey("z", () => { for (let houseGroup of getHouseGroups()) { insertVanillaColumn(houseGroup, 1) - updateHouseDimensions(houseGroup) } }) useKey("Z", () => { for (let houseGroup of getHouseGroups()) { insertVanillaColumn(houseGroup, -1) - updateHouseDimensions(houseGroup) } }) useKey("d", () => { for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, 1) - updateHouseDimensions(houseGroup) } }) useKey("D", () => { for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, -1) - updateHouseDimensions(houseGroup) } }) useKey("t", () => { for (let houseGroup of getHouseGroups()) { houseGroup.position.add(new Vector3(1, 0, 1)) + // updateHouseOBB(houseGroup) invalidate() + console.log(houseGroup) } }) useKey("r", () => { for (let houseGroup of getHouseGroups()) { + const houseCenter = houseGroup.position.clone() + // .add(new Vector3(0, 0, houseGroup.userData.length / 2)) + + // console.log(houseCenter) + + houseGroup.position.sub(houseCenter) + houseGroup.position.applyAxisAngle(yAxis, PI / 8) // rotate the POSITION + houseGroup.position.add(houseCenter) houseGroup.rotateOnAxis(yAxis, PI / 8) + houseGroup.updateMatrix() + // updateHouseOBB(houseGroup) + invalidate() - console.log(rootRef.current) } }) @@ -169,22 +187,24 @@ const FreshApp = () => { // object: { userData }, } = intersections[0] - if (object.parent?.parent) { - const objects = object.parent.parent.children.flatMap((x) => x.children) - dispatchOutline({ - objects, - }) - } + // if (object.parent?.parent) { + // const objects = object.parent.parent.children.flatMap((x) => x.children) + // dispatchOutline({ + // objects, + // }) + // } + switch (siteCtx.mode) { case SiteCtxModeEnum.Enum.SITE: - // if (object.parent?.parent?.parent) { - // const objects = object.parent.parent.parent.children.flatMap((x) => - // x.children.flatMap((y) => y.children) - // ) - // dispatchOutline({ - // objects, - // }) - // } + if (object.parent?.parent?.parent?.parent) { + const objects = object.parent.parent.parent.parent.children.flatMap( + (x) => + x.children.flatMap((y) => y.children.flatMap((z) => z.children)) + ) + dispatchOutline({ + objects, + }) + } break case SiteCtxModeEnum.Enum.BUILDING: // OUTLINE COLUMN ?! @@ -223,8 +243,66 @@ const FreshApp = () => { }) ) }, + onDoubleClick: ({ event, event: { intersections } }) => { + event.stopPropagation() + + if (intersections.length === 0) return + + const { object } = intersections[0] + + const userData: UserData = object.userData as UserData + + switch (userData.type) { + case UserDataTypeEnum.Enum.ElementMesh: + const houseId = + object.parent?.parent?.parent?.parent?.userData.houseId + const levelIndex = object.parent?.parent?.userData.levelIndex + if (houseId && levelIndex) downMode({ houseId, levelIndex }) + } + + // if (userData) { + // if (userData?.identifier?.houseId) { + // downMode({ ...userData.identifier }) + // } + // } + + invalidate() + }, }) + useSubscribe( + settings.verticalCuts, + () => { + const { width, length } = settings.verticalCuts + const { levelIndex } = siteCtx + + rootRef.current?.traverseVisible((o3) => { + const userData = o3.userData as UserData + + switch (userData.type) { + case UserDataTypeEnum.Enum.ElementMesh: + // console.log(userData) + break + case UserDataTypeEnum.Enum.HouseGroup: + console.log(userData) + // TypeScript knows that userData is of type HouseModuleGroupUserData in this block + // console.log(userData.length) // This is valid + // console.log(userData.houseId) // TypeScript error, houseId doesn't exist on HouseModuleGroupUserData + break + } + }) + // Object.values(elementMaterials.current).forEach((material) => { + // material.clippingPlanes = [ + // width ? [clippingPlaneZ] : [], + // levelIndex !== null ? [clippingPlaneY] : [], + // length ? [clippingPlaneX] : [], + // ].flat() + // }) + invalidate() + }, + true + ) + return ( {/* diff --git a/app/design/ui-3d/fresh/clippingPlanes.ts b/app/design/ui-3d/fresh/clippingPlanes.ts new file mode 100644 index 00000000..13c5abfa --- /dev/null +++ b/app/design/ui-3d/fresh/clippingPlanes.ts @@ -0,0 +1,145 @@ +import { Plane, Vector3 } from "three" + +const clippingPlanes = { + x: new Plane(), + y: new Plane(), + z: new Plane(), +} + +const axes = { + x: new Vector3(1, 0, 0), + y: new Vector3(0, -1, 0), + z: new Vector3(0, 0, 1), +} + +// const clippingPlaneX = useMemo(() => new Plane(), []) +// const clippingPlaneY = useMemo(() => new Plane(), []) +// const clippingPlaneZ = useMemo(() => new Plane(), []) + +// export const dispatchClippingPlanesUpdate = () => {} + +// export const onClippingPlanesUpdate = () => {} + +export default clippingPlanes + +// export const useClippingPlanes = ({ +// ref, +// houseId, +// layoutsKey, +// }: { +// houseId: string +// ref: RefObject +// layoutsKey: string +// }) => { +// const systemId = houses[houseId].systemId +// const elementMaterials = useRef>({}) +// const categoryElements = useRef>({}) +// const elements = useSystemElements({ systemId }) + +// const getPlaneMatrix = usePlaneMatrix(houseId) + +// const xAxis = useMemo(() => new Vector3(1, 0, 0), []) +// const yAxis = useMemo(() => new Vector3(0, -1, 0), []) +// const zAxis = useMemo(() => new Vector3(0, 0, 1), []) + +// const levelHeight = +// siteCtx.levelIndex === null +// ? Infinity +// : layouts[layoutsKey][0].gridGroups[siteCtx.levelIndex].y + +// layouts[layoutsKey][0].gridGroups[siteCtx.levelIndex].modules[0].module +// .height / +// 2 + +// const updateMatrices = () => { +// const planeMatrix = getPlaneMatrix() + +// clippingPlaneX.set(xAxis, 0) +// clippingPlaneX.applyMatrix4(planeMatrix) +// clippingPlaneY.set(yAxis, levelHeight) +// clippingPlaneY.applyMatrix4(planeMatrix) +// clippingPlaneZ.set(zAxis, 0) +// clippingPlaneZ.applyMatrix4(planeMatrix) + +// invalidate() +// } +// useSubscribeKey(postTransformsTransients, houseId, updateMatrices, true) + +// useEffect(() => { +// ref.current?.traverse((o3) => { +// if ( +// !isMesh(o3) || +// Array.isArray(o3.material) || +// o3.userData.identifier?.identifierType !== "HOUSE_ELEMENT" || +// o3.userData.identifier?.elementName in elementMaterials.current +// ) { +// return +// } + +// const { elementName } = o3.userData.identifier + +// elementMaterials.current[elementName] = o3.material + +// pipe( +// elements, +// RA.findFirstMap(({ name, category }) => +// name === elementName ? O.some(category) : O.none +// ), +// O.map((category) => { +// if (!(category in categoryElements.current)) { +// categoryElements.current[category] = [elementName] +// } else { +// categoryElements.current[category].push(elementName) +// } +// }) +// ) +// }) + +// return () => { +// elementMaterials.current = {} +// categoryElements.current = {} +// } +// }, [elements, ref]) + +// useSubscribe( +// elementCategories, +// (...ops) => { +// pipe( +// ops, +// A.map( +// A.chain(([_, categories, value]: any): any => { +// pipe( +// categoryElements.current, +// R.mapWithIndex((cat, elements) => { +// if (categories.includes(cat)) { +// for (let element of elements) { +// elementMaterials.current[element].visible = value +// } +// } +// }) +// ) +// }) +// ) +// ) +// invalidate() +// }, +// true +// ) + +// useSubscribeKey( +// settings, +// "verticalCuts", +// () => { +// const { length, width } = settings.verticalCuts +// const { levelIndex } = siteCtx +// Object.values(elementMaterials.current).forEach((material) => { +// material.clippingPlanes = [ +// width ? [clippingPlaneZ] : [], +// levelIndex !== null ? [clippingPlaneY] : [], +// length ? [clippingPlaneX] : [], +// ].flat() +// }) +// invalidate() +// }, +// true +// ) +// } diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts new file mode 100644 index 00000000..f0e7978d --- /dev/null +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -0,0 +1,67 @@ +import { + BoxGeometry, + Group, + Matrix3, + Matrix4, + Mesh, + MeshBasicMaterial, + Object3D, + Vector3, +} from "three" +import { OBB } from "three-stdlib" +import { HouseGroupUserData } from "./userData" + +let lastMesh: Mesh | null + +const renderOBB = (obb: OBB, scene: Object3D) => { + const size = obb.halfSize.clone().multiplyScalar(2) + + if (lastMesh) scene.remove(lastMesh) + + const geom = new BoxGeometry(size.x, size.y, size.z) + const material = new MeshBasicMaterial({ color: "tomato" }) + const mesh = new Mesh(geom, material) + mesh.position.copy(obb.center) + mesh.setRotationFromMatrix(new Matrix4().setFromMatrix3(obb.rotation)) + scene.add(mesh) + lastMesh = mesh +} + +export const updateHouseOBB = (houseGroup: Group) => { + const houseGroupUserData = houseGroup.userData as HouseGroupUserData + + const { width, height, length } = houseGroupUserData + + const { x, y, z } = houseGroup.position + + const center = new Vector3(x, y + height / 2, z + length / 2) + const halfSize = new Vector3(width / 2, height / 2, length / 2) + const rotation = new Matrix3().setFromMatrix4(houseGroup.matrix) + + houseGroupUserData.obb.set(center, halfSize, rotation) + + if (houseGroup.parent) renderOBB(houseGroupUserData.obb, houseGroup.parent) + + console.log(houseGroupUserData.obb) +} + +export const updateHouseWidth = (houseGroup: Group) => {} + +export const updateHouseHeight = (houseGroup: Group) => {} + +export const updateHouseLength = (houseGroup: Group) => { + const { children: columnGroups } = houseGroup + + console.log(`houseGroup length before: ${houseGroup.userData.length}`) + + houseGroup.userData.length = columnGroups.reduce( + (acc, columnGroup) => acc + columnGroup.userData.length, + 0 + ) + + console.log(`houseGroup length after: ${houseGroup.userData.length}`) + + // updateHouseOBB(houseGroup) + + // update length should update dimensions, obb etc... +} diff --git a/app/design/ui-3d/fresh/events/menu.ts b/app/design/ui-3d/fresh/events/menu.ts new file mode 100644 index 00000000..ed153fab --- /dev/null +++ b/app/design/ui-3d/fresh/events/menu.ts @@ -0,0 +1,3 @@ +const foo = {} + +export default foo diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 68db18ee..6e1e10b2 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -8,10 +8,10 @@ import { Mesh, MeshStandardMaterial, Object3D, + Plane, Vector3, } from "three" import { OBB } from "three-stdlib" -import { z } from "zod" import { Module } from "../../../../server/data/modules" import layoutsDB, { ColumnLayout, @@ -21,10 +21,18 @@ import layoutsDB, { VanillaColumn, VanillaColumnsKey, } from "../../../db/layouts" -import { House } from "../../../db/user" import { A, Num, O, Ord, R, S } from "../../../utils/functions" import { getLayoutsWorker } from "../../../workers" +import { updateHouseLength } from "./dimensions" import { getMaterial } from "./systems" +import { + ColumnGroupUserData, + ElementMeshUserData, + GridGroupUserData, + HouseGroupUserData, + ModuleGroupUserData, + UserDataTypeEnum, +} from "./userData" // serialized layout key : column export let vanillaColumns: Record = {} @@ -79,24 +87,14 @@ export const getGeometry = ({ ifcTag: string }) => models[speckleBranchUrl][ifcTag] -export const UserDataTypeEnum = z.enum(["HouseElementMesh", "HouseModuleGroup"]) -export type UserDataTypeEnum = z.infer - export const moduleToGroup = ({ systemId, - houseId, - columnIndex, - levelIndex, gridGroupIndex, module: { speckleBranchUrl, length, dna }, }: { systemId: string - houseId: string - columnIndex: number - levelIndex: number gridGroupIndex: number module: Module - endColumn?: boolean }) => { const moduleGroup = new Group() const taggedModelGeometries = models[speckleBranchUrl] @@ -109,70 +107,81 @@ export const moduleToGroup = ({ }) as MeshStandardMaterial const mesh = new Mesh(geometry, material) mesh.castShadow = true - mesh.userData = { - type: UserDataTypeEnum.Enum.HouseElementMesh, - systemId, - houseId, - moduleDna: dna, + + const elementMeshUserData: ElementMeshUserData = { + type: UserDataTypeEnum.Enum.ElementMesh, ifcTag, - columnIndex, - gridGroupIndex, - levelIndex, } - + mesh.userData = elementMeshUserData moduleGroup.add(mesh) } - moduleGroup.userData.type = UserDataTypeEnum.Enum.HouseModuleGroup - moduleGroup.userData.length = length - moduleGroup.userData.systemId = systemId - moduleGroup.userData.dna = dna + const moduleGroupUserData: ModuleGroupUserData = { + type: UserDataTypeEnum.Enum.ModuleGroup, + gridGroupIndex, + dna, + length, + // module + } + + moduleGroup.userData = moduleGroupUserData return moduleGroup } export const createColumnGroup = ({ systemId, - houseId, gridGroups, columnIndex, + startColumn = false, endColumn = false, }: { systemId: string - houseId: string gridGroups: GridGroup[] columnIndex: number + startColumn?: boolean endColumn?: boolean }): Group => { const columnGroup = new Group() gridGroups.forEach(({ modules, y, levelIndex }) => { - modules.forEach(({ z, module, gridGroupIndex }) => { - const moduleGroup = moduleToGroup({ - systemId, - houseId, - module, - endColumn, - columnIndex, - levelIndex, - gridGroupIndex, - }) - moduleGroup.scale.set(1, 1, endColumn ? 1 : -1) - moduleGroup.position.set( - 0, - y, - endColumn ? z + module.length / 2 : z - module.length / 2 - ) - columnGroup.add(moduleGroup) - }) + const gridGroup = new Group() + const length = modules.reduce( + (acc, { z, module, gridGroupIndex }): number => { + const moduleGroup = moduleToGroup({ + systemId, + module, + gridGroupIndex, + }) + moduleGroup.scale.set(1, 1, endColumn ? 1 : -1) + moduleGroup.position.set( + 0, + y, + endColumn ? z + module.length / 2 : z - module.length / 2 + ) + gridGroup.add(moduleGroup) + return acc + module.length + }, + 0 + ) + const gridGroupUserData: GridGroupUserData = { + type: UserDataTypeEnum.Enum.GridGroup, + levelIndex, + length, + } + gridGroup.userData = gridGroupUserData + columnGroup.add(gridGroup) }) - columnGroup.userData.length = gridGroups[0].modules.reduce( - (acc, v) => acc + v.module.length, - 0 - ) - columnGroup.userData.startColumn = false - columnGroup.userData.endColumn = false + const columnGroupUserData: ColumnGroupUserData = { + type: UserDataTypeEnum.Enum.ColumnGroup, + columnIndex, + length: gridGroups[0].length, + startColumn, + endColumn, + } + + columnGroup.userData = columnGroupUserData return columnGroup } @@ -194,34 +203,16 @@ export const houseLayoutToColumns = ({ const group = createColumnGroup({ systemId, - houseId, gridGroups, + startColumn, endColumn, columnIndex, }) group.position.set(0, 0, z) - group.userData = { - ...group.userData, - columnIndex, - endColumn, - startColumn, - } return group }) ) -export const createHouseDimensions = (houseGroup: Group) => { - houseGroup.userData.obb = new OBB() -} - -export const updateHouseDimensions = (houseGroup: Group) => { - const { children } = houseGroup - houseGroup.userData.length = pipe( - children, - A.reduce(0, (acc, v) => acc + v.userData.length) - ) -} - export let houseLayouts: Record = {} liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( @@ -232,9 +223,17 @@ liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( } ) -export const createHouseGroup = async (house: House) => { - const { systemId, id: houseId, dnas } = house - +export const createHouseGroup = async ({ + systemId, + houseId, + dnas, + friendlyName, +}: { + systemId: string + houseId: string + dnas: string[] + friendlyName: string +}) => { const houseLayoutToHouseGroup = async ({ systemId, houseId, @@ -250,14 +249,37 @@ export const createHouseGroup = async (house: House) => { houseLayout, }) const houseGroup = new Group() - houseGroup.userData = { - ...house, + const width = houseLayout[0].gridGroups[0].modules[0].module.width + const height = houseLayout[0].gridGroups.reduce( + (acc, v) => acc + v.modules[0].module.height, + 0 + ) + const length = columnGroups.reduce( + (acc, columnGroup) => acc + columnGroup.userData.length, + 0 + ) + const obb = new OBB() + const houseGroupUserData: HouseGroupUserData = { + type: UserDataTypeEnum.Enum.HouseGroup, + systemId, + houseId, + width, + length, + height, + dnas, + friendlyName, + modifiedMaterials: {}, + obb, + clippingPlanes: { + x: new Plane(), + y: new Plane(), + z: new Plane(), + }, levelTypes: houseLayoutToLevelTypes(houseLayout), - columnGroupCount: columnGroups.length, + columnCount: columnGroups.length, } + houseGroup.userData = houseGroupUserData houseGroup.add(...columnGroups) - createHouseDimensions(houseGroup) - updateHouseDimensions(houseGroup) return houseGroup } @@ -295,14 +317,15 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { const levelTypes: string[] = houseGroup.userData.levelTypes const systemId: string = houseGroup.userData.systemId - const columnGroupCount = houseGroup.userData.columnGroupCount + const columnCount = houseGroup.userData.columnCount const vanillaColumn = vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] + console.log({ vanillaColumn, systemId, levelTypes, houseGroup }) + const vanillaColumnGroup = createColumnGroup({ systemId, - houseId: houseGroup.userData.houseId, gridGroups: vanillaColumn.gridGroups, columnIndex: -1, }) @@ -312,7 +335,7 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { if (direction === 1) { pipe( children, - A.findFirst((x) => x.userData.columnIndex === columnGroupCount - 1), + A.findFirst((x) => x.userData.columnIndex === columnCount - 1), O.map((endColumnGroup) => { vanillaColumnGroup.position.setZ( endColumnGroup.position.z + vanillaColumnLength / 2 @@ -326,7 +349,7 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { endColumnGroup.userData.columnIndex++ - houseGroup.userData.columnGroupCount = columnGroupCount + 1 + houseGroup.userData.columnCount = columnCount + 1 invalidate() }) @@ -350,12 +373,14 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { ) houseGroup.add(vanillaColumnGroup) - houseGroup.userData.columnGroupCount = columnGroupCount + 1 + houseGroup.userData.columnCount = columnCount + 1 invalidate() } ) } + + updateHouseLength(houseGroup) } export const subtractPenultimateColumn = ( @@ -364,14 +389,14 @@ export const subtractPenultimateColumn = ( ) => { const { children } = houseGroup - const columnGroupCount: number = houseGroup.userData.columnGroupCount + const columnCount: number = houseGroup.userData.columnCount - if (columnGroupCount <= 3) return + if (columnCount <= 3) return if (direction === 1) { pipe( children, - A.filter((x) => x.userData.columnIndex >= columnGroupCount - 2), + A.filter((x) => x.userData.columnIndex >= columnCount - 2), A.sort( pipe( Num.Ord, @@ -385,7 +410,7 @@ export const subtractPenultimateColumn = ( houseGroup.remove(penultimateColumnGroup) - houseGroup.userData.columnGroupCount = columnGroupCount - 1 + houseGroup.userData.columnCount = columnCount - 1 endColumnGroup.userData.columnIndex-- invalidate() @@ -411,7 +436,7 @@ export const subtractPenultimateColumn = ( houseGroup.remove(secondColumnGroup) - houseGroup.userData.columnGroupCount = columnGroupCount - 1 + houseGroup.userData.columnCount = columnCount - 1 for (let otherColumnGroup of otherColumnGroups) { otherColumnGroup.userData.columnIndex-- @@ -422,6 +447,8 @@ export const subtractPenultimateColumn = ( ) ) } + + updateHouseLength(houseGroup) } export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts new file mode 100644 index 00000000..ebbfc910 --- /dev/null +++ b/app/design/ui-3d/fresh/userData.ts @@ -0,0 +1,101 @@ +import { Group, Plane } from "three" +import { OBB } from "three-stdlib" +import { z } from "zod" + +// maybe want some updater functions to update all the user data in the tree + +// or at certain levels/scopes? + +// houseGroup has +// -> columnGroups as children has +// -> gridGroups as children has +// -> moduleGroups as children has +// -> elementMeshes as children + +export const UserDataTypeEnum = z.enum([ + "HouseGroup", + "ColumnGroup", + "GridGroup", + "ModuleGroup", + "ElementMesh", +]) +export type UserDataTypeEnum = z.infer + +export type HouseGroupUserData = { + type: typeof UserDataTypeEnum.Enum.HouseGroup + systemId: string + houseId: string + dnas: string[] + friendlyName: string + modifiedMaterials: Record + height: number + length: number + width: number + obb: OBB + clippingPlanes: { + x: Plane + y: Plane + z: Plane + } + columnCount: number + levelTypes: string[] +} + +export type ColumnGroupUserData = { + type: typeof UserDataTypeEnum.Enum.ColumnGroup + columnIndex: number + length: number + startColumn?: boolean + endColumn?: boolean +} + +export type GridGroupUserData = { + type: typeof UserDataTypeEnum.Enum.GridGroup + levelIndex: number + length: number +} + +export type ModuleGroupUserData = { + type: typeof UserDataTypeEnum.Enum.ModuleGroup + gridGroupIndex: number + dna: string + length: number +} + +export type ElementMeshUserData = { + type: typeof UserDataTypeEnum.Enum.ElementMesh + ifcTag: string +} + +export type UserData = + | ElementMeshUserData + | ModuleGroupUserData + | GridGroupUserData + | ColumnGroupUserData + | HouseGroupUserData + +// ASSUMPTIONS +// ----------- +// we've updated the ... something or other... +export const updateHouseUserData = (houseGroup: Group) => {} + +// MORE GRANULAR +// ------------- +// update SOME PART OF THE HOUSE +// based on SOME OTHER PARTS OF THE HOUSE +export const updateClippingPlanes = (houseGroup: Group) => { + const houseGroupUserData = houseGroup.userData as HouseGroupUserData + + // x, y, z... + + // x and z is like okay yeah standard or... well... + // dynamic on user controls could be cool + + // y is like dependent on levelIndex of level mode + + // first we want the clipping planes in the user data +} + +export const getClippingPlanesStuff = (houseGroup: Group) => { + // dimensions should do it I guess? +} diff --git a/app/design/ui/menu/ContextMenu.tsx b/app/design/ui/menu/ContextMenu.tsx index b144fba7..f6854e35 100644 --- a/app/design/ui/menu/ContextMenu.tsx +++ b/app/design/ui/menu/ContextMenu.tsx @@ -9,7 +9,6 @@ import { useClickAway, useEscape } from "~/ui/utils" export type ContextMenuProps = { pageX: number pageY: number - selected: ScopeItem onClose?: () => void children?: ReactNode } diff --git a/app/design/ui/menu/ContextMenuEntry.tsx b/app/design/ui/menu/ContextMenuEntry.tsx index 78fdb5e8..f81c801b 100644 --- a/app/design/ui/menu/ContextMenuEntry.tsx +++ b/app/design/ui/menu/ContextMenuEntry.tsx @@ -38,7 +38,6 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { closeMenu() invalidate() }, - selected, } const { diff --git a/app/design/ui/menu/FreshContextMenu.tsx b/app/design/ui/menu/FreshContextMenu.tsx new file mode 100644 index 00000000..4a0014c8 --- /dev/null +++ b/app/design/ui/menu/FreshContextMenu.tsx @@ -0,0 +1,142 @@ +import React, { Fragment, useMemo, useState } from "react" +import { Pencil, TextCursor } from "../../../ui/icons" +import { ScopeItem } from "../../state/scope" +import siteCtx, { downMode, getModeBools } from "../../state/siteCtx" +import RenameForm from "../RenameForm" +import ContextMenu from "./ContextMenu" +import ContextMenuButton from "./ContextMenuButton" + +const FreshContextMenu = () => { + const [item, setItem] = useState(null) + const [{ pageX, pageY }, setPageXY] = useState({ pageX: 0, pageY: 0 }) + + const { buildingMode, buildingOrLevelMode, levelMode, siteMode } = + useMemo(() => { + const modeBools = getModeBools(siteCtx.mode) + return { + ...modeBools, + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [item]) + + const editBuilding = () => { + if (!item) return + downMode(item) + // close() + } + + return ( + + {siteMode && ( + + {/* {!renaming && ( + } + text="Edit building" + unpaddedSvg + onClick={editBuilding} + /> + )} */} + {/* } + text="Rename" + unpaddedSvg + onClick={() => void setRenaming(true)} + /> */} + {/* {renaming && ( + { + houses[houseId].friendlyName = newName + setRenaming(false) + }} + /> + )} */} + {/* {!renaming && ( + + } + text="Reset" + onClick={resetBuilding} + /> + } + text="Delete" + onClick={deleteBuilding} + /> + + )} */} + + )} + + {/* {buildingMode && ( + + } + text="Edit level" + unpaddedSvg + onClick={editLevel} + /> + + + + + + + )} */} + + {/* {levelMode && ( + + + + + + )} */} + + {/* */} + + ) +} + +export default FreshContextMenu From b447962b90aa8796b6188c05e3bfa774aa5a01cf Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 17 Jul 2023 09:23:42 +0100 Subject: [PATCH 049/132] wip translate/rotate with obb --- app/design/state/settings.ts | 2 +- app/design/ui-3d/fresh/FreshApp.tsx | 28 ++-- app/design/ui-3d/fresh/dimensions.ts | 28 ++-- app/design/ui-3d/fresh/helpers.ts | 215 ++++++++++++++------------- app/design/ui-3d/fresh/userData.ts | 9 +- 5 files changed, 155 insertions(+), 127 deletions(-) diff --git a/app/design/state/settings.ts b/app/design/state/settings.ts index 69212882..d359ed65 100644 --- a/app/design/state/settings.ts +++ b/app/design/state/settings.ts @@ -14,7 +14,7 @@ type AppSettings = { const settings = proxy({ mapEnabled: false, sidebar: false, - groundPlaneEnabled: true, + groundPlaneEnabled: false, verticalCuts: { width: false, length: false, diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 21d6419e..16219422 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -138,25 +138,33 @@ const FreshApp = () => { useKey("t", () => { for (let houseGroup of getHouseGroups()) { houseGroup.position.add(new Vector3(1, 0, 1)) - // updateHouseOBB(houseGroup) + updateHouseOBB(houseGroup) + invalidate() + } + }) + useKey("T", () => { + for (let houseGroup of getHouseGroups()) { + houseGroup.position.add(new Vector3(-1, 0, -1)) + updateHouseOBB(houseGroup) invalidate() - console.log(houseGroup) } }) useKey("r", () => { for (let houseGroup of getHouseGroups()) { - const houseCenter = houseGroup.position.clone() - // .add(new Vector3(0, 0, houseGroup.userData.length / 2)) + houseGroup.rotateOnAxis(yAxis, PI / 8) + houseGroup.updateMatrix() + updateHouseOBB(houseGroup) - // console.log(houseCenter) + invalidate() + } + }) - houseGroup.position.sub(houseCenter) - houseGroup.position.applyAxisAngle(yAxis, PI / 8) // rotate the POSITION - houseGroup.position.add(houseCenter) - houseGroup.rotateOnAxis(yAxis, PI / 8) + useKey("R", () => { + for (let houseGroup of getHouseGroups()) { + houseGroup.rotateOnAxis(yAxis, -PI / 8) houseGroup.updateMatrix() - // updateHouseOBB(houseGroup) + updateHouseOBB(houseGroup) invalidate() } diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index f0e7978d..243be451 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -1,3 +1,4 @@ +import { pipe } from "fp-ts/lib/function" import { BoxGeometry, Group, @@ -9,6 +10,7 @@ import { Vector3, } from "three" import { OBB } from "three-stdlib" +import { A, O } from "../../../utils/functions" import { HouseGroupUserData } from "./userData" let lastMesh: Mesh | null @@ -34,7 +36,7 @@ export const updateHouseOBB = (houseGroup: Group) => { const { x, y, z } = houseGroup.position - const center = new Vector3(x, y + height / 2, z + length / 2) + const center = new Vector3(x, y + height / 2, z) const halfSize = new Vector3(width / 2, height / 2, length / 2) const rotation = new Matrix3().setFromMatrix4(houseGroup.matrix) @@ -50,18 +52,22 @@ export const updateHouseWidth = (houseGroup: Group) => {} export const updateHouseHeight = (houseGroup: Group) => {} export const updateHouseLength = (houseGroup: Group) => { - const { children: columnGroups } = houseGroup + pipe( + houseGroup.children, + A.head, + O.map(({ children: columnGroups }) => { + console.log(`houseGroup length before: ${houseGroup.userData.length}`) - console.log(`houseGroup length before: ${houseGroup.userData.length}`) + houseGroup.userData.length = columnGroups.reduce( + (acc, columnGroup) => acc + columnGroup.userData.length, + 0 + ) - houseGroup.userData.length = columnGroups.reduce( - (acc, columnGroup) => acc + columnGroup.userData.length, - 0 - ) - - console.log(`houseGroup length after: ${houseGroup.userData.length}`) + console.log(`houseGroup length after: ${houseGroup.userData.length}`) - // updateHouseOBB(houseGroup) + // updateHouseOBB(houseGroup) - // update length should update dimensions, obb etc... + // update length should update dimensions, obb etc... + }) + ) } diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 6e1e10b2..bdf668f3 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -248,7 +248,8 @@ export const createHouseGroup = async ({ houseId, houseLayout, }) - const houseGroup = new Group() + const topLevelHouseGroup = new Group() + const zCenterHouseGroup = new Group() const width = houseLayout[0].gridGroups[0].modules[0].module.width const height = houseLayout[0].gridGroups.reduce( (acc, v) => acc + v.modules[0].module.height, @@ -278,9 +279,11 @@ export const createHouseGroup = async ({ levelTypes: houseLayoutToLevelTypes(houseLayout), columnCount: columnGroups.length, } - houseGroup.userData = houseGroupUserData - houseGroup.add(...columnGroups) - return houseGroup + topLevelHouseGroup.userData = houseGroupUserData + zCenterHouseGroup.add(...columnGroups) + zCenterHouseGroup.position.setZ(-length / 2) + topLevelHouseGroup.add(zCenterHouseGroup) + return topLevelHouseGroup } const houseGroup = pipe( @@ -313,142 +316,152 @@ export const getFirstHouseLayout = () => ) export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { - const { children } = houseGroup - - const levelTypes: string[] = houseGroup.userData.levelTypes - const systemId: string = houseGroup.userData.systemId - const columnCount = houseGroup.userData.columnCount + pipe( + houseGroup.children, + A.head, + O.map(({ children }) => { + const levelTypes: string[] = houseGroup.userData.levelTypes + const systemId: string = houseGroup.userData.systemId + const columnCount = houseGroup.userData.columnCount - const vanillaColumn = - vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] + const vanillaColumn = + vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] - console.log({ vanillaColumn, systemId, levelTypes, houseGroup }) + console.log({ vanillaColumn, systemId, levelTypes, houseGroup }) - const vanillaColumnGroup = createColumnGroup({ - systemId, - gridGroups: vanillaColumn.gridGroups, - columnIndex: -1, - }) + const vanillaColumnGroup = createColumnGroup({ + systemId, + gridGroups: vanillaColumn.gridGroups, + columnIndex: -1, + }) - const vanillaColumnLength = vanillaColumnGroup.userData.length + const vanillaColumnLength = vanillaColumnGroup.userData.length - if (direction === 1) { - pipe( - children, - A.findFirst((x) => x.userData.columnIndex === columnCount - 1), - O.map((endColumnGroup) => { - vanillaColumnGroup.position.setZ( - endColumnGroup.position.z + vanillaColumnLength / 2 - ) - houseGroup.add(vanillaColumnGroup) + if (direction === 1) { + pipe( + children, + A.findFirst((x) => x.userData.columnIndex === columnCount - 1), + O.map((endColumnGroup) => { + vanillaColumnGroup.position.setZ( + endColumnGroup.position.z + vanillaColumnLength / 2 + ) + houseGroup.add(vanillaColumnGroup) - endColumnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) + endColumnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) - vanillaColumnGroup.userData.columnIndex = - endColumnGroup.userData.columnIndex + vanillaColumnGroup.userData.columnIndex = + endColumnGroup.userData.columnIndex - endColumnGroup.userData.columnIndex++ + endColumnGroup.userData.columnIndex++ - houseGroup.userData.columnCount = columnCount + 1 + houseGroup.userData.columnCount = columnCount + 1 - invalidate() - }) - ) - } else if (direction === -1) { - pipe( - children, - A.partition((x) => x.userData.columnIndex !== 0), - ({ left: [startColumnGroup], right: otherColumnGroups }) => { - startColumnGroup.position.add(new Vector3(0, 0, -vanillaColumnLength)) - - for (let otherColumnGroup of otherColumnGroups) { - otherColumnGroup.userData.columnIndex++ - } - - vanillaColumnGroup.userData.columnIndex = 1 - vanillaColumnGroup.position.setZ( - startColumnGroup.position.z + - startColumnGroup.userData.length + - vanillaColumnLength / 2 + invalidate() + }) ) - houseGroup.add(vanillaColumnGroup) + } else if (direction === -1) { + pipe( + children, + A.partition((x) => x.userData.columnIndex !== 0), + ({ left: [startColumnGroup], right: otherColumnGroups }) => { + startColumnGroup.position.add( + new Vector3(0, 0, -vanillaColumnLength) + ) - houseGroup.userData.columnCount = columnCount + 1 + for (let otherColumnGroup of otherColumnGroups) { + otherColumnGroup.userData.columnIndex++ + } + + vanillaColumnGroup.userData.columnIndex = 1 + vanillaColumnGroup.position.setZ( + startColumnGroup.position.z + + startColumnGroup.userData.length + + vanillaColumnLength / 2 + ) + houseGroup.add(vanillaColumnGroup) - invalidate() + houseGroup.userData.columnCount = columnCount + 1 + + invalidate() + } + ) } - ) - } - updateHouseLength(houseGroup) + updateHouseLength(houseGroup) + }) + ) } export const subtractPenultimateColumn = ( houseGroup: Group, direction: 1 | -1 ) => { - const { children } = houseGroup - - const columnCount: number = houseGroup.userData.columnCount - - if (columnCount <= 3) return - - if (direction === 1) { - pipe( - children, - A.filter((x) => x.userData.columnIndex >= columnCount - 2), - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), - ([penultimateColumnGroup, endColumnGroup]) => { - endColumnGroup.position.add( - new Vector3(0, 0, -penultimateColumnGroup.userData.length) - ) - - houseGroup.remove(penultimateColumnGroup) + pipe( + houseGroup.children, + A.head, + O.map(({ children }) => { + const columnCount: number = houseGroup.userData.columnCount - houseGroup.userData.columnCount = columnCount - 1 - endColumnGroup.userData.columnIndex-- + if (columnCount <= 3) return - invalidate() - } - ) - } else if (direction === -1) { - pipe( - children, - A.partition((x) => x.userData.columnIndex >= 2), - ({ left: startColumnGroups, right: otherColumnGroups }) => + if (direction === 1) { pipe( - startColumnGroups, + children, + A.filter((x) => x.userData.columnIndex >= columnCount - 2), A.sort( pipe( Num.Ord, Ord.contramap((x: Object3D) => x.userData.columnIndex) ) ), - ([startColumnGroup, secondColumnGroup]) => { - startColumnGroup.position.add( - new Vector3(0, 0, secondColumnGroup.userData.length) + ([penultimateColumnGroup, endColumnGroup]) => { + endColumnGroup.position.add( + new Vector3(0, 0, -penultimateColumnGroup.userData.length) ) - houseGroup.remove(secondColumnGroup) + houseGroup.remove(penultimateColumnGroup) houseGroup.userData.columnCount = columnCount - 1 - - for (let otherColumnGroup of otherColumnGroups) { - otherColumnGroup.userData.columnIndex-- - } + endColumnGroup.userData.columnIndex-- invalidate() } ) - ) - } + } else if (direction === -1) { + pipe( + children, + A.partition((x) => x.userData.columnIndex >= 2), + ({ left: startColumnGroups, right: otherColumnGroups }) => + pipe( + startColumnGroups, + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ), + ([startColumnGroup, secondColumnGroup]) => { + startColumnGroup.position.add( + new Vector3(0, 0, secondColumnGroup.userData.length) + ) + + houseGroup.remove(secondColumnGroup) + + houseGroup.userData.columnCount = columnCount - 1 + + for (let otherColumnGroup of otherColumnGroups) { + otherColumnGroup.userData.columnIndex-- + } + + invalidate() + } + ) + ) + } - updateHouseLength(houseGroup) + updateHouseLength(houseGroup) + }) + ) } export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index ebbfc910..14775886 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -7,10 +7,11 @@ import { z } from "zod" // or at certain levels/scopes? // houseGroup has -// -> columnGroups as children has -// -> gridGroups as children has -// -> moduleGroups as children has -// -> elementMeshes as children +// -> zCenterGroup as singleton child has +// -> columnGroups as children has +// -> gridGroups as children has +// -> moduleGroups as children has +// -> elementMeshes as children export const UserDataTypeEnum = z.enum([ "HouseGroup", From 0b89b0fb65b15a5a8d900eeab3289406f8f44001 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 17 Jul 2023 12:20:53 +0100 Subject: [PATCH 050/132] wip move/rotate/stretch obb consistency --- app/design/ui-3d/fresh/FreshApp.tsx | 7 +- app/design/ui-3d/fresh/dimensions.ts | 21 +++--- app/design/ui-3d/fresh/helpers.ts | 103 +++++++++++++++------------ app/design/ui-3d/fresh/userData.ts | 18 +++-- 4 files changed, 84 insertions(+), 65 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 16219422..77fe9456 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -96,8 +96,6 @@ const FreshApp = () => { useDeleteHouseListener(({ id }) => { if (!rootRef.current) return - console.log(`deleting id ${id}`) - const target = rootRef.current.children.find((x) => x.userData.id === id) if (target) { @@ -108,7 +106,7 @@ const FreshApp = () => { const getHouseGroups = () => (rootRef.current?.children ?? []).filter( - (x) => x.userData.type === UserDataTypeEnum.Enum.HouseGroup + (x) => x.userData.type === UserDataTypeEnum.Enum.HouseRootGroup ) as Group[] useKey("z", () => { @@ -291,8 +289,7 @@ const FreshApp = () => { case UserDataTypeEnum.Enum.ElementMesh: // console.log(userData) break - case UserDataTypeEnum.Enum.HouseGroup: - console.log(userData) + case UserDataTypeEnum.Enum.HouseRootGroup: // TypeScript knows that userData is of type HouseModuleGroupUserData in this block // console.log(userData.length) // This is valid // console.log(userData.houseId) // TypeScript error, houseId doesn't exist on HouseModuleGroupUserData diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 243be451..2a869497 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -11,7 +11,9 @@ import { } from "three" import { OBB } from "three-stdlib" import { A, O } from "../../../utils/functions" -import { HouseGroupUserData } from "./userData" +import { HouseRootGroupUserData } from "./userData" + +export const DEBUG = false let lastMesh: Mesh | null @@ -30,7 +32,7 @@ const renderOBB = (obb: OBB, scene: Object3D) => { } export const updateHouseOBB = (houseGroup: Group) => { - const houseGroupUserData = houseGroup.userData as HouseGroupUserData + const houseGroupUserData = houseGroup.userData as HouseRootGroupUserData const { width, height, length } = houseGroupUserData @@ -42,9 +44,8 @@ export const updateHouseOBB = (houseGroup: Group) => { houseGroupUserData.obb.set(center, halfSize, rotation) - if (houseGroup.parent) renderOBB(houseGroupUserData.obb, houseGroup.parent) - - console.log(houseGroupUserData.obb) + if (DEBUG && houseGroup.parent) + renderOBB(houseGroupUserData.obb, houseGroup.parent) } export const updateHouseWidth = (houseGroup: Group) => {} @@ -55,19 +56,17 @@ export const updateHouseLength = (houseGroup: Group) => { pipe( houseGroup.children, A.head, - O.map(({ children: columnGroups }) => { - console.log(`houseGroup length before: ${houseGroup.userData.length}`) + O.map((zCenterHouseGroup) => { + const { children: columnGroups } = zCenterHouseGroup houseGroup.userData.length = columnGroups.reduce( (acc, columnGroup) => acc + columnGroup.userData.length, 0 ) - console.log(`houseGroup length after: ${houseGroup.userData.length}`) - - // updateHouseOBB(houseGroup) + zCenterHouseGroup.position.setZ(-houseGroup.userData.length / 2) - // update length should update dimensions, obb etc... + updateHouseOBB(houseGroup) }) ) } diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index bdf668f3..f941317f 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -29,7 +29,7 @@ import { ColumnGroupUserData, ElementMeshUserData, GridGroupUserData, - HouseGroupUserData, + HouseRootGroupUserData, ModuleGroupUserData, UserDataTypeEnum, } from "./userData" @@ -260,8 +260,8 @@ export const createHouseGroup = async ({ 0 ) const obb = new OBB() - const houseGroupUserData: HouseGroupUserData = { - type: UserDataTypeEnum.Enum.HouseGroup, + const houseGroupUserData: HouseRootGroupUserData = { + type: UserDataTypeEnum.Enum.HouseRootGroup, systemId, houseId, width, @@ -315,11 +315,29 @@ export const getFirstHouseLayout = () => O.chain((k) => pipe(houseLayouts, R.lookup(k))) ) +export const addColumnToHouse = (houseGroup: Object3D, columnGroup: Object3D) => + pipe( + houseGroup.children, + A.head, + O.map((zCenterHouseGroup) => void zCenterHouseGroup.add(columnGroup)) + ) + +export const removeColumnFromHouse = ( + houseGroup: Object3D, + columnGroup: Object3D +) => + pipe( + houseGroup.children, + A.head, + O.map((zCenterHouseGroup) => void zCenterHouseGroup.remove(columnGroup)) + ) + export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { pipe( houseGroup.children, A.head, - O.map(({ children }) => { + O.map((zCenterHouseGroup) => { + const { children: columnGroups } = zCenterHouseGroup const levelTypes: string[] = houseGroup.userData.levelTypes const systemId: string = houseGroup.userData.systemId const columnCount = houseGroup.userData.columnCount @@ -327,8 +345,6 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { const vanillaColumn = vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] - console.log({ vanillaColumn, systemId, levelTypes, houseGroup }) - const vanillaColumnGroup = createColumnGroup({ systemId, gridGroups: vanillaColumn.gridGroups, @@ -339,13 +355,13 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { if (direction === 1) { pipe( - children, + columnGroups, A.findFirst((x) => x.userData.columnIndex === columnCount - 1), O.map((endColumnGroup) => { vanillaColumnGroup.position.setZ( endColumnGroup.position.z + vanillaColumnLength / 2 ) - houseGroup.add(vanillaColumnGroup) + addColumnToHouse(houseGroup, vanillaColumnGroup) endColumnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) @@ -361,15 +377,17 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { ) } else if (direction === -1) { pipe( - children, - A.partition((x) => x.userData.columnIndex !== 0), - ({ left: [startColumnGroup], right: otherColumnGroups }) => { - startColumnGroup.position.add( - new Vector3(0, 0, -vanillaColumnLength) + columnGroups, + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) ) - - for (let otherColumnGroup of otherColumnGroups) { - otherColumnGroup.userData.columnIndex++ + ), + ([startColumnGroup, ...restColumnGroups]) => { + for (let columnGroup of restColumnGroups) { + columnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) + columnGroup.userData.columnIndex++ } vanillaColumnGroup.userData.columnIndex = 1 @@ -378,7 +396,7 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { startColumnGroup.userData.length + vanillaColumnLength / 2 ) - houseGroup.add(vanillaColumnGroup) + addColumnToHouse(houseGroup, vanillaColumnGroup) houseGroup.userData.columnCount = columnCount + 1 @@ -415,11 +433,11 @@ export const subtractPenultimateColumn = ( ) ), ([penultimateColumnGroup, endColumnGroup]) => { - endColumnGroup.position.add( - new Vector3(0, 0, -penultimateColumnGroup.userData.length) + endColumnGroup.position.sub( + new Vector3(0, 0, penultimateColumnGroup.userData.length) ) - houseGroup.remove(penultimateColumnGroup) + removeColumnFromHouse(houseGroup, penultimateColumnGroup) houseGroup.userData.columnCount = columnCount - 1 endColumnGroup.userData.columnIndex-- @@ -430,32 +448,29 @@ export const subtractPenultimateColumn = ( } else if (direction === -1) { pipe( children, - A.partition((x) => x.userData.columnIndex >= 2), - ({ left: startColumnGroups, right: otherColumnGroups }) => + A.sort( pipe( - startColumnGroups, - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), - ([startColumnGroup, secondColumnGroup]) => { - startColumnGroup.position.add( - new Vector3(0, 0, secondColumnGroup.userData.length) - ) - - houseGroup.remove(secondColumnGroup) - - houseGroup.userData.columnCount = columnCount - 1 - - for (let otherColumnGroup of otherColumnGroups) { - otherColumnGroup.userData.columnIndex-- - } - - invalidate() - } + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) ) + ), + ([_, secondColumnGroup, ...restColumnGroups]) => { + //don't do anything? + + const subV = new Vector3(0, 0, secondColumnGroup.userData.length) + + restColumnGroups.forEach((columnGroup) => { + columnGroup.position.sub(subV) + columnGroup.userData.columnIndex-- + }) + + removeColumnFromHouse(houseGroup, secondColumnGroup) + + houseGroup.userData.columnCount = columnCount - 1 + + invalidate() + } + // A.partition((x) => x.userData.columnIndex >= 2), ) } diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 14775886..3c41ac32 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -13,8 +13,12 @@ import { z } from "zod" // -> moduleGroups as children has // -> elementMeshes as children +// needs HouseTransformGroup +// HouseColumnsGroup + export const UserDataTypeEnum = z.enum([ - "HouseGroup", + "HouseRootGroup", + "HouseColumnsContainerGroup", "ColumnGroup", "GridGroup", "ModuleGroup", @@ -22,8 +26,8 @@ export const UserDataTypeEnum = z.enum([ ]) export type UserDataTypeEnum = z.infer -export type HouseGroupUserData = { - type: typeof UserDataTypeEnum.Enum.HouseGroup +export type HouseRootGroupUserData = { + type: typeof UserDataTypeEnum.Enum.HouseRootGroup systemId: string houseId: string dnas: string[] @@ -42,6 +46,10 @@ export type HouseGroupUserData = { levelTypes: string[] } +export type HouseColumnsContainerUserData = { + type: typeof UserDataTypeEnum.Enum.HouseColumnsContainerGroup +} + export type ColumnGroupUserData = { type: typeof UserDataTypeEnum.Enum.ColumnGroup columnIndex: number @@ -73,7 +81,7 @@ export type UserData = | ModuleGroupUserData | GridGroupUserData | ColumnGroupUserData - | HouseGroupUserData + | HouseRootGroupUserData // ASSUMPTIONS // ----------- @@ -85,7 +93,7 @@ export const updateHouseUserData = (houseGroup: Group) => {} // update SOME PART OF THE HOUSE // based on SOME OTHER PARTS OF THE HOUSE export const updateClippingPlanes = (houseGroup: Group) => { - const houseGroupUserData = houseGroup.userData as HouseGroupUserData + const houseGroupUserData = houseGroup.userData as HouseRootGroupUserData // x, y, z... From 5bdec1fba151bed924a0708a9d4de3d1ae10c5a9 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 20 Jul 2023 11:38:39 +0100 Subject: [PATCH 051/132] wip minor --- app/design/ui-3d/fresh/dimensions.ts | 32 ++++++++++++++++++++++++++-- app/design/ui-3d/fresh/userData.ts | 26 ---------------------- app/utils/three.ts | 3 +++ 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 2a869497..9294ad0d 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -1,7 +1,8 @@ -import { pipe } from "fp-ts/lib/function" +import { flow, pipe } from "fp-ts/lib/function" import { BoxGeometry, Group, + Material, Matrix3, Matrix4, Mesh, @@ -11,7 +12,8 @@ import { } from "three" import { OBB } from "three-stdlib" import { A, O } from "../../../utils/functions" -import { HouseRootGroupUserData } from "./userData" +import { isMesh, xAxis, yAxis, zAxis } from "../../../utils/three" +import { HouseRootGroupUserData, UserDataTypeEnum } from "./userData" export const DEBUG = false @@ -52,6 +54,30 @@ export const updateHouseWidth = (houseGroup: Group) => {} export const updateHouseHeight = (houseGroup: Group) => {} +export const updateClippingPlanes = flow((houseGroup: Group) => { + const { clippingPlanes } = houseGroup.userData as HouseRootGroupUserData + houseGroup.updateMatrix() + clippingPlanes.x.set(xAxis, 0) + clippingPlanes.x.applyMatrix4(houseGroup.matrix) + clippingPlanes.y.set(yAxis, 0) + clippingPlanes.y.applyMatrix4(houseGroup.matrix) + clippingPlanes.z.set(zAxis, 0) + clippingPlanes.z.applyMatrix4(houseGroup.matrix) + + houseGroup.traverse((node) => { + switch (node.userData.type) { + case UserDataTypeEnum.Enum.ElementMesh: { + if (!isMesh(node)) return + ;((node as Mesh).material as Material).clippingPlanes = [ + clippingPlanes.x, + clippingPlanes.y, + clippingPlanes.z, + ] + } + } + }) +}) + export const updateHouseLength = (houseGroup: Group) => { pipe( houseGroup.children, @@ -69,4 +95,6 @@ export const updateHouseLength = (houseGroup: Group) => { updateHouseOBB(houseGroup) }) ) + + updateClippingPlanes(houseGroup) } diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 3c41ac32..f12e318d 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -82,29 +82,3 @@ export type UserData = | GridGroupUserData | ColumnGroupUserData | HouseRootGroupUserData - -// ASSUMPTIONS -// ----------- -// we've updated the ... something or other... -export const updateHouseUserData = (houseGroup: Group) => {} - -// MORE GRANULAR -// ------------- -// update SOME PART OF THE HOUSE -// based on SOME OTHER PARTS OF THE HOUSE -export const updateClippingPlanes = (houseGroup: Group) => { - const houseGroupUserData = houseGroup.userData as HouseRootGroupUserData - - // x, y, z... - - // x and z is like okay yeah standard or... well... - // dynamic on user controls could be cool - - // y is like dependent on levelIndex of level mode - - // first we want the clipping planes in the user data -} - -export const getClippingPlanesStuff = (houseGroup: Group) => { - // dimensions should do it I guess? -} diff --git a/app/utils/three.ts b/app/utils/three.ts index a9d58f8c..67cfc612 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -47,6 +47,7 @@ export const createMaterial = (config: Material) => { // wireframe: true, // opacity: 0.6, // depthTest: false, + clipShadows: true, }) } @@ -152,7 +153,9 @@ export const useSetRotation = (houseId: string) => { ) } +export const xAxis = new Vector3(1, 0, 0) export const yAxis = new Vector3(0, 1, 0) +export const zAxis = new Vector3(0, 0, 1) export const useRotations = () => { const rotationMatrix = useRef(new Matrix4()) From 61266eb632bafd58fd23188c343245c35386f180 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 20 Jul 2023 14:14:47 +0100 Subject: [PATCH 052/132] wip pre stencil --- app/design/ui-3d/fresh/FreshApp.tsx | 25 +++++++------- app/design/ui-3d/fresh/dimensions.ts | 40 ++++++++++------------ app/design/ui-3d/fresh/helpers.ts | 51 ++++++++++++++-------------- app/design/ui-3d/fresh/userData.ts | 6 +--- app/design/ui-3d/init/Effects.tsx | 5 ++- 5 files changed, 60 insertions(+), 67 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 77fe9456..4f680765 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -14,7 +14,7 @@ import { openMenu } from "../../state/menu" import scope, { ScopeItem } from "../../state/scope" import settings from "../../state/settings" import siteCtx, { downMode, SiteCtxModeEnum } from "../../state/siteCtx" -import { updateHouseOBB } from "./dimensions" +import { updateEverything, updateHouseOBB } from "./dimensions" import { dispatchAddHouse, useAddHouseIntentListener, @@ -48,6 +48,7 @@ const FreshApp = () => { friendlyName, }) rootRef.current.add(houseGroup) + invalidate() userDB.houses.put(house) @@ -102,6 +103,8 @@ const FreshApp = () => { rootRef.current.remove(target) userDB.houses.delete(id) } + + invalidate() }) const getHouseGroups = () => @@ -112,59 +115,55 @@ const FreshApp = () => { useKey("z", () => { for (let houseGroup of getHouseGroups()) { insertVanillaColumn(houseGroup, 1) + updateEverything(houseGroup) } }) useKey("Z", () => { for (let houseGroup of getHouseGroups()) { insertVanillaColumn(houseGroup, -1) + updateEverything(houseGroup) } }) useKey("d", () => { for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, 1) + updateEverything(houseGroup) } }) useKey("D", () => { for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, -1) + updateEverything(houseGroup) } }) useKey("t", () => { for (let houseGroup of getHouseGroups()) { houseGroup.position.add(new Vector3(1, 0, 1)) - updateHouseOBB(houseGroup) - invalidate() + updateEverything(houseGroup) } }) useKey("T", () => { for (let houseGroup of getHouseGroups()) { houseGroup.position.add(new Vector3(-1, 0, -1)) - updateHouseOBB(houseGroup) - invalidate() + updateEverything(houseGroup) } }) useKey("r", () => { for (let houseGroup of getHouseGroups()) { houseGroup.rotateOnAxis(yAxis, PI / 8) - houseGroup.updateMatrix() - updateHouseOBB(houseGroup) - - invalidate() + updateEverything(houseGroup) } }) useKey("R", () => { for (let houseGroup of getHouseGroups()) { houseGroup.rotateOnAxis(yAxis, -PI / 8) - houseGroup.updateMatrix() - updateHouseOBB(houseGroup) - - invalidate() + updateEverything(houseGroup) } }) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 9294ad0d..292271f8 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -1,3 +1,4 @@ +import { invalidate } from "@react-three/fiber" import { flow, pipe } from "fp-ts/lib/function" import { BoxGeometry, @@ -55,27 +56,19 @@ export const updateHouseWidth = (houseGroup: Group) => {} export const updateHouseHeight = (houseGroup: Group) => {} export const updateClippingPlanes = flow((houseGroup: Group) => { - const { clippingPlanes } = houseGroup.userData as HouseRootGroupUserData + const { + clippingPlanes, + clippingPlanes: [planeX, planeY, planeZ], + } = houseGroup.userData as HouseRootGroupUserData + houseGroup.updateMatrix() - clippingPlanes.x.set(xAxis, 0) - clippingPlanes.x.applyMatrix4(houseGroup.matrix) - clippingPlanes.y.set(yAxis, 0) - clippingPlanes.y.applyMatrix4(houseGroup.matrix) - clippingPlanes.z.set(zAxis, 0) - clippingPlanes.z.applyMatrix4(houseGroup.matrix) - - houseGroup.traverse((node) => { - switch (node.userData.type) { - case UserDataTypeEnum.Enum.ElementMesh: { - if (!isMesh(node)) return - ;((node as Mesh).material as Material).clippingPlanes = [ - clippingPlanes.x, - clippingPlanes.y, - clippingPlanes.z, - ] - } - } - }) + + planeX.set(new Vector3(1, 0, 0), 0) + planeX.applyMatrix4(houseGroup.matrix) + planeY.set(new Vector3(0, 1, 0), 0) + planeY.applyMatrix4(houseGroup.matrix) + planeZ.set(new Vector3(0, 0, 1), 0) + planeZ.applyMatrix4(houseGroup.matrix) }) export const updateHouseLength = (houseGroup: Group) => { @@ -91,10 +84,13 @@ export const updateHouseLength = (houseGroup: Group) => { ) zCenterHouseGroup.position.setZ(-houseGroup.userData.length / 2) - - updateHouseOBB(houseGroup) }) ) +} +export const updateEverything = (houseGroup: Group) => { + updateHouseLength(houseGroup) + updateHouseOBB(houseGroup) updateClippingPlanes(houseGroup) + invalidate() } diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index f941317f..97d96dc0 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -1,4 +1,3 @@ -import { invalidate } from "@react-three/fiber" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" import { @@ -23,7 +22,6 @@ import layoutsDB, { } from "../../../db/layouts" import { A, Num, O, Ord, R, S } from "../../../utils/functions" import { getLayoutsWorker } from "../../../workers" -import { updateHouseLength } from "./dimensions" import { getMaterial } from "./systems" import { ColumnGroupUserData, @@ -91,10 +89,12 @@ export const moduleToGroup = ({ systemId, gridGroupIndex, module: { speckleBranchUrl, length, dna }, + clippingPlanes, }: { systemId: string gridGroupIndex: number module: Module + clippingPlanes: Plane[] }) => { const moduleGroup = new Group() const taggedModelGeometries = models[speckleBranchUrl] @@ -105,6 +105,7 @@ export const moduleToGroup = ({ ifcTag, houseId: "", }) as MeshStandardMaterial + material.clippingPlanes = clippingPlanes const mesh = new Mesh(geometry, material) mesh.castShadow = true @@ -135,12 +136,14 @@ export const createColumnGroup = ({ columnIndex, startColumn = false, endColumn = false, + clippingPlanes, }: { systemId: string gridGroups: GridGroup[] columnIndex: number startColumn?: boolean endColumn?: boolean + clippingPlanes: Plane[] }): Group => { const columnGroup = new Group() @@ -152,6 +155,7 @@ export const createColumnGroup = ({ systemId, module, gridGroupIndex, + clippingPlanes, }) moduleGroup.scale.set(1, 1, endColumn ? 1 : -1) moduleGroup.position.set( @@ -190,10 +194,12 @@ export const houseLayoutToColumns = ({ systemId, houseId, houseLayout, + clippingPlanes, }: { systemId: string houseId: string houseLayout: ColumnLayout + clippingPlanes: Plane[] }): Group[] => pipe( houseLayout, @@ -207,6 +213,7 @@ export const houseLayoutToColumns = ({ startColumn, endColumn, columnIndex, + clippingPlanes, }) group.position.set(0, 0, z) return group @@ -234,6 +241,12 @@ export const createHouseGroup = async ({ dnas: string[] friendlyName: string }) => { + const clippingPlanes: Plane[] = [ + new Plane(new Vector3(1, 0, 0), 0), + new Plane(new Vector3(0, 1, 0), 0), + new Plane(new Vector3(0, 0, 1), 0), + ] + const houseLayoutToHouseGroup = async ({ systemId, houseId, @@ -247,6 +260,7 @@ export const createHouseGroup = async ({ systemId, houseId, houseLayout, + clippingPlanes, }) const topLevelHouseGroup = new Group() const zCenterHouseGroup = new Group() @@ -271,11 +285,7 @@ export const createHouseGroup = async ({ friendlyName, modifiedMaterials: {}, obb, - clippingPlanes: { - x: new Plane(), - y: new Plane(), - z: new Plane(), - }, + clippingPlanes, levelTypes: houseLayoutToLevelTypes(houseLayout), columnCount: columnGroups.length, } @@ -297,7 +307,11 @@ export const createHouseGroup = async ({ systemId, dnas, }) - return houseLayoutToHouseGroup({ systemId, houseId, houseLayout }) + return houseLayoutToHouseGroup({ + systemId, + houseId, + houseLayout, + }) }, (houseLayout) => houseLayoutToHouseGroup({ systemId, houseId, houseLayout }) @@ -333,14 +347,13 @@ export const removeColumnFromHouse = ( ) export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { + const { levelTypes, systemId, columnCount, clippingPlanes } = + houseGroup.userData as HouseRootGroupUserData pipe( houseGroup.children, A.head, O.map((zCenterHouseGroup) => { const { children: columnGroups } = zCenterHouseGroup - const levelTypes: string[] = houseGroup.userData.levelTypes - const systemId: string = houseGroup.userData.systemId - const columnCount = houseGroup.userData.columnCount const vanillaColumn = vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] @@ -349,6 +362,7 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { systemId, gridGroups: vanillaColumn.gridGroups, columnIndex: -1, + clippingPlanes, }) const vanillaColumnLength = vanillaColumnGroup.userData.length @@ -371,8 +385,6 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { endColumnGroup.userData.columnIndex++ houseGroup.userData.columnCount = columnCount + 1 - - invalidate() }) ) } else if (direction === -1) { @@ -399,13 +411,9 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { addColumnToHouse(houseGroup, vanillaColumnGroup) houseGroup.userData.columnCount = columnCount + 1 - - invalidate() } ) } - - updateHouseLength(houseGroup) }) ) } @@ -441,8 +449,6 @@ export const subtractPenultimateColumn = ( houseGroup.userData.columnCount = columnCount - 1 endColumnGroup.userData.columnIndex-- - - invalidate() } ) } else if (direction === -1) { @@ -455,8 +461,6 @@ export const subtractPenultimateColumn = ( ) ), ([_, secondColumnGroup, ...restColumnGroups]) => { - //don't do anything? - const subV = new Vector3(0, 0, secondColumnGroup.userData.length) restColumnGroups.forEach((columnGroup) => { @@ -467,14 +471,9 @@ export const subtractPenultimateColumn = ( removeColumnFromHouse(houseGroup, secondColumnGroup) houseGroup.userData.columnCount = columnCount - 1 - - invalidate() } - // A.partition((x) => x.userData.columnIndex >= 2), ) } - - updateHouseLength(houseGroup) }) ) } diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index f12e318d..914e29eb 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -37,11 +37,7 @@ export type HouseRootGroupUserData = { length: number width: number obb: OBB - clippingPlanes: { - x: Plane - y: Plane - z: Plane - } + clippingPlanes: Plane[] columnCount: number levelTypes: string[] } diff --git a/app/design/ui-3d/init/Effects.tsx b/app/design/ui-3d/init/Effects.tsx index 31fe1a6c..ddc9c91c 100644 --- a/app/design/ui-3d/init/Effects.tsx +++ b/app/design/ui-3d/init/Effects.tsx @@ -35,7 +35,10 @@ const defaultRenderPriority: number = 1 const Effects = () => { const { gl, camera, size, scene } = useThree() - const renderTarget = useFBO(size.width, size.height, { depthBuffer: true }) + const renderTarget = useFBO(size.width, size.height, { + depthBuffer: true, + stencilBuffer: true, + }) // const selectiveBloomEffect = useMemo(() => { // const effect = new SelectiveBloomEffect(scene, camera, { From ea9abb562f4fde66f2ec2775077bd9d91d1e1fcc Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 20 Jul 2023 15:31:57 +0100 Subject: [PATCH 053/132] wip delete clippingPlanes file --- app/design/ui-3d/fresh/clippingPlanes.ts | 145 ----------------------- 1 file changed, 145 deletions(-) delete mode 100644 app/design/ui-3d/fresh/clippingPlanes.ts diff --git a/app/design/ui-3d/fresh/clippingPlanes.ts b/app/design/ui-3d/fresh/clippingPlanes.ts deleted file mode 100644 index 13c5abfa..00000000 --- a/app/design/ui-3d/fresh/clippingPlanes.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { Plane, Vector3 } from "three" - -const clippingPlanes = { - x: new Plane(), - y: new Plane(), - z: new Plane(), -} - -const axes = { - x: new Vector3(1, 0, 0), - y: new Vector3(0, -1, 0), - z: new Vector3(0, 0, 1), -} - -// const clippingPlaneX = useMemo(() => new Plane(), []) -// const clippingPlaneY = useMemo(() => new Plane(), []) -// const clippingPlaneZ = useMemo(() => new Plane(), []) - -// export const dispatchClippingPlanesUpdate = () => {} - -// export const onClippingPlanesUpdate = () => {} - -export default clippingPlanes - -// export const useClippingPlanes = ({ -// ref, -// houseId, -// layoutsKey, -// }: { -// houseId: string -// ref: RefObject -// layoutsKey: string -// }) => { -// const systemId = houses[houseId].systemId -// const elementMaterials = useRef>({}) -// const categoryElements = useRef>({}) -// const elements = useSystemElements({ systemId }) - -// const getPlaneMatrix = usePlaneMatrix(houseId) - -// const xAxis = useMemo(() => new Vector3(1, 0, 0), []) -// const yAxis = useMemo(() => new Vector3(0, -1, 0), []) -// const zAxis = useMemo(() => new Vector3(0, 0, 1), []) - -// const levelHeight = -// siteCtx.levelIndex === null -// ? Infinity -// : layouts[layoutsKey][0].gridGroups[siteCtx.levelIndex].y + -// layouts[layoutsKey][0].gridGroups[siteCtx.levelIndex].modules[0].module -// .height / -// 2 - -// const updateMatrices = () => { -// const planeMatrix = getPlaneMatrix() - -// clippingPlaneX.set(xAxis, 0) -// clippingPlaneX.applyMatrix4(planeMatrix) -// clippingPlaneY.set(yAxis, levelHeight) -// clippingPlaneY.applyMatrix4(planeMatrix) -// clippingPlaneZ.set(zAxis, 0) -// clippingPlaneZ.applyMatrix4(planeMatrix) - -// invalidate() -// } -// useSubscribeKey(postTransformsTransients, houseId, updateMatrices, true) - -// useEffect(() => { -// ref.current?.traverse((o3) => { -// if ( -// !isMesh(o3) || -// Array.isArray(o3.material) || -// o3.userData.identifier?.identifierType !== "HOUSE_ELEMENT" || -// o3.userData.identifier?.elementName in elementMaterials.current -// ) { -// return -// } - -// const { elementName } = o3.userData.identifier - -// elementMaterials.current[elementName] = o3.material - -// pipe( -// elements, -// RA.findFirstMap(({ name, category }) => -// name === elementName ? O.some(category) : O.none -// ), -// O.map((category) => { -// if (!(category in categoryElements.current)) { -// categoryElements.current[category] = [elementName] -// } else { -// categoryElements.current[category].push(elementName) -// } -// }) -// ) -// }) - -// return () => { -// elementMaterials.current = {} -// categoryElements.current = {} -// } -// }, [elements, ref]) - -// useSubscribe( -// elementCategories, -// (...ops) => { -// pipe( -// ops, -// A.map( -// A.chain(([_, categories, value]: any): any => { -// pipe( -// categoryElements.current, -// R.mapWithIndex((cat, elements) => { -// if (categories.includes(cat)) { -// for (let element of elements) { -// elementMaterials.current[element].visible = value -// } -// } -// }) -// ) -// }) -// ) -// ) -// invalidate() -// }, -// true -// ) - -// useSubscribeKey( -// settings, -// "verticalCuts", -// () => { -// const { length, width } = settings.verticalCuts -// const { levelIndex } = siteCtx -// Object.values(elementMaterials.current).forEach((material) => { -// material.clippingPlanes = [ -// width ? [clippingPlaneZ] : [], -// levelIndex !== null ? [clippingPlaneY] : [], -// length ? [clippingPlaneX] : [], -// ].flat() -// }) -// invalidate() -// }, -// true -// ) -// } From e63ce52dc96db503a08f0cafb632945f58099d93 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 26 Jul 2023 12:46:02 +0100 Subject: [PATCH 054/132] wip; layout algo type checking --- app/data/elements.ts | 4 - app/data/modules.ts | 16 +- app/design/ui-3d/fresh/FreshApp.tsx | 27 +- app/design/ui-3d/fresh/helpers.ts | 6 + app/design/ui-3d/fresh/stretchXPreviews.ts | 36 ++ app/design/ui-3d/fresh/userData.ts | 1 + .../grouped/stretchWidth/StretchWidth.tsx | 14 +- app/utils/functions.ts | 4 + app/utils/three.ts | 5 +- app/workers/layouts/worker.ts | 403 ++++++++++++++---- 10 files changed, 419 insertions(+), 97 deletions(-) create mode 100644 app/design/ui-3d/fresh/stretchXPreviews.ts diff --git a/app/data/elements.ts b/app/data/elements.ts index fd075e9e..78e93aea 100644 --- a/app/data/elements.ts +++ b/app/data/elements.ts @@ -1,17 +1,13 @@ import { trpc } from "@/client/trpc" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" -import { useMemo } from "react" -import { suspend } from "suspend-react" import { BufferGeometry, BufferGeometryLoader } from "three" import { proxy, ref, useSnapshot } from "valtio" import { O, R, RA, S } from "~/utils/functions" import { Element } from "../../server/data/elements" -import { Module } from "../../server/data/modules" import layoutsDB from "../db/layouts" import systemsDB from "../db/systems" import { isSSR } from "../utils/next" -import { getLayoutsWorker } from "../workers" export const useElements = (): Element[] => { const { data = [] } = trpc.elements.useQuery() diff --git a/app/data/modules.ts b/app/data/modules.ts index 1f3a5488..7918baa1 100644 --- a/app/data/modules.ts +++ b/app/data/modules.ts @@ -101,13 +101,15 @@ export const filterCompatibleModules = "gridType", ] ) => - (module: Module) => - A.filter((m: Module) => - ks.reduce( - (acc: boolean, k) => - acc && m.structuredDna[k] === module.structuredDna[k], - true - ) + (moduleA: Module) => + A.filter( + (moduleB: Module) => + moduleB.systemId === moduleA.systemId && + ks.reduce( + (acc: boolean, k) => + acc && moduleB.structuredDna[k] === moduleA.structuredDna[k], + true + ) ) export const keysFilter = diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 4f680765..06bd78e7 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -14,7 +14,7 @@ import { openMenu } from "../../state/menu" import scope, { ScopeItem } from "../../state/scope" import settings from "../../state/settings" import siteCtx, { downMode, SiteCtxModeEnum } from "../../state/siteCtx" -import { updateEverything, updateHouseOBB } from "./dimensions" +import { updateEverything } from "./dimensions" import { dispatchAddHouse, useAddHouseIntentListener, @@ -27,9 +27,11 @@ import { insertVanillaColumn, subtractPenultimateColumn, } from "./helpers" +import getStretchXPreviews from "./stretchXPreviews" import { UserData, UserDataTypeEnum } from "./userData" -// let houseGroups: Record = {} +const liveHouses: Record = {} +const stretchXHouses: Record> = {} const FreshApp = () => { const rootRef = useRef(null) @@ -39,15 +41,26 @@ const FreshApp = () => { } const addHouse = async (house: House) => { - const { id: houseId, systemId, dnas, friendlyName } = house if (!rootRef.current) return + + const { id: houseId, systemId, dnas, friendlyName } = house + const houseGroup = await createHouseGroup({ systemId, houseId, dnas, friendlyName, }) + rootRef.current.add(houseGroup) + liveHouses[houseId] = houseGroup + + getStretchXPreviews(houseGroup).then((x) => { + console.log(x, `hi`) + }) + + // stretchXHouses[houseId] = getStretchXPreviews(houseGroup) + // (stretchXHouses) invalidate() @@ -126,6 +139,12 @@ const FreshApp = () => { } }) + // stretch width + + useKey("x", () => {}) + + // stretch width - + useKey("X", () => {}) + useKey("d", () => { for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, 1) @@ -199,6 +218,8 @@ const FreshApp = () => { // }) // } + console.log(object) + switch (siteCtx.mode) { case SiteCtxModeEnum.Enum.SITE: if (object.parent?.parent?.parent?.parent) { diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 97d96dc0..fe518d11 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -264,6 +264,10 @@ export const createHouseGroup = async ({ }) const topLevelHouseGroup = new Group() const zCenterHouseGroup = new Group() + + const sectionType = + houseLayout[0].gridGroups[0].modules[0].module.structuredDna.sectionType + const width = houseLayout[0].gridGroups[0].modules[0].module.width const height = houseLayout[0].gridGroups.reduce( (acc, v) => acc + v.modules[0].module.height, @@ -286,6 +290,7 @@ export const createHouseGroup = async ({ modifiedMaterials: {}, obb, clippingPlanes, + sectionType, levelTypes: houseLayoutToLevelTypes(houseLayout), columnCount: columnGroups.length, } @@ -293,6 +298,7 @@ export const createHouseGroup = async ({ zCenterHouseGroup.add(...columnGroups) zCenterHouseGroup.position.setZ(-length / 2) topLevelHouseGroup.add(zCenterHouseGroup) + return topLevelHouseGroup } diff --git a/app/design/ui-3d/fresh/stretchXPreviews.ts b/app/design/ui-3d/fresh/stretchXPreviews.ts new file mode 100644 index 00000000..5b7977d6 --- /dev/null +++ b/app/design/ui-3d/fresh/stretchXPreviews.ts @@ -0,0 +1,36 @@ +import { Group } from "three" +import { getLayoutsWorker } from "../../../workers" +import { HouseRootGroupUserData } from "./userData" + +const getStretchXPreviews = async ( + houseGroup: Group +): Promise> => { + const layoutsWorker = getLayoutsWorker() + + if (layoutsWorker === null) + throw new Error(`layoutsWorker null in stretchXPreviews`) + + const previews: Record = {} + + const { houseId } = houseGroup.userData as HouseRootGroupUserData + + // const foo = await layoutsWorker.getStretchXPreviews(houseId) + // console.log(foo) + + // get the dnas? + // check old func for dnas -> new dnas + + // iter section types first + + // doesn't this go in a worker though? + + // check db + + // yeah we just need to post for layouts + + // we could post houseId, sectionType + + return previews +} + +export default getStretchXPreviews diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 914e29eb..723dba5e 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -39,6 +39,7 @@ export type HouseRootGroupUserData = { obb: OBB clippingPlanes: Plane[] columnCount: number + sectionType: string levelTypes: string[] } diff --git a/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx b/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx index 4e7ce948..ba1954c9 100644 --- a/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx +++ b/app/design/ui-3d/grouped/stretchWidth/StretchWidth.tsx @@ -40,6 +40,13 @@ import { } from "../../../../db/layouts" import { columnLayoutToDnas } from "../../../../workers/layouts/worker" +export type AugSectionType = SectionType & { + // live: boolean + houseDna: string[] + houseDnaKey: string + dx: number +} + export type StretchWidthRaw = { direction: 1 | -1 dx: number @@ -79,13 +86,6 @@ const StretchWidth = forwardRef((props, rootRef) => { const { width: houseWidth } = useHouseDimensionsUpdates(houseId) - type AugSectionType = SectionType & { - // live: boolean - houseDna: string[] - houseDnaKey: string - dx: number - } - const augSectionTypes: NonEmptyArray = pipe( sectionTypes, A.filterMap((st) => diff --git a/app/utils/functions.ts b/app/utils/functions.ts index 464aa23d..05c21ff8 100644 --- a/app/utils/functions.ts +++ b/app/utils/functions.ts @@ -17,6 +17,10 @@ export * as RM from "fp-ts/ReadonlyMap" export * as RNEA from "fp-ts/ReadonlyNonEmptyArray" export * as RR from "fp-ts/ReadonlyRecord" export * as SG from "fp-ts/Semigroup" +export * as TO from "fp-ts/TaskOption" +export * as TE from "fp-ts/TaskEither" +export * as T from "fp-ts/Task" + export { clamp_ as clamp } export { A, Num, O, R, RA, S } diff --git a/app/utils/three.ts b/app/utils/three.ts index 67cfc612..3c964436 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -30,10 +30,7 @@ export const isMesh = (x: Object3D): x is Mesh => x.type === "Mesh" export const onCreated = (state: RootState) => { state.gl.localClippingEnabled = true - state.raycaster.layers.disableAll() - state.raycaster.layers.enable(CameraLayer.VISIBLE) - state.raycaster.layers.enable(CameraLayer.INVISIBLE) - state.raycaster.layers.enable(RaycasterLayer.ENABLED) + state.raycaster.layers.set(RaycasterLayer.ENABLED) } export const createMaterial = (config: Material) => { diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 38543289..58fac8dc 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -1,10 +1,15 @@ import { expose } from "comlink" import { liveQuery } from "dexie" import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" -import { pipe } from "fp-ts/lib/function" +import { flow, identity, pipe } from "fp-ts/lib/function" import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" import { Module } from "../../../server/data/modules" +import { SectionType } from "../../../server/data/sectionTypes" +import { + filterCompatibleModules, + topCandidateByHamming, +} from "../../data/modules" import layoutsDB, { ColumnLayout, HouseLayoutsKey, @@ -12,12 +17,25 @@ import layoutsDB, { PositionedModule, PositionedRow, getHouseLayoutsKey, + GridGroup, + IndexedVanillaModule, } from "../../db/layouts" import systemsDB, { LastFetchStamped } from "../../db/systems" -import { A, O } from "../../utils/functions" +import userDB from "../../db/user" +import { AugSectionType } from "../../design/ui-3d/grouped/stretchWidth/StretchWidth" +import { + A, + mapToOption, + Num, + O, + Ord, + reduceToOption, + TO, +} from "../../utils/functions" +import { sign } from "../../utils/math" import { isSSR } from "../../utils/next" import { syncModels } from "./models" -import { createVanillaModuleGetter } from "./vanilla" +import { createVanillaModuleGetter, getIndexedVanillaModule } from "./vanilla" let modulesCache: LastFetchStamped[] = [] let layoutsQueue: HouseLayoutsKey[] = [] @@ -282,82 +300,92 @@ export const splitColumns = (layout: ColumnLayout) => }) ) -const processLayout = async ({ systemId, dnas }: HouseLayoutsKey) => { - const modules = pipe( - dnas, - A.filterMap((dna) => - pipe( - modulesCache, - A.findFirst( - (systemModule: Module) => - systemModule.systemId === systemId && systemModule.dna === dna +// also posts vanilla columns +const getLayout = async ({ + systemId, + dnas, +}: HouseLayoutsKey): Promise => { + const maybeLayout = await layoutsDB.houseLayouts + .get(getHouseLayoutsKey({ systemId, dnas })) + .then((x) => x?.layout) + + if (maybeLayout) { + return maybeLayout + } else { + const modules = pipe( + dnas, + A.filterMap((dna) => + pipe( + modulesCache, + A.findFirst( + (systemModule: Module) => + systemModule.systemId === systemId && systemModule.dna === dna + ) ) ) ) - ) - - const layout = modulesToColumnLayout(modules) - - layoutsDB.houseLayouts.put({ - layout, - systemId, - dnas, - }) + const layout = modulesToColumnLayout(modules) + layoutsDB.houseLayouts.put({ + layout, + systemId, + dnas, + }) - const { - startColumn: { gridGroups }, - } = splitColumns(layout) + const { + startColumn: { gridGroups }, + } = splitColumns(layout) - const getVanillaModule = createVanillaModuleGetter(modulesCache)({ - constrainGridType: false, - positionType: "MID", - }) + const getVanillaModule = createVanillaModuleGetter(modulesCache)({ + constrainGridType: false, + positionType: "MID", + }) - pipe( - gridGroups, - A.traverse(O.Applicative)( - ({ - levelIndex, - levelType, - y, - modules: [{ module }], - }): O.Option => - pipe( - module, - getVanillaModule, - O.map((vanillaModule) => ({ - modules: [ - { - module: vanillaModule, - gridGroupIndex: 0, - z: 0, - }, - ], - length: vanillaModule.length, - y, - levelIndex, - levelType, - })) + pipe( + gridGroups, + A.traverse(O.Applicative)( + ({ + levelIndex, + levelType, + y, + modules: [{ module }], + }): O.Option => + pipe( + module, + getVanillaModule, + O.map((vanillaModule) => ({ + modules: [ + { + module: vanillaModule, + gridGroupIndex: 0, + z: 0, + }, + ], + length: vanillaModule.length, + y, + levelIndex, + levelType, + })) + ) + ), + O.map((gridGroups) => { + const levelTypes = pipe( + gridGroups, + A.map((gridGroup) => gridGroup.levelType) ) - ), - O.map((gridGroups) => { - const levelTypes = pipe( - gridGroups, - A.map((gridGroup) => gridGroup.levelType) - ) - layoutsDB.vanillaColumns.put({ - systemId, - levelTypes, - vanillaColumn: { - gridGroups, - length: gridGroups[0].length, - }, + layoutsDB.vanillaColumns.put({ + systemId, + levelTypes, + vanillaColumn: { + gridGroups, + length: gridGroups[0].length, + }, + }) }) - }) - ) + ) - return layout + return layout + } } const processLayoutsQueue = async () => { @@ -369,7 +397,7 @@ const processLayoutsQueue = async () => { while (layoutsQueue.length > 0) { const layoutsKey = layoutsQueue.shift() if (layoutsKey) { - await processLayout(layoutsKey) + await getLayout(layoutsKey) } } } @@ -449,17 +477,248 @@ const processZStretchLayout = async ({ endColumn, ]) - processLayout({ + getLayout({ systemId, dnas: nextDnas, }) } +if (!isSSR()) { + // CONT + liveQuery(async () => { + const houses = await userDB.houses.toArray() + const sectionTypes = await systemsDB.sectionTypes.toArray() + + return { houses, sectionTypes } + }).subscribe(async ({ houses, sectionTypes }) => { + // for each house, for each section type + + for (const house of houses) { + const { systemId, dnas, id: houseId } = house + + const layout = await getLayout({ systemId, dnas }) + + // CONT + // getOrPutLayout({ systemId, dnas }) + + // for (const { code } of sectionTypes) { + // if (!(dnas[0] === code)) { + // console.log(`dnas[0] === code`) + // continue + // } + // dnas -> new sectionType + // skip if code in dnas + + const otherSectionTypes = sectionTypes.filter( + (x) => + x.code !== + layout[0].gridGroups[0].modules[0].module.structuredDna.sectionType + ) + + const changeLayoutSectionType = ( + layout: ColumnLayout, + st: SectionType + ) => { + const { code: sectionType } = st + + return pipe( + layout, + A.traverse(TO.ApplicativeSeq)((positionedColumn) => + pipe( + positionedColumn.gridGroups, + A.traverse(TO.ApplicativeSeq)((gridGroup) => { + const { + modules, + modules: [ + { + module: { + structuredDna: { levelType, positionType, gridType }, + }, + }, + ], + } = gridGroup + + const vanillaModuleTask: TO.TaskOption = pipe( + TO.fromTask(() => + getIndexedVanillaModule({ + systemId, + sectionType, + positionType, + levelType, + gridType, + }) + ), + TO.chainOptionK( + flow( + O.fromNullable, + O.chain((a) => + pipe( + modulesCache, + A.findFirst( + (b) => + a.systemId === b.systemId && a.moduleDna === b.dna + ) + ) + ) + ) + ) + ) + // what must I do? + + return pipe( + vanillaModuleTask, + TO.chain((vanillaModule) => + pipe( + modules, + reduceToOption( + O.some([]), + ( + i, + acc: O.Option, + positionedModule + ) => { + // target is existent module with target section type + const target = { + structuredDna: { + ...positionedModule.module.structuredDna, + sectionType: st.code, + }, + } as Module + + const compatModules = pipe( + modulesCache, + filterCompatibleModules()(target) + ) + + if (compatModules.length === 0) return O.none + + return pipe( + compatModules, + topCandidateByHamming(target), + O.map((bestModule) => { + const distanceToTarget = + target.structuredDna.gridUnits - + bestModule.structuredDna.gridUnits + switch (true) { + case sign(distanceToTarget) > 0: + // fill in some vanilla + return [ + bestModule, + ...A.replicate( + distanceToTarget / vanillaModule.length, + vanillaModule + ), + ] + case sign(distanceToTarget) < 0: + // abort and only vanilla + return A.replicate( + positionedModule.module.length / + vanillaModule.length, + vanillaModule + ) + + case sign(distanceToTarget) === 0: + default: + return [bestModule] + // swap the module + } + }), + O.map((nextModules) => + pipe( + acc, + O.map((positionedModules) => [ + ...positionedModules, + ...nextModules.map( + (module, i) => + ({ + module, + z: positionedModule.z, + gridGroupIndex: i, + } as PositionedModule) + ), + ]) + ) + ), + O.flatten + ) + } + ), + TO.fromOption + ) + ) + ) + }) + ) + ) + ) + } + + // } + // ), + // O.map( + // (modules): GridGroup => ({ + // ...gridGroupRest, + // modules, + // }) + // ) + // ) + // } catch (e) { + // return O.none + // } + // } + // ), + // O.map((gridGroups) => ({ + // ...columnRest, + // gridGroups, + // })) + // ) + // ), + // O.map((columnLayout): AugSectionType => { + // const houseDna = columnLayoutToDnas(columnLayout) + // return { + // ...st, + // dx: (st.width - houseWidth) / 2, + // houseDna, + // houseDnaKey: houseDna.toString(), + // } + // }) + // ) + // ), + // A.sort( + // pipe( + // Num.Ord, + // Ord.contramap((st: AugSectionType) => st.width) + // ) + // ), + // (as) => + // A.isNonEmpty(as) + // ? as + // : ((): any => { + // throw new Error("empty section types") + // })() + // ) + // } + } + }) +} + +const getStretchXLayout = (houseId: string, sectionType: string) => { + // check if already there + // if then return + // else get and return +} + +const getStretchXPreviews = (houseId: string, currentSectionType: string) => { + return { foo: "bar" } +} + const api = { postLayout, postLayouts, - processLayout, + processLayout: getLayout, processZStretchLayout, + // getStretchXLayout, + getStretchXPreviews, syncModels, } From 85fff6e331e30ba10b23bb7b8a4ab590618e92f7 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 26 Jul 2023 15:57:14 +0100 Subject: [PATCH 055/132] wip --- app/design/ui-3d/fresh/FreshApp.tsx | 8 ++- app/workers/layouts/vanilla.ts | 7 ++- app/workers/layouts/worker.ts | 77 ++++++++++++++++++----------- 3 files changed, 57 insertions(+), 35 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 06bd78e7..3d629dbd 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -55,9 +55,9 @@ const FreshApp = () => { rootRef.current.add(houseGroup) liveHouses[houseId] = houseGroup - getStretchXPreviews(houseGroup).then((x) => { - console.log(x, `hi`) - }) + // getStretchXPreviews(houseGroup).then((x) => { + // console.log(x, `hi`) + // }) // stretchXHouses[houseId] = getStretchXPreviews(houseGroup) // (stretchXHouses) @@ -218,8 +218,6 @@ const FreshApp = () => { // }) // } - console.log(object) - switch (siteCtx.mode) { case SiteCtxModeEnum.Enum.SITE: if (object.parent?.parent?.parent?.parent) { diff --git a/app/workers/layouts/vanilla.ts b/app/workers/layouts/vanilla.ts index eda29b9e..55fc5078 100644 --- a/app/workers/layouts/vanilla.ts +++ b/app/workers/layouts/vanilla.ts @@ -98,14 +98,17 @@ export const getIndexedVanillaModule = ({ positionType: string levelType: string gridType: string -}) => - layoutsDB.vanillaModules.get([ +}) => { + console.log("am I ever?") + + return layoutsDB.vanillaModules.get([ systemId, sectionType, positionType, levelType, gridType, ]) +} liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe((dbLayouts) => { for (let dbLayout of dbLayouts) { diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 58fac8dc..47eda8af 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -1,7 +1,8 @@ import { expose } from "comlink" import { liveQuery } from "dexie" import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" -import { flow, identity, pipe } from "fp-ts/lib/function" +import { sequenceS } from "fp-ts/lib/Apply" +import { flow, pipe } from "fp-ts/lib/function" import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" import { Module } from "../../../server/data/modules" @@ -12,24 +13,22 @@ import { } from "../../data/modules" import layoutsDB, { ColumnLayout, + getHouseLayoutsKey, + GridGroup, HouseLayoutsKey, PositionedColumn, PositionedModule, PositionedRow, - getHouseLayoutsKey, - GridGroup, - IndexedVanillaModule, } from "../../db/layouts" import systemsDB, { LastFetchStamped } from "../../db/systems" import userDB from "../../db/user" -import { AugSectionType } from "../../design/ui-3d/grouped/stretchWidth/StretchWidth" import { A, - mapToOption, - Num, O, - Ord, + pipeLog, + pipeLogWith, reduceToOption, + T, TO, } from "../../utils/functions" import { sign } from "../../utils/math" @@ -498,23 +497,6 @@ if (!isSSR()) { const layout = await getLayout({ systemId, dnas }) - // CONT - // getOrPutLayout({ systemId, dnas }) - - // for (const { code } of sectionTypes) { - // if (!(dnas[0] === code)) { - // console.log(`dnas[0] === code`) - // continue - // } - // dnas -> new sectionType - // skip if code in dnas - - const otherSectionTypes = sectionTypes.filter( - (x) => - x.code !== - layout[0].gridGroups[0].modules[0].module.structuredDna.sectionType - ) - const changeLayoutSectionType = ( layout: ColumnLayout, st: SectionType @@ -548,9 +530,11 @@ if (!isSSR()) { gridType, }) ), + pipeLogWith(() => 1), TO.chainOptionK( flow( O.fromNullable, + pipeLogWith(() => 2), O.chain((a) => pipe( modulesCache, @@ -563,7 +547,6 @@ if (!isSSR()) { ) ) ) - // what must I do? return pipe( vanillaModuleTask, @@ -579,6 +562,7 @@ if (!isSSR()) { ) => { // target is existent module with target section type const target = { + systemId, structuredDna: { ...positionedModule.module.structuredDna, sectionType: st.code, @@ -643,16 +627,53 @@ if (!isSSR()) { ) } ), + O.map( + (modules): GridGroup => ({ + ...gridGroup, + modules, + }) + ), TO.fromOption ) ) ) - }) + }), + TO.map((gridGroups) => ({ + ...positionedColumn, + gridGroups, + })) ) ) - ) + )() } + const otherSectionTypes = sectionTypes.filter( + (x) => + x.code !== + layout[0].gridGroups[0].modules[0].module.structuredDna.sectionType + ) + + const unwrapSome = ( + taskOptionArray: T.Task[]> + ): T.Task => + pipe( + taskOptionArray, + T.map(A.compact) // compact function removes None and unwraps Some values + ) + + const otherLayouts = await pipe( + otherSectionTypes, + A.map( + (sectionType): TO.TaskOption => + () => + changeLayoutSectionType(layout, sectionType) + ), + A.sequence(T.ApplicativeSeq), // Converts Array>> to Task>> + unwrapSome // Transforms Task>> to Task>, unwrapping Some and ignoring None + )() + + console.log({ otherLayouts }) + // } // ), // O.map( From 9b9088f16856fdb3873ee14dc47319c9d380aa78 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 26 Jul 2023 23:16:16 +0100 Subject: [PATCH 056/132] wip pre sub altSectionTypeLayouts --- app/db/layouts.ts | 8 +++ app/utils/functions.ts | 13 ++++- app/workers/layouts/vanilla.ts | 7 +-- app/workers/layouts/worker.ts | 100 ++++++++++----------------------- 4 files changed, 49 insertions(+), 79 deletions(-) diff --git a/app/db/layouts.ts b/app/db/layouts.ts index c61abf76..de5f3acb 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -111,11 +111,17 @@ export const invertVanillaColumnsKey = (key: string): VanillaColumnsKey => { return { systemId, levelTypes } } +type IndexedAltSectionTypeLayouts = { + houseId: string + altSectionTypeLayouts: Record +} + class LayoutsDatabase extends Dexie { models: Dexie.Table houseLayouts: Dexie.Table vanillaModules: Dexie.Table vanillaColumns: Dexie.Table + altSectionTypeLayouts: Dexie.Table constructor() { super("LayoutsDatabase") @@ -124,11 +130,13 @@ class LayoutsDatabase extends Dexie { houseLayouts: "[systemId+dnas]", vanillaModules: "[systemId+sectionType+positionType+levelType+gridType]", vanillaColumns: "[systemId+levelTypes]", + altSectionTypeLayouts: "houseId", }) this.houseLayouts = this.table("houseLayouts") this.models = this.table("models") this.vanillaModules = this.table("vanillaModules") this.vanillaColumns = this.table("vanillaColumns") + this.altSectionTypeLayouts = this.table("altSectionTypeLayouts") } } diff --git a/app/utils/functions.ts b/app/utils/functions.ts index 05c21ff8..e90532b4 100644 --- a/app/utils/functions.ts +++ b/app/utils/functions.ts @@ -7,7 +7,7 @@ import { clamp } from "fp-ts/Ord" import * as RA from "fp-ts/ReadonlyArray" import * as R from "fp-ts/Record" import * as S from "fp-ts/string" - +import * as T from "fp-ts/Task" const clamp_ = clamp(Num.Ord) export * as M from "fp-ts/Map" @@ -19,10 +19,9 @@ export * as RR from "fp-ts/ReadonlyRecord" export * as SG from "fp-ts/Semigroup" export * as TO from "fp-ts/TaskOption" export * as TE from "fp-ts/TaskEither" -export * as T from "fp-ts/Task" export { clamp_ as clamp } -export { A, Num, O, R, RA, S } +export { A, Num, O, R, RA, S, T } export const any = (...args: boolean[]) => args.reduce((acc, v) => acc || v, false) @@ -114,3 +113,11 @@ export const clearRecord = (obj: Record) => { } } } + +export const unwrapSome = ( + taskOptionArray: T.Task[]> +): T.Task => + pipe( + taskOptionArray, + T.map(A.compact) // compact function removes None and unwraps Some values + ) diff --git a/app/workers/layouts/vanilla.ts b/app/workers/layouts/vanilla.ts index 55fc5078..eda29b9e 100644 --- a/app/workers/layouts/vanilla.ts +++ b/app/workers/layouts/vanilla.ts @@ -98,17 +98,14 @@ export const getIndexedVanillaModule = ({ positionType: string levelType: string gridType: string -}) => { - console.log("am I ever?") - - return layoutsDB.vanillaModules.get([ +}) => + layoutsDB.vanillaModules.get([ systemId, sectionType, positionType, levelType, gridType, ]) -} liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe((dbLayouts) => { for (let dbLayout of dbLayouts) { diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 47eda8af..365405da 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -27,9 +27,12 @@ import { O, pipeLog, pipeLogWith, + R, reduceToOption, + SG, T, TO, + unwrapSome, } from "../../utils/functions" import { sign } from "../../utils/math" import { isSSR } from "../../utils/next" @@ -653,93 +656,48 @@ if (!isSSR()) { layout[0].gridGroups[0].modules[0].module.structuredDna.sectionType ) - const unwrapSome = ( - taskOptionArray: T.Task[]> - ): T.Task => - pipe( - taskOptionArray, - T.map(A.compact) // compact function removes None and unwraps Some values - ) - const otherLayouts = await pipe( otherSectionTypes, A.map( - (sectionType): TO.TaskOption => + ( + sectionType + ): TO.TaskOption<{ + layout: ColumnLayout + sectionType: SectionType + }> => () => - changeLayoutSectionType(layout, sectionType) + changeLayoutSectionType(layout, sectionType).then( + O.map((layout) => ({ layout, sectionType })) + ) ), - A.sequence(T.ApplicativeSeq), // Converts Array>> to Task>> - unwrapSome // Transforms Task>> to Task>, unwrapping Some and ignoring None + A.sequence(T.ApplicativeSeq), + unwrapSome, + (x) => x )() - console.log({ otherLayouts }) - - // } - // ), - // O.map( - // (modules): GridGroup => ({ - // ...gridGroupRest, - // modules, - // }) - // ) - // ) - // } catch (e) { - // return O.none - // } - // } - // ), - // O.map((gridGroups) => ({ - // ...columnRest, - // gridGroups, - // })) - // ) - // ), - // O.map((columnLayout): AugSectionType => { - // const houseDna = columnLayoutToDnas(columnLayout) - // return { - // ...st, - // dx: (st.width - houseWidth) / 2, - // houseDna, - // houseDnaKey: houseDna.toString(), - // } - // }) - // ) - // ), - // A.sort( - // pipe( - // Num.Ord, - // Ord.contramap((st: AugSectionType) => st.width) - // ) - // ), - // (as) => - // A.isNonEmpty(as) - // ? as - // : ((): any => { - // throw new Error("empty section types") - // })() - // ) - // } + const altSectionTypeLayouts = pipe( + otherLayouts, + A.reduce( + {}, + (acc: Record, { layout, sectionType }) => { + return { + ...acc, + [sectionType.code]: layout, + } + } + ) + ) + + layoutsDB.altSectionTypeLayouts.put({ houseId, altSectionTypeLayouts }) } }) } -const getStretchXLayout = (houseId: string, sectionType: string) => { - // check if already there - // if then return - // else get and return -} - -const getStretchXPreviews = (houseId: string, currentSectionType: string) => { - return { foo: "bar" } -} - const api = { postLayout, postLayouts, processLayout: getLayout, processZStretchLayout, - // getStretchXLayout, - getStretchXPreviews, syncModels, } From f3bfe2eccd1c1f39d8613507964579b9acc70a07 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 27 Jul 2023 08:53:44 +0100 Subject: [PATCH 057/132] wip --- app/design/ui-3d/fresh/FreshApp.tsx | 24 ++++++- app/design/ui-3d/fresh/helpers.ts | 106 ++++++++++++++-------------- app/design/ui-3d/fresh/userData.ts | 22 +++--- 3 files changed, 84 insertions(+), 68 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 3d629dbd..92f68176 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,12 +1,14 @@ import { invalidate, ThreeEvent } from "@react-three/fiber" import { useGesture } from "@use-gesture/react" +import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" import { nanoid } from "nanoid" import { useEffect, useRef } from "react" import { useKey } from "react-use" import { Group, Vector3 } from "three" +import layoutsDB from "../../../db/layouts" import userDB, { House } from "../../../db/user" -import { A, O } from "../../../utils/functions" +import { A, O, R } from "../../../utils/functions" import { useSubscribe } from "../../../utils/hooks" import { floor, PI } from "../../../utils/math" import { isMesh, yAxis } from "../../../utils/three" @@ -186,6 +188,26 @@ const FreshApp = () => { } }) + liveQuery(() => layoutsDB.altSectionTypeLayouts.toArray()).subscribe( + (data) => { + for (const { houseId, altSectionTypeLayouts } of data) { + // CONT + // this needs to .clone(false) on the house group first + // then fill said house group with the layout + // --- + // maybe we want to do this so that createHouseGroup uses this also? + // and then we'd want to modify the + stretchXHouses[houseId] = pipe( + altSectionTypeLayouts, + R.map((x) => { + // yeah you need something that's gonna turn the layout into a scene graph + const phonyHouseGroup = createHouseGroup() + }) + ) + } + } + ) + const bindAll: any = useGesture<{ drag: ThreeEvent hover: ThreeEvent diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index fe518d11..b354580b 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -230,16 +230,14 @@ liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( } ) -export const createHouseGroup = async ({ +const houseLayoutToHouseGroup = async ({ systemId, houseId, - dnas, - friendlyName, + houseLayout, }: { systemId: string houseId: string - dnas: string[] - friendlyName: string + houseLayout: ColumnLayout }) => { const clippingPlanes: Plane[] = [ new Plane(new Vector3(1, 0, 0), 0), @@ -247,61 +245,63 @@ export const createHouseGroup = async ({ new Plane(new Vector3(0, 0, 1), 0), ] - const houseLayoutToHouseGroup = async ({ + const columnGroups = houseLayoutToColumns({ systemId, houseId, houseLayout, - }: { - systemId: string - houseId: string - houseLayout: ColumnLayout - }) => { - const columnGroups = houseLayoutToColumns({ - systemId, - houseId, - houseLayout, - clippingPlanes, - }) - const topLevelHouseGroup = new Group() - const zCenterHouseGroup = new Group() - - const sectionType = - houseLayout[0].gridGroups[0].modules[0].module.structuredDna.sectionType + clippingPlanes, + }) + const topLevelHouseGroup = new Group() + const zCenterHouseGroup = new Group() - const width = houseLayout[0].gridGroups[0].modules[0].module.width - const height = houseLayout[0].gridGroups.reduce( - (acc, v) => acc + v.modules[0].module.height, - 0 - ) - const length = columnGroups.reduce( - (acc, columnGroup) => acc + columnGroup.userData.length, - 0 - ) - const obb = new OBB() - const houseGroupUserData: HouseRootGroupUserData = { - type: UserDataTypeEnum.Enum.HouseRootGroup, - systemId, - houseId, - width, - length, - height, - dnas, - friendlyName, - modifiedMaterials: {}, - obb, - clippingPlanes, - sectionType, - levelTypes: houseLayoutToLevelTypes(houseLayout), - columnCount: columnGroups.length, - } - topLevelHouseGroup.userData = houseGroupUserData - zCenterHouseGroup.add(...columnGroups) - zCenterHouseGroup.position.setZ(-length / 2) - topLevelHouseGroup.add(zCenterHouseGroup) + const sectionType = + houseLayout[0].gridGroups[0].modules[0].module.structuredDna.sectionType - return topLevelHouseGroup + const width = houseLayout[0].gridGroups[0].modules[0].module.width + const height = houseLayout[0].gridGroups.reduce( + (acc, v) => acc + v.modules[0].module.height, + 0 + ) + const length = columnGroups.reduce( + (acc, columnGroup) => acc + columnGroup.userData.length, + 0 + ) + const obb = new OBB() + const houseGroupUserData: Partial = { + type: UserDataTypeEnum.Enum.HouseRootGroup, + systemId, + houseId, + width, + length, + height, + // dnas, + // friendlyName, + modifiedMaterials: {}, + obb, + clippingPlanes, + sectionType, + levelTypes: houseLayoutToLevelTypes(houseLayout), + columnCount: columnGroups.length, } + topLevelHouseGroup.userData = houseGroupUserData + zCenterHouseGroup.add(...columnGroups) + zCenterHouseGroup.position.setZ(-length / 2) + topLevelHouseGroup.add(zCenterHouseGroup) + + return topLevelHouseGroup +} +export const createHouseGroup = async ({ + systemId, + houseId, + dnas, +}: // friendlyName, +{ + systemId: string + houseId: string + dnas: string[] + // friendlyName: string +}) => { const houseGroup = pipe( houseLayouts, R.lookup(getHouseLayoutsKey({ systemId, dnas })), diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 723dba5e..845634aa 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -1,20 +1,13 @@ -import { Group, Plane } from "three" +import { Plane } from "three" import { OBB } from "three-stdlib" import { z } from "zod" -// maybe want some updater functions to update all the user data in the tree - -// or at certain levels/scopes? - -// houseGroup has -// -> zCenterGroup as singleton child has -// -> columnGroups as children has -// -> gridGroups as children has -// -> moduleGroups as children has -// -> elementMeshes as children - -// needs HouseTransformGroup -// HouseColumnsGroup +// HouseRootGroup has +// -> HouseColumnsContainerGroup as singleton child has +// -> ColumnsGroup's as children has +// -> GridGroup's as children has +// -> ModuleGroup's as children has +// -> ElementMesh's as children export const UserDataTypeEnum = z.enum([ "HouseRootGroup", @@ -78,4 +71,5 @@ export type UserData = | ModuleGroupUserData | GridGroupUserData | ColumnGroupUserData + | HouseColumnsContainerUserData | HouseRootGroupUserData From a23fb61539885cd0e35697d265ce5ecc22a91092 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 27 Jul 2023 10:57:22 +0100 Subject: [PATCH 058/132] wip yooo first pass --- app/db/layouts.ts | 6 +- app/design/ui-3d/fresh/FreshApp.tsx | 104 +++++++++++++++++----- app/design/ui-3d/fresh/dimensions.ts | 4 +- app/design/ui-3d/fresh/helpers.ts | 30 ++++--- app/design/ui-3d/init/RectangularGrid.tsx | 1 + app/utils/three.ts | 6 ++ app/workers/layouts/worker.ts | 26 ++---- 7 files changed, 127 insertions(+), 50 deletions(-) diff --git a/app/db/layouts.ts b/app/db/layouts.ts index de5f3acb..f9dc5d43 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -1,5 +1,6 @@ import Dexie from "dexie" import { Module } from "../../server/data/modules" +import { SectionType } from "../../server/data/sectionTypes" import { LastFetchStamped } from "./systems" export type PositionedModule = { @@ -113,7 +114,10 @@ export const invertVanillaColumnsKey = (key: string): VanillaColumnsKey => { type IndexedAltSectionTypeLayouts = { houseId: string - altSectionTypeLayouts: Record + altSectionTypeLayouts: Record< + string, + { layout: ColumnLayout; sectionType: SectionType } + > } class LayoutsDatabase extends Dexie { diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 92f68176..d2b14e00 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,17 +1,19 @@ -import { invalidate, ThreeEvent } from "@react-three/fiber" +import { invalidate, ThreeEvent, useThree } from "@react-three/fiber" import { useGesture } from "@use-gesture/react" import { liveQuery } from "dexie" -import { pipe } from "fp-ts/lib/function" +import { identity, pipe } from "fp-ts/lib/function" +import { Task } from "fp-ts/lib/Task" import { nanoid } from "nanoid" import { useEffect, useRef } from "react" -import { useKey } from "react-use" -import { Group, Vector3 } from "three" +import { useInterval, useKey } from "react-use" +import { Group, Object3D, Vector3 } from "three" import layoutsDB from "../../../db/layouts" import userDB, { House } from "../../../db/user" -import { A, O, R } from "../../../utils/functions" +import { A, O, R, T } from "../../../utils/functions" import { useSubscribe } from "../../../utils/hooks" import { floor, PI } from "../../../utils/math" -import { isMesh, yAxis } from "../../../utils/three" +import { isMesh, setLayer, yAxis } from "../../../utils/three" +import { CameraLayer } from "../../state/constants" import { openMenu } from "../../state/menu" import scope, { ScopeItem } from "../../state/scope" import settings from "../../state/settings" @@ -26,11 +28,12 @@ import { import { dispatchOutline } from "./events/outlines" import { createHouseGroup, + houseLayoutToHouseGroup, insertVanillaColumn, subtractPenultimateColumn, } from "./helpers" import getStretchXPreviews from "./stretchXPreviews" -import { UserData, UserDataTypeEnum } from "./userData" +import { HouseRootGroupUserData, UserData, UserDataTypeEnum } from "./userData" const liveHouses: Record = {} const stretchXHouses: Record> = {} @@ -141,8 +144,37 @@ const FreshApp = () => { } }) - // stretch width + - useKey("x", () => {}) + // toggle stretch width first pass + useKey("x", () => { + for (let houseId of Object.keys(liveHouses)) { + const liveHouseGroup: Group = liveHouses[houseId] + const stretchXGroups: Record = stretchXHouses[houseId] + + pipe( + R.keys(stretchXGroups), + A.head, + O.map((k) => + pipe( + stretchXGroups, + R.lookup(k), + O.map((firstGroup) => { + setLayer(firstGroup, CameraLayer.VISIBLE) + setLayer(liveHouseGroup, CameraLayer.INVISIBLE) + + stretchXGroups[ + (liveHouseGroup.userData as HouseRootGroupUserData).sectionType + ] = liveHouseGroup + liveHouses[houseId] = firstGroup + + delete stretchXGroups[k] + + invalidate() + }) + ) + ) + ) + } + }) // stretch width - useKey("X", () => {}) @@ -191,17 +223,49 @@ const FreshApp = () => { liveQuery(() => layoutsDB.altSectionTypeLayouts.toArray()).subscribe( (data) => { for (const { houseId, altSectionTypeLayouts } of data) { - // CONT - // this needs to .clone(false) on the house group first - // then fill said house group with the layout - // --- - // maybe we want to do this so that createHouseGroup uses this also? - // and then we'd want to modify the - stretchXHouses[houseId] = pipe( - altSectionTypeLayouts, - R.map((x) => { - // yeah you need something that's gonna turn the layout into a scene graph - const phonyHouseGroup = createHouseGroup() + pipe( + liveHouses, + R.lookup(houseId), + O.map((houseGroup) => { + const layoutTasks: Record> = pipe( + altSectionTypeLayouts, + R.map(({ layout: houseLayout }) => { + const { systemId, friendlyName } = + houseGroup.userData as HouseRootGroupUserData + // const houseLayout = altSectionTypeLayouts[] + return () => + houseLayoutToHouseGroup({ + systemId, + friendlyName, + houseId, + houseLayout, + }) + }) + ) + + const layoutsTask = pipe( + layoutTasks, + R.traverse(T.ApplicativeSeq)(identity) + ) + + layoutsTask().then((groups) => { + pipe( + stretchXHouses[houseId], + R.map((group) => rootRef.current?.remove(group)) + ) + pipe( + groups, + R.map((group) => { + // const layerUp = (object: Object3D, layers: ) + + setLayer(group, CameraLayer.INVISIBLE) + + rootRef.current?.add(group) + }) + ) + stretchXHouses[houseId] = groups + console.log({ rootRef: rootRef.current }) + }) }) ) } diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 292271f8..7f140a5b 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -55,7 +55,7 @@ export const updateHouseWidth = (houseGroup: Group) => {} export const updateHouseHeight = (houseGroup: Group) => {} -export const updateClippingPlanes = flow((houseGroup: Group) => { +export const updateClippingPlanes = (houseGroup: Group) => { const { clippingPlanes, clippingPlanes: [planeX, planeY, planeZ], @@ -69,7 +69,7 @@ export const updateClippingPlanes = flow((houseGroup: Group) => { planeY.applyMatrix4(houseGroup.matrix) planeZ.set(new Vector3(0, 0, 1), 0) planeZ.applyMatrix4(houseGroup.matrix) -}) +} export const updateHouseLength = (houseGroup: Group) => { pipe( diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index b354580b..f7af787b 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -22,6 +22,7 @@ import layoutsDB, { } from "../../../db/layouts" import { A, Num, O, Ord, R, S } from "../../../utils/functions" import { getLayoutsWorker } from "../../../workers" +import { CameraLayer, RaycasterLayer } from "../../state/constants" import { getMaterial } from "./systems" import { ColumnGroupUserData, @@ -105,7 +106,7 @@ export const moduleToGroup = ({ ifcTag, houseId: "", }) as MeshStandardMaterial - material.clippingPlanes = clippingPlanes + // material.clippingPlanes = clippingPlanes const mesh = new Mesh(geometry, material) mesh.castShadow = true @@ -230,19 +231,22 @@ liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( } ) -const houseLayoutToHouseGroup = async ({ +export const houseLayoutToHouseGroup = async ({ systemId, houseId, houseLayout, + friendlyName, }: { systemId: string houseId: string houseLayout: ColumnLayout + friendlyName: string }) => { + const BIG_NUMBER = 99 const clippingPlanes: Plane[] = [ - new Plane(new Vector3(1, 0, 0), 0), - new Plane(new Vector3(0, 1, 0), 0), - new Plane(new Vector3(0, 0, 1), 0), + new Plane(new Vector3(BIG_NUMBER, 0, 0), 0), + new Plane(new Vector3(0, BIG_NUMBER, 0), 0), + new Plane(new Vector3(0, 0, BIG_NUMBER), 0), ] const columnGroups = houseLayoutToColumns({ @@ -295,14 +299,14 @@ export const createHouseGroup = async ({ systemId, houseId, dnas, -}: // friendlyName, -{ + friendlyName, +}: { systemId: string houseId: string dnas: string[] - // friendlyName: string + friendlyName: string }) => { - const houseGroup = pipe( + const houseGroup = await pipe( houseLayouts, R.lookup(getHouseLayoutsKey({ systemId, dnas })), O.match( @@ -317,10 +321,16 @@ export const createHouseGroup = async ({ systemId, houseId, houseLayout, + friendlyName, }) }, (houseLayout) => - houseLayoutToHouseGroup({ systemId, houseId, houseLayout }) + houseLayoutToHouseGroup({ + systemId, + houseId, + houseLayout, + friendlyName, + }) ) ) diff --git a/app/design/ui-3d/init/RectangularGrid.tsx b/app/design/ui-3d/init/RectangularGrid.tsx index 7f12d7a6..c6ac54a3 100644 --- a/app/design/ui-3d/init/RectangularGrid.tsx +++ b/app/design/ui-3d/init/RectangularGrid.tsx @@ -7,6 +7,7 @@ import { LineDashedMaterial, LineSegments, } from "three" +import { CameraLayer } from "../../state/constants" interface Axis { cells: number diff --git a/app/utils/three.ts b/app/utils/three.ts index 3c964436..2142e4af 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -185,3 +185,9 @@ export const useRotations = () => { return { rotateV2, unrotateV2 } } + +export const setLayer = (object: Object3D, layer: number) => { + object.traverse((node) => { + node.layers.set(layer) + }) +} diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 365405da..fa4069e0 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -1,7 +1,6 @@ import { expose } from "comlink" import { liveQuery } from "dexie" import { transpose as transposeRA } from "fp-ts-std/ReadonlyArray" -import { sequenceS } from "fp-ts/lib/Apply" import { flow, pipe } from "fp-ts/lib/function" import * as RA from "fp-ts/ReadonlyArray" import produce from "immer" @@ -22,18 +21,7 @@ import layoutsDB, { } from "../../db/layouts" import systemsDB, { LastFetchStamped } from "../../db/systems" import userDB from "../../db/user" -import { - A, - O, - pipeLog, - pipeLogWith, - R, - reduceToOption, - SG, - T, - TO, - unwrapSome, -} from "../../utils/functions" +import { A, O, reduceToOption, T, TO, unwrapSome } from "../../utils/functions" import { sign } from "../../utils/math" import { isSSR } from "../../utils/next" import { syncModels } from "./models" @@ -533,11 +521,9 @@ if (!isSSR()) { gridType, }) ), - pipeLogWith(() => 1), TO.chainOptionK( flow( O.fromNullable, - pipeLogWith(() => 2), O.chain((a) => pipe( modulesCache, @@ -679,10 +665,16 @@ if (!isSSR()) { otherLayouts, A.reduce( {}, - (acc: Record, { layout, sectionType }) => { + ( + acc: Record< + string, + { layout: ColumnLayout; sectionType: SectionType } + >, + { layout, sectionType } + ) => { return { ...acc, - [sectionType.code]: layout, + [sectionType.code]: { layout, sectionType }, } } ) From a016d19830149b2316cffa1bc1d778eea3a6eec5 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 27 Jul 2023 13:02:00 +0100 Subject: [PATCH 059/132] wip going nicely --- app/analyse/ui/FloorAreaChart.tsx | 2 +- app/build/materials/useMaterialsListRows.ts | 2 +- app/build/order/useOrderListData.ts | 2 +- app/db/user.ts | 4 +- app/design/state/houses.ts | 12 ++-- app/design/state/settings.ts | 2 +- app/design/ui-3d/fresh/FreshApp.tsx | 71 ++++++++++++--------- app/design/ui-3d/fresh/dimensions.ts | 27 ++++++++ app/design/ui-3d/fresh/helpers.ts | 9 +-- app/design/ui-3d/fresh/stretchXPreviews.ts | 36 ----------- app/design/ui-3d/fresh/userData.ts | 1 + app/design/ui-3d/grouped/GroupedHouse2.tsx | 2 +- app/design/ui/menu/ContextMenuEntry.tsx | 2 +- app/utils/three.ts | 24 ++++++- app/workers/layouts/vanilla.ts | 33 ---------- app/workers/layouts/worker.ts | 2 +- 16 files changed, 108 insertions(+), 123 deletions(-) delete mode 100644 app/design/ui-3d/fresh/stretchXPreviews.ts diff --git a/app/analyse/ui/FloorAreaChart.tsx b/app/analyse/ui/FloorAreaChart.tsx index 8ae4d2e7..3321ad89 100644 --- a/app/analyse/ui/FloorAreaChart.tsx +++ b/app/analyse/ui/FloorAreaChart.tsx @@ -51,7 +51,7 @@ const FloorAreaChart = () => { pipe( selectedHouses, A.filterMap((selectedHouse) => - selectedHouse.id === houseId + selectedHouse.houseId === houseId ? O.some({ houseId, floorArea, diff --git a/app/build/materials/useMaterialsListRows.ts b/app/build/materials/useMaterialsListRows.ts index 205a4441..8e9bd201 100644 --- a/app/build/materials/useMaterialsListRows.ts +++ b/app/build/materials/useMaterialsListRows.ts @@ -136,7 +136,7 @@ export const useMaterialsListRows = () => { embodiedCarbonPerUnit, linkUrl, unit, - } = getElementMaterial(house.id, item) + } = getElementMaterial(house.houseId, item) const quantity = pipe(houseModules, A.reduce(0, reducer)) diff --git a/app/build/order/useOrderListData.ts b/app/build/order/useOrderListData.ts index ec1fe396..6884f7d4 100644 --- a/app/build/order/useOrderListData.ts +++ b/app/build/order/useOrderListData.ts @@ -69,7 +69,7 @@ export const useOrderListData = () => { return pipe( selectedHouses, - A.chain(({ id: houseId, dnas: dnas, ...house }) => + A.chain(({ houseId: houseId, dnas: dnas, ...house }) => pipe( dnas, A.map((dna) => ({ diff --git a/app/db/user.ts b/app/db/user.ts index 20511366..93ae757a 100644 --- a/app/db/user.ts +++ b/app/db/user.ts @@ -2,7 +2,7 @@ import Dexie from "dexie" import { z } from "zod" export const houseParser = z.object({ - id: z.string().min(1), + houseId: z.string().min(1), houseTypeId: z.string().min(1), systemId: z.string().min(1), dnas: z.array(z.string().min(1)), @@ -24,7 +24,7 @@ class UserDatabase extends Dexie { constructor() { super("UserDatabase") this.version(1).stores({ - houses: "id,&friendlyName", + houses: "houseId,&friendlyName", }) this.houses = this.table("houses") } diff --git a/app/design/state/houses.ts b/app/design/state/houses.ts index 40c8c11d..8425de41 100644 --- a/app/design/state/houses.ts +++ b/app/design/state/houses.ts @@ -28,17 +28,17 @@ const initHouses = async () => { } housesArray.forEach((house) => { - houses[house.id] = ref(house) + houses[house.houseId] = ref(house) }) } initHouses().then(() => { subscribe(houses, () => { Object.values(houses).forEach(async (house) => { - const existingHouse = await userDB.houses.get(house.id) + const existingHouse = await userDB.houses.get(house.houseId) if (existingHouse) { - userDB.houses.update(house.id, Dexie.deepClone(house)) + userDB.houses.update(house.houseId, Dexie.deepClone(house)) } else { userDB.houses.add(Dexie.deepClone(house)) } @@ -188,9 +188,9 @@ export const useInsert1000Skylarks = () => { for (let x = startX; x < incX * count; x += incX) { for (let z = startZ; z < incZ * count; z += incZ) { - const id = nanoid() - houses[id] = ref({ - id, + const houseId = nanoid() + houses[houseId] = ref({ + houseId, houseTypeId, systemId: houseType.systemId, position: new Vector3(x, 0, z), diff --git a/app/design/state/settings.ts b/app/design/state/settings.ts index d359ed65..69212882 100644 --- a/app/design/state/settings.ts +++ b/app/design/state/settings.ts @@ -14,7 +14,7 @@ type AppSettings = { const settings = proxy({ mapEnabled: false, sidebar: false, - groundPlaneEnabled: false, + groundPlaneEnabled: true, verticalCuts: { width: false, length: false, diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index d2b14e00..dfe1546c 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,18 +1,22 @@ -import { invalidate, ThreeEvent, useThree } from "@react-three/fiber" +import { invalidate, ThreeEvent } from "@react-three/fiber" import { useGesture } from "@use-gesture/react" import { liveQuery } from "dexie" import { identity, pipe } from "fp-ts/lib/function" -import { Task } from "fp-ts/lib/Task" import { nanoid } from "nanoid" import { useEffect, useRef } from "react" -import { useInterval, useKey } from "react-use" -import { Group, Object3D, Vector3 } from "three" +import { useKey } from "react-use" +import { Group, Vector3 } from "three" import layoutsDB from "../../../db/layouts" import userDB, { House } from "../../../db/user" import { A, O, R, T } from "../../../utils/functions" import { useSubscribe } from "../../../utils/hooks" import { floor, PI } from "../../../utils/math" -import { isMesh, setLayer, yAxis } from "../../../utils/three" +import { + isMesh, + setInvisible, + setVisibleAndRaycast, + yAxis, +} from "../../../utils/three" import { CameraLayer } from "../../state/constants" import { openMenu } from "../../state/menu" import scope, { ScopeItem } from "../../state/scope" @@ -32,7 +36,6 @@ import { insertVanillaColumn, subtractPenultimateColumn, } from "./helpers" -import getStretchXPreviews from "./stretchXPreviews" import { HouseRootGroupUserData, UserData, UserDataTypeEnum } from "./userData" const liveHouses: Record = {} @@ -48,7 +51,14 @@ const FreshApp = () => { const addHouse = async (house: House) => { if (!rootRef.current) return - const { id: houseId, systemId, dnas, friendlyName } = house + const { + houseId: houseId, + systemId, + dnas, + friendlyName, + position, + rotation, + } = house const houseGroup = await createHouseGroup({ systemId, @@ -57,15 +67,13 @@ const FreshApp = () => { friendlyName, }) - rootRef.current.add(houseGroup) - liveHouses[houseId] = houseGroup + houseGroup.position.set(position.x, position.y, position.z) + houseGroup.rotation.set(0, rotation, 0) - // getStretchXPreviews(houseGroup).then((x) => { - // console.log(x, `hi`) - // }) + setVisibleAndRaycast(houseGroup) - // stretchXHouses[houseId] = getStretchXPreviews(houseGroup) - // (stretchXHouses) + rootRef.current.add(houseGroup) + liveHouses[houseId] = houseGroup invalidate() @@ -101,7 +109,7 @@ const FreshApp = () => { const friendlyName = getFriendlyName() dispatchAddHouse({ - id, + houseId: id, systemId, houseTypeId, dnas, @@ -125,10 +133,10 @@ const FreshApp = () => { invalidate() }) - const getHouseGroups = () => - (rootRef.current?.children ?? []).filter( - (x) => x.userData.type === UserDataTypeEnum.Enum.HouseRootGroup - ) as Group[] + const getHouseGroups = () => Object.values(liveHouses) + // (rootRef.current?.children ?? []).filter( + // (x) => x.userData.type === UserDataTypeEnum.Enum.HouseRootGroup + // ) as Group[] useKey("z", () => { for (let houseGroup of getHouseGroups()) { @@ -158,8 +166,8 @@ const FreshApp = () => { stretchXGroups, R.lookup(k), O.map((firstGroup) => { - setLayer(firstGroup, CameraLayer.VISIBLE) - setLayer(liveHouseGroup, CameraLayer.INVISIBLE) + setVisibleAndRaycast(firstGroup) + setInvisible(liveHouseGroup) stretchXGroups[ (liveHouseGroup.userData as HouseRootGroupUserData).sectionType @@ -226,17 +234,16 @@ const FreshApp = () => { pipe( liveHouses, R.lookup(houseId), - O.map((houseGroup) => { + O.map((liveHouseGroup) => { const layoutTasks: Record> = pipe( altSectionTypeLayouts, R.map(({ layout: houseLayout }) => { const { systemId, friendlyName } = - houseGroup.userData as HouseRootGroupUserData + liveHouseGroup.userData as HouseRootGroupUserData // const houseLayout = altSectionTypeLayouts[] return () => houseLayoutToHouseGroup({ systemId, - friendlyName, houseId, houseLayout, }) @@ -255,16 +262,21 @@ const FreshApp = () => { ) pipe( groups, - R.map((group) => { + R.map((altHouseGroup) => { // const layerUp = (object: Object3D, layers: ) - setLayer(group, CameraLayer.INVISIBLE) + setInvisible(altHouseGroup) + + liveHouseGroup.matrix.decompose( + altHouseGroup.position, + altHouseGroup.quaternion, + altHouseGroup.scale + ) - rootRef.current?.add(group) + rootRef.current?.add(altHouseGroup) }) ) stretchXHouses[houseId] = groups - console.log({ rootRef: rootRef.current }) }) }) ) @@ -391,12 +403,9 @@ const FreshApp = () => { switch (userData.type) { case UserDataTypeEnum.Enum.ElementMesh: - // console.log(userData) break case UserDataTypeEnum.Enum.HouseRootGroup: // TypeScript knows that userData is of type HouseModuleGroupUserData in this block - // console.log(userData.length) // This is valid - // console.log(userData.houseId) // TypeScript error, houseId doesn't exist on HouseModuleGroupUserData break } }) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 7f140a5b..1ee9aa73 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -12,6 +12,7 @@ import { Vector3, } from "three" import { OBB } from "three-stdlib" +import userDB from "../../../db/user" import { A, O } from "../../../utils/functions" import { isMesh, xAxis, yAxis, zAxis } from "../../../utils/three" import { HouseRootGroupUserData, UserDataTypeEnum } from "./userData" @@ -88,9 +89,35 @@ export const updateHouseLength = (houseGroup: Group) => { ) } +const updateDnas = (houseGroup: Group) => { + // infer the dnas from the layout + houseGroup.traverse((node) => { + console.log(node) + }) +} + export const updateEverything = (houseGroup: Group) => { updateHouseLength(houseGroup) updateHouseOBB(houseGroup) updateClippingPlanes(houseGroup) + updateDnas(houseGroup) + + const { dnas, houseId, friendlyName, houseTypeId } = + houseGroup.userData as HouseRootGroupUserData + + console.log(houseGroup.userData.dnas) + + const rotation = houseGroup.rotation.y + const position = houseGroup.position + + console.log({ dnas }) + + userDB.houses.update(houseId, { + dnas, + friendlyName, + position, + rotation, + }) + invalidate() } diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index f7af787b..3590ee50 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -235,12 +235,10 @@ export const houseLayoutToHouseGroup = async ({ systemId, houseId, houseLayout, - friendlyName, }: { systemId: string houseId: string houseLayout: ColumnLayout - friendlyName: string }) => { const BIG_NUMBER = 99 const clippingPlanes: Plane[] = [ @@ -278,8 +276,6 @@ export const houseLayoutToHouseGroup = async ({ width, length, height, - // dnas, - // friendlyName, modifiedMaterials: {}, obb, clippingPlanes, @@ -321,7 +317,6 @@ export const createHouseGroup = async ({ systemId, houseId, houseLayout, - friendlyName, }) }, (houseLayout) => @@ -329,11 +324,13 @@ export const createHouseGroup = async ({ systemId, houseId, houseLayout, - friendlyName, }) ) ) + houseGroup.userData.dnas = dnas + houseGroup.userData.friendlyName = friendlyName + return houseGroup } diff --git a/app/design/ui-3d/fresh/stretchXPreviews.ts b/app/design/ui-3d/fresh/stretchXPreviews.ts deleted file mode 100644 index 5b7977d6..00000000 --- a/app/design/ui-3d/fresh/stretchXPreviews.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Group } from "three" -import { getLayoutsWorker } from "../../../workers" -import { HouseRootGroupUserData } from "./userData" - -const getStretchXPreviews = async ( - houseGroup: Group -): Promise> => { - const layoutsWorker = getLayoutsWorker() - - if (layoutsWorker === null) - throw new Error(`layoutsWorker null in stretchXPreviews`) - - const previews: Record = {} - - const { houseId } = houseGroup.userData as HouseRootGroupUserData - - // const foo = await layoutsWorker.getStretchXPreviews(houseId) - // console.log(foo) - - // get the dnas? - // check old func for dnas -> new dnas - - // iter section types first - - // doesn't this go in a worker though? - - // check db - - // yeah we just need to post for layouts - - // we could post houseId, sectionType - - return previews -} - -export default getStretchXPreviews diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 845634aa..e2c50da9 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -23,6 +23,7 @@ export type HouseRootGroupUserData = { type: typeof UserDataTypeEnum.Enum.HouseRootGroup systemId: string houseId: string + houseTypeId: string dnas: string[] friendlyName: string modifiedMaterials: Record diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index c52e8bdc..014f451e 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -32,7 +32,7 @@ type Props = { const GroupedHouse2 = (props: Props) => { console.log("GroupedHouse2") const { house } = props - const { systemId, id: houseId, position, rotation } = house + const { systemId, houseId: houseId, position, rotation } = house const dnas = [...house.dnas] const layoutKey: HouseLayoutsKey = { systemId, dnas } diff --git a/app/design/ui/menu/ContextMenuEntry.tsx b/app/design/ui/menu/ContextMenuEntry.tsx index f81c801b..fb810bf9 100644 --- a/app/design/ui/menu/ContextMenuEntry.tsx +++ b/app/design/ui/menu/ContextMenuEntry.tsx @@ -77,7 +77,7 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { const deleteBuilding = () => { dispatchDeleteHouse({ - id: house.id, + id: house.houseId, }) // delete houses[houseId] scope.selected = null diff --git a/app/utils/three.ts b/app/utils/three.ts index 2142e4af..d59624fb 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -186,8 +186,28 @@ export const useRotations = () => { return { rotateV2, unrotateV2 } } -export const setLayer = (object: Object3D, layer: number) => { +export const setVisibleAndRaycast = (object: Object3D) => { object.traverse((node) => { - node.layers.set(layer) + node.layers.set(CameraLayer.VISIBLE) + node.layers.enable(RaycasterLayer.ENABLED) + }) +} + +export const setVisibleOnly = (object: Object3D) => { + object.traverse((node) => { + node.layers.set(CameraLayer.VISIBLE) + }) +} + +export const setInvisible = (object: Object3D) => { + object.traverse((node) => { + node.layers.set(CameraLayer.INVISIBLE) + }) +} + +export const setInvisibleAndRaycast = (object: Object3D) => { + object.traverse((node) => { + node.layers.set(CameraLayer.INVISIBLE) + node.layers.enable(RaycasterLayer.ENABLED) }) } diff --git a/app/workers/layouts/vanilla.ts b/app/workers/layouts/vanilla.ts index eda29b9e..d7c1e4cd 100644 --- a/app/workers/layouts/vanilla.ts +++ b/app/workers/layouts/vanilla.ts @@ -106,36 +106,3 @@ export const getIndexedVanillaModule = ({ levelType, gridType, ]) - -liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe((dbLayouts) => { - for (let dbLayout of dbLayouts) { - // const { layoutsKey } = dbLayout - } -}) - -// this actually needs to SUBSCRIBE TO HOUSES -// get their `dnas` -// get their column layout (maybe we're subscribing to layouts actually) -// layoutKey -> vanillaColumn we post -// -// --------------------------------------------------------------------- -// -// liveQuery(() => layoutsDB.vanillaModules.toArray()).subscribe( -// async (dbVanillaMods) => { -// if (dbVanillaMods.length > 0) { -// const { -// systemId, -// sectionType, -// positionType, -// levelType, -// gridType, -// moduleDna, -// } = dbVanillaMods[0] - -// console.log( -// [systemId, sectionType, positionType, levelType, gridType].toString() -// ) -// } - -// } -// ) diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index fa4069e0..f02a76dc 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -484,7 +484,7 @@ if (!isSSR()) { // for each house, for each section type for (const house of houses) { - const { systemId, dnas, id: houseId } = house + const { systemId, dnas, houseId: houseId } = house const layout = await getLayout({ systemId, dnas }) From fed64283a1ec572ccdf6e211e135befeb91907dc Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 27 Jul 2023 15:16:56 +0100 Subject: [PATCH 060/132] wip bingo w/ keyboard at least --- app/design/ui-3d/fresh/FreshApp.tsx | 2 +- app/design/ui-3d/fresh/dimensions.ts | 52 ++++++++++++++++++++-------- app/design/ui-3d/fresh/helpers.ts | 19 ++++++---- app/design/ui-3d/fresh/userData.ts | 2 ++ 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index dfe1546c..566c96d0 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -17,7 +17,6 @@ import { setVisibleAndRaycast, yAxis, } from "../../../utils/three" -import { CameraLayer } from "../../state/constants" import { openMenu } from "../../state/menu" import scope, { ScopeItem } from "../../state/scope" import settings from "../../state/settings" @@ -177,6 +176,7 @@ const FreshApp = () => { delete stretchXGroups[k] invalidate() + updateEverything(firstGroup) }) ) ) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 1ee9aa73..1683792e 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -1,9 +1,8 @@ import { invalidate } from "@react-three/fiber" -import { flow, pipe } from "fp-ts/lib/function" +import { pipe } from "fp-ts/lib/function" import { BoxGeometry, Group, - Material, Matrix3, Matrix4, Mesh, @@ -14,8 +13,12 @@ import { import { OBB } from "three-stdlib" import userDB from "../../../db/user" import { A, O } from "../../../utils/functions" -import { isMesh, xAxis, yAxis, zAxis } from "../../../utils/three" -import { HouseRootGroupUserData, UserDataTypeEnum } from "./userData" +import { columnSorter } from "./helpers" +import { + HouseRootGroupUserData, + ModuleGroupUserData, + UserDataTypeEnum, +} from "./userData" export const DEBUG = false @@ -90,10 +93,35 @@ export const updateHouseLength = (houseGroup: Group) => { } const updateDnas = (houseGroup: Group) => { - // infer the dnas from the layout - houseGroup.traverse((node) => { - console.log(node) - }) + let result: string[][] = [] + pipe( + houseGroup.children, + A.lookup(0), + O.map((columnsContainerGroup) => + pipe( + columnsContainerGroup.children, + columnSorter, + A.map((v) => { + v.traverse((node) => { + if (node.userData.type === UserDataTypeEnum.Enum.ModuleGroup) { + const { dna } = node.userData as ModuleGroupUserData + if ( + node.parent?.userData.type !== UserDataTypeEnum.Enum.GridGroup + ) + throw new Error("non-GridGroup parent of ModuleGroup") + + const levelIndex = node.parent!.userData.levelIndex + if (!result[levelIndex]) { + result[levelIndex] = [] + } + result[levelIndex].push(dna) + } + }) + }) + ) + ) + ) + houseGroup.userData.dnas = result.flat() } export const updateEverything = (houseGroup: Group) => { @@ -102,19 +130,13 @@ export const updateEverything = (houseGroup: Group) => { updateClippingPlanes(houseGroup) updateDnas(houseGroup) - const { dnas, houseId, friendlyName, houseTypeId } = - houseGroup.userData as HouseRootGroupUserData - - console.log(houseGroup.userData.dnas) + const { dnas, houseId } = houseGroup.userData as HouseRootGroupUserData const rotation = houseGroup.rotation.y const position = houseGroup.position - console.log({ dnas }) - userDB.houses.update(houseId, { dnas, - friendlyName, position, rotation, }) diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 3590ee50..f03b3b2e 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -282,10 +282,15 @@ export const houseLayoutToHouseGroup = async ({ sectionType, levelTypes: houseLayoutToLevelTypes(houseLayout), columnCount: columnGroups.length, + houseLayout, } topLevelHouseGroup.userData = houseGroupUserData + zCenterHouseGroup.add(...columnGroups) zCenterHouseGroup.position.setZ(-length / 2) + zCenterHouseGroup.userData.type = + UserDataTypeEnum.Enum.HouseColumnsContainerGroup + topLevelHouseGroup.add(zCenterHouseGroup) return topLevelHouseGroup @@ -359,6 +364,13 @@ export const removeColumnFromHouse = ( O.map((zCenterHouseGroup) => void zCenterHouseGroup.remove(columnGroup)) ) +export const columnSorter = A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) +) + export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { const { levelTypes, systemId, columnCount, clippingPlanes } = houseGroup.userData as HouseRootGroupUserData @@ -403,12 +415,7 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { } else if (direction === -1) { pipe( columnGroups, - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), + columnSorter, ([startColumnGroup, ...restColumnGroups]) => { for (let columnGroup of restColumnGroups) { columnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index e2c50da9..961887cb 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -1,6 +1,7 @@ import { Plane } from "three" import { OBB } from "three-stdlib" import { z } from "zod" +import { ColumnLayout } from "../../../db/layouts" // HouseRootGroup has // -> HouseColumnsContainerGroup as singleton child has @@ -25,6 +26,7 @@ export type HouseRootGroupUserData = { houseId: string houseTypeId: string dnas: string[] + houseLayout: ColumnLayout friendlyName: string modifiedMaterials: Record height: number From 601d32772dc9339a20a9f72c770482905fb5e3c7 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Fri, 28 Jul 2023 13:21:20 +0100 Subject: [PATCH 061/132] wip --- app/design/state/gestures/index.ts | 20 +- app/design/ui-3d/fresh/FreshApp.tsx | 425 +----------------- app/design/ui-3d/fresh/events/houses.ts | 108 ++++- app/design/ui-3d/fresh/useCuts.ts | 40 ++ app/design/ui-3d/fresh/useGestures.ts | 127 ++++++ .../ui-3d/fresh/useKeyTestInteractions.ts | 182 ++++++++ app/design/ui-3d/fresh/useModeHandling.ts | 24 + app/design/ui-3d/fresh/userData.ts | 10 +- app/design/ui/menu/ContextMenuEntry.tsx | 2 +- 9 files changed, 511 insertions(+), 427 deletions(-) create mode 100644 app/design/ui-3d/fresh/useCuts.ts create mode 100644 app/design/ui-3d/fresh/useGestures.ts create mode 100644 app/design/ui-3d/fresh/useKeyTestInteractions.ts create mode 100644 app/design/ui-3d/fresh/useModeHandling.ts diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index 7abbee3e..d26d0837 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -7,6 +7,12 @@ import { preTransformsTransients } from "~/design/state/transients/transforms" import { A, O } from "~/utils/functions" import { useSubscribeKey } from "~/utils/hooks" import { isMesh, useRotations } from "~/utils/three" +import { + ElementMeshUserData, + GridGroupUserData, + HouseRootGroupUserData, + UserDataTypeEnum, +} from "../../ui-3d/fresh/userData" import { setCameraEnabled } from "../camera" import { getHouseCenter } from "../dimensions" import { dispatchMoveHouseIntent } from "../events/moveRotate" @@ -286,12 +292,20 @@ export const useGestures = (): any => if (intersections.length === 0) return const { - object: { userData }, + object: { userData, parent }, } = intersections[0] if (userData) { - if (userData?.identifier?.houseId) { - downMode({ ...userData.identifier }) + if (parent?.parent?.userData.type === UserDataTypeEnum.Enum.GridGroup) { + const { levelIndex } = parent.parent.userData as GridGroupUserData + if ( + parent.parent.parent?.parent?.parent?.userData.type === + UserDataTypeEnum.Enum.HouseRootGroup + ) { + const { houseId } = parent.parent.parent?.parent?.parent + ?.userData as HouseRootGroupUserData + downMode({ houseId, levelIndex }) + } } } diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 566c96d0..72048a48 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,425 +1,16 @@ -import { invalidate, ThreeEvent } from "@react-three/fiber" -import { useGesture } from "@use-gesture/react" -import { liveQuery } from "dexie" -import { identity, pipe } from "fp-ts/lib/function" -import { nanoid } from "nanoid" -import { useEffect, useRef } from "react" -import { useKey } from "react-use" -import { Group, Vector3 } from "three" -import layoutsDB from "../../../db/layouts" -import userDB, { House } from "../../../db/user" -import { A, O, R, T } from "../../../utils/functions" -import { useSubscribe } from "../../../utils/hooks" -import { floor, PI } from "../../../utils/math" -import { - isMesh, - setInvisible, - setVisibleAndRaycast, - yAxis, -} from "../../../utils/three" -import { openMenu } from "../../state/menu" -import scope, { ScopeItem } from "../../state/scope" -import settings from "../../state/settings" -import siteCtx, { downMode, SiteCtxModeEnum } from "../../state/siteCtx" -import { updateEverything } from "./dimensions" -import { - dispatchAddHouse, - useAddHouseIntentListener, - useAddHouseListener, - useDeleteHouseListener, -} from "./events/houses" -import { dispatchOutline } from "./events/outlines" -import { - createHouseGroup, - houseLayoutToHouseGroup, - insertVanillaColumn, - subtractPenultimateColumn, -} from "./helpers" -import { HouseRootGroupUserData, UserData, UserDataTypeEnum } from "./userData" - -const liveHouses: Record = {} -const stretchXHouses: Record> = {} +import { useRef } from "react" +import { Group } from "three" +import { useHousesEvents } from "./events/houses" +import useGestures from "./useGestures" +import useModeHandling from "./useModeHandling" const FreshApp = () => { const rootRef = useRef(null) - const cleanup = () => { - rootRef.current?.clear() - } - - const addHouse = async (house: House) => { - if (!rootRef.current) return - - const { - houseId: houseId, - systemId, - dnas, - friendlyName, - position, - rotation, - } = house - - const houseGroup = await createHouseGroup({ - systemId, - houseId, - dnas, - friendlyName, - }) - - houseGroup.position.set(position.x, position.y, position.z) - houseGroup.rotation.set(0, rotation, 0) - - setVisibleAndRaycast(houseGroup) - - rootRef.current.add(houseGroup) - liveHouses[houseId] = houseGroup - - invalidate() - - userDB.houses.put(house) - } - - useAddHouseListener(addHouse) - - const init = () => { - userDB.houses.toArray().then((houses) => { - pipe(houses, A.map(addHouse)) - }) - - invalidate() - - return cleanup - } - - useEffect(init, []) - // useKey("l", insert1VanillaColumn) - - useAddHouseIntentListener(({ dnas, id: houseTypeId, systemId }) => { - // maybe cameraGroundRaycast - // maybe collisions - - const id = nanoid() - const position = new Vector3(0, 0, 0) - - const getFriendlyName = () => { - return `yo+${floor(Math.random() * 99999)}` // Object.keys(houses).length + 1 - } - - const friendlyName = getFriendlyName() - - dispatchAddHouse({ - houseId: id, - systemId, - houseTypeId, - dnas, - position, - friendlyName, - modifiedMaterials: {}, - rotation: 0, - }) - }) - - useDeleteHouseListener(({ id }) => { - if (!rootRef.current) return - - const target = rootRef.current.children.find((x) => x.userData.id === id) - - if (target) { - rootRef.current.remove(target) - userDB.houses.delete(id) - } - - invalidate() - }) - - const getHouseGroups = () => Object.values(liveHouses) - // (rootRef.current?.children ?? []).filter( - // (x) => x.userData.type === UserDataTypeEnum.Enum.HouseRootGroup - // ) as Group[] - - useKey("z", () => { - for (let houseGroup of getHouseGroups()) { - insertVanillaColumn(houseGroup, 1) - updateEverything(houseGroup) - } - }) - - useKey("Z", () => { - for (let houseGroup of getHouseGroups()) { - insertVanillaColumn(houseGroup, -1) - updateEverything(houseGroup) - } - }) - - // toggle stretch width first pass - useKey("x", () => { - for (let houseId of Object.keys(liveHouses)) { - const liveHouseGroup: Group = liveHouses[houseId] - const stretchXGroups: Record = stretchXHouses[houseId] - - pipe( - R.keys(stretchXGroups), - A.head, - O.map((k) => - pipe( - stretchXGroups, - R.lookup(k), - O.map((firstGroup) => { - setVisibleAndRaycast(firstGroup) - setInvisible(liveHouseGroup) - - stretchXGroups[ - (liveHouseGroup.userData as HouseRootGroupUserData).sectionType - ] = liveHouseGroup - liveHouses[houseId] = firstGroup - - delete stretchXGroups[k] + useHousesEvents(rootRef) + useModeHandling(rootRef) - invalidate() - updateEverything(firstGroup) - }) - ) - ) - ) - } - }) - - // stretch width - - useKey("X", () => {}) - - useKey("d", () => { - for (let houseGroup of getHouseGroups()) { - subtractPenultimateColumn(houseGroup, 1) - updateEverything(houseGroup) - } - }) - - useKey("D", () => { - for (let houseGroup of getHouseGroups()) { - subtractPenultimateColumn(houseGroup, -1) - updateEverything(houseGroup) - } - }) - - useKey("t", () => { - for (let houseGroup of getHouseGroups()) { - houseGroup.position.add(new Vector3(1, 0, 1)) - updateEverything(houseGroup) - } - }) - useKey("T", () => { - for (let houseGroup of getHouseGroups()) { - houseGroup.position.add(new Vector3(-1, 0, -1)) - updateEverything(houseGroup) - } - }) - - useKey("r", () => { - for (let houseGroup of getHouseGroups()) { - houseGroup.rotateOnAxis(yAxis, PI / 8) - updateEverything(houseGroup) - } - }) - - useKey("R", () => { - for (let houseGroup of getHouseGroups()) { - houseGroup.rotateOnAxis(yAxis, -PI / 8) - updateEverything(houseGroup) - } - }) - - liveQuery(() => layoutsDB.altSectionTypeLayouts.toArray()).subscribe( - (data) => { - for (const { houseId, altSectionTypeLayouts } of data) { - pipe( - liveHouses, - R.lookup(houseId), - O.map((liveHouseGroup) => { - const layoutTasks: Record> = pipe( - altSectionTypeLayouts, - R.map(({ layout: houseLayout }) => { - const { systemId, friendlyName } = - liveHouseGroup.userData as HouseRootGroupUserData - // const houseLayout = altSectionTypeLayouts[] - return () => - houseLayoutToHouseGroup({ - systemId, - houseId, - houseLayout, - }) - }) - ) - - const layoutsTask = pipe( - layoutTasks, - R.traverse(T.ApplicativeSeq)(identity) - ) - - layoutsTask().then((groups) => { - pipe( - stretchXHouses[houseId], - R.map((group) => rootRef.current?.remove(group)) - ) - pipe( - groups, - R.map((altHouseGroup) => { - // const layerUp = (object: Object3D, layers: ) - - setInvisible(altHouseGroup) - - liveHouseGroup.matrix.decompose( - altHouseGroup.position, - altHouseGroup.quaternion, - altHouseGroup.scale - ) - - rootRef.current?.add(altHouseGroup) - }) - ) - stretchXHouses[houseId] = groups - }) - }) - ) - } - } - ) - - const bindAll: any = useGesture<{ - drag: ThreeEvent - hover: ThreeEvent - onContextMenu: ThreeEvent & - React.MouseEvent - onDoubleClick: ThreeEvent & - React.MouseEvent - }>({ - onHover: ({ event, event: { intersections }, hovering }) => { - event.stopPropagation() - if (intersections.length === 0) { - document.body.style.cursor = "" - dispatchOutline({ - objects: [], - }) - invalidate() - // scope.hovered = null - return - } - const { - object, - eventObject, - // object: { userData }, - } = intersections[0] - - // if (object.parent?.parent) { - // const objects = object.parent.parent.children.flatMap((x) => x.children) - // dispatchOutline({ - // objects, - // }) - // } - - switch (siteCtx.mode) { - case SiteCtxModeEnum.Enum.SITE: - if (object.parent?.parent?.parent?.parent) { - const objects = object.parent.parent.parent.parent.children.flatMap( - (x) => - x.children.flatMap((y) => y.children.flatMap((z) => z.children)) - ) - dispatchOutline({ - objects, - }) - } - break - case SiteCtxModeEnum.Enum.BUILDING: - // OUTLINE COLUMN ?! - break - case SiteCtxModeEnum.Enum.LEVEL: - if (object.parent) { - dispatchOutline({ objects: object.parent.children }) - } - break - } - - // scope.hovered = { - // ...userData.identifier, - // } - - if (hovering) { - document.body.style.cursor = "grab" - } - - invalidate() - }, - onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { - event.stopPropagation() - pipe( - intersections, - A.findFirst((x) => { - return ( - isMesh(x.object) && - !Array.isArray(x.object.material) && - x.object.material.visible - ) - }), - O.map(({ object: { userData } }) => { - scope.selected = userData as ScopeItem - openMenu(pageX, pageY) - }) - ) - }, - onDoubleClick: ({ event, event: { intersections } }) => { - event.stopPropagation() - - if (intersections.length === 0) return - - const { object } = intersections[0] - - const userData: UserData = object.userData as UserData - - switch (userData.type) { - case UserDataTypeEnum.Enum.ElementMesh: - const houseId = - object.parent?.parent?.parent?.parent?.userData.houseId - const levelIndex = object.parent?.parent?.userData.levelIndex - if (houseId && levelIndex) downMode({ houseId, levelIndex }) - } - - // if (userData) { - // if (userData?.identifier?.houseId) { - // downMode({ ...userData.identifier }) - // } - // } - - invalidate() - }, - }) - - useSubscribe( - settings.verticalCuts, - () => { - const { width, length } = settings.verticalCuts - const { levelIndex } = siteCtx - - rootRef.current?.traverseVisible((o3) => { - const userData = o3.userData as UserData - - switch (userData.type) { - case UserDataTypeEnum.Enum.ElementMesh: - break - case UserDataTypeEnum.Enum.HouseRootGroup: - // TypeScript knows that userData is of type HouseModuleGroupUserData in this block - break - } - }) - // Object.values(elementMaterials.current).forEach((material) => { - // material.clippingPlanes = [ - // width ? [clippingPlaneZ] : [], - // levelIndex !== null ? [clippingPlaneY] : [], - // length ? [clippingPlaneX] : [], - // ].flat() - // }) - invalidate() - }, - true - ) + const bindAll = useGestures() return ( diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index ca427085..d5677a2c 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -1,6 +1,15 @@ +import { invalidate } from "@react-three/fiber" +import { pipe } from "fp-ts/lib/function" +import { MutableRefObject, RefObject, useEffect } from "react" import { useEvent } from "react-use" +import { Group, Vector3 } from "three" import { HouseType } from "../../../../../server/data/houseTypes" -import { House } from "../../../../db/user" +import userDB, { House } from "../../../../db/user" +import { A } from "../../../../utils/functions" +import { setVisibleAndRaycast } from "../../../../utils/three" +import { createHouseGroup } from "../helpers" +import { nanoid } from "nanoid" +import { floor } from "../../../../utils/math" const ADD_HOUSE_INTENT_EVENT = "AddHouseIntentEvent" const ADD_HOUSE_EVENT = "AddHouseEvent" @@ -28,7 +37,7 @@ export const useAddHouseListener = (f: (eventDetail: House) => void) => useEvent(ADD_HOUSE_EVENT, ({ detail }) => f(detail)) type DeleteHouseDetail = { - id: string + houseId: string } export const dispatchDeleteHouse = (detail: DeleteHouseDetail) => @@ -37,3 +46,98 @@ export const dispatchDeleteHouse = (detail: DeleteHouseDetail) => export const useDeleteHouseListener = ( f: (eventDetail: DeleteHouseDetail) => void ) => useEvent(DELETE_HOUSE_EVENT, ({ detail }) => f(detail)) + +export const useHousesEvents = (rootRef: RefObject) => { + const addHouse = async (house: House) => { + if (!rootRef.current) return + + const { + houseId: houseId, + systemId, + dnas, + friendlyName, + position, + rotation, + } = house + + const houseGroup = await createHouseGroup({ + systemId, + houseId, + dnas, + friendlyName, + }) + + houseGroup.position.set(position.x, position.y, position.z) + houseGroup.rotation.set(0, rotation, 0) + + setVisibleAndRaycast(houseGroup) + + rootRef.current.add(houseGroup) + // liveHouses[houseId] = houseGroup + + invalidate() + + userDB.houses.put(house) + } + + const cleanup = () => { + rootRef.current?.clear() + } + + const initHouses = () => { + userDB.houses.toArray().then((houses) => { + pipe(houses, A.map(addHouse)) + }) + + invalidate() + + return cleanup + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(initHouses, []) + + useAddHouseIntentListener(({ dnas, id: houseTypeId, systemId }) => { + // maybe cameraGroundRaycast + // maybe collisions + + const id = nanoid() + const position = new Vector3(0, 0, 0) + + const getFriendlyName = () => { + return `yo+${floor(Math.random() * 99999)}` // Object.keys(houses).length + 1 + } + + const friendlyName = getFriendlyName() + + dispatchAddHouse({ + houseId: id, + systemId, + houseTypeId, + dnas, + position, + friendlyName, + modifiedMaterials: {}, + rotation: 0, + }) + }) + + useAddHouseListener(addHouse) + + useDeleteHouseListener(({ houseId }) => { + if (!rootRef.current) return + + console.log("hi", houseId) + + const target = rootRef.current.children.find( + (x) => x.userData.houseId === houseId + ) + + if (target) { + rootRef.current.remove(target) + userDB.houses.delete(houseId) + } + + invalidate() + }) +} diff --git a/app/design/ui-3d/fresh/useCuts.ts b/app/design/ui-3d/fresh/useCuts.ts new file mode 100644 index 00000000..e5cf041d --- /dev/null +++ b/app/design/ui-3d/fresh/useCuts.ts @@ -0,0 +1,40 @@ +import { invalidate } from "@react-three/fiber" +import { RefObject } from "react" +import { Group } from "three" +import { useSubscribe } from "../../../utils/hooks" +import settings from "../../state/settings" +import siteCtx from "../../state/siteCtx" +import { UserData, UserDataTypeEnum } from "./userData" + +const useCuts = (rootRef: RefObject) => { + useSubscribe( + settings.verticalCuts, + () => { + const { width, length } = settings.verticalCuts + const { levelIndex } = siteCtx + + rootRef.current?.traverseVisible((o3) => { + const userData = o3.userData as UserData + + switch (userData.type) { + case UserDataTypeEnum.Enum.ElementMesh: + break + case UserDataTypeEnum.Enum.HouseRootGroup: + // TypeScript knows that userData is of type HouseModuleGroupUserData in this block + break + } + }) + // Object.values(elementMaterials.current).forEach((material) => { + // material.clippingPlanes = [ + // width ? [clippingPlaneZ] : [], + // levelIndex !== null ? [clippingPlaneY] : [], + // length ? [clippingPlaneX] : [], + // ].flat() + // }) + invalidate() + }, + true + ) +} + +export default useCuts diff --git a/app/design/ui-3d/fresh/useGestures.ts b/app/design/ui-3d/fresh/useGestures.ts new file mode 100644 index 00000000..7d1d418f --- /dev/null +++ b/app/design/ui-3d/fresh/useGestures.ts @@ -0,0 +1,127 @@ +import { invalidate, ThreeEvent } from "@react-three/fiber" +import { useGesture } from "@use-gesture/react" +import { pipe } from "fp-ts/lib/function" +import { A, O } from "../../../utils/functions" +import { isMesh } from "../../../utils/three" +import { openMenu } from "../../state/menu" +import scope, { ScopeItem } from "../../state/scope" +import siteCtx, { downMode, SiteCtxModeEnum } from "../../state/siteCtx" +import { dispatchOutline } from "./events/outlines" +import { UserData, UserDataTypeEnum } from "./userData" + +const useGestures = () => { + return useGesture<{ + drag: ThreeEvent + hover: ThreeEvent + onContextMenu: ThreeEvent & + React.MouseEvent + onDoubleClick: ThreeEvent & + React.MouseEvent + }>({ + onHover: ({ event, event: { intersections }, hovering }) => { + console.log("HELLO?") + event.stopPropagation() + + if (intersections.length === 0) { + document.body.style.cursor = "" + dispatchOutline({ + objects: [], + }) + invalidate() + // scope.hovered = null + return + } + + const { + object, + eventObject, + // object: { userData }, + } = intersections[0] + + // if (object.parent?.parent) { + // const objects = object.parent.parent.children.flatMap((x) => x.children) + // dispatchOutline({ + // objects, + // }) + // } + + switch (siteCtx.mode) { + case SiteCtxModeEnum.Enum.SITE: + console.log("yo") + if (object.parent?.parent?.parent?.parent) { + console.log("sup") + const objects = object.parent.parent.parent.parent.children.flatMap( + (x) => + x.children.flatMap((y) => y.children.flatMap((z) => z.children)) + ) + dispatchOutline({ + objects, + }) + } + break + case SiteCtxModeEnum.Enum.BUILDING: + // OUTLINE COLUMN ?! + break + case SiteCtxModeEnum.Enum.LEVEL: + if (object.parent) { + dispatchOutline({ objects: object.parent.children }) + } + break + } + + // scope.hovered = { + // ...userData.identifier, + // } + + if (hovering) { + document.body.style.cursor = "grab" + } + + invalidate() + }, + onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { + event.stopPropagation() + pipe( + intersections, + A.findFirst((x) => { + return ( + isMesh(x.object) && + !Array.isArray(x.object.material) && + x.object.material.visible + ) + }), + O.map(({ object: { userData } }) => { + scope.selected = userData as ScopeItem + openMenu(pageX, pageY) + }) + ) + }, + onDoubleClick: ({ event, event: { intersections } }) => { + event.stopPropagation() + + if (intersections.length === 0) return + + const { object } = intersections[0] + + const userData: UserData = object.userData as UserData + + switch (userData.type) { + case UserDataTypeEnum.Enum.ElementMesh: + const houseId = + object.parent?.parent?.parent?.parent?.userData.houseId + const levelIndex = object.parent?.parent?.userData.levelIndex + if (houseId && levelIndex) downMode({ houseId, levelIndex }) + } + + // if (userData) { + // if (userData?.identifier?.houseId) { + // downMode({ ...userData.identifier }) + // } + // } + + invalidate() + }, + }) as any +} + +export default useGestures diff --git a/app/design/ui-3d/fresh/useKeyTestInteractions.ts b/app/design/ui-3d/fresh/useKeyTestInteractions.ts new file mode 100644 index 00000000..fdb0e512 --- /dev/null +++ b/app/design/ui-3d/fresh/useKeyTestInteractions.ts @@ -0,0 +1,182 @@ +import { RefObject } from "react" +import { useKey } from "react-use" +import { Group, Object3D, Vector3 } from "three" +import { PI } from "../../../utils/math" +import { yAxis } from "../../../utils/three" +import { updateEverything } from "./dimensions" +import { insertVanillaColumn, subtractPenultimateColumn } from "./helpers" +import { UserDataTypeEnum } from "./userData" + +const useKeyTestInteractions = (rootRef: RefObject) => { + const getHouseGroups = () => { + return ( + rootRef.current?.children.filter( + (x: Object3D): x is Group => + x.userData.type === UserDataTypeEnum.Enum.HouseRootGroup + ) ?? [] + ) + } + + useKey("z", () => { + for (let houseGroup of getHouseGroups()) { + insertVanillaColumn(houseGroup, 1) + updateEverything(houseGroup) + } + }) + + useKey("Z", () => { + for (let houseGroup of getHouseGroups()) { + insertVanillaColumn(houseGroup, -1) + updateEverything(houseGroup) + } + }) + + // stretch width - + useKey("X", () => {}) + + useKey("d", () => { + for (let houseGroup of getHouseGroups()) { + subtractPenultimateColumn(houseGroup, 1) + updateEverything(houseGroup) + } + }) + + useKey("D", () => { + for (let houseGroup of getHouseGroups()) { + subtractPenultimateColumn(houseGroup, -1) + updateEverything(houseGroup) + } + }) + + useKey("t", () => { + for (let houseGroup of getHouseGroups()) { + houseGroup.position.add(new Vector3(1, 0, 1)) + updateEverything(houseGroup) + } + }) + useKey("T", () => { + for (let houseGroup of getHouseGroups()) { + houseGroup.position.add(new Vector3(-1, 0, -1)) + updateEverything(houseGroup) + } + }) + + useKey("r", () => { + for (let houseGroup of getHouseGroups()) { + houseGroup.rotateOnAxis(yAxis, PI / 8) + updateEverything(houseGroup) + } + }) + + useKey("R", () => { + for (let houseGroup of getHouseGroups()) { + houseGroup.rotateOnAxis(yAxis, -PI / 8) + updateEverything(houseGroup) + } + }) + + // toggle stretch width first pass + // useKey("x", () => { + // // console.log(liveHouses) + // for (let houseId of Object.keys(liveHouses)) { + // const liveHouseGroup: Group = liveHouses[houseId] + // const stretchXGroups: Record = stretchXHouses[houseId] + + // pipe( + // R.keys(stretchXGroups), + // A.head, + // O.map((k) => + // pipe( + // stretchXGroups, + // R.lookup(k), + // O.map((firstGroup) => { + // setVisibleAndRaycast(firstGroup) + // setInvisible(liveHouseGroup) + + // console.log("swapping groups") + + // stretchXGroups[ + // (liveHouseGroup.userData as HouseRootGroupUserData).sectionType + // ] = liveHouseGroup + + // liveHouses[houseId] = firstGroup + + // delete stretchXGroups[k] + + // updateEverything(firstGroup) + + // invalidate() + // }) + // ) + // ) + // ) + // } + // }) + + // liveQuery(() => layoutsDB.altSectionTypeLayouts.toArray()).subscribe( + // (data) => { + // for (const { houseId, altSectionTypeLayouts } of data) { + // pipe( + // liveHouses, + // R.lookup(houseId), + // O.map((liveHouseGroup) => { + // const layoutTasks: Record> = pipe( + // altSectionTypeLayouts, + // R.map(({ layout: houseLayout }) => { + // const { systemId, friendlyName } = + // liveHouseGroup.userData as HouseRootGroupUserData + // // const houseLayout = altSectionTypeLayouts[] + + // return () => + // houseLayoutToHouseGroup({ + // systemId, + // houseId, + // houseLayout, + // }) + // }) + // ) + + // const layoutsTask = pipe( + // layoutTasks, + // R.traverse(T.ApplicativeSeq)(identity) + // ) + + // layoutsTask().then((groups) => { + // pipe( + // stretchXHouses[houseId], + // R.map((group) => rootRef.current?.remove(group)) + // ) + // pipe( + // groups, + // R.map((altHouseGroup) => { + // setInvisible(altHouseGroup) + + // liveHouseGroup.matrix.decompose( + // altHouseGroup.position, + // altHouseGroup.quaternion, + // altHouseGroup.scale + // ) + + // console.log(`adding altHouseGroup ${altHouseGroup.uuid}`) + + // rootRef.current?.add(altHouseGroup) + // }) + // ) + + // console.log( + // `stretchXHouses[houseId] = ${Object.values(groups).map( + // (x) => x.uuid + // )}` + // ) + // stretchXHouses[houseId] = groups + + // console.log(stretchXHouses) + // }) + // }) + // ) + // } + // } + // ) +} + +export default useKeyTestInteractions diff --git a/app/design/ui-3d/fresh/useModeHandling.ts b/app/design/ui-3d/fresh/useModeHandling.ts new file mode 100644 index 00000000..dfc008ed --- /dev/null +++ b/app/design/ui-3d/fresh/useModeHandling.ts @@ -0,0 +1,24 @@ +import { RefObject } from "react" +import { Group } from "three" +import { useSubscribeKey } from "../../../utils/hooks" +import siteCtx, { SiteCtxModeEnum } from "../../state/siteCtx" + +const useModeHandling = (rootRef: RefObject) => { + useSubscribeKey(siteCtx, "mode", () => { + const { mode } = siteCtx + + switch (mode) { + case SiteCtxModeEnum.Enum.SITE: + console.log("site mode") + break + case SiteCtxModeEnum.Enum.BUILDING: + console.log("building mode") + break + case SiteCtxModeEnum.Enum.LEVEL: + console.log("level mode") + break + } + }) +} + +export default useModeHandling diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 961887cb..c4298fcc 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -21,22 +21,24 @@ export const UserDataTypeEnum = z.enum([ export type UserDataTypeEnum = z.infer export type HouseRootGroupUserData = { + // all type: typeof UserDataTypeEnum.Enum.HouseRootGroup systemId: string houseId: string houseTypeId: string - dnas: string[] - houseLayout: ColumnLayout friendlyName: string - modifiedMaterials: Record + clippingPlanes: Plane[] + // preview specific height: number length: number width: number obb: OBB - clippingPlanes: Plane[] columnCount: number sectionType: string levelTypes: string[] + modifiedMaterials: Record + dnas: string[] + houseLayout: ColumnLayout } export type HouseColumnsContainerUserData = { diff --git a/app/design/ui/menu/ContextMenuEntry.tsx b/app/design/ui/menu/ContextMenuEntry.tsx index fb810bf9..57780099 100644 --- a/app/design/ui/menu/ContextMenuEntry.tsx +++ b/app/design/ui/menu/ContextMenuEntry.tsx @@ -77,7 +77,7 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { const deleteBuilding = () => { dispatchDeleteHouse({ - id: house.houseId, + houseId: houseId, }) // delete houses[houseId] scope.selected = null From dde9e68f2986d8d09b2eb974303932a077e929c5 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 31 Jul 2023 07:48:22 +0100 Subject: [PATCH 062/132] wip start of week --- app/design/ui-3d/fresh/FreshApp.tsx | 3 + app/design/ui-3d/fresh/events/houses.ts | 8 +- app/design/ui-3d/fresh/handleMaterial.ts | 18 ++++ app/design/ui-3d/fresh/handles.tsx | 56 ++++++++++ app/design/ui-3d/fresh/helpers.ts | 9 ++ app/design/ui-3d/fresh/useGestures.ts | 119 ++++++++++++++++++---- app/design/ui-3d/fresh/useModeHandling.ts | 23 ++++- app/design/ui-3d/fresh/userData.ts | 16 +++ app/design/ui-3d/fresh/util.ts | 27 +++++ app/design/ui-3d/init/RectangularGrid.tsx | 1 - app/design/ui/menu/ContextMenuEntry.tsx | 2 +- 11 files changed, 253 insertions(+), 29 deletions(-) create mode 100644 app/design/ui-3d/fresh/handleMaterial.ts create mode 100644 app/design/ui-3d/fresh/handles.tsx create mode 100644 app/design/ui-3d/fresh/util.ts diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 72048a48..35d43790 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -2,6 +2,7 @@ import { useRef } from "react" import { Group } from "three" import { useHousesEvents } from "./events/houses" import useGestures from "./useGestures" +import useKeyTestInteractions from "./useKeyTestInteractions" import useModeHandling from "./useModeHandling" const FreshApp = () => { @@ -12,6 +13,8 @@ const FreshApp = () => { const bindAll = useGestures() + useKeyTestInteractions(rootRef) + return ( {/* diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index d5677a2c..fb1fa96d 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -127,11 +127,9 @@ export const useHousesEvents = (rootRef: RefObject) => { useDeleteHouseListener(({ houseId }) => { if (!rootRef.current) return - console.log("hi", houseId) - - const target = rootRef.current.children.find( - (x) => x.userData.houseId === houseId - ) + const target = rootRef.current.children.find((x) => { + return x.userData.houseId === houseId + }) if (target) { rootRef.current.remove(target) diff --git a/app/design/ui-3d/fresh/handleMaterial.ts b/app/design/ui-3d/fresh/handleMaterial.ts new file mode 100644 index 00000000..6ab69526 --- /dev/null +++ b/app/design/ui-3d/fresh/handleMaterial.ts @@ -0,0 +1,18 @@ +import tailwindConfig from "@/tailwind.config" +import { DoubleSide, MeshStandardMaterial } from "three" + +const colors = { + default: "white", + alt: + (tailwindConfig.theme?.extend?.colors as any)?.["grey"]?.["90"] ?? "black", +} + +const color = colors.default + +const handleMaterial = new MeshStandardMaterial({ + color, + emissive: color, + side: DoubleSide, +}) + +export default handleMaterial diff --git a/app/design/ui-3d/fresh/handles.tsx b/app/design/ui-3d/fresh/handles.tsx new file mode 100644 index 00000000..6d509027 --- /dev/null +++ b/app/design/ui-3d/fresh/handles.tsx @@ -0,0 +1,56 @@ +import { Mesh } from "three" +import { RoundedBoxGeometry } from "three-stdlib" +import { PI } from "../../../utils/math" +import handleMaterial from "./handleMaterial" +import { UserDataTypeEnum } from "./userData" + +export const createStretchHandles = ({ + width, + length, +}: { + width: number + length: number +}) => { + const stretchHandlesData: Array<{ axis: "z" | "x"; direction: 1 | -1 }> = [ + { axis: "z", direction: 1 }, + { axis: "z", direction: -1 }, + { axis: "x", direction: 1 }, + { axis: "x", direction: -1 }, + ] + + return stretchHandlesData.map(({ axis, direction }, i) => { + const OFFSET_XZ = 0.5 + const OFFSET_Y = 0.1 + + const position: [number, number, number] = + axis === "z" + ? direction === 1 + ? [0, OFFSET_Y, length / 2 + OFFSET_XZ] + : [0, OFFSET_Y, -OFFSET_XZ - length / 2] + : direction === 1 + ? [width / 2 + OFFSET_XZ, OFFSET_Y, 0] + : [-(width / 2 + OFFSET_XZ), OFFSET_Y, 0] + + const rotation: [number, number, number] = + axis === "x" ? [0, PI / 2, 0] : [0, 0, 0] + + const geometry = new RoundedBoxGeometry( + ((axis === "z" ? width : length) * 2) / 1.5, + 1, + 1 + ) + const material = handleMaterial + + const mesh = new Mesh(geometry, material) + mesh.position.set(...position) + mesh.rotation.set(...rotation) + mesh.scale.set(0.4, 0.001, 0.4) + mesh.userData = { + type: UserDataTypeEnum.Enum.StretchHandleMesh, + axis, + direction, + } + + return mesh + }) +} diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index f03b3b2e..286b50d2 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -1,3 +1,4 @@ +import { RoundedBox } from "@react-three/drei" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" import { @@ -21,8 +22,11 @@ import layoutsDB, { VanillaColumnsKey, } from "../../../db/layouts" import { A, Num, O, Ord, R, S } from "../../../utils/functions" +import { PI } from "../../../utils/math" import { getLayoutsWorker } from "../../../workers" import { CameraLayer, RaycasterLayer } from "../../state/constants" +import handleMaterial from "./handleMaterial" +import { createStretchHandles } from "./handles" import { getMaterial } from "./systems" import { ColumnGroupUserData, @@ -30,6 +34,7 @@ import { GridGroupUserData, HouseRootGroupUserData, ModuleGroupUserData, + StretchHandleMeshUserData, UserDataTypeEnum, } from "./userData" @@ -293,6 +298,10 @@ export const houseLayoutToHouseGroup = async ({ topLevelHouseGroup.add(zCenterHouseGroup) + createStretchHandles({ width, length }).forEach((mesh) => { + topLevelHouseGroup.add(mesh) + }) + return topLevelHouseGroup } diff --git a/app/design/ui-3d/fresh/useGestures.ts b/app/design/ui-3d/fresh/useGestures.ts index 7d1d418f..beb03b58 100644 --- a/app/design/ui-3d/fresh/useGestures.ts +++ b/app/design/ui-3d/fresh/useGestures.ts @@ -2,12 +2,28 @@ import { invalidate, ThreeEvent } from "@react-three/fiber" import { useGesture } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { A, O } from "../../../utils/functions" +import { useSubscribeKey } from "../../../utils/hooks" import { isMesh } from "../../../utils/three" +import { setCameraEnabled } from "../../state/camera" +import { dispatchMoveHouseIntent } from "../../state/events/moveRotate" +import dragProxy, { Drag } from "../../state/gestures/drag" import { openMenu } from "../../state/menu" +import pointer from "../../state/pointer" import scope, { ScopeItem } from "../../state/scope" -import siteCtx, { downMode, SiteCtxModeEnum } from "../../state/siteCtx" +import siteCtx, { + downMode, + EditModeEnum, + SiteCtxModeEnum, +} from "../../state/siteCtx" import { dispatchOutline } from "./events/outlines" -import { UserData, UserDataTypeEnum } from "./userData" +import { + ElementMeshUserData, + GridGroupUserData, + HouseRootGroupUserData, + RotateHandleMeshUserData, + StretchHandleMeshUserData, + UserDataTypeEnum, +} from "./userData" const useGestures = () => { return useGesture<{ @@ -18,8 +34,75 @@ const useGestures = () => { onDoubleClick: ThreeEvent & React.MouseEvent }>({ + onDrag: ({ + first, + last, + event, + event: { + intersections: [ + { + object: { userData }, + point: intersectionPoint, + }, + ], + }, + }) => { + const [x, z] = pointer.xz + const y = pointer.y + + // DISPATCH DRAGS INSTEAD OF PROXYING? + + // const drag: Drag = { + // userData, + // point: { x, y, z }, + // } + + // onClick here + if (first) { + event.stopPropagation() + setCameraEnabled(false) + // XZ and Y planes should subscribe here to jump to right place + + switch (userData.type as UserDataTypeEnum) { + case UserDataTypeEnum.Enum.ElementMesh: + const {} = userData as ElementMeshUserData + break + case UserDataTypeEnum.Enum.StretchHandleMesh: + const {} = userData as StretchHandleMeshUserData + break + case UserDataTypeEnum.Enum.RotateHandleMesh: + const {} = userData as RotateHandleMeshUserData + break + } + + // if (type) { + + // scope.selected = { + // ...identifier, + // } + + // if (siteCtx.editMode === null) { + // siteCtx.editMode = EditModeEnum.Enum.MOVE_ROTATE + // } + // if (siteCtx.houseId !== identifier.houseId) { + // siteCtx.houseId = identifier.houseId + // } + + // dragProxy.start = { + // identifier, + // point: intersectionPoint, + // } + // dragProxy.end = false + // } + } else if (last) { + // event.stopPropagation() + // dragProxy.end = true + // setCameraEnabled(true) + } else { + // dragProxy.drag = drag + } + }, onHover: ({ event, event: { intersections }, hovering }) => { - console.log("HELLO?") event.stopPropagation() if (intersections.length === 0) { @@ -47,9 +130,7 @@ const useGestures = () => { switch (siteCtx.mode) { case SiteCtxModeEnum.Enum.SITE: - console.log("yo") if (object.parent?.parent?.parent?.parent) { - console.log("sup") const objects = object.parent.parent.parent.parent.children.flatMap( (x) => x.children.flatMap((y) => y.children.flatMap((z) => z.children)) @@ -101,24 +182,22 @@ const useGestures = () => { if (intersections.length === 0) return - const { object } = intersections[0] - - const userData: UserData = object.userData as UserData + const { + object: { parent }, + } = intersections[0] - switch (userData.type) { - case UserDataTypeEnum.Enum.ElementMesh: - const houseId = - object.parent?.parent?.parent?.parent?.userData.houseId - const levelIndex = object.parent?.parent?.userData.levelIndex - if (houseId && levelIndex) downMode({ houseId, levelIndex }) + if (parent?.parent?.userData.type === UserDataTypeEnum.Enum.GridGroup) { + const { levelIndex } = parent.parent.userData as GridGroupUserData + if ( + parent.parent.parent?.parent?.parent?.userData.type === + UserDataTypeEnum.Enum.HouseRootGroup + ) { + const { houseId } = parent.parent.parent?.parent?.parent + ?.userData as HouseRootGroupUserData + downMode({ houseId, levelIndex }) + } } - // if (userData) { - // if (userData?.identifier?.houseId) { - // downMode({ ...userData.identifier }) - // } - // } - invalidate() }, }) as any diff --git a/app/design/ui-3d/fresh/useModeHandling.ts b/app/design/ui-3d/fresh/useModeHandling.ts index dfc008ed..f6d99407 100644 --- a/app/design/ui-3d/fresh/useModeHandling.ts +++ b/app/design/ui-3d/fresh/useModeHandling.ts @@ -1,17 +1,36 @@ +import { invalidate } from "@react-three/fiber" import { RefObject } from "react" -import { Group } from "three" +import { BoxGeometry, Group, Mesh, MeshBasicMaterial } from "three" import { useSubscribeKey } from "../../../utils/hooks" +import { setVisibleOnly } from "../../../utils/three" import siteCtx, { SiteCtxModeEnum } from "../../state/siteCtx" +import getUtils from "./util" const useModeHandling = (rootRef: RefObject) => { + const { applyToHouseRootGroup } = getUtils(rootRef) + + let i = 4 + useSubscribeKey(siteCtx, "mode", () => { - const { mode } = siteCtx + const { mode, houseId, levelIndex } = siteCtx switch (mode) { case SiteCtxModeEnum.Enum.SITE: console.log("site mode") break case SiteCtxModeEnum.Enum.BUILDING: + if (houseId) + applyToHouseRootGroup(houseId, (houseRootGroup) => { + const newMesh = new Mesh( + new BoxGeometry(), + new MeshBasicMaterial({ color: "red" }) + ) + newMesh.position.y = i + i++ + console.log(i) + houseRootGroup.add(newMesh) + }) + invalidate() console.log("building mode") break case SiteCtxModeEnum.Enum.LEVEL: diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index c4298fcc..5cf5057c 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -9,6 +9,8 @@ import { ColumnLayout } from "../../../db/layouts" // -> GridGroup's as children has // -> ModuleGroup's as children has // -> ElementMesh's as children +// -> Stretch Handles +// -> Rotate Handles export const UserDataTypeEnum = z.enum([ "HouseRootGroup", @@ -17,6 +19,8 @@ export const UserDataTypeEnum = z.enum([ "GridGroup", "ModuleGroup", "ElementMesh", + "StretchHandleMesh", + "RotateHandleMesh", ]) export type UserDataTypeEnum = z.infer @@ -71,6 +75,16 @@ export type ElementMeshUserData = { ifcTag: string } +export type StretchHandleMeshUserData = { + type: typeof UserDataTypeEnum.Enum.StretchHandleMesh + axis: "z" | "x" + direction: 1 | -1 +} + +export type RotateHandleMeshUserData = { + type: typeof UserDataTypeEnum.Enum.RotateHandleMesh +} + export type UserData = | ElementMeshUserData | ModuleGroupUserData @@ -78,3 +92,5 @@ export type UserData = | ColumnGroupUserData | HouseColumnsContainerUserData | HouseRootGroupUserData + | StretchHandleMeshUserData + | RotateHandleMeshUserData diff --git a/app/design/ui-3d/fresh/util.ts b/app/design/ui-3d/fresh/util.ts new file mode 100644 index 00000000..10c0569e --- /dev/null +++ b/app/design/ui-3d/fresh/util.ts @@ -0,0 +1,27 @@ +import { pipe } from "fp-ts/lib/function" +import { RefObject } from "react" +import { Group, Object3D } from "three" +import { A, O } from "../../../utils/functions" +import { UserDataTypeEnum } from "./userData" + +const getUtils = (rootRef: RefObject) => { + const applyToHouseRootGroup = ( + houseId: string, + f: (object: Object3D) => void + ) => + pipe( + rootRef.current?.children ?? [], + A.findFirst( + ({ userData }) => + userData.type === UserDataTypeEnum.Enum.HouseRootGroup && + userData.houseId === houseId + ), + O.map(f) + ) + + return { + applyToHouseRootGroup, + } +} + +export default getUtils diff --git a/app/design/ui-3d/init/RectangularGrid.tsx b/app/design/ui-3d/init/RectangularGrid.tsx index c6ac54a3..7f12d7a6 100644 --- a/app/design/ui-3d/init/RectangularGrid.tsx +++ b/app/design/ui-3d/init/RectangularGrid.tsx @@ -7,7 +7,6 @@ import { LineDashedMaterial, LineSegments, } from "three" -import { CameraLayer } from "../../state/constants" interface Axis { cells: number diff --git a/app/design/ui/menu/ContextMenuEntry.tsx b/app/design/ui/menu/ContextMenuEntry.tsx index 57780099..9a2bd9a1 100644 --- a/app/design/ui/menu/ContextMenuEntry.tsx +++ b/app/design/ui/menu/ContextMenuEntry.tsx @@ -77,7 +77,7 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { const deleteBuilding = () => { dispatchDeleteHouse({ - houseId: houseId, + houseId, }) // delete houses[houseId] scope.selected = null From 54aa64633c47c9de4ee4dac8e0af74f08f436894 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 31 Jul 2023 09:20:43 +0100 Subject: [PATCH 063/132] wip pre re scope --- app/design/state/camera.ts | 2 +- app/design/state/gestures/index.ts | 6 +- app/design/ui-3d/VerticalHandle.tsx | 6 +- app/design/ui-3d/XZPlane.tsx | 37 ++-- app/design/ui-3d/fresh/FreshApp.tsx | 16 +- app/design/ui-3d/fresh/events/gestures.ts | 223 ++++++++++++++++++++++ app/design/ui-3d/fresh/useGestures.ts | 206 -------------------- 7 files changed, 261 insertions(+), 235 deletions(-) create mode 100644 app/design/ui-3d/fresh/events/gestures.ts delete mode 100644 app/design/ui-3d/fresh/useGestures.ts diff --git a/app/design/state/camera.ts b/app/design/state/camera.ts index f8606c67..303a5397 100644 --- a/app/design/state/camera.ts +++ b/app/design/state/camera.ts @@ -19,7 +19,7 @@ const camera = proxy({ lastLookAt: [...defaultCamPos, ...defaultCamTgt], }) -export const setCameraEnabled = (b: boolean) => { +export const setCameraControlsEnabled = (b: boolean) => { if (camera.controls) camera.controls.enabled = b } diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index d26d0837..f03037a0 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -13,7 +13,7 @@ import { HouseRootGroupUserData, UserDataTypeEnum, } from "../../ui-3d/fresh/userData" -import { setCameraEnabled } from "../camera" +import { setCameraControlsEnabled } from "../camera" import { getHouseCenter } from "../dimensions" import { dispatchMoveHouseIntent } from "../events/moveRotate" import { @@ -222,7 +222,7 @@ export const useGestures = (): any => // onClick here if (first) { event.stopPropagation() - setCameraEnabled(false) + setCameraControlsEnabled(false) // XZ and Y planes should subscribe here to jump to right place if (identifier) { @@ -246,7 +246,7 @@ export const useGestures = (): any => } else if (last) { event.stopPropagation() dragProxy.end = true - setCameraEnabled(true) + setCameraControlsEnabled(true) } else { dragProxy.drag = drag } diff --git a/app/design/ui-3d/VerticalHandle.tsx b/app/design/ui-3d/VerticalHandle.tsx index c5bd8d29..d541fa13 100644 --- a/app/design/ui-3d/VerticalHandle.tsx +++ b/app/design/ui-3d/VerticalHandle.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect, useRef, useState } from "react" import { Group, Mesh } from "three" import { subscribeKey } from "valtio/utils" import { RaycasterLayer } from "~/design/state/constants" -import { setCameraEnabled } from "../state/camera" +import { setCameraControlsEnabled } from "../state/camera" import dimensions from "../state/dimensions" import houses from "../state/houses" import pointer from "../state/pointer" @@ -69,7 +69,7 @@ const VerticalHandle = (props: Props) => { if (!handleRef.current) return if (first) { y0.current = pointer.y - setCameraEnabled(false) + setCameraControlsEnabled(false) dragging.current = true return } @@ -85,7 +85,7 @@ const VerticalHandle = (props: Props) => { y0.current = pointer.y if (last) { - setCameraEnabled(true) + setCameraControlsEnabled(true) dragging.current = false } }, diff --git a/app/design/ui-3d/XZPlane.tsx b/app/design/ui-3d/XZPlane.tsx index 3a72a274..3417350c 100644 --- a/app/design/ui-3d/XZPlane.tsx +++ b/app/design/ui-3d/XZPlane.tsx @@ -1,11 +1,16 @@ import { ThreeEvent } from "@react-three/fiber" import { useGesture } from "@use-gesture/react" -import { forwardRef, useEffect, useRef } from "react" +import { forwardRef, useRef } from "react" import mergeRefs from "react-merge-refs" import { DoubleSide, Mesh } from "three" -import { RaycasterLayer } from "../state/constants" -import { useDragStart } from "../state/gestures" +import { CameraLayer, RaycasterLayer } from "../state/constants" import pointer from "../state/pointer" +import { + usePointerDownListener, + usePointerUpListener, +} from "./fresh/events/gestures" + +const DEBUG = false type Props = { size?: number @@ -18,16 +23,21 @@ const XZPlane = forwardRef((props, ref) => { const localRef = useRef(null) const { size = DEFAULT_SIZE } = props - const dragStart = useDragStart() - - useEffect(() => { + usePointerDownListener(({ point: { y }, userData }) => { if (!localRef.current) return - if (dragStart) { - localRef.current.position.setY(dragStart.point.y) - } else { - localRef.current.position.setY(0) + localRef.current.position.setY(y) + localRef.current.layers.set(RaycasterLayer.ENABLED) + if (DEBUG) { + console.log(`DEBUG XZPlane: POINTER_DOWN CameraLayer.VISIBLE`) + localRef.current.layers.enable(CameraLayer.VISIBLE) } - }, [dragStart]) + }) + + usePointerUpListener(({ point: { y }, userData }) => { + if (!localRef.current) return + localRef.current.layers.set(RaycasterLayer.DISABLED) + if (DEBUG) console.log(`DEBUG XZPlane: POINTER_UP RaycasterLayer.DISABLED`) + }) const bind: any = useGesture<{ onPointerMove: ThreeEvent @@ -42,10 +52,7 @@ const XZPlane = forwardRef((props, ref) => { diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 35d43790..226a6609 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,9 +1,10 @@ -import { useRef } from "react" +import { Fragment, useRef } from "react" import { Group } from "three" import { useHousesEvents } from "./events/houses" -import useGestures from "./useGestures" +import useGestures from "./events/gestures" import useKeyTestInteractions from "./useKeyTestInteractions" import useModeHandling from "./useModeHandling" +import XZPlane from "../XZPlane" const FreshApp = () => { const rootRef = useRef(null) @@ -11,15 +12,16 @@ const FreshApp = () => { useHousesEvents(rootRef) useModeHandling(rootRef) - const bindAll = useGestures() + const bindAll = useGestures(rootRef) useKeyTestInteractions(rootRef) return ( - - {/* - */} - + + + + {/* */} + ) } diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts new file mode 100644 index 00000000..df490054 --- /dev/null +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -0,0 +1,223 @@ +import { invalidate, ThreeEvent } from "@react-three/fiber" +import { useGesture } from "@use-gesture/react" +import { pipe } from "fp-ts/lib/function" +import { RefObject, useRef } from "react" +import { Group } from "three" +import { A, O } from "../../../../utils/functions" +import { useSubscribeKey } from "../../../../utils/hooks" +import { isMesh } from "../../../../utils/three" +import { setCameraControlsEnabled } from "../../../state/camera" +import { dispatchMoveHouseIntent } from "../../../state/events/moveRotate" +import dragProxy, { Drag } from "../../../state/gestures/drag" +import { openMenu } from "../../../state/menu" +import pointer from "../../../state/pointer" +import scope, { ScopeItem } from "../../../state/scope" +import siteCtx, { + downMode, + EditModeEnum, + SiteCtxModeEnum, +} from "../../../state/siteCtx" +import { dispatchOutline } from "./outlines" +import { + ElementMeshUserData, + GridGroupUserData, + HouseRootGroupUserData, + RotateHandleMeshUserData, + StretchHandleMeshUserData, + UserData, + UserDataTypeEnum, +} from "../userData" +import { useEvent } from "react-use" +import { z } from "zod" + +export const GestureEventType = z.enum(["POINTER_DOWN", "POINTER_UP"]) + +export type GestureEventType = z.infer + +export type GestureEventDetail = { + point: V3 + userData: UserData +} + +export const usePointerDownListener = ( + f: (eventDetail: GestureEventDetail) => void +) => useEvent(GestureEventType.Enum.POINTER_DOWN, ({ detail }) => f(detail)) + +export const dispatchPointerDown = (detail: GestureEventDetail) => + dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_DOWN, { detail })) + +export const usePointerUpListener = ( + f: (eventDetail: GestureEventDetail) => void +) => useEvent(GestureEventType.Enum.POINTER_UP, ({ detail }) => f(detail)) + +export const dispatchPointerUp = (detail: GestureEventDetail) => + dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_UP, { detail })) + +const useGestures = (rootRef: RefObject) => { + const dragStartRef = useRef(null) + + return useGesture<{ + drag: ThreeEvent + hover: ThreeEvent + onContextMenu: ThreeEvent & + React.MouseEvent + onDoubleClick: ThreeEvent & + React.MouseEvent + onClick: ThreeEvent & + React.MouseEvent + }>({ + onDrag: ({ first, last, event }) => { + pipe( + event.intersections, + A.head, + O.map((ix0) => { + event.stopPropagation() + + const userData = ix0.object.userData as UserData + const [x, z] = pointer.xz + const y = pointer.y + + if (first) { + setCameraControlsEnabled(false) + dispatchPointerDown({ point: ix0.point, userData }) + dragStartRef.current = ix0.point + + // switch (userData.type as UserDataTypeEnum) { + // case UserDataTypeEnum.Enum.ElementMesh: + // const {} = userData as ElementMeshUserData + // break + // case UserDataTypeEnum.Enum.StretchHandleMesh: + // const {} = userData as StretchHandleMeshUserData + // break + // case UserDataTypeEnum.Enum.RotateHandleMesh: + // const {} = userData as RotateHandleMeshUserData + // break + // } + + // if (type) { + + // scope.selected = { + // ...identifier, + // } + + // if (siteCtx.editMode === null) { + // siteCtx.editMode = EditModeEnum.Enum.MOVE_ROTATE + // } + // if (siteCtx.houseId !== identifier.houseId) { + // siteCtx.houseId = identifier.houseId + // } + + // } + } else if (last) { + setCameraControlsEnabled(true) + dispatchPointerUp({ point: { x, y, z }, userData }) + } else { + // cont + } + + invalidate() + }) + ) + }, + onHover: ({ event, event: { intersections }, hovering }) => { + event.stopPropagation() + + if (intersections.length === 0) { + document.body.style.cursor = "" + dispatchOutline({ + objects: [], + }) + invalidate() + // scope.hovered = null + return + } + + const { + object, + eventObject, + // object: { userData }, + } = intersections[0] + + // if (object.parent?.parent) { + // const objects = object.parent.parent.children.flatMap((x) => x.children) + // dispatchOutline({ + // objects, + // }) + // } + + switch (siteCtx.mode) { + case SiteCtxModeEnum.Enum.SITE: + if (object.parent?.parent?.parent?.parent) { + const objects = object.parent.parent.parent.parent.children.flatMap( + (x) => + x.children.flatMap((y) => y.children.flatMap((z) => z.children)) + ) + dispatchOutline({ + objects, + }) + } + break + case SiteCtxModeEnum.Enum.BUILDING: + // OUTLINE COLUMN ?! + break + case SiteCtxModeEnum.Enum.LEVEL: + if (object.parent) { + dispatchOutline({ objects: object.parent.children }) + } + break + } + + // scope.hovered = { + // ...userData.identifier, + // } + + if (hovering) { + document.body.style.cursor = "grab" + } + + invalidate() + }, + onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { + event.stopPropagation() + pipe( + intersections, + A.findFirst((x) => { + return ( + isMesh(x.object) && + !Array.isArray(x.object.material) && + x.object.material.visible + ) + }), + O.map(({ object: { userData } }) => { + scope.selected = userData as ScopeItem + openMenu(pageX, pageY) + }) + ) + }, + onDoubleClick: ({ event, event: { intersections } }) => { + event.stopPropagation() + + if (intersections.length === 0) return + + const { + object: { parent }, + } = intersections[0] + + if (parent?.parent?.userData.type === UserDataTypeEnum.Enum.GridGroup) { + const { levelIndex } = parent.parent.userData as GridGroupUserData + if ( + parent.parent.parent?.parent?.parent?.userData.type === + UserDataTypeEnum.Enum.HouseRootGroup + ) { + const { houseId } = parent.parent.parent?.parent?.parent + ?.userData as HouseRootGroupUserData + downMode({ houseId, levelIndex }) + } + } + + invalidate() + }, + }) as any +} + +export default useGestures diff --git a/app/design/ui-3d/fresh/useGestures.ts b/app/design/ui-3d/fresh/useGestures.ts deleted file mode 100644 index beb03b58..00000000 --- a/app/design/ui-3d/fresh/useGestures.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { invalidate, ThreeEvent } from "@react-three/fiber" -import { useGesture } from "@use-gesture/react" -import { pipe } from "fp-ts/lib/function" -import { A, O } from "../../../utils/functions" -import { useSubscribeKey } from "../../../utils/hooks" -import { isMesh } from "../../../utils/three" -import { setCameraEnabled } from "../../state/camera" -import { dispatchMoveHouseIntent } from "../../state/events/moveRotate" -import dragProxy, { Drag } from "../../state/gestures/drag" -import { openMenu } from "../../state/menu" -import pointer from "../../state/pointer" -import scope, { ScopeItem } from "../../state/scope" -import siteCtx, { - downMode, - EditModeEnum, - SiteCtxModeEnum, -} from "../../state/siteCtx" -import { dispatchOutline } from "./events/outlines" -import { - ElementMeshUserData, - GridGroupUserData, - HouseRootGroupUserData, - RotateHandleMeshUserData, - StretchHandleMeshUserData, - UserDataTypeEnum, -} from "./userData" - -const useGestures = () => { - return useGesture<{ - drag: ThreeEvent - hover: ThreeEvent - onContextMenu: ThreeEvent & - React.MouseEvent - onDoubleClick: ThreeEvent & - React.MouseEvent - }>({ - onDrag: ({ - first, - last, - event, - event: { - intersections: [ - { - object: { userData }, - point: intersectionPoint, - }, - ], - }, - }) => { - const [x, z] = pointer.xz - const y = pointer.y - - // DISPATCH DRAGS INSTEAD OF PROXYING? - - // const drag: Drag = { - // userData, - // point: { x, y, z }, - // } - - // onClick here - if (first) { - event.stopPropagation() - setCameraEnabled(false) - // XZ and Y planes should subscribe here to jump to right place - - switch (userData.type as UserDataTypeEnum) { - case UserDataTypeEnum.Enum.ElementMesh: - const {} = userData as ElementMeshUserData - break - case UserDataTypeEnum.Enum.StretchHandleMesh: - const {} = userData as StretchHandleMeshUserData - break - case UserDataTypeEnum.Enum.RotateHandleMesh: - const {} = userData as RotateHandleMeshUserData - break - } - - // if (type) { - - // scope.selected = { - // ...identifier, - // } - - // if (siteCtx.editMode === null) { - // siteCtx.editMode = EditModeEnum.Enum.MOVE_ROTATE - // } - // if (siteCtx.houseId !== identifier.houseId) { - // siteCtx.houseId = identifier.houseId - // } - - // dragProxy.start = { - // identifier, - // point: intersectionPoint, - // } - // dragProxy.end = false - // } - } else if (last) { - // event.stopPropagation() - // dragProxy.end = true - // setCameraEnabled(true) - } else { - // dragProxy.drag = drag - } - }, - onHover: ({ event, event: { intersections }, hovering }) => { - event.stopPropagation() - - if (intersections.length === 0) { - document.body.style.cursor = "" - dispatchOutline({ - objects: [], - }) - invalidate() - // scope.hovered = null - return - } - - const { - object, - eventObject, - // object: { userData }, - } = intersections[0] - - // if (object.parent?.parent) { - // const objects = object.parent.parent.children.flatMap((x) => x.children) - // dispatchOutline({ - // objects, - // }) - // } - - switch (siteCtx.mode) { - case SiteCtxModeEnum.Enum.SITE: - if (object.parent?.parent?.parent?.parent) { - const objects = object.parent.parent.parent.parent.children.flatMap( - (x) => - x.children.flatMap((y) => y.children.flatMap((z) => z.children)) - ) - dispatchOutline({ - objects, - }) - } - break - case SiteCtxModeEnum.Enum.BUILDING: - // OUTLINE COLUMN ?! - break - case SiteCtxModeEnum.Enum.LEVEL: - if (object.parent) { - dispatchOutline({ objects: object.parent.children }) - } - break - } - - // scope.hovered = { - // ...userData.identifier, - // } - - if (hovering) { - document.body.style.cursor = "grab" - } - - invalidate() - }, - onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { - event.stopPropagation() - pipe( - intersections, - A.findFirst((x) => { - return ( - isMesh(x.object) && - !Array.isArray(x.object.material) && - x.object.material.visible - ) - }), - O.map(({ object: { userData } }) => { - scope.selected = userData as ScopeItem - openMenu(pageX, pageY) - }) - ) - }, - onDoubleClick: ({ event, event: { intersections } }) => { - event.stopPropagation() - - if (intersections.length === 0) return - - const { - object: { parent }, - } = intersections[0] - - if (parent?.parent?.userData.type === UserDataTypeEnum.Enum.GridGroup) { - const { levelIndex } = parent.parent.userData as GridGroupUserData - if ( - parent.parent.parent?.parent?.parent?.userData.type === - UserDataTypeEnum.Enum.HouseRootGroup - ) { - const { houseId } = parent.parent.parent?.parent?.parent - ?.userData as HouseRootGroupUserData - downMode({ houseId, levelIndex }) - } - } - - invalidate() - }, - }) as any -} - -export default useGestures From 4c7e3fa021a52acd9b998b1dfbfe9d77ca877d36 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 31 Jul 2023 09:57:12 +0100 Subject: [PATCH 064/132] wip wip --- app/design/ui-3d/fresh/events/gestures.ts | 42 +++++++++++++++++++++-- app/design/ui-3d/fresh/handles.tsx | 7 ++-- app/design/ui-3d/fresh/helpers.ts | 2 +- app/design/ui-3d/fresh/userData.ts | 1 + 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index df490054..980a3539 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -29,6 +29,7 @@ import { } from "../userData" import { useEvent } from "react-use" import { z } from "zod" +import { insertVanillaColumn } from "../helpers" export const GestureEventType = z.enum(["POINTER_DOWN", "POINTER_UP"]) @@ -53,8 +54,13 @@ export const usePointerUpListener = ( export const dispatchPointerUp = (detail: GestureEventDetail) => dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_UP, { detail })) +type GestureTarget = { + userData: UserData + point: V3 +} + const useGestures = (rootRef: RefObject) => { - const dragStartRef = useRef(null) + const dragStartRef = useRef(null) return useGesture<{ drag: ThreeEvent @@ -80,7 +86,7 @@ const useGestures = (rootRef: RefObject) => { if (first) { setCameraControlsEnabled(false) dispatchPointerDown({ point: ix0.point, userData }) - dragStartRef.current = ix0.point + dragStartRef.current = { userData, point: ix0.point } // switch (userData.type as UserDataTypeEnum) { // case UserDataTypeEnum.Enum.ElementMesh: @@ -112,7 +118,37 @@ const useGestures = (rootRef: RefObject) => { setCameraControlsEnabled(true) dispatchPointerUp({ point: { x, y, z }, userData }) } else { - // cont + if (!dragStartRef.current) return + const d0 = dragStartRef.current + const { userData, point: p0 } = d0 + + switch (userData.type) { + case UserDataTypeEnum.Enum.StretchHandleMesh: + const delta = { + x: x - p0.x, + y: y - p0.y, + z: z - p0.z, + } + const { houseId, direction, axis } = + userData as StretchHandleMeshUserData + + // rootRef to house group + + switch (axis) { + case "z": + // measure vanilla column length vs. delta + // maybe insert or remove + + // insertVanillaColumn(houseGroup, direction) + break + case "x": + break + } + + break + default: + break + } } invalidate() diff --git a/app/design/ui-3d/fresh/handles.tsx b/app/design/ui-3d/fresh/handles.tsx index 6d509027..672e0b2a 100644 --- a/app/design/ui-3d/fresh/handles.tsx +++ b/app/design/ui-3d/fresh/handles.tsx @@ -2,12 +2,14 @@ import { Mesh } from "three" import { RoundedBoxGeometry } from "three-stdlib" import { PI } from "../../../utils/math" import handleMaterial from "./handleMaterial" -import { UserDataTypeEnum } from "./userData" +import { StretchHandleMeshUserData, UserDataTypeEnum } from "./userData" export const createStretchHandles = ({ + houseId, width, length, }: { + houseId: string width: number length: number }) => { @@ -49,7 +51,8 @@ export const createStretchHandles = ({ type: UserDataTypeEnum.Enum.StretchHandleMesh, axis, direction, - } + houseId, + } as StretchHandleMeshUserData return mesh }) diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers.ts index 286b50d2..5f71bc92 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers.ts @@ -298,7 +298,7 @@ export const houseLayoutToHouseGroup = async ({ topLevelHouseGroup.add(zCenterHouseGroup) - createStretchHandles({ width, length }).forEach((mesh) => { + createStretchHandles({ houseId, width, length }).forEach((mesh) => { topLevelHouseGroup.add(mesh) }) diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 5cf5057c..c2d5ad4e 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -79,6 +79,7 @@ export type StretchHandleMeshUserData = { type: typeof UserDataTypeEnum.Enum.StretchHandleMesh axis: "z" | "x" direction: 1 | -1 + houseId: string } export type RotateHandleMeshUserData = { From 6c110a1d259df439028151ba20477d242fc718a1 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 31 Jul 2023 14:02:55 +0100 Subject: [PATCH 065/132] wip handles moving --- app/design/ui-3d/XZPlane.tsx | 4 +- app/design/ui-3d/fresh/dimensions.ts | 2 +- app/design/ui-3d/fresh/events/gestures.ts | 134 ++++++++---------- app/design/ui-3d/fresh/events/houses.ts | 2 +- .../fresh/{handles.tsx => helpers/handles.ts} | 6 +- .../fresh/{helpers.ts => helpers/layouts.ts} | 19 +-- .../ui-3d/fresh/helpers/sceneQueries.ts | 14 ++ .../ui-3d/fresh/useKeyTestInteractions.ts | 5 +- 8 files changed, 94 insertions(+), 92 deletions(-) rename app/design/ui-3d/fresh/{handles.tsx => helpers/handles.ts} (89%) rename app/design/ui-3d/fresh/{helpers.ts => helpers/layouts.ts} (96%) create mode 100644 app/design/ui-3d/fresh/helpers/sceneQueries.ts diff --git a/app/design/ui-3d/XZPlane.tsx b/app/design/ui-3d/XZPlane.tsx index 3417350c..318eb4be 100644 --- a/app/design/ui-3d/XZPlane.tsx +++ b/app/design/ui-3d/XZPlane.tsx @@ -23,7 +23,7 @@ const XZPlane = forwardRef((props, ref) => { const localRef = useRef(null) const { size = DEFAULT_SIZE } = props - usePointerDownListener(({ point: { y }, userData }) => { + usePointerDownListener(({ point: { y } }) => { if (!localRef.current) return localRef.current.position.setY(y) localRef.current.layers.set(RaycasterLayer.ENABLED) @@ -33,7 +33,7 @@ const XZPlane = forwardRef((props, ref) => { } }) - usePointerUpListener(({ point: { y }, userData }) => { + usePointerUpListener(() => { if (!localRef.current) return localRef.current.layers.set(RaycasterLayer.DISABLED) if (DEBUG) console.log(`DEBUG XZPlane: POINTER_UP RaycasterLayer.DISABLED`) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 1683792e..0c367845 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -13,7 +13,7 @@ import { import { OBB } from "three-stdlib" import userDB from "../../../db/user" import { A, O } from "../../../utils/functions" -import { columnSorter } from "./helpers" +import { columnSorter } from "./helpers/layouts" import { HouseRootGroupUserData, ModuleGroupUserData, diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 980a3539..a739f25d 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -2,34 +2,25 @@ import { invalidate, ThreeEvent } from "@react-three/fiber" import { useGesture } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { RefObject, useRef } from "react" -import { Group } from "three" +import { useEvent } from "react-use" +import { Group, Object3D, Vector3 } from "three" +import { z } from "zod" import { A, O } from "../../../../utils/functions" -import { useSubscribeKey } from "../../../../utils/hooks" import { isMesh } from "../../../../utils/three" import { setCameraControlsEnabled } from "../../../state/camera" -import { dispatchMoveHouseIntent } from "../../../state/events/moveRotate" -import dragProxy, { Drag } from "../../../state/gestures/drag" import { openMenu } from "../../../state/menu" import pointer from "../../../state/pointer" import scope, { ScopeItem } from "../../../state/scope" -import siteCtx, { - downMode, - EditModeEnum, - SiteCtxModeEnum, -} from "../../../state/siteCtx" -import { dispatchOutline } from "./outlines" +import siteCtx, { downMode, SiteCtxModeEnum } from "../../../state/siteCtx" +import { rootHouseGroupQuery } from "../helpers/sceneQueries" import { - ElementMeshUserData, GridGroupUserData, HouseRootGroupUserData, - RotateHandleMeshUserData, StretchHandleMeshUserData, UserData, UserDataTypeEnum, } from "../userData" -import { useEvent } from "react-use" -import { z } from "zod" -import { insertVanillaColumn } from "../helpers" +import { dispatchOutline } from "./outlines" export const GestureEventType = z.enum(["POINTER_DOWN", "POINTER_UP"]) @@ -37,7 +28,7 @@ export type GestureEventType = z.infer export type GestureEventDetail = { point: V3 - userData: UserData + object: Object3D } export const usePointerDownListener = ( @@ -55,12 +46,13 @@ export const dispatchPointerUp = (detail: GestureEventDetail) => dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_UP, { detail })) type GestureTarget = { - userData: UserData point: V3 + object: Object3D } const useGestures = (rootRef: RefObject) => { const dragStartRef = useRef(null) + const lastDragRef = useRef(null) return useGesture<{ drag: ThreeEvent @@ -79,78 +71,76 @@ const useGestures = (rootRef: RefObject) => { O.map((ix0) => { event.stopPropagation() - const userData = ix0.object.userData as UserData - const [x, z] = pointer.xz - const y = pointer.y + const { point, object } = ix0 + const [px, pz] = pointer.xz + const py = pointer.y if (first) { setCameraControlsEnabled(false) - dispatchPointerDown({ point: ix0.point, userData }) - dragStartRef.current = { userData, point: ix0.point } - - // switch (userData.type as UserDataTypeEnum) { - // case UserDataTypeEnum.Enum.ElementMesh: - // const {} = userData as ElementMeshUserData - // break - // case UserDataTypeEnum.Enum.StretchHandleMesh: - // const {} = userData as StretchHandleMeshUserData - // break - // case UserDataTypeEnum.Enum.RotateHandleMesh: - // const {} = userData as RotateHandleMeshUserData - // break - // } - - // if (type) { - - // scope.selected = { - // ...identifier, - // } - - // if (siteCtx.editMode === null) { - // siteCtx.editMode = EditModeEnum.Enum.MOVE_ROTATE - // } - // if (siteCtx.houseId !== identifier.houseId) { - // siteCtx.houseId = identifier.houseId - // } - - // } + dispatchPointerDown({ point, object }) + dragStartRef.current = { point, object } + lastDragRef.current = point } else if (last) { setCameraControlsEnabled(true) - dispatchPointerUp({ point: { x, y, z }, userData }) + dispatchPointerUp({ point, object }) } else { if (!dragStartRef.current) return - const d0 = dragStartRef.current - const { userData, point: p0 } = d0 + + const { point: p0, object } = dragStartRef.current + + const userData = object.userData as UserData switch (userData.type) { - case UserDataTypeEnum.Enum.StretchHandleMesh: - const delta = { - x: x - p0.x, - y: y - p0.y, - z: z - p0.z, - } + case UserDataTypeEnum.Enum.StretchHandleMesh: { const { houseId, direction, axis } = userData as StretchHandleMeshUserData - // rootRef to house group - - switch (axis) { - case "z": - // measure vanilla column length vs. delta - // maybe insert or remove + const maybeHouseGroup = rootHouseGroupQuery(rootRef, houseId) + + pipe( + maybeHouseGroup, + O.map((houseGroup) => { + if (!lastDragRef.current) return + + const delta = new Vector3( + px - lastDragRef.current.x, + py - lastDragRef.current.y, + pz - lastDragRef.current.z + ).applyAxisAngle( + new Vector3(0, 1, 0), + -houseGroup.rotation.y + ) + + switch (axis) { + case "z": + console.log(object.position) + object.position.add(new Vector3(0, 0, delta.z)) + console.log(object.position, delta.z) + + switch (direction) { + case 1: + break + case -1: + } + + // measure vanilla column length vs. delta + // maybe insert or remove + + // insertVanillaColumn(houseGroup, direction) + break + case "x": + break + } + }) + ) - // insertVanillaColumn(houseGroup, direction) - break - case "x": - break - } - - break - default: break + } } } + lastDragRef.current = { x: px, y: py, z: pz } + invalidate() }) ) diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index fb1fa96d..8195ac37 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -7,7 +7,7 @@ import { HouseType } from "../../../../../server/data/houseTypes" import userDB, { House } from "../../../../db/user" import { A } from "../../../../utils/functions" import { setVisibleAndRaycast } from "../../../../utils/three" -import { createHouseGroup } from "../helpers" +import { createHouseGroup } from "../helpers/layouts" import { nanoid } from "nanoid" import { floor } from "../../../../utils/math" diff --git a/app/design/ui-3d/fresh/handles.tsx b/app/design/ui-3d/fresh/helpers/handles.ts similarity index 89% rename from app/design/ui-3d/fresh/handles.tsx rename to app/design/ui-3d/fresh/helpers/handles.ts index 672e0b2a..8724299e 100644 --- a/app/design/ui-3d/fresh/handles.tsx +++ b/app/design/ui-3d/fresh/helpers/handles.ts @@ -1,8 +1,8 @@ import { Mesh } from "three" import { RoundedBoxGeometry } from "three-stdlib" -import { PI } from "../../../utils/math" -import handleMaterial from "./handleMaterial" -import { StretchHandleMeshUserData, UserDataTypeEnum } from "./userData" +import { PI } from "../../../../utils/math" +import handleMaterial from "../handleMaterial" +import { StretchHandleMeshUserData, UserDataTypeEnum } from "../userData" export const createStretchHandles = ({ houseId, diff --git a/app/design/ui-3d/fresh/helpers.ts b/app/design/ui-3d/fresh/helpers/layouts.ts similarity index 96% rename from app/design/ui-3d/fresh/helpers.ts rename to app/design/ui-3d/fresh/helpers/layouts.ts index 5f71bc92..b2cefab2 100644 --- a/app/design/ui-3d/fresh/helpers.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -1,4 +1,3 @@ -import { RoundedBox } from "@react-three/drei" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" import { @@ -12,7 +11,7 @@ import { Vector3, } from "three" import { OBB } from "three-stdlib" -import { Module } from "../../../../server/data/modules" +import { Module } from "../../../../../server/data/modules" import layoutsDB, { ColumnLayout, getHouseLayoutsKey, @@ -20,23 +19,19 @@ import layoutsDB, { GridGroup, VanillaColumn, VanillaColumnsKey, -} from "../../../db/layouts" -import { A, Num, O, Ord, R, S } from "../../../utils/functions" -import { PI } from "../../../utils/math" -import { getLayoutsWorker } from "../../../workers" -import { CameraLayer, RaycasterLayer } from "../../state/constants" -import handleMaterial from "./handleMaterial" -import { createStretchHandles } from "./handles" -import { getMaterial } from "./systems" +} from "../../../../db/layouts" +import { A, Num, O, Ord, R, S } from "../../../../utils/functions" +import { getLayoutsWorker } from "../../../../workers" +import { getMaterial } from "../systems" import { ColumnGroupUserData, ElementMeshUserData, GridGroupUserData, HouseRootGroupUserData, ModuleGroupUserData, - StretchHandleMeshUserData, UserDataTypeEnum, -} from "./userData" +} from "../userData" +import { createStretchHandles } from "./handles" // serialized layout key : column export let vanillaColumns: Record = {} diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts new file mode 100644 index 00000000..4f7a90c5 --- /dev/null +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -0,0 +1,14 @@ +import { pipe } from "fp-ts/lib/function" +import { RefObject } from "react" +import { Group } from "three" +import { A, O } from "../../../../utils/functions" + +export const rootHouseGroupQuery = ( + rootRef: RefObject, + houseId: string +) => + pipe( + rootRef.current?.children, + O.fromNullable, + O.chain(A.findFirst((x) => x.userData.houseId === houseId)) + ) diff --git a/app/design/ui-3d/fresh/useKeyTestInteractions.ts b/app/design/ui-3d/fresh/useKeyTestInteractions.ts index fdb0e512..4c998b9b 100644 --- a/app/design/ui-3d/fresh/useKeyTestInteractions.ts +++ b/app/design/ui-3d/fresh/useKeyTestInteractions.ts @@ -4,7 +4,10 @@ import { Group, Object3D, Vector3 } from "three" import { PI } from "../../../utils/math" import { yAxis } from "../../../utils/three" import { updateEverything } from "./dimensions" -import { insertVanillaColumn, subtractPenultimateColumn } from "./helpers" +import { + insertVanillaColumn, + subtractPenultimateColumn, +} from "./helpers/layouts" import { UserDataTypeEnum } from "./userData" const useKeyTestInteractions = (rootRef: RefObject) => { From df6e714133fec486cd21ad494a28b4271cb945b8 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 1 Aug 2023 15:57:09 +0100 Subject: [PATCH 066/132] wip --- app/design/ui-3d/fresh/FreshApp.tsx | 6 +- app/design/ui-3d/fresh/events/gestures.ts | 261 +++++++++++------- app/design/ui-3d/fresh/helpers/handles.ts | 73 +++-- app/design/ui-3d/fresh/helpers/layouts.ts | 41 ++- .../ui-3d/fresh/helpers/sceneQueries.ts | 27 +- app/design/ui-3d/fresh/userData.ts | 3 +- app/utils/functions.ts | 11 + 7 files changed, 278 insertions(+), 144 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 226a6609..26c060a1 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -18,9 +18,9 @@ const FreshApp = () => { return ( - - - {/* */} + + + ) } diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index a739f25d..917475ca 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -1,5 +1,5 @@ import { invalidate, ThreeEvent } from "@react-three/fiber" -import { useGesture } from "@use-gesture/react" +import { Handler, useGesture, UserHandlers } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { RefObject, useRef } from "react" import { useEvent } from "react-use" @@ -12,12 +12,13 @@ import { openMenu } from "../../../state/menu" import pointer from "../../../state/pointer" import scope, { ScopeItem } from "../../../state/scope" import siteCtx, { downMode, SiteCtxModeEnum } from "../../../state/siteCtx" -import { rootHouseGroupQuery } from "../helpers/sceneQueries" +import { + handleColumnGroupParentQuery, + rootHouseGroupParentQuery, +} from "../helpers/sceneQueries" import { GridGroupUserData, HouseRootGroupUserData, - StretchHandleMeshUserData, - UserData, UserDataTypeEnum, } from "../userData" import { dispatchOutline } from "./outlines" @@ -38,21 +39,80 @@ export const usePointerDownListener = ( export const dispatchPointerDown = (detail: GestureEventDetail) => dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_DOWN, { detail })) -export const usePointerUpListener = ( - f: (eventDetail: GestureEventDetail) => void -) => useEvent(GestureEventType.Enum.POINTER_UP, ({ detail }) => f(detail)) +export const usePointerUpListener = (f: () => void) => + useEvent(GestureEventType.Enum.POINTER_UP, () => f()) -export const dispatchPointerUp = (detail: GestureEventDetail) => - dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_UP, { detail })) - -type GestureTarget = { - point: V3 - object: Object3D -} +export const dispatchPointerUp = () => + dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_UP)) const useGestures = (rootRef: RefObject) => { - const dragStartRef = useRef(null) - const lastDragRef = useRef(null) + // const firstGestureDataRef = useRef<{ + // gestureTarget: Object3D + // gestureTargetHouseGroup: Group + // point: Vector3 + // } | null>(null) + + // const stretchDragGroup = useRef(null) + + const stretchData = useRef<{ + handleObject: Object3D + houseGroup: Group + handleGroup: Group + point0: Vector3 + handleGroupPos0: Vector3 + } | null>(null) + + const onDragStretch: Handler<"drag", ThreeEvent> = ({ + first, + last, + event, + event: { object, point }, + }) => { + switch (true) { + case first: { + setCameraControlsEnabled(false) + const handleGroup = handleColumnGroupParentQuery(object) + stretchData.current = { + handleObject: object, + houseGroup: rootHouseGroupParentQuery(object), + handleGroup, + handleGroupPos0: handleGroup.position.clone(), + point0: point, + } + dispatchPointerDown({ point, object }) + break + } + case !first && !last: { + if (!stretchData.current) throw new Error("first didn't set first") + + const { handleGroup, handleGroupPos0, point0 } = stretchData.current + + const [x, z] = pointer.xz + const z0 = stretchData.current.point0.z + const y = pointer.y + + // const pointerVector = new Vector3(x, y, z) + + console.log(`z - z0 = ${z - z0}`) + + stretchData.current.handleGroup.position.set( + 0, + 0, + handleGroupPos0.z + (z - z0) + ) + + break + } + case last: { + if (stretchData.current === null) + throw new Error("stretchData.current null unexpectedly") + dispatchPointerUp() + stretchData.current = null + setCameraControlsEnabled(true) + break + } + } + } return useGesture<{ drag: ThreeEvent @@ -64,86 +124,99 @@ const useGestures = (rootRef: RefObject) => { onClick: ThreeEvent & React.MouseEvent }>({ - onDrag: ({ first, last, event }) => { - pipe( - event.intersections, - A.head, - O.map((ix0) => { - event.stopPropagation() - - const { point, object } = ix0 - const [px, pz] = pointer.xz - const py = pointer.y - - if (first) { - setCameraControlsEnabled(false) - dispatchPointerDown({ point, object }) - dragStartRef.current = { point, object } - lastDragRef.current = point - } else if (last) { - setCameraControlsEnabled(true) - dispatchPointerUp({ point, object }) - } else { - if (!dragStartRef.current) return - - const { point: p0, object } = dragStartRef.current - - const userData = object.userData as UserData - - switch (userData.type) { - case UserDataTypeEnum.Enum.StretchHandleMesh: { - const { houseId, direction, axis } = - userData as StretchHandleMeshUserData - - const maybeHouseGroup = rootHouseGroupQuery(rootRef, houseId) - - pipe( - maybeHouseGroup, - O.map((houseGroup) => { - if (!lastDragRef.current) return - - const delta = new Vector3( - px - lastDragRef.current.x, - py - lastDragRef.current.y, - pz - lastDragRef.current.z - ).applyAxisAngle( - new Vector3(0, 1, 0), - -houseGroup.rotation.y - ) - - switch (axis) { - case "z": - console.log(object.position) - object.position.add(new Vector3(0, 0, delta.z)) - console.log(object.position, delta.z) - - switch (direction) { - case 1: - break - case -1: - } - - // measure vanilla column length vs. delta - // maybe insert or remove - - // insertVanillaColumn(houseGroup, direction) - break - case "x": - break - } - }) - ) - - break - } - } - } + onDrag: (state) => { + const stretch = true + switch (true) { + case stretch: { + onDragStretch(state) + break + } + } - lastDragRef.current = { x: px, y: py, z: pz } + invalidate() - invalidate() - }) - ) + // pipe( + // event.intersections, + // A.head, + // O.map((ix0) => { + // event.stopPropagation() + + // const { point, object } = ix0 + + // if (first) { + // setCameraControlsEnabled(false) + // const [x, z] = pointer.xz + // const y = pointer.y + // pointerZeroRef.current = { x, y, z } + // lastPointerRef.current = { x, y, z } + // gestureTargetRef.current = object + // dispatchPointerDown({ point, object: gestureTargetRef.current }) + // } else if (last) { + // dispatchPointerUp({ point, object: gestureTargetRef.current! }) + // pointerZeroRef.current = null + // lastPointerRef.current = null + // gestureTargetRef.current = null + // setCameraControlsEnabled(true) + // } else { + // const userData = gestureTargetRef.current?.userData as UserData + + // switch (userData.type) { + // case UserDataTypeEnum.Enum.StretchHandleMesh: { + // const { houseId, direction, axis } = + // userData as StretchHandleMeshUserData + + // const maybeHouseGroup = rootHouseGroupQuery(rootRef, houseId) + + // pipe( + // maybeHouseGroup, + // O.map((houseGroup) => { + // if (lastPointerRef.current === null) return + + // const [x1, z1] = pointer.xz + // const y1 = pointer.y + + // const { x: x0, y: y0, z: z0 } = lastPointerRef.current + + // const delta = new Vector3( + // x1 - x0, + // y1 - y0, + // z1 - z0 + // ).applyAxisAngle( + // new Vector3(0, 1, 0), + // -houseGroup.rotation.y + // ) + + // switch (axis) { + // case "z": + // gestureTargetRef.current!.position.z += delta.z + // // object.position.lerp( + // // new Vector3(0, 0, object.position.z + delta.z), + // // 0.1 + // // ) + + // switch (direction) { + // case 1: + // break + // case -1: + // } + + // break + // case "x": + // break + // } + + // lastPointerRef.current = { x: x1, y: y1, z: z1 } + // }) + // ) + + // break + // } + // } + // } + + // invalidate() + // }) + // ) }, onHover: ({ event, event: { intersections }, hovering }) => { event.stopPropagation() diff --git a/app/design/ui-3d/fresh/helpers/handles.ts b/app/design/ui-3d/fresh/helpers/handles.ts index 8724299e..ac152d42 100644 --- a/app/design/ui-3d/fresh/helpers/handles.ts +++ b/app/design/ui-3d/fresh/helpers/handles.ts @@ -4,56 +4,51 @@ import { PI } from "../../../../utils/math" import handleMaterial from "../handleMaterial" import { StretchHandleMeshUserData, UserDataTypeEnum } from "../userData" -export const createStretchHandles = ({ +export const createStretchHandle = ({ houseId, width, length, + axis, + direction, }: { houseId: string width: number length: number + axis: "z" | "x" + direction: 1 | -1 }) => { - const stretchHandlesData: Array<{ axis: "z" | "x"; direction: 1 | -1 }> = [ - { axis: "z", direction: 1 }, - { axis: "z", direction: -1 }, - { axis: "x", direction: 1 }, - { axis: "x", direction: -1 }, - ] + const OFFSET_XZ = 0.5 + const OFFSET_Y = 0.1 - return stretchHandlesData.map(({ axis, direction }, i) => { - const OFFSET_XZ = 0.5 - const OFFSET_Y = 0.1 + const position: [number, number, number] = + axis === "z" + ? direction === 1 + ? [0, OFFSET_Y, OFFSET_XZ] + : [0, OFFSET_Y, -OFFSET_XZ] + : direction === 1 + ? [width / 2 + OFFSET_XZ, OFFSET_Y, 0] + : [-(width / 2 + OFFSET_XZ), OFFSET_Y, 0] - const position: [number, number, number] = - axis === "z" - ? direction === 1 - ? [0, OFFSET_Y, length / 2 + OFFSET_XZ] - : [0, OFFSET_Y, -OFFSET_XZ - length / 2] - : direction === 1 - ? [width / 2 + OFFSET_XZ, OFFSET_Y, 0] - : [-(width / 2 + OFFSET_XZ), OFFSET_Y, 0] + const rotation: [number, number, number] = + axis === "x" ? [0, PI / 2, 0] : [0, 0, 0] - const rotation: [number, number, number] = - axis === "x" ? [0, PI / 2, 0] : [0, 0, 0] + const geometry = new RoundedBoxGeometry( + ((axis === "z" ? width : length) * 2) / 1.5, + 1, + 1 + ) + const material = handleMaterial - const geometry = new RoundedBoxGeometry( - ((axis === "z" ? width : length) * 2) / 1.5, - 1, - 1 - ) - const material = handleMaterial + const mesh = new Mesh(geometry, material) + mesh.position.set(...position) + mesh.rotation.set(...rotation) + mesh.scale.set(0.4, 0.001, 0.4) + mesh.userData = { + type: UserDataTypeEnum.Enum.StretchHandleMesh, + axis, + direction, + houseId, + } as StretchHandleMeshUserData - const mesh = new Mesh(geometry, material) - mesh.position.set(...position) - mesh.rotation.set(...rotation) - mesh.scale.set(0.4, 0.001, 0.4) - mesh.userData = { - type: UserDataTypeEnum.Enum.StretchHandleMesh, - axis, - direction, - houseId, - } as StretchHandleMeshUserData - - return mesh - }) + return mesh } diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index b2cefab2..65ad6ba7 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -31,7 +31,7 @@ import { ModuleGroupUserData, UserDataTypeEnum, } from "../userData" -import { createStretchHandles } from "./handles" +import { createStretchHandle } from "./handles" // serialized layout key : column export let vanillaColumns: Record = {} @@ -241,6 +241,7 @@ export const houseLayoutToHouseGroup = async ({ houseLayout: ColumnLayout }) => { const BIG_NUMBER = 99 + const clippingPlanes: Plane[] = [ new Plane(new Vector3(BIG_NUMBER, 0, 0), 0), new Plane(new Vector3(0, BIG_NUMBER, 0), 0), @@ -253,6 +254,7 @@ export const houseLayoutToHouseGroup = async ({ houseLayout, clippingPlanes, }) + const topLevelHouseGroup = new Group() const zCenterHouseGroup = new Group() @@ -269,6 +271,7 @@ export const houseLayoutToHouseGroup = async ({ 0 ) const obb = new OBB() + const levelTypes = houseLayoutToLevelTypes(houseLayout) const houseGroupUserData: Partial = { type: UserDataTypeEnum.Enum.HouseRootGroup, systemId, @@ -280,7 +283,8 @@ export const houseLayoutToHouseGroup = async ({ obb, clippingPlanes, sectionType, - levelTypes: houseLayoutToLevelTypes(houseLayout), + levelTypes, + vanillaColumn: getVanillaColumn({ systemId, levelTypes }), columnCount: columnGroups.length, houseLayout, } @@ -293,9 +297,36 @@ export const houseLayoutToHouseGroup = async ({ topLevelHouseGroup.add(zCenterHouseGroup) - createStretchHandles({ houseId, width, length }).forEach((mesh) => { - topLevelHouseGroup.add(mesh) - }) + pipe( + columnGroups, + A.findFirst((x) => x.userData.columnIndex === 0), + O.map((firstColumn) => { + const stretchHandle = createStretchHandle({ + houseId, + axis: "z", + direction: -1, + length, + width, + }) + firstColumn.add(stretchHandle) + }) + ) + + pipe( + columnGroups, + A.findFirst((x) => x.userData.columnIndex === columnGroups.length - 1), + O.map((lastColumn) => { + const stretchHandle = createStretchHandle({ + houseId, + axis: "z", + direction: 1, + length, + width, + }) + stretchHandle.position.z += lastColumn.userData.length + lastColumn.add(stretchHandle) + }) + ) return topLevelHouseGroup } diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 4f7a90c5..b4a2f074 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -1,9 +1,10 @@ import { pipe } from "fp-ts/lib/function" import { RefObject } from "react" -import { Group } from "three" +import { Group, Object3D } from "three" import { A, O } from "../../../../utils/functions" +import { UserDataTypeEnum } from "../userData" -export const rootHouseGroupQuery = ( +export const rootHouseGroupChildQuery = ( rootRef: RefObject, houseId: string ) => @@ -12,3 +13,25 @@ export const rootHouseGroupQuery = ( O.fromNullable, O.chain(A.findFirst((x) => x.userData.houseId === houseId)) ) + +export const rootHouseGroupParentQuery = (object: Object3D) => { + let x = object + while (x.parent) { + if (x.userData.type === UserDataTypeEnum.Enum.HouseRootGroup) { + return x as Group + } + x = x.parent + } + throw new Error(`No HouseRootGroup parent found for ${object}`) +} + +export const handleColumnGroupParentQuery = (object: Object3D) => { + let x = object + while (x.parent) { + if (x.userData.type === UserDataTypeEnum.Enum.ColumnGroup) { + return x as Group + } + x = x.parent + } + throw new Error(`No HouseRootGroup parent found for ${object}`) +} diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index c2d5ad4e..249cbfe3 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -1,7 +1,7 @@ import { Plane } from "three" import { OBB } from "three-stdlib" import { z } from "zod" -import { ColumnLayout } from "../../../db/layouts" +import { ColumnLayout, VanillaColumn } from "../../../db/layouts" // HouseRootGroup has // -> HouseColumnsContainerGroup as singleton child has @@ -43,6 +43,7 @@ export type HouseRootGroupUserData = { modifiedMaterials: Record dnas: string[] houseLayout: ColumnLayout + vanillaColumn: VanillaColumn } export type HouseColumnsContainerUserData = { diff --git a/app/utils/functions.ts b/app/utils/functions.ts index e90532b4..aace8874 100644 --- a/app/utils/functions.ts +++ b/app/utils/functions.ts @@ -121,3 +121,14 @@ export const unwrapSome = ( taskOptionArray, T.map(A.compact) // compact function removes None and unwraps Some values ) + +export const headTail = (xs: T[]) => + pipe( + xs, + A.partitionWithIndex((i) => i === 0 || i === xs.length - 1), + ({ left: middle, right: [start, end] }) => ({ + start, + end, + middle, + }) + ) From 1a6bc08e0c9b5dd85680e4e685ca9038b6619ef3 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 1 Aug 2023 16:14:57 +0100 Subject: [PATCH 067/132] wip w/ rotation --- app/design/ui-3d/fresh/events/gestures.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 917475ca..1caff1bc 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -85,21 +85,14 @@ const useGestures = (rootRef: RefObject) => { case !first && !last: { if (!stretchData.current) throw new Error("first didn't set first") - const { handleGroup, handleGroupPos0, point0 } = stretchData.current + const { handleGroup, handleGroupPos0, point0, houseGroup } = + stretchData.current - const [x, z] = pointer.xz - const z0 = stretchData.current.point0.z - const y = pointer.y + const [x1, z1] = pointer.xz + const v = new Vector3(x1, 0, z1).sub(point0) + v.applyAxisAngle(new Vector3(0, 1, 0), -houseGroup.rotation.y) - // const pointerVector = new Vector3(x, y, z) - - console.log(`z - z0 = ${z - z0}`) - - stretchData.current.handleGroup.position.set( - 0, - 0, - handleGroupPos0.z + (z - z0) - ) + handleGroup.position.set(0, 0, handleGroupPos0.z + v.z) break } From 426f7209cea09c01256685b2ef02b619edbfdfd8 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 2 Aug 2023 08:42:11 +0100 Subject: [PATCH 068/132] wip mess --- app/design/ui-3d/fresh/events/gestures.ts | 95 +++++++++++++++++-- .../ui-3d/fresh/helpers/sceneQueries.ts | 26 ++++- 2 files changed, 110 insertions(+), 11 deletions(-) diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 1caff1bc..6735f005 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -12,13 +12,16 @@ import { openMenu } from "../../../state/menu" import pointer from "../../../state/pointer" import scope, { ScopeItem } from "../../../state/scope" import siteCtx, { downMode, SiteCtxModeEnum } from "../../../state/siteCtx" +import { insertVanillaColumn } from "../helpers/layouts" import { + getHouseGroupColumns, handleColumnGroupParentQuery, rootHouseGroupParentQuery, } from "../helpers/sceneQueries" import { GridGroupUserData, HouseRootGroupUserData, + StretchHandleMeshUserData, UserDataTypeEnum, } from "../userData" import { dispatchOutline } from "./outlines" @@ -60,8 +63,12 @@ const useGestures = (rootRef: RefObject) => { handleGroup: Group point0: Vector3 handleGroupPos0: Vector3 + lastDistance: number + columnsAddedToEnd: number } | null>(null) + // we could pre-process each gate + const onDragStretch: Handler<"drag", ThreeEvent> = ({ first, last, @@ -72,12 +79,15 @@ const useGestures = (rootRef: RefObject) => { case first: { setCameraControlsEnabled(false) const handleGroup = handleColumnGroupParentQuery(object) + const houseGroup = rootHouseGroupParentQuery(object) stretchData.current = { handleObject: object, - houseGroup: rootHouseGroupParentQuery(object), + houseGroup, handleGroup, handleGroupPos0: handleGroup.position.clone(), point0: point, + lastDistance: 0, + columnsAddedToEnd: 0, } dispatchPointerDown({ point, object }) break @@ -85,14 +95,81 @@ const useGestures = (rootRef: RefObject) => { case !first && !last: { if (!stretchData.current) throw new Error("first didn't set first") - const { handleGroup, handleGroupPos0, point0, houseGroup } = - stretchData.current - - const [x1, z1] = pointer.xz - const v = new Vector3(x1, 0, z1).sub(point0) - v.applyAxisAngle(new Vector3(0, 1, 0), -houseGroup.rotation.y) - - handleGroup.position.set(0, 0, handleGroupPos0.z + v.z) + const { + handleGroup, + handleGroupPos0, + point0, + houseGroup, + handleObject, + lastDistance, + columnsAddedToEnd, + } = stretchData.current + + const { direction, axis } = + handleObject.userData as StretchHandleMeshUserData + + switch (axis) { + case "z": + switch (direction) { + case 1: { + const vanillaColumnLength = ( + houseGroup.userData as HouseRootGroupUserData + ).vanillaColumn.length + + const [x1, z1] = pointer.xz + const distanceVector = new Vector3(x1, 0, z1).sub(point0) + distanceVector.applyAxisAngle( + new Vector3(0, 1, 0), + -houseGroup.rotation.y + ) + const distance = distanceVector.z + + handleGroup.position.set(0, 0, handleGroupPos0.z + distance) + + // maybe we want to oper on the house group columns doodah itself + + // and then constantly be computing whether we want to add or subtract + + // so we're tracking the next gate up or down + + // probably want to just set visible false and turn off raycasting + // when we delete columns + + // let's work on adding some first + + // gate 1 = z'end + vanillaColumnLength + + switch (true) { + case distance > lastDistance: { + if ( + distance > + vanillaColumnLength * (columnsAddedToEnd + 1) + ) { + console.log("add vanilla column") + insertVanillaColumn(houseGroup, 1) + stretchData.current.columnsAddedToEnd++ + } + break + } + // case distance < lastDistance: { + // if ( + // distance < + // vanillaColumnLength * columnsDelta + vanillaColumnLength + // ) { + // console.log("subtract vanilla column") + // stretchData.current.columnsDelta++ + // } + // } + } + + // gates on err... + + // vanilla column length up + + // existing column length down + } + } + } break } diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index b4a2f074..64cd7ff4 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -1,8 +1,8 @@ import { pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { Group, Object3D } from "three" -import { A, O } from "../../../../utils/functions" -import { UserDataTypeEnum } from "../userData" +import { A, O, someOrError } from "../../../../utils/functions" +import { ColumnGroupUserData, UserDataTypeEnum } from "../userData" export const rootHouseGroupChildQuery = ( rootRef: RefObject, @@ -35,3 +35,25 @@ export const handleColumnGroupParentQuery = (object: Object3D) => { } throw new Error(`No HouseRootGroup parent found for ${object}`) } + +export const getHouseGroupColumns = (houseGroup: Group) => + pipe( + houseGroup.children, + A.head, + O.map((columnsContainer) => columnsContainer.children as Group[]), + someOrError("no columns container in house group") + ) + +// should just be able to use house length + +// export const getLastColumnEndZ = (houseGroup: Group) => { +// return pipe( +// getHouseGroupColumns(houseGroup), +// A.last, +// O.map((lastColumn) => { +// const { length } = lastColumn.userData as ColumnGroupUserData +// return length +// // return pipe(lastColumn.children, A.last) +// }) +// ) +// } From c714978db6dcebc22ad2dfe1b9c3ce123f5d48b9 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 2 Aug 2023 09:10:46 +0100 Subject: [PATCH 069/132] wip z additive up working --- app/design/ui-3d/fresh/events/gestures.ts | 17 ++++++++--------- app/design/ui-3d/fresh/helpers/layouts.ts | 22 ++++++++++++++++------ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 6735f005..fafd6c9c 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -141,10 +141,7 @@ const useGestures = (rootRef: RefObject) => { switch (true) { case distance > lastDistance: { - if ( - distance > - vanillaColumnLength * (columnsAddedToEnd + 1) - ) { + if (distance > vanillaColumnLength * columnsAddedToEnd) { console.log("add vanilla column") insertVanillaColumn(houseGroup, 1) stretchData.current.columnsAddedToEnd++ @@ -161,6 +158,7 @@ const useGestures = (rootRef: RefObject) => { // } // } } + stretchData.current.lastDistance = distance // gates on err... @@ -303,7 +301,6 @@ const useGestures = (rootRef: RefObject) => { const { object, - eventObject, // object: { userData }, } = intersections[0] @@ -314,6 +311,12 @@ const useGestures = (rootRef: RefObject) => { // }) // } + if (hovering) { + document.body.style.cursor = "grab" + } + + if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return + switch (siteCtx.mode) { case SiteCtxModeEnum.Enum.SITE: if (object.parent?.parent?.parent?.parent) { @@ -340,10 +343,6 @@ const useGestures = (rootRef: RefObject) => { // ...userData.identifier, // } - if (hovering) { - document.body.style.cursor = "grab" - } - invalidate() }, onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 65ad6ba7..1f901813 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -430,22 +430,32 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { if (direction === 1) { pipe( columnGroups, - A.findFirst((x) => x.userData.columnIndex === columnCount - 1), - O.map((endColumnGroup) => { + A.filter((x) => x.userData.columnIndex >= columnCount - 2), + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ), + ([penultimateColumnGroup, endColumnGroup]) => { vanillaColumnGroup.position.setZ( - endColumnGroup.position.z + vanillaColumnLength / 2 + penultimateColumnGroup.position.z + + penultimateColumnGroup.userData.length / 2 + + vanillaColumnLength / 2 ) addColumnToHouse(houseGroup, vanillaColumnGroup) - endColumnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) + // penultimateColumnGroup.position.add( + // new Vector3(0, 0, vanillaColumnLength) + // ) vanillaColumnGroup.userData.columnIndex = - endColumnGroup.userData.columnIndex + penultimateColumnGroup.userData.columnIndex + 1 endColumnGroup.userData.columnIndex++ houseGroup.userData.columnCount = columnCount + 1 - }) + } ) } else if (direction === -1) { pipe( From b2a8d7fe2ade151deb05321047aa06045bf83893 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 2 Aug 2023 11:16:41 +0100 Subject: [PATCH 070/132] wip --- app/design/ui-3d/fresh/events/gestures.ts | 21 ++++++++++++--------- app/design/ui-3d/fresh/helpers/layouts.ts | 4 ---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index fafd6c9c..3328a83e 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -148,15 +148,18 @@ const useGestures = (rootRef: RefObject) => { } break } - // case distance < lastDistance: { - // if ( - // distance < - // vanillaColumnLength * columnsDelta + vanillaColumnLength - // ) { - // console.log("subtract vanilla column") - // stretchData.current.columnsDelta++ - // } - // } + case distance < lastDistance: { + if (distance < vanillaColumnLength * columnsAddedToEnd) { + console.log("going down") + } + // if ( + // distance < + // vanillaColumnLength * columnsDelta + vanillaColumnLength + // ) { + // console.log("subtract vanilla column") + // stretchData.current.columnsDelta++ + // } + } } stretchData.current.lastDistance = distance diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 1f901813..8972d0d1 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -445,10 +445,6 @@ export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { ) addColumnToHouse(houseGroup, vanillaColumnGroup) - // penultimateColumnGroup.position.add( - // new Vector3(0, 0, vanillaColumnLength) - // ) - vanillaColumnGroup.userData.columnIndex = penultimateColumnGroup.userData.columnIndex + 1 From 637236e242f6772e62ff8a197e8fc6605b64e20c Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 2 Aug 2023 11:22:59 +0100 Subject: [PATCH 071/132] wip --- app/design/ui-3d/fresh/helpers/layouts.ts | 97 +++++++++++++++-------- 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 8972d0d1..26cc6a9a 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -59,21 +59,33 @@ const loader = new BufferGeometryLoader() // speckle branch url : geometry by ifc tag export let models: Record> = {} +const putModel = ({ + geometries, + speckleBranchUrl, +}: { + speckleBranchUrl: string + geometries: any +}) => { + const loadedModels: Record = pipe( + geometries, + R.map((x) => loader.parse(x) as BufferGeometry), + R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { + geometry.computeVertexNormals() + return { + ...acc, + [ifcTag]: geometry, + } + }) + ) + models[speckleBranchUrl] = loadedModels + + return loadedModels +} + liveQuery(() => layoutsDB.models.toArray()).subscribe((dbModels) => { for (let { speckleBranchUrl, geometries } of dbModels) { if (!(speckleBranchUrl in models)) { - const loadedModels: Record = pipe( - geometries, - R.map((x) => loader.parse(x) as BufferGeometry), - R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { - geometry.computeVertexNormals() - return { - ...acc, - [ifcTag]: geometry, - } - }) - ) - models[speckleBranchUrl] = loadedModels + putModel({ speckleBranchUrl, geometries }) } } }) @@ -86,7 +98,7 @@ export const getGeometry = ({ ifcTag: string }) => models[speckleBranchUrl][ifcTag] -export const moduleToGroup = ({ +export const moduleToGroup = async ({ systemId, gridGroupIndex, module: { speckleBranchUrl, length, dna }, @@ -98,26 +110,47 @@ export const moduleToGroup = ({ clippingPlanes: Plane[] }) => { const moduleGroup = new Group() - const taggedModelGeometries = models[speckleBranchUrl] - for (let ifcTag of Object.keys(taggedModelGeometries)) { - const geometry = getGeometry({ speckleBranchUrl, ifcTag }) - const material = getMaterial({ - systemId, - ifcTag, - houseId: "", - }) as MeshStandardMaterial - // material.clippingPlanes = clippingPlanes - const mesh = new Mesh(geometry, material) - mesh.castShadow = true - - const elementMeshUserData: ElementMeshUserData = { - type: UserDataTypeEnum.Enum.ElementMesh, - ifcTag, + + const processModel = ( + modelGeometriesByIfcTag: Record + ) => { + for (let ifcTag of Object.keys(modelGeometriesByIfcTag)) { + const geometry = getGeometry({ speckleBranchUrl, ifcTag }) + const material = getMaterial({ + systemId, + ifcTag, + houseId: "", + }) as MeshStandardMaterial + // material.clippingPlanes = clippingPlanes + const mesh = new Mesh(geometry, material) + mesh.castShadow = true + + const elementMeshUserData: ElementMeshUserData = { + type: UserDataTypeEnum.Enum.ElementMesh, + ifcTag, + } + mesh.userData = elementMeshUserData + moduleGroup.add(mesh) } - mesh.userData = elementMeshUserData - moduleGroup.add(mesh) } + await pipe( + models, + R.lookup(speckleBranchUrl), + O.match( + async () => { + const model = await layoutsDB.models.get(speckleBranchUrl) + if (model === undefined) + throw new Error(`no model for ${speckleBranchUrl}`) + const loadedModel = putModel(model) + processModel(loadedModel) + }, + async (loadedModel) => { + processModel(loadedModel) + } + ) + ) + const moduleGroupUserData: ModuleGroupUserData = { type: UserDataTypeEnum.Enum.ModuleGroup, gridGroupIndex, @@ -131,7 +164,7 @@ export const moduleToGroup = ({ return moduleGroup } -export const createColumnGroup = ({ +export const createColumnGroup = async ({ systemId, gridGroups, columnIndex, @@ -145,7 +178,7 @@ export const createColumnGroup = ({ startColumn?: boolean endColumn?: boolean clippingPlanes: Plane[] -}): Group => { +}): Promise => { const columnGroup = new Group() gridGroups.forEach(({ modules, y, levelIndex }) => { From c572d1f350574f09d611c6b494edf8b586c5e974 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 2 Aug 2023 17:05:14 +0100 Subject: [PATCH 072/132] wip pass 1 --- app/design/ui-3d/fresh/helpers/layouts.ts | 513 +++++++++++----------- app/workers/layouts/worker.ts | 15 + 2 files changed, 275 insertions(+), 253 deletions(-) diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 26cc6a9a..dcadc04c 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -20,7 +20,7 @@ import layoutsDB, { VanillaColumn, VanillaColumnsKey, } from "../../../../db/layouts" -import { A, Num, O, Ord, R, S } from "../../../../utils/functions" +import { A, Num, O, Ord, R, S, T } from "../../../../utils/functions" import { getLayoutsWorker } from "../../../../workers" import { getMaterial } from "../systems" import { @@ -39,9 +39,25 @@ export let vanillaColumns: Record = {} export const getVanillaColumn = ({ systemId, levelTypes, -}: VanillaColumnsKey) => { +}: VanillaColumnsKey): T.Task => { const key = getVanillaColumnsKey({ systemId, levelTypes }) - return vanillaColumns[key] + + return pipe( + vanillaColumns, + R.lookup(key), + O.match( + () => { + const layoutsWorker = getLayoutsWorker() + if (!layoutsWorker) throw new Error(`no layouts worker`) + return () => + layoutsWorker.getVanillaColumn({ + systemId, + levelTypes, + }) + }, + (vanillaColumn) => T.of(vanillaColumn) + ) + ) } liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( @@ -98,16 +114,14 @@ export const getGeometry = ({ ifcTag: string }) => models[speckleBranchUrl][ifcTag] -export const moduleToGroup = async ({ +export const createModuleGroup = async ({ systemId, gridGroupIndex, module: { speckleBranchUrl, length, dna }, - clippingPlanes, }: { systemId: string gridGroupIndex: number module: Module - clippingPlanes: Plane[] }) => { const moduleGroup = new Group() @@ -156,7 +170,6 @@ export const moduleToGroup = async ({ gridGroupIndex, dna, length, - // module } moduleGroup.userData = moduleGroupUserData @@ -164,94 +177,100 @@ export const moduleToGroup = async ({ return moduleGroup } -export const createColumnGroup = async ({ - systemId, - gridGroups, - columnIndex, - startColumn = false, - endColumn = false, - clippingPlanes, -}: { - systemId: string - gridGroups: GridGroup[] - columnIndex: number - startColumn?: boolean - endColumn?: boolean - clippingPlanes: Plane[] -}): Promise => { - const columnGroup = new Group() - - gridGroups.forEach(({ modules, y, levelIndex }) => { - const gridGroup = new Group() - const length = modules.reduce( - (acc, { z, module, gridGroupIndex }): number => { - const moduleGroup = moduleToGroup({ +export const createColumnGroup = + ({ + systemId, + gridGroups, + columnIndex, + startColumn = false, + endColumn = false, + }: { + systemId: string + gridGroups: GridGroup[] + columnIndex: number + startColumn?: boolean + endColumn?: boolean + }): T.Task => + async () => { + const columnGroup = new Group() + + for (let { modules, y, levelIndex } of gridGroups) { + const gridGroup = new Group() + let length = 0 + + for (let { z, module, gridGroupIndex } of modules) { + const moduleGroup = await createModuleGroup({ systemId, module, gridGroupIndex, - clippingPlanes, }) + moduleGroup.scale.set(1, 1, endColumn ? 1 : -1) moduleGroup.position.set( 0, y, endColumn ? z + module.length / 2 : z - module.length / 2 ) + gridGroup.add(moduleGroup) - return acc + module.length - }, - 0 - ) - const gridGroupUserData: GridGroupUserData = { - type: UserDataTypeEnum.Enum.GridGroup, - levelIndex, - length, + + length += module.length + } + + const gridGroupUserData: GridGroupUserData = { + type: UserDataTypeEnum.Enum.GridGroup, + levelIndex, + length, + } + gridGroup.userData = gridGroupUserData + + columnGroup.add(gridGroup) } - gridGroup.userData = gridGroupUserData - columnGroup.add(gridGroup) - }) - const columnGroupUserData: ColumnGroupUserData = { - type: UserDataTypeEnum.Enum.ColumnGroup, - columnIndex, - length: gridGroups[0].length, - startColumn, - endColumn, - } + const columnGroupUserData: ColumnGroupUserData = { + type: UserDataTypeEnum.Enum.ColumnGroup, + columnIndex, + length: gridGroups[0].length, + startColumn, + endColumn, + } - columnGroup.userData = columnGroupUserData + columnGroup.userData = columnGroupUserData - return columnGroup -} + return columnGroup + } -export const houseLayoutToColumns = ({ +export const createColumnGroups = ({ systemId, - houseId, houseLayout, - clippingPlanes, }: { systemId: string - houseId: string houseLayout: ColumnLayout - clippingPlanes: Plane[] -}): Group[] => +}): T.Task => pipe( houseLayout, - A.mapWithIndex((i, { gridGroups, z, columnIndex, length }) => { - const startColumn = i === 0 - const endColumn = i === houseLayout.length - 1 + A.traverseWithIndex(T.ApplicativeSeq)( + (i, { gridGroups, z, columnIndex }) => { + const startColumn = i === 0 + const endColumn = i === houseLayout.length - 1 - const group = createColumnGroup({ - systemId, - gridGroups, - startColumn, - endColumn, - columnIndex, - clippingPlanes, - }) - group.position.set(0, 0, z) - return group - }) + const task = createColumnGroup({ + systemId, + gridGroups, + startColumn, + endColumn, + columnIndex, + }) + + return pipe( + task, + T.map((columnGroup) => { + columnGroup.position.set(0, 0, z) + return columnGroup + }) + ) + } + ) ) export let houseLayouts: Record = {} @@ -264,7 +283,7 @@ liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( } ) -export const houseLayoutToHouseGroup = async ({ +export const houseLayoutToHouseGroup = ({ systemId, houseId, houseLayout, @@ -272,99 +291,102 @@ export const houseLayoutToHouseGroup = async ({ systemId: string houseId: string houseLayout: ColumnLayout -}) => { - const BIG_NUMBER = 99 - - const clippingPlanes: Plane[] = [ - new Plane(new Vector3(BIG_NUMBER, 0, 0), 0), - new Plane(new Vector3(0, BIG_NUMBER, 0), 0), - new Plane(new Vector3(0, 0, BIG_NUMBER), 0), - ] - - const columnGroups = houseLayoutToColumns({ - systemId, - houseId, - houseLayout, - clippingPlanes, - }) +}): T.Task => + pipe( + createColumnGroups({ + systemId, + houseLayout, + }), + T.chain((columnGroups) => async () => { + const topLevelHouseGroup = new Group() + const zCenterHouseGroup = new Group() + + const BIG_NUMBER = 999 + + const clippingPlanes: Plane[] = [ + new Plane(new Vector3(BIG_NUMBER, 0, 0), 0), + new Plane(new Vector3(0, BIG_NUMBER, 0), 0), + new Plane(new Vector3(0, 0, BIG_NUMBER), 0), + ] + + const sectionType = + houseLayout[0].gridGroups[0].modules[0].module.structuredDna.sectionType + + const width = houseLayout[0].gridGroups[0].modules[0].module.width + const height = houseLayout[0].gridGroups.reduce( + (acc, v) => acc + v.modules[0].module.height, + 0 + ) + const length = columnGroups.reduce( + (acc, columnGroup) => acc + columnGroup.userData.length, + 0 + ) + const obb = new OBB() + const levelTypes = houseLayoutToLevelTypes(houseLayout) - const topLevelHouseGroup = new Group() - const zCenterHouseGroup = new Group() + const vanillaColumn = await getVanillaColumn({ systemId, levelTypes })() - const sectionType = - houseLayout[0].gridGroups[0].modules[0].module.structuredDna.sectionType + const houseGroupUserData: Partial = { + type: UserDataTypeEnum.Enum.HouseRootGroup, + systemId, + houseId, + width, + length, + height, + modifiedMaterials: {}, + obb, + clippingPlanes, + sectionType, + levelTypes, + vanillaColumn, + columnCount: columnGroups.length, + houseLayout, + } + topLevelHouseGroup.userData = houseGroupUserData - const width = houseLayout[0].gridGroups[0].modules[0].module.width - const height = houseLayout[0].gridGroups.reduce( - (acc, v) => acc + v.modules[0].module.height, - 0 - ) - const length = columnGroups.reduce( - (acc, columnGroup) => acc + columnGroup.userData.length, - 0 - ) - const obb = new OBB() - const levelTypes = houseLayoutToLevelTypes(houseLayout) - const houseGroupUserData: Partial = { - type: UserDataTypeEnum.Enum.HouseRootGroup, - systemId, - houseId, - width, - length, - height, - modifiedMaterials: {}, - obb, - clippingPlanes, - sectionType, - levelTypes, - vanillaColumn: getVanillaColumn({ systemId, levelTypes }), - columnCount: columnGroups.length, - houseLayout, - } - topLevelHouseGroup.userData = houseGroupUserData + zCenterHouseGroup.add(...columnGroups) + zCenterHouseGroup.position.setZ(-length / 2) + zCenterHouseGroup.userData.type = + UserDataTypeEnum.Enum.HouseColumnsContainerGroup - zCenterHouseGroup.add(...columnGroups) - zCenterHouseGroup.position.setZ(-length / 2) - zCenterHouseGroup.userData.type = - UserDataTypeEnum.Enum.HouseColumnsContainerGroup + topLevelHouseGroup.add(zCenterHouseGroup) - topLevelHouseGroup.add(zCenterHouseGroup) + pipe( + columnGroups, + A.findFirst((x) => x.userData.columnIndex === 0), + O.map((firstColumn) => { + const stretchHandle = createStretchHandle({ + houseId, + axis: "z", + direction: -1, + length, + width, + }) + firstColumn.add(stretchHandle) + }) + ) - pipe( - columnGroups, - A.findFirst((x) => x.userData.columnIndex === 0), - O.map((firstColumn) => { - const stretchHandle = createStretchHandle({ - houseId, - axis: "z", - direction: -1, - length, - width, - }) - firstColumn.add(stretchHandle) - }) - ) + pipe( + columnGroups, + A.findFirst((x) => x.userData.columnIndex === columnGroups.length - 1), + O.map((lastColumn) => { + const stretchHandle = createStretchHandle({ + houseId, + axis: "z", + direction: 1, + length, + width, + }) + stretchHandle.position.z += lastColumn.userData.length + lastColumn.add(stretchHandle) + }) + ) - pipe( - columnGroups, - A.findFirst((x) => x.userData.columnIndex === columnGroups.length - 1), - O.map((lastColumn) => { - const stretchHandle = createStretchHandle({ - houseId, - axis: "z", - direction: 1, - length, - width, - }) - stretchHandle.position.z += lastColumn.userData.length - lastColumn.add(stretchHandle) + return topLevelHouseGroup }) ) - return topLevelHouseGroup -} - -export const createHouseGroup = async ({ +export const createHouseGroup = ({ systemId, houseId, dnas, @@ -374,45 +396,28 @@ export const createHouseGroup = async ({ houseId: string dnas: string[] friendlyName: string -}) => { - const houseGroup = await pipe( +}): T.Task => + pipe( houseLayouts, R.lookup(getHouseLayoutsKey({ systemId, dnas })), O.match( - async () => { + (): T.Task => async () => { const layoutsWorker = getLayoutsWorker() if (!layoutsWorker) throw new Error(`no layouts worker`) - const houseLayout = await layoutsWorker.processLayout({ + return await layoutsWorker.processLayout({ systemId, dnas, }) - return houseLayoutToHouseGroup({ - systemId, - houseId, - houseLayout, - }) }, - (houseLayout) => - houseLayoutToHouseGroup({ - systemId, - houseId, - houseLayout, - }) - ) - ) - - houseGroup.userData.dnas = dnas - houseGroup.userData.friendlyName = friendlyName - - return houseGroup -} - -export const getFirstHouseLayout = () => - pipe( - houseLayouts, - R.keys, - A.head, - O.chain((k) => pipe(houseLayouts, R.lookup(k))) + (houseLayout) => T.of(houseLayout) + ), + T.chain((houseLayout) => + houseLayoutToHouseGroup({ systemId, houseId, houseLayout }) + ), + T.map((group) => { + group.userData.friendlyName = friendlyName + return group + }) ) export const addColumnToHouse = (houseGroup: Object3D, columnGroup: Object3D) => @@ -439,78 +444,80 @@ export const columnSorter = A.sort( ) ) -export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1) => { - const { levelTypes, systemId, columnCount, clippingPlanes } = - houseGroup.userData as HouseRootGroupUserData - pipe( - houseGroup.children, - A.head, - O.map((zCenterHouseGroup) => { - const { children: columnGroups } = zCenterHouseGroup - - const vanillaColumn = - vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] +export const insertVanillaColumn = + (houseGroup: Group, direction: 1 | -1): T.Task => + async () => { + const { levelTypes, systemId, columnCount, clippingPlanes } = + houseGroup.userData as HouseRootGroupUserData - const vanillaColumnGroup = createColumnGroup({ - systemId, - gridGroups: vanillaColumn.gridGroups, - columnIndex: -1, - clippingPlanes, - }) - - const vanillaColumnLength = vanillaColumnGroup.userData.length - - if (direction === 1) { - pipe( - columnGroups, - A.filter((x) => x.userData.columnIndex >= columnCount - 2), - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), - ([penultimateColumnGroup, endColumnGroup]) => { - vanillaColumnGroup.position.setZ( - penultimateColumnGroup.position.z + - penultimateColumnGroup.userData.length / 2 + - vanillaColumnLength / 2 - ) - addColumnToHouse(houseGroup, vanillaColumnGroup) + pipe( + houseGroup.children, + A.head, + O.map((zCenterHouseGroup) => async () => { + const { children: columnGroups } = zCenterHouseGroup - vanillaColumnGroup.userData.columnIndex = - penultimateColumnGroup.userData.columnIndex + 1 + const vanillaColumn = + vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] - endColumnGroup.userData.columnIndex++ - - houseGroup.userData.columnCount = columnCount + 1 - } - ) - } else if (direction === -1) { - pipe( - columnGroups, - columnSorter, - ([startColumnGroup, ...restColumnGroups]) => { - for (let columnGroup of restColumnGroups) { - columnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) - columnGroup.userData.columnIndex++ + const vanillaColumnGroup = await createColumnGroup({ + systemId, + gridGroups: vanillaColumn.gridGroups, + columnIndex: -1, + })() + + const vanillaColumnLength = vanillaColumnGroup.userData.length + + if (direction === 1) { + pipe( + columnGroups, + A.filter((x) => x.userData.columnIndex >= columnCount - 2), + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ), + ([penultimateColumnGroup, endColumnGroup]) => { + vanillaColumnGroup.position.setZ( + penultimateColumnGroup.position.z + + penultimateColumnGroup.userData.length / 2 + + vanillaColumnLength / 2 + ) + addColumnToHouse(houseGroup, vanillaColumnGroup) + + vanillaColumnGroup.userData.columnIndex = + penultimateColumnGroup.userData.columnIndex + 1 + + endColumnGroup.userData.columnIndex++ + + houseGroup.userData.columnCount = columnCount + 1 } - - vanillaColumnGroup.userData.columnIndex = 1 - vanillaColumnGroup.position.setZ( - startColumnGroup.position.z + - startColumnGroup.userData.length + - vanillaColumnLength / 2 - ) - addColumnToHouse(houseGroup, vanillaColumnGroup) - - houseGroup.userData.columnCount = columnCount + 1 - } - ) - } - }) - ) -} + ) + } else if (direction === -1) { + pipe( + columnGroups, + columnSorter, + ([startColumnGroup, ...restColumnGroups]) => { + for (let columnGroup of restColumnGroups) { + columnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) + columnGroup.userData.columnIndex++ + } + + vanillaColumnGroup.userData.columnIndex = 1 + vanillaColumnGroup.position.setZ( + startColumnGroup.position.z + + startColumnGroup.userData.length + + vanillaColumnLength / 2 + ) + addColumnToHouse(houseGroup, vanillaColumnGroup) + + houseGroup.userData.columnCount = columnCount + 1 + } + ) + } + }) + ) + } export const subtractPenultimateColumn = ( houseGroup: Group, diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index f02a76dc..f294c2db 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -13,11 +13,13 @@ import { import layoutsDB, { ColumnLayout, getHouseLayoutsKey, + getVanillaColumnsKey, GridGroup, HouseLayoutsKey, PositionedColumn, PositionedModule, PositionedRow, + VanillaColumnsKey, } from "../../db/layouts" import systemsDB, { LastFetchStamped } from "../../db/systems" import userDB from "../../db/user" @@ -685,12 +687,25 @@ if (!isSSR()) { }) } +const getVanillaColumn = async (key: VanillaColumnsKey) => { + const maybeVanillaColumn = await layoutsDB.vanillaColumns.get( + getVanillaColumnsKey(key) + ) + + if (maybeVanillaColumn) { + return maybeVanillaColumn.vanillaColumn + } else { + throw new Error(`No vanilla module found for ${key}`) + } +} + const api = { postLayout, postLayouts, processLayout: getLayout, processZStretchLayout, syncModels, + getVanillaColumn, } export type LayoutsAPI = typeof api From 24b5569a032af7153b4391dca93541515ad2c261 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 3 Aug 2023 08:09:39 +0100 Subject: [PATCH 073/132] wip --- app/design/state/gestures/index.ts | 3 -- app/design/state/interactions/ZStretch.tsx | 7 +-- app/design/state/layouts.ts | 4 +- app/design/ui-3d/XZPlane.tsx | 5 ++- app/design/ui-3d/fresh/FreshApp.tsx | 2 - app/design/ui-3d/fresh/events/gestures.ts | 6 +-- app/design/ui-3d/fresh/events/houses.ts | 2 +- app/design/ui-3d/fresh/helpers/layouts.ts | 26 +++++------ app/design/ui-3d/fresh/useModeHandling.ts | 43 ------------------- app/design/ui-3d/grouped/GroupedHouse2.tsx | 1 - .../ui-3d/grouped/preview/PreviewHouses.tsx | 1 - .../stretchLength/GroupedStretchColumn.tsx | 15 +++---- app/design/ui/menu/ContextMenuEntry.tsx | 2 - app/workers/layouts/worker.ts | 40 +---------------- 14 files changed, 29 insertions(+), 128 deletions(-) delete mode 100644 app/design/ui-3d/fresh/useModeHandling.ts diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index f03037a0..41e98244 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -75,7 +75,6 @@ export const useDragHandler = () => { const { direction = 1, axis } = identifier as StretchHandleIdentifier if (axis === "z") { - console.log("dispatching NORMAL") dispatchZStretchHouseIntent({ houseId, direction, @@ -108,7 +107,6 @@ export const useDragHandler = () => { if (!dragProxy.end) return const cleanup = () => { - console.log("cleaning up") dragProxy.start = null dragProxy.drag = null dragProxy.end = false @@ -156,7 +154,6 @@ export const useDragHandler = () => { identifier as StretchHandleIdentifier if (axis === "z") { - console.log("dispatching LAST") dispatchZStretchHouseIntent({ houseId, direction, diff --git a/app/design/state/interactions/ZStretch.tsx b/app/design/state/interactions/ZStretch.tsx index 74ae9998..6f06d35f 100644 --- a/app/design/state/interactions/ZStretch.tsx +++ b/app/design/state/interactions/ZStretch.tsx @@ -52,20 +52,17 @@ const ZStretch = ({ startRef, endRef, }: Props) => { - console.log("ZStretch") const { systemId } = layoutKey const strLayoutKey = getHouseLayoutsKey(layoutKey) const vanillaColumn = suspend(async () => { if (strLayoutKey in vanillaColumns) { - console.log("strLayoutKey in vanillaColumns") return vanillaColumns[strLayoutKey] } else { const layoutsWorker = getLayoutsWorker() if (!layoutsWorker) throw new Error("no layouts worker") - await layoutsWorker.processLayout(layoutKey) + await layoutsWorker.getLayout(layoutKey) const vanillaColumn = await layoutsDB.vanillaColumns.get(strLayoutKey) - console.log("awaited everything") if (!vanillaColumn) throw new Error(`no vanilla column for ${strLayoutKey}`) return vanillaColumn.vanillaColumn @@ -211,8 +208,6 @@ const ZStretch = ({ const { distance, direction, dx, dz, last } = detail - console.log(distance) - switch (direction) { case 1: { const clamped = -distance > length || distance > maxStretchLengthUp diff --git a/app/design/state/layouts.ts b/app/design/state/layouts.ts index 9678b022..da5d070b 100644 --- a/app/design/state/layouts.ts +++ b/app/design/state/layouts.ts @@ -40,7 +40,6 @@ export const useDnasLayout = (layoutsKey: HouseLayoutsKey): ColumnLayout => { return suspend(async () => { if (maybeLayout) { - console.log("maybeLayout") return maybeLayout } @@ -49,10 +48,9 @@ export const useDnasLayout = (layoutsKey: HouseLayoutsKey): ColumnLayout => { if (layoutsWorker === null) throw new Error(`layoutsWorker null in useDnasLayout`) - const layout = await layoutsWorker.processLayout(layoutsKey) + const layout = await layoutsWorker.getLayout(layoutsKey) layouts[serialKey] = layout - console.log("awaited layout") return layout }, [maybeLayout]) } diff --git a/app/design/ui-3d/XZPlane.tsx b/app/design/ui-3d/XZPlane.tsx index 318eb4be..e81c5e1f 100644 --- a/app/design/ui-3d/XZPlane.tsx +++ b/app/design/ui-3d/XZPlane.tsx @@ -28,7 +28,7 @@ const XZPlane = forwardRef((props, ref) => { localRef.current.position.setY(y) localRef.current.layers.set(RaycasterLayer.ENABLED) if (DEBUG) { - console.log(`DEBUG XZPlane: POINTER_DOWN CameraLayer.VISIBLE`) + console.debug(`DEBUG XZPlane: POINTER_DOWN CameraLayer.VISIBLE`) localRef.current.layers.enable(CameraLayer.VISIBLE) } }) @@ -36,7 +36,8 @@ const XZPlane = forwardRef((props, ref) => { usePointerUpListener(() => { if (!localRef.current) return localRef.current.layers.set(RaycasterLayer.DISABLED) - if (DEBUG) console.log(`DEBUG XZPlane: POINTER_UP RaycasterLayer.DISABLED`) + if (DEBUG) + console.debug(`DEBUG XZPlane: POINTER_UP RaycasterLayer.DISABLED`) }) const bind: any = useGesture<{ diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 26c060a1..bb76e748 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -3,14 +3,12 @@ import { Group } from "three" import { useHousesEvents } from "./events/houses" import useGestures from "./events/gestures" import useKeyTestInteractions from "./useKeyTestInteractions" -import useModeHandling from "./useModeHandling" import XZPlane from "../XZPlane" const FreshApp = () => { const rootRef = useRef(null) useHousesEvents(rootRef) - useModeHandling(rootRef) const bindAll = useGestures(rootRef) diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 3328a83e..5457740a 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -69,7 +69,7 @@ const useGestures = (rootRef: RefObject) => { // we could pre-process each gate - const onDragStretch: Handler<"drag", ThreeEvent> = ({ + const onDragStretch: Handler<"drag", ThreeEvent> = async ({ first, last, event, @@ -142,8 +142,7 @@ const useGestures = (rootRef: RefObject) => { switch (true) { case distance > lastDistance: { if (distance > vanillaColumnLength * columnsAddedToEnd) { - console.log("add vanilla column") - insertVanillaColumn(houseGroup, 1) + insertVanillaColumn(houseGroup, 1)() stretchData.current.columnsAddedToEnd++ } break @@ -156,7 +155,6 @@ const useGestures = (rootRef: RefObject) => { // distance < // vanillaColumnLength * columnsDelta + vanillaColumnLength // ) { - // console.log("subtract vanilla column") // stretchData.current.columnsDelta++ // } } diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index 8195ac37..460f76b1 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -65,7 +65,7 @@ export const useHousesEvents = (rootRef: RefObject) => { houseId, dnas, friendlyName, - }) + })() houseGroup.position.set(position.x, position.y, position.z) houseGroup.rotation.set(0, rotation, 0) diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index dcadc04c..79fd8628 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -20,7 +20,7 @@ import layoutsDB, { VanillaColumn, VanillaColumnsKey, } from "../../../../db/layouts" -import { A, Num, O, Ord, R, S, T } from "../../../../utils/functions" +import { A, Num, O, Ord, pipeLog, R, S, T } from "../../../../utils/functions" import { getLayoutsWorker } from "../../../../workers" import { getMaterial } from "../systems" import { @@ -404,7 +404,7 @@ export const createHouseGroup = ({ (): T.Task => async () => { const layoutsWorker = getLayoutsWorker() if (!layoutsWorker) throw new Error(`no layouts worker`) - return await layoutsWorker.processLayout({ + return await layoutsWorker.getLayout({ systemId, dnas, }) @@ -447,24 +447,24 @@ export const columnSorter = A.sort( export const insertVanillaColumn = (houseGroup: Group, direction: 1 | -1): T.Task => async () => { - const { levelTypes, systemId, columnCount, clippingPlanes } = + const { levelTypes, systemId, columnCount } = houseGroup.userData as HouseRootGroupUserData + const vanillaColumn = + vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] + + const vanillaColumnGroup = await createColumnGroup({ + systemId, + gridGroups: vanillaColumn.gridGroups, + columnIndex: -1, + })() + pipe( houseGroup.children, A.head, - O.map((zCenterHouseGroup) => async () => { + O.map((zCenterHouseGroup) => { const { children: columnGroups } = zCenterHouseGroup - const vanillaColumn = - vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] - - const vanillaColumnGroup = await createColumnGroup({ - systemId, - gridGroups: vanillaColumn.gridGroups, - columnIndex: -1, - })() - const vanillaColumnLength = vanillaColumnGroup.userData.length if (direction === 1) { diff --git a/app/design/ui-3d/fresh/useModeHandling.ts b/app/design/ui-3d/fresh/useModeHandling.ts deleted file mode 100644 index f6d99407..00000000 --- a/app/design/ui-3d/fresh/useModeHandling.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { invalidate } from "@react-three/fiber" -import { RefObject } from "react" -import { BoxGeometry, Group, Mesh, MeshBasicMaterial } from "three" -import { useSubscribeKey } from "../../../utils/hooks" -import { setVisibleOnly } from "../../../utils/three" -import siteCtx, { SiteCtxModeEnum } from "../../state/siteCtx" -import getUtils from "./util" - -const useModeHandling = (rootRef: RefObject) => { - const { applyToHouseRootGroup } = getUtils(rootRef) - - let i = 4 - - useSubscribeKey(siteCtx, "mode", () => { - const { mode, houseId, levelIndex } = siteCtx - - switch (mode) { - case SiteCtxModeEnum.Enum.SITE: - console.log("site mode") - break - case SiteCtxModeEnum.Enum.BUILDING: - if (houseId) - applyToHouseRootGroup(houseId, (houseRootGroup) => { - const newMesh = new Mesh( - new BoxGeometry(), - new MeshBasicMaterial({ color: "red" }) - ) - newMesh.position.y = i - i++ - console.log(i) - houseRootGroup.add(newMesh) - }) - invalidate() - console.log("building mode") - break - case SiteCtxModeEnum.Enum.LEVEL: - console.log("level mode") - break - } - }) -} - -export default useModeHandling diff --git a/app/design/ui-3d/grouped/GroupedHouse2.tsx b/app/design/ui-3d/grouped/GroupedHouse2.tsx index 014f451e..684dcee2 100644 --- a/app/design/ui-3d/grouped/GroupedHouse2.tsx +++ b/app/design/ui-3d/grouped/GroupedHouse2.tsx @@ -30,7 +30,6 @@ type Props = { } const GroupedHouse2 = (props: Props) => { - console.log("GroupedHouse2") const { house } = props const { systemId, houseId: houseId, position, rotation } = house const dnas = [...house.dnas] diff --git a/app/design/ui-3d/grouped/preview/PreviewHouses.tsx b/app/design/ui-3d/grouped/preview/PreviewHouses.tsx index 3d5465a7..9e5dc343 100644 --- a/app/design/ui-3d/grouped/preview/PreviewHouses.tsx +++ b/app/design/ui-3d/grouped/preview/PreviewHouses.tsx @@ -21,7 +21,6 @@ const PreviewHouses = (props: Props) => { {pipe( dnaPreviews, R.collect(S.Ord)((k, { value }) => { - // console.log({ houseId, k }) return ( { const groupRef = useRef(null) - useEffect(() => { - const layoutsWorker = getLayoutsWorker() - if (!layoutsWorker) { - console.log("no layouts workers") - return - } - layoutsWorker.processZStretchLayout({ direction, i, layoutKey }) - }, [direction, houseId, i, layoutKey]) + // useEffect(() => { + // const layoutsWorker = getLayoutsWorker() + // if (!layoutsWorker) { + // return + // } + // layoutsWorker.processZStretchLayout({ direction, i, layoutKey }) + // }, [direction, houseId, i, layoutKey]) useZStretchHouseListener((detail) => { if (houseId !== detail.houseId) return diff --git a/app/design/ui/menu/ContextMenuEntry.tsx b/app/design/ui/menu/ContextMenuEntry.tsx index 9a2bd9a1..c6501cb9 100644 --- a/app/design/ui/menu/ContextMenuEntry.tsx +++ b/app/design/ui/menu/ContextMenuEntry.tsx @@ -29,8 +29,6 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { if (!selected) throw new Error("null selected") - console.log({ selected }) - const props: ContextMenuProps = { pageX, pageY, diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index f294c2db..8a3c7f4e 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -438,43 +438,6 @@ export const columnLayoutToDnas = ( RA.flatten ) as string[] -const processZStretchLayout = async ({ - layoutKey, - direction, - i, -}: { - layoutKey: HouseLayoutsKey - direction: number - i: number -}) => { - const { systemId } = layoutKey - const strLayoutKey = getHouseLayoutsKey(layoutKey) - const layout = await layoutsDB.houseLayouts.get(strLayoutKey) - if (!layout) { - console.log(`no layout for ${strLayoutKey}`) - return - } - const vanillaColumn = await layoutsDB.vanillaColumns.get(strLayoutKey) - if (!vanillaColumn) { - console.log(`no layout for ${strLayoutKey}`) - return - } - - const { startColumn, midColumns, endColumn } = splitColumns(layout.layout) - - const nextDnas = columnLayoutToDnas([ - startColumn, - ...A.replicate(i, vanillaColumn.vanillaColumn), - ...midColumns, - endColumn, - ]) - - getLayout({ - systemId, - dnas: nextDnas, - }) -} - if (!isSSR()) { // CONT liveQuery(async () => { @@ -702,8 +665,7 @@ const getVanillaColumn = async (key: VanillaColumnsKey) => { const api = { postLayout, postLayouts, - processLayout: getLayout, - processZStretchLayout, + getLayout, syncModels, getVanillaColumn, } From edaf119af7acf9042b3d4335ecf86acaf7fea9e9 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 3 Aug 2023 10:57:43 +0100 Subject: [PATCH 074/132] wip z-stretch+ re-recovered --- app/design/state/gestures/index.ts | 6 +- app/design/ui-3d/fresh/dimensions.ts | 41 +- app/design/ui-3d/fresh/events/gestures.ts | 20 +- app/design/ui-3d/fresh/events/houses.ts | 6 +- app/design/ui-3d/fresh/helpers/layouts.ts | 407 ++++++++---------- .../ui-3d/fresh/helpers/sceneQueries.ts | 49 ++- app/design/ui-3d/fresh/helpers/stretchZ.ts | 137 ++++++ app/design/ui-3d/fresh/useCuts.ts | 2 +- .../ui-3d/fresh/useKeyTestInteractions.ts | 84 ++-- app/design/ui-3d/fresh/userData.ts | 65 ++- app/design/ui-3d/fresh/util.ts | 27 -- 11 files changed, 511 insertions(+), 333 deletions(-) create mode 100644 app/design/ui-3d/fresh/helpers/stretchZ.ts delete mode 100644 app/design/ui-3d/fresh/util.ts diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index 41e98244..fc031b97 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -10,7 +10,7 @@ import { isMesh, useRotations } from "~/utils/three" import { ElementMeshUserData, GridGroupUserData, - HouseRootGroupUserData, + HouseTransformsGroupUserData, UserDataTypeEnum, } from "../../ui-3d/fresh/userData" import { setCameraControlsEnabled } from "../camera" @@ -297,10 +297,10 @@ export const useGestures = (): any => const { levelIndex } = parent.parent.userData as GridGroupUserData if ( parent.parent.parent?.parent?.parent?.userData.type === - UserDataTypeEnum.Enum.HouseRootGroup + UserDataTypeEnum.Enum.HouseTransformsGroup ) { const { houseId } = parent.parent.parent?.parent?.parent - ?.userData as HouseRootGroupUserData + ?.userData as HouseTransformsGroupUserData downMode({ houseId, levelIndex }) } } diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 0c367845..7d0a0a1f 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -15,7 +15,11 @@ import userDB from "../../../db/user" import { A, O } from "../../../utils/functions" import { columnSorter } from "./helpers/layouts" import { - HouseRootGroupUserData, + getActiveHouseUserData, + getActiveLayoutGroup, +} from "./helpers/sceneQueries" +import { + HouseTransformsGroupUserData, ModuleGroupUserData, UserDataTypeEnum, } from "./userData" @@ -38,21 +42,20 @@ const renderOBB = (obb: OBB, scene: Object3D) => { lastMesh = mesh } -export const updateHouseOBB = (houseGroup: Group) => { - const houseGroupUserData = houseGroup.userData as HouseRootGroupUserData - - const { width, height, length } = houseGroupUserData +export const updateHouseOBB = (houseTransformsGroup: Group) => { + const { width, height, length } = getActiveHouseUserData(houseTransformsGroup) + const activeLayoutGroup = getActiveLayoutGroup(houseTransformsGroup) - const { x, y, z } = houseGroup.position + const { x, y, z } = houseTransformsGroup.position const center = new Vector3(x, y + height / 2, z) const halfSize = new Vector3(width / 2, height / 2, length / 2) - const rotation = new Matrix3().setFromMatrix4(houseGroup.matrix) + const rotation = new Matrix3().setFromMatrix4(houseTransformsGroup.matrix) - houseGroupUserData.obb.set(center, halfSize, rotation) + activeLayoutGroup.userData.obb.set(center, halfSize, rotation) - if (DEBUG && houseGroup.parent) - renderOBB(houseGroupUserData.obb, houseGroup.parent) + if (DEBUG && houseTransformsGroup.parent) + renderOBB(activeLayoutGroup.userData.obb, houseTransformsGroup.parent) } export const updateHouseWidth = (houseGroup: Group) => {} @@ -63,7 +66,7 @@ export const updateClippingPlanes = (houseGroup: Group) => { const { clippingPlanes, clippingPlanes: [planeX, planeY, planeZ], - } = houseGroup.userData as HouseRootGroupUserData + } = houseGroup.userData as HouseTransformsGroupUserData houseGroup.updateMatrix() @@ -124,16 +127,16 @@ const updateDnas = (houseGroup: Group) => { houseGroup.userData.dnas = result.flat() } -export const updateEverything = (houseGroup: Group) => { - updateHouseLength(houseGroup) - updateHouseOBB(houseGroup) - updateClippingPlanes(houseGroup) - updateDnas(houseGroup) +export const updateEverything = (houseTransformsGroup: Group) => { + updateHouseLength(houseTransformsGroup) + updateHouseOBB(houseTransformsGroup) + updateClippingPlanes(houseTransformsGroup) + updateDnas(houseTransformsGroup) - const { dnas, houseId } = houseGroup.userData as HouseRootGroupUserData + const { dnas, houseId } = getActiveHouseUserData(houseTransformsGroup) - const rotation = houseGroup.rotation.y - const position = houseGroup.position + const rotation = houseTransformsGroup.rotation.y + const position = houseTransformsGroup.position userDB.houses.update(houseId, { dnas, diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 5457740a..6524185d 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -12,15 +12,16 @@ import { openMenu } from "../../../state/menu" import pointer from "../../../state/pointer" import scope, { ScopeItem } from "../../../state/scope" import siteCtx, { downMode, SiteCtxModeEnum } from "../../../state/siteCtx" -import { insertVanillaColumn } from "../helpers/layouts" import { + getActiveHouseUserData, getHouseGroupColumns, handleColumnGroupParentQuery, rootHouseGroupParentQuery, } from "../helpers/sceneQueries" +import { insertVanillaColumn } from "../helpers/stretchZ" import { GridGroupUserData, - HouseRootGroupUserData, + HouseTransformsGroupUserData, StretchHandleMeshUserData, UserDataTypeEnum, } from "../userData" @@ -105,6 +106,8 @@ const useGestures = (rootRef: RefObject) => { columnsAddedToEnd, } = stretchData.current + const { vanillaColumn } = getActiveHouseUserData(houseGroup) + const { direction, axis } = handleObject.userData as StretchHandleMeshUserData @@ -112,10 +115,6 @@ const useGestures = (rootRef: RefObject) => { case "z": switch (direction) { case 1: { - const vanillaColumnLength = ( - houseGroup.userData as HouseRootGroupUserData - ).vanillaColumn.length - const [x1, z1] = pointer.xz const distanceVector = new Vector3(x1, 0, z1).sub(point0) distanceVector.applyAxisAngle( @@ -141,14 +140,15 @@ const useGestures = (rootRef: RefObject) => { switch (true) { case distance > lastDistance: { - if (distance > vanillaColumnLength * columnsAddedToEnd) { + if (distance > vanillaColumn.length * columnsAddedToEnd) { + console.log("am i even ever") insertVanillaColumn(houseGroup, 1)() stretchData.current.columnsAddedToEnd++ } break } case distance < lastDistance: { - if (distance < vanillaColumnLength * columnsAddedToEnd) { + if (distance < vanillaColumn.length * columnsAddedToEnd) { console.log("going down") } // if ( @@ -376,10 +376,10 @@ const useGestures = (rootRef: RefObject) => { const { levelIndex } = parent.parent.userData as GridGroupUserData if ( parent.parent.parent?.parent?.parent?.userData.type === - UserDataTypeEnum.Enum.HouseRootGroup + UserDataTypeEnum.Enum.HouseTransformsGroup ) { const { houseId } = parent.parent.parent?.parent?.parent - ?.userData as HouseRootGroupUserData + ?.userData as HouseTransformsGroupUserData downMode({ houseId, levelIndex }) } } diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index 460f76b1..76b84547 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -7,9 +7,9 @@ import { HouseType } from "../../../../../server/data/houseTypes" import userDB, { House } from "../../../../db/user" import { A } from "../../../../utils/functions" import { setVisibleAndRaycast } from "../../../../utils/three" -import { createHouseGroup } from "../helpers/layouts" import { nanoid } from "nanoid" import { floor } from "../../../../utils/math" +import { createInitialHouse } from "../helpers/layouts" const ADD_HOUSE_INTENT_EVENT = "AddHouseIntentEvent" const ADD_HOUSE_EVENT = "AddHouseEvent" @@ -58,13 +58,15 @@ export const useHousesEvents = (rootRef: RefObject) => { friendlyName, position, rotation, + houseTypeId, } = house - const houseGroup = await createHouseGroup({ + const houseGroup = await createInitialHouse({ systemId, houseId, dnas, friendlyName, + houseTypeId, })() houseGroup.position.set(position.x, position.y, position.z) diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 79fd8628..212d6432 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -20,14 +20,15 @@ import layoutsDB, { VanillaColumn, VanillaColumnsKey, } from "../../../../db/layouts" -import { A, Num, O, Ord, pipeLog, R, S, T } from "../../../../utils/functions" +import { A, Num, O, Ord, R, S, T } from "../../../../utils/functions" import { getLayoutsWorker } from "../../../../workers" import { getMaterial } from "../systems" import { ColumnGroupUserData, ElementMeshUserData, GridGroupUserData, - HouseRootGroupUserData, + HouseLayoutGroupUserData, + HouseTransformsGroupUserData, ModuleGroupUserData, UserDataTypeEnum, } from "../userData" @@ -283,31 +284,26 @@ liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( } ) -export const houseLayoutToHouseGroup = ({ +export const createLayoutGroup = ({ systemId, houseId, + dnas, houseLayout, }: { systemId: string houseId: string + dnas: string[] houseLayout: ColumnLayout -}): T.Task => +}) => pipe( createColumnGroups({ systemId, houseLayout, }), - T.chain((columnGroups) => async () => { - const topLevelHouseGroup = new Group() - const zCenterHouseGroup = new Group() + T.chain((columnGroups) => { + const layoutGroup = new Group() - const BIG_NUMBER = 999 - - const clippingPlanes: Plane[] = [ - new Plane(new Vector3(BIG_NUMBER, 0, 0), 0), - new Plane(new Vector3(0, BIG_NUMBER, 0), 0), - new Plane(new Vector3(0, 0, BIG_NUMBER), 0), - ] + const columnCount = columnGroups.length const sectionType = houseLayout[0].gridGroups[0].modules[0].module.structuredDna.sectionType @@ -324,79 +320,120 @@ export const houseLayoutToHouseGroup = ({ const obb = new OBB() const levelTypes = houseLayoutToLevelTypes(houseLayout) - const vanillaColumn = await getVanillaColumn({ systemId, levelTypes })() - - const houseGroupUserData: Partial = { - type: UserDataTypeEnum.Enum.HouseRootGroup, - systemId, - houseId, - width, - length, - height, - modifiedMaterials: {}, - obb, - clippingPlanes, - sectionType, - levelTypes, - vanillaColumn, - columnCount: columnGroups.length, - houseLayout, - } - topLevelHouseGroup.userData = houseGroupUserData - - zCenterHouseGroup.add(...columnGroups) - zCenterHouseGroup.position.setZ(-length / 2) - zCenterHouseGroup.userData.type = - UserDataTypeEnum.Enum.HouseColumnsContainerGroup + return pipe( + getVanillaColumn({ systemId, levelTypes }), + T.map((vanillaColumn) => { + const userData: HouseLayoutGroupUserData = { + type: UserDataTypeEnum.Enum.HouseLayoutGroup, + dnas, + houseLayout, + columnCount, + sectionType, + levelTypes, + width, + height, + length, + obb, + modifiedMaterials: {}, + vanillaColumn, + } + layoutGroup.userData = userData + layoutGroup.position.setZ(-length / 2) + layoutGroup.add(...columnGroups) - topLevelHouseGroup.add(zCenterHouseGroup) + pipe( + columnGroups, + A.findFirst((x) => x.userData.columnIndex === 0), + O.map((firstColumn) => { + const stretchHandle = createStretchHandle({ + houseId, + axis: "z", + direction: -1, + length, + width, + }) + firstColumn.add(stretchHandle) + }) + ) - pipe( - columnGroups, - A.findFirst((x) => x.userData.columnIndex === 0), - O.map((firstColumn) => { - const stretchHandle = createStretchHandle({ - houseId, - axis: "z", - direction: -1, - length, - width, - }) - firstColumn.add(stretchHandle) - }) - ) + pipe( + columnGroups, + A.findFirst( + (x) => x.userData.columnIndex === columnGroups.length - 1 + ), + O.map((lastColumn) => { + const stretchHandle = createStretchHandle({ + houseId, + axis: "z", + direction: 1, + length, + width, + }) + stretchHandle.position.z += lastColumn.userData.length + lastColumn.add(stretchHandle) + }) + ) - pipe( - columnGroups, - A.findFirst((x) => x.userData.columnIndex === columnGroups.length - 1), - O.map((lastColumn) => { - const stretchHandle = createStretchHandle({ - houseId, - axis: "z", - direction: 1, - length, - width, - }) - stretchHandle.position.z += lastColumn.userData.length - lastColumn.add(stretchHandle) + return layoutGroup }) ) - - return topLevelHouseGroup }) ) -export const createHouseGroup = ({ +// export const createTransformsGroup = () => {} + +// export const houseLayoutToHouseGroup = ({ +// systemId, +// houseId, +// houseLayout, +// }: { +// systemId: string +// houseId: string +// houseLayout: ColumnLayout +// }): T.Task => +// pipe( +// createColumnGroups({ +// systemId, +// houseLayout, +// }), +// T.chain((columnGroups) => async () => { +// const transformsGroup = new Group() +// const layoutGroup = new Group() + +// const BIG_NUMBER = 999 + +// const clippingPlanes: Plane[] = [ +// new Plane(new Vector3(BIG_NUMBER, 0, 0), 0), +// new Plane(new Vector3(0, BIG_NUMBER, 0), 0), +// new Plane(new Vector3(0, 0, BIG_NUMBER), 0), +// ] + +// const houseTransformsGroupUserData: Partial = +// { +// type: UserDataTypeEnum.Enum.HouseTransformsGroup, +// systemId, +// houseId, +// clippingPlanes, +// } +// transformsGroup.userData = houseTransformsGroupUserData + +// layoutGroup.add(...columnGroups) +// layoutGroup.position.setZ(-length / 2) +// layoutGroup.userData.type = UserDataTypeEnum.Enum.HouseLayoutGroup + +// transformsGroup.add(layoutGroup) + +// return transformsGroup +// }) +// ) + +export const getHouseLayout = ({ systemId, - houseId, dnas, - friendlyName, }: { systemId: string - houseId: string dnas: string[] - friendlyName: string -}): T.Task => +}): T.Task => pipe( houseLayouts, R.lookup(getHouseLayoutsKey({ systemId, dnas })), @@ -410,22 +447,87 @@ export const createHouseGroup = ({ }) }, (houseLayout) => T.of(houseLayout) - ), + ) + ) + +export const createInitialHouse = ({ + systemId, + houseId, + dnas, + friendlyName, + houseTypeId, +}: { + systemId: string + houseId: string + dnas: string[] + friendlyName: string + houseTypeId: string +}): T.Task => + pipe( + getHouseLayout({ systemId, dnas }), T.chain((houseLayout) => - houseLayoutToHouseGroup({ systemId, houseId, houseLayout }) + createLayoutGroup({ houseLayout, dnas, systemId, houseId }) ), - T.map((group) => { - group.userData.friendlyName = friendlyName - return group + T.map((layoutGroup) => { + const transformsGroup = new Group() + + const BIG_NUMBER = 999 + + const clippingPlanes: Plane[] = [ + new Plane(new Vector3(BIG_NUMBER, 0, 0), 0), + new Plane(new Vector3(0, BIG_NUMBER, 0), 0), + new Plane(new Vector3(0, 0, BIG_NUMBER), 0), + ] + + const houseTransformsGroupUserData: HouseTransformsGroupUserData = { + type: UserDataTypeEnum.Enum.HouseTransformsGroup, + systemId, + houseId, + clippingPlanes, + friendlyName, + activeChildUuid: layoutGroup.uuid, + houseTypeId, + } + transformsGroup.userData = houseTransformsGroupUserData + transformsGroup.add(layoutGroup) + + return transformsGroup }) ) -export const addColumnToHouse = (houseGroup: Object3D, columnGroup: Object3D) => - pipe( - houseGroup.children, - A.head, - O.map((zCenterHouseGroup) => void zCenterHouseGroup.add(columnGroup)) - ) +// export const createHouseGroup = ({ +// systemId, +// houseId, +// dnas, +// friendlyName, +// }: { +// systemId: string +// houseId: string +// dnas: string[] +// friendlyName: string +// }): T.Task => +// pipe( +// houseLayouts, +// R.lookup(getHouseLayoutsKey({ systemId, dnas })), +// O.match( +// (): T.Task => async () => { +// const layoutsWorker = getLayoutsWorker() +// if (!layoutsWorker) throw new Error(`no layouts worker`) +// return await layoutsWorker.getLayout({ +// systemId, +// dnas, +// }) +// }, +// (houseLayout) => T.of(houseLayout) +// ), +// T.chain((houseLayout) => +// houseLayoutToHouseGroup({ systemId, houseId, houseLayout }) +// ), +// T.map((group) => { +// group.userData.friendlyName = friendlyName +// return group +// }) +// ) export const removeColumnFromHouse = ( houseGroup: Object3D, @@ -444,141 +546,6 @@ export const columnSorter = A.sort( ) ) -export const insertVanillaColumn = - (houseGroup: Group, direction: 1 | -1): T.Task => - async () => { - const { levelTypes, systemId, columnCount } = - houseGroup.userData as HouseRootGroupUserData - - const vanillaColumn = - vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] - - const vanillaColumnGroup = await createColumnGroup({ - systemId, - gridGroups: vanillaColumn.gridGroups, - columnIndex: -1, - })() - - pipe( - houseGroup.children, - A.head, - O.map((zCenterHouseGroup) => { - const { children: columnGroups } = zCenterHouseGroup - - const vanillaColumnLength = vanillaColumnGroup.userData.length - - if (direction === 1) { - pipe( - columnGroups, - A.filter((x) => x.userData.columnIndex >= columnCount - 2), - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), - ([penultimateColumnGroup, endColumnGroup]) => { - vanillaColumnGroup.position.setZ( - penultimateColumnGroup.position.z + - penultimateColumnGroup.userData.length / 2 + - vanillaColumnLength / 2 - ) - addColumnToHouse(houseGroup, vanillaColumnGroup) - - vanillaColumnGroup.userData.columnIndex = - penultimateColumnGroup.userData.columnIndex + 1 - - endColumnGroup.userData.columnIndex++ - - houseGroup.userData.columnCount = columnCount + 1 - } - ) - } else if (direction === -1) { - pipe( - columnGroups, - columnSorter, - ([startColumnGroup, ...restColumnGroups]) => { - for (let columnGroup of restColumnGroups) { - columnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) - columnGroup.userData.columnIndex++ - } - - vanillaColumnGroup.userData.columnIndex = 1 - vanillaColumnGroup.position.setZ( - startColumnGroup.position.z + - startColumnGroup.userData.length + - vanillaColumnLength / 2 - ) - addColumnToHouse(houseGroup, vanillaColumnGroup) - - houseGroup.userData.columnCount = columnCount + 1 - } - ) - } - }) - ) - } - -export const subtractPenultimateColumn = ( - houseGroup: Group, - direction: 1 | -1 -) => { - pipe( - houseGroup.children, - A.head, - O.map(({ children }) => { - const columnCount: number = houseGroup.userData.columnCount - - if (columnCount <= 3) return - - if (direction === 1) { - pipe( - children, - A.filter((x) => x.userData.columnIndex >= columnCount - 2), - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), - ([penultimateColumnGroup, endColumnGroup]) => { - endColumnGroup.position.sub( - new Vector3(0, 0, penultimateColumnGroup.userData.length) - ) - - removeColumnFromHouse(houseGroup, penultimateColumnGroup) - - houseGroup.userData.columnCount = columnCount - 1 - endColumnGroup.userData.columnIndex-- - } - ) - } else if (direction === -1) { - pipe( - children, - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), - ([_, secondColumnGroup, ...restColumnGroups]) => { - const subV = new Vector3(0, 0, secondColumnGroup.userData.length) - - restColumnGroups.forEach((columnGroup) => { - columnGroup.position.sub(subV) - columnGroup.userData.columnIndex-- - }) - - removeColumnFromHouse(houseGroup, secondColumnGroup) - - houseGroup.userData.columnCount = columnCount - 1 - } - ) - } - }) - ) -} - export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => pipe( columnLayout, diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 64cd7ff4..28575017 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -2,7 +2,11 @@ import { pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { Group, Object3D } from "three" import { A, O, someOrError } from "../../../../utils/functions" -import { ColumnGroupUserData, UserDataTypeEnum } from "../userData" +import { + HouseLayoutGroupUserData, + HouseTransformsGroupUserData, + UserDataTypeEnum, +} from "../userData" export const rootHouseGroupChildQuery = ( rootRef: RefObject, @@ -17,12 +21,14 @@ export const rootHouseGroupChildQuery = ( export const rootHouseGroupParentQuery = (object: Object3D) => { let x = object while (x.parent) { - if (x.userData.type === UserDataTypeEnum.Enum.HouseRootGroup) { + if (x.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup) { return x as Group } x = x.parent } - throw new Error(`No HouseRootGroup parent found for ${object}`) + throw new Error( + `No ${UserDataTypeEnum.Enum.HouseTransformsGroup} parent found for ${object}` + ) } export const handleColumnGroupParentQuery = (object: Object3D) => { @@ -33,7 +39,9 @@ export const handleColumnGroupParentQuery = (object: Object3D) => { } x = x.parent } - throw new Error(`No HouseRootGroup parent found for ${object}`) + throw new Error( + `No ${UserDataTypeEnum.Enum.ColumnGroup} parent found for ${object}` + ) } export const getHouseGroupColumns = (houseGroup: Group) => @@ -44,6 +52,39 @@ export const getHouseGroupColumns = (houseGroup: Group) => someOrError("no columns container in house group") ) +export const getActiveLayoutGroup = (houseTransformsGroup: Group): Group => + pipe( + houseTransformsGroup.children, + A.findFirst( + (x) => + x.uuid === + (houseTransformsGroup.userData as HouseTransformsGroupUserData) + .activeChildUuid + ), + someOrError(`getActiveLayoutGroup failure`) + ) as Group + +export const getActiveHouseUserData = (houseTransformsGroup: Group) => + pipe( + houseTransformsGroup.children, + A.findFirstMap((x) => + x.uuid === + (houseTransformsGroup.userData as HouseTransformsGroupUserData) + .activeChildUuid + ? O.some({ + ...(houseTransformsGroup.userData as HouseTransformsGroupUserData), + ...(x.userData as HouseLayoutGroupUserData), + }) + : O.none + ), + someOrError(`getActiveHouseUserData failure`) + ) + +export const getLayoutGroupColumnGroups = (layoutGroup: Group): Group[] => + layoutGroup.children.filter( + (x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup + ) as Group[] + // should just be able to use house length // export const getLastColumnEndZ = (houseGroup: Group) => { diff --git a/app/design/ui-3d/fresh/helpers/stretchZ.ts b/app/design/ui-3d/fresh/helpers/stretchZ.ts new file mode 100644 index 00000000..1690a642 --- /dev/null +++ b/app/design/ui-3d/fresh/helpers/stretchZ.ts @@ -0,0 +1,137 @@ +import { pipe } from "fp-ts/lib/function" +import { Group, Object3D, Vector3 } from "three" +import { A, Num, Ord, T } from "../../../../utils/functions" +import { decrementColumnCount, incrementColumnCount } from "../userData" +import { columnSorter, createColumnGroup, getVanillaColumn } from "./layouts" +import { + getActiveHouseUserData, + getActiveLayoutGroup, + getLayoutGroupColumnGroups, +} from "./sceneQueries" + +export const insertVanillaColumn = ( + houseTransformsGroup: Group, + direction: 1 | -1 +) => { + const { systemId, levelTypes, columnCount } = + getActiveHouseUserData(houseTransformsGroup) + + // const vanillaColumn = + // vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] + + return pipe( + getVanillaColumn({ systemId, levelTypes }), + T.chain(({ gridGroups }) => + pipe(createColumnGroup({ systemId, gridGroups, columnIndex: -1 })) + ), + T.map((vanillaColumnGroup) => { + const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) + const vanillaColumnLength = vanillaColumnGroup.userData.length + + if (direction === 1) { + pipe( + getLayoutGroupColumnGroups(layoutGroup), + A.filter((x) => x.userData.columnIndex >= columnCount - 2), + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ), + ([penultimateColumnGroup, endColumnGroup]) => { + vanillaColumnGroup.position.setZ( + penultimateColumnGroup.position.z + + penultimateColumnGroup.userData.length / 2 + + vanillaColumnLength / 2 + ) + layoutGroup.add(vanillaColumnGroup) + + vanillaColumnGroup.userData.columnIndex = + penultimateColumnGroup.userData.columnIndex + 1 + + endColumnGroup.userData.columnIndex++ + + incrementColumnCount(layoutGroup) + } + ) + } else if (direction === -1) { + pipe( + getLayoutGroupColumnGroups(layoutGroup), + columnSorter, + ([startColumnGroup, ...restColumnGroups]) => { + for (let columnGroup of restColumnGroups) { + columnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) + columnGroup.userData.columnIndex++ + } + + vanillaColumnGroup.userData.columnIndex = 1 + vanillaColumnGroup.position.setZ( + startColumnGroup.position.z + + startColumnGroup.userData.length + + vanillaColumnLength / 2 + ) + layoutGroup.add(vanillaColumnGroup) + + decrementColumnCount(layoutGroup) + } + ) + } + }) + ) +} + +export const subtractPenultimateColumn = ( + houseGroup: Group, + direction: 1 | -1 +) => { + const layoutGroup = getActiveLayoutGroup(houseGroup) + const columnGroups = getLayoutGroupColumnGroups(layoutGroup) + const { columnCount } = getActiveHouseUserData(houseGroup) + + if (columnCount <= 3) return + + if (direction === 1) { + pipe( + columnGroups, + A.filter((x) => x.userData.columnIndex >= columnCount - 2), + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ), + ([penultimateColumnGroup, endColumnGroup]) => { + endColumnGroup.position.sub( + new Vector3(0, 0, penultimateColumnGroup.userData.length) + ) + + layoutGroup.remove(penultimateColumnGroup) + + decrementColumnCount(layoutGroup) + endColumnGroup.userData.columnIndex-- + } + ) + } else if (direction === -1) { + pipe( + columnGroups, + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ), + ([_, secondColumnGroup, ...restColumnGroups]) => { + const subV = new Vector3(0, 0, secondColumnGroup.userData.length) + + restColumnGroups.forEach((columnGroup) => { + columnGroup.position.sub(subV) + columnGroup.userData.columnIndex-- + }) + + layoutGroup.remove(secondColumnGroup) + + decrementColumnCount(layoutGroup) + } + ) + } +} diff --git a/app/design/ui-3d/fresh/useCuts.ts b/app/design/ui-3d/fresh/useCuts.ts index e5cf041d..746dc1f2 100644 --- a/app/design/ui-3d/fresh/useCuts.ts +++ b/app/design/ui-3d/fresh/useCuts.ts @@ -19,7 +19,7 @@ const useCuts = (rootRef: RefObject) => { switch (userData.type) { case UserDataTypeEnum.Enum.ElementMesh: break - case UserDataTypeEnum.Enum.HouseRootGroup: + case UserDataTypeEnum.Enum.HouseTransformsGroup: // TypeScript knows that userData is of type HouseModuleGroupUserData in this block break } diff --git a/app/design/ui-3d/fresh/useKeyTestInteractions.ts b/app/design/ui-3d/fresh/useKeyTestInteractions.ts index 4c998b9b..f2a8859e 100644 --- a/app/design/ui-3d/fresh/useKeyTestInteractions.ts +++ b/app/design/ui-3d/fresh/useKeyTestInteractions.ts @@ -1,13 +1,19 @@ +import { liveQuery } from "dexie" +import { pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { useKey } from "react-use" import { Group, Object3D, Vector3 } from "three" +import layoutsDB from "../../../db/layouts" +import { A, O, R } from "../../../utils/functions" import { PI } from "../../../utils/math" import { yAxis } from "../../../utils/three" import { updateEverything } from "./dimensions" +import { createLayoutGroup } from "./helpers/layouts" +import { getActiveHouseUserData } from "./helpers/sceneQueries" import { insertVanillaColumn, subtractPenultimateColumn, -} from "./helpers/layouts" +} from "./helpers/stretchZ" import { UserDataTypeEnum } from "./userData" const useKeyTestInteractions = (rootRef: RefObject) => { @@ -15,21 +21,21 @@ const useKeyTestInteractions = (rootRef: RefObject) => { return ( rootRef.current?.children.filter( (x: Object3D): x is Group => - x.userData.type === UserDataTypeEnum.Enum.HouseRootGroup + x.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup ) ?? [] ) } - useKey("z", () => { + useKey("z", async () => { for (let houseGroup of getHouseGroups()) { - insertVanillaColumn(houseGroup, 1) + await insertVanillaColumn(houseGroup, 1)() updateEverything(houseGroup) } }) - useKey("Z", () => { + useKey("Z", async () => { for (let houseGroup of getHouseGroups()) { - insertVanillaColumn(houseGroup, -1) + await insertVanillaColumn(houseGroup, -1)() updateEverything(houseGroup) } }) @@ -78,6 +84,11 @@ const useKeyTestInteractions = (rootRef: RefObject) => { } }) + useKey("x", () => { + for (let houseGroup of getHouseGroups()) { + } + }) + // toggle stretch width first pass // useKey("x", () => { // // console.log(liveHouses) @@ -99,7 +110,7 @@ const useKeyTestInteractions = (rootRef: RefObject) => { // console.log("swapping groups") // stretchXGroups[ - // (liveHouseGroup.userData as HouseRootGroupUserData).sectionType + // (liveHouseGroup.userData as HouseTransformsGroupUserData).sectionType // ] = liveHouseGroup // liveHouses[houseId] = firstGroup @@ -116,28 +127,51 @@ const useKeyTestInteractions = (rootRef: RefObject) => { // } // }) - // liveQuery(() => layoutsDB.altSectionTypeLayouts.toArray()).subscribe( - // (data) => { + liveQuery(() => layoutsDB.altSectionTypeLayouts.toArray()).subscribe( + (data) => { + const houseGroups = getHouseGroups() + for (const { houseId, altSectionTypeLayouts } of data) { + pipe( + houseGroups, + A.findFirst((houseGroup) => houseGroup.userData.houseId === houseId), + O.chain((houseTransformsGroup) => + pipe( + houseTransformsGroup.children, + A.head, + O.map((houseLayoutGroup) => + pipe( + altSectionTypeLayouts, + R.map(({ layout: houseLayout }) => { + const { systemId, dnas } = + getActiveHouseUserData(houseTransformsGroup) + + createLayoutGroup({ + systemId, + dnas, + houseId, + houseLayout, + }) + + // probably needs to be the layout group + // houseLayoutToHouseGroup({ + // systemId, + // houseId, + // houseLayout, + // }) + }) + ) + ) + ) + ) + ) + } + } + ) // for (const { houseId, altSectionTypeLayouts } of data) { // pipe( // liveHouses, // R.lookup(houseId), // O.map((liveHouseGroup) => { - // const layoutTasks: Record> = pipe( - // altSectionTypeLayouts, - // R.map(({ layout: houseLayout }) => { - // const { systemId, friendlyName } = - // liveHouseGroup.userData as HouseRootGroupUserData - // // const houseLayout = altSectionTypeLayouts[] - - // return () => - // houseLayoutToHouseGroup({ - // systemId, - // houseId, - // houseLayout, - // }) - // }) - // ) // const layoutsTask = pipe( // layoutTasks, @@ -178,8 +212,6 @@ const useKeyTestInteractions = (rootRef: RefObject) => { // }) // ) // } - // } - // ) } export default useKeyTestInteractions diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 249cbfe3..e23a7cc9 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -1,20 +1,24 @@ -import { Plane } from "three" +import { Group, Plane } from "three" import { OBB } from "three-stdlib" import { z } from "zod" import { ColumnLayout, VanillaColumn } from "../../../db/layouts" -// HouseRootGroup has -// -> HouseColumnsContainerGroup as singleton child has -// -> ColumnsGroup's as children has -// -> GridGroup's as children has -// -> ModuleGroup's as children has +// HouseTransformsGroup has + +// -> HouseLayoutGroup's as children +// (alternative layouts; +// visibility/raycasting disabled +// except 1) +// -> ColumnsGroup's as children have +// -> GridGroup's as children have +// -> ModuleGroup's as children have // -> ElementMesh's as children // -> Stretch Handles // -> Rotate Handles export const UserDataTypeEnum = z.enum([ - "HouseRootGroup", - "HouseColumnsContainerGroup", + "HouseTransformsGroup", + "HouseLayoutGroup", "ColumnGroup", "GridGroup", "ModuleGroup", @@ -24,30 +28,31 @@ export const UserDataTypeEnum = z.enum([ ]) export type UserDataTypeEnum = z.infer -export type HouseRootGroupUserData = { +export type HouseTransformsGroupUserData = { // all - type: typeof UserDataTypeEnum.Enum.HouseRootGroup + type: typeof UserDataTypeEnum.Enum.HouseTransformsGroup systemId: string houseId: string houseTypeId: string friendlyName: string clippingPlanes: Plane[] + activeChildUuid: string // preview specific +} + +export type HouseLayoutGroupUserData = { + type: typeof UserDataTypeEnum.Enum.HouseLayoutGroup + dnas: string[] + houseLayout: ColumnLayout + vanillaColumn: VanillaColumn + levelTypes: string[] + obb: OBB height: number length: number width: number - obb: OBB columnCount: number sectionType: string - levelTypes: string[] modifiedMaterials: Record - dnas: string[] - houseLayout: ColumnLayout - vanillaColumn: VanillaColumn -} - -export type HouseColumnsContainerUserData = { - type: typeof UserDataTypeEnum.Enum.HouseColumnsContainerGroup } export type ColumnGroupUserData = { @@ -76,6 +81,8 @@ export type ElementMeshUserData = { ifcTag: string } +// --- HANDLES --- + export type StretchHandleMeshUserData = { type: typeof UserDataTypeEnum.Enum.StretchHandleMesh axis: "z" | "x" @@ -87,12 +94,28 @@ export type RotateHandleMeshUserData = { type: typeof UserDataTypeEnum.Enum.RotateHandleMesh } +// --- + export type UserData = | ElementMeshUserData | ModuleGroupUserData | GridGroupUserData | ColumnGroupUserData - | HouseColumnsContainerUserData - | HouseRootGroupUserData + | HouseLayoutGroupUserData + | HouseTransformsGroupUserData | StretchHandleMeshUserData | RotateHandleMeshUserData + +export const incrementColumnCount = (layoutGroup: Group) => { + const userData = layoutGroup.userData as HouseLayoutGroupUserData + if (userData.type !== UserDataTypeEnum.Enum.HouseLayoutGroup) + throw new Error(`incrementColumnCount called on ${userData.type}`) + userData.columnCount++ +} + +export const decrementColumnCount = (layoutGroup: Group) => { + const userData = layoutGroup.userData as HouseLayoutGroupUserData + if (userData.type !== UserDataTypeEnum.Enum.HouseLayoutGroup) + throw new Error(`incrementColumnCount called on ${userData.type}`) + userData.columnCount-- +} diff --git a/app/design/ui-3d/fresh/util.ts b/app/design/ui-3d/fresh/util.ts deleted file mode 100644 index 10c0569e..00000000 --- a/app/design/ui-3d/fresh/util.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { pipe } from "fp-ts/lib/function" -import { RefObject } from "react" -import { Group, Object3D } from "three" -import { A, O } from "../../../utils/functions" -import { UserDataTypeEnum } from "./userData" - -const getUtils = (rootRef: RefObject) => { - const applyToHouseRootGroup = ( - houseId: string, - f: (object: Object3D) => void - ) => - pipe( - rootRef.current?.children ?? [], - A.findFirst( - ({ userData }) => - userData.type === UserDataTypeEnum.Enum.HouseRootGroup && - userData.houseId === houseId - ), - O.map(f) - ) - - return { - applyToHouseRootGroup, - } -} - -export default getUtils From a305ae3eb729f32df585444ce1bfa5ee9ab49e2f Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 3 Aug 2023 13:44:34 +0100 Subject: [PATCH 075/132] wip --- .../ui-3d/fresh/helpers/sceneChanges.ts | 23 ++++++ .../ui-3d/fresh/helpers/sceneQueries.ts | 60 +++++++++++---- .../ui-3d/fresh/useKeyTestInteractions.ts | 73 +++++++++++-------- app/utils/three.ts | 3 + 4 files changed, 116 insertions(+), 43 deletions(-) create mode 100644 app/design/ui-3d/fresh/helpers/sceneChanges.ts diff --git a/app/design/ui-3d/fresh/helpers/sceneChanges.ts b/app/design/ui-3d/fresh/helpers/sceneChanges.ts new file mode 100644 index 00000000..0c85b1d5 --- /dev/null +++ b/app/design/ui-3d/fresh/helpers/sceneChanges.ts @@ -0,0 +1,23 @@ +import { pipe } from "fp-ts/lib/function" +import { Group } from "three" +import { A, O } from "../../../../utils/functions" +import { setInvisible, setVisibleAndRaycast } from "../../../../utils/three" +import { getPartitionedLayoutGroups } from "./sceneQueries" + +export const debugNextLayout = (houseTransformsGroup: Group) => { + const { + left: rest, + right: [active], + } = getPartitionedLayoutGroups(houseTransformsGroup) + + pipe( + rest, + A.head, + O.map((nextLayout) => { + setVisibleAndRaycast(nextLayout) + console.log({ active }) + setInvisible(active) + houseTransformsGroup.userData.activeChildUuid = nextLayout.uuid + }) + ) +} diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 28575017..fddaf950 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -1,7 +1,7 @@ import { pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { Group, Object3D } from "three" -import { A, O, someOrError } from "../../../../utils/functions" +import { A, O, pipeLog, someOrError } from "../../../../utils/functions" import { HouseLayoutGroupUserData, HouseTransformsGroupUserData, @@ -52,18 +52,6 @@ export const getHouseGroupColumns = (houseGroup: Group) => someOrError("no columns container in house group") ) -export const getActiveLayoutGroup = (houseTransformsGroup: Group): Group => - pipe( - houseTransformsGroup.children, - A.findFirst( - (x) => - x.uuid === - (houseTransformsGroup.userData as HouseTransformsGroupUserData) - .activeChildUuid - ), - someOrError(`getActiveLayoutGroup failure`) - ) as Group - export const getActiveHouseUserData = (houseTransformsGroup: Group) => pipe( houseTransformsGroup.children, @@ -80,11 +68,57 @@ export const getActiveHouseUserData = (houseTransformsGroup: Group) => someOrError(`getActiveHouseUserData failure`) ) +export const getLayoutGroups = (houseTransformsGroup: Group): Group[] => + houseTransformsGroup.children.filter( + (x) => x.userData.type === UserDataTypeEnum.Enum.HouseLayoutGroup + ) as Group[] + +export const getActiveLayoutGroup = (houseTransformsGroup: Group): Group => + pipe( + houseTransformsGroup.children, + A.findFirst( + (x) => + x.uuid === + (houseTransformsGroup.userData as HouseTransformsGroupUserData) + .activeChildUuid + ), + someOrError(`getActiveLayoutGroup failure`) + ) as Group + +export const getPartitionedLayoutGroups = (houseTransformsGroup: Group) => + pipe( + houseTransformsGroup, + getLayoutGroups, + pipeLog, + A.partition((x) => x.uuid === houseTransformsGroup.userData.activeChildUuid) + ) + export const getLayoutGroupColumnGroups = (layoutGroup: Group): Group[] => layoutGroup.children.filter( (x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup ) as Group[] +export const getLayoutGroupBySectionType = ( + houseTransformsGroup: Group, + sectionType: string +) => { + const houseTransformsUserData = + houseTransformsGroup.userData as HouseTransformsGroupUserData + if ( + houseTransformsUserData.type !== UserDataTypeEnum.Enum.HouseTransformsGroup + ) + throw new Error( + `getLayoutGroupBySectionType called on type other than ${UserDataTypeEnum.Enum.HouseTransformsGroup}` + ) + return pipe( + houseTransformsGroup.children, + A.findFirst( + (group) => + (group.userData as HouseLayoutGroupUserData).sectionType === sectionType + ) + ) +} + // should just be able to use house length // export const getLastColumnEndZ = (houseGroup: Group) => { diff --git a/app/design/ui-3d/fresh/useKeyTestInteractions.ts b/app/design/ui-3d/fresh/useKeyTestInteractions.ts index f2a8859e..fbc7247a 100644 --- a/app/design/ui-3d/fresh/useKeyTestInteractions.ts +++ b/app/design/ui-3d/fresh/useKeyTestInteractions.ts @@ -1,15 +1,20 @@ +import { invalidate } from "@react-three/fiber" import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { useKey } from "react-use" import { Group, Object3D, Vector3 } from "three" import layoutsDB from "../../../db/layouts" -import { A, O, R } from "../../../utils/functions" +import { A, O, R, T } from "../../../utils/functions" import { PI } from "../../../utils/math" -import { yAxis } from "../../../utils/three" +import { setInvisible, yAxis } from "../../../utils/three" import { updateEverything } from "./dimensions" import { createLayoutGroup } from "./helpers/layouts" -import { getActiveHouseUserData } from "./helpers/sceneQueries" +import { debugNextLayout } from "./helpers/sceneChanges" +import { + getActiveHouseUserData, + getLayoutGroupBySectionType, +} from "./helpers/sceneQueries" import { insertVanillaColumn, subtractPenultimateColumn, @@ -40,9 +45,6 @@ const useKeyTestInteractions = (rootRef: RefObject) => { } }) - // stretch width - - useKey("X", () => {}) - useKey("d", () => { for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, 1) @@ -86,6 +88,11 @@ const useKeyTestInteractions = (rootRef: RefObject) => { useKey("x", () => { for (let houseGroup of getHouseGroups()) { + debugNextLayout(houseGroup) + invalidate() + + // @ts-ignore + window.houseGroup = houseGroup } }) @@ -131,36 +138,42 @@ const useKeyTestInteractions = (rootRef: RefObject) => { (data) => { const houseGroups = getHouseGroups() for (const { houseId, altSectionTypeLayouts } of data) { - pipe( + const foo = pipe( houseGroups, A.findFirst((houseGroup) => houseGroup.userData.houseId === houseId), - O.chain((houseTransformsGroup) => + O.map((houseTransformsGroup) => pipe( - houseTransformsGroup.children, - A.head, - O.map((houseLayoutGroup) => + altSectionTypeLayouts, + R.map(({ layout: houseLayout, sectionType }) => { + const { systemId, dnas } = + getActiveHouseUserData(houseTransformsGroup) + pipe( - altSectionTypeLayouts, - R.map(({ layout: houseLayout }) => { - const { systemId, dnas } = - getActiveHouseUserData(houseTransformsGroup) - - createLayoutGroup({ - systemId, - dnas, - houseId, - houseLayout, - }) - - // probably needs to be the layout group - // houseLayoutToHouseGroup({ - // systemId, - // houseId, - // houseLayout, - // }) + getLayoutGroupBySectionType( + houseTransformsGroup, + sectionType.code + ), + O.map((maybeSectionTypeLayoutGroup) => { + houseTransformsGroup.remove(maybeSectionTypeLayoutGroup) }) ) - ) + + const taskOut: T.Task = async () => { + const layoutGroup = await createLayoutGroup({ + systemId, + dnas, + houseId, + houseLayout, + })() + + setInvisible(layoutGroup) + + houseTransformsGroup.add(layoutGroup) + console.log(houseTransformsGroup) + } + + taskOut() + }) ) ) ) diff --git a/app/utils/three.ts b/app/utils/three.ts index d59624fb..1b10c0be 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -187,6 +187,7 @@ export const useRotations = () => { } export const setVisibleAndRaycast = (object: Object3D) => { + object.visible = true object.traverse((node) => { node.layers.set(CameraLayer.VISIBLE) node.layers.enable(RaycasterLayer.ENABLED) @@ -200,8 +201,10 @@ export const setVisibleOnly = (object: Object3D) => { } export const setInvisible = (object: Object3D) => { + object.visible = false object.traverse((node) => { node.layers.set(CameraLayer.INVISIBLE) + node.layers.enable(RaycasterLayer.DISABLED) }) } From 3b93cd0371d2308d3b6085c69810f13a08e3a56b Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 3 Aug 2023 14:33:53 +0100 Subject: [PATCH 076/132] wip scope stuff --- app/design/state/scope.ts | 1 + app/design/ui-3d/fresh/events/gestures.ts | 123 ++++-------------- app/design/ui-3d/fresh/events/houses.ts | 5 + .../ui-3d/fresh/helpers/clippingPlanes.ts | 45 +++++++ app/design/ui-3d/fresh/helpers/layouts.ts | 9 +- .../ui-3d/fresh/helpers/sceneChanges.ts | 1 - .../ui-3d/fresh/helpers/sceneQueries.ts | 10 +- .../ui-3d/fresh/useKeyTestInteractions.ts | 8 +- app/design/ui-3d/fresh/userData.ts | 29 ++++- app/design/ui/HtmlUi.tsx | 12 ++ 10 files changed, 139 insertions(+), 104 deletions(-) create mode 100644 app/design/ui-3d/fresh/helpers/clippingPlanes.ts diff --git a/app/design/state/scope.ts b/app/design/state/scope.ts index 48ba6fbf..2e7718d3 100644 --- a/app/design/state/scope.ts +++ b/app/design/state/scope.ts @@ -8,6 +8,7 @@ import houses from "./houses" export type ScopeItem = { ifcTag: string + dna: string gridGroupIndex: number levelIndex: number columnIndex: number diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 6524185d..7044b336 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -20,6 +20,7 @@ import { } from "../helpers/sceneQueries" import { insertVanillaColumn } from "../helpers/stretchZ" import { + elementMeshToScopeItem, GridGroupUserData, HouseTransformsGroupUserData, StretchHandleMeshUserData, @@ -49,6 +50,23 @@ export const usePointerUpListener = (f: () => void) => export const dispatchPointerUp = () => dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_UP)) +const objectToHouseObjects = (object: Object3D) => + object.parent!.parent!.parent!.parent!.children.flatMap((x) => + x.children.flatMap((y) => y.children.flatMap((z) => z.children)) + ) + +const objectToIfcTagObjects = (object: Object3D) => { + const ifcTag: string = object.userData.ifcTag + + return object.parent!.parent!.parent!.parent!.children.flatMap((x) => + x.children.flatMap((y) => + y.children.flatMap((z) => + z.children.filter((x) => x.userData.ifcTag === ifcTag) + ) + ) + ) +} + const useGestures = (rootRef: RefObject) => { // const firstGestureDataRef = useRef<{ // gestureTarget: Object3D @@ -141,7 +159,6 @@ const useGestures = (rootRef: RefObject) => { switch (true) { case distance > lastDistance: { if (distance > vanillaColumn.length * columnsAddedToEnd) { - console.log("am i even ever") insertVanillaColumn(houseGroup, 1)() stretchData.current.columnsAddedToEnd++ } @@ -203,89 +220,6 @@ const useGestures = (rootRef: RefObject) => { } invalidate() - - // pipe( - // event.intersections, - // A.head, - // O.map((ix0) => { - // event.stopPropagation() - - // const { point, object } = ix0 - - // if (first) { - // setCameraControlsEnabled(false) - // const [x, z] = pointer.xz - // const y = pointer.y - // pointerZeroRef.current = { x, y, z } - // lastPointerRef.current = { x, y, z } - // gestureTargetRef.current = object - // dispatchPointerDown({ point, object: gestureTargetRef.current }) - // } else if (last) { - // dispatchPointerUp({ point, object: gestureTargetRef.current! }) - // pointerZeroRef.current = null - // lastPointerRef.current = null - // gestureTargetRef.current = null - // setCameraControlsEnabled(true) - // } else { - // const userData = gestureTargetRef.current?.userData as UserData - - // switch (userData.type) { - // case UserDataTypeEnum.Enum.StretchHandleMesh: { - // const { houseId, direction, axis } = - // userData as StretchHandleMeshUserData - - // const maybeHouseGroup = rootHouseGroupQuery(rootRef, houseId) - - // pipe( - // maybeHouseGroup, - // O.map((houseGroup) => { - // if (lastPointerRef.current === null) return - - // const [x1, z1] = pointer.xz - // const y1 = pointer.y - - // const { x: x0, y: y0, z: z0 } = lastPointerRef.current - - // const delta = new Vector3( - // x1 - x0, - // y1 - y0, - // z1 - z0 - // ).applyAxisAngle( - // new Vector3(0, 1, 0), - // -houseGroup.rotation.y - // ) - - // switch (axis) { - // case "z": - // gestureTargetRef.current!.position.z += delta.z - // // object.position.lerp( - // // new Vector3(0, 0, object.position.z + delta.z), - // // 0.1 - // // ) - - // switch (direction) { - // case 1: - // break - // case -1: - // } - - // break - // case "x": - // break - // } - - // lastPointerRef.current = { x: x1, y: y1, z: z1 } - // }) - // ) - - // break - // } - // } - // } - - // invalidate() - // }) - // ) }, onHover: ({ event, event: { intersections }, hovering }) => { event.stopPropagation() @@ -317,23 +251,22 @@ const useGestures = (rootRef: RefObject) => { } if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return - + const scopeItem = elementMeshToScopeItem(object) + scope.hovered = scopeItem switch (siteCtx.mode) { case SiteCtxModeEnum.Enum.SITE: - if (object.parent?.parent?.parent?.parent) { - const objects = object.parent.parent.parent.parent.children.flatMap( - (x) => - x.children.flatMap((y) => y.children.flatMap((z) => z.children)) - ) - dispatchOutline({ - objects, - }) - } + dispatchOutline({ + objects: objectToHouseObjects(object), + }) break case SiteCtxModeEnum.Enum.BUILDING: - // OUTLINE COLUMN ?! + dispatchOutline({ + objects: objectToIfcTagObjects(object), + }) + // object to all of ifc tag break case SiteCtxModeEnum.Enum.LEVEL: + // object to all of module group if (object.parent) { dispatchOutline({ objects: object.parent.children }) } diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index 76b84547..4f2996e9 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -10,6 +10,7 @@ import { setVisibleAndRaycast } from "../../../../utils/three" import { nanoid } from "nanoid" import { floor } from "../../../../utils/math" import { createInitialHouse } from "../helpers/layouts" +import useClippingPlaneHelpers from "../helpers/clippingPlanes" const ADD_HOUSE_INTENT_EVENT = "AddHouseIntentEvent" const ADD_HOUSE_EVENT = "AddHouseEvent" @@ -48,6 +49,8 @@ export const useDeleteHouseListener = ( ) => useEvent(DELETE_HOUSE_EVENT, ({ detail }) => f(detail)) export const useHousesEvents = (rootRef: RefObject) => { + const { initClippingPlanes } = useClippingPlaneHelpers(rootRef) + const addHouse = async (house: House) => { if (!rootRef.current) return @@ -80,6 +83,8 @@ export const useHousesEvents = (rootRef: RefObject) => { invalidate() userDB.houses.put(house) + + initClippingPlanes(houseId) } const cleanup = () => { diff --git a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts new file mode 100644 index 00000000..2db31d06 --- /dev/null +++ b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts @@ -0,0 +1,45 @@ +import { pipe } from "fp-ts/lib/function" +import { RefObject } from "react" +import { Group, Material, Mesh } from "three" +import { A, O } from "../../../../utils/functions" +import { HouseTransformsGroupUserData, UserDataTypeEnum } from "../userData" +import { + getActiveHouseUserData, + getActiveLayoutGroup, + getHouseTransformGroup, + mapHouseTransformGroup, +} from "./sceneQueries" + +const useClippingPlaneHelpers = (rootRef: RefObject) => { + const initClippingPlanes = (houseId: string) => { + mapHouseTransformGroup(rootRef, houseId, (houseTransformGroup) => { + const { clippingPlanes } = + houseTransformGroup.userData as HouseTransformsGroupUserData + + console.log({ clippingPlanes }) + + houseTransformGroup.traverse((x) => { + if (x.userData.type === UserDataTypeEnum.Enum.ElementMesh) { + ;((x as Mesh).material as Material).clippingPlanes = clippingPlanes + } + }) + + console.log(houseTransformGroup) + }) + } + + const setYCut = (houseId: string, y: number) => { + mapHouseTransformGroup(rootRef, houseId, (houseTransformGroup) => { + const { + clippingPlanes, + clippingPlanes: [cpx, cpy, cpz], + height, + } = getActiveHouseUserData(houseTransformGroup) + cpy.constant = height / 2 + }) + } + + return { setYCut, initClippingPlanes } +} + +export default useClippingPlaneHelpers diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 212d6432..5fe107a9 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -136,7 +136,6 @@ export const createModuleGroup = async ({ ifcTag, houseId: "", }) as MeshStandardMaterial - // material.clippingPlanes = clippingPlanes const mesh = new Mesh(geometry, material) mesh.castShadow = true @@ -473,10 +472,12 @@ export const createInitialHouse = ({ const BIG_NUMBER = 999 + const NORMAL_DIRECTION = -1 + const clippingPlanes: Plane[] = [ - new Plane(new Vector3(BIG_NUMBER, 0, 0), 0), - new Plane(new Vector3(0, BIG_NUMBER, 0), 0), - new Plane(new Vector3(0, 0, BIG_NUMBER), 0), + new Plane(new Vector3(NORMAL_DIRECTION, 0, 0), BIG_NUMBER), + new Plane(new Vector3(0, NORMAL_DIRECTION, 0), BIG_NUMBER), + new Plane(new Vector3(0, 0, NORMAL_DIRECTION), BIG_NUMBER), ] const houseTransformsGroupUserData: HouseTransformsGroupUserData = { diff --git a/app/design/ui-3d/fresh/helpers/sceneChanges.ts b/app/design/ui-3d/fresh/helpers/sceneChanges.ts index 0c85b1d5..96e0b721 100644 --- a/app/design/ui-3d/fresh/helpers/sceneChanges.ts +++ b/app/design/ui-3d/fresh/helpers/sceneChanges.ts @@ -15,7 +15,6 @@ export const debugNextLayout = (houseTransformsGroup: Group) => { A.head, O.map((nextLayout) => { setVisibleAndRaycast(nextLayout) - console.log({ active }) setInvisible(active) houseTransformsGroup.userData.activeChildUuid = nextLayout.uuid }) diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index fddaf950..9cd3e68b 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -8,7 +8,7 @@ import { UserDataTypeEnum, } from "../userData" -export const rootHouseGroupChildQuery = ( +export const getHouseTransformGroup = ( rootRef: RefObject, houseId: string ) => @@ -16,7 +16,13 @@ export const rootHouseGroupChildQuery = ( rootRef.current?.children, O.fromNullable, O.chain(A.findFirst((x) => x.userData.houseId === houseId)) - ) + ) as O.Option + +export const mapHouseTransformGroup = ( + rootRef: RefObject, + houseId: string, + f: (houseTransformGroup: Group) => void +) => pipe(getHouseTransformGroup(rootRef, houseId), O.map(f)) export const rootHouseGroupParentQuery = (object: Object3D) => { let x = object diff --git a/app/design/ui-3d/fresh/useKeyTestInteractions.ts b/app/design/ui-3d/fresh/useKeyTestInteractions.ts index fbc7247a..6fd889fc 100644 --- a/app/design/ui-3d/fresh/useKeyTestInteractions.ts +++ b/app/design/ui-3d/fresh/useKeyTestInteractions.ts @@ -9,6 +9,7 @@ import { A, O, R, T } from "../../../utils/functions" import { PI } from "../../../utils/math" import { setInvisible, yAxis } from "../../../utils/three" import { updateEverything } from "./dimensions" +import useClippingPlaneHelpers from "./helpers/clippingPlanes" import { createLayoutGroup } from "./helpers/layouts" import { debugNextLayout } from "./helpers/sceneChanges" import { @@ -96,6 +97,12 @@ const useKeyTestInteractions = (rootRef: RefObject) => { } }) + const { setYCut } = useClippingPlaneHelpers(rootRef) + + useKey("c", () => { + setYCut(getHouseGroups()[0].userData.houseId, 0.5) + }) + // toggle stretch width first pass // useKey("x", () => { // // console.log(liveHouses) @@ -169,7 +176,6 @@ const useKeyTestInteractions = (rootRef: RefObject) => { setInvisible(layoutGroup) houseTransformsGroup.add(layoutGroup) - console.log(houseTransformsGroup) } taskOut() diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index e23a7cc9..2a318551 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -1,7 +1,8 @@ -import { Group, Plane } from "three" +import { Group, Object3D, Plane } from "three" import { OBB } from "three-stdlib" import { z } from "zod" import { ColumnLayout, VanillaColumn } from "../../../db/layouts" +import { ScopeItem } from "../../state/scope" // HouseTransformsGroup has @@ -119,3 +120,29 @@ export const decrementColumnCount = (layoutGroup: Group) => { throw new Error(`incrementColumnCount called on ${userData.type}`) userData.columnCount-- } + +export const elementMeshToScopeItem = (object: Object3D): ScopeItem => { + const userData = object.userData as ElementMeshUserData + + if (userData.type !== UserDataTypeEnum.Enum.ElementMesh) + throw new Error( + `userData.type is ${userData.type} in elementMeshToScopeItem` + ) + + const { ifcTag } = userData + const { gridGroupIndex, dna } = object.parent!.userData as ModuleGroupUserData + const { levelIndex } = object.parent!.parent!.userData as GridGroupUserData + const { columnIndex } = object.parent!.parent!.parent! + .userData as ColumnGroupUserData + const { houseId } = object.parent!.parent!.parent!.parent!.parent! + .userData as HouseTransformsGroupUserData + + return { + ifcTag, + gridGroupIndex, + levelIndex, + columnIndex, + houseId, + dna, + } +} diff --git a/app/design/ui/HtmlUi.tsx b/app/design/ui/HtmlUi.tsx index 8b991d61..dda4988a 100644 --- a/app/design/ui/HtmlUi.tsx +++ b/app/design/ui/HtmlUi.tsx @@ -31,6 +31,8 @@ import ContextMenuEntry from "./menu/ContextMenuEntry" import Breadcrumbs from "./Breadcrumbs" import ExitMode from "./ExitMode" import { useMenu } from "../state/menu" +import { useSiteCtx } from "../state/siteCtx" +import { useScope } from "../state/scope" type Props = { controlsEnabled: boolean @@ -64,6 +66,11 @@ const HtmlUi = (props: Props) => { internalShowHide: false, }) + const { mode } = useSiteCtx() + const { hovered, selected } = useScope() + + const DEBUG = true + return (
@@ -193,6 +200,11 @@ const HtmlUi = (props: Props) => { {menu.open && } + {DEBUG && ( +
+
{JSON.stringify({ mode, hovered, selected }, null, 2)}
+
+ )} From 919bd92737a79047cf8ae64814a6fb9fd124c54c Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 3 Aug 2023 15:24:45 +0100 Subject: [PATCH 077/132] wip level planes --- app/design/state/siteCtx.ts | 31 ++++++++ app/design/ui-3d/fresh/dimensions.ts | 2 +- app/design/ui-3d/fresh/events/outlines.ts | 5 +- .../ui-3d/fresh/helpers/clippingPlanes.ts | 76 +++++++++++++++++-- app/design/ui-3d/fresh/helpers/layouts.ts | 14 ++-- app/design/ui-3d/fresh/userData.ts | 1 + app/design/ui-3d/init/Effects.tsx | 4 +- 7 files changed, 114 insertions(+), 19 deletions(-) diff --git a/app/design/state/siteCtx.ts b/app/design/state/siteCtx.ts index 02773b5c..c19d30c8 100644 --- a/app/design/state/siteCtx.ts +++ b/app/design/state/siteCtx.ts @@ -1,4 +1,5 @@ import { useEffect } from "react" +import { useEvent } from "react-use" import { proxy, subscribe, useSnapshot } from "valtio" import * as z from "zod" import { isSSR } from "~/utils/next" @@ -113,8 +114,16 @@ export const upMode = () => { const { houseId, mode } = siteCtx if (mode === SiteCtxModeEnum.Enum.LEVEL && houseId) { enterBuildingMode(houseId) + dispatchModeChange({ + previous: SiteCtxModeEnum.Enum.LEVEL, + next: SiteCtxModeEnum.Enum.BUILDING, + }) } else if (mode === SiteCtxModeEnum.Enum.BUILDING) { exitBuildingMode() + dispatchModeChange({ + previous: SiteCtxModeEnum.Enum.BUILDING, + next: SiteCtxModeEnum.Enum.SITE, + }) } } @@ -122,11 +131,33 @@ export const downMode = (incoming: { levelIndex: number; houseId: string }) => { const { mode } = siteCtx if (mode === SiteCtxModeEnum.Enum.SITE) { enterBuildingMode(incoming.houseId) + dispatchModeChange({ + previous: SiteCtxModeEnum.Enum.SITE, + next: SiteCtxModeEnum.Enum.BUILDING, + }) } else if (mode === SiteCtxModeEnum.Enum.BUILDING) { enterLevelMode(incoming.levelIndex) + dispatchModeChange({ + previous: SiteCtxModeEnum.Enum.BUILDING, + next: SiteCtxModeEnum.Enum.LEVEL, + }) } } +const MODE_CHANGE_EVENT = "ModeChangeEvent" + +export type ModeChangeEventDetail = { + previous: SiteCtxMode + next: SiteCtxMode +} + +export const dispatchModeChange = (detail: ModeChangeEventDetail) => + dispatchEvent(new CustomEvent(MODE_CHANGE_EVENT, { detail })) + +export const useModeChangeListener = ( + f: (eventDetail: ModeChangeEventDetail) => void +) => useEvent(MODE_CHANGE_EVENT, ({ detail }) => f(detail)) + export const useSiteCurrency = () => { const { region } = useSiteCtx() const symbol = region === "UK" ? "£" : "€" diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 7d0a0a1f..2f1d10eb 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -130,7 +130,7 @@ const updateDnas = (houseGroup: Group) => { export const updateEverything = (houseTransformsGroup: Group) => { updateHouseLength(houseTransformsGroup) updateHouseOBB(houseTransformsGroup) - updateClippingPlanes(houseTransformsGroup) + // updateClippingPlanes(houseTransformsGroup) updateDnas(houseTransformsGroup) const { dnas, houseId } = getActiveHouseUserData(houseTransformsGroup) diff --git a/app/design/ui-3d/fresh/events/outlines.ts b/app/design/ui-3d/fresh/events/outlines.ts index 0f8e1c93..dc9f45b1 100644 --- a/app/design/ui-3d/fresh/events/outlines.ts +++ b/app/design/ui-3d/fresh/events/outlines.ts @@ -10,5 +10,6 @@ type OutlineEventDetail = { export const dispatchOutline = (detail: OutlineEventDetail) => dispatchEvent(new CustomEvent(OUTLINE_EVENT, { detail })) -export const useOutlineEvent = (f: (eventDetail: OutlineEventDetail) => void) => - useEvent(OUTLINE_EVENT, ({ detail }) => f(detail)) +export const useOutlineListener = ( + f: (eventDetail: OutlineEventDetail) => void +) => useEvent(OUTLINE_EVENT, ({ detail }) => f(detail)) diff --git a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts index 2db31d06..830a552d 100644 --- a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts +++ b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts @@ -1,12 +1,23 @@ +import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { Group, Material, Mesh } from "three" import { A, O } from "../../../../utils/functions" -import { HouseTransformsGroupUserData, UserDataTypeEnum } from "../userData" +import siteCtx, { + SiteCtxModeEnum, + useModeChangeListener, +} from "../../../state/siteCtx" +import { + GridGroupUserData, + HouseTransformsGroupUserData, + UserDataTypeEnum, +} from "../userData" +import { BIG_CLIP_NUMBER } from "./layouts" import { getActiveHouseUserData, getActiveLayoutGroup, getHouseTransformGroup, + getLayoutGroupColumnGroups, mapHouseTransformGroup, } from "./sceneQueries" @@ -16,29 +27,78 @@ const useClippingPlaneHelpers = (rootRef: RefObject) => { const { clippingPlanes } = houseTransformGroup.userData as HouseTransformsGroupUserData - console.log({ clippingPlanes }) - houseTransformGroup.traverse((x) => { if (x.userData.type === UserDataTypeEnum.Enum.ElementMesh) { ;((x as Mesh).material as Material).clippingPlanes = clippingPlanes } }) - - console.log(houseTransformGroup) }) } const setYCut = (houseId: string, y: number) => { mapHouseTransformGroup(rootRef, houseId, (houseTransformGroup) => { const { - clippingPlanes, - clippingPlanes: [cpx, cpy, cpz], + clippingPlanes: [, cpy], height, } = getActiveHouseUserData(houseTransformGroup) - cpy.constant = height / 2 + cpy.constant = y }) } + const houseLevelIndexToCutHeight = (houseId: string, levelIndex: number) => { + return pipe( + getHouseTransformGroup(rootRef, houseId), + O.chain((houseTransformGroup) => + pipe( + houseTransformGroup, + getActiveLayoutGroup, + getLayoutGroupColumnGroups, + A.head, + O.chain((columnGroup) => { + const gridGroups = columnGroup.children + return pipe( + gridGroups, + A.findFirst((gridGroup) => { + const gridGroupUserData = + gridGroup.userData as GridGroupUserData + + return gridGroupUserData.levelIndex === levelIndex + }) + ) + }) + ) + ), + O.map((gridGroup) => { + const { height } = gridGroup.userData as GridGroupUserData + return gridGroup.position.y + height / 2 + }) + ) + } + + useModeChangeListener(({ previous, next }) => { + const { houseId, levelIndex } = siteCtx + + switch (true) { + case next === SiteCtxModeEnum.Enum.LEVEL: + if (houseId === null || levelIndex === null) break + // find level index height + + pipe( + houseLevelIndexToCutHeight(houseId, levelIndex), + O.map((cutHeight) => { + setYCut(houseId, cutHeight) + }) + ) + break + default: + if (houseId === null) break + setYCut(houseId, BIG_CLIP_NUMBER) + break + } + + invalidate() + }) + return { setYCut, initClippingPlanes } } diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 5fe107a9..c4700edf 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -34,6 +34,8 @@ import { } from "../userData" import { createStretchHandle } from "./handles" +export const BIG_CLIP_NUMBER = 999 + // serialized layout key : column export let vanillaColumns: Record = {} @@ -208,10 +210,11 @@ export const createColumnGroup = moduleGroup.scale.set(1, 1, endColumn ? 1 : -1) moduleGroup.position.set( 0, - y, + 0, endColumn ? z + module.length / 2 : z - module.length / 2 ) + gridGroup.position.setY(y) gridGroup.add(moduleGroup) length += module.length @@ -221,6 +224,7 @@ export const createColumnGroup = type: UserDataTypeEnum.Enum.GridGroup, levelIndex, length, + height: modules[0].module.height, } gridGroup.userData = gridGroupUserData @@ -470,14 +474,12 @@ export const createInitialHouse = ({ T.map((layoutGroup) => { const transformsGroup = new Group() - const BIG_NUMBER = 999 - const NORMAL_DIRECTION = -1 const clippingPlanes: Plane[] = [ - new Plane(new Vector3(NORMAL_DIRECTION, 0, 0), BIG_NUMBER), - new Plane(new Vector3(0, NORMAL_DIRECTION, 0), BIG_NUMBER), - new Plane(new Vector3(0, 0, NORMAL_DIRECTION), BIG_NUMBER), + new Plane(new Vector3(NORMAL_DIRECTION, 0, 0), BIG_CLIP_NUMBER), + new Plane(new Vector3(0, NORMAL_DIRECTION, 0), BIG_CLIP_NUMBER), + new Plane(new Vector3(0, 0, NORMAL_DIRECTION), BIG_CLIP_NUMBER), ] const houseTransformsGroupUserData: HouseTransformsGroupUserData = { diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 2a318551..c4cbd9af 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -68,6 +68,7 @@ export type GridGroupUserData = { type: typeof UserDataTypeEnum.Enum.GridGroup levelIndex: number length: number + height: number } export type ModuleGroupUserData = { diff --git a/app/design/ui-3d/init/Effects.tsx b/app/design/ui-3d/init/Effects.tsx index ddc9c91c..b0c7b4e3 100644 --- a/app/design/ui-3d/init/Effects.tsx +++ b/app/design/ui-3d/init/Effects.tsx @@ -7,7 +7,7 @@ import { RenderPass, } from "postprocessing" import { useEffect, useMemo } from "react" -import { useOutlineEvent } from "../fresh/events/outlines" +import { useOutlineListener } from "../fresh/events/outlines" export type UseOutlineEffectParams = ConstructorParameters< typeof OutlineEffectRaw @@ -80,7 +80,7 @@ const Effects = () => { // selectiveBloomEffect ]) - useOutlineEvent(({ objects }) => { + useOutlineListener(({ objects }) => { outlineEffect.selection.set(objects) }) From 5cca471bf06aa99fec6937c71394083bff55fac5 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 3 Aug 2023 15:51:56 +0100 Subject: [PATCH 078/132] wip debug stretch type --- app/design/ui-3d/fresh/events/gestures.ts | 93 +++++++++++-------- .../ui-3d/fresh/helpers/sceneQueries.ts | 3 +- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 7044b336..6879d919 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -3,7 +3,7 @@ import { Handler, useGesture, UserHandlers } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { RefObject, useRef } from "react" import { useEvent } from "react-use" -import { Group, Object3D, Vector3 } from "three" +import { Group, Material, Mesh, Object3D, Plane, Vector3 } from "three" import { z } from "zod" import { A, O } from "../../../../utils/functions" import { isMesh } from "../../../../utils/three" @@ -234,10 +234,60 @@ const useGestures = (rootRef: RefObject) => { return } - const { - object, - // object: { userData }, - } = intersections[0] + pipe( + intersections, + A.findFirst((ix) => { + const { object, point } = ix + if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) + return false + return ( + ((object as Mesh).material as Material).clippingPlanes as Plane[] + ).every((plane) => { + return plane.distanceToPoint(point) > 0 + }) + }), + O.map((intersection) => { + const { object } = intersection + + if (hovering) { + document.body.style.cursor = "grab" + } + + if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return + const scopeItem = elementMeshToScopeItem(object) + scope.hovered = scopeItem + switch (siteCtx.mode) { + case SiteCtxModeEnum.Enum.SITE: + dispatchOutline({ + objects: objectToHouseObjects(object), + }) + break + case SiteCtxModeEnum.Enum.BUILDING: + dispatchOutline({ + objects: objectToIfcTagObjects(object), + }) + // object to all of ifc tag + break + case SiteCtxModeEnum.Enum.LEVEL: + // object to all of module group + if (object.parent) { + dispatchOutline({ objects: object.parent.children }) + } + break + } + + // scope.hovered = { + // ...userData.identifier, + // } + + invalidate() + }) + ) + + // const { + // object, + // // object: { userData }, + // } = ix0 // if (object.parent?.parent) { // const objects = object.parent.parent.children.flatMap((x) => x.children) @@ -245,39 +295,6 @@ const useGestures = (rootRef: RefObject) => { // objects, // }) // } - - if (hovering) { - document.body.style.cursor = "grab" - } - - if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return - const scopeItem = elementMeshToScopeItem(object) - scope.hovered = scopeItem - switch (siteCtx.mode) { - case SiteCtxModeEnum.Enum.SITE: - dispatchOutline({ - objects: objectToHouseObjects(object), - }) - break - case SiteCtxModeEnum.Enum.BUILDING: - dispatchOutline({ - objects: objectToIfcTagObjects(object), - }) - // object to all of ifc tag - break - case SiteCtxModeEnum.Enum.LEVEL: - // object to all of module group - if (object.parent) { - dispatchOutline({ objects: object.parent.children }) - } - break - } - - // scope.hovered = { - // ...userData.identifier, - // } - - invalidate() }, onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { event.stopPropagation() diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 9cd3e68b..876d1f4f 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -1,7 +1,7 @@ import { pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { Group, Object3D } from "three" -import { A, O, pipeLog, someOrError } from "../../../../utils/functions" +import { A, O, someOrError } from "../../../../utils/functions" import { HouseLayoutGroupUserData, HouseTransformsGroupUserData, @@ -95,7 +95,6 @@ export const getPartitionedLayoutGroups = (houseTransformsGroup: Group) => pipe( houseTransformsGroup, getLayoutGroups, - pipeLog, A.partition((x) => x.uuid === houseTransformsGroup.userData.activeChildUuid) ) From 593f0800bcda33b665b3ae195a0578252d98f3b0 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 3 Aug 2023 16:00:38 +0100 Subject: [PATCH 079/132] wip de-pointer debug pane --- app/design/ui-3d/fresh/helpers/layouts.ts | 2 ++ app/design/ui/HtmlUi.tsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index c4700edf..ff2d2022 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -214,6 +214,8 @@ export const createColumnGroup = endColumn ? z + module.length / 2 : z - module.length / 2 ) + console.log({ gridGroupY: y, moduleDna: moduleGroup.userData.dna }) + gridGroup.position.setY(y) gridGroup.add(moduleGroup) diff --git a/app/design/ui/HtmlUi.tsx b/app/design/ui/HtmlUi.tsx index dda4988a..26e92b6d 100644 --- a/app/design/ui/HtmlUi.tsx +++ b/app/design/ui/HtmlUi.tsx @@ -201,7 +201,7 @@ const HtmlUi = (props: Props) => { {menu.open && } {DEBUG && ( -
+
{JSON.stringify({ mode, hovered, selected }, null, 2)}
)} From b9fe23963934b54a2068ee092ccbe9cf2712447a Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 3 Aug 2023 16:11:59 +0100 Subject: [PATCH 080/132] wip comment breadcrumbs --- app/design/ui/HtmlUi.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/app/design/ui/HtmlUi.tsx b/app/design/ui/HtmlUi.tsx index 26e92b6d..469e8c9c 100644 --- a/app/design/ui/HtmlUi.tsx +++ b/app/design/ui/HtmlUi.tsx @@ -15,9 +15,9 @@ import SiteSidebar from "./SiteSidebar" import { pipe } from "fp-ts/lib/function" import { keys } from "fp-ts/lib/Record" import usePortal from "react-cool-portal" -import { useSnapshot } from "valtio" import Checklist from "~/ui/Checklist" import Radio from "~/ui/Radio" +import { R, S } from "~/utils/functions" import { setOrthographic, useCameraReset, @@ -26,13 +26,11 @@ import { import elementCategories, { useElementCategories, } from "../state/elementCategories" -import { R, S } from "~/utils/functions" -import ContextMenuEntry from "./menu/ContextMenuEntry" -import Breadcrumbs from "./Breadcrumbs" -import ExitMode from "./ExitMode" import { useMenu } from "../state/menu" -import { useSiteCtx } from "../state/siteCtx" import { useScope } from "../state/scope" +import { useSiteCtx } from "../state/siteCtx" +import ExitMode from "./ExitMode" +import ContextMenuEntry from "./menu/ContextMenuEntry" type Props = { controlsEnabled: boolean @@ -195,9 +193,7 @@ const HtmlUi = (props: Props) => { close={() => setUniversalMenu(false)} /> - - - + {/* */} {menu.open && } {DEBUG && ( From b8e69a9d4d436fc44fb9d0e27d953fe3105eb636 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Sat, 5 Aug 2023 17:24:07 +0100 Subject: [PATCH 081/132] wip deleted edit mode --- app/design/state/gestures/index.ts | 89 +++++++++++------------ app/design/state/siteCtx.ts | 23 +----- app/design/ui-3d/fresh/events/gestures.ts | 14 +++- 3 files changed, 59 insertions(+), 67 deletions(-) diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index fc031b97..f7436983 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -8,7 +8,6 @@ import { A, O } from "~/utils/functions" import { useSubscribeKey } from "~/utils/hooks" import { isMesh, useRotations } from "~/utils/three" import { - ElementMeshUserData, GridGroupUserData, HouseTransformsGroupUserData, UserDataTypeEnum, @@ -22,7 +21,7 @@ import { } from "../events/stretch" import { openMenu } from "../menu" import pointer from "../pointer" -import siteCtx, { downMode, EditModeEnum } from "../siteCtx" +import siteCtx, { downMode } from "../siteCtx" import dragProxy, { Drag, StretchHandleIdentifier } from "./drag" export const useDragHandler = () => { @@ -135,47 +134,45 @@ export const useDragHandler = () => { } if (dragProxy.end) { - switch (siteCtx.editMode) { - case EditModeEnum.Enum.MOVE_ROTATE: - dispatchMoveHouseIntent({ - houseId, - delta, - last: true, - }) - break - case EditModeEnum.Enum.STRETCH: - const [distanceX, distanceZ] = unrotateV2(houseId, [ - x1 - x0, - z1 - z0, - ]) - const [dx, dz] = rotateV2(houseId, [0, distanceZ]) - - const { direction = 1, axis } = - identifier as StretchHandleIdentifier - - if (axis === "z") { - dispatchZStretchHouseIntent({ - houseId, - direction, - distance: distanceZ, - dx, - dz, - last: true, - }) - } else if (axis === "x") { - dispatchXStretchHouseIntent({ - houseId, - direction, - distance: distanceX, - dx, - dz, - last: true, - }) - } - return - // setStretchLength() - // setPreviews() - } + // switch (siteCtx.editMode) { + // case EditModeEnum.Enum.MOVE_ROTATE: + // dispatchMoveHouseIntent({ + // houseId, + // delta, + // last: true, + // }) + // break + // case EditModeEnum.Enum.STRETCH: + // const [distanceX, distanceZ] = unrotateV2(houseId, [ + // x1 - x0, + // z1 - z0, + // ]) + // const [dx, dz] = rotateV2(houseId, [0, distanceZ]) + // const { direction = 1, axis } = + // identifier as StretchHandleIdentifier + // if (axis === "z") { + // dispatchZStretchHouseIntent({ + // houseId, + // direction, + // distance: distanceZ, + // dx, + // dz, + // last: true, + // }) + // } else if (axis === "x") { + // dispatchXStretchHouseIntent({ + // houseId, + // direction, + // distance: distanceX, + // dx, + // dz, + // last: true, + // }) + // } + // return + // // setStretchLength() + // // setPreviews() + // } } cleanup() @@ -227,9 +224,9 @@ export const useGestures = (): any => ...identifier, } - if (siteCtx.editMode === null) { - siteCtx.editMode = EditModeEnum.Enum.MOVE_ROTATE - } + // if (siteCtx.editMode === null) { + // siteCtx.editMode = EditModeEnum.Enum.MOVE_ROTATE + // } if (siteCtx.houseId !== identifier.houseId) { siteCtx.houseId = identifier.houseId } diff --git a/app/design/state/siteCtx.ts b/app/design/state/siteCtx.ts index c19d30c8..d4d27407 100644 --- a/app/design/state/siteCtx.ts +++ b/app/design/state/siteCtx.ts @@ -7,15 +7,11 @@ import { formatWithUnit } from "../../analyse/state/data" import { BUILDX_LOCAL_STORAGE_CONTEXT_KEY } from "./constants" import houses from "./houses" -export const EditModeEnum = z.enum(["MOVE_ROTATE", "STRETCH"]) -export type EditMode = z.infer - export const SiteCtxModeEnum = z.enum(["SITE", "BUILDING", "LEVEL"]) export type SiteCtxMode = z.infer type SiteCtx = { mode: SiteCtxMode - editMode: EditMode | null houseId: string | null levelIndex: number | null projectName: string | null @@ -49,17 +45,14 @@ export const useIsBuilding = (houseId: string) => { } export const useTransformabilityBooleans = (houseId: string) => { - const { mode, editMode, houseId: ctxHouseId } = useSiteCtx() + const { mode, houseId: ctxHouseId } = useSiteCtx() return { stretchEnabled: - mode === SiteCtxModeEnum.Enum.BUILDING && - editMode === EditModeEnum.Enum.STRETCH && - houseId === ctxHouseId, + mode === SiteCtxModeEnum.Enum.BUILDING || + (mode === SiteCtxModeEnum.Enum.LEVEL && houseId === ctxHouseId), moveRotateEnabled: - mode === SiteCtxModeEnum.Enum.SITE && - editMode === EditModeEnum.Enum.MOVE_ROTATE && - houseId === ctxHouseId, + mode === SiteCtxModeEnum.Enum.SITE && houseId === ctxHouseId, } } @@ -82,16 +75,9 @@ export const useProjectName = () => { else return projectName } -export const useEditMode = (): EditMode | null => { - const { editMode } = useSiteCtx() - return editMode -} - export const enterBuildingMode = (houseId: string) => { if (siteCtx.houseId !== houseId) siteCtx.houseId = houseId if (siteCtx.levelIndex !== null) siteCtx.levelIndex = null - if (siteCtx.editMode !== EditModeEnum.Enum.STRETCH) - siteCtx.editMode = EditModeEnum.Enum.STRETCH if (siteCtx.mode !== SiteCtxModeEnum.Enum.BUILDING) siteCtx.mode = SiteCtxModeEnum.Enum.BUILDING } @@ -99,7 +85,6 @@ export const enterBuildingMode = (houseId: string) => { export const exitBuildingMode = () => { if (siteCtx.levelIndex !== null) siteCtx.levelIndex = null if (siteCtx.houseId !== null) siteCtx.houseId = null - if (siteCtx.editMode !== null) siteCtx.editMode = null if (siteCtx.mode !== SiteCtxModeEnum.Enum.SITE) siteCtx.mode = SiteCtxModeEnum.Enum.SITE } diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 6879d919..80ec40aa 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -11,7 +11,11 @@ import { setCameraControlsEnabled } from "../../../state/camera" import { openMenu } from "../../../state/menu" import pointer from "../../../state/pointer" import scope, { ScopeItem } from "../../../state/scope" -import siteCtx, { downMode, SiteCtxModeEnum } from "../../../state/siteCtx" +import siteCtx, { + downMode, + SiteCtxMode, + SiteCtxModeEnum, +} from "../../../state/siteCtx" import { getActiveHouseUserData, getHouseGroupColumns, @@ -211,7 +215,13 @@ const useGestures = (rootRef: RefObject) => { React.MouseEvent }>({ onDrag: (state) => { - const stretch = true + const stretchModes: SiteCtxMode[] = [ + SiteCtxModeEnum.Enum.BUILDING, + SiteCtxModeEnum.Enum.LEVEL, + ] + + const stretch = stretchModes.includes(siteCtx.mode) + switch (true) { case stretch: { onDragStretch(state) From bd1265c8479c5aea2eff51782baec79e10529dfd Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 8 Aug 2023 10:06:36 +0100 Subject: [PATCH 082/132] wip pre perf --- app/design/ui-3d/fresh/events/gestures.ts | 248 +++++++++++++----- app/design/ui-3d/fresh/events/outlines.ts | 3 +- app/design/ui-3d/fresh/helpers/handles.ts | 60 ++++- app/design/ui-3d/fresh/helpers/layouts.ts | 6 +- .../ui-3d/fresh/helpers/sceneQueries.ts | 34 +++ app/design/ui-3d/init/Effects.tsx | 25 +- app/design/ui/HtmlUi.tsx | 2 +- 7 files changed, 295 insertions(+), 83 deletions(-) diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 80ec40aa..171d36c7 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -3,7 +3,15 @@ import { Handler, useGesture, UserHandlers } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { RefObject, useRef } from "react" import { useEvent } from "react-use" -import { Group, Material, Mesh, Object3D, Plane, Vector3 } from "three" +import { + Group, + Intersection, + Material, + Mesh, + Object3D, + Plane, + Vector3, +} from "three" import { z } from "zod" import { A, O } from "../../../../utils/functions" import { isMesh } from "../../../../utils/three" @@ -19,12 +27,15 @@ import siteCtx, { import { getActiveHouseUserData, getHouseGroupColumns, + getHouseTransformGroup, handleColumnGroupParentQuery, rootHouseGroupParentQuery, + traverseUpUntil, } from "../helpers/sceneQueries" import { insertVanillaColumn } from "../helpers/stretchZ" import { elementMeshToScopeItem, + ElementMeshUserData, GridGroupUserData, HouseTransformsGroupUserData, StretchHandleMeshUserData, @@ -72,14 +83,6 @@ const objectToIfcTagObjects = (object: Object3D) => { } const useGestures = (rootRef: RefObject) => { - // const firstGestureDataRef = useRef<{ - // gestureTarget: Object3D - // gestureTargetHouseGroup: Group - // point: Vector3 - // } | null>(null) - - // const stretchDragGroup = useRef(null) - const stretchData = useRef<{ handleObject: Object3D houseGroup: Group @@ -90,8 +93,6 @@ const useGestures = (rootRef: RefObject) => { columnsAddedToEnd: number } | null>(null) - // we could pre-process each gate - const onDragStretch: Handler<"drag", ThreeEvent> = async ({ first, last, @@ -170,7 +171,6 @@ const useGestures = (rootRef: RefObject) => { } case distance < lastDistance: { if (distance < vanillaColumn.length * columnsAddedToEnd) { - console.log("going down") } // if ( // distance < @@ -204,6 +204,104 @@ const useGestures = (rootRef: RefObject) => { } } + const moveData = useRef<{ + lastPoint: Vector3 + houseObject: Object3D + houseTransformGroupPos0: Vector3 + point0: Vector3 + } | null>(null) + + const onDragMove: Handler<"drag", ThreeEvent> = async ( + state + ) => { + const { + first, + last, + event: { intersections }, + } = state + + pipe( + intersections, + A.head, + O.map(({ point, object }) => { + if (first) { + if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return + + dispatchPointerDown({ point, object }) + + setCameraControlsEnabled(false) + + traverseUpUntil( + object, + (o) => + o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, + (houseTransformGroup) => { + moveData.current = { + houseObject: houseTransformGroup, + lastPoint: point, + houseTransformGroupPos0: houseTransformGroup.position.clone(), + point0: point, + } + } + ) + return + } + + if (!moveData.current) { + console.warn(`no moveData.current in onDragMove`) + return + } + + const [x, z] = pointer.xz + const { houseTransformGroupPos0, point0 } = moveData.current + const delta = new Vector3(x, point0.y, z).sub(point0) + moveData.current.houseObject.position.addVectors( + houseTransformGroupPos0, + delta + ) + + // const { lastPoint } = moveData.current + // const pv = new Vector3(x, 0, z) + // const delta = pv.sub(lastPoint) + // moveData.current.houseObject.position.add(delta) + + if (last) { + setCameraControlsEnabled(true) + moveData.current = null + dispatchPointerUp() + return + } + }) + ) + } + + const rotateData = useRef(null) + + const onDragRotate: Handler< + "drag", + ThreeEvent + > = async ({}) => {} + + const mapNearestCutIntersection = ( + intersections: Intersection[], + f: (ix: Intersection) => void + ) => { + pipe( + intersections, + A.findFirst((ix) => { + const { object, point } = ix + if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) + return false + return ( + ((object as Mesh).material as Material).clippingPlanes as Plane[] + ).every((plane) => { + return plane.distanceToPoint(point) > 0 + }) + }), + O.map(f) + ) + } + return useGesture<{ drag: ThreeEvent hover: ThreeEvent @@ -220,13 +318,31 @@ const useGestures = (rootRef: RefObject) => { SiteCtxModeEnum.Enum.LEVEL, ] - const stretch = stretchModes.includes(siteCtx.mode) + const type = state.event.object.userData?.type + + const stretch = + stretchModes.includes(siteCtx.mode) && + type === UserDataTypeEnum.Enum.StretchHandleMesh + + const move = !stretch && type === UserDataTypeEnum.Enum.ElementMesh + + const rotate = !stretch && type === UserDataTypeEnum.Enum.RotateHandleMesh switch (true) { case stretch: { onDragStretch(state) break } + case move: { + onDragMove(state) + break + } + case rotate: { + onDragRotate(state) + } + default: { + break + } } invalidate() @@ -237,74 +353,62 @@ const useGestures = (rootRef: RefObject) => { if (intersections.length === 0) { document.body.style.cursor = "" dispatchOutline({ - objects: [], + hoveredObjects: [], }) invalidate() // scope.hovered = null return } - pipe( - intersections, - A.findFirst((ix) => { - const { object, point } = ix - if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) - return false - return ( - ((object as Mesh).material as Material).clippingPlanes as Plane[] - ).every((plane) => { - return plane.distanceToPoint(point) > 0 - }) - }), - O.map((intersection) => { - const { object } = intersection + mapNearestCutIntersection(intersections, (intersection) => { + const { object } = intersection - if (hovering) { - document.body.style.cursor = "grab" - } - - if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return - const scopeItem = elementMeshToScopeItem(object) - scope.hovered = scopeItem - switch (siteCtx.mode) { - case SiteCtxModeEnum.Enum.SITE: - dispatchOutline({ - objects: objectToHouseObjects(object), - }) - break - case SiteCtxModeEnum.Enum.BUILDING: - dispatchOutline({ - objects: objectToIfcTagObjects(object), - }) - // object to all of ifc tag - break - case SiteCtxModeEnum.Enum.LEVEL: - // object to all of module group - if (object.parent) { - dispatchOutline({ objects: object.parent.children }) - } - break - } - - // scope.hovered = { - // ...userData.identifier, - // } + if (hovering) { + document.body.style.cursor = "grab" + } - invalidate() - }) - ) + if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return + + const scopeItem = elementMeshToScopeItem(object) + scope.hovered = scopeItem + + switch (siteCtx.mode) { + case SiteCtxModeEnum.Enum.SITE: + dispatchOutline({ + hoveredObjects: objectToHouseObjects(object), + }) + break + case SiteCtxModeEnum.Enum.BUILDING: + dispatchOutline({ + hoveredObjects: objectToIfcTagObjects(object), + }) + // object to all of ifc tag + break + case SiteCtxModeEnum.Enum.LEVEL: + // object to all of module group + if (object.parent) { + dispatchOutline({ hoveredObjects: object.parent.children }) + } + break + } + }) - // const { - // object, - // // object: { userData }, - // } = ix0 - - // if (object.parent?.parent) { - // const objects = object.parent.parent.children.flatMap((x) => x.children) - // dispatchOutline({ - // objects, - // }) - // } + invalidate() + }, + onClick: ({ event: { intersections } }) => { + mapNearestCutIntersection(intersections, (intersection) => { + const { object } = intersection + const scopeItem = elementMeshToScopeItem(object) + scope.selected = scopeItem + + switch (siteCtx.mode) { + case SiteCtxModeEnum.Enum.SITE: + dispatchOutline({ + selectedObjects: objectToHouseObjects(object), + }) + break + } + }) }, onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { event.stopPropagation() diff --git a/app/design/ui-3d/fresh/events/outlines.ts b/app/design/ui-3d/fresh/events/outlines.ts index dc9f45b1..b477ee94 100644 --- a/app/design/ui-3d/fresh/events/outlines.ts +++ b/app/design/ui-3d/fresh/events/outlines.ts @@ -4,7 +4,8 @@ import { Object3D } from "three" const OUTLINE_EVENT = "OutlineEvent" type OutlineEventDetail = { - objects: Object3D[] + hoveredObjects?: Object3D[] + selectedObjects?: Object3D[] } export const dispatchOutline = (detail: OutlineEventDetail) => diff --git a/app/design/ui-3d/fresh/helpers/handles.ts b/app/design/ui-3d/fresh/helpers/handles.ts index ac152d42..cc5262fe 100644 --- a/app/design/ui-3d/fresh/helpers/handles.ts +++ b/app/design/ui-3d/fresh/helpers/handles.ts @@ -1,8 +1,13 @@ -import { Mesh } from "three" +import { CircleGeometry, Group, Mesh, PlaneGeometry } from "three" import { RoundedBoxGeometry } from "three-stdlib" import { PI } from "../../../../utils/math" +import { setInvisible } from "../../../../utils/three" import handleMaterial from "../handleMaterial" -import { StretchHandleMeshUserData, UserDataTypeEnum } from "../userData" +import { + RotateHandleMeshUserData, + StretchHandleMeshUserData, + UserDataTypeEnum, +} from "../userData" export const createStretchHandle = ({ houseId, @@ -50,5 +55,56 @@ export const createStretchHandle = ({ houseId, } as StretchHandleMeshUserData + setInvisible(mesh) + return mesh } + +const ROTATE_HANDLE_OFFSET = 5 +const ROTATE_HANDLE_SIZE = 0.3 +const rotateHandleCircleGeometry = new CircleGeometry(0.5, 16) + +export const createRotateHandles = ({ + width, + length, +}: { + width: number + length: number +}) => { + const userData: RotateHandleMeshUserData = { + type: UserDataTypeEnum.Enum.RotateHandleMesh, + } + + const circleMesh1 = new Mesh(rotateHandleCircleGeometry, handleMaterial) + circleMesh1.position.set(0, 0, -ROTATE_HANDLE_OFFSET) + circleMesh1.rotation.x = -PI / 2 + circleMesh1.userData = userData + + const planeMesh1 = new Mesh( + new PlaneGeometry(ROTATE_HANDLE_SIZE, ROTATE_HANDLE_OFFSET), + handleMaterial + ) + planeMesh1.rotation.x = -PI / 2 + planeMesh1.position.set(0, 0, -ROTATE_HANDLE_OFFSET / 2) + planeMesh1.userData = userData + + const circleMesh2 = new Mesh(rotateHandleCircleGeometry, handleMaterial) + circleMesh2.rotation.x = -PI / 2 + circleMesh2.position.set(-ROTATE_HANDLE_OFFSET - width / 4, 0, length / 2) + circleMesh2.userData = userData + + const planeMesh2 = new Mesh( + new PlaneGeometry(ROTATE_HANDLE_OFFSET, ROTATE_HANDLE_SIZE), + handleMaterial + ) + planeMesh2.rotation.x = -PI / 2 + planeMesh2.position.set(-width / 1.05, 0, length / 2) + planeMesh2.userData = userData + + const handleGroup = new Group() + handleGroup.add(circleMesh1, planeMesh1, circleMesh2, planeMesh2) + + setInvisible(handleGroup) + + return handleGroup +} diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index ff2d2022..688a02f7 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -32,7 +32,7 @@ import { ModuleGroupUserData, UserDataTypeEnum, } from "../userData" -import { createStretchHandle } from "./handles" +import { createRotateHandles, createStretchHandle } from "./handles" export const BIG_CLIP_NUMBER = 999 @@ -214,8 +214,6 @@ export const createColumnGroup = endColumn ? z + module.length / 2 : z - module.length / 2 ) - console.log({ gridGroupY: y, moduleDna: moduleGroup.userData.dna }) - gridGroup.position.setY(y) gridGroup.add(moduleGroup) @@ -379,6 +377,8 @@ export const createLayoutGroup = ({ }) ) + layoutGroup.add(createRotateHandles({ width, length })) + return layoutGroup }) ) diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 876d1f4f..086c7a20 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -8,6 +8,40 @@ import { UserDataTypeEnum, } from "../userData" +export const traverseUpUntil = ( + object: Object3D, + condition: (o: Object3D) => boolean, + callback: (o: Object3D) => void +) => { + if (condition(object)) { + callback(object) + return + } + + const parent = object.parent + + if (parent !== null) { + traverseUpUntil(parent, condition, callback) + } +} + +export const traverseDownUntil = ( + object: Object3D, + condition: (o: Object3D) => boolean, + callback: (o: Object3D) => void +) => { + if (condition(object)) { + callback(object) + return + } + + const children = object.children + + for (let i = 0, l = children.length; i < l; i++) { + traverseDownUntil(children[i], condition, callback) + } +} + export const getHouseTransformGroup = ( rootRef: RefObject, houseId: string diff --git a/app/design/ui-3d/init/Effects.tsx b/app/design/ui-3d/init/Effects.tsx index b0c7b4e3..9e95b700 100644 --- a/app/design/ui-3d/init/Effects.tsx +++ b/app/design/ui-3d/init/Effects.tsx @@ -6,7 +6,8 @@ import { OutlineEffect as OutlineEffectRaw, RenderPass, } from "postprocessing" -import { useEffect, useMemo } from "react" +import { useEffect, useMemo, useRef } from "react" +import { Object3D } from "three" import { useOutlineListener } from "../fresh/events/outlines" export type UseOutlineEffectParams = ConstructorParameters< @@ -37,7 +38,7 @@ const Effects = () => { const renderTarget = useFBO(size.width, size.height, { depthBuffer: true, - stencilBuffer: true, + // stencilBuffer: true, }) // const selectiveBloomEffect = useMemo(() => { @@ -80,8 +81,24 @@ const Effects = () => { // selectiveBloomEffect ]) - useOutlineListener(({ objects }) => { - outlineEffect.selection.set(objects) + const hoveredObjects = useRef([]) + const selectedObjects = useRef([]) + + const setSelection = () => { + outlineEffect.selection.set( + selectedObjects.current.concat(hoveredObjects.current) + ) + } + + useOutlineListener((incoming) => { + if (incoming.hoveredObjects) { + hoveredObjects.current = incoming.hoveredObjects + } else if (incoming.selectedObjects) { + selectedObjects.current = incoming.selectedObjects + } else { + return + } + setSelection() }) // subscribeKey(highlights, "illuminated", () => { diff --git a/app/design/ui/HtmlUi.tsx b/app/design/ui/HtmlUi.tsx index 469e8c9c..c301b436 100644 --- a/app/design/ui/HtmlUi.tsx +++ b/app/design/ui/HtmlUi.tsx @@ -67,7 +67,7 @@ const HtmlUi = (props: Props) => { const { mode } = useSiteCtx() const { hovered, selected } = useScope() - const DEBUG = true + const DEBUG = false return ( From 0f78a32095341d8d115f0dc77fe653a36e840d2e Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 8 Aug 2023 10:44:54 +0100 Subject: [PATCH 083/132] wip move working --- app/design/ui-3d/fresh/FreshApp.tsx | 5 +- app/design/ui-3d/fresh/events/gestures.ts | 109 ++++++++---------- .../ui-3d/fresh/useKeyTestInteractions.ts | 16 +-- 3 files changed, 61 insertions(+), 69 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index bb76e748..5f245df4 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -16,9 +16,8 @@ const FreshApp = () => { return ( - - - + + ) } diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 171d36c7..9f36f9c1 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -93,7 +93,7 @@ const useGestures = (rootRef: RefObject) => { columnsAddedToEnd: number } | null>(null) - const onDragStretch: Handler<"drag", ThreeEvent> = async ({ + const onDragStretch: Handler<"drag", ThreeEvent> = ({ first, last, event, @@ -207,80 +207,72 @@ const useGestures = (rootRef: RefObject) => { const moveData = useRef<{ lastPoint: Vector3 houseObject: Object3D - houseTransformGroupPos0: Vector3 - point0: Vector3 + // houseTransformGroupPos0: Vector3 + // point0: Vector3 } | null>(null) - const onDragMove: Handler<"drag", ThreeEvent> = async ( - state - ) => { + const onDragMove: Handler<"drag", ThreeEvent> = (state) => { const { first, last, - event: { intersections }, + event: { intersections, stopPropagation }, } = state - pipe( - intersections, - A.head, - O.map(({ point, object }) => { - if (first) { - if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return - - dispatchPointerDown({ point, object }) - - setCameraControlsEnabled(false) - - traverseUpUntil( - object, - (o) => - o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, - (houseTransformGroup) => { - moveData.current = { - houseObject: houseTransformGroup, - lastPoint: point, - houseTransformGroupPos0: houseTransformGroup.position.clone(), - point0: point, - } - } - ) - return - } + stopPropagation() + switch (true) { + case first: { + pipe( + intersections, + A.head, + O.map(({ point, object }) => { + if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) + return + + traverseUpUntil( + object, + (o) => + o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, + (houseTransformGroup) => { + dispatchPointerDown({ point, object }) + moveData.current = { + houseObject: houseTransformGroup, + lastPoint: point.setY(0), + } + setCameraControlsEnabled(false) + } + ) + return + }) + ) + break + } + case !first && !last: { if (!moveData.current) { console.warn(`no moveData.current in onDragMove`) return } - const [x, z] = pointer.xz - const { houseTransformGroupPos0, point0 } = moveData.current - const delta = new Vector3(x, point0.y, z).sub(point0) - moveData.current.houseObject.position.addVectors( - houseTransformGroupPos0, - delta - ) - - // const { lastPoint } = moveData.current - // const pv = new Vector3(x, 0, z) - // const delta = pv.sub(lastPoint) - // moveData.current.houseObject.position.add(delta) - - if (last) { - setCameraControlsEnabled(true) - moveData.current = null - dispatchPointerUp() - return - } - }) - ) + const { lastPoint, houseObject } = moveData.current + const [px, pz] = pointer.xz + const thisPoint = new Vector3(px, 0, pz) + const delta = thisPoint.clone().sub(lastPoint) + moveData.current.lastPoint = thisPoint + houseObject.position.add(delta) + return + } + case last: { + setCameraControlsEnabled(true) + moveData.current = null + dispatchPointerUp() + return + } + } } const rotateData = useRef(null) - const onDragRotate: Handler< - "drag", - ThreeEvent - > = async ({}) => {} + const onDragRotate: Handler<"drag", ThreeEvent> = ({}) => {} const mapNearestCutIntersection = ( intersections: Intersection[], @@ -339,6 +331,7 @@ const useGestures = (rootRef: RefObject) => { } case rotate: { onDragRotate(state) + break } default: { break diff --git a/app/design/ui-3d/fresh/useKeyTestInteractions.ts b/app/design/ui-3d/fresh/useKeyTestInteractions.ts index 6fd889fc..512cfd90 100644 --- a/app/design/ui-3d/fresh/useKeyTestInteractions.ts +++ b/app/design/ui-3d/fresh/useKeyTestInteractions.ts @@ -35,55 +35,55 @@ const useKeyTestInteractions = (rootRef: RefObject) => { useKey("z", async () => { for (let houseGroup of getHouseGroups()) { await insertVanillaColumn(houseGroup, 1)() - updateEverything(houseGroup) + // updateEverything(houseGroup) } }) useKey("Z", async () => { for (let houseGroup of getHouseGroups()) { await insertVanillaColumn(houseGroup, -1)() - updateEverything(houseGroup) + // updateEverything(houseGroup) } }) useKey("d", () => { for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, 1) - updateEverything(houseGroup) + // updateEverything(houseGroup) } }) useKey("D", () => { for (let houseGroup of getHouseGroups()) { subtractPenultimateColumn(houseGroup, -1) - updateEverything(houseGroup) + // updateEverything(houseGroup) } }) useKey("t", () => { for (let houseGroup of getHouseGroups()) { houseGroup.position.add(new Vector3(1, 0, 1)) - updateEverything(houseGroup) + // updateEverything(houseGroup) } }) useKey("T", () => { for (let houseGroup of getHouseGroups()) { houseGroup.position.add(new Vector3(-1, 0, -1)) - updateEverything(houseGroup) + // updateEverything(houseGroup) } }) useKey("r", () => { for (let houseGroup of getHouseGroups()) { houseGroup.rotateOnAxis(yAxis, PI / 8) - updateEverything(houseGroup) + // updateEverything(houseGroup) } }) useKey("R", () => { for (let houseGroup of getHouseGroups()) { houseGroup.rotateOnAxis(yAxis, -PI / 8) - updateEverything(houseGroup) + // updateEverything(houseGroup) } }) From 1c6d7e4912dc36716d87150d0192efe5d4a6fd9f Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 9 Aug 2023 11:58:05 +0100 Subject: [PATCH 084/132] wip rotate mostly working --- app/design/ui-3d/fresh/dimensions.ts | 18 +- app/design/ui-3d/fresh/events/gestures.ts | 275 +++++++++++++----- app/design/ui-3d/fresh/helpers/handles.ts | 57 +++- .../ui-3d/fresh/helpers/sceneQueries.ts | 40 ++- app/design/ui-3d/fresh/userData.ts | 7 +- app/utils/math.ts | 2 +- 6 files changed, 302 insertions(+), 97 deletions(-) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 2f1d10eb..4925f9c1 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -42,7 +42,7 @@ const renderOBB = (obb: OBB, scene: Object3D) => { lastMesh = mesh } -export const updateHouseOBB = (houseTransformsGroup: Group) => { +export const updateHouseOBB = (houseTransformsGroup: Object3D) => { const { width, height, length } = getActiveHouseUserData(houseTransformsGroup) const activeLayoutGroup = getActiveLayoutGroup(houseTransformsGroup) @@ -127,6 +127,22 @@ const updateDnas = (houseGroup: Group) => { houseGroup.userData.dnas = result.flat() } +export const updateIndexedHouseTransforms = ( + houseTransformsGroup: Object3D +) => { + const { houseId } = getActiveHouseUserData(houseTransformsGroup) + + const rotation = houseTransformsGroup.rotation.y + const position = houseTransformsGroup.position + + userDB.houses.update(houseId, { + position, + rotation, + }) + + updateHouseOBB(houseTransformsGroup) +} + export const updateEverything = (houseTransformsGroup: Group) => { updateHouseLength(houseTransformsGroup) updateHouseOBB(houseTransformsGroup) diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 9f36f9c1..7b68f18c 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -1,5 +1,5 @@ import { invalidate, ThreeEvent } from "@react-three/fiber" -import { Handler, useGesture, UserHandlers } from "@use-gesture/react" +import { Handler, useGesture } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { RefObject, useRef } from "react" import { useEvent } from "react-use" @@ -14,6 +14,7 @@ import { } from "three" import { z } from "zod" import { A, O } from "../../../../utils/functions" +import { atan2 } from "../../../../utils/math" import { isMesh } from "../../../../utils/three" import { setCameraControlsEnabled } from "../../../state/camera" import { openMenu } from "../../../state/menu" @@ -24,18 +25,17 @@ import siteCtx, { SiteCtxMode, SiteCtxModeEnum, } from "../../../state/siteCtx" +import { updateIndexedHouseTransforms } from "../dimensions" import { getActiveHouseUserData, - getHouseGroupColumns, - getHouseTransformGroup, handleColumnGroupParentQuery, rootHouseGroupParentQuery, + traverseDownUntil, traverseUpUntil, } from "../helpers/sceneQueries" import { insertVanillaColumn } from "../helpers/stretchZ" import { elementMeshToScopeItem, - ElementMeshUserData, GridGroupUserData, HouseTransformsGroupUserData, StretchHandleMeshUserData, @@ -82,6 +82,33 @@ const objectToIfcTagObjects = (object: Object3D) => { ) } +const mapNearestCutIntersection = ( + intersections: Intersection[], + f: (ix: Intersection) => void +) => { + pipe( + intersections, + A.findFirst((ix) => { + const { object, point } = ix + switch (object.userData.type) { + case UserDataTypeEnum.Enum.ElementMesh: { + return ( + ((object as Mesh).material as Material).clippingPlanes as Plane[] + ).every((plane) => { + return plane.distanceToPoint(point) > 0 + }) + } + case UserDataTypeEnum.Enum.RotateHandleMesh: + case UserDataTypeEnum.Enum.StretchHandleMesh: + return true + default: + return false + } + }), + O.map(f) + ) +} + const useGestures = (rootRef: RefObject) => { const stretchData = useRef<{ handleObject: Object3D @@ -116,7 +143,15 @@ const useGestures = (rootRef: RefObject) => { dispatchPointerDown({ point, object }) break } - case !first && !last: { + case last: { + if (stretchData.current === null) + throw new Error("stretchData.current null unexpectedly") + dispatchPointerUp() + stretchData.current = null + setCameraControlsEnabled(true) + break + } + default: { if (!stretchData.current) throw new Error("first didn't set first") const { @@ -193,22 +228,21 @@ const useGestures = (rootRef: RefObject) => { break } - case last: { - if (stretchData.current === null) - throw new Error("stretchData.current null unexpectedly") - dispatchPointerUp() - stretchData.current = null - setCameraControlsEnabled(true) - break - } } } + const activeRotateHandlesRef = useRef(null) + const updateActiveRotateHandles = (object: Object3D) => { + if (activeRotateHandlesRef.current !== null) { + activeRotateHandlesRef.current.visible = false + } + activeRotateHandlesRef.current = object + activeRotateHandlesRef.current.visible = true + } + const moveData = useRef<{ lastPoint: Vector3 - houseObject: Object3D - // houseTransformGroupPos0: Vector3 - // point0: Vector3 + houseObject: Group } | null>(null) const onDragMove: Handler<"drag", ThreeEvent> = (state) => { @@ -235,11 +269,32 @@ const useGestures = (rootRef: RefObject) => { o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, (houseTransformGroup) => { dispatchPointerDown({ point, object }) + moveData.current = { - houseObject: houseTransformGroup, + houseObject: houseTransformGroup as Group, lastPoint: point.setY(0), } + setCameraControlsEnabled(false) + + const scopeItem = elementMeshToScopeItem(object) + scope.selected = scopeItem + + dispatchOutline({ + selectedObjects: objectToHouseObjects(object), + }) + + traverseDownUntil(houseTransformGroup, (object) => { + if ( + object.userData.type === + UserDataTypeEnum.Enum.RotateHandlesGroup + ) { + console.log(object.uuid) + updateActiveRotateHandles(object) + return true + } + return false + }) } ) return @@ -247,7 +302,14 @@ const useGestures = (rootRef: RefObject) => { ) break } - case !first && !last: { + case last: { + setCameraControlsEnabled(true) + updateIndexedHouseTransforms(moveData.current!.houseObject) + moveData.current = null + dispatchPointerUp() + return + } + default: { if (!moveData.current) { console.warn(`no moveData.current in onDragMove`) return @@ -261,37 +323,96 @@ const useGestures = (rootRef: RefObject) => { houseObject.position.add(delta) return } + } + } + + const rotateData = useRef<{ + houseTransformsGroup: Object3D + center: Vector3 + rotation0: number + angle0: number + angle: number + } | null>(null) + + const onDragRotate: Handler<"drag", ThreeEvent> = (state) => { + const { + first, + last, + event: { intersections, stopPropagation }, + } = state + + stopPropagation() + + switch (true) { + case first: { + pipe( + intersections, + A.head, + O.map(({ point, object }) => { + if (object.userData.type !== UserDataTypeEnum.Enum.RotateHandleMesh) + return + + traverseUpUntil( + object, + (o) => + o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, + (houseTransformsGroup) => { + dispatchPointerDown({ point, object }) + + const { + obb: { + center, + center: { x: cx, z: cz }, + }, + } = getActiveHouseUserData(houseTransformsGroup) + + const { x: x0, z: z0 } = point + + const angle0 = atan2(cz - z0, cx - x0) + + rotateData.current = { + houseTransformsGroup, + center, + rotation0: houseTransformsGroup.rotation.y, + angle0, + angle: angle0, + } + + setCameraControlsEnabled(false) + } + ) + return + }) + ) + break + } case last: { - setCameraControlsEnabled(true) - moveData.current = null dispatchPointerUp() - return + updateIndexedHouseTransforms(rotateData.current!.houseTransformsGroup) + setCameraControlsEnabled(true) + rotateData.current = null + break } - } - } + default: { + if (!rotateData.current) + throw new Error(`no rotateData in onDragRotate progress`) - const rotateData = useRef(null) + const [px, pz] = pointer.xz - const onDragRotate: Handler<"drag", ThreeEvent> = ({}) => {} + const { + center: { x: cx, z: cz }, + houseTransformsGroup, + } = rotateData.current - const mapNearestCutIntersection = ( - intersections: Intersection[], - f: (ix: Intersection) => void - ) => { - pipe( - intersections, - A.findFirst((ix) => { - const { object, point } = ix - if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) - return false - return ( - ((object as Mesh).material as Material).clippingPlanes as Plane[] - ).every((plane) => { - return plane.distanceToPoint(point) > 0 - }) - }), - O.map(f) - ) + rotateData.current.angle = atan2(cz - pz, cx - px) + + houseTransformsGroup.rotation.y = + rotateData.current.rotation0 - + (rotateData.current.angle - rotateData.current.angle0) + + break + } + } } return useGesture<{ @@ -356,33 +477,45 @@ const useGestures = (rootRef: RefObject) => { mapNearestCutIntersection(intersections, (intersection) => { const { object } = intersection - if (hovering) { - document.body.style.cursor = "grab" - } + switch (object.userData.type) { + case UserDataTypeEnum.Enum.ElementMesh: { + const scopeItem = elementMeshToScopeItem(object) + scope.hovered = scopeItem - if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return - - const scopeItem = elementMeshToScopeItem(object) - scope.hovered = scopeItem - - switch (siteCtx.mode) { - case SiteCtxModeEnum.Enum.SITE: - dispatchOutline({ - hoveredObjects: objectToHouseObjects(object), - }) - break - case SiteCtxModeEnum.Enum.BUILDING: - dispatchOutline({ - hoveredObjects: objectToIfcTagObjects(object), - }) - // object to all of ifc tag + switch (siteCtx.mode) { + case SiteCtxModeEnum.Enum.SITE: + if (hovering) { + document.body.style.cursor = "grab" + } + dispatchOutline({ + hoveredObjects: objectToHouseObjects(object), + }) + break + case SiteCtxModeEnum.Enum.BUILDING: + dispatchOutline({ + hoveredObjects: objectToIfcTagObjects(object), + }) + // object to all of ifc tag + break + case SiteCtxModeEnum.Enum.LEVEL: + // object to all of module group + if (object.parent) { + dispatchOutline({ hoveredObjects: object.parent.children }) + } + break + } break - case SiteCtxModeEnum.Enum.LEVEL: - // object to all of module group - if (object.parent) { - dispatchOutline({ hoveredObjects: object.parent.children }) + } + case UserDataTypeEnum.Enum.StretchHandleMesh: + case UserDataTypeEnum.Enum.RotateHandleMesh: { + if (hovering) { + document.body.style.cursor = "grab" } break + } + default: { + break + } } }) @@ -391,16 +524,6 @@ const useGestures = (rootRef: RefObject) => { onClick: ({ event: { intersections } }) => { mapNearestCutIntersection(intersections, (intersection) => { const { object } = intersection - const scopeItem = elementMeshToScopeItem(object) - scope.selected = scopeItem - - switch (siteCtx.mode) { - case SiteCtxModeEnum.Enum.SITE: - dispatchOutline({ - selectedObjects: objectToHouseObjects(object), - }) - break - } }) }, onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { diff --git a/app/design/ui-3d/fresh/helpers/handles.ts b/app/design/ui-3d/fresh/helpers/handles.ts index cc5262fe..f535aa95 100644 --- a/app/design/ui-3d/fresh/helpers/handles.ts +++ b/app/design/ui-3d/fresh/helpers/handles.ts @@ -1,13 +1,32 @@ -import { CircleGeometry, Group, Mesh, PlaneGeometry } from "three" +import { CircleGeometry, Group, Mesh, Object3D, PlaneGeometry } from "three" import { RoundedBoxGeometry } from "three-stdlib" import { PI } from "../../../../utils/math" -import { setInvisible } from "../../../../utils/three" +import { setInvisible, setVisibleAndRaycast } from "../../../../utils/three" import handleMaterial from "../handleMaterial" import { RotateHandleMeshUserData, + RotateHandlesGroupUserData, StretchHandleMeshUserData, UserDataTypeEnum, } from "../userData" +import { traverseDownUntil } from "./sceneQueries" + +export const setStretchHandleVisibility = ( + houseTransformGroup: Object3D, + value: boolean +) => { + let handleCount = 2 + traverseDownUntil(houseTransformGroup, (object) => { + if (handleCount === 0) return true + if (object.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh) { + if (value) setVisibleAndRaycast(object) + else setInvisible(object) + console.log(object) + handleCount-- + } + return false + }) +} export const createStretchHandle = ({ houseId, @@ -71,14 +90,14 @@ export const createRotateHandles = ({ width: number length: number }) => { - const userData: RotateHandleMeshUserData = { + const meshUserData: RotateHandleMeshUserData = { type: UserDataTypeEnum.Enum.RotateHandleMesh, } const circleMesh1 = new Mesh(rotateHandleCircleGeometry, handleMaterial) circleMesh1.position.set(0, 0, -ROTATE_HANDLE_OFFSET) circleMesh1.rotation.x = -PI / 2 - circleMesh1.userData = userData + circleMesh1.userData = meshUserData const planeMesh1 = new Mesh( new PlaneGeometry(ROTATE_HANDLE_SIZE, ROTATE_HANDLE_OFFSET), @@ -86,12 +105,12 @@ export const createRotateHandles = ({ ) planeMesh1.rotation.x = -PI / 2 planeMesh1.position.set(0, 0, -ROTATE_HANDLE_OFFSET / 2) - planeMesh1.userData = userData + planeMesh1.userData = meshUserData const circleMesh2 = new Mesh(rotateHandleCircleGeometry, handleMaterial) circleMesh2.rotation.x = -PI / 2 circleMesh2.position.set(-ROTATE_HANDLE_OFFSET - width / 4, 0, length / 2) - circleMesh2.userData = userData + circleMesh2.userData = meshUserData const planeMesh2 = new Mesh( new PlaneGeometry(ROTATE_HANDLE_OFFSET, ROTATE_HANDLE_SIZE), @@ -99,12 +118,34 @@ export const createRotateHandles = ({ ) planeMesh2.rotation.x = -PI / 2 planeMesh2.position.set(-width / 1.05, 0, length / 2) - planeMesh2.userData = userData + planeMesh2.userData = meshUserData const handleGroup = new Group() handleGroup.add(circleMesh1, planeMesh1, circleMesh2, planeMesh2) - setInvisible(handleGroup) + const groupUserData: RotateHandlesGroupUserData = { + type: UserDataTypeEnum.Enum.RotateHandlesGroup, + } + + handleGroup.userData = groupUserData + + handleGroup.visible = false + + // setInvisible(handleGroup) return handleGroup } + +// export const setRotateHandleVisibility = ( +// houseTransformGroup: Object3D, +// value: boolean +// ) => { +// traverseDownUntil(houseTransformGroup, (object) => { +// if (object.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup) { +// object.visible = value +// o = object +// return true +// } +// return false +// }) +// } diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 086c7a20..fa863abe 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -24,24 +24,42 @@ export const traverseUpUntil = ( traverseUpUntil(parent, condition, callback) } } - export const traverseDownUntil = ( object: Object3D, - condition: (o: Object3D) => boolean, - callback: (o: Object3D) => void -) => { - if (condition(object)) { - callback(object) - return + callback: (o: Object3D) => boolean // stops if returns true +): boolean => { + if (callback(object)) { + return true } const children = object.children for (let i = 0, l = children.length; i < l; i++) { - traverseDownUntil(children[i], condition, callback) + if (traverseDownUntil(children[i], callback)) { + return true + } } + + return false } +// export const traverseDownUntil = ( +// object: Object3D, +// condition: (o: Object3D) => boolean, +// callback: (o: Object3D) => void +// ) => { +// if (condition(object)) { +// callback(object) +// return +// } + +// const children = object.children + +// for (let i = 0, l = children.length; i < l; i++) { +// traverseDownUntil(children[i], condition, callback) +// } +// } + export const getHouseTransformGroup = ( rootRef: RefObject, houseId: string @@ -92,7 +110,7 @@ export const getHouseGroupColumns = (houseGroup: Group) => someOrError("no columns container in house group") ) -export const getActiveHouseUserData = (houseTransformsGroup: Group) => +export const getActiveHouseUserData = (houseTransformsGroup: Object3D) => pipe( houseTransformsGroup.children, A.findFirstMap((x) => @@ -113,7 +131,9 @@ export const getLayoutGroups = (houseTransformsGroup: Group): Group[] => (x) => x.userData.type === UserDataTypeEnum.Enum.HouseLayoutGroup ) as Group[] -export const getActiveLayoutGroup = (houseTransformsGroup: Group): Group => +export const getActiveLayoutGroup = ( + houseTransformsGroup: Object3D +): Object3D => pipe( houseTransformsGroup.children, A.findFirst( diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index c4cbd9af..9503111b 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -25,6 +25,7 @@ export const UserDataTypeEnum = z.enum([ "ModuleGroup", "ElementMesh", "StretchHandleMesh", + "RotateHandlesGroup", "RotateHandleMesh", ]) export type UserDataTypeEnum = z.infer @@ -92,6 +93,10 @@ export type StretchHandleMeshUserData = { houseId: string } +export type RotateHandlesGroupUserData = { + type: typeof UserDataTypeEnum.Enum.RotateHandlesGroup +} + export type RotateHandleMeshUserData = { type: typeof UserDataTypeEnum.Enum.RotateHandleMesh } @@ -106,7 +111,7 @@ export type UserData = | HouseLayoutGroupUserData | HouseTransformsGroupUserData | StretchHandleMeshUserData - | RotateHandleMeshUserData + | RotateHandlesGroupUserData export const incrementColumnCount = (layoutGroup: Group) => { const userData = layoutGroup.userData as HouseLayoutGroupUserData diff --git a/app/utils/math.ts b/app/utils/math.ts index 5ee6ea07..32af5b65 100644 --- a/app/utils/math.ts +++ b/app/utils/math.ts @@ -1,6 +1,6 @@ import { A } from "./functions" -export const { abs, min, max, ceil, floor, round, PI, sign, sqrt } = Math +export const { abs, atan2, min, max, ceil, floor, round, PI, sign, sqrt } = Math // export { abs, min, max } From f665ccec08739acc4e31a75b46a5eedefdb6a1a8 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 9 Aug 2023 12:37:27 +0100 Subject: [PATCH 085/132] wip rotate move very nice --- app/design/ui-3d/XZPlane.tsx | 3 ++- app/design/ui-3d/fresh/events/gestures.ts | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/design/ui-3d/XZPlane.tsx b/app/design/ui-3d/XZPlane.tsx index e81c5e1f..cdcc49de 100644 --- a/app/design/ui-3d/XZPlane.tsx +++ b/app/design/ui-3d/XZPlane.tsx @@ -23,8 +23,9 @@ const XZPlane = forwardRef((props, ref) => { const localRef = useRef(null) const { size = DEFAULT_SIZE } = props - usePointerDownListener(({ point: { y } }) => { + usePointerDownListener(({ point: { x, y, z } }) => { if (!localRef.current) return + pointer.xz = [x, z] localRef.current.position.setY(y) localRef.current.layers.set(RaycasterLayer.ENABLED) if (DEBUG) { diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts index 7b68f18c..6678f5d1 100644 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ b/app/design/ui-3d/fresh/events/gestures.ts @@ -289,7 +289,6 @@ const useGestures = (rootRef: RefObject) => { object.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup ) { - console.log(object.uuid) updateActiveRotateHandles(object) return true } @@ -406,9 +405,11 @@ const useGestures = (rootRef: RefObject) => { rotateData.current.angle = atan2(cz - pz, cx - px) + const angleDifference = + rotateData.current.angle - rotateData.current.angle0 + houseTransformsGroup.rotation.y = - rotateData.current.rotation0 - - (rotateData.current.angle - rotateData.current.angle0) + rotateData.current.rotation0 - angleDifference break } From 46044c529df634e8a97ee478c1affeaecd44ed20 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 9 Aug 2023 12:42:26 +0100 Subject: [PATCH 086/132] wip type updates --- app/design/ui-3d/fresh/helpers/sceneQueries.ts | 10 +++++----- app/design/ui-3d/fresh/userData.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index fa863abe..46746d38 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -61,19 +61,19 @@ export const traverseDownUntil = ( // } export const getHouseTransformGroup = ( - rootRef: RefObject, + rootRef: RefObject, houseId: string ) => pipe( rootRef.current?.children, O.fromNullable, O.chain(A.findFirst((x) => x.userData.houseId === houseId)) - ) as O.Option + ) as O.Option export const mapHouseTransformGroup = ( - rootRef: RefObject, + rootRef: RefObject, houseId: string, - f: (houseTransformGroup: Group) => void + f: (houseTransformGroup: Object3D) => void ) => pipe(getHouseTransformGroup(rootRef, houseId), O.map(f)) export const rootHouseGroupParentQuery = (object: Object3D) => { @@ -152,7 +152,7 @@ export const getPartitionedLayoutGroups = (houseTransformsGroup: Group) => A.partition((x) => x.uuid === houseTransformsGroup.userData.activeChildUuid) ) -export const getLayoutGroupColumnGroups = (layoutGroup: Group): Group[] => +export const getLayoutGroupColumnGroups = (layoutGroup: Object3D): Object3D[] => layoutGroup.children.filter( (x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup ) as Group[] diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 9503111b..9e894923 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -113,14 +113,14 @@ export type UserData = | StretchHandleMeshUserData | RotateHandlesGroupUserData -export const incrementColumnCount = (layoutGroup: Group) => { +export const incrementColumnCount = (layoutGroup: Object3D) => { const userData = layoutGroup.userData as HouseLayoutGroupUserData if (userData.type !== UserDataTypeEnum.Enum.HouseLayoutGroup) throw new Error(`incrementColumnCount called on ${userData.type}`) userData.columnCount++ } -export const decrementColumnCount = (layoutGroup: Group) => { +export const decrementColumnCount = (layoutGroup: Object3D) => { const userData = layoutGroup.userData as HouseLayoutGroupUserData if (userData.type !== UserDataTypeEnum.Enum.HouseLayoutGroup) throw new Error(`incrementColumnCount called on ${userData.type}`) From 2dff242c8886446241c1820dada58e92137fa488 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 10 Aug 2023 12:00:59 +0100 Subject: [PATCH 087/132] wip handles show/hide improvement --- app/design/ui-3d/XZPlane.tsx | 2 +- app/design/ui-3d/fresh/FreshApp.tsx | 119 +++- app/design/ui-3d/fresh/events/gestures.ts | 573 ------------------ app/design/ui-3d/fresh/gestures/events.ts | 25 + app/design/ui-3d/fresh/gestures/index.ts | 189 ++++++ app/design/ui-3d/fresh/gestures/move.ts | 98 +++ app/design/ui-3d/fresh/gestures/rotate.ts | 113 ++++ app/design/ui-3d/fresh/gestures/stretch.ts | 170 ++++++ .../ui-3d/fresh/helpers/clippingPlanes.ts | 26 +- .../ui-3d/fresh/helpers/sceneQueries.ts | 46 +- 10 files changed, 757 insertions(+), 604 deletions(-) delete mode 100644 app/design/ui-3d/fresh/events/gestures.ts create mode 100644 app/design/ui-3d/fresh/gestures/events.ts create mode 100644 app/design/ui-3d/fresh/gestures/index.ts create mode 100644 app/design/ui-3d/fresh/gestures/move.ts create mode 100644 app/design/ui-3d/fresh/gestures/rotate.ts create mode 100644 app/design/ui-3d/fresh/gestures/stretch.ts diff --git a/app/design/ui-3d/XZPlane.tsx b/app/design/ui-3d/XZPlane.tsx index cdcc49de..3be5f257 100644 --- a/app/design/ui-3d/XZPlane.tsx +++ b/app/design/ui-3d/XZPlane.tsx @@ -8,7 +8,7 @@ import pointer from "../state/pointer" import { usePointerDownListener, usePointerUpListener, -} from "./fresh/events/gestures" +} from "./fresh/gestures/events" const DEBUG = false diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 5f245df4..b704f095 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,18 +1,129 @@ import { Fragment, useRef } from "react" -import { Group } from "three" +import { Group, Object3D } from "three" import { useHousesEvents } from "./events/houses" -import useGestures from "./events/gestures" +import useGestures from "./gestures" import useKeyTestInteractions from "./useKeyTestInteractions" import XZPlane from "../XZPlane" +import siteCtx, { + SiteCtxMode, + SiteCtxModeEnum, + useModeChangeListener, +} from "../../state/siteCtx" +import useClippingPlaneHelpers from "./helpers/clippingPlanes" +import { pipe } from "fp-ts/lib/function" +import { O } from "../../../utils/functions" +import { BIG_CLIP_NUMBER } from "./helpers/layouts" +import { invalidate } from "@react-three/fiber" +import { + getHouseTransformGroup, + traverseDownUntil, +} from "./helpers/sceneQueries" +import { UserDataTypeEnum } from "./userData" +import { useSubscribeKey } from "../../../utils/hooks" +import scope, { ScopeItem } from "../../state/scope" const FreshApp = () => { const rootRef = useRef(null) useHousesEvents(rootRef) - const bindAll = useGestures(rootRef) + const bindAll = useGestures() - useKeyTestInteractions(rootRef) + // useKeyTestInteractions(rootRef) + + const showHouseStretchHandles = (houseId: string) => { + pipe( + getHouseTransformGroup(rootRef, houseId), + O.map((houseTransformGroup) => { + houseTransformGroup.traverse((node) => { + if (node.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh) { + node.visible = true + } + }) + }) + ) + } + + const hideAllHandles = () => { + rootRef.current?.traverse((node) => { + if ( + node.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh || + node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup + ) { + node.visible = false + } + }) + } + + const { houseLevelIndexToCutHeight, setYCut } = + useClippingPlaneHelpers(rootRef) + + // const activeRotateHandlesRef = useRef(null) + // const updateActiveRotateHandles = (object: Object3D) => { + // if (activeRotateHandlesRef.current !== null) { + // activeRotateHandlesRef.current.visible = false + // } + // activeRotateHandlesRef.current = object + // activeRotateHandlesRef.current.visible = true + // } + + const showHouseRotateHandles = (houseId: string) => { + pipe( + getHouseTransformGroup(rootRef, houseId), + O.map((houseTransformGroup) => { + houseTransformGroup.traverse((node) => { + if (node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup) { + node.visible = true + } + }) + }) + ) + } + + const f = () => { + const { houseId, levelIndex, mode } = siteCtx + const { selected } = scope + + const stretchModes: SiteCtxMode[] = [ + SiteCtxModeEnum.Enum.BUILDING, + SiteCtxModeEnum.Enum.LEVEL, + ] + + hideAllHandles() + + if (stretchModes.includes(mode)) { + if (houseId) showHouseStretchHandles(houseId) + } + + if (mode === SiteCtxModeEnum.Enum.LEVEL) { + if (houseId !== null && levelIndex !== null) { + pipe( + houseLevelIndexToCutHeight(houseId, levelIndex), + O.map((cutHeight) => { + setYCut(houseId, cutHeight) + }) + ) + } + } + + if (mode === SiteCtxModeEnum.Enum.SITE) { + if (selected !== null) { + const { houseId } = selected + showHouseRotateHandles(houseId) + } + } + + // switch (true) { + // default: + // if (houseId === null) break + // setYCut(houseId, BIG_CLIP_NUMBER) + // } + + invalidate() + } + + useSubscribeKey(scope, "selected", f) + useModeChangeListener(f) return ( diff --git a/app/design/ui-3d/fresh/events/gestures.ts b/app/design/ui-3d/fresh/events/gestures.ts deleted file mode 100644 index 6678f5d1..00000000 --- a/app/design/ui-3d/fresh/events/gestures.ts +++ /dev/null @@ -1,573 +0,0 @@ -import { invalidate, ThreeEvent } from "@react-three/fiber" -import { Handler, useGesture } from "@use-gesture/react" -import { pipe } from "fp-ts/lib/function" -import { RefObject, useRef } from "react" -import { useEvent } from "react-use" -import { - Group, - Intersection, - Material, - Mesh, - Object3D, - Plane, - Vector3, -} from "three" -import { z } from "zod" -import { A, O } from "../../../../utils/functions" -import { atan2 } from "../../../../utils/math" -import { isMesh } from "../../../../utils/three" -import { setCameraControlsEnabled } from "../../../state/camera" -import { openMenu } from "../../../state/menu" -import pointer from "../../../state/pointer" -import scope, { ScopeItem } from "../../../state/scope" -import siteCtx, { - downMode, - SiteCtxMode, - SiteCtxModeEnum, -} from "../../../state/siteCtx" -import { updateIndexedHouseTransforms } from "../dimensions" -import { - getActiveHouseUserData, - handleColumnGroupParentQuery, - rootHouseGroupParentQuery, - traverseDownUntil, - traverseUpUntil, -} from "../helpers/sceneQueries" -import { insertVanillaColumn } from "../helpers/stretchZ" -import { - elementMeshToScopeItem, - GridGroupUserData, - HouseTransformsGroupUserData, - StretchHandleMeshUserData, - UserDataTypeEnum, -} from "../userData" -import { dispatchOutline } from "./outlines" - -export const GestureEventType = z.enum(["POINTER_DOWN", "POINTER_UP"]) - -export type GestureEventType = z.infer - -export type GestureEventDetail = { - point: V3 - object: Object3D -} - -export const usePointerDownListener = ( - f: (eventDetail: GestureEventDetail) => void -) => useEvent(GestureEventType.Enum.POINTER_DOWN, ({ detail }) => f(detail)) - -export const dispatchPointerDown = (detail: GestureEventDetail) => - dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_DOWN, { detail })) - -export const usePointerUpListener = (f: () => void) => - useEvent(GestureEventType.Enum.POINTER_UP, () => f()) - -export const dispatchPointerUp = () => - dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_UP)) - -const objectToHouseObjects = (object: Object3D) => - object.parent!.parent!.parent!.parent!.children.flatMap((x) => - x.children.flatMap((y) => y.children.flatMap((z) => z.children)) - ) - -const objectToIfcTagObjects = (object: Object3D) => { - const ifcTag: string = object.userData.ifcTag - - return object.parent!.parent!.parent!.parent!.children.flatMap((x) => - x.children.flatMap((y) => - y.children.flatMap((z) => - z.children.filter((x) => x.userData.ifcTag === ifcTag) - ) - ) - ) -} - -const mapNearestCutIntersection = ( - intersections: Intersection[], - f: (ix: Intersection) => void -) => { - pipe( - intersections, - A.findFirst((ix) => { - const { object, point } = ix - switch (object.userData.type) { - case UserDataTypeEnum.Enum.ElementMesh: { - return ( - ((object as Mesh).material as Material).clippingPlanes as Plane[] - ).every((plane) => { - return plane.distanceToPoint(point) > 0 - }) - } - case UserDataTypeEnum.Enum.RotateHandleMesh: - case UserDataTypeEnum.Enum.StretchHandleMesh: - return true - default: - return false - } - }), - O.map(f) - ) -} - -const useGestures = (rootRef: RefObject) => { - const stretchData = useRef<{ - handleObject: Object3D - houseGroup: Group - handleGroup: Group - point0: Vector3 - handleGroupPos0: Vector3 - lastDistance: number - columnsAddedToEnd: number - } | null>(null) - - const onDragStretch: Handler<"drag", ThreeEvent> = ({ - first, - last, - event, - event: { object, point }, - }) => { - switch (true) { - case first: { - setCameraControlsEnabled(false) - const handleGroup = handleColumnGroupParentQuery(object) - const houseGroup = rootHouseGroupParentQuery(object) - stretchData.current = { - handleObject: object, - houseGroup, - handleGroup, - handleGroupPos0: handleGroup.position.clone(), - point0: point, - lastDistance: 0, - columnsAddedToEnd: 0, - } - dispatchPointerDown({ point, object }) - break - } - case last: { - if (stretchData.current === null) - throw new Error("stretchData.current null unexpectedly") - dispatchPointerUp() - stretchData.current = null - setCameraControlsEnabled(true) - break - } - default: { - if (!stretchData.current) throw new Error("first didn't set first") - - const { - handleGroup, - handleGroupPos0, - point0, - houseGroup, - handleObject, - lastDistance, - columnsAddedToEnd, - } = stretchData.current - - const { vanillaColumn } = getActiveHouseUserData(houseGroup) - - const { direction, axis } = - handleObject.userData as StretchHandleMeshUserData - - switch (axis) { - case "z": - switch (direction) { - case 1: { - const [x1, z1] = pointer.xz - const distanceVector = new Vector3(x1, 0, z1).sub(point0) - distanceVector.applyAxisAngle( - new Vector3(0, 1, 0), - -houseGroup.rotation.y - ) - const distance = distanceVector.z - - handleGroup.position.set(0, 0, handleGroupPos0.z + distance) - - // maybe we want to oper on the house group columns doodah itself - - // and then constantly be computing whether we want to add or subtract - - // so we're tracking the next gate up or down - - // probably want to just set visible false and turn off raycasting - // when we delete columns - - // let's work on adding some first - - // gate 1 = z'end + vanillaColumnLength - - switch (true) { - case distance > lastDistance: { - if (distance > vanillaColumn.length * columnsAddedToEnd) { - insertVanillaColumn(houseGroup, 1)() - stretchData.current.columnsAddedToEnd++ - } - break - } - case distance < lastDistance: { - if (distance < vanillaColumn.length * columnsAddedToEnd) { - } - // if ( - // distance < - // vanillaColumnLength * columnsDelta + vanillaColumnLength - // ) { - // stretchData.current.columnsDelta++ - // } - } - } - stretchData.current.lastDistance = distance - - // gates on err... - - // vanilla column length up - - // existing column length down - } - } - } - - break - } - } - } - - const activeRotateHandlesRef = useRef(null) - const updateActiveRotateHandles = (object: Object3D) => { - if (activeRotateHandlesRef.current !== null) { - activeRotateHandlesRef.current.visible = false - } - activeRotateHandlesRef.current = object - activeRotateHandlesRef.current.visible = true - } - - const moveData = useRef<{ - lastPoint: Vector3 - houseObject: Group - } | null>(null) - - const onDragMove: Handler<"drag", ThreeEvent> = (state) => { - const { - first, - last, - event: { intersections, stopPropagation }, - } = state - - stopPropagation() - - switch (true) { - case first: { - pipe( - intersections, - A.head, - O.map(({ point, object }) => { - if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) - return - - traverseUpUntil( - object, - (o) => - o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, - (houseTransformGroup) => { - dispatchPointerDown({ point, object }) - - moveData.current = { - houseObject: houseTransformGroup as Group, - lastPoint: point.setY(0), - } - - setCameraControlsEnabled(false) - - const scopeItem = elementMeshToScopeItem(object) - scope.selected = scopeItem - - dispatchOutline({ - selectedObjects: objectToHouseObjects(object), - }) - - traverseDownUntil(houseTransformGroup, (object) => { - if ( - object.userData.type === - UserDataTypeEnum.Enum.RotateHandlesGroup - ) { - updateActiveRotateHandles(object) - return true - } - return false - }) - } - ) - return - }) - ) - break - } - case last: { - setCameraControlsEnabled(true) - updateIndexedHouseTransforms(moveData.current!.houseObject) - moveData.current = null - dispatchPointerUp() - return - } - default: { - if (!moveData.current) { - console.warn(`no moveData.current in onDragMove`) - return - } - - const { lastPoint, houseObject } = moveData.current - const [px, pz] = pointer.xz - const thisPoint = new Vector3(px, 0, pz) - const delta = thisPoint.clone().sub(lastPoint) - moveData.current.lastPoint = thisPoint - houseObject.position.add(delta) - return - } - } - } - - const rotateData = useRef<{ - houseTransformsGroup: Object3D - center: Vector3 - rotation0: number - angle0: number - angle: number - } | null>(null) - - const onDragRotate: Handler<"drag", ThreeEvent> = (state) => { - const { - first, - last, - event: { intersections, stopPropagation }, - } = state - - stopPropagation() - - switch (true) { - case first: { - pipe( - intersections, - A.head, - O.map(({ point, object }) => { - if (object.userData.type !== UserDataTypeEnum.Enum.RotateHandleMesh) - return - - traverseUpUntil( - object, - (o) => - o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, - (houseTransformsGroup) => { - dispatchPointerDown({ point, object }) - - const { - obb: { - center, - center: { x: cx, z: cz }, - }, - } = getActiveHouseUserData(houseTransformsGroup) - - const { x: x0, z: z0 } = point - - const angle0 = atan2(cz - z0, cx - x0) - - rotateData.current = { - houseTransformsGroup, - center, - rotation0: houseTransformsGroup.rotation.y, - angle0, - angle: angle0, - } - - setCameraControlsEnabled(false) - } - ) - return - }) - ) - break - } - case last: { - dispatchPointerUp() - updateIndexedHouseTransforms(rotateData.current!.houseTransformsGroup) - setCameraControlsEnabled(true) - rotateData.current = null - break - } - default: { - if (!rotateData.current) - throw new Error(`no rotateData in onDragRotate progress`) - - const [px, pz] = pointer.xz - - const { - center: { x: cx, z: cz }, - houseTransformsGroup, - } = rotateData.current - - rotateData.current.angle = atan2(cz - pz, cx - px) - - const angleDifference = - rotateData.current.angle - rotateData.current.angle0 - - houseTransformsGroup.rotation.y = - rotateData.current.rotation0 - angleDifference - - break - } - } - } - - return useGesture<{ - drag: ThreeEvent - hover: ThreeEvent - onContextMenu: ThreeEvent & - React.MouseEvent - onDoubleClick: ThreeEvent & - React.MouseEvent - onClick: ThreeEvent & - React.MouseEvent - }>({ - onDrag: (state) => { - const stretchModes: SiteCtxMode[] = [ - SiteCtxModeEnum.Enum.BUILDING, - SiteCtxModeEnum.Enum.LEVEL, - ] - - const type = state.event.object.userData?.type - - const stretch = - stretchModes.includes(siteCtx.mode) && - type === UserDataTypeEnum.Enum.StretchHandleMesh - - const move = !stretch && type === UserDataTypeEnum.Enum.ElementMesh - - const rotate = !stretch && type === UserDataTypeEnum.Enum.RotateHandleMesh - - switch (true) { - case stretch: { - onDragStretch(state) - break - } - case move: { - onDragMove(state) - break - } - case rotate: { - onDragRotate(state) - break - } - default: { - break - } - } - - invalidate() - }, - onHover: ({ event, event: { intersections }, hovering }) => { - event.stopPropagation() - - if (intersections.length === 0) { - document.body.style.cursor = "" - dispatchOutline({ - hoveredObjects: [], - }) - invalidate() - // scope.hovered = null - return - } - - mapNearestCutIntersection(intersections, (intersection) => { - const { object } = intersection - - switch (object.userData.type) { - case UserDataTypeEnum.Enum.ElementMesh: { - const scopeItem = elementMeshToScopeItem(object) - scope.hovered = scopeItem - - switch (siteCtx.mode) { - case SiteCtxModeEnum.Enum.SITE: - if (hovering) { - document.body.style.cursor = "grab" - } - dispatchOutline({ - hoveredObjects: objectToHouseObjects(object), - }) - break - case SiteCtxModeEnum.Enum.BUILDING: - dispatchOutline({ - hoveredObjects: objectToIfcTagObjects(object), - }) - // object to all of ifc tag - break - case SiteCtxModeEnum.Enum.LEVEL: - // object to all of module group - if (object.parent) { - dispatchOutline({ hoveredObjects: object.parent.children }) - } - break - } - break - } - case UserDataTypeEnum.Enum.StretchHandleMesh: - case UserDataTypeEnum.Enum.RotateHandleMesh: { - if (hovering) { - document.body.style.cursor = "grab" - } - break - } - default: { - break - } - } - }) - - invalidate() - }, - onClick: ({ event: { intersections } }) => { - mapNearestCutIntersection(intersections, (intersection) => { - const { object } = intersection - }) - }, - onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { - event.stopPropagation() - pipe( - intersections, - A.findFirst((x) => { - return ( - isMesh(x.object) && - !Array.isArray(x.object.material) && - x.object.material.visible - ) - }), - O.map(({ object: { userData } }) => { - scope.selected = userData as ScopeItem - openMenu(pageX, pageY) - }) - ) - }, - onDoubleClick: ({ event, event: { intersections } }) => { - event.stopPropagation() - - if (intersections.length === 0) return - - const { - object: { parent }, - } = intersections[0] - - if (parent?.parent?.userData.type === UserDataTypeEnum.Enum.GridGroup) { - const { levelIndex } = parent.parent.userData as GridGroupUserData - if ( - parent.parent.parent?.parent?.parent?.userData.type === - UserDataTypeEnum.Enum.HouseTransformsGroup - ) { - const { houseId } = parent.parent.parent?.parent?.parent - ?.userData as HouseTransformsGroupUserData - downMode({ houseId, levelIndex }) - } - } - - invalidate() - }, - }) as any -} - -export default useGestures diff --git a/app/design/ui-3d/fresh/gestures/events.ts b/app/design/ui-3d/fresh/gestures/events.ts new file mode 100644 index 00000000..4eb57dff --- /dev/null +++ b/app/design/ui-3d/fresh/gestures/events.ts @@ -0,0 +1,25 @@ +import { useEvent } from "react-use" +import { Object3D } from "three" +import { z } from "zod" + +export const GestureEventType = z.enum(["POINTER_DOWN", "POINTER_UP"]) + +export type GestureEventType = z.infer + +export type GestureEventDetail = { + point: V3 + object: Object3D +} + +export const usePointerDownListener = ( + f: (eventDetail: GestureEventDetail) => void +) => useEvent(GestureEventType.Enum.POINTER_DOWN, ({ detail }) => f(detail)) + +export const dispatchPointerDown = (detail: GestureEventDetail) => + dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_DOWN, { detail })) + +export const usePointerUpListener = (f: () => void) => + useEvent(GestureEventType.Enum.POINTER_UP, () => f()) + +export const dispatchPointerUp = () => + dispatchEvent(new CustomEvent(GestureEventType.Enum.POINTER_UP)) diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts new file mode 100644 index 00000000..b4a7b97f --- /dev/null +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -0,0 +1,189 @@ +import { invalidate, ThreeEvent } from "@react-three/fiber" +import { useGesture } from "@use-gesture/react" +import { pipe } from "fp-ts/lib/function" +import { A, O } from "../../../../utils/functions" +import { isMesh } from "../../../../utils/three" +import { openMenu } from "../../../state/menu" +import scope, { ScopeItem } from "../../../state/scope" +import siteCtx, { + downMode, + SiteCtxMode, + SiteCtxModeEnum, + useModeChangeListener, +} from "../../../state/siteCtx" +import { dispatchOutline } from "../events/outlines" +import { + mapNearestCutIntersection, + objectToHouseObjects, + objectToIfcTagObjects, +} from "../helpers/sceneQueries" +import { + elementMeshToScopeItem, + GridGroupUserData, + HouseTransformsGroupUserData, + UserDataTypeEnum, +} from "../userData" +import useOnDragMove from "./move" +import useOnDragRotate from "./rotate" +import useOnDragStretch from "./stretch" + +const useGestures = () => { + const onDragStretch = useOnDragStretch() + const onDragMove = useOnDragMove() + const onDragRotate = useOnDragRotate() + + return useGesture<{ + drag: ThreeEvent + hover: ThreeEvent + onContextMenu: ThreeEvent & + React.MouseEvent + onDoubleClick: ThreeEvent & + React.MouseEvent + onClick: ThreeEvent & + React.MouseEvent + }>({ + onDrag: (state) => { + const stretchModes: SiteCtxMode[] = [ + SiteCtxModeEnum.Enum.BUILDING, + SiteCtxModeEnum.Enum.LEVEL, + ] + + const type = state.event.object.userData?.type + + const stretch = + stretchModes.includes(siteCtx.mode) && + type === UserDataTypeEnum.Enum.StretchHandleMesh + + const move = !stretch && type === UserDataTypeEnum.Enum.ElementMesh + + const rotate = !stretch && type === UserDataTypeEnum.Enum.RotateHandleMesh + + switch (true) { + case stretch: { + onDragStretch(state) + break + } + case move: { + onDragMove(state) + break + } + case rotate: { + onDragRotate(state) + break + } + default: { + break + } + } + + invalidate() + }, + onHover: ({ event, event: { intersections }, hovering }) => { + event.stopPropagation() + + if (intersections.length === 0) { + document.body.style.cursor = "" + dispatchOutline({ + hoveredObjects: [], + }) + invalidate() + // scope.hovered = null + return + } + + mapNearestCutIntersection(intersections, (intersection) => { + const { object } = intersection + + switch (object.userData.type) { + case UserDataTypeEnum.Enum.ElementMesh: { + const scopeItem = elementMeshToScopeItem(object) + scope.hovered = scopeItem + + switch (siteCtx.mode) { + case SiteCtxModeEnum.Enum.SITE: + if (hovering) { + document.body.style.cursor = "grab" + } + dispatchOutline({ + hoveredObjects: objectToHouseObjects(object), + }) + break + case SiteCtxModeEnum.Enum.BUILDING: + dispatchOutline({ + hoveredObjects: objectToIfcTagObjects(object), + }) + // object to all of ifc tag + break + case SiteCtxModeEnum.Enum.LEVEL: + // object to all of module group + if (object.parent) { + dispatchOutline({ hoveredObjects: object.parent.children }) + } + break + } + break + } + case UserDataTypeEnum.Enum.StretchHandleMesh: + case UserDataTypeEnum.Enum.RotateHandleMesh: { + if (hovering) { + document.body.style.cursor = "grab" + } + break + } + default: { + break + } + } + }) + + invalidate() + }, + onClick: ({ event: { intersections } }) => { + mapNearestCutIntersection(intersections, (intersection) => { + const { object } = intersection + }) + }, + onContextMenu: ({ event, event: { intersections, pageX, pageY } }) => { + event.stopPropagation() + pipe( + intersections, + A.findFirst((x) => { + return ( + isMesh(x.object) && + !Array.isArray(x.object.material) && + x.object.material.visible + ) + }), + O.map(({ object: { userData } }) => { + scope.selected = userData as ScopeItem + openMenu(pageX, pageY) + }) + ) + }, + onDoubleClick: ({ event, event: { intersections } }) => { + event.stopPropagation() + + if (intersections.length === 0) return + + const { + object: { parent }, + } = intersections[0] + + if (parent?.parent?.userData.type === UserDataTypeEnum.Enum.GridGroup) { + const { levelIndex } = parent.parent.userData as GridGroupUserData + if ( + parent.parent.parent?.parent?.parent?.userData.type === + UserDataTypeEnum.Enum.HouseTransformsGroup + ) { + const { houseId } = parent.parent.parent?.parent?.parent + ?.userData as HouseTransformsGroupUserData + downMode({ houseId, levelIndex }) + } + } + + invalidate() + }, + }) as any +} + +export default useGestures diff --git a/app/design/ui-3d/fresh/gestures/move.ts b/app/design/ui-3d/fresh/gestures/move.ts new file mode 100644 index 00000000..4be37f62 --- /dev/null +++ b/app/design/ui-3d/fresh/gestures/move.ts @@ -0,0 +1,98 @@ +import { ThreeEvent } from "@react-three/fiber" +import { Handler } from "@use-gesture/react" +import { pipe } from "fp-ts/lib/function" +import { useRef } from "react" +import { Group, Object3D, Vector3 } from "three" +import { dispatchPointerDown, dispatchPointerUp } from "./events" +import { A, O } from "../../../../utils/functions" +import { setCameraControlsEnabled } from "../../../state/camera" +import pointer from "../../../state/pointer" +import scope from "../../../state/scope" +import { updateIndexedHouseTransforms } from "../dimensions" +import { dispatchOutline } from "../events/outlines" +import { + objectToHouseObjects, + traverseDownUntil, + traverseUpUntil, +} from "../helpers/sceneQueries" +import { elementMeshToScopeItem, UserDataTypeEnum } from "../userData" + +const useOnDragMove = () => { + const moveData = useRef<{ + lastPoint: Vector3 + houseObject: Group + } | null>(null) + + const onDragMove: Handler<"drag", ThreeEvent> = (state) => { + const { + first, + last, + event: { intersections, stopPropagation }, + } = state + + stopPropagation() + + switch (true) { + case first: { + pipe( + intersections, + A.head, + O.map(({ point, object }) => { + if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) + return + + traverseUpUntil( + object, + (o) => + o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, + (houseTransformGroup) => { + dispatchPointerDown({ point, object }) + + moveData.current = { + houseObject: houseTransformGroup as Group, + lastPoint: point.setY(0), + } + + setCameraControlsEnabled(false) + + const scopeItem = elementMeshToScopeItem(object) + scope.selected = scopeItem + + dispatchOutline({ + selectedObjects: objectToHouseObjects(object), + }) + } + ) + return + }) + ) + break + } + case last: { + setCameraControlsEnabled(true) + updateIndexedHouseTransforms(moveData.current!.houseObject) + moveData.current = null + dispatchPointerUp() + return + } + default: { + if (!moveData.current) { + console.warn(`no moveData.current in onDragMove`) + return + } + + const { lastPoint, houseObject } = moveData.current + const [px, pz] = pointer.xz + const thisPoint = new Vector3(px, 0, pz) + const delta = thisPoint.clone().sub(lastPoint) + moveData.current.lastPoint = thisPoint + houseObject.position.add(delta) + return + } + } + } + + return onDragMove +} + +export default useOnDragMove diff --git a/app/design/ui-3d/fresh/gestures/rotate.ts b/app/design/ui-3d/fresh/gestures/rotate.ts new file mode 100644 index 00000000..1daca07c --- /dev/null +++ b/app/design/ui-3d/fresh/gestures/rotate.ts @@ -0,0 +1,113 @@ +import { ThreeEvent } from "@react-three/fiber" +import { Handler } from "@use-gesture/react" +import { pipe } from "fp-ts/lib/function" +import { useRef } from "react" +import { Object3D, Vector3 } from "three" +import { dispatchPointerDown, dispatchPointerUp } from "./events" +import { A, O } from "../../../../utils/functions" +import { atan2 } from "../../../../utils/math" +import { setCameraControlsEnabled } from "../../../state/camera" +import pointer from "../../../state/pointer" +import { updateIndexedHouseTransforms } from "../dimensions" +import { + getActiveHouseUserData, + traverseUpUntil, +} from "../helpers/sceneQueries" +import { UserDataTypeEnum } from "../userData" + +const useOnDragRotate = () => { + const rotateData = useRef<{ + houseTransformsGroup: Object3D + center: Vector3 + rotation0: number + angle0: number + angle: number + } | null>(null) + + const onDragRotate: Handler<"drag", ThreeEvent> = (state) => { + const { + first, + last, + event: { intersections, stopPropagation }, + } = state + + stopPropagation() + + switch (true) { + case first: { + pipe( + intersections, + A.head, + O.map(({ point, object }) => { + if (object.userData.type !== UserDataTypeEnum.Enum.RotateHandleMesh) + return + + traverseUpUntil( + object, + (o) => + o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, + (houseTransformsGroup) => { + dispatchPointerDown({ point, object }) + + const { + obb: { + center, + center: { x: cx, z: cz }, + }, + } = getActiveHouseUserData(houseTransformsGroup) + + const { x: x0, z: z0 } = point + + const angle0 = atan2(cz - z0, cx - x0) + + rotateData.current = { + houseTransformsGroup, + center, + rotation0: houseTransformsGroup.rotation.y, + angle0, + angle: angle0, + } + + setCameraControlsEnabled(false) + } + ) + return + }) + ) + break + } + case last: { + dispatchPointerUp() + updateIndexedHouseTransforms(rotateData.current!.houseTransformsGroup) + setCameraControlsEnabled(true) + rotateData.current = null + break + } + default: { + if (!rotateData.current) + throw new Error(`no rotateData in onDragRotate progress`) + + const [px, pz] = pointer.xz + + const { + center: { x: cx, z: cz }, + houseTransformsGroup, + } = rotateData.current + + rotateData.current.angle = atan2(cz - pz, cx - px) + + const angleDifference = + rotateData.current.angle - rotateData.current.angle0 + + houseTransformsGroup.rotation.y = + rotateData.current.rotation0 - angleDifference + + break + } + } + } + + return onDragRotate +} + +export default useOnDragRotate diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts new file mode 100644 index 00000000..cc3fa9db --- /dev/null +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -0,0 +1,170 @@ +import { ThreeEvent } from "@react-three/fiber" +import { Handler } from "@use-gesture/react" +import { useRef } from "react" +import { Group, Object3D, Vector3 } from "three" +import { dispatchPointerDown, dispatchPointerUp } from "./events" +import { setCameraControlsEnabled } from "../../../state/camera" +import pointer from "../../../state/pointer" +import { + getActiveHouseUserData, + handleColumnGroupParentQuery, + rootHouseGroupParentQuery, +} from "../helpers/sceneQueries" +import { insertVanillaColumn } from "../helpers/stretchZ" +import { StretchHandleMeshUserData } from "../userData" + +const useOnDragStretch = () => { + const stretchDataRef = useRef<{ + handleObject: Object3D + houseGroup: Group + handleGroup: Group + point0: Vector3 + handleGroupPos0: Vector3 + axis: "x" | "z" + direction: number + lastDistance: number + columnsAddedToEnd: number + } | null>(null) + + const onStretchXProgress: Handler<"drag", ThreeEvent> = () => {} + + const onStretchZProgress: Handler<"drag", ThreeEvent> = () => { + const stretchData = stretchDataRef.current! + const { + handleGroup, + handleGroupPos0, + point0, + houseGroup, + axis, + direction, + handleObject, + lastDistance, + columnsAddedToEnd, + } = stretchData + + switch (direction) { + case -1: { + return + } + case 1: { + const [x1, z1] = pointer.xz + const distanceVector = new Vector3(x1, 0, z1).sub(point0) + distanceVector.applyAxisAngle( + new Vector3(0, 1, 0), + -houseGroup.rotation.y + ) + const distance = distanceVector.z + + handleGroup.position.set(0, 0, handleGroupPos0.z + distance) + + // maybe we want to oper on the house group columns doodah itself + + // and then constantly be computing whether we want to add or subtract + + // so we're tracking the next gate up or down + + // probably want to just set visible false and turn off raycasting + // when we delete columns + + // let's work on adding some first + + // gate 1 = z'end + vanillaColumnLength + + const { vanillaColumn } = getActiveHouseUserData(houseGroup) + + switch (true) { + case distance > lastDistance: { + if (distance > vanillaColumn.length * columnsAddedToEnd) { + insertVanillaColumn(houseGroup, 1)() + stretchData.columnsAddedToEnd++ + } + break + } + case distance < lastDistance: { + if (distance < vanillaColumn.length * columnsAddedToEnd) { + } + // if ( + // distance < + // vanillaColumnLength * columnsDelta + vanillaColumnLength + // ) { + // stretchData.current.columnsDelta++ + // } + } + } + stretchData.lastDistance = distance + + // gates on err... + + // vanilla column length up + + // existing column length down + return + } + } + } + + const onDragStretch: Handler<"drag", ThreeEvent> = (state) => { + const { + first, + last, + event, + event: { object, point }, + } = state + + switch (true) { + case first: { + setCameraControlsEnabled(false) + + const { direction, axis } = object.userData as StretchHandleMeshUserData + + switch (axis) { + case "x": + return + case "z": + const handleGroup = handleColumnGroupParentQuery(object) + const houseGroup = rootHouseGroupParentQuery(object) + stretchDataRef.current = { + handleObject: object, + houseGroup, + handleGroup, + handleGroupPos0: handleGroup.position.clone(), + point0: point, + lastDistance: 0, + direction, + axis, + columnsAddedToEnd: 0, + } + dispatchPointerDown({ point, object }) + return + } + } + case last: { + if (stretchDataRef.current === null) + throw new Error("stretchData.current null unexpectedly") + dispatchPointerUp() + stretchDataRef.current = null + setCameraControlsEnabled(true) + return + } + default: { + if (!stretchDataRef.current) + throw new Error(`onDragStretch first didn't set first`) + + const { axis } = stretchDataRef.current + + switch (axis) { + case "z": + onStretchZProgress(state) + return + case "x": + onStretchXProgress(state) + return + } + } + } + } + + return onDragStretch +} + +export default useOnDragStretch diff --git a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts index 830a552d..5b808537 100644 --- a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts +++ b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts @@ -75,31 +75,7 @@ const useClippingPlaneHelpers = (rootRef: RefObject) => { ) } - useModeChangeListener(({ previous, next }) => { - const { houseId, levelIndex } = siteCtx - - switch (true) { - case next === SiteCtxModeEnum.Enum.LEVEL: - if (houseId === null || levelIndex === null) break - // find level index height - - pipe( - houseLevelIndexToCutHeight(houseId, levelIndex), - O.map((cutHeight) => { - setYCut(houseId, cutHeight) - }) - ) - break - default: - if (houseId === null) break - setYCut(houseId, BIG_CLIP_NUMBER) - break - } - - invalidate() - }) - - return { setYCut, initClippingPlanes } + return { setYCut, initClippingPlanes, houseLevelIndexToCutHeight } } export default useClippingPlaneHelpers diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 46746d38..b29450fc 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -1,6 +1,6 @@ import { pipe } from "fp-ts/lib/function" import { RefObject } from "react" -import { Group, Object3D } from "three" +import { Group, Intersection, Material, Mesh, Object3D, Plane } from "three" import { A, O, someOrError } from "../../../../utils/functions" import { HouseLayoutGroupUserData, @@ -191,3 +191,47 @@ export const getLayoutGroupBySectionType = ( // }) // ) // } + +export const objectToHouseObjects = (object: Object3D) => + object.parent!.parent!.parent!.parent!.children.flatMap((x) => + x.children.flatMap((y) => y.children.flatMap((z) => z.children)) + ) + +export const objectToIfcTagObjects = (object: Object3D) => { + const ifcTag: string = object.userData.ifcTag + + return object.parent!.parent!.parent!.parent!.children.flatMap((x) => + x.children.flatMap((y) => + y.children.flatMap((z) => + z.children.filter((x) => x.userData.ifcTag === ifcTag) + ) + ) + ) +} + +export const mapNearestCutIntersection = ( + intersections: Intersection[], + f: (ix: Intersection) => void +) => { + pipe( + intersections, + A.findFirst((ix) => { + const { object, point } = ix + switch (object.userData.type) { + case UserDataTypeEnum.Enum.ElementMesh: { + return ( + ((object as Mesh).material as Material).clippingPlanes as Plane[] + ).every((plane) => { + return plane.distanceToPoint(point) > 0 + }) + } + case UserDataTypeEnum.Enum.RotateHandleMesh: + case UserDataTypeEnum.Enum.StretchHandleMesh: + return true + default: + return false + } + }), + O.map(f) + ) +} From a92195dd3c7ae297e6bfb8e38a365c42491256bb Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 10 Aug 2023 12:50:39 +0100 Subject: [PATCH 088/132] wip level plane off --- app/design/ui-3d/fresh/FreshApp.tsx | 44 +++++++++++++-- .../ui-3d/fresh/helpers/clippingPlanes.ts | 5 +- .../ui-3d/fresh/helpers/sceneQueries.ts | 12 ++++ app/design/ui-3d/fresh/userData.ts | 55 ++++++++++++++++++- 4 files changed, 108 insertions(+), 8 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index b704f095..e5300d57 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,5 +1,5 @@ import { Fragment, useRef } from "react" -import { Group, Object3D } from "three" +import { Group, Material, Mesh, Object3D, Plane } from "three" import { useHousesEvents } from "./events/houses" import useGestures from "./gestures" import useKeyTestInteractions from "./useKeyTestInteractions" @@ -11,16 +11,23 @@ import siteCtx, { } from "../../state/siteCtx" import useClippingPlaneHelpers from "./helpers/clippingPlanes" import { pipe } from "fp-ts/lib/function" -import { O } from "../../../utils/functions" +import { A, O } from "../../../utils/functions" import { BIG_CLIP_NUMBER } from "./helpers/layouts" import { invalidate } from "@react-three/fiber" import { + getActiveHouseUserData, getHouseTransformGroup, + mapAllHouseTransformGroups, traverseDownUntil, } from "./helpers/sceneQueries" -import { UserDataTypeEnum } from "./userData" +import { + isElementMesh, + isHouseTransformsGroup, + UserDataTypeEnum, +} from "./userData" import { useSubscribeKey } from "../../../utils/hooks" import scope, { ScopeItem } from "../../state/scope" +import { useKey } from "react-use" const FreshApp = () => { const rootRef = useRef(null) @@ -80,6 +87,28 @@ const FreshApp = () => { ) } + useKey("q", () => { + pipe( + rootRef.current?.children ?? [], + A.filter(isHouseTransformsGroup), + A.map((houseTransformGroup) => { + traverseDownUntil(houseTransformGroup, (object) => { + if (isElementMesh(object)) { + // const clippingPlaneUuids = object.material.clippingPlanes + // .map((x: Plane) => + // .join(",") + // console.log( + // `houseUuid: ${houseTransformGroup.uuid}; planesUuids: ${clippingPlaneUuids}` + // ) + console.log(object.material.clippingPlanes) + return true + } + return false + }) + }) + ) + }) + const f = () => { const { houseId, levelIndex, mode } = siteCtx const { selected } = scope @@ -90,6 +119,10 @@ const FreshApp = () => { ] hideAllHandles() + mapAllHouseTransformGroups(rootRef, (houseTransformGroup) => { + const { houseId } = getActiveHouseUserData(houseTransformGroup) + setYCut(houseId, BIG_CLIP_NUMBER) + }) if (stretchModes.includes(mode)) { if (houseId) showHouseStretchHandles(houseId) @@ -100,6 +133,10 @@ const FreshApp = () => { pipe( houseLevelIndexToCutHeight(houseId, levelIndex), O.map((cutHeight) => { + console.log({ + selected: selected?.houseId, + siteCtx: siteCtx.houseId, + }) setYCut(houseId, cutHeight) }) ) @@ -116,7 +153,6 @@ const FreshApp = () => { // switch (true) { // default: // if (houseId === null) break - // setYCut(houseId, BIG_CLIP_NUMBER) // } invalidate() diff --git a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts index 5b808537..419490f0 100644 --- a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts +++ b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts @@ -45,7 +45,10 @@ const useClippingPlaneHelpers = (rootRef: RefObject) => { }) } - const houseLevelIndexToCutHeight = (houseId: string, levelIndex: number) => { + const houseLevelIndexToCutHeight = ( + houseId: string, + levelIndex: number + ): O.Option => { return pipe( getHouseTransformGroup(rootRef, houseId), O.chain((houseTransformGroup) => diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index b29450fc..2235b7c2 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -5,6 +5,7 @@ import { A, O, someOrError } from "../../../../utils/functions" import { HouseLayoutGroupUserData, HouseTransformsGroupUserData, + isHouseTransformsGroup, UserDataTypeEnum, } from "../userData" @@ -76,6 +77,17 @@ export const mapHouseTransformGroup = ( f: (houseTransformGroup: Object3D) => void ) => pipe(getHouseTransformGroup(rootRef, houseId), O.map(f)) +export const mapAllHouseTransformGroups = ( + rootRef: RefObject, + f: (houseTransformGroup: Object3D) => void +): void => + pipe( + rootRef.current?.children ?? [], + A.filter(isHouseTransformsGroup), + A.map(f), + () => void null + ) + export const rootHouseGroupParentQuery = (object: Object3D) => { let x = object while (x.parent) { diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 9e894923..f9d8110b 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -1,4 +1,4 @@ -import { Group, Object3D, Plane } from "three" +import { Group, Material, Mesh, Object3D, Plane } from "three" import { OBB } from "three-stdlib" import { z } from "zod" import { ColumnLayout, VanillaColumn } from "../../../db/layouts" @@ -14,8 +14,9 @@ import { ScopeItem } from "../../state/scope" // -> GridGroup's as children have // -> ModuleGroup's as children have // -> ElementMesh's as children -// -> Stretch Handles -// -> Rotate Handles +// -> Stretch Handles meshes +// -> Rotate Handles group +// -> Rotate Handles meshes export const UserDataTypeEnum = z.enum([ "HouseTransformsGroup", @@ -28,6 +29,7 @@ export const UserDataTypeEnum = z.enum([ "RotateHandlesGroup", "RotateHandleMesh", ]) + export type UserDataTypeEnum = z.infer export type HouseTransformsGroupUserData = { @@ -79,6 +81,8 @@ export type ModuleGroupUserData = { length: number } +// M + export type ElementMeshUserData = { type: typeof UserDataTypeEnum.Enum.ElementMesh ifcTag: string @@ -113,6 +117,51 @@ export type UserData = | StretchHandleMeshUserData | RotateHandlesGroupUserData +export const isElementMesh = ( + node: Object3D +): node is Mesh & { userData: ElementMeshUserData; material: Material } => + node.userData?.type === UserDataTypeEnum.Enum.ElementMesh + +export const isStretchHandleMesh = ( + node: Object3D +): node is Mesh & { userData: StretchHandleMeshUserData; material: Material } => + node.userData?.type === UserDataTypeEnum.Enum.StretchHandleMesh + +export const isRotateHandleMesh = ( + node: Object3D +): node is Mesh & { userData: RotateHandleMeshUserData; material: Material } => + node.userData?.type === UserDataTypeEnum.Enum.RotateHandleMesh + +export const isModuleGroup = ( + node: Object3D +): node is Group & { userData: ModuleGroupUserData } => + node.userData?.type === UserDataTypeEnum.Enum.ModuleGroup + +export const isGridGroup = ( + node: Object3D +): node is Group & { userData: GridGroupUserData } => + node.userData?.type === UserDataTypeEnum.Enum.GridGroup + +export const isColumnGroup = ( + node: Object3D +): node is Group & { userData: ColumnGroupUserData } => + node.userData?.type === UserDataTypeEnum.Enum.ColumnGroup + +export const isHouseLayoutGroup = ( + node: Object3D +): node is Group & { userData: HouseLayoutGroupUserData } => + node.userData?.type === UserDataTypeEnum.Enum.HouseLayoutGroup + +export const isHouseTransformsGroup = ( + node: Object3D +): node is Group & { userData: HouseTransformsGroupUserData } => + node.userData?.type === UserDataTypeEnum.Enum.HouseTransformsGroup + +export const isRotateHandlesGroup = ( + node: Object3D +): node is Group & { userData: RotateHandlesGroupUserData } => + node.userData?.type === UserDataTypeEnum.Enum.RotateHandlesGroup + export const incrementColumnCount = (layoutGroup: Object3D) => { const userData = layoutGroup.userData as HouseLayoutGroupUserData if (userData.type !== UserDataTypeEnum.Enum.HouseLayoutGroup) From a84d4b18e07aec853711b80c503b04589544b7be Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 10 Aug 2023 13:23:40 +0100 Subject: [PATCH 089/132] wip fixed material sharing --- app/design/ui-3d/fresh/FreshApp.tsx | 58 ++++++++++++++++------- app/design/ui-3d/fresh/helpers/layouts.ts | 11 ++++- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index e5300d57..171abddf 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -11,7 +11,7 @@ import siteCtx, { } from "../../state/siteCtx" import useClippingPlaneHelpers from "./helpers/clippingPlanes" import { pipe } from "fp-ts/lib/function" -import { A, O } from "../../../utils/functions" +import { A, O, R, S } from "../../../utils/functions" import { BIG_CLIP_NUMBER } from "./helpers/layouts" import { invalidate } from "@react-three/fiber" import { @@ -28,6 +28,7 @@ import { import { useSubscribeKey } from "../../../utils/hooks" import scope, { ScopeItem } from "../../state/scope" import { useKey } from "react-use" +import { values } from "fp-ts-std/Record" const FreshApp = () => { const rootRef = useRef(null) @@ -88,25 +89,46 @@ const FreshApp = () => { } useKey("q", () => { - pipe( - rootRef.current?.children ?? [], - A.filter(isHouseTransformsGroup), - A.map((houseTransformGroup) => { - traverseDownUntil(houseTransformGroup, (object) => { - if (isElementMesh(object)) { - // const clippingPlaneUuids = object.material.clippingPlanes - // .map((x: Plane) => - // .join(",") - // console.log( - // `houseUuid: ${houseTransformGroup.uuid}; planesUuids: ${clippingPlaneUuids}` - // ) - console.log(object.material.clippingPlanes) - return true + let results: Record = {} + + mapAllHouseTransformGroups(rootRef, (houseTransformGroup) => { + const { uuid: houseUuid } = houseTransformGroup + + if (!results[houseUuid]) { + results[houseUuid] = [] + } + + houseTransformGroup.traverse((node) => { + if (isElementMesh(node)) { + const { uuid: materialUuid } = node.material + + if (!results[houseUuid].includes(materialUuid)) { + results[houseUuid].push(materialUuid) } - return false - }) + } }) - ) + }) + + const doesAnyHouseIntersectWithAnother = ( + results: Record + ): boolean => + pipe( + R.toArray(results), + A.exists(([houseUuid, uuids]) => + pipe( + results, + R.filterWithIndex((k2, _) => k2 !== houseUuid), + R.collect(S.Ord)((_, uuids2) => uuids2), + A.exists( + (uuids2) => !A.isEmpty(A.intersection(S.Eq)(uuids, uuids2)) + ) + ) + ) + ) + + const noIntersectionsAtAllEver = !doesAnyHouseIntersectWithAnother(results) + + console.log({ noIntersectionsAtAllEver }) }) const f = () => { diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 688a02f7..94286f37 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -119,10 +119,12 @@ export const getGeometry = ({ export const createModuleGroup = async ({ systemId, + houseId, gridGroupIndex, module: { speckleBranchUrl, length, dna }, }: { systemId: string + houseId: string gridGroupIndex: number module: Module }) => { @@ -136,7 +138,7 @@ export const createModuleGroup = async ({ const material = getMaterial({ systemId, ifcTag, - houseId: "", + houseId, }) as MeshStandardMaterial const mesh = new Mesh(geometry, material) mesh.castShadow = true @@ -182,12 +184,14 @@ export const createModuleGroup = async ({ export const createColumnGroup = ({ systemId, + houseId, gridGroups, columnIndex, startColumn = false, endColumn = false, }: { systemId: string + houseId: string gridGroups: GridGroup[] columnIndex: number startColumn?: boolean @@ -203,6 +207,7 @@ export const createColumnGroup = for (let { z, module, gridGroupIndex } of modules) { const moduleGroup = await createModuleGroup({ systemId, + houseId, module, gridGroupIndex, }) @@ -246,9 +251,11 @@ export const createColumnGroup = export const createColumnGroups = ({ systemId, + houseId, houseLayout, }: { systemId: string + houseId: string houseLayout: ColumnLayout }): T.Task => pipe( @@ -260,6 +267,7 @@ export const createColumnGroups = ({ const task = createColumnGroup({ systemId, + houseId, gridGroups, startColumn, endColumn, @@ -301,6 +309,7 @@ export const createLayoutGroup = ({ pipe( createColumnGroups({ systemId, + houseId, houseLayout, }), T.chain((columnGroups) => { From be099bc1a93a21da52856fce2c65d2c9ffd5fbb6 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 10 Aug 2023 13:26:51 +0100 Subject: [PATCH 090/132] wip cleanup --- app/design/ui-3d/fresh/FreshApp.tsx | 84 +++------------------- app/design/ui-3d/fresh/helpers/stretchZ.ts | 9 ++- 2 files changed, 14 insertions(+), 79 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 171abddf..00c029ec 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,34 +1,26 @@ +import { invalidate } from "@react-three/fiber" +import { pipe } from "fp-ts/lib/function" import { Fragment, useRef } from "react" -import { Group, Material, Mesh, Object3D, Plane } from "three" -import { useHousesEvents } from "./events/houses" -import useGestures from "./gestures" -import useKeyTestInteractions from "./useKeyTestInteractions" -import XZPlane from "../XZPlane" +import { Group } from "three" +import { O } from "../../../utils/functions" +import { useSubscribeKey } from "../../../utils/hooks" +import scope from "../../state/scope" import siteCtx, { SiteCtxMode, SiteCtxModeEnum, useModeChangeListener, } from "../../state/siteCtx" +import XZPlane from "../XZPlane" +import { useHousesEvents } from "./events/houses" +import useGestures from "./gestures" import useClippingPlaneHelpers from "./helpers/clippingPlanes" -import { pipe } from "fp-ts/lib/function" -import { A, O, R, S } from "../../../utils/functions" import { BIG_CLIP_NUMBER } from "./helpers/layouts" -import { invalidate } from "@react-three/fiber" import { getActiveHouseUserData, getHouseTransformGroup, mapAllHouseTransformGroups, - traverseDownUntil, } from "./helpers/sceneQueries" -import { - isElementMesh, - isHouseTransformsGroup, - UserDataTypeEnum, -} from "./userData" -import { useSubscribeKey } from "../../../utils/hooks" -import scope, { ScopeItem } from "../../state/scope" -import { useKey } from "react-use" -import { values } from "fp-ts-std/Record" +import { UserDataTypeEnum } from "./userData" const FreshApp = () => { const rootRef = useRef(null) @@ -66,15 +58,6 @@ const FreshApp = () => { const { houseLevelIndexToCutHeight, setYCut } = useClippingPlaneHelpers(rootRef) - // const activeRotateHandlesRef = useRef(null) - // const updateActiveRotateHandles = (object: Object3D) => { - // if (activeRotateHandlesRef.current !== null) { - // activeRotateHandlesRef.current.visible = false - // } - // activeRotateHandlesRef.current = object - // activeRotateHandlesRef.current.visible = true - // } - const showHouseRotateHandles = (houseId: string) => { pipe( getHouseTransformGroup(rootRef, houseId), @@ -88,49 +71,6 @@ const FreshApp = () => { ) } - useKey("q", () => { - let results: Record = {} - - mapAllHouseTransformGroups(rootRef, (houseTransformGroup) => { - const { uuid: houseUuid } = houseTransformGroup - - if (!results[houseUuid]) { - results[houseUuid] = [] - } - - houseTransformGroup.traverse((node) => { - if (isElementMesh(node)) { - const { uuid: materialUuid } = node.material - - if (!results[houseUuid].includes(materialUuid)) { - results[houseUuid].push(materialUuid) - } - } - }) - }) - - const doesAnyHouseIntersectWithAnother = ( - results: Record - ): boolean => - pipe( - R.toArray(results), - A.exists(([houseUuid, uuids]) => - pipe( - results, - R.filterWithIndex((k2, _) => k2 !== houseUuid), - R.collect(S.Ord)((_, uuids2) => uuids2), - A.exists( - (uuids2) => !A.isEmpty(A.intersection(S.Eq)(uuids, uuids2)) - ) - ) - ) - ) - - const noIntersectionsAtAllEver = !doesAnyHouseIntersectWithAnother(results) - - console.log({ noIntersectionsAtAllEver }) - }) - const f = () => { const { houseId, levelIndex, mode } = siteCtx const { selected } = scope @@ -155,10 +95,6 @@ const FreshApp = () => { pipe( houseLevelIndexToCutHeight(houseId, levelIndex), O.map((cutHeight) => { - console.log({ - selected: selected?.houseId, - siteCtx: siteCtx.houseId, - }) setYCut(houseId, cutHeight) }) ) diff --git a/app/design/ui-3d/fresh/helpers/stretchZ.ts b/app/design/ui-3d/fresh/helpers/stretchZ.ts index 1690a642..3e7e759c 100644 --- a/app/design/ui-3d/fresh/helpers/stretchZ.ts +++ b/app/design/ui-3d/fresh/helpers/stretchZ.ts @@ -13,16 +13,15 @@ export const insertVanillaColumn = ( houseTransformsGroup: Group, direction: 1 | -1 ) => { - const { systemId, levelTypes, columnCount } = + const { systemId, houseId, levelTypes, columnCount } = getActiveHouseUserData(houseTransformsGroup) - // const vanillaColumn = - // vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] - return pipe( getVanillaColumn({ systemId, levelTypes }), T.chain(({ gridGroups }) => - pipe(createColumnGroup({ systemId, gridGroups, columnIndex: -1 })) + pipe( + createColumnGroup({ systemId, houseId, gridGroups, columnIndex: -1 }) + ) ), T.map((vanillaColumnGroup) => { const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) From 9358ebab433779258b7058fae91a24a3d7a3befa Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 10 Aug 2023 15:41:58 +0100 Subject: [PATCH 091/132] wip z stretch down --- app/design/ui-3d/fresh/FreshApp.tsx | 18 +- app/design/ui-3d/fresh/gestures/index.ts | 25 +- app/design/ui-3d/fresh/gestures/stretch.ts | 281 +++++++++++++----- .../ui-3d/fresh/helpers/clippingPlanes.ts | 4 +- app/design/ui-3d/fresh/helpers/handles.ts | 9 +- .../ui-3d/fresh/helpers/sceneChanges.ts | 7 +- .../ui-3d/fresh/helpers/sceneQueries.ts | 6 +- app/design/ui-3d/fresh/helpers/stretchZ.ts | 8 + .../ui-3d/fresh/useKeyTestInteractions.ts | 4 +- app/utils/three.ts | 4 +- 10 files changed, 272 insertions(+), 94 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 00c029ec..74acd6c4 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -4,6 +4,10 @@ import { Fragment, useRef } from "react" import { Group } from "three" import { O } from "../../../utils/functions" import { useSubscribeKey } from "../../../utils/hooks" +import { + setInvisibleNoRaycast, + setVisibleAndRaycast, +} from "../../../utils/three" import scope from "../../state/scope" import siteCtx, { SiteCtxMode, @@ -17,7 +21,7 @@ import useClippingPlaneHelpers from "./helpers/clippingPlanes" import { BIG_CLIP_NUMBER } from "./helpers/layouts" import { getActiveHouseUserData, - getHouseTransformGroup, + getHouseTransformsGroupDown, mapAllHouseTransformGroups, } from "./helpers/sceneQueries" import { UserDataTypeEnum } from "./userData" @@ -33,11 +37,12 @@ const FreshApp = () => { const showHouseStretchHandles = (houseId: string) => { pipe( - getHouseTransformGroup(rootRef, houseId), + getHouseTransformsGroupDown(rootRef, houseId), O.map((houseTransformGroup) => { houseTransformGroup.traverse((node) => { if (node.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh) { - node.visible = true + setVisibleAndRaycast(node) + // node.visible = true } }) }) @@ -50,7 +55,8 @@ const FreshApp = () => { node.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh || node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup ) { - node.visible = false + setInvisibleNoRaycast(node) + // node.visible = false } }) } @@ -60,11 +66,11 @@ const FreshApp = () => { const showHouseRotateHandles = (houseId: string) => { pipe( - getHouseTransformGroup(rootRef, houseId), + getHouseTransformsGroupDown(rootRef, houseId), O.map((houseTransformGroup) => { houseTransformGroup.traverse((node) => { if (node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup) { - node.visible = true + setVisibleAndRaycast(node) } }) }) diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts index b4a7b97f..ef4f9daa 100644 --- a/app/design/ui-3d/fresh/gestures/index.ts +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -1,6 +1,7 @@ import { invalidate, ThreeEvent } from "@react-three/fiber" import { useGesture } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" +import { useRef } from "react" import { A, O } from "../../../../utils/functions" import { isMesh } from "../../../../utils/three" import { openMenu } from "../../../state/menu" @@ -9,7 +10,6 @@ import siteCtx, { downMode, SiteCtxMode, SiteCtxModeEnum, - useModeChangeListener, } from "../../../state/siteCtx" import { dispatchOutline } from "../events/outlines" import { @@ -32,6 +32,10 @@ const useGestures = () => { const onDragMove = useOnDragMove() const onDragRotate = useOnDragRotate() + let stretching = false, + moving = false, + rotating = false + return useGesture<{ drag: ThreeEvent hover: ThreeEvent @@ -49,26 +53,37 @@ const useGestures = () => { ] const type = state.event.object.userData?.type + const { first, last } = state const stretch = - stretchModes.includes(siteCtx.mode) && - type === UserDataTypeEnum.Enum.StretchHandleMesh + stretching || + (stretchModes.includes(siteCtx.mode) && + type === UserDataTypeEnum.Enum.StretchHandleMesh) - const move = !stretch && type === UserDataTypeEnum.Enum.ElementMesh + const move = + moving || (!stretch && type === UserDataTypeEnum.Enum.ElementMesh) - const rotate = !stretch && type === UserDataTypeEnum.Enum.RotateHandleMesh + const rotate = + rotating || + (!stretch && type === UserDataTypeEnum.Enum.RotateHandleMesh) switch (true) { case stretch: { + if (first) stretching = true onDragStretch(state) + if (last) stretching = false break } case move: { + if (first) moving = true onDragMove(state) + if (last) moving = false break } case rotate: { + if (first) rotating = true onDragRotate(state) + if (last) rotating = false break } default: { diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index cc3fa9db..ecf338f8 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,103 +1,143 @@ import { ThreeEvent } from "@react-three/fiber" import { Handler } from "@use-gesture/react" +import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Group, Object3D, Vector3 } from "three" -import { dispatchPointerDown, dispatchPointerUp } from "./events" +import { VanillaColumn } from "../../../../db/layouts" +import { A, Num, O, Ord, T } from "../../../../utils/functions" import { setCameraControlsEnabled } from "../../../state/camera" import pointer from "../../../state/pointer" +import { createColumnGroup } from "../helpers/layouts" import { getActiveHouseUserData, + getActiveLayoutGroup, + getHouseTransformsGroupUp, + getLayoutGroupColumnGroups, handleColumnGroupParentQuery, - rootHouseGroupParentQuery, } from "../helpers/sceneQueries" -import { insertVanillaColumn } from "../helpers/stretchZ" -import { StretchHandleMeshUserData } from "../userData" +import { + decrementColumnCount, + incrementColumnCount, + StretchHandleMeshUserData, +} from "../userData" +import { dispatchPointerDown, dispatchPointerUp } from "./events" + +type StretchData = StretchZUpData | StretchZDownData + +type StretchZDataShared = { + systemId: string + houseId: string + axis: "z" | "x" + handleObject: Object3D + houseTransformsGroup: Object3D + handleGroup: Object3D + layoutGroup: Object3D + point0: Vector3 + handleGroupPos0: Vector3 + lastDistance: number + vanillaColumn: VanillaColumn + vanillaColumnGroup: Object3D + columnsAdded: number + vanillaColumnsAdded: Object3D[] +} + +type StretchZUpData = { + direction: 1 + penultimateColumnGroup: Object3D + endColumnGroup: Object3D +} & StretchZDataShared + +type StretchZDownData = { + direction: -1 + startColumnGroup: Object3D + secondColumnGroup: Object3D +} & StretchZDataShared const useOnDragStretch = () => { - const stretchDataRef = useRef<{ - handleObject: Object3D - houseGroup: Group - handleGroup: Group - point0: Vector3 - handleGroupPos0: Vector3 - axis: "x" | "z" - direction: number - lastDistance: number - columnsAddedToEnd: number - } | null>(null) + const stretchDataRef = useRef(null) const onStretchXProgress: Handler<"drag", ThreeEvent> = () => {} const onStretchZProgress: Handler<"drag", ThreeEvent> = () => { const stretchData = stretchDataRef.current! - const { - handleGroup, - handleGroupPos0, - point0, - houseGroup, - axis, - direction, - handleObject, - lastDistance, - columnsAddedToEnd, - } = stretchData - - switch (direction) { + + switch (stretchData.direction) { case -1: { return } case 1: { + const { + handleGroup, + handleGroupPos0, + point0, + houseTransformsGroup, + layoutGroup, + lastDistance, + columnsAdded, + vanillaColumn, + vanillaColumnGroup, + penultimateColumnGroup, + endColumnGroup, + } = stretchData as StretchZUpData + const [x1, z1] = pointer.xz const distanceVector = new Vector3(x1, 0, z1).sub(point0) distanceVector.applyAxisAngle( new Vector3(0, 1, 0), - -houseGroup.rotation.y + -houseTransformsGroup.rotation.y ) const distance = distanceVector.z handleGroup.position.set(0, 0, handleGroupPos0.z + distance) - // maybe we want to oper on the house group columns doodah itself - - // and then constantly be computing whether we want to add or subtract + switch (true) { + // this part works + case distance > lastDistance: { + if (distance > vanillaColumn.length * columnsAdded) { + const newColumnGroup = vanillaColumnGroup.clone() - // so we're tracking the next gate up or down + newColumnGroup.position.setZ( + penultimateColumnGroup.position.z + + penultimateColumnGroup.userData.length / 2 + + columnsAdded * vanillaColumn.length + + vanillaColumn.length / 2 + ) - // probably want to just set visible false and turn off raycasting - // when we delete columns + layoutGroup.add(newColumnGroup) - // let's work on adding some first + newColumnGroup.userData.columnIndex = + penultimateColumnGroup.userData.columnIndex + 1 - // gate 1 = z'end + vanillaColumnLength + endColumnGroup.userData.columnIndex++ - const { vanillaColumn } = getActiveHouseUserData(houseGroup) + incrementColumnCount(layoutGroup) - switch (true) { - case distance > lastDistance: { - if (distance > vanillaColumn.length * columnsAddedToEnd) { - insertVanillaColumn(houseGroup, 1)() - stretchData.columnsAddedToEnd++ + stretchData.columnsAdded++ + stretchData.vanillaColumnsAdded.push(newColumnGroup) } break } + // this part doesn't work case distance < lastDistance: { - if (distance < vanillaColumn.length * columnsAddedToEnd) { + if (distance < vanillaColumn.length * (columnsAdded - 1)) { + pipe( + stretchData.vanillaColumnsAdded, + A.last, + O.map((x) => { + x.removeFromParent() + stretchData.columnsAdded-- + decrementColumnCount(layoutGroup) + endColumnGroup.userData.columnIndex-- + stretchData.vanillaColumnsAdded.pop() + }) + ) } - // if ( - // distance < - // vanillaColumnLength * columnsDelta + vanillaColumnLength - // ) { - // stretchData.current.columnsDelta++ - // } + break } } - stretchData.lastDistance = distance - - // gates on err... - // vanilla column length up + stretchData.lastDistance = distance - // existing column length down return } } @@ -114,27 +154,130 @@ const useOnDragStretch = () => { switch (true) { case first: { setCameraControlsEnabled(false) + dispatchPointerDown({ point, object }) const { direction, axis } = object.userData as StretchHandleMeshUserData + const handleGroup = handleColumnGroupParentQuery(object) + const houseTransformsGroup = getHouseTransformsGroupUp(object) + const { systemId, houseId, vanillaColumn, columnCount } = + getActiveHouseUserData(houseTransformsGroup) + const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) + switch (axis) { - case "x": + case "x": { return + } case "z": - const handleGroup = handleColumnGroupParentQuery(object) - const houseGroup = rootHouseGroupParentQuery(object) - stretchDataRef.current = { - handleObject: object, - houseGroup, - handleGroup, - handleGroupPos0: handleGroup.position.clone(), - point0: point, - lastDistance: 0, - direction, - axis, - columnsAddedToEnd: 0, + { + switch (direction) { + case 1: { + const [penultimateColumnGroup, endColumnGroup] = pipe( + layoutGroup, + getLayoutGroupColumnGroups, + A.filter((x) => x.userData.columnIndex >= columnCount - 2), + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ) + ) + + const zUpTask = pipe( + T.of(vanillaColumn), + T.chain(({ gridGroups }) => + pipe( + createColumnGroup({ + systemId, + houseId, + gridGroups, + columnIndex: -1, + }), + T.map((vanillaColumnGroup) => { + const foo: StretchZUpData = { + systemId, + houseId, + handleObject: object, + houseTransformsGroup: houseTransformsGroup, + handleGroup, + handleGroupPos0: handleGroup.position.clone(), + point0: point, + lastDistance: 0, + direction, + axis, + columnsAdded: 0, + vanillaColumn, + endColumnGroup, + penultimateColumnGroup, + layoutGroup, + vanillaColumnGroup, + vanillaColumnsAdded: [], + } + stretchDataRef.current = foo + }) + ) + ) + ) + + zUpTask() + + break + } + case -1: + const [startColumnGroup, secondColumnGroup] = pipe( + layoutGroup, + getLayoutGroupColumnGroups, + A.filter((x) => x.userData.columnIndex <= 1), + A.sort( + pipe( + Num.Ord, + Ord.contramap((x: Object3D) => x.userData.columnIndex) + ) + ) + ) + + const zDownTask = pipe( + T.of(vanillaColumn), + T.chain(({ gridGroups }) => + pipe( + createColumnGroup({ + systemId, + houseId, + gridGroups, + columnIndex: -1, + }), + T.map((vanillaColumnGroup) => { + stretchDataRef.current = { + systemId, + houseId, + handleObject: object, + houseTransformsGroup: houseTransformsGroup, + handleGroup, + handleGroupPos0: handleGroup.position.clone(), + point0: point, + lastDistance: 0, + direction, + axis, + columnsAdded: 0, + vanillaColumn, + layoutGroup, + startColumnGroup, + secondColumnGroup, + vanillaColumnGroup, + vanillaColumnsAdded: [], + } as StretchZDownData + }) + ) + ) + ) + + zDownTask() + + break + } } - dispatchPointerDown({ point, object }) + return } } diff --git a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts index 419490f0..a02bed61 100644 --- a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts +++ b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts @@ -16,7 +16,7 @@ import { BIG_CLIP_NUMBER } from "./layouts" import { getActiveHouseUserData, getActiveLayoutGroup, - getHouseTransformGroup, + getHouseTransformsGroupDown, getLayoutGroupColumnGroups, mapHouseTransformGroup, } from "./sceneQueries" @@ -50,7 +50,7 @@ const useClippingPlaneHelpers = (rootRef: RefObject) => { levelIndex: number ): O.Option => { return pipe( - getHouseTransformGroup(rootRef, houseId), + getHouseTransformsGroupDown(rootRef, houseId), O.chain((houseTransformGroup) => pipe( houseTransformGroup, diff --git a/app/design/ui-3d/fresh/helpers/handles.ts b/app/design/ui-3d/fresh/helpers/handles.ts index f535aa95..e8f60fd2 100644 --- a/app/design/ui-3d/fresh/helpers/handles.ts +++ b/app/design/ui-3d/fresh/helpers/handles.ts @@ -1,7 +1,10 @@ import { CircleGeometry, Group, Mesh, Object3D, PlaneGeometry } from "three" import { RoundedBoxGeometry } from "three-stdlib" import { PI } from "../../../../utils/math" -import { setInvisible, setVisibleAndRaycast } from "../../../../utils/three" +import { + setInvisibleNoRaycast, + setVisibleAndRaycast, +} from "../../../../utils/three" import handleMaterial from "../handleMaterial" import { RotateHandleMeshUserData, @@ -20,7 +23,7 @@ export const setStretchHandleVisibility = ( if (handleCount === 0) return true if (object.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh) { if (value) setVisibleAndRaycast(object) - else setInvisible(object) + else setInvisibleNoRaycast(object) console.log(object) handleCount-- } @@ -74,7 +77,7 @@ export const createStretchHandle = ({ houseId, } as StretchHandleMeshUserData - setInvisible(mesh) + setInvisibleNoRaycast(mesh) return mesh } diff --git a/app/design/ui-3d/fresh/helpers/sceneChanges.ts b/app/design/ui-3d/fresh/helpers/sceneChanges.ts index 96e0b721..3a8033a4 100644 --- a/app/design/ui-3d/fresh/helpers/sceneChanges.ts +++ b/app/design/ui-3d/fresh/helpers/sceneChanges.ts @@ -1,7 +1,10 @@ import { pipe } from "fp-ts/lib/function" import { Group } from "three" import { A, O } from "../../../../utils/functions" -import { setInvisible, setVisibleAndRaycast } from "../../../../utils/three" +import { + setInvisibleNoRaycast, + setVisibleAndRaycast, +} from "../../../../utils/three" import { getPartitionedLayoutGroups } from "./sceneQueries" export const debugNextLayout = (houseTransformsGroup: Group) => { @@ -15,7 +18,7 @@ export const debugNextLayout = (houseTransformsGroup: Group) => { A.head, O.map((nextLayout) => { setVisibleAndRaycast(nextLayout) - setInvisible(active) + setInvisibleNoRaycast(active) houseTransformsGroup.userData.activeChildUuid = nextLayout.uuid }) ) diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 2235b7c2..6e9d7da7 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -61,7 +61,7 @@ export const traverseDownUntil = ( // } // } -export const getHouseTransformGroup = ( +export const getHouseTransformsGroupDown = ( rootRef: RefObject, houseId: string ) => @@ -75,7 +75,7 @@ export const mapHouseTransformGroup = ( rootRef: RefObject, houseId: string, f: (houseTransformGroup: Object3D) => void -) => pipe(getHouseTransformGroup(rootRef, houseId), O.map(f)) +) => pipe(getHouseTransformsGroupDown(rootRef, houseId), O.map(f)) export const mapAllHouseTransformGroups = ( rootRef: RefObject, @@ -88,7 +88,7 @@ export const mapAllHouseTransformGroups = ( () => void null ) -export const rootHouseGroupParentQuery = (object: Object3D) => { +export const getHouseTransformsGroupUp = (object: Object3D) => { let x = object while (x.parent) { if (x.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup) { diff --git a/app/design/ui-3d/fresh/helpers/stretchZ.ts b/app/design/ui-3d/fresh/helpers/stretchZ.ts index 3e7e759c..69ed360b 100644 --- a/app/design/ui-3d/fresh/helpers/stretchZ.ts +++ b/app/design/ui-3d/fresh/helpers/stretchZ.ts @@ -29,7 +29,9 @@ export const insertVanillaColumn = ( if (direction === 1) { pipe( + // get column groups getLayoutGroupColumnGroups(layoutGroup), + // last two A.filter((x) => x.userData.columnIndex >= columnCount - 2), A.sort( pipe( @@ -38,19 +40,25 @@ export const insertVanillaColumn = ( ) ), ([penultimateColumnGroup, endColumnGroup]) => { + // put vanilla column group in place vanillaColumnGroup.position.setZ( penultimateColumnGroup.position.z + penultimateColumnGroup.userData.length / 2 + vanillaColumnLength / 2 ) + // add it layoutGroup.add(vanillaColumnGroup) + // set its index vanillaColumnGroup.userData.columnIndex = penultimateColumnGroup.userData.columnIndex + 1 + // adjust end's index endColumnGroup.userData.columnIndex++ incrementColumnCount(layoutGroup) + // n.b. you're already adjusting the endColumnGroup's + // position in the handler } ) } else if (direction === -1) { diff --git a/app/design/ui-3d/fresh/useKeyTestInteractions.ts b/app/design/ui-3d/fresh/useKeyTestInteractions.ts index 512cfd90..5e3bca2f 100644 --- a/app/design/ui-3d/fresh/useKeyTestInteractions.ts +++ b/app/design/ui-3d/fresh/useKeyTestInteractions.ts @@ -7,7 +7,7 @@ import { Group, Object3D, Vector3 } from "three" import layoutsDB from "../../../db/layouts" import { A, O, R, T } from "../../../utils/functions" import { PI } from "../../../utils/math" -import { setInvisible, yAxis } from "../../../utils/three" +import { setInvisibleNoRaycast, yAxis } from "../../../utils/three" import { updateEverything } from "./dimensions" import useClippingPlaneHelpers from "./helpers/clippingPlanes" import { createLayoutGroup } from "./helpers/layouts" @@ -173,7 +173,7 @@ const useKeyTestInteractions = (rootRef: RefObject) => { houseLayout, })() - setInvisible(layoutGroup) + setInvisibleNoRaycast(layoutGroup) houseTransformsGroup.add(layoutGroup) } diff --git a/app/utils/three.ts b/app/utils/three.ts index 1b10c0be..be76c610 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -200,7 +200,7 @@ export const setVisibleOnly = (object: Object3D) => { }) } -export const setInvisible = (object: Object3D) => { +export const setInvisibleNoRaycast = (object: Object3D) => { object.visible = false object.traverse((node) => { node.layers.set(CameraLayer.INVISIBLE) @@ -208,7 +208,7 @@ export const setInvisible = (object: Object3D) => { }) } -export const setInvisibleAndRaycast = (object: Object3D) => { +export const setInvisibleButRaycast = (object: Object3D) => { object.traverse((node) => { node.layers.set(CameraLayer.INVISIBLE) node.layers.enable(RaycasterLayer.ENABLED) From b6c02002d6ee2129503ec1d6f8dde3711e1c617f Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Fri, 11 Aug 2023 12:08:12 +0100 Subject: [PATCH 092/132] wip --- app/design/ui-3d/fresh/gestures/stretch.ts | 131 +++++++++++++++++---- app/design/ui-3d/fresh/helpers/layouts.ts | 70 ++--------- app/design/ui-3d/fresh/helpers/stretchZ.ts | 2 +- 3 files changed, 120 insertions(+), 83 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index ecf338f8..f2429ff3 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -7,7 +7,8 @@ import { VanillaColumn } from "../../../../db/layouts" import { A, Num, O, Ord, T } from "../../../../utils/functions" import { setCameraControlsEnabled } from "../../../state/camera" import pointer from "../../../state/pointer" -import { createColumnGroup } from "../helpers/layouts" +import { dispatchOutline } from "../events/outlines" +import { columnSorter, createColumnGroup } from "../helpers/layouts" import { getActiveHouseUserData, getActiveLayoutGroup, @@ -19,6 +20,7 @@ import { decrementColumnCount, incrementColumnCount, StretchHandleMeshUserData, + UserDataTypeEnum, } from "../userData" import { dispatchPointerDown, dispatchPointerUp } from "./events" @@ -50,9 +52,26 @@ type StretchZUpData = { type StretchZDownData = { direction: -1 startColumnGroup: Object3D - secondColumnGroup: Object3D + restColumnGroups: Object3D[] } & StretchZDataShared +const recomputeLayoutGroup = (layoutGroup: Object3D) => { + const oldLength = layoutGroup.userData.length + + const length = layoutGroup.children + .filter((x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup) + .reduce((acc, v) => acc + v.userData.length, 0) + + layoutGroup.position.setZ(-length / 2) + layoutGroup.userData.length = length + layoutGroup.parent?.position.add( + new Vector3(0, 0, (length - oldLength) / 2).applyAxisAngle( + new Vector3(0, 1, 0), + layoutGroup.parent.rotation.y + ) + ) +} + const useOnDragStretch = () => { const stretchDataRef = useRef(null) @@ -63,7 +82,74 @@ const useOnDragStretch = () => { switch (stretchData.direction) { case -1: { - return + // const { + // handleGroup, + // handleGroupPos0, + // point0, + // houseTransformsGroup, + // layoutGroup, + // lastDistance, + // columnsAdded, + // vanillaColumn, + // vanillaColumnGroup, + // startColumnGroup, + // restColumnGroups, + // } = stretchData as StretchZDownData + // const [x1, z1] = pointer.xz + // const distanceVector = new Vector3(x1, 0, z1).sub(point0) + // distanceVector.applyAxisAngle( + // new Vector3(0, 1, 0), + // -houseTransformsGroup.rotation.y + // ) + // const distance = distanceVector.z + // handleGroup.position.set(0, 0, handleGroupPos0.z + distance) + // switch (true) { + // // addy + // case distance < lastDistance: { + // console.log(`addy`) + // if (distance < vanillaColumn.length * -columnsAdded) { + // console.log(`addy go`) + // for (let columnGroup of restColumnGroups) { + // columnGroup.position.add( + // new Vector3(0, 0, vanillaColumn.length) + // ) + // columnGroup.userData.columnIndex++ + // } + // const [secondColumnGroup] = restColumnGroups + // const newColumnGroup = vanillaColumnGroup.clone() + // newColumnGroup.position.setZ( + // secondColumnGroup.position.z - + // secondColumnGroup.userData.length / 2 - + // vanillaColumn.length / 2 + // ) + // layoutGroup.add(newColumnGroup) + // // newColumnGroup.userData.columnIndex = 1 + // // endColumnGroup.userData.columnIndex++ + // // incrementColumnCount(layoutGroup) + // stretchData.columnsAdded++ + // // stretchData.vanillaColumnsAdded.push(newColumnGroup) + // } + // break + // } + // // subby + // case distance > lastDistance: { + // // if (distance < vanillaColumn.length * (columnsAdded - 1)) { + // // pipe( + // // stretchData.vanillaColumnsAdded, + // // A.last, + // // O.map((x) => { + // // x.removeFromParent() + // // stretchData.columnsAdded-- + // // decrementColumnCount(layoutGroup) + // // // endColumnGroup.userData.columnIndex-- + // // stretchData.vanillaColumnsAdded.pop() + // // }) + // // ) + // // } + // break + // } + // } + // stretchData.lastDistance = distance } case 1: { const { @@ -91,7 +177,6 @@ const useOnDragStretch = () => { handleGroup.position.set(0, 0, handleGroupPos0.z + distance) switch (true) { - // this part works case distance > lastDistance: { if (distance > vanillaColumn.length * columnsAdded) { const newColumnGroup = vanillaColumnGroup.clone() @@ -114,10 +199,10 @@ const useOnDragStretch = () => { stretchData.columnsAdded++ stretchData.vanillaColumnsAdded.push(newColumnGroup) + recomputeLayoutGroup(layoutGroup) } break } - // this part doesn't work case distance < lastDistance: { if (distance < vanillaColumn.length * (columnsAdded - 1)) { pipe( @@ -129,6 +214,7 @@ const useOnDragStretch = () => { decrementColumnCount(layoutGroup) endColumnGroup.userData.columnIndex-- stretchData.vanillaColumnsAdded.pop() + recomputeLayoutGroup(layoutGroup) }) ) } @@ -156,6 +242,11 @@ const useOnDragStretch = () => { setCameraControlsEnabled(false) dispatchPointerDown({ point, object }) + // dispatchOutline({ + // hoveredObjects: [], + // selectedObjects: [], + // }) + const { direction, axis } = object.userData as StretchHandleMeshUserData const handleGroup = handleColumnGroupParentQuery(object) @@ -176,12 +267,7 @@ const useOnDragStretch = () => { layoutGroup, getLayoutGroupColumnGroups, A.filter((x) => x.userData.columnIndex >= columnCount - 2), - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ) + columnSorter ) const zUpTask = pipe( @@ -195,7 +281,7 @@ const useOnDragStretch = () => { columnIndex: -1, }), T.map((vanillaColumnGroup) => { - const foo: StretchZUpData = { + const data: StretchZUpData = { systemId, houseId, handleObject: object, @@ -214,7 +300,7 @@ const useOnDragStretch = () => { vanillaColumnGroup, vanillaColumnsAdded: [], } - stretchDataRef.current = foo + stretchDataRef.current = data }) ) ) @@ -225,16 +311,10 @@ const useOnDragStretch = () => { break } case -1: - const [startColumnGroup, secondColumnGroup] = pipe( + const [startColumnGroup, ...restColumnGroups] = pipe( layoutGroup, getLayoutGroupColumnGroups, - A.filter((x) => x.userData.columnIndex <= 1), - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ) + columnSorter ) const zDownTask = pipe( @@ -248,7 +328,7 @@ const useOnDragStretch = () => { columnIndex: -1, }), T.map((vanillaColumnGroup) => { - stretchDataRef.current = { + const data: StretchZDownData = { systemId, houseId, handleObject: object, @@ -262,11 +342,12 @@ const useOnDragStretch = () => { columnsAdded: 0, vanillaColumn, layoutGroup, - startColumnGroup, - secondColumnGroup, vanillaColumnGroup, + startColumnGroup, + restColumnGroups, vanillaColumnsAdded: [], - } as StretchZDownData + } + stretchDataRef.current = data }) ) ) diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 94286f37..0cbd2e1b 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -295,6 +295,19 @@ liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( } ) +export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => + pipe( + columnLayout, + A.head, + O.map(({ gridGroups }) => + pipe( + gridGroups, + A.map(({ levelType }) => levelType) + ) + ), + O.getOrElse((): string[] => []) + ) + export const createLayoutGroup = ({ systemId, houseId, @@ -509,66 +522,9 @@ export const createInitialHouse = ({ }) ) -// export const createHouseGroup = ({ -// systemId, -// houseId, -// dnas, -// friendlyName, -// }: { -// systemId: string -// houseId: string -// dnas: string[] -// friendlyName: string -// }): T.Task => -// pipe( -// houseLayouts, -// R.lookup(getHouseLayoutsKey({ systemId, dnas })), -// O.match( -// (): T.Task => async () => { -// const layoutsWorker = getLayoutsWorker() -// if (!layoutsWorker) throw new Error(`no layouts worker`) -// return await layoutsWorker.getLayout({ -// systemId, -// dnas, -// }) -// }, -// (houseLayout) => T.of(houseLayout) -// ), -// T.chain((houseLayout) => -// houseLayoutToHouseGroup({ systemId, houseId, houseLayout }) -// ), -// T.map((group) => { -// group.userData.friendlyName = friendlyName -// return group -// }) -// ) - -export const removeColumnFromHouse = ( - houseGroup: Object3D, - columnGroup: Object3D -) => - pipe( - houseGroup.children, - A.head, - O.map((zCenterHouseGroup) => void zCenterHouseGroup.remove(columnGroup)) - ) - export const columnSorter = A.sort( pipe( Num.Ord, Ord.contramap((x: Object3D) => x.userData.columnIndex) ) ) - -export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => - pipe( - columnLayout, - A.head, - O.map(({ gridGroups }) => - pipe( - gridGroups, - A.map(({ levelType }) => levelType) - ) - ), - O.getOrElse((): string[] => []) - ) diff --git a/app/design/ui-3d/fresh/helpers/stretchZ.ts b/app/design/ui-3d/fresh/helpers/stretchZ.ts index 69ed360b..66a28e69 100644 --- a/app/design/ui-3d/fresh/helpers/stretchZ.ts +++ b/app/design/ui-3d/fresh/helpers/stretchZ.ts @@ -79,7 +79,7 @@ export const insertVanillaColumn = ( ) layoutGroup.add(vanillaColumnGroup) - decrementColumnCount(layoutGroup) + incrementColumnCount(layoutGroup) } ) } From 65f1af3b356fc70ac148f23637c81904c0fbe249 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Fri, 11 Aug 2023 13:31:18 +0100 Subject: [PATCH 093/132] wip --- app/design/ui-3d/fresh/gestures/stretch.ts | 10 ++++++---- app/design/ui-3d/init/Effects.tsx | 5 ++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index f2429ff3..0deef1eb 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -7,6 +7,7 @@ import { VanillaColumn } from "../../../../db/layouts" import { A, Num, O, Ord, T } from "../../../../utils/functions" import { setCameraControlsEnabled } from "../../../state/camera" import pointer from "../../../state/pointer" +import { updateHouseOBB } from "../dimensions" import { dispatchOutline } from "../events/outlines" import { columnSorter, createColumnGroup } from "../helpers/layouts" import { @@ -70,6 +71,7 @@ const recomputeLayoutGroup = (layoutGroup: Object3D) => { layoutGroup.parent.rotation.y ) ) + updateHouseOBB(layoutGroup.parent!) } const useOnDragStretch = () => { @@ -242,10 +244,10 @@ const useOnDragStretch = () => { setCameraControlsEnabled(false) dispatchPointerDown({ point, object }) - // dispatchOutline({ - // hoveredObjects: [], - // selectedObjects: [], - // }) + dispatchOutline({ + hoveredObjects: [], + selectedObjects: [], + }) const { direction, axis } = object.userData as StretchHandleMeshUserData diff --git a/app/design/ui-3d/init/Effects.tsx b/app/design/ui-3d/init/Effects.tsx index 9e95b700..d58be651 100644 --- a/app/design/ui-3d/init/Effects.tsx +++ b/app/design/ui-3d/init/Effects.tsx @@ -93,10 +93,9 @@ const Effects = () => { useOutlineListener((incoming) => { if (incoming.hoveredObjects) { hoveredObjects.current = incoming.hoveredObjects - } else if (incoming.selectedObjects) { + } + if (incoming.selectedObjects) { selectedObjects.current = incoming.selectedObjects - } else { - return } setSelection() }) From eacd07221be0f488af4b207cd3190b93d97f0148 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 14 Aug 2023 09:30:12 +0100 Subject: [PATCH 094/132] wip --- app/design/ui-3d/fresh/gestures/index.ts | 2 +- app/design/ui-3d/fresh/gestures/stretchX.ts | 10 ++ .../gestures/{stretch.ts => stretchZ.ts} | 148 +++++++++--------- app/design/ui-3d/fresh/helpers/handles.ts | 4 +- app/design/ui-3d/fresh/helpers/layouts.ts | 20 +++ 5 files changed, 110 insertions(+), 74 deletions(-) create mode 100644 app/design/ui-3d/fresh/gestures/stretchX.ts rename app/design/ui-3d/fresh/gestures/{stretch.ts => stretchZ.ts} (98%) diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts index ef4f9daa..9a687d74 100644 --- a/app/design/ui-3d/fresh/gestures/index.ts +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -25,7 +25,7 @@ import { } from "../userData" import useOnDragMove from "./move" import useOnDragRotate from "./rotate" -import useOnDragStretch from "./stretch" +import useOnDragStretch from "./stretchZ" const useGestures = () => { const onDragStretch = useOnDragStretch() diff --git a/app/design/ui-3d/fresh/gestures/stretchX.ts b/app/design/ui-3d/fresh/gestures/stretchX.ts new file mode 100644 index 00000000..065419d8 --- /dev/null +++ b/app/design/ui-3d/fresh/gestures/stretchX.ts @@ -0,0 +1,10 @@ +import { ThreeEvent } from "@react-three/fiber" +import { Handler } from "@use-gesture/react" + +const useOnDragStretchX = () => { + const onDragStretchX: Handler<"drag", ThreeEvent> = () => {} + + return onDragStretchX +} + +export default useOnDragStretchX diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretchZ.ts similarity index 98% rename from app/design/ui-3d/fresh/gestures/stretch.ts rename to app/design/ui-3d/fresh/gestures/stretchZ.ts index 0deef1eb..9a4feca3 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretchZ.ts @@ -24,6 +24,7 @@ import { UserDataTypeEnum, } from "../userData" import { dispatchPointerDown, dispatchPointerUp } from "./events" +import useOnDragStretchX from "./stretchX" type StretchData = StretchZUpData | StretchZDownData @@ -77,82 +78,12 @@ const recomputeLayoutGroup = (layoutGroup: Object3D) => { const useOnDragStretch = () => { const stretchDataRef = useRef(null) - const onStretchXProgress: Handler<"drag", ThreeEvent> = () => {} + const onStretchXProgress = useOnDragStretchX() const onStretchZProgress: Handler<"drag", ThreeEvent> = () => { const stretchData = stretchDataRef.current! switch (stretchData.direction) { - case -1: { - // const { - // handleGroup, - // handleGroupPos0, - // point0, - // houseTransformsGroup, - // layoutGroup, - // lastDistance, - // columnsAdded, - // vanillaColumn, - // vanillaColumnGroup, - // startColumnGroup, - // restColumnGroups, - // } = stretchData as StretchZDownData - // const [x1, z1] = pointer.xz - // const distanceVector = new Vector3(x1, 0, z1).sub(point0) - // distanceVector.applyAxisAngle( - // new Vector3(0, 1, 0), - // -houseTransformsGroup.rotation.y - // ) - // const distance = distanceVector.z - // handleGroup.position.set(0, 0, handleGroupPos0.z + distance) - // switch (true) { - // // addy - // case distance < lastDistance: { - // console.log(`addy`) - // if (distance < vanillaColumn.length * -columnsAdded) { - // console.log(`addy go`) - // for (let columnGroup of restColumnGroups) { - // columnGroup.position.add( - // new Vector3(0, 0, vanillaColumn.length) - // ) - // columnGroup.userData.columnIndex++ - // } - // const [secondColumnGroup] = restColumnGroups - // const newColumnGroup = vanillaColumnGroup.clone() - // newColumnGroup.position.setZ( - // secondColumnGroup.position.z - - // secondColumnGroup.userData.length / 2 - - // vanillaColumn.length / 2 - // ) - // layoutGroup.add(newColumnGroup) - // // newColumnGroup.userData.columnIndex = 1 - // // endColumnGroup.userData.columnIndex++ - // // incrementColumnCount(layoutGroup) - // stretchData.columnsAdded++ - // // stretchData.vanillaColumnsAdded.push(newColumnGroup) - // } - // break - // } - // // subby - // case distance > lastDistance: { - // // if (distance < vanillaColumn.length * (columnsAdded - 1)) { - // // pipe( - // // stretchData.vanillaColumnsAdded, - // // A.last, - // // O.map((x) => { - // // x.removeFromParent() - // // stretchData.columnsAdded-- - // // decrementColumnCount(layoutGroup) - // // // endColumnGroup.userData.columnIndex-- - // // stretchData.vanillaColumnsAdded.pop() - // // }) - // // ) - // // } - // break - // } - // } - // stretchData.lastDistance = distance - } case 1: { const { handleGroup, @@ -179,6 +110,7 @@ const useOnDragStretch = () => { handleGroup.position.set(0, 0, handleGroupPos0.z + distance) switch (true) { + // addy case distance > lastDistance: { if (distance > vanillaColumn.length * columnsAdded) { const newColumnGroup = vanillaColumnGroup.clone() @@ -205,8 +137,11 @@ const useOnDragStretch = () => { } break } + // subby case distance < lastDistance: { if (distance < vanillaColumn.length * (columnsAdded - 1)) { + // only if vanilla columns added + // otherwise we need to remove actual stuff! pipe( stretchData.vanillaColumnsAdded, A.last, @@ -228,6 +163,76 @@ const useOnDragStretch = () => { return } + case -1: { + // const { + // handleGroup, + // handleGroupPos0, + // point0, + // houseTransformsGroup, + // layoutGroup, + // lastDistance, + // columnsAdded, + // vanillaColumn, + // vanillaColumnGroup, + // startColumnGroup, + // restColumnGroups, + // } = stretchData as StretchZDownData + // const [x1, z1] = pointer.xz + // const distanceVector = new Vector3(x1, 0, z1).sub(point0) + // distanceVector.applyAxisAngle( + // new Vector3(0, 1, 0), + // -houseTransformsGroup.rotation.y + // ) + // const distance = distanceVector.z + // handleGroup.position.set(0, 0, handleGroupPos0.z + distance) + // switch (true) { + // // addy + // case distance < lastDistance: { + // console.log(`addy`) + // if (distance < vanillaColumn.length * -columnsAdded) { + // console.log(`addy go`) + // for (let columnGroup of restColumnGroups) { + // columnGroup.position.add( + // new Vector3(0, 0, vanillaColumn.length) + // ) + // columnGroup.userData.columnIndex++ + // } + // const [secondColumnGroup] = restColumnGroups + // const newColumnGroup = vanillaColumnGroup.clone() + // newColumnGroup.position.setZ( + // secondColumnGroup.position.z - + // secondColumnGroup.userData.length / 2 - + // vanillaColumn.length / 2 + // ) + // layoutGroup.add(newColumnGroup) + // // newColumnGroup.userData.columnIndex = 1 + // // endColumnGroup.userData.columnIndex++ + // // incrementColumnCount(layoutGroup) + // stretchData.columnsAdded++ + // // stretchData.vanillaColumnsAdded.push(newColumnGroup) + // } + // break + // } + // // subby + // case distance > lastDistance: { + // // if (distance < vanillaColumn.length * (columnsAdded - 1)) { + // // pipe( + // // stretchData.vanillaColumnsAdded, + // // A.last, + // // O.map((x) => { + // // x.removeFromParent() + // // stretchData.columnsAdded-- + // // decrementColumnCount(layoutGroup) + // // // endColumnGroup.userData.columnIndex-- + // // stretchData.vanillaColumnsAdded.pop() + // // }) + // // ) + // // } + // break + // } + // } + // stretchData.lastDistance = distance + } } } @@ -368,6 +373,7 @@ const useOnDragStretch = () => { if (stretchDataRef.current === null) throw new Error("stretchData.current null unexpectedly") dispatchPointerUp() + // so we need to... stretchDataRef.current = null setCameraControlsEnabled(true) return diff --git a/app/design/ui-3d/fresh/helpers/handles.ts b/app/design/ui-3d/fresh/helpers/handles.ts index e8f60fd2..893ec629 100644 --- a/app/design/ui-3d/fresh/helpers/handles.ts +++ b/app/design/ui-3d/fresh/helpers/handles.ts @@ -53,8 +53,8 @@ export const createStretchHandle = ({ ? [0, OFFSET_Y, OFFSET_XZ] : [0, OFFSET_Y, -OFFSET_XZ] : direction === 1 - ? [width / 2 + OFFSET_XZ, OFFSET_Y, 0] - : [-(width / 2 + OFFSET_XZ), OFFSET_Y, 0] + ? [width / 2 + OFFSET_XZ, OFFSET_Y, length / 2] + : [-(width / 2 + OFFSET_XZ), OFFSET_Y, length / 2] const rotation: [number, number, number] = axis === "x" ? [0, PI / 2, 0] : [0, 0, 0] diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 0cbd2e1b..bc7b07b5 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -381,6 +381,26 @@ export const createLayoutGroup = ({ }) ) + layoutGroup.add( + createStretchHandle({ + axis: "x", + direction: 1, + houseId, + length, + width, + }) + ) + + layoutGroup.add( + createStretchHandle({ + axis: "x", + direction: -1, + houseId, + length, + width, + }) + ) + pipe( columnGroups, A.findFirst( From 1c751a91bf199586f12578c9580eb166967acb13 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 14 Aug 2023 13:49:53 +0100 Subject: [PATCH 095/132] wip nicen --- app/design/state/siteCtx.ts | 4 +- app/design/ui-3d/fresh/dimensions.ts | 18 ++ app/design/ui-3d/fresh/gestures/index.ts | 86 +++++----- app/design/ui-3d/fresh/gestures/move.ts | 6 - app/design/ui-3d/fresh/gestures/rotate.ts | 6 - app/design/ui-3d/fresh/gestures/stretch.ts | 178 ++++++++++++++++++++ app/design/ui-3d/fresh/gestures/stretchZ.ts | 22 +-- app/design/ui-3d/fresh/userData.ts | 71 +++++--- 8 files changed, 298 insertions(+), 93 deletions(-) create mode 100644 app/design/ui-3d/fresh/gestures/stretch.ts diff --git a/app/design/state/siteCtx.ts b/app/design/state/siteCtx.ts index d4d27407..c1e60bbf 100644 --- a/app/design/state/siteCtx.ts +++ b/app/design/state/siteCtx.ts @@ -162,7 +162,9 @@ export const useSystemId = () => { return houses[houseId].systemId } -export const getModeBools = (mode: SiteCtxMode) => { +export const getModeBools = (_mode?: SiteCtxMode) => { + const mode = _mode ?? siteCtx.mode + const siteMode = mode === SiteCtxModeEnum.Enum.SITE const buildingMode = mode === SiteCtxModeEnum.Enum.BUILDING diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 4925f9c1..309f2b72 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -162,3 +162,21 @@ export const updateEverything = (houseTransformsGroup: Group) => { invalidate() } + +export const recomputeLayoutGroup = (layoutGroup: Object3D) => { + const oldLength = layoutGroup.userData.length + + const length = layoutGroup.children + .filter((x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup) + .reduce((acc, v) => acc + v.userData.length, 0) + + layoutGroup.position.setZ(-length / 2) + layoutGroup.userData.length = length + layoutGroup.parent?.position.add( + new Vector3(0, 0, (length - oldLength) / 2).applyAxisAngle( + new Vector3(0, 1, 0), + layoutGroup.parent.rotation.y + ) + ) + updateHouseOBB(layoutGroup.parent!) +} diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts index 9a687d74..28dd8e4c 100644 --- a/app/design/ui-3d/fresh/gestures/index.ts +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -4,10 +4,12 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { A, O } from "../../../../utils/functions" import { isMesh } from "../../../../utils/three" +import { setCameraControlsEnabled } from "../../../state/camera" import { openMenu } from "../../../state/menu" import scope, { ScopeItem } from "../../../state/scope" import siteCtx, { downMode, + getModeBools, SiteCtxMode, SiteCtxModeEnum, } from "../../../state/siteCtx" @@ -21,20 +23,23 @@ import { elementMeshToScopeItem, GridGroupUserData, HouseTransformsGroupUserData, + isElementMesh, + isRotateHandleMesh, + isStretchHandleMesh, + StretchHandleMeshUserData, UserDataTypeEnum, } from "../userData" +import { dispatchPointerDown, dispatchPointerUp } from "./events" import useOnDragMove from "./move" import useOnDragRotate from "./rotate" -import useOnDragStretch from "./stretchZ" +import useOnDragStretch from "./stretch" const useGestures = () => { - const onDragStretch = useOnDragStretch() + const { onDragStretchZ, onDragStretchX } = useOnDragStretch() const onDragMove = useOnDragMove() const onDragRotate = useOnDragRotate() - let stretching = false, - moving = false, - rotating = false + const firstDragEventRef = useRef | null>(null) return useGesture<{ drag: ThreeEvent @@ -47,50 +52,55 @@ const useGestures = () => { React.MouseEvent }>({ onDrag: (state) => { - const stretchModes: SiteCtxMode[] = [ - SiteCtxModeEnum.Enum.BUILDING, - SiteCtxModeEnum.Enum.LEVEL, - ] - - const type = state.event.object.userData?.type const { first, last } = state - const stretch = - stretching || - (stretchModes.includes(siteCtx.mode) && - type === UserDataTypeEnum.Enum.StretchHandleMesh) + if (first) { + setCameraControlsEnabled(false) + firstDragEventRef.current = state.event + const { point, object } = firstDragEventRef.current + dispatchPointerDown({ point, object }) + } + + const { object, point } = firstDragEventRef.current! - const move = - moving || (!stretch && type === UserDataTypeEnum.Enum.ElementMesh) + const { siteMode, buildingOrLevelMode } = getModeBools() - const rotate = - rotating || - (!stretch && type === UserDataTypeEnum.Enum.RotateHandleMesh) + // stretch + if (buildingOrLevelMode && isStretchHandleMesh(object)) { + const { + userData, + userData: { axis }, + } = object - switch (true) { - case stretch: { - if (first) stretching = true - onDragStretch(state) - if (last) stretching = false - break + if (axis === "z" && isStretchHandleMesh(object)) { + if (first) onDragStretchZ.first({ handleObject: object, point }) + if (!first && !last) onDragStretchZ.mid() + if (last) onDragStretchZ.last() } - case move: { - if (first) moving = true - onDragMove(state) - if (last) moving = false - break + if (axis === "x") { + // if (first) onDragStretchX.first(userData) + // if (!first && !last) onDragStretchX.mid(userData) + // if (last) onDragStretchX.last(userData) } - case rotate: { - if (first) rotating = true + } + + // move/rotate + if (siteMode) { + // move + if (isElementMesh(object)) { + onDragMove(state) + // rotate + } else if (isRotateHandleMesh(object)) { onDragRotate(state) - if (last) rotating = false - break - } - default: { - break } } + if (last) { + firstDragEventRef.current = null + dispatchPointerUp() + setCameraControlsEnabled(true) + } + invalidate() }, onHover: ({ event, event: { intersections }, hovering }) => { diff --git a/app/design/ui-3d/fresh/gestures/move.ts b/app/design/ui-3d/fresh/gestures/move.ts index 4be37f62..2a878dc6 100644 --- a/app/design/ui-3d/fresh/gestures/move.ts +++ b/app/design/ui-3d/fresh/gestures/move.ts @@ -46,15 +46,11 @@ const useOnDragMove = () => { (o) => o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, (houseTransformGroup) => { - dispatchPointerDown({ point, object }) - moveData.current = { houseObject: houseTransformGroup as Group, lastPoint: point.setY(0), } - setCameraControlsEnabled(false) - const scopeItem = elementMeshToScopeItem(object) scope.selected = scopeItem @@ -69,10 +65,8 @@ const useOnDragMove = () => { break } case last: { - setCameraControlsEnabled(true) updateIndexedHouseTransforms(moveData.current!.houseObject) moveData.current = null - dispatchPointerUp() return } default: { diff --git a/app/design/ui-3d/fresh/gestures/rotate.ts b/app/design/ui-3d/fresh/gestures/rotate.ts index 1daca07c..41a8872a 100644 --- a/app/design/ui-3d/fresh/gestures/rotate.ts +++ b/app/design/ui-3d/fresh/gestures/rotate.ts @@ -47,8 +47,6 @@ const useOnDragRotate = () => { (o) => o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, (houseTransformsGroup) => { - dispatchPointerDown({ point, object }) - const { obb: { center, @@ -67,8 +65,6 @@ const useOnDragRotate = () => { angle0, angle: angle0, } - - setCameraControlsEnabled(false) } ) return @@ -77,9 +73,7 @@ const useOnDragRotate = () => { break } case last: { - dispatchPointerUp() updateIndexedHouseTransforms(rotateData.current!.houseTransformsGroup) - setCameraControlsEnabled(true) rotateData.current = null break } diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts new file mode 100644 index 00000000..b14b6381 --- /dev/null +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -0,0 +1,178 @@ +import { pipe } from "fp-ts/lib/function" +import { useRef } from "react" +import { Object3D, Vector3 } from "three" +import { T } from "../../../../utils/functions" +import pointer from "../../../state/pointer" +import { recomputeLayoutGroup } from "../dimensions" +import { columnSorter, createColumnGroup } from "../helpers/layouts" +import { + getActiveHouseUserData, + getActiveLayoutGroup, + getHouseTransformsGroupUp, + getLayoutGroupColumnGroups, + handleColumnGroupParentQuery, +} from "../helpers/sceneQueries" +import { + incrementColumnCount, + StretchHandleMesh, + StretchHandleMeshUserData, +} from "../userData" + +const useOnDragStretch = () => { + const stretchZInitialDataRef = useRef<{ + direction: number + point0: Vector3 + handleGroup: Object3D + houseTransformsGroup: Object3D + layoutGroup: Object3D + handleGroupZ0: number + columnGroups: Object3D[] + templateVanillaColumnGroup: Object3D + vanillaLength: number + } | null>(null) + + const stretchZProgressDataRef = useRef<{ + vanillaColumnGroups: Object3D[] + lastDistance: number + }>({ + vanillaColumnGroups: [], + lastDistance: 0, + }) + + const onDragStretchZ = { + first: ({ + handleObject, + point, + }: { + handleObject: StretchHandleMesh + point: Vector3 + }) => { + const handleGroup = handleColumnGroupParentQuery(handleObject) + const houseTransformsGroup = getHouseTransformsGroupUp(handleGroup) + + const { systemId, houseId, vanillaColumn } = + getActiveHouseUserData(houseTransformsGroup) + + const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) + const columnGroups = pipe( + layoutGroup, + getLayoutGroupColumnGroups, + columnSorter + ) + + const task = pipe( + T.of(vanillaColumn), + T.chain(({ gridGroups }) => + pipe( + createColumnGroup({ + systemId, + houseId, + gridGroups, + columnIndex: -1, + }), + T.map((templateVanillaColumnGroup) => { + stretchZInitialDataRef.current = { + direction: handleObject.userData.direction, + handleGroup, + layoutGroup, + houseTransformsGroup, + point0: point, + handleGroupZ0: handleGroup.position.z, + templateVanillaColumnGroup, + vanillaLength: templateVanillaColumnGroup.userData.length, + columnGroups, + } + }) + ) + ) + ) + + task() + }, + mid: () => { + if (!stretchZInitialDataRef.current) return + + const { + point0, + houseTransformsGroup, + handleGroup, + handleGroupZ0, + vanillaLength, + templateVanillaColumnGroup, + columnGroups, + layoutGroup, + } = stretchZInitialDataRef.current + + const { lastDistance, vanillaColumnGroups } = + stretchZProgressDataRef.current + + const [x1, z1] = pointer.xz + const distanceVector = new Vector3(x1, 0, z1).sub(point0) + distanceVector.applyAxisAngle( + new Vector3(0, 1, 0), + -houseTransformsGroup.rotation.y + ) + const distance = distanceVector.z + handleGroup.position.set(0, 0, handleGroupZ0 + distance) + + // direction dependent + switch (true) { + // addy + case distance > lastDistance: { + if (distance > vanillaLength * vanillaColumnGroups.length) { + const newColumnGroup = templateVanillaColumnGroup.clone() + const penultimateColumnGroup = columnGroups[columnGroups.length - 2] + const endColumnGroup = columnGroups[columnGroups.length - 1] + + newColumnGroup.position.setZ( + penultimateColumnGroup.position.z + + penultimateColumnGroup.userData.length / 2 + + (vanillaColumnGroups.length + 0.5) * vanillaLength + ) + + layoutGroup.add(newColumnGroup) + + newColumnGroup.userData.columnIndex = + penultimateColumnGroup.userData.columnIndex + 1 + + endColumnGroup.userData.columnIndex++ + + incrementColumnCount(layoutGroup) + + stretchZProgressDataRef.current.vanillaColumnGroups.push( + newColumnGroup + ) + + recomputeLayoutGroup(layoutGroup) + } + } + // subby + case distance < lastDistance: { + if (distance < vanillaLength * (vanillaColumnGroups.length - 1)) { + } + } + } + + stretchZProgressDataRef.current.lastDistance = distance + }, + last: () => { + stretchZInitialDataRef.current = null + stretchZProgressDataRef.current = { + lastDistance: 0, + vanillaColumnGroups: [], + } + }, + } + + const stretchXData = useRef<{} | null>(null) + + const onDragStretchX = { + first: (stretchHandleUserData: StretchHandleMeshUserData) => {}, + mid: (stretchHandleUserData: StretchHandleMeshUserData) => {}, + last: (stretchHandleUserData: StretchHandleMeshUserData) => {}, + } + + return { onDragStretchZ, onDragStretchX } +} + +export default useOnDragStretch diff --git a/app/design/ui-3d/fresh/gestures/stretchZ.ts b/app/design/ui-3d/fresh/gestures/stretchZ.ts index 9a4feca3..6b14b0a4 100644 --- a/app/design/ui-3d/fresh/gestures/stretchZ.ts +++ b/app/design/ui-3d/fresh/gestures/stretchZ.ts @@ -7,7 +7,7 @@ import { VanillaColumn } from "../../../../db/layouts" import { A, Num, O, Ord, T } from "../../../../utils/functions" import { setCameraControlsEnabled } from "../../../state/camera" import pointer from "../../../state/pointer" -import { updateHouseOBB } from "../dimensions" +import { recomputeLayoutGroup, updateHouseOBB } from "../dimensions" import { dispatchOutline } from "../events/outlines" import { columnSorter, createColumnGroup } from "../helpers/layouts" import { @@ -57,24 +57,6 @@ type StretchZDownData = { restColumnGroups: Object3D[] } & StretchZDataShared -const recomputeLayoutGroup = (layoutGroup: Object3D) => { - const oldLength = layoutGroup.userData.length - - const length = layoutGroup.children - .filter((x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup) - .reduce((acc, v) => acc + v.userData.length, 0) - - layoutGroup.position.setZ(-length / 2) - layoutGroup.userData.length = length - layoutGroup.parent?.position.add( - new Vector3(0, 0, (length - oldLength) / 2).applyAxisAngle( - new Vector3(0, 1, 0), - layoutGroup.parent.rotation.y - ) - ) - updateHouseOBB(layoutGroup.parent!) -} - const useOnDragStretch = () => { const stretchDataRef = useRef(null) @@ -339,7 +321,7 @@ const useOnDragStretch = () => { systemId, houseId, handleObject: object, - houseTransformsGroup: houseTransformsGroup, + houseTransformsGroup, handleGroup, handleGroupPos0: handleGroup.position.clone(), point0: point, diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index f9d8110b..6274da7d 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -1,4 +1,4 @@ -import { Group, Material, Mesh, Object3D, Plane } from "three" +import { BufferGeometry, Group, Material, Mesh, Object3D, Plane } from "three" import { OBB } from "three-stdlib" import { z } from "zod" import { ColumnLayout, VanillaColumn } from "../../../db/layouts" @@ -117,49 +117,76 @@ export type UserData = | StretchHandleMeshUserData | RotateHandlesGroupUserData -export const isElementMesh = ( - node: Object3D -): node is Mesh & { userData: ElementMeshUserData; material: Material } => +// Mesh Types with type variables +export type ElementMesh = Mesh & { + userData: ElementMeshUserData +} + +export type StretchHandleMesh = Mesh & { + userData: StretchHandleMeshUserData +} + +export type RotateHandleMesh = Mesh & { + userData: RotateHandleMeshUserData +} + +// Group Types remain the same +export type ModuleGroup = Group & { + userData: ModuleGroupUserData +} + +export type GridGroup = Group & { + userData: GridGroupUserData +} + +export type ColumnGroup = Group & { + userData: ColumnGroupUserData +} + +export type HouseLayoutGroup = Group & { + userData: HouseLayoutGroupUserData +} + +export type HouseTransformsGroup = Group & { + userData: HouseTransformsGroupUserData +} + +export type RotateHandlesGroup = Group & { + userData: RotateHandlesGroupUserData +} + +// Type Guards +export const isElementMesh = (node: Object3D): node is ElementMesh => node.userData?.type === UserDataTypeEnum.Enum.ElementMesh export const isStretchHandleMesh = ( node: Object3D -): node is Mesh & { userData: StretchHandleMeshUserData; material: Material } => +): node is StretchHandleMesh => node.userData?.type === UserDataTypeEnum.Enum.StretchHandleMesh -export const isRotateHandleMesh = ( - node: Object3D -): node is Mesh & { userData: RotateHandleMeshUserData; material: Material } => +export const isRotateHandleMesh = (node: Object3D): node is RotateHandleMesh => node.userData?.type === UserDataTypeEnum.Enum.RotateHandleMesh -export const isModuleGroup = ( - node: Object3D -): node is Group & { userData: ModuleGroupUserData } => +export const isModuleGroup = (node: Object3D): node is ModuleGroup => node.userData?.type === UserDataTypeEnum.Enum.ModuleGroup -export const isGridGroup = ( - node: Object3D -): node is Group & { userData: GridGroupUserData } => +export const isGridGroup = (node: Object3D): node is GridGroup => node.userData?.type === UserDataTypeEnum.Enum.GridGroup -export const isColumnGroup = ( - node: Object3D -): node is Group & { userData: ColumnGroupUserData } => +export const isColumnGroup = (node: Object3D): node is ColumnGroup => node.userData?.type === UserDataTypeEnum.Enum.ColumnGroup -export const isHouseLayoutGroup = ( - node: Object3D -): node is Group & { userData: HouseLayoutGroupUserData } => +export const isHouseLayoutGroup = (node: Object3D): node is HouseLayoutGroup => node.userData?.type === UserDataTypeEnum.Enum.HouseLayoutGroup export const isHouseTransformsGroup = ( node: Object3D -): node is Group & { userData: HouseTransformsGroupUserData } => +): node is HouseTransformsGroup => node.userData?.type === UserDataTypeEnum.Enum.HouseTransformsGroup export const isRotateHandlesGroup = ( node: Object3D -): node is Group & { userData: RotateHandlesGroupUserData } => +): node is RotateHandlesGroup => node.userData?.type === UserDataTypeEnum.Enum.RotateHandlesGroup export const incrementColumnCount = (layoutGroup: Object3D) => { From 0d34891de424bd313e60b1550608077ea0c10a0a Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 14 Aug 2023 20:58:02 +0100 Subject: [PATCH 096/132] wip builds --- app/design/ui-3d/fresh/FreshApp.tsx | 6 +- app/design/ui-3d/fresh/dimensions.ts | 15 +- app/design/ui-3d/fresh/gestures/move.ts | 34 ++-- app/design/ui-3d/fresh/gestures/rotate.ts | 24 +-- app/design/ui-3d/fresh/gestures/stretch.ts | 116 ++++++++++---- app/design/ui-3d/fresh/gestures/stretchZ.ts | 3 +- .../ui-3d/fresh/helpers/clippingPlanes.ts | 4 +- app/design/ui-3d/fresh/helpers/handles.ts | 22 +-- app/design/ui-3d/fresh/helpers/layouts.ts | 15 ++ .../ui-3d/fresh/helpers/sceneQueries.ts | 147 +++++++++++------- app/design/ui-3d/fresh/helpers/stretchZ.ts | 14 +- .../ui-3d/fresh/useKeyTestInteractions.ts | 23 +-- 12 files changed, 273 insertions(+), 150 deletions(-) diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 74acd6c4..e59b19ed 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -21,7 +21,7 @@ import useClippingPlaneHelpers from "./helpers/clippingPlanes" import { BIG_CLIP_NUMBER } from "./helpers/layouts" import { getActiveHouseUserData, - getHouseTransformsGroupDown, + findHouseTransformsGroupDown, mapAllHouseTransformGroups, } from "./helpers/sceneQueries" import { UserDataTypeEnum } from "./userData" @@ -37,7 +37,7 @@ const FreshApp = () => { const showHouseStretchHandles = (houseId: string) => { pipe( - getHouseTransformsGroupDown(rootRef, houseId), + findHouseTransformsGroupDown(rootRef, houseId), O.map((houseTransformGroup) => { houseTransformGroup.traverse((node) => { if (node.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh) { @@ -66,7 +66,7 @@ const FreshApp = () => { const showHouseRotateHandles = (houseId: string) => { pipe( - getHouseTransformsGroupDown(rootRef, houseId), + findHouseTransformsGroupDown(rootRef, houseId), O.map((houseTransformGroup) => { houseTransformGroup.traverse((node) => { if (node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup) { diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 309f2b72..40406756 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -19,6 +19,8 @@ import { getActiveLayoutGroup, } from "./helpers/sceneQueries" import { + HouseLayoutGroup, + HouseTransformsGroup, HouseTransformsGroupUserData, ModuleGroupUserData, UserDataTypeEnum, @@ -42,7 +44,7 @@ const renderOBB = (obb: OBB, scene: Object3D) => { lastMesh = mesh } -export const updateHouseOBB = (houseTransformsGroup: Object3D) => { +export const updateHouseOBB = (houseTransformsGroup: HouseTransformsGroup) => { const { width, height, length } = getActiveHouseUserData(houseTransformsGroup) const activeLayoutGroup = getActiveLayoutGroup(houseTransformsGroup) @@ -128,7 +130,7 @@ const updateDnas = (houseGroup: Group) => { } export const updateIndexedHouseTransforms = ( - houseTransformsGroup: Object3D + houseTransformsGroup: HouseTransformsGroup ) => { const { houseId } = getActiveHouseUserData(houseTransformsGroup) @@ -143,7 +145,9 @@ export const updateIndexedHouseTransforms = ( updateHouseOBB(houseTransformsGroup) } -export const updateEverything = (houseTransformsGroup: Group) => { +export const updateEverything = ( + houseTransformsGroup: HouseTransformsGroup +) => { updateHouseLength(houseTransformsGroup) updateHouseOBB(houseTransformsGroup) // updateClippingPlanes(houseTransformsGroup) @@ -163,7 +167,7 @@ export const updateEverything = (houseTransformsGroup: Group) => { invalidate() } -export const recomputeLayoutGroup = (layoutGroup: Object3D) => { +export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { const oldLength = layoutGroup.userData.length const length = layoutGroup.children @@ -178,5 +182,6 @@ export const recomputeLayoutGroup = (layoutGroup: Object3D) => { layoutGroup.parent.rotation.y ) ) - updateHouseOBB(layoutGroup.parent!) + + updateHouseOBB(layoutGroup.parent as HouseTransformsGroup) } diff --git a/app/design/ui-3d/fresh/gestures/move.ts b/app/design/ui-3d/fresh/gestures/move.ts index 2a878dc6..0a9beb9b 100644 --- a/app/design/ui-3d/fresh/gestures/move.ts +++ b/app/design/ui-3d/fresh/gestures/move.ts @@ -2,25 +2,24 @@ import { ThreeEvent } from "@react-three/fiber" import { Handler } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { useRef } from "react" -import { Group, Object3D, Vector3 } from "three" -import { dispatchPointerDown, dispatchPointerUp } from "./events" +import { Vector3 } from "three" import { A, O } from "../../../../utils/functions" -import { setCameraControlsEnabled } from "../../../state/camera" import pointer from "../../../state/pointer" import scope from "../../../state/scope" import { updateIndexedHouseTransforms } from "../dimensions" import { dispatchOutline } from "../events/outlines" +import { objectToHouseObjects, findFirstGuardUp } from "../helpers/sceneQueries" import { - objectToHouseObjects, - traverseDownUntil, - traverseUpUntil, -} from "../helpers/sceneQueries" -import { elementMeshToScopeItem, UserDataTypeEnum } from "../userData" + elementMeshToScopeItem, + HouseTransformsGroup, + isHouseTransformsGroup, + UserDataTypeEnum, +} from "../userData" const useOnDragMove = () => { const moveData = useRef<{ lastPoint: Vector3 - houseObject: Group + houseTransformsGroup: HouseTransformsGroup } | null>(null) const onDragMove: Handler<"drag", ThreeEvent> = (state) => { @@ -41,13 +40,12 @@ const useOnDragMove = () => { if (object.userData.type !== UserDataTypeEnum.Enum.ElementMesh) return - traverseUpUntil( + pipe( object, - (o) => - o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, - (houseTransformGroup) => { + findFirstGuardUp(isHouseTransformsGroup), + O.map((houseTransformsGroup) => { moveData.current = { - houseObject: houseTransformGroup as Group, + houseTransformsGroup, lastPoint: point.setY(0), } @@ -57,15 +55,16 @@ const useOnDragMove = () => { dispatchOutline({ selectedObjects: objectToHouseObjects(object), }) - } + }) ) + return }) ) break } case last: { - updateIndexedHouseTransforms(moveData.current!.houseObject) + updateIndexedHouseTransforms(moveData.current!.houseTransformsGroup) moveData.current = null return } @@ -75,7 +74,8 @@ const useOnDragMove = () => { return } - const { lastPoint, houseObject } = moveData.current + const { lastPoint, houseTransformsGroup: houseObject } = + moveData.current const [px, pz] = pointer.xz const thisPoint = new Vector3(px, 0, pz) const delta = thisPoint.clone().sub(lastPoint) diff --git a/app/design/ui-3d/fresh/gestures/rotate.ts b/app/design/ui-3d/fresh/gestures/rotate.ts index 41a8872a..4f993b01 100644 --- a/app/design/ui-3d/fresh/gestures/rotate.ts +++ b/app/design/ui-3d/fresh/gestures/rotate.ts @@ -2,22 +2,24 @@ import { ThreeEvent } from "@react-three/fiber" import { Handler } from "@use-gesture/react" import { pipe } from "fp-ts/lib/function" import { useRef } from "react" -import { Object3D, Vector3 } from "three" -import { dispatchPointerDown, dispatchPointerUp } from "./events" +import { Vector3 } from "three" import { A, O } from "../../../../utils/functions" import { atan2 } from "../../../../utils/math" -import { setCameraControlsEnabled } from "../../../state/camera" import pointer from "../../../state/pointer" import { updateIndexedHouseTransforms } from "../dimensions" import { getActiveHouseUserData, - traverseUpUntil, + findFirstGuardUp, } from "../helpers/sceneQueries" -import { UserDataTypeEnum } from "../userData" +import { + HouseTransformsGroup, + isHouseTransformsGroup, + UserDataTypeEnum, +} from "../userData" const useOnDragRotate = () => { const rotateData = useRef<{ - houseTransformsGroup: Object3D + houseTransformsGroup: HouseTransformsGroup center: Vector3 rotation0: number angle0: number @@ -42,11 +44,10 @@ const useOnDragRotate = () => { if (object.userData.type !== UserDataTypeEnum.Enum.RotateHandleMesh) return - traverseUpUntil( + pipe( object, - (o) => - o.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup, - (houseTransformsGroup) => { + findFirstGuardUp(isHouseTransformsGroup), + O.map((houseTransformsGroup) => { const { obb: { center, @@ -65,8 +66,9 @@ const useOnDragRotate = () => { angle0, angle: angle0, } - } + }) ) + return }) ) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index b14b6381..cdb2f50d 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,10 +1,17 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" -import { T } from "../../../../utils/functions" +import { A, O, T } from "../../../../utils/functions" +import { setInvisibleNoRaycast } from "../../../../utils/three" +import { splitColumns } from "../../../../workers/layouts/worker" import pointer from "../../../state/pointer" import { recomputeLayoutGroup } from "../dimensions" -import { columnSorter, createColumnGroup } from "../helpers/layouts" +import { + columnSorter, + createColumnGroup, + splitColumnGroups, + vanillaColumns, +} from "../helpers/layouts" import { getActiveHouseUserData, getActiveLayoutGroup, @@ -13,6 +20,9 @@ import { handleColumnGroupParentQuery, } from "../helpers/sceneQueries" import { + ColumnGroup, + HouseLayoutGroup, + HouseTransformsGroup, incrementColumnCount, StretchHandleMesh, StretchHandleMeshUserData, @@ -23,19 +33,27 @@ const useOnDragStretch = () => { direction: number point0: Vector3 handleGroup: Object3D - houseTransformsGroup: Object3D - layoutGroup: Object3D + houseTransformsGroup: HouseTransformsGroup + layoutGroup: HouseLayoutGroup handleGroupZ0: number columnGroups: Object3D[] + startColumnGroup: Object3D + midColumnGroups: Object3D[] + endColumnGroup: Object3D templateVanillaColumnGroup: Object3D vanillaLength: number } | null>(null) + type Fence = { + z: number + columnGroup: Object3D + } + const stretchZProgressDataRef = useRef<{ - vanillaColumnGroups: Object3D[] + vanillaFences: Fence[] lastDistance: number }>({ - vanillaColumnGroups: [], + vanillaFences: [], lastDistance: 0, }) @@ -50,6 +68,7 @@ const useOnDragStretch = () => { const handleGroup = handleColumnGroupParentQuery(handleObject) const houseTransformsGroup = getHouseTransformsGroupUp(handleGroup) + const { direction } = handleObject.userData const { systemId, houseId, vanillaColumn } = getActiveHouseUserData(houseTransformsGroup) @@ -71,8 +90,11 @@ const useOnDragStretch = () => { columnIndex: -1, }), T.map((templateVanillaColumnGroup) => { + const { startColumnGroup, midColumnGroups, endColumnGroup } = + splitColumnGroups(columnGroups) + stretchZInitialDataRef.current = { - direction: handleObject.userData.direction, + direction, handleGroup, layoutGroup, houseTransformsGroup, @@ -81,6 +103,9 @@ const useOnDragStretch = () => { templateVanillaColumnGroup, vanillaLength: templateVanillaColumnGroup.userData.length, columnGroups, + startColumnGroup, + midColumnGroups, + endColumnGroup, } }) ) @@ -93,6 +118,7 @@ const useOnDragStretch = () => { if (!stretchZInitialDataRef.current) return const { + direction, point0, houseTransformsGroup, handleGroup, @@ -103,8 +129,7 @@ const useOnDragStretch = () => { layoutGroup, } = stretchZInitialDataRef.current - const { lastDistance, vanillaColumnGroups } = - stretchZProgressDataRef.current + const { lastDistance, vanillaFences } = stretchZProgressDataRef.current const [x1, z1] = pointer.xz const distanceVector = new Vector3(x1, 0, z1).sub(point0) @@ -112,22 +137,27 @@ const useOnDragStretch = () => { new Vector3(0, 1, 0), -houseTransformsGroup.rotation.y ) - const distance = distanceVector.z - handleGroup.position.set(0, 0, handleGroupZ0 + distance) - - // direction dependent - switch (true) { - // addy - case distance > lastDistance: { - if (distance > vanillaLength * vanillaColumnGroups.length) { - const newColumnGroup = templateVanillaColumnGroup.clone() + const distance = distanceVector.z * direction + handleGroup.position.set(0, 0, handleGroupZ0 + distance * direction) + + // additive + if (distance > lastDistance) { + // prob check if we need to add some hidden ones + if (undefined as any) { + } + + // gate check + if (distance > vanillaLength * vanillaFences.length) { + const newColumnGroup = templateVanillaColumnGroup.clone() + + if (direction === 1) { const penultimateColumnGroup = columnGroups[columnGroups.length - 2] const endColumnGroup = columnGroups[columnGroups.length - 1] newColumnGroup.position.setZ( penultimateColumnGroup.position.z + penultimateColumnGroup.userData.length / 2 + - (vanillaColumnGroups.length + 0.5) * vanillaLength + (vanillaFences.length + 0.5) * vanillaLength ) layoutGroup.add(newColumnGroup) @@ -139,27 +169,59 @@ const useOnDragStretch = () => { incrementColumnCount(layoutGroup) - stretchZProgressDataRef.current.vanillaColumnGroups.push( - newColumnGroup - ) + stretchZProgressDataRef.current.vanillaFences.push({ + columnGroup: newColumnGroup, + z: distance, + }) recomputeLayoutGroup(layoutGroup) } - } - // subby - case distance < lastDistance: { - if (distance < vanillaLength * (vanillaColumnGroups.length - 1)) { + + if (direction === -1) { + // newColumnGroup.position.setZ( + // secondColumnGroup.position.z + + // penultimateColumnGroup.userData.length / 2 + + // (vanillaColumnGroups.length + 0.5) * vanillaLength + // ) + // layoutGroup.add(newColumnGroup) } } } + // subtractive + if (distance < lastDistance) { + // gate check + if (distance > 0 && vanillaFences.length > 0) { + const foo = pipe( + vanillaFences, + A.last, + O.map(({ z, columnGroup }) => { + if (distance < z) { + columnGroup.removeFromParent() + vanillaFences.pop() + // vanillaFences.pop() + } + }) + ) + // if (distance < stretchZProgressDataRef.current.vanillaFences) { + // // we want to be tracking vanilla columns first + // // otherwise + // } + } + + if (distance < 0) { + // column group fence stuff here + // we would want to track an index and vis vs. no vis + } + } + stretchZProgressDataRef.current.lastDistance = distance }, last: () => { stretchZInitialDataRef.current = null stretchZProgressDataRef.current = { lastDistance: 0, - vanillaColumnGroups: [], + vanillaFences: [], } }, } diff --git a/app/design/ui-3d/fresh/gestures/stretchZ.ts b/app/design/ui-3d/fresh/gestures/stretchZ.ts index 6b14b0a4..bee45e1b 100644 --- a/app/design/ui-3d/fresh/gestures/stretchZ.ts +++ b/app/design/ui-3d/fresh/gestures/stretchZ.ts @@ -19,6 +19,7 @@ import { } from "../helpers/sceneQueries" import { decrementColumnCount, + HouseLayoutGroup, incrementColumnCount, StretchHandleMeshUserData, UserDataTypeEnum, @@ -35,7 +36,7 @@ type StretchZDataShared = { handleObject: Object3D houseTransformsGroup: Object3D handleGroup: Object3D - layoutGroup: Object3D + layoutGroup: HouseLayoutGroup point0: Vector3 handleGroupPos0: Vector3 lastDistance: number diff --git a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts index a02bed61..a2cb15d8 100644 --- a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts +++ b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts @@ -16,7 +16,7 @@ import { BIG_CLIP_NUMBER } from "./layouts" import { getActiveHouseUserData, getActiveLayoutGroup, - getHouseTransformsGroupDown, + findHouseTransformsGroupDown, getLayoutGroupColumnGroups, mapHouseTransformGroup, } from "./sceneQueries" @@ -50,7 +50,7 @@ const useClippingPlaneHelpers = (rootRef: RefObject) => { levelIndex: number ): O.Option => { return pipe( - getHouseTransformsGroupDown(rootRef, houseId), + findHouseTransformsGroupDown(rootRef, houseId), O.chain((houseTransformGroup) => pipe( houseTransformGroup, diff --git a/app/design/ui-3d/fresh/helpers/handles.ts b/app/design/ui-3d/fresh/helpers/handles.ts index 893ec629..be0208e4 100644 --- a/app/design/ui-3d/fresh/helpers/handles.ts +++ b/app/design/ui-3d/fresh/helpers/handles.ts @@ -1,5 +1,7 @@ +import { pipe } from "fp-ts/lib/function" import { CircleGeometry, Group, Mesh, Object3D, PlaneGeometry } from "three" import { RoundedBoxGeometry } from "three-stdlib" +import { A } from "../../../../utils/functions" import { PI } from "../../../../utils/math" import { setInvisibleNoRaycast, @@ -7,28 +9,26 @@ import { } from "../../../../utils/three" import handleMaterial from "../handleMaterial" import { + isStretchHandleMesh, RotateHandleMeshUserData, RotateHandlesGroupUserData, StretchHandleMeshUserData, UserDataTypeEnum, } from "../userData" -import { traverseDownUntil } from "./sceneQueries" +import { takeWhileGuardDown } from "./sceneQueries" -export const setStretchHandleVisibility = ( +export const setStretchHandlesVisibility = ( houseTransformGroup: Object3D, value: boolean ) => { - let handleCount = 2 - traverseDownUntil(houseTransformGroup, (object) => { - if (handleCount === 0) return true - if (object.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh) { + pipe( + houseTransformGroup, + takeWhileGuardDown(isStretchHandleMesh, 2), + A.map((object) => { if (value) setVisibleAndRaycast(object) else setInvisibleNoRaycast(object) - console.log(object) - handleCount-- - } - return false - }) + }) + ) } export const createStretchHandle = ({ diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index bc7b07b5..7cb18267 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -24,6 +24,7 @@ import { A, Num, O, Ord, R, S, T } from "../../../../utils/functions" import { getLayoutsWorker } from "../../../../workers" import { getMaterial } from "../systems" import { + ColumnGroup, ColumnGroupUserData, ElementMeshUserData, GridGroupUserData, @@ -548,3 +549,17 @@ export const columnSorter = A.sort( Ord.contramap((x: Object3D) => x.userData.columnIndex) ) ) + +export const splitColumnGroups = (columnGroups: ColumnGroup[]) => + pipe( + columnGroups, + A.partition( + ({ userData: { columnIndex } }) => + columnIndex === 0 || columnIndex === columnGroups.length - 1 + ), + ({ left: midColumnGroups, right: [startColumnGroup, endColumnGroup] }) => ({ + startColumnGroup, + endColumnGroup, + midColumnGroups, + }) + ) diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 6e9d7da7..26fbf6ce 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -3,65 +3,95 @@ import { RefObject } from "react" import { Group, Intersection, Material, Mesh, Object3D, Plane } from "three" import { A, O, someOrError } from "../../../../utils/functions" import { + ColumnGroup, + HouseLayoutGroup, HouseLayoutGroupUserData, + HouseTransformsGroup, HouseTransformsGroupUserData, isHouseTransformsGroup, UserDataTypeEnum, } from "../userData" -export const traverseUpUntil = ( - object: Object3D, - condition: (o: Object3D) => boolean, - callback: (o: Object3D) => void -) => { - if (condition(object)) { - callback(object) - return - } +export const findFirstGuardUp = + (guard: (o: Object3D) => o is T) => + (object: Object3D): O.Option => { + if (guard(object)) { + return O.some(object) + } - const parent = object.parent + const parent = object.parent + + if (parent !== null) { + return findFirstGuardUp(guard)(parent) + } - if (parent !== null) { - traverseUpUntil(parent, condition, callback) + return O.none } -} -export const traverseDownUntil = ( - object: Object3D, - callback: (o: Object3D) => boolean // stops if returns true -): boolean => { - if (callback(object)) { - return true + +export const findFirstGuardDown = + (guard: (o: Object3D) => o is T) => + (object: Object3D): O.Option => { + if (guard(object)) { + return O.some(object) + } + + const children = object.children + + for (let i = 0, l = children.length; i < l; i++) { + const child = children[i] + if (guard(child)) { + return O.some(child) + } + } + + return O.none } - const children = object.children +// findGuardAllDown will keep searching until no more matches are found. +export const findAllGuardDown = + (guard: (o: Object3D) => o is T) => + (object: Object3D): T[] => { + let results: T[] = [] - for (let i = 0, l = children.length; i < l; i++) { - if (traverseDownUntil(children[i], callback)) { - return true + if (guard(object)) { + results.push(object) } + + const children = object.children + + for (let i = 0, l = children.length; i < l; i++) { + results = results.concat(findAllGuardDown(guard)(children[i])) + } + + return results } - return false -} +// findGuardNDown will keep searching until N matches are found. +export const takeWhileGuardDown = + (guard: (o: Object3D) => o is T, n: number) => + (object: Object3D): T[] => { + let results: T[] = [] -// export const traverseDownUntil = ( -// object: Object3D, -// condition: (o: Object3D) => boolean, -// callback: (o: Object3D) => void -// ) => { -// if (condition(object)) { -// callback(object) -// return -// } - -// const children = object.children - -// for (let i = 0, l = children.length; i < l; i++) { -// traverseDownUntil(children[i], condition, callback) -// } -// } + if (results.length >= n) { + return results + } + + if (guard(object)) { + results.push(object) + } + + const children = object.children + + for (let i = 0, l = children.length; i < l && results.length < n; i++) { + results = results.concat( + takeWhileGuardDown(guard, n - results.length)(children[i]) + ) + } + + return results + } -export const getHouseTransformsGroupDown = ( +export const findHouseTransformsGroupDown = ( rootRef: RefObject, houseId: string ) => @@ -69,13 +99,13 @@ export const getHouseTransformsGroupDown = ( rootRef.current?.children, O.fromNullable, O.chain(A.findFirst((x) => x.userData.houseId === houseId)) - ) as O.Option + ) as O.Option export const mapHouseTransformGroup = ( rootRef: RefObject, houseId: string, f: (houseTransformGroup: Object3D) => void -) => pipe(getHouseTransformsGroupDown(rootRef, houseId), O.map(f)) +) => pipe(findHouseTransformsGroupDown(rootRef, houseId), O.map(f)) export const mapAllHouseTransformGroups = ( rootRef: RefObject, @@ -88,12 +118,12 @@ export const mapAllHouseTransformGroups = ( () => void null ) -export const getHouseTransformsGroupUp = (object: Object3D) => { +export const getHouseTransformsGroupUp = ( + object: Object3D +): HouseTransformsGroup => { let x = object while (x.parent) { - if (x.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup) { - return x as Group - } + if (isHouseTransformsGroup(x)) return x x = x.parent } throw new Error( @@ -144,18 +174,16 @@ export const getLayoutGroups = (houseTransformsGroup: Group): Group[] => ) as Group[] export const getActiveLayoutGroup = ( - houseTransformsGroup: Object3D -): Object3D => + houseTransformsGroup: HouseTransformsGroup +): HouseLayoutGroup => pipe( houseTransformsGroup.children, A.findFirst( - (x) => - x.uuid === - (houseTransformsGroup.userData as HouseTransformsGroupUserData) - .activeChildUuid + (x): x is HouseLayoutGroup => + x.uuid === houseTransformsGroup.userData.activeChildUuid ), someOrError(`getActiveLayoutGroup failure`) - ) as Group + ) export const getPartitionedLayoutGroups = (houseTransformsGroup: Group) => pipe( @@ -164,10 +192,13 @@ export const getPartitionedLayoutGroups = (houseTransformsGroup: Group) => A.partition((x) => x.uuid === houseTransformsGroup.userData.activeChildUuid) ) -export const getLayoutGroupColumnGroups = (layoutGroup: Object3D): Object3D[] => +export const getLayoutGroupColumnGroups = ( + layoutGroup: HouseLayoutGroup +): ColumnGroup[] => layoutGroup.children.filter( - (x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup - ) as Group[] + (x): x is ColumnGroup => + x.userData.type === UserDataTypeEnum.Enum.ColumnGroup + ) export const getLayoutGroupBySectionType = ( houseTransformsGroup: Group, diff --git a/app/design/ui-3d/fresh/helpers/stretchZ.ts b/app/design/ui-3d/fresh/helpers/stretchZ.ts index 66a28e69..6940d5f4 100644 --- a/app/design/ui-3d/fresh/helpers/stretchZ.ts +++ b/app/design/ui-3d/fresh/helpers/stretchZ.ts @@ -1,7 +1,11 @@ import { pipe } from "fp-ts/lib/function" import { Group, Object3D, Vector3 } from "three" import { A, Num, Ord, T } from "../../../../utils/functions" -import { decrementColumnCount, incrementColumnCount } from "../userData" +import { + decrementColumnCount, + HouseTransformsGroup, + incrementColumnCount, +} from "../userData" import { columnSorter, createColumnGroup, getVanillaColumn } from "./layouts" import { getActiveHouseUserData, @@ -10,7 +14,7 @@ import { } from "./sceneQueries" export const insertVanillaColumn = ( - houseTransformsGroup: Group, + houseTransformsGroup: HouseTransformsGroup, direction: 1 | -1 ) => { const { systemId, houseId, levelTypes, columnCount } = @@ -88,12 +92,12 @@ export const insertVanillaColumn = ( } export const subtractPenultimateColumn = ( - houseGroup: Group, + houseTransformsGroup: HouseTransformsGroup, direction: 1 | -1 ) => { - const layoutGroup = getActiveLayoutGroup(houseGroup) + const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) const columnGroups = getLayoutGroupColumnGroups(layoutGroup) - const { columnCount } = getActiveHouseUserData(houseGroup) + const { columnCount } = getActiveHouseUserData(houseTransformsGroup) if (columnCount <= 3) return diff --git a/app/design/ui-3d/fresh/useKeyTestInteractions.ts b/app/design/ui-3d/fresh/useKeyTestInteractions.ts index 5e3bca2f..d3259782 100644 --- a/app/design/ui-3d/fresh/useKeyTestInteractions.ts +++ b/app/design/ui-3d/fresh/useKeyTestInteractions.ts @@ -1,6 +1,6 @@ import { invalidate } from "@react-three/fiber" import { liveQuery } from "dexie" -import { pipe } from "fp-ts/lib/function" +import { flow, pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { useKey } from "react-use" import { Group, Object3D, Vector3 } from "three" @@ -8,11 +8,11 @@ import layoutsDB from "../../../db/layouts" import { A, O, R, T } from "../../../utils/functions" import { PI } from "../../../utils/math" import { setInvisibleNoRaycast, yAxis } from "../../../utils/three" -import { updateEverything } from "./dimensions" import useClippingPlaneHelpers from "./helpers/clippingPlanes" import { createLayoutGroup } from "./helpers/layouts" import { debugNextLayout } from "./helpers/sceneChanges" import { + findAllGuardDown, getActiveHouseUserData, getLayoutGroupBySectionType, } from "./helpers/sceneQueries" @@ -20,17 +20,20 @@ import { insertVanillaColumn, subtractPenultimateColumn, } from "./helpers/stretchZ" -import { UserDataTypeEnum } from "./userData" +import { + HouseTransformsGroup, + isHouseTransformsGroup, + UserDataTypeEnum, +} from "./userData" const useKeyTestInteractions = (rootRef: RefObject) => { - const getHouseGroups = () => { - return ( - rootRef.current?.children.filter( - (x: Object3D): x is Group => - x.userData.type === UserDataTypeEnum.Enum.HouseTransformsGroup - ) ?? [] + const getHouseGroups = () => + pipe( + rootRef.current, + O.fromNullable, + O.map(flow(findAllGuardDown(isHouseTransformsGroup))), + O.getOrElse((): HouseTransformsGroup[] => []) ) - } useKey("z", async () => { for (let houseGroup of getHouseGroups()) { From 77ef51be5148d904c5f4e5f118adfa2448a84fa8 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 14 Aug 2023 22:40:24 +0100 Subject: [PATCH 097/132] wip --- app/design/ui-3d/fresh/gestures/stretch.ts | 148 ++++++++++++++++++--- app/design/ui-3d/fresh/helpers/layouts.ts | 4 +- app/utils/three.ts | 8 ++ 3 files changed, 143 insertions(+), 17 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index cdb2f50d..55c64c34 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -2,15 +2,17 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" import { A, O, T } from "../../../../utils/functions" -import { setInvisibleNoRaycast } from "../../../../utils/three" -import { splitColumns } from "../../../../workers/layouts/worker" +import { + replicateObject, + setInvisibleNoRaycast, + setVisibleAndRaycast, +} from "../../../../utils/three" import pointer from "../../../state/pointer" import { recomputeLayoutGroup } from "../dimensions" import { columnSorter, createColumnGroup, splitColumnGroups, - vanillaColumns, } from "../helpers/layouts" import { getActiveHouseUserData, @@ -28,6 +30,8 @@ import { StretchHandleMeshUserData, } from "../userData" +const TMP_MAX_LENGTH = 10 + const useOnDragStretch = () => { const stretchZInitialDataRef = useRef<{ direction: number @@ -40,21 +44,24 @@ const useOnDragStretch = () => { startColumnGroup: Object3D midColumnGroups: Object3D[] endColumnGroup: Object3D - templateVanillaColumnGroup: Object3D + templateVanillaColumnGroup: ColumnGroup vanillaLength: number + maxLength: number } | null>(null) type Fence = { z: number - columnGroup: Object3D + columnGroup: ColumnGroup } const stretchZProgressDataRef = useRef<{ - vanillaFences: Fence[] + fences: Fence[] lastDistance: number + fenceIndex: number }>({ - vanillaFences: [], + fences: [], lastDistance: 0, + fenceIndex: 0, }) const onDragStretchZ = { @@ -93,6 +100,8 @@ const useOnDragStretch = () => { const { startColumnGroup, midColumnGroups, endColumnGroup } = splitColumnGroups(columnGroups) + const vanillaLength = templateVanillaColumnGroup.userData.length + stretchZInitialDataRef.current = { direction, handleGroup, @@ -101,11 +110,89 @@ const useOnDragStretch = () => { point0: point, handleGroupZ0: handleGroup.position.z, templateVanillaColumnGroup, - vanillaLength: templateVanillaColumnGroup.userData.length, + vanillaLength, columnGroups, startColumnGroup, midColumnGroups, endColumnGroup, + maxLength: TMP_MAX_LENGTH, + } + + if (direction === 1) { + let fences: Fence[] = [] + + for (let columnGroup of midColumnGroups) { + fences.push({ + columnGroup, + z: -( + endColumnGroup.position.z + + endColumnGroup.userData.length / 2 - + columnGroup.position.z + + columnGroup.userData.length / 2 + ), + }) + } + replicateObject(3, templateVanillaColumnGroup).forEach( + (columnGroup, i) => { + const lastColumnGroup = + fences[fences.length - 1].columnGroup + + columnGroup.position.setZ( + lastColumnGroup.position.z + + lastColumnGroup.userData.length / 2 + + vanillaLength * i + ) + + columnGroup.userData.columnIndex = + lastColumnGroup.userData.columnIndex + 1 + + setInvisibleNoRaycast(columnGroup) + layoutGroup.add(columnGroup) + + fences.push({ + columnGroup, + z: i * vanillaLength, + }) + } + ) + + // const fences = pipe( + // midColumnGroups, + // A.reverse, + // A.map( + // (columnGroup): Fence => ({ + // columnGroup, + // z: -( + // endColumnGroup.position.z + + // endColumnGroup.userData.length / 2 - + // columnGroup.position.z + + // columnGroup.userData.length / 2 + // ), + // }) + // ), + // A.reverse, + // A.concat( + // pipe( + // replicateObject(3, templateVanillaColumnGroup), + // A.mapWithIndex((i, columnGroup): Fence => { + // columnGroup.position.z = i * vanillaLength + // columnGroup.userData + // layoutGroup.add(columnGroup) + // return { + // columnGroup, + // z: i * vanillaLength, + // } + // }) + // ) + // ) + // ) + + stretchZProgressDataRef.current.fences = fences + stretchZProgressDataRef.current.fenceIndex = 0 + } + + if (direction === -1) { + // complete me } }) ) @@ -129,7 +216,8 @@ const useOnDragStretch = () => { layoutGroup, } = stretchZInitialDataRef.current - const { lastDistance, vanillaFences } = stretchZProgressDataRef.current + const { lastDistance, fences: vanillaFences } = + stretchZProgressDataRef.current const [x1, z1] = pointer.xz const distanceVector = new Vector3(x1, 0, z1).sub(point0) @@ -140,6 +228,38 @@ const useOnDragStretch = () => { const distance = distanceVector.z * direction handleGroup.position.set(0, 0, handleGroupZ0 + distance * direction) + const { fenceIndex, fences } = stretchZProgressDataRef.current + const lastVisibleFence = fences[fenceIndex] + + if (direction === 1) { + if (distance > lastDistance) { + if (fenceIndex + 1 < fences.length) { + const nextFence = fences[fenceIndex + 1] + if (distance > nextFence.z) { + setVisibleAndRaycast(nextFence.columnGroup) + console.log(nextFence.columnGroup.position) + // function to add another vanilla column group to the fences + // so long as not max + stretchZProgressDataRef.current.fenceIndex++ + } + } + } + + if (distance < lastDistance) { + if (fenceIndex - 1 >= 0) { + const prevFence = fences[fenceIndex - 1] + if (distance < prevFence.z) { + setInvisibleNoRaycast(prevFence.columnGroup) + stretchZProgressDataRef.current.fenceIndex-- + } + } + } + } + + stretchZProgressDataRef.current.lastDistance = distance + + return + // additive if (distance > lastDistance) { // prob check if we need to add some hidden ones @@ -169,7 +289,7 @@ const useOnDragStretch = () => { incrementColumnCount(layoutGroup) - stretchZProgressDataRef.current.vanillaFences.push({ + stretchZProgressDataRef.current.fences.push({ columnGroup: newColumnGroup, z: distance, }) @@ -192,14 +312,13 @@ const useOnDragStretch = () => { if (distance < lastDistance) { // gate check if (distance > 0 && vanillaFences.length > 0) { - const foo = pipe( + pipe( vanillaFences, A.last, O.map(({ z, columnGroup }) => { if (distance < z) { columnGroup.removeFromParent() vanillaFences.pop() - // vanillaFences.pop() } }) ) @@ -214,14 +333,13 @@ const useOnDragStretch = () => { // we would want to track an index and vis vs. no vis } } - - stretchZProgressDataRef.current.lastDistance = distance }, last: () => { stretchZInitialDataRef.current = null stretchZProgressDataRef.current = { lastDistance: 0, - vanillaFences: [], + fences: [], + fenceIndex: 0, } }, } diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 7cb18267..d74682e7 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -197,7 +197,7 @@ export const createColumnGroup = columnIndex: number startColumn?: boolean endColumn?: boolean - }): T.Task => + }): T.Task => async () => { const columnGroup = new Group() @@ -247,7 +247,7 @@ export const createColumnGroup = columnGroup.userData = columnGroupUserData - return columnGroup + return columnGroup as ColumnGroup } export const createColumnGroups = ({ diff --git a/app/utils/three.ts b/app/utils/three.ts index be76c610..4b9a297e 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -214,3 +214,11 @@ export const setInvisibleButRaycast = (object: Object3D) => { node.layers.enable(RaycasterLayer.ENABLED) }) } + +export const replicateObject = (n: number, obj: T): T[] => { + const replicated: T[] = [] + for (let i = 0; i < n; i++) { + replicated.push(obj.clone() as T) + } + return replicated +} From 762e033cc18eb6651624c7bd40967149a5973aca Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 15 Aug 2023 08:39:49 +0100 Subject: [PATCH 098/132] wip fixed positioning gnarl --- app/design/ui-3d/fresh/dimensions.ts | 2 + app/design/ui-3d/fresh/gestures/stretch.ts | 89 +++++++++------------- app/design/ui-3d/fresh/helpers/layouts.ts | 3 +- app/design/ui-3d/fresh/systems.ts | 15 ++-- app/design/ui-3d/fresh/userData.ts | 15 +++- app/utils/three.ts | 25 ++++++ 6 files changed, 84 insertions(+), 65 deletions(-) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 40406756..138ef334 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -174,6 +174,8 @@ export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { .filter((x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup) .reduce((acc, v) => acc + v.userData.length, 0) + console.log({ oldLength, length }) + layoutGroup.position.setZ(-length / 2) layoutGroup.userData.length = length layoutGroup.parent?.position.add( diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 55c64c34..a47375ed 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -3,6 +3,7 @@ import { useRef } from "react" import { Object3D, Vector3 } from "three" import { A, O, T } from "../../../../utils/functions" import { + addDebugLineAtZ, replicateObject, setInvisibleNoRaycast, setVisibleAndRaycast, @@ -26,12 +27,15 @@ import { HouseLayoutGroup, HouseTransformsGroup, incrementColumnCount, + isElementMesh, StretchHandleMesh, StretchHandleMeshUserData, } from "../userData" const TMP_MAX_LENGTH = 10 +const DEBUG_FENCES = true + const useOnDragStretch = () => { const stretchZInitialDataRef = useRef<{ direction: number @@ -43,6 +47,8 @@ const useOnDragStretch = () => { columnGroups: Object3D[] startColumnGroup: Object3D midColumnGroups: Object3D[] + midStartZ: number + midEndZ: number endColumnGroup: Object3D templateVanillaColumnGroup: ColumnGroup vanillaLength: number @@ -116,20 +122,20 @@ const useOnDragStretch = () => { midColumnGroups, endColumnGroup, maxLength: TMP_MAX_LENGTH, + midStartZ: midColumnGroups[0].position.z, + midEndZ: endColumnGroup.position.z, } + const { midStartZ, midEndZ } = stretchZInitialDataRef.current + if (direction === 1) { let fences: Fence[] = [] for (let columnGroup of midColumnGroups) { + const z = columnGroup.position.z + columnGroup.userData.length fences.push({ columnGroup, - z: -( - endColumnGroup.position.z + - endColumnGroup.userData.length / 2 - - columnGroup.position.z + - columnGroup.userData.length / 2 - ), + z, }) } replicateObject(3, templateVanillaColumnGroup).forEach( @@ -137,56 +143,29 @@ const useOnDragStretch = () => { const lastColumnGroup = fences[fences.length - 1].columnGroup - columnGroup.position.setZ( - lastColumnGroup.position.z + - lastColumnGroup.userData.length / 2 + - vanillaLength * i - ) - columnGroup.userData.columnIndex = - lastColumnGroup.userData.columnIndex + 1 + lastColumnGroup.userData.columnIndex + i + 1 + + const positionZ = + midEndZ + i * vanillaLength + vanillaLength / 2 + + columnGroup.position.setZ(positionZ) - setInvisibleNoRaycast(columnGroup) layoutGroup.add(columnGroup) + const fenceZ = positionZ + fences.push({ columnGroup, - z: i * vanillaLength, + z: fenceZ, }) + + if (DEBUG_FENCES) { + addDebugLineAtZ(layoutGroup, fenceZ, undefined, "red") + } } ) - // const fences = pipe( - // midColumnGroups, - // A.reverse, - // A.map( - // (columnGroup): Fence => ({ - // columnGroup, - // z: -( - // endColumnGroup.position.z + - // endColumnGroup.userData.length / 2 - - // columnGroup.position.z + - // columnGroup.userData.length / 2 - // ), - // }) - // ), - // A.reverse, - // A.concat( - // pipe( - // replicateObject(3, templateVanillaColumnGroup), - // A.mapWithIndex((i, columnGroup): Fence => { - // columnGroup.position.z = i * vanillaLength - // columnGroup.userData - // layoutGroup.add(columnGroup) - // return { - // columnGroup, - // z: i * vanillaLength, - // } - // }) - // ) - // ) - // ) - stretchZProgressDataRef.current.fences = fences stretchZProgressDataRef.current.fenceIndex = 0 } @@ -229,28 +208,28 @@ const useOnDragStretch = () => { handleGroup.position.set(0, 0, handleGroupZ0 + distance * direction) const { fenceIndex, fences } = stretchZProgressDataRef.current - const lastVisibleFence = fences[fenceIndex] if (direction === 1) { if (distance > lastDistance) { if (fenceIndex + 1 < fences.length) { const nextFence = fences[fenceIndex + 1] if (distance > nextFence.z) { - setVisibleAndRaycast(nextFence.columnGroup) - console.log(nextFence.columnGroup.position) + // setVisibleAndRaycast(nextFence.columnGroup) // function to add another vanilla column group to the fences // so long as not max - stretchZProgressDataRef.current.fenceIndex++ + // stretchZProgressDataRef.current.fenceIndex++ } } } + // if (distance < lastDistance) { - if (fenceIndex - 1 >= 0) { - const prevFence = fences[fenceIndex - 1] - if (distance < prevFence.z) { - setInvisibleNoRaycast(prevFence.columnGroup) - stretchZProgressDataRef.current.fenceIndex-- + if (fenceIndex >= 0) { + const lastVisibleFence = fences[fenceIndex] + + if (distance < lastVisibleFence.z) { + // setInvisibleNoRaycast(prevFence.columnGroup) + // stretchZProgressDataRef.current.fenceIndex-- } } } diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index d74682e7..3c7389d0 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -140,7 +140,8 @@ export const createModuleGroup = async ({ systemId, ifcTag, houseId, - }) as MeshStandardMaterial + }) + material.wireframe = true const mesh = new Mesh(geometry, material) mesh.castShadow = true diff --git a/app/design/ui-3d/fresh/systems.ts b/app/design/ui-3d/fresh/systems.ts index f6e73c6b..dba4744c 100644 --- a/app/design/ui-3d/fresh/systems.ts +++ b/app/design/ui-3d/fresh/systems.ts @@ -1,7 +1,12 @@ import { liveQuery } from "dexie" import { Element } from "../../../../server/data/elements" import { Material } from "../../../../server/data/materials" -import { DoubleSide, Material as ThreeMaterial, MeshBasicMaterial } from "three" +import { + DoubleSide, + Material as ThreeMaterial, + MeshBasicMaterial, + MeshStandardMaterial, +} from "three" import systemsDB, { LastFetchStamped } from "../../../db/systems" import { identity, pipe } from "fp-ts/lib/function" import { O, R } from "../../../utils/functions" @@ -24,13 +29,13 @@ liveQuery(() => systemsDB.materials.toArray()).subscribe((dbMaterials) => { } }) -const basicMaterial: ThreeMaterial = new MeshBasicMaterial({ +const basicMaterial = new MeshStandardMaterial({ color: "tomato", side: DoubleSide, }) // hash(systemId, houseId, material specification) : ThreeMaterial -const threeMaterials: Record = {} +const threeMaterials: Record = {} const getMaterialHash = ({ systemId, @@ -51,7 +56,7 @@ const getThreeMaterial = ({ systemId: string houseId: string material: Material -}): ThreeMaterial => { +}): MeshStandardMaterial => { const materialHash = getMaterialHash({ systemId, houseId, specification }) return pipe( threeMaterials, @@ -69,7 +74,7 @@ export const getMaterial = ( houseId: string ifcTag: string } | null = null -): ThreeMaterial => { +): MeshStandardMaterial => { if (input === null) return basicMaterial const { systemId, houseId, ifcTag } = input diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 6274da7d..20c94856 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -1,4 +1,11 @@ -import { BufferGeometry, Group, Material, Mesh, Object3D, Plane } from "three" +import { + BufferGeometry, + Group, + Mesh, + MeshStandardMaterial, + Object3D, + Plane, +} from "three" import { OBB } from "three-stdlib" import { z } from "zod" import { ColumnLayout, VanillaColumn } from "../../../db/layouts" @@ -118,15 +125,15 @@ export type UserData = | RotateHandlesGroupUserData // Mesh Types with type variables -export type ElementMesh = Mesh & { +export type ElementMesh = Mesh & { userData: ElementMeshUserData } -export type StretchHandleMesh = Mesh & { +export type StretchHandleMesh = Mesh & { userData: StretchHandleMeshUserData } -export type RotateHandleMesh = Mesh & { +export type RotateHandleMesh = Mesh & { userData: RotateHandleMeshUserData } diff --git a/app/utils/three.ts b/app/utils/three.ts index 4b9a297e..571cc831 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -3,8 +3,12 @@ import { useGLTF as useGLTFDrei } from "@react-three/drei" import { RootState } from "@react-three/fiber" import { useCallback, useMemo, useRef } from "react" import { + BufferGeometry, + Color, DoubleSide, Group, + Line, + LineBasicMaterial, Matrix4, Mesh, MeshStandardMaterial, @@ -222,3 +226,24 @@ export const replicateObject = (n: number, obj: T): T[] => { } return replicated } + +export const addDebugLineAtZ = ( + parent: Object3D, + z: number, + length: number = 5, + color: Color | number | string = 0xff0000 +): void => { + // Step 1: Create geometry for the line segment. + const lineGeometry = new BufferGeometry().setFromPoints([ + new Vector3(-length / 2, 0, 0), + new Vector3(length / 2, 0, 0), + ]) + + // Step 2: Create a material for the line. + const lineMaterial = new LineBasicMaterial({ color: color }) + + // Step 3: Create the line object, set its z position, and add it to the parent. + const line = new Line(lineGeometry, lineMaterial) + line.position.set(0, 0, z) + parent.add(line) +} From d358c3bb56465e65dd91b5d50164a5b4a579d9cc Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 15 Aug 2023 09:28:38 +0100 Subject: [PATCH 099/132] wip progress --- app/design/ui-3d/fresh/gestures/stretch.ts | 129 ++++++++++++++------- app/design/ui-3d/fresh/helpers/layouts.ts | 1 - 2 files changed, 88 insertions(+), 42 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index a47375ed..8fcb7183 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -47,9 +47,7 @@ const useOnDragStretch = () => { columnGroups: Object3D[] startColumnGroup: Object3D midColumnGroups: Object3D[] - midStartZ: number - midEndZ: number - endColumnGroup: Object3D + endColumnGroup: ColumnGroup templateVanillaColumnGroup: ColumnGroup vanillaLength: number maxLength: number @@ -70,6 +68,43 @@ const useOnDragStretch = () => { fenceIndex: 0, }) + const addInvisibleVanillaToEnd = () => { + if (!stretchZInitialDataRef.current) return + + const { templateVanillaColumnGroup, layoutGroup, vanillaLength } = + stretchZInitialDataRef.current + const { fences } = stretchZProgressDataRef.current + + const lastColumnGroup = fences[fences.length - 1].columnGroup + + const columnGroup = templateVanillaColumnGroup.clone() + + columnGroup.userData.columnIndex = lastColumnGroup.userData.columnIndex + 1 + + const positionZ = + lastColumnGroup.position.z + + lastColumnGroup.userData.length / 2 + + vanillaLength / 2 + + columnGroup.position.setZ(positionZ) + + setInvisibleNoRaycast(columnGroup) + + layoutGroup.add(columnGroup) + + const fenceZ = positionZ + + fences.push({ + columnGroup, + z: fenceZ, + }) + + if (DEBUG_FENCES) { + addDebugLineAtZ(layoutGroup, fenceZ, undefined, "red") + console.log(`debug line at ${fenceZ}`) + } + } + const onDragStretchZ = { first: ({ handleObject, @@ -122,52 +157,57 @@ const useOnDragStretch = () => { midColumnGroups, endColumnGroup, maxLength: TMP_MAX_LENGTH, - midStartZ: midColumnGroups[0].position.z, - midEndZ: endColumnGroup.position.z, } - const { midStartZ, midEndZ } = stretchZInitialDataRef.current - if (direction === 1) { - let fences: Fence[] = [] - - for (let columnGroup of midColumnGroups) { - const z = columnGroup.position.z + columnGroup.userData.length - fences.push({ - columnGroup, - z, + stretchZProgressDataRef.current.fences = pipe( + midColumnGroups, + A.map((columnGroup) => { + const z = + columnGroup.position.z + columnGroup.userData.length + return { + columnGroup, + z, + } }) + ) + stretchZProgressDataRef.current.fenceIndex = 0 + + for (let i = 0; i < 3; i++) { + addInvisibleVanillaToEnd() } - replicateObject(3, templateVanillaColumnGroup).forEach( - (columnGroup, i) => { - const lastColumnGroup = - fences[fences.length - 1].columnGroup - columnGroup.userData.columnIndex = - lastColumnGroup.userData.columnIndex + i + 1 + // replicateObject(3, templateVanillaColumnGroup).forEach( + // (columnGroup) => { + // const lastColumnGroup = + // fences[fences.length - 1].columnGroup - const positionZ = - midEndZ + i * vanillaLength + vanillaLength / 2 + // columnGroup.userData.columnIndex = + // lastColumnGroup.userData.columnIndex + 1 - columnGroup.position.setZ(positionZ) + // const positionZ = + // lastColumnGroup.position.z + + // lastColumnGroup.userData.length / 2 + + // vanillaLength / 2 - layoutGroup.add(columnGroup) + // columnGroup.position.setZ(positionZ) - const fenceZ = positionZ + // setInvisibleNoRaycast(columnGroup) - fences.push({ - columnGroup, - z: fenceZ, - }) + // layoutGroup.add(columnGroup) - if (DEBUG_FENCES) { - addDebugLineAtZ(layoutGroup, fenceZ, undefined, "red") - } - } - ) + // const fenceZ = positionZ - stretchZProgressDataRef.current.fences = fences - stretchZProgressDataRef.current.fenceIndex = 0 + // fences.push({ + // columnGroup, + // z: fenceZ, + // }) + + // if (DEBUG_FENCES) { + // addDebugLineAtZ(layoutGroup, fenceZ, undefined, "red") + // } + // } + // ) } if (direction === -1) { @@ -193,6 +233,8 @@ const useOnDragStretch = () => { templateVanillaColumnGroup, columnGroups, layoutGroup, + endColumnGroup, + maxLength, } = stretchZInitialDataRef.current const { lastDistance, fences: vanillaFences } = @@ -213,11 +255,16 @@ const useOnDragStretch = () => { if (distance > lastDistance) { if (fenceIndex + 1 < fences.length) { const nextFence = fences[fenceIndex + 1] - if (distance > nextFence.z) { - // setVisibleAndRaycast(nextFence.columnGroup) - // function to add another vanilla column group to the fences - // so long as not max - // stretchZProgressDataRef.current.fenceIndex++ + if (distance >= nextFence.z) { + setVisibleAndRaycast(nextFence.columnGroup) + endColumnGroup.userData.columnIndex++ + stretchZProgressDataRef.current.fenceIndex++ + + addDebugLineAtZ(layoutGroup, distance, 50, "black") + + if (nextFence.z < maxLength) { + addInvisibleVanillaToEnd() + } } } } diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 3c7389d0..90154729 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -141,7 +141,6 @@ export const createModuleGroup = async ({ ifcTag, houseId, }) - material.wireframe = true const mesh = new Mesh(geometry, material) mesh.castShadow = true From 4ef0e6310fa7d8d1e68060be40e24a6c4c1dce43 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 15 Aug 2023 09:41:23 +0100 Subject: [PATCH 100/132] wip additive/subtractive on front/back sides nicely in mid --- app/design/ui-3d/fresh/gestures/stretch.ts | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 8fcb7183..83b3b81e 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -51,6 +51,8 @@ const useOnDragStretch = () => { templateVanillaColumnGroup: ColumnGroup vanillaLength: number maxLength: number + midStartZ: number + midEndZ: number } | null>(null) type Fence = { @@ -98,11 +100,6 @@ const useOnDragStretch = () => { columnGroup, z: fenceZ, }) - - if (DEBUG_FENCES) { - addDebugLineAtZ(layoutGroup, fenceZ, undefined, "red") - console.log(`debug line at ${fenceZ}`) - } } const onDragStretchZ = { @@ -157,6 +154,8 @@ const useOnDragStretch = () => { midColumnGroups, endColumnGroup, maxLength: TMP_MAX_LENGTH, + midStartZ: startColumnGroup.userData.length, + midEndZ: endColumnGroup.position.z, } if (direction === 1) { @@ -235,6 +234,7 @@ const useOnDragStretch = () => { layoutGroup, endColumnGroup, maxLength, + midEndZ, } = stretchZInitialDataRef.current const { lastDistance, fences: vanillaFences } = @@ -252,16 +252,16 @@ const useOnDragStretch = () => { const { fenceIndex, fences } = stretchZProgressDataRef.current if (direction === 1) { + // additive direction to back side if (distance > lastDistance) { if (fenceIndex + 1 < fences.length) { const nextFence = fences[fenceIndex + 1] - if (distance >= nextFence.z) { + const realDistance = midEndZ + distance + if (realDistance >= nextFence.z) { setVisibleAndRaycast(nextFence.columnGroup) endColumnGroup.userData.columnIndex++ stretchZProgressDataRef.current.fenceIndex++ - addDebugLineAtZ(layoutGroup, distance, 50, "black") - if (nextFence.z < maxLength) { addInvisibleVanillaToEnd() } @@ -269,14 +269,16 @@ const useOnDragStretch = () => { } } - // + // subtractive direction to back side if (distance < lastDistance) { - if (fenceIndex >= 0) { + if (fenceIndex > 0) { + const realDistance = midEndZ + distance const lastVisibleFence = fences[fenceIndex] - if (distance < lastVisibleFence.z) { - // setInvisibleNoRaycast(prevFence.columnGroup) - // stretchZProgressDataRef.current.fenceIndex-- + if (realDistance < lastVisibleFence.z) { + setInvisibleNoRaycast(lastVisibleFence.columnGroup) + stretchZProgressDataRef.current.fenceIndex-- + endColumnGroup.userData.columnIndex-- } } } From 9fb23160d61e951673c845b293bc5c05268fbf75 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 15 Aug 2023 12:59:14 +0100 Subject: [PATCH 101/132] wip reconcile stretch z drop --- app/design/ui-3d/fresh/dimensions.ts | 11 +++ app/design/ui-3d/fresh/gestures/stretch.ts | 76 ++++++------------- .../ui-3d/fresh/helpers/sceneQueries.ts | 19 ++++- 3 files changed, 50 insertions(+), 56 deletions(-) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 138ef334..bc26a50b 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -17,6 +17,7 @@ import { columnSorter } from "./helpers/layouts" import { getActiveHouseUserData, getActiveLayoutGroup, + getLayoutGroupColumnGroups, } from "./helpers/sceneQueries" import { HouseLayoutGroup, @@ -186,4 +187,14 @@ export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { ) updateHouseOBB(layoutGroup.parent as HouseTransformsGroup) + + pipe( + layoutGroup, + getLayoutGroupColumnGroups, + A.filter((columnGroup) => !columnGroup.visible) + ).forEach((columnGroup) => { + columnGroup.removeFromParent() + }) + + console.log(pipe(layoutGroup, getLayoutGroupColumnGroups)) } diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 83b3b81e..ca7cf899 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,25 +1,21 @@ +import { takeRight } from "fp-ts/lib/Array" import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" import { A, O, T } from "../../../../utils/functions" import { addDebugLineAtZ, - replicateObject, setInvisibleNoRaycast, setVisibleAndRaycast, } from "../../../../utils/three" import pointer from "../../../state/pointer" import { recomputeLayoutGroup } from "../dimensions" -import { - columnSorter, - createColumnGroup, - splitColumnGroups, -} from "../helpers/layouts" +import { createColumnGroup, splitColumnGroups } from "../helpers/layouts" import { getActiveHouseUserData, getActiveLayoutGroup, getHouseTransformsGroupUp, - getLayoutGroupColumnGroups, + getSortedVisibleColumnGroups, handleColumnGroupParentQuery, } from "../helpers/sceneQueries" import { @@ -27,7 +23,6 @@ import { HouseLayoutGroup, HouseTransformsGroup, incrementColumnCount, - isElementMesh, StretchHandleMesh, StretchHandleMeshUserData, } from "../userData" @@ -83,22 +78,18 @@ const useOnDragStretch = () => { columnGroup.userData.columnIndex = lastColumnGroup.userData.columnIndex + 1 - const positionZ = + columnGroup.position.z = lastColumnGroup.position.z + lastColumnGroup.userData.length / 2 + - vanillaLength / 2 - - columnGroup.position.setZ(positionZ) + columnGroup.userData.length / 2 setInvisibleNoRaycast(columnGroup) layoutGroup.add(columnGroup) - const fenceZ = positionZ - fences.push({ columnGroup, - z: fenceZ, + z: columnGroup.position.z, }) } @@ -118,11 +109,7 @@ const useOnDragStretch = () => { getActiveHouseUserData(houseTransformsGroup) const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) - const columnGroups = pipe( - layoutGroup, - getLayoutGroupColumnGroups, - columnSorter - ) + const columnGroups = pipe(layoutGroup, getSortedVisibleColumnGroups) const task = pipe( T.of(vanillaColumn), @@ -175,38 +162,6 @@ const useOnDragStretch = () => { for (let i = 0; i < 3; i++) { addInvisibleVanillaToEnd() } - - // replicateObject(3, templateVanillaColumnGroup).forEach( - // (columnGroup) => { - // const lastColumnGroup = - // fences[fences.length - 1].columnGroup - - // columnGroup.userData.columnIndex = - // lastColumnGroup.userData.columnIndex + 1 - - // const positionZ = - // lastColumnGroup.position.z + - // lastColumnGroup.userData.length / 2 + - // vanillaLength / 2 - - // columnGroup.position.setZ(positionZ) - - // setInvisibleNoRaycast(columnGroup) - - // layoutGroup.add(columnGroup) - - // const fenceZ = positionZ - - // fences.push({ - // columnGroup, - // z: fenceZ, - // }) - - // if (DEBUG_FENCES) { - // addDebugLineAtZ(layoutGroup, fenceZ, undefined, "red") - // } - // } - // ) } if (direction === -1) { @@ -363,6 +318,23 @@ const useOnDragStretch = () => { } }, last: () => { + if (!stretchZInitialDataRef.current) return + const { layoutGroup, endColumnGroup } = stretchZInitialDataRef.current + + pipe( + layoutGroup, + getSortedVisibleColumnGroups, + takeRight(2), + ([penultimateColumnGroup]) => { + endColumnGroup.position.setZ( + penultimateColumnGroup.position.z + + penultimateColumnGroup.userData.length / 2 + ) + } + ) + + recomputeLayoutGroup(layoutGroup) + stretchZInitialDataRef.current = null stretchZProgressDataRef.current = { lastDistance: 0, diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 26fbf6ce..a7b5f45b 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -1,4 +1,4 @@ -import { pipe } from "fp-ts/lib/function" +import { flow, pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { Group, Intersection, Material, Mesh, Object3D, Plane } from "three" import { A, O, someOrError } from "../../../../utils/functions" @@ -8,9 +8,11 @@ import { HouseLayoutGroupUserData, HouseTransformsGroup, HouseTransformsGroupUserData, + isColumnGroup, isHouseTransformsGroup, UserDataTypeEnum, } from "../userData" +import { columnSorter } from "./layouts" export const findFirstGuardUp = (guard: (o: Object3D) => o is T) => @@ -194,10 +196,19 @@ export const getPartitionedLayoutGroups = (houseTransformsGroup: Group) => export const getLayoutGroupColumnGroups = ( layoutGroup: HouseLayoutGroup +): ColumnGroup[] => pipe(layoutGroup.children, A.filter(isColumnGroup)) + +export const getSortedVisibleColumnGroups = ( + layoutGroup: HouseLayoutGroup ): ColumnGroup[] => - layoutGroup.children.filter( - (x): x is ColumnGroup => - x.userData.type === UserDataTypeEnum.Enum.ColumnGroup + pipe( + layoutGroup.children, + A.filter((x): x is ColumnGroup => { + if (!isColumnGroup(x)) return false + if (x.visible === false) return false + return true + }), + columnSorter ) export const getLayoutGroupBySectionType = ( From dffa5bfc0ef2797c6aed3b188627af502171386e Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 15 Aug 2023 16:36:07 +0100 Subject: [PATCH 102/132] wip needs clamp --- app/design/ui-3d/fresh/gestures/index.ts | 5 +---- app/design/ui-3d/fresh/gestures/stretch.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts index 28dd8e4c..3ad6798a 100644 --- a/app/design/ui-3d/fresh/gestures/index.ts +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -10,7 +10,6 @@ import scope, { ScopeItem } from "../../../state/scope" import siteCtx, { downMode, getModeBools, - SiteCtxMode, SiteCtxModeEnum, } from "../../../state/siteCtx" import { dispatchOutline } from "../events/outlines" @@ -26,7 +25,6 @@ import { isElementMesh, isRotateHandleMesh, isStretchHandleMesh, - StretchHandleMeshUserData, UserDataTypeEnum, } from "../userData" import { dispatchPointerDown, dispatchPointerUp } from "./events" @@ -68,7 +66,6 @@ const useGestures = () => { // stretch if (buildingOrLevelMode && isStretchHandleMesh(object)) { const { - userData, userData: { axis }, } = object @@ -104,7 +101,7 @@ const useGestures = () => { invalidate() }, onHover: ({ event, event: { intersections }, hovering }) => { - event.stopPropagation() + if (firstDragEventRef.current) return if (intersections.length === 0) { document.body.style.cursor = "" diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index ca7cf899..3a1a6c7b 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -157,7 +157,8 @@ const useOnDragStretch = () => { } }) ) - stretchZProgressDataRef.current.fenceIndex = 0 + stretchZProgressDataRef.current.fenceIndex = + stretchZProgressDataRef.current.fences.length - 1 for (let i = 0; i < 3; i++) { addInvisibleVanillaToEnd() @@ -230,7 +231,10 @@ const useOnDragStretch = () => { const realDistance = midEndZ + distance const lastVisibleFence = fences[fenceIndex] - if (realDistance < lastVisibleFence.z) { + if ( + realDistance < + lastVisibleFence.z - lastVisibleFence.columnGroup.userData.length + ) { setInvisibleNoRaycast(lastVisibleFence.columnGroup) stretchZProgressDataRef.current.fenceIndex-- endColumnGroup.userData.columnIndex-- From b9caef9d9c3375d486b60456883a01fb85442bfb Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 16 Aug 2023 21:14:39 +0100 Subject: [PATCH 103/132] wip van col len bug solved --- app/design/ui-3d/fresh/dimensions.ts | 4 - app/design/ui-3d/fresh/gestures/stretch.ts | 181 +++++++++------------ app/design/ui-3d/fresh/helpers/layouts.ts | 1 + app/workers/layouts/worker.ts | 12 +- 4 files changed, 86 insertions(+), 112 deletions(-) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index bc26a50b..0b62a223 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -175,8 +175,6 @@ export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { .filter((x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup) .reduce((acc, v) => acc + v.userData.length, 0) - console.log({ oldLength, length }) - layoutGroup.position.setZ(-length / 2) layoutGroup.userData.length = length layoutGroup.parent?.position.add( @@ -195,6 +193,4 @@ export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { ).forEach((columnGroup) => { columnGroup.removeFromParent() }) - - console.log(pipe(layoutGroup, getLayoutGroupColumnGroups)) } diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 3a1a6c7b..40e6a94f 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -2,14 +2,14 @@ import { takeRight } from "fp-ts/lib/Array" import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" -import { A, O, T } from "../../../../utils/functions" +import { A, T } from "../../../../utils/functions" import { - addDebugLineAtZ, setInvisibleNoRaycast, setVisibleAndRaycast, } from "../../../../utils/three" import pointer from "../../../state/pointer" import { recomputeLayoutGroup } from "../dimensions" +import { dispatchOutline } from "../events/outlines" import { createColumnGroup, splitColumnGroups } from "../helpers/layouts" import { getActiveHouseUserData, @@ -22,15 +22,12 @@ import { ColumnGroup, HouseLayoutGroup, HouseTransformsGroup, - incrementColumnCount, StretchHandleMesh, StretchHandleMeshUserData, } from "../userData" const TMP_MAX_LENGTH = 10 -const DEBUG_FENCES = true - const useOnDragStretch = () => { const stretchZInitialDataRef = useRef<{ direction: number @@ -65,23 +62,28 @@ const useOnDragStretch = () => { fenceIndex: 0, }) - const addInvisibleVanillaToEnd = () => { + const addVanilla = (side: 1 | -1) => { if (!stretchZInitialDataRef.current) return - const { templateVanillaColumnGroup, layoutGroup, vanillaLength } = + const { templateVanillaColumnGroup, layoutGroup } = stretchZInitialDataRef.current + const { fences } = stretchZProgressDataRef.current const lastColumnGroup = fences[fences.length - 1].columnGroup - const columnGroup = templateVanillaColumnGroup.clone() - columnGroup.userData.columnIndex = lastColumnGroup.userData.columnIndex + 1 + columnGroup.userData.columnIndex = + lastColumnGroup.userData.columnIndex + 1 * side - columnGroup.position.z = - lastColumnGroup.position.z + - lastColumnGroup.userData.length / 2 + - columnGroup.userData.length / 2 + let z = 0 + if (side === 1) { + z = lastColumnGroup.position.z + lastColumnGroup.userData.length + } else if (side === -1) { + z = lastColumnGroup.position.z - columnGroup.userData.length + } + + columnGroup.position.setZ(z) setInvisibleNoRaycast(columnGroup) @@ -89,7 +91,7 @@ const useOnDragStretch = () => { fences.push({ columnGroup, - z: columnGroup.position.z, + z: z + columnGroup.userData.length / 2, }) } @@ -101,6 +103,11 @@ const useOnDragStretch = () => { handleObject: StretchHandleMesh point: Vector3 }) => { + dispatchOutline({ + hoveredObjects: [], + selectedObjects: [], + }) + const handleGroup = handleColumnGroupParentQuery(handleObject) const houseTransformsGroup = getHouseTransformsGroupUp(handleGroup) @@ -149,11 +156,11 @@ const useOnDragStretch = () => { stretchZProgressDataRef.current.fences = pipe( midColumnGroups, A.map((columnGroup) => { - const z = - columnGroup.position.z + columnGroup.userData.length return { columnGroup, - z, + z: + columnGroup.position.z + + columnGroup.userData.length / 2, } }) ) @@ -161,12 +168,23 @@ const useOnDragStretch = () => { stretchZProgressDataRef.current.fences.length - 1 for (let i = 0; i < 3; i++) { - addInvisibleVanillaToEnd() + addVanilla(direction) } } if (direction === -1) { - // complete me + stretchZProgressDataRef.current.fences = pipe( + midColumnGroups, + A.map((columnGroup) => { + const z = columnGroup.position.z + return { + columnGroup, + z, + } + }) + ) + stretchZProgressDataRef.current.fenceIndex = + stretchZProgressDataRef.current.fences.length - 1 } }) ) @@ -191,6 +209,7 @@ const useOnDragStretch = () => { endColumnGroup, maxLength, midEndZ, + midStartZ, } = stretchZInitialDataRef.current const { lastDistance, fences: vanillaFences } = @@ -202,12 +221,15 @@ const useOnDragStretch = () => { new Vector3(0, 1, 0), -houseTransformsGroup.rotation.y ) - const distance = distanceVector.z * direction - handleGroup.position.set(0, 0, handleGroupZ0 + distance * direction) + const distance = distanceVector.z + + handleGroup.position.set(0, 0, handleGroupZ0 + distance) const { fenceIndex, fences } = stretchZProgressDataRef.current if (direction === 1) { + // const cl = clamp(lo, hi) + // additive direction to back side if (distance > lastDistance) { if (fenceIndex + 1 < fences.length) { @@ -219,7 +241,7 @@ const useOnDragStretch = () => { stretchZProgressDataRef.current.fenceIndex++ if (nextFence.z < maxLength) { - addInvisibleVanillaToEnd() + addVanilla(direction) } } } @@ -231,10 +253,7 @@ const useOnDragStretch = () => { const realDistance = midEndZ + distance const lastVisibleFence = fences[fenceIndex] - if ( - realDistance < - lastVisibleFence.z - lastVisibleFence.columnGroup.userData.length - ) { + if (realDistance < lastVisibleFence.z) { setInvisibleNoRaycast(lastVisibleFence.columnGroup) stretchZProgressDataRef.current.fenceIndex-- endColumnGroup.userData.columnIndex-- @@ -243,83 +262,40 @@ const useOnDragStretch = () => { } } - stretchZProgressDataRef.current.lastDistance = distance - - return - - // additive - if (distance > lastDistance) { - // prob check if we need to add some hidden ones - if (undefined as any) { - } - - // gate check - if (distance > vanillaLength * vanillaFences.length) { - const newColumnGroup = templateVanillaColumnGroup.clone() - - if (direction === 1) { - const penultimateColumnGroup = columnGroups[columnGroups.length - 2] - const endColumnGroup = columnGroups[columnGroups.length - 1] - - newColumnGroup.position.setZ( - penultimateColumnGroup.position.z + - penultimateColumnGroup.userData.length / 2 + - (vanillaFences.length + 0.5) * vanillaLength - ) - - layoutGroup.add(newColumnGroup) - - newColumnGroup.userData.columnIndex = - penultimateColumnGroup.userData.columnIndex + 1 - - endColumnGroup.userData.columnIndex++ - - incrementColumnCount(layoutGroup) - - stretchZProgressDataRef.current.fences.push({ - columnGroup: newColumnGroup, - z: distance, - }) - - recomputeLayoutGroup(layoutGroup) - } - - if (direction === -1) { - // newColumnGroup.position.setZ( - // secondColumnGroup.position.z + - // penultimateColumnGroup.userData.length / 2 + - // (vanillaColumnGroups.length + 0.5) * vanillaLength - // ) - // layoutGroup.add(newColumnGroup) - } - } + if (direction === -1) { + // const cl = clamp(lo, hi) + // additive direction to back side + // if (distance > lastDistance) { + // if (fenceIndex + 1 < fences.length) { + // const nextFence = fences[fenceIndex + 1] + // const realDistance = midStartZ - distance + // if (realDistance <= nextFence.z) { + // setVisibleAndRaycast(nextFence.columnGroup) + // // up all column indices ahead + // // endColumnGroup.userData.columnIndex++ + // stretchZProgressDataRef.current.fenceIndex++ + // // naive + // if (nextFence.z < -maxLength) { + // addVanilla(direction) + // } + // } + // } + // } + // subtractive direction to back side + // if (distance < lastDistance) { + // if (fenceIndex > 0) { + // const realDistance = midEndZ + distance + // const lastVisibleFence = fences[fenceIndex] + // if (realDistance < lastVisibleFence.z) { + // setInvisibleNoRaycast(lastVisibleFence.columnGroup) + // stretchZProgressDataRef.current.fenceIndex-- + // endColumnGroup.userData.columnIndex-- + // } + // } + // } } - // subtractive - if (distance < lastDistance) { - // gate check - if (distance > 0 && vanillaFences.length > 0) { - pipe( - vanillaFences, - A.last, - O.map(({ z, columnGroup }) => { - if (distance < z) { - columnGroup.removeFromParent() - vanillaFences.pop() - } - }) - ) - // if (distance < stretchZProgressDataRef.current.vanillaFences) { - // // we want to be tracking vanilla columns first - // // otherwise - // } - } - - if (distance < 0) { - // column group fence stuff here - // we would want to track an index and vis vs. no vis - } - } + stretchZProgressDataRef.current.lastDistance = distance }, last: () => { if (!stretchZInitialDataRef.current) return @@ -332,7 +308,8 @@ const useOnDragStretch = () => { ([penultimateColumnGroup]) => { endColumnGroup.position.setZ( penultimateColumnGroup.position.z + - penultimateColumnGroup.userData.length / 2 + penultimateColumnGroup.userData.length + // / 2 + endColumnGroup.userData.length / 2 ) } ) diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 90154729..6049c685 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -141,6 +141,7 @@ export const createModuleGroup = async ({ ifcTag, houseId, }) + material.wireframe = false const mesh = new Mesh(geometry, material) mesh.castShadow = true diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 8a3c7f4e..8a31731c 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -317,23 +317,22 @@ const getLayout = async ({ ) ) const layout = modulesToColumnLayout(modules) + layoutsDB.houseLayouts.put({ layout, systemId, dnas, }) - const { - startColumn: { gridGroups }, - } = splitColumns(layout) - const getVanillaModule = createVanillaModuleGetter(modulesCache)({ constrainGridType: false, positionType: "MID", }) + const { startColumn } = splitColumns(layout) + pipe( - gridGroups, + startColumn.gridGroups, A.traverse(O.Applicative)( ({ levelIndex, @@ -349,7 +348,8 @@ const getLayout = async ({ { module: vanillaModule, gridGroupIndex: 0, - z: 0, + // TODO: document me (quirk) + z: vanillaModule.length / 2, }, ], length: vanillaModule.length, From 1538fae70a91653ea1060ba37cfb731b60c4a2f5 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 17 Aug 2023 06:54:29 +0100 Subject: [PATCH 104/132] wip rounded handles --- app/design/ui-3d/fresh/helpers/handles.ts | 5 +- app/design/ui-3d/fresh/shapes/roundedBox.ts | 68 +++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 app/design/ui-3d/fresh/shapes/roundedBox.ts diff --git a/app/design/ui-3d/fresh/helpers/handles.ts b/app/design/ui-3d/fresh/helpers/handles.ts index be0208e4..4c48c97f 100644 --- a/app/design/ui-3d/fresh/helpers/handles.ts +++ b/app/design/ui-3d/fresh/helpers/handles.ts @@ -62,8 +62,11 @@ export const createStretchHandle = ({ const geometry = new RoundedBoxGeometry( ((axis === "z" ? width : length) * 2) / 1.5, 1, - 1 + 1, + undefined, + 0.5 ) + const material = handleMaterial const mesh = new Mesh(geometry, material) diff --git a/app/design/ui-3d/fresh/shapes/roundedBox.ts b/app/design/ui-3d/fresh/shapes/roundedBox.ts new file mode 100644 index 00000000..82e41ece --- /dev/null +++ b/app/design/ui-3d/fresh/shapes/roundedBox.ts @@ -0,0 +1,68 @@ +import { + Mesh, + Shape, + ExtrudeGeometry, + MeshStandardMaterial, + Material, +} from "three" +import { toCreasedNormals } from "three-stdlib" + +const eps = 0.00001 + +const createShape = (width: number, height: number, radius0: number): Shape => { + const shape = new Shape() + const radius = radius0 - eps + shape.absarc(eps, eps, eps, -Math.PI / 2, -Math.PI, true) + shape.absarc(eps, height - radius * 2, eps, Math.PI, Math.PI / 2, true) + shape.absarc( + width - radius * 2, + height - radius * 2, + eps, + Math.PI / 2, + 0, + true + ) + shape.absarc(width - radius * 2, eps, eps, 0, -Math.PI / 2, true) + return shape +} + +interface RoundedBoxMeshOptions { + width?: number + height?: number + depth?: number + radius?: number + steps?: number + smoothness?: number + creaseAngle?: number + material?: Material +} + +const createRoundedBoxMesh = ({ + width = 1, + height = 1, + depth = 1, + radius = 0.05, + steps = 1, + smoothness = 4, + creaseAngle = 0.4, + material = new MeshStandardMaterial({ color: 0xffffff }), +}: RoundedBoxMeshOptions = {}): Mesh => { + const shape = createShape(width, height, radius) + const params = { + depth: depth - radius * 2, + bevelEnabled: true, + bevelSegments: smoothness * 2, + steps, + bevelSize: radius - eps, + bevelThickness: radius, + curveSegments: smoothness, + } + + const geometry = new ExtrudeGeometry(shape, params) + geometry.center() + toCreasedNormals(geometry, creaseAngle) + + return new Mesh(geometry, material) +} + +export default createRoundedBoxMesh From 816c17c94b69c019d7e3f678181a653fb5534145 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 17 Aug 2023 07:37:34 +0100 Subject: [PATCH 105/132] wip pre vanilla indexing bug --- app/design/ui-3d/fresh/gestures/stretch.ts | 129 ++++++++++++------ .../ui-3d/fresh/helpers/sceneQueries.ts | 5 + 2 files changed, 94 insertions(+), 40 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 40e6a94f..929e3bf1 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,8 +1,7 @@ -import { takeRight } from "fp-ts/lib/Array" import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" -import { A, T } from "../../../../utils/functions" +import { A, pipeLog, T } from "../../../../utils/functions" import { setInvisibleNoRaycast, setVisibleAndRaycast, @@ -15,6 +14,8 @@ import { getActiveHouseUserData, getActiveLayoutGroup, getHouseTransformsGroupUp, + getLayoutGroupColumnGroups, + getLayoutGroupColumnIndices, getSortedVisibleColumnGroups, handleColumnGroupParentQuery, } from "../helpers/sceneQueries" @@ -73,9 +74,6 @@ const useOnDragStretch = () => { const lastColumnGroup = fences[fences.length - 1].columnGroup const columnGroup = templateVanillaColumnGroup.clone() - columnGroup.userData.columnIndex = - lastColumnGroup.userData.columnIndex + 1 * side - let z = 0 if (side === 1) { z = lastColumnGroup.position.z + lastColumnGroup.userData.length @@ -170,11 +168,15 @@ const useOnDragStretch = () => { for (let i = 0; i < 3; i++) { addVanilla(direction) } + + console.log(`fences 1`) + console.log(stretchZProgressDataRef.current.fences) } if (direction === -1) { stretchZProgressDataRef.current.fences = pipe( midColumnGroups, + A.reverse, A.map((columnGroup) => { const z = columnGroup.position.z return { @@ -185,6 +187,10 @@ const useOnDragStretch = () => { ) stretchZProgressDataRef.current.fenceIndex = stretchZProgressDataRef.current.fences.length - 1 + + for (let i = 0; i < 3; i++) { + addVanilla(direction) + } } }) ) @@ -227,6 +233,7 @@ const useOnDragStretch = () => { const { fenceIndex, fences } = stretchZProgressDataRef.current + // back side if (direction === 1) { // const cl = clamp(lo, hi) @@ -238,6 +245,8 @@ const useOnDragStretch = () => { if (realDistance >= nextFence.z) { setVisibleAndRaycast(nextFence.columnGroup) endColumnGroup.userData.columnIndex++ + nextFence.columnGroup.userData.columnIndex = + endColumnGroup.userData.columnIndex - 1 stretchZProgressDataRef.current.fenceIndex++ if (nextFence.z < maxLength) { @@ -256,60 +265,100 @@ const useOnDragStretch = () => { if (realDistance < lastVisibleFence.z) { setInvisibleNoRaycast(lastVisibleFence.columnGroup) stretchZProgressDataRef.current.fenceIndex-- + lastVisibleFence.columnGroup.userData.columnIndex = -1 endColumnGroup.userData.columnIndex-- } } } } + // front side if (direction === -1) { // const cl = clamp(lo, hi) - // additive direction to back side - // if (distance > lastDistance) { - // if (fenceIndex + 1 < fences.length) { - // const nextFence = fences[fenceIndex + 1] - // const realDistance = midStartZ - distance - // if (realDistance <= nextFence.z) { - // setVisibleAndRaycast(nextFence.columnGroup) - // // up all column indices ahead - // // endColumnGroup.userData.columnIndex++ - // stretchZProgressDataRef.current.fenceIndex++ - // // naive - // if (nextFence.z < -maxLength) { - // addVanilla(direction) - // } - // } - // } - // } - // subtractive direction to back side - // if (distance < lastDistance) { - // if (fenceIndex > 0) { - // const realDistance = midEndZ + distance - // const lastVisibleFence = fences[fenceIndex] - // if (realDistance < lastVisibleFence.z) { - // setInvisibleNoRaycast(lastVisibleFence.columnGroup) - // stretchZProgressDataRef.current.fenceIndex-- - // endColumnGroup.userData.columnIndex-- - // } - // } - // } + // additive direction to front side + if (distance < lastDistance) { + if (fenceIndex + 1 < fences.length) { + const nextFence = fences[fenceIndex + 1] + const realDistance = midStartZ + distance + if (realDistance <= nextFence.z) { + setVisibleAndRaycast(nextFence.columnGroup) + + nextFence.columnGroup.userData.columnIndex = 1 + + pipe( + layoutGroup, + getSortedVisibleColumnGroups, + A.dropLeft(2) + ).forEach((columnGroup) => { + columnGroup.userData.columnIndex++ + }) + stretchZProgressDataRef.current.fenceIndex++ + + // naive + if (nextFence.z < maxLength) { + addVanilla(direction) + } + } + } + } + // subtractive direction to front side + if (distance > lastDistance) { + if (fenceIndex > 0) { + const realDistance = midStartZ + distance + const lastVisibleFence = fences[fenceIndex] + + if (realDistance > lastVisibleFence.z) { + setInvisibleNoRaycast(lastVisibleFence.columnGroup) + lastVisibleFence.columnGroup.userData.columnIndex = -1 + + pipe( + layoutGroup, + getSortedVisibleColumnGroups, + A.dropLeft(2) + ).forEach((columnGroup) => { + columnGroup.userData.columnIndex-- + }) + stretchZProgressDataRef.current.fenceIndex-- + + pipe( + layoutGroup, + getSortedVisibleColumnGroups, + A.map((x) => x.userData.columnIndex), + pipeLog + ) + } + } + } } stretchZProgressDataRef.current.lastDistance = distance }, last: () => { if (!stretchZInitialDataRef.current) return - const { layoutGroup, endColumnGroup } = stretchZInitialDataRef.current - pipe( + const { layoutGroup } = stretchZInitialDataRef.current + + const sortedVisibleColumnGroups = pipe( layoutGroup, - getSortedVisibleColumnGroups, - takeRight(2), - ([penultimateColumnGroup]) => { + getSortedVisibleColumnGroups + ) + + pipe( + sortedVisibleColumnGroups, + A.takeRight(2), + ([penultimateColumnGroup, endColumnGroup]) => { endColumnGroup.position.setZ( penultimateColumnGroup.position.z + penultimateColumnGroup.userData.length - // / 2 + endColumnGroup.userData.length / 2 + ) + } + ) + pipe( + sortedVisibleColumnGroups, + A.takeLeft(2), + ([startColumnGroup, secondColumnGroup]) => { + startColumnGroup.position.setZ( + secondColumnGroup.position.z - startColumnGroup.userData.length ) } ) diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index a7b5f45b..bf8df6eb 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -211,6 +211,11 @@ export const getSortedVisibleColumnGroups = ( columnSorter ) +export const getLayoutGroupColumnIndices = flow( + getSortedVisibleColumnGroups, + A.map((x) => x.userData.columnIndex) +) + export const getLayoutGroupBySectionType = ( houseTransformsGroup: Group, sectionType: string From e206b58f0cb0fb0b42f606bdd66c99bf1f961c19 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 17 Aug 2023 07:54:52 +0100 Subject: [PATCH 106/132] wip pre front mid fences --- app/design/ui-3d/fresh/dimensions.ts | 19 +++++++------ app/design/ui-3d/fresh/gestures/stretch.ts | 28 ++++++------------- app/design/ui-3d/fresh/gestures/stretchZ.ts | 3 +- app/design/ui-3d/fresh/helpers/layouts.ts | 7 ----- .../ui-3d/fresh/helpers/sceneQueries.ts | 17 +++++++++-- app/design/ui-3d/fresh/helpers/stretchZ.ts | 3 +- 6 files changed, 38 insertions(+), 39 deletions(-) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 0b62a223..f34f30ed 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -13,16 +13,19 @@ import { import { OBB } from "three-stdlib" import userDB from "../../../db/user" import { A, O } from "../../../utils/functions" -import { columnSorter } from "./helpers/layouts" import { + columnSorter, + findFirstGuardDown, getActiveHouseUserData, getActiveLayoutGroup, getLayoutGroupColumnGroups, + getSortedVisibleColumnGroups, } from "./helpers/sceneQueries" import { HouseLayoutGroup, HouseTransformsGroup, HouseTransformsGroupUserData, + isHouseLayoutGroup, ModuleGroupUserData, UserDataTypeEnum, } from "./userData" @@ -98,15 +101,15 @@ export const updateHouseLength = (houseGroup: Group) => { ) } -const updateDnas = (houseGroup: Group) => { +const updateDnas = (houseTransformsGroup: HouseTransformsGroup) => { let result: string[][] = [] pipe( - houseGroup.children, - A.lookup(0), - O.map((columnsContainerGroup) => + houseTransformsGroup, + findFirstGuardDown(isHouseLayoutGroup), + O.map((layoutGroup) => pipe( - columnsContainerGroup.children, - columnSorter, + layoutGroup, + getSortedVisibleColumnGroups, A.map((v) => { v.traverse((node) => { if (node.userData.type === UserDataTypeEnum.Enum.ModuleGroup) { @@ -127,7 +130,7 @@ const updateDnas = (houseGroup: Group) => { ) ) ) - houseGroup.userData.dnas = result.flat() + houseTransformsGroup.userData.dnas = result.flat() } export const updateIndexedHouseTransforms = ( diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 929e3bf1..0b881665 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,7 +1,7 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" -import { A, pipeLog, T } from "../../../../utils/functions" +import { A, T } from "../../../../utils/functions" import { setInvisibleNoRaycast, setVisibleAndRaycast, @@ -14,9 +14,8 @@ import { getActiveHouseUserData, getActiveLayoutGroup, getHouseTransformsGroupUp, - getLayoutGroupColumnGroups, - getLayoutGroupColumnIndices, getSortedVisibleColumnGroups, + getVisibleColumnGroups, handleColumnGroupParentQuery, } from "../helpers/sceneQueries" import { @@ -168,9 +167,6 @@ const useOnDragStretch = () => { for (let i = 0; i < 3; i++) { addVanilla(direction) } - - console.log(`fences 1`) - console.log(stretchZProgressDataRef.current.fences) } if (direction === -1) { @@ -283,15 +279,16 @@ const useOnDragStretch = () => { if (realDistance <= nextFence.z) { setVisibleAndRaycast(nextFence.columnGroup) - nextFence.columnGroup.userData.columnIndex = 1 - pipe( layoutGroup, - getSortedVisibleColumnGroups, - A.dropLeft(2) + getVisibleColumnGroups, + A.filter((x) => x.userData.columnIndex >= 1) ).forEach((columnGroup) => { columnGroup.userData.columnIndex++ }) + + nextFence.columnGroup.userData.columnIndex = 1 + stretchZProgressDataRef.current.fenceIndex++ // naive @@ -313,19 +310,12 @@ const useOnDragStretch = () => { pipe( layoutGroup, - getSortedVisibleColumnGroups, - A.dropLeft(2) + getVisibleColumnGroups, + A.filter((x) => x.userData.columnIndex > 1) ).forEach((columnGroup) => { columnGroup.userData.columnIndex-- }) stretchZProgressDataRef.current.fenceIndex-- - - pipe( - layoutGroup, - getSortedVisibleColumnGroups, - A.map((x) => x.userData.columnIndex), - pipeLog - ) } } } diff --git a/app/design/ui-3d/fresh/gestures/stretchZ.ts b/app/design/ui-3d/fresh/gestures/stretchZ.ts index bee45e1b..8a8356ff 100644 --- a/app/design/ui-3d/fresh/gestures/stretchZ.ts +++ b/app/design/ui-3d/fresh/gestures/stretchZ.ts @@ -9,8 +9,9 @@ import { setCameraControlsEnabled } from "../../../state/camera" import pointer from "../../../state/pointer" import { recomputeLayoutGroup, updateHouseOBB } from "../dimensions" import { dispatchOutline } from "../events/outlines" -import { columnSorter, createColumnGroup } from "../helpers/layouts" +import { createColumnGroup } from "../helpers/layouts" import { + columnSorter, getActiveHouseUserData, getActiveLayoutGroup, getHouseTransformsGroupUp, diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 6049c685..0e04cab0 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -544,13 +544,6 @@ export const createInitialHouse = ({ }) ) -export const columnSorter = A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) -) - export const splitColumnGroups = (columnGroups: ColumnGroup[]) => pipe( columnGroups, diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index bf8df6eb..770a8dde 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -1,7 +1,7 @@ import { flow, pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { Group, Intersection, Material, Mesh, Object3D, Plane } from "three" -import { A, O, someOrError } from "../../../../utils/functions" +import { A, Num, O, Ord, someOrError } from "../../../../utils/functions" import { ColumnGroup, HouseLayoutGroup, @@ -12,7 +12,13 @@ import { isHouseTransformsGroup, UserDataTypeEnum, } from "../userData" -import { columnSorter } from "./layouts" + +export const columnSorter = A.sort( + pipe( + Num.Ord, + Ord.contramap((x: ColumnGroup) => x.userData.columnIndex) + ) +) export const findFirstGuardUp = (guard: (o: Object3D) => o is T) => @@ -198,7 +204,7 @@ export const getLayoutGroupColumnGroups = ( layoutGroup: HouseLayoutGroup ): ColumnGroup[] => pipe(layoutGroup.children, A.filter(isColumnGroup)) -export const getSortedVisibleColumnGroups = ( +export const getVisibleColumnGroups = ( layoutGroup: HouseLayoutGroup ): ColumnGroup[] => pipe( @@ -211,6 +217,11 @@ export const getSortedVisibleColumnGroups = ( columnSorter ) +export const getSortedVisibleColumnGroups = flow( + getVisibleColumnGroups, + columnSorter +) + export const getLayoutGroupColumnIndices = flow( getSortedVisibleColumnGroups, A.map((x) => x.userData.columnIndex) diff --git a/app/design/ui-3d/fresh/helpers/stretchZ.ts b/app/design/ui-3d/fresh/helpers/stretchZ.ts index 6940d5f4..10572ed6 100644 --- a/app/design/ui-3d/fresh/helpers/stretchZ.ts +++ b/app/design/ui-3d/fresh/helpers/stretchZ.ts @@ -6,8 +6,9 @@ import { HouseTransformsGroup, incrementColumnCount, } from "../userData" -import { columnSorter, createColumnGroup, getVanillaColumn } from "./layouts" +import { createColumnGroup, getVanillaColumn } from "./layouts" import { + columnSorter, getActiveHouseUserData, getActiveLayoutGroup, getLayoutGroupColumnGroups, From 3a1cb8fa8d1ebec05b2a715a4bbe8596495da2e3 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 17 Aug 2023 09:34:55 +0100 Subject: [PATCH 107/132] wip yippy --- app/design/ui-3d/fresh/dimensions.ts | 3 +- app/design/ui-3d/fresh/gestures/stretch.ts | 92 +++++++++++++++++----- 2 files changed, 74 insertions(+), 21 deletions(-) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index f34f30ed..4a2d2005 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -13,6 +13,7 @@ import { import { OBB } from "three-stdlib" import userDB from "../../../db/user" import { A, O } from "../../../utils/functions" +import { yAxis } from "../../../utils/three" import { columnSorter, findFirstGuardDown, @@ -182,7 +183,7 @@ export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { layoutGroup.userData.length = length layoutGroup.parent?.position.add( new Vector3(0, 0, (length - oldLength) / 2).applyAxisAngle( - new Vector3(0, 1, 0), + yAxis, layoutGroup.parent.rotation.y ) ) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 0b881665..605e5650 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,10 +1,11 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" -import { A, T } from "../../../../utils/functions" +import { A, pipeLog, T } from "../../../../utils/functions" import { setInvisibleNoRaycast, setVisibleAndRaycast, + yAxis, } from "../../../../utils/three" import pointer from "../../../state/pointer" import { recomputeLayoutGroup } from "../dimensions" @@ -28,6 +29,13 @@ import { const TMP_MAX_LENGTH = 10 +const logColumnGroupZs = (columnGroups: ColumnGroup[]) => + pipe( + columnGroups, + A.map((x) => x.position.z), + pipeLog + ) + const useOnDragStretch = () => { const stretchZInitialDataRef = useRef<{ direction: number @@ -167,6 +175,15 @@ const useOnDragStretch = () => { for (let i = 0; i < 3; i++) { addVanilla(direction) } + console.log(`fences 1`) + console.log( + stretchZProgressDataRef.current.fences.map( + ({ columnGroup, z }) => [ + columnGroup.userData.columnIndex, + z, + ] + ) + ) } if (direction === -1) { @@ -188,6 +205,16 @@ const useOnDragStretch = () => { addVanilla(direction) } } + + const { fenceIndex, fences } = stretchZProgressDataRef.current + + console.log({ + fenceIndex, + fences: stretchZProgressDataRef.current.fences.map( + ({ columnGroup, z }) => [columnGroup.userData.columnIndex, z] + ), + fence: fences[fenceIndex], + }) }) ) ) @@ -326,32 +353,57 @@ const useOnDragStretch = () => { last: () => { if (!stretchZInitialDataRef.current) return - const { layoutGroup } = stretchZInitialDataRef.current + const { layoutGroup, direction, houseTransformsGroup } = + stretchZInitialDataRef.current const sortedVisibleColumnGroups = pipe( layoutGroup, getSortedVisibleColumnGroups ) - pipe( - sortedVisibleColumnGroups, - A.takeRight(2), - ([penultimateColumnGroup, endColumnGroup]) => { - endColumnGroup.position.setZ( - penultimateColumnGroup.position.z + - penultimateColumnGroup.userData.length - ) - } - ) - pipe( - sortedVisibleColumnGroups, - A.takeLeft(2), - ([startColumnGroup, secondColumnGroup]) => { - startColumnGroup.position.setZ( - secondColumnGroup.position.z - startColumnGroup.userData.length + if (direction === 1) { + pipe( + sortedVisibleColumnGroups, + A.takeRight(2), + ([penultimateColumnGroup, endColumnGroup]) => { + endColumnGroup.position.setZ( + penultimateColumnGroup.position.z + + penultimateColumnGroup.userData.length + ) + } + ) + } + + logColumnGroupZs(sortedVisibleColumnGroups) + + console.log("------------") + + if (direction === -1) { + pipe( + sortedVisibleColumnGroups, + A.takeLeft(2), + ([startColumnGroup, secondColumnGroup]) => { + startColumnGroup.position.setZ( + secondColumnGroup.position.z - startColumnGroup.userData.length + ) + } + ) + + const delta = -sortedVisibleColumnGroups[0].position.z + + sortedVisibleColumnGroups.forEach((columnGroup) => { + columnGroup.position.z += delta + }) + + houseTransformsGroup.position.sub( + new Vector3(0, 0, delta).applyAxisAngle( + yAxis, + houseTransformsGroup.rotation.y ) - } - ) + ) + } + + logColumnGroupZs(sortedVisibleColumnGroups) recomputeLayoutGroup(layoutGroup) From 867b65945debb3ceeab445807a3199b6b075058d Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 17 Aug 2023 11:27:43 +0100 Subject: [PATCH 108/132] wip solved obb update --- app/design/ui-3d/fresh/dimensions.ts | 48 +++++++++------------- app/design/ui-3d/fresh/gestures/stretch.ts | 15 +------ 2 files changed, 20 insertions(+), 43 deletions(-) diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 4a2d2005..bebfdc3b 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -16,11 +16,13 @@ import { A, O } from "../../../utils/functions" import { yAxis } from "../../../utils/three" import { columnSorter, + findAllGuardDown, findFirstGuardDown, getActiveHouseUserData, getActiveLayoutGroup, getLayoutGroupColumnGroups, getSortedVisibleColumnGroups, + getVisibleColumnGroups, } from "./helpers/sceneQueries" import { HouseLayoutGroup, @@ -31,7 +33,7 @@ import { UserDataTypeEnum, } from "./userData" -export const DEBUG = false +export const DEBUG = true let lastMesh: Mesh | null @@ -45,24 +47,30 @@ const renderOBB = (obb: OBB, scene: Object3D) => { const mesh = new Mesh(geom, material) mesh.position.copy(obb.center) mesh.setRotationFromMatrix(new Matrix4().setFromMatrix3(obb.rotation)) + mesh.userData.type = "OBB" scene.add(mesh) lastMesh = mesh } export const updateHouseOBB = (houseTransformsGroup: HouseTransformsGroup) => { - const { width, height, length } = getActiveHouseUserData(houseTransformsGroup) const activeLayoutGroup = getActiveLayoutGroup(houseTransformsGroup) + const { width, height, length } = getActiveHouseUserData(houseTransformsGroup) + const { x, y, z } = houseTransformsGroup.position + console.log({ width, height, length }) + console.log({ x, y, z }) + const center = new Vector3(x, y + height / 2, z) const halfSize = new Vector3(width / 2, height / 2, length / 2) const rotation = new Matrix3().setFromMatrix4(houseTransformsGroup.matrix) activeLayoutGroup.userData.obb.set(center, halfSize, rotation) - if (DEBUG && houseTransformsGroup.parent) + if (DEBUG && houseTransformsGroup.parent) { renderOBB(activeLayoutGroup.userData.obb, houseTransformsGroup.parent) + } } export const updateHouseWidth = (houseGroup: Group) => {} @@ -85,23 +93,6 @@ export const updateClippingPlanes = (houseGroup: Group) => { planeZ.applyMatrix4(houseGroup.matrix) } -export const updateHouseLength = (houseGroup: Group) => { - pipe( - houseGroup.children, - A.head, - O.map((zCenterHouseGroup) => { - const { children: columnGroups } = zCenterHouseGroup - - houseGroup.userData.length = columnGroups.reduce( - (acc, columnGroup) => acc + columnGroup.userData.length, - 0 - ) - - zCenterHouseGroup.position.setZ(-houseGroup.userData.length / 2) - }) - ) -} - const updateDnas = (houseTransformsGroup: HouseTransformsGroup) => { let result: string[][] = [] pipe( @@ -153,7 +144,6 @@ export const updateIndexedHouseTransforms = ( export const updateEverything = ( houseTransformsGroup: HouseTransformsGroup ) => { - updateHouseLength(houseTransformsGroup) updateHouseOBB(houseTransformsGroup) // updateClippingPlanes(houseTransformsGroup) updateDnas(houseTransformsGroup) @@ -173,6 +163,14 @@ export const updateEverything = ( } export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { + pipe( + layoutGroup, + getLayoutGroupColumnGroups, + A.filter((columnGroup) => !columnGroup.visible) + ).forEach((columnGroup) => { + columnGroup.removeFromParent() + }) + const oldLength = layoutGroup.userData.length const length = layoutGroup.children @@ -189,12 +187,4 @@ export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { ) updateHouseOBB(layoutGroup.parent as HouseTransformsGroup) - - pipe( - layoutGroup, - getLayoutGroupColumnGroups, - A.filter((columnGroup) => !columnGroup.visible) - ).forEach((columnGroup) => { - columnGroup.removeFromParent() - }) } diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 605e5650..d08294b5 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,7 +1,7 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" -import { A, pipeLog, T } from "../../../../utils/functions" +import { A, T } from "../../../../utils/functions" import { setInvisibleNoRaycast, setVisibleAndRaycast, @@ -29,13 +29,6 @@ import { const TMP_MAX_LENGTH = 10 -const logColumnGroupZs = (columnGroups: ColumnGroup[]) => - pipe( - columnGroups, - A.map((x) => x.position.z), - pipeLog - ) - const useOnDragStretch = () => { const stretchZInitialDataRef = useRef<{ direction: number @@ -374,10 +367,6 @@ const useOnDragStretch = () => { ) } - logColumnGroupZs(sortedVisibleColumnGroups) - - console.log("------------") - if (direction === -1) { pipe( sortedVisibleColumnGroups, @@ -403,8 +392,6 @@ const useOnDragStretch = () => { ) } - logColumnGroupZs(sortedVisibleColumnGroups) - recomputeLayoutGroup(layoutGroup) stretchZInitialDataRef.current = null From 4da98540266c32c632b933a4525db3c55971ecb1 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Sun, 20 Aug 2023 15:15:39 +0100 Subject: [PATCH 109/132] wip we're so back --- app/design/ui-3d/fresh/FreshApp.tsx | 83 ++-- app/design/ui-3d/fresh/dimensions.ts | 21 +- app/design/ui-3d/fresh/gestures/index.ts | 7 +- app/design/ui-3d/fresh/gestures/move.ts | 2 - app/design/ui-3d/fresh/gestures/stretch.ts | 58 +-- app/design/ui-3d/fresh/gestures/stretchX.ts | 10 - app/design/ui-3d/fresh/gestures/stretchZ.ts | 386 ------------------ .../ui-3d/fresh/helpers/clippingPlanes.ts | 76 ++-- app/design/ui-3d/fresh/helpers/handles.ts | 157 ------- app/design/ui-3d/fresh/helpers/layouts.ts | 300 +++++++++----- .../ui-3d/fresh/helpers/sceneQueries.ts | 27 -- .../ui-3d/fresh/shapes/rotateHandles.ts | 67 +++ .../ui-3d/fresh/shapes/stretchHandle.ts | 74 ++++ .../ui-3d/fresh/useKeyTestInteractions.ts | 239 ----------- app/design/ui-3d/fresh/userData.ts | 21 +- app/utils/functions.ts | 7 + app/utils/three.ts | 7 +- 17 files changed, 497 insertions(+), 1045 deletions(-) delete mode 100644 app/design/ui-3d/fresh/gestures/stretchX.ts delete mode 100644 app/design/ui-3d/fresh/gestures/stretchZ.ts delete mode 100644 app/design/ui-3d/fresh/helpers/handles.ts create mode 100644 app/design/ui-3d/fresh/shapes/rotateHandles.ts create mode 100644 app/design/ui-3d/fresh/shapes/stretchHandle.ts delete mode 100644 app/design/ui-3d/fresh/useKeyTestInteractions.ts diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index e59b19ed..aaee68fd 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,15 +1,18 @@ import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" import { Fragment, useRef } from "react" +import { useKey } from "react-use" import { Group } from "three" -import { O } from "../../../utils/functions" +import { A, O } from "../../../utils/functions" import { useSubscribeKey } from "../../../utils/hooks" import { setInvisibleNoRaycast, + setVisibility, setVisibleAndRaycast, } from "../../../utils/three" import scope from "../../state/scope" import siteCtx, { + getModeBools, SiteCtxMode, SiteCtxModeEnum, useModeChangeListener, @@ -20,11 +23,15 @@ import useGestures from "./gestures" import useClippingPlaneHelpers from "./helpers/clippingPlanes" import { BIG_CLIP_NUMBER } from "./helpers/layouts" import { + findAllGuardDown, getActiveHouseUserData, - findHouseTransformsGroupDown, - mapAllHouseTransformGroups, } from "./helpers/sceneQueries" -import { UserDataTypeEnum } from "./userData" +import createStretchHandle from "./shapes/stretchHandle" +import { + isHouseTransformsGroup, + isStretchHandleGroup, + UserDataTypeEnum, +} from "./userData" const FreshApp = () => { const rootRef = useRef(null) @@ -33,19 +40,25 @@ const FreshApp = () => { const bindAll = useGestures() - // useKeyTestInteractions(rootRef) - const showHouseStretchHandles = (houseId: string) => { + if (!rootRef.current) return + + const allHouseTransformGroups = pipe( + rootRef.current.children, + A.filter(isHouseTransformsGroup) + ) + pipe( - findHouseTransformsGroupDown(rootRef, houseId), - O.map((houseTransformGroup) => { - houseTransformGroup.traverse((node) => { - if (node.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh) { - setVisibleAndRaycast(node) - // node.visible = true - } + allHouseTransformGroups, + A.findFirst((x) => x.userData.houseId === houseId), + O.map((houseTransformsGroup) => + pipe( + houseTransformsGroup, + findAllGuardDown(isStretchHandleGroup) + ).forEach((handleGroup) => { + setVisibleAndRaycast(handleGroup) }) - }) + ) ) } @@ -56,7 +69,6 @@ const FreshApp = () => { node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup ) { setInvisibleNoRaycast(node) - // node.visible = false } }) } @@ -65,19 +77,29 @@ const FreshApp = () => { useClippingPlaneHelpers(rootRef) const showHouseRotateHandles = (houseId: string) => { + if (!rootRef.current) return + + const allHouseTransformGroups = pipe( + rootRef.current.children, + A.filter(isHouseTransformsGroup) + ) + pipe( - findHouseTransformsGroupDown(rootRef, houseId), - O.map((houseTransformGroup) => { - houseTransformGroup.traverse((node) => { + allHouseTransformGroups, + A.findFirst((x) => x.userData.houseId === houseId), + O.map((houseTransformsGroup) => + houseTransformsGroup.traverse((node) => { if (node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup) { setVisibleAndRaycast(node) } }) - }) + ) ) } const f = () => { + if (!rootRef.current) return + const { houseId, levelIndex, mode } = siteCtx const { selected } = scope @@ -87,10 +109,20 @@ const FreshApp = () => { ] hideAllHandles() - mapAllHouseTransformGroups(rootRef, (houseTransformGroup) => { - const { houseId } = getActiveHouseUserData(houseTransformGroup) - setYCut(houseId, BIG_CLIP_NUMBER) - }) + + const allHouseTransformGroups = pipe( + rootRef.current.children, + A.filter(isHouseTransformsGroup) + ) + + pipe( + allHouseTransformGroups, + A.findFirst((x) => x.userData.houseId === houseId), + O.map((houseTransformsGroup) => { + const { houseId } = getActiveHouseUserData(houseTransformsGroup) + setYCut(houseId, BIG_CLIP_NUMBER) + }) + ) if (stretchModes.includes(mode)) { if (houseId) showHouseStretchHandles(houseId) @@ -114,11 +146,6 @@ const FreshApp = () => { } } - // switch (true) { - // default: - // if (houseId === null) break - // } - invalidate() } diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index bebfdc3b..29aaeace 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -33,11 +33,11 @@ import { UserDataTypeEnum, } from "./userData" -export const DEBUG = true +export const DEBUG = false let lastMesh: Mesh | null -const renderOBB = (obb: OBB, scene: Object3D) => { +export const renderOBB = (obb: OBB, scene: Object3D) => { const size = obb.halfSize.clone().multiplyScalar(2) if (lastMesh) scene.remove(lastMesh) @@ -59,9 +59,6 @@ export const updateHouseOBB = (houseTransformsGroup: HouseTransformsGroup) => { const { x, y, z } = houseTransformsGroup.position - console.log({ width, height, length }) - console.log({ x, y, z }) - const center = new Vector3(x, y + height / 2, z) const halfSize = new Vector3(width / 2, height / 2, length / 2) const rotation = new Matrix3().setFromMatrix4(houseTransformsGroup.matrix) @@ -162,7 +159,8 @@ export const updateEverything = ( invalidate() } -export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { +// triggers trap for OBB etc +export const updateLayoutGroupLength = (layoutGroup: HouseLayoutGroup) => { pipe( layoutGroup, getLayoutGroupColumnGroups, @@ -171,20 +169,9 @@ export const recomputeLayoutGroup = (layoutGroup: HouseLayoutGroup) => { columnGroup.removeFromParent() }) - const oldLength = layoutGroup.userData.length - const length = layoutGroup.children .filter((x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup) .reduce((acc, v) => acc + v.userData.length, 0) - layoutGroup.position.setZ(-length / 2) layoutGroup.userData.length = length - layoutGroup.parent?.position.add( - new Vector3(0, 0, (length - oldLength) / 2).applyAxisAngle( - yAxis, - layoutGroup.parent.rotation.y - ) - ) - - updateHouseOBB(layoutGroup.parent as HouseTransformsGroup) } diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts index 3ad6798a..d90dc7e2 100644 --- a/app/design/ui-3d/fresh/gestures/index.ts +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -25,6 +25,7 @@ import { isElementMesh, isRotateHandleMesh, isStretchHandleMesh, + StretchHandleGroup, UserDataTypeEnum, } from "../userData" import { dispatchPointerDown, dispatchPointerUp } from "./events" @@ -65,12 +66,14 @@ const useGestures = () => { // stretch if (buildingOrLevelMode && isStretchHandleMesh(object)) { + console.log("hello stretch") + const handleGroup = object.parent as StretchHandleGroup const { userData: { axis }, - } = object + } = handleGroup if (axis === "z" && isStretchHandleMesh(object)) { - if (first) onDragStretchZ.first({ handleObject: object, point }) + if (first) onDragStretchZ.first({ handleGroup, point }) if (!first && !last) onDragStretchZ.mid() if (last) onDragStretchZ.last() } diff --git a/app/design/ui-3d/fresh/gestures/move.ts b/app/design/ui-3d/fresh/gestures/move.ts index 0a9beb9b..b8dec13b 100644 --- a/app/design/ui-3d/fresh/gestures/move.ts +++ b/app/design/ui-3d/fresh/gestures/move.ts @@ -29,8 +29,6 @@ const useOnDragMove = () => { event: { intersections, stopPropagation }, } = state - stopPropagation() - switch (true) { case first: { pipe( diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index d08294b5..f9af4378 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -8,7 +8,7 @@ import { yAxis, } from "../../../../utils/three" import pointer from "../../../state/pointer" -import { recomputeLayoutGroup } from "../dimensions" +import { updateLayoutGroupLength } from "../dimensions" import { dispatchOutline } from "../events/outlines" import { createColumnGroup, splitColumnGroups } from "../helpers/layouts" import { @@ -23,6 +23,7 @@ import { ColumnGroup, HouseLayoutGroup, HouseTransformsGroup, + StretchHandleGroup, StretchHandleMesh, StretchHandleMeshUserData, } from "../userData" @@ -33,7 +34,7 @@ const useOnDragStretch = () => { const stretchZInitialDataRef = useRef<{ direction: number point0: Vector3 - handleGroup: Object3D + handleColumnGroup: Object3D houseTransformsGroup: HouseTransformsGroup layoutGroup: HouseLayoutGroup handleGroupZ0: number @@ -66,6 +67,8 @@ const useOnDragStretch = () => { const addVanilla = (side: 1 | -1) => { if (!stretchZInitialDataRef.current) return + console.log(`add vanilla`) + const { templateVanillaColumnGroup, layoutGroup } = stretchZInitialDataRef.current @@ -95,10 +98,10 @@ const useOnDragStretch = () => { const onDragStretchZ = { first: ({ - handleObject, + handleGroup, point, }: { - handleObject: StretchHandleMesh + handleGroup: StretchHandleGroup point: Vector3 }) => { dispatchOutline({ @@ -106,10 +109,10 @@ const useOnDragStretch = () => { selectedObjects: [], }) - const handleGroup = handleColumnGroupParentQuery(handleObject) - const houseTransformsGroup = getHouseTransformsGroupUp(handleGroup) + const handleColumnGroup = handleColumnGroupParentQuery(handleGroup) + const houseTransformsGroup = getHouseTransformsGroupUp(handleColumnGroup) - const { direction } = handleObject.userData + const { side } = handleGroup.userData const { systemId, houseId, vanillaColumn } = getActiveHouseUserData(houseTransformsGroup) @@ -133,12 +136,12 @@ const useOnDragStretch = () => { const vanillaLength = templateVanillaColumnGroup.userData.length stretchZInitialDataRef.current = { - direction, - handleGroup, + direction: side, + handleColumnGroup: handleColumnGroup, layoutGroup, houseTransformsGroup, point0: point, - handleGroupZ0: handleGroup.position.z, + handleGroupZ0: handleColumnGroup.position.z, templateVanillaColumnGroup, vanillaLength, columnGroups, @@ -150,7 +153,7 @@ const useOnDragStretch = () => { midEndZ: endColumnGroup.position.z, } - if (direction === 1) { + if (side === 1) { stretchZProgressDataRef.current.fences = pipe( midColumnGroups, A.map((columnGroup) => { @@ -166,20 +169,11 @@ const useOnDragStretch = () => { stretchZProgressDataRef.current.fences.length - 1 for (let i = 0; i < 3; i++) { - addVanilla(direction) + addVanilla(side) } - console.log(`fences 1`) - console.log( - stretchZProgressDataRef.current.fences.map( - ({ columnGroup, z }) => [ - columnGroup.userData.columnIndex, - z, - ] - ) - ) } - if (direction === -1) { + if (side === -1) { stretchZProgressDataRef.current.fences = pipe( midColumnGroups, A.reverse, @@ -195,19 +189,11 @@ const useOnDragStretch = () => { stretchZProgressDataRef.current.fences.length - 1 for (let i = 0; i < 3; i++) { - addVanilla(direction) + addVanilla(side) } } - const { fenceIndex, fences } = stretchZProgressDataRef.current - - console.log({ - fenceIndex, - fences: stretchZProgressDataRef.current.fences.map( - ({ columnGroup, z }) => [columnGroup.userData.columnIndex, z] - ), - fence: fences[fenceIndex], - }) + console.log(stretchZProgressDataRef.current.fences) }) ) ) @@ -222,7 +208,7 @@ const useOnDragStretch = () => { direction, point0, houseTransformsGroup, - handleGroup, + handleColumnGroup: handleGroup, handleGroupZ0, vanillaLength, templateVanillaColumnGroup, @@ -234,7 +220,7 @@ const useOnDragStretch = () => { midStartZ, } = stretchZInitialDataRef.current - const { lastDistance, fences: vanillaFences } = + const { lastDistance, fences, fenceIndex } = stretchZProgressDataRef.current const [x1, z1] = pointer.xz @@ -247,8 +233,6 @@ const useOnDragStretch = () => { handleGroup.position.set(0, 0, handleGroupZ0 + distance) - const { fenceIndex, fences } = stretchZProgressDataRef.current - // back side if (direction === 1) { // const cl = clamp(lo, hi) @@ -392,7 +376,7 @@ const useOnDragStretch = () => { ) } - recomputeLayoutGroup(layoutGroup) + updateLayoutGroupLength(layoutGroup) stretchZInitialDataRef.current = null stretchZProgressDataRef.current = { diff --git a/app/design/ui-3d/fresh/gestures/stretchX.ts b/app/design/ui-3d/fresh/gestures/stretchX.ts deleted file mode 100644 index 065419d8..00000000 --- a/app/design/ui-3d/fresh/gestures/stretchX.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ThreeEvent } from "@react-three/fiber" -import { Handler } from "@use-gesture/react" - -const useOnDragStretchX = () => { - const onDragStretchX: Handler<"drag", ThreeEvent> = () => {} - - return onDragStretchX -} - -export default useOnDragStretchX diff --git a/app/design/ui-3d/fresh/gestures/stretchZ.ts b/app/design/ui-3d/fresh/gestures/stretchZ.ts deleted file mode 100644 index 8a8356ff..00000000 --- a/app/design/ui-3d/fresh/gestures/stretchZ.ts +++ /dev/null @@ -1,386 +0,0 @@ -import { ThreeEvent } from "@react-three/fiber" -import { Handler } from "@use-gesture/react" -import { pipe } from "fp-ts/lib/function" -import { useRef } from "react" -import { Group, Object3D, Vector3 } from "three" -import { VanillaColumn } from "../../../../db/layouts" -import { A, Num, O, Ord, T } from "../../../../utils/functions" -import { setCameraControlsEnabled } from "../../../state/camera" -import pointer from "../../../state/pointer" -import { recomputeLayoutGroup, updateHouseOBB } from "../dimensions" -import { dispatchOutline } from "../events/outlines" -import { createColumnGroup } from "../helpers/layouts" -import { - columnSorter, - getActiveHouseUserData, - getActiveLayoutGroup, - getHouseTransformsGroupUp, - getLayoutGroupColumnGroups, - handleColumnGroupParentQuery, -} from "../helpers/sceneQueries" -import { - decrementColumnCount, - HouseLayoutGroup, - incrementColumnCount, - StretchHandleMeshUserData, - UserDataTypeEnum, -} from "../userData" -import { dispatchPointerDown, dispatchPointerUp } from "./events" -import useOnDragStretchX from "./stretchX" - -type StretchData = StretchZUpData | StretchZDownData - -type StretchZDataShared = { - systemId: string - houseId: string - axis: "z" | "x" - handleObject: Object3D - houseTransformsGroup: Object3D - handleGroup: Object3D - layoutGroup: HouseLayoutGroup - point0: Vector3 - handleGroupPos0: Vector3 - lastDistance: number - vanillaColumn: VanillaColumn - vanillaColumnGroup: Object3D - columnsAdded: number - vanillaColumnsAdded: Object3D[] -} - -type StretchZUpData = { - direction: 1 - penultimateColumnGroup: Object3D - endColumnGroup: Object3D -} & StretchZDataShared - -type StretchZDownData = { - direction: -1 - startColumnGroup: Object3D - restColumnGroups: Object3D[] -} & StretchZDataShared - -const useOnDragStretch = () => { - const stretchDataRef = useRef(null) - - const onStretchXProgress = useOnDragStretchX() - - const onStretchZProgress: Handler<"drag", ThreeEvent> = () => { - const stretchData = stretchDataRef.current! - - switch (stretchData.direction) { - case 1: { - const { - handleGroup, - handleGroupPos0, - point0, - houseTransformsGroup, - layoutGroup, - lastDistance, - columnsAdded, - vanillaColumn, - vanillaColumnGroup, - penultimateColumnGroup, - endColumnGroup, - } = stretchData as StretchZUpData - - const [x1, z1] = pointer.xz - const distanceVector = new Vector3(x1, 0, z1).sub(point0) - distanceVector.applyAxisAngle( - new Vector3(0, 1, 0), - -houseTransformsGroup.rotation.y - ) - const distance = distanceVector.z - - handleGroup.position.set(0, 0, handleGroupPos0.z + distance) - - switch (true) { - // addy - case distance > lastDistance: { - if (distance > vanillaColumn.length * columnsAdded) { - const newColumnGroup = vanillaColumnGroup.clone() - - newColumnGroup.position.setZ( - penultimateColumnGroup.position.z + - penultimateColumnGroup.userData.length / 2 + - columnsAdded * vanillaColumn.length + - vanillaColumn.length / 2 - ) - - layoutGroup.add(newColumnGroup) - - newColumnGroup.userData.columnIndex = - penultimateColumnGroup.userData.columnIndex + 1 - - endColumnGroup.userData.columnIndex++ - - incrementColumnCount(layoutGroup) - - stretchData.columnsAdded++ - stretchData.vanillaColumnsAdded.push(newColumnGroup) - recomputeLayoutGroup(layoutGroup) - } - break - } - // subby - case distance < lastDistance: { - if (distance < vanillaColumn.length * (columnsAdded - 1)) { - // only if vanilla columns added - // otherwise we need to remove actual stuff! - pipe( - stretchData.vanillaColumnsAdded, - A.last, - O.map((x) => { - x.removeFromParent() - stretchData.columnsAdded-- - decrementColumnCount(layoutGroup) - endColumnGroup.userData.columnIndex-- - stretchData.vanillaColumnsAdded.pop() - recomputeLayoutGroup(layoutGroup) - }) - ) - } - break - } - } - - stretchData.lastDistance = distance - - return - } - case -1: { - // const { - // handleGroup, - // handleGroupPos0, - // point0, - // houseTransformsGroup, - // layoutGroup, - // lastDistance, - // columnsAdded, - // vanillaColumn, - // vanillaColumnGroup, - // startColumnGroup, - // restColumnGroups, - // } = stretchData as StretchZDownData - // const [x1, z1] = pointer.xz - // const distanceVector = new Vector3(x1, 0, z1).sub(point0) - // distanceVector.applyAxisAngle( - // new Vector3(0, 1, 0), - // -houseTransformsGroup.rotation.y - // ) - // const distance = distanceVector.z - // handleGroup.position.set(0, 0, handleGroupPos0.z + distance) - // switch (true) { - // // addy - // case distance < lastDistance: { - // console.log(`addy`) - // if (distance < vanillaColumn.length * -columnsAdded) { - // console.log(`addy go`) - // for (let columnGroup of restColumnGroups) { - // columnGroup.position.add( - // new Vector3(0, 0, vanillaColumn.length) - // ) - // columnGroup.userData.columnIndex++ - // } - // const [secondColumnGroup] = restColumnGroups - // const newColumnGroup = vanillaColumnGroup.clone() - // newColumnGroup.position.setZ( - // secondColumnGroup.position.z - - // secondColumnGroup.userData.length / 2 - - // vanillaColumn.length / 2 - // ) - // layoutGroup.add(newColumnGroup) - // // newColumnGroup.userData.columnIndex = 1 - // // endColumnGroup.userData.columnIndex++ - // // incrementColumnCount(layoutGroup) - // stretchData.columnsAdded++ - // // stretchData.vanillaColumnsAdded.push(newColumnGroup) - // } - // break - // } - // // subby - // case distance > lastDistance: { - // // if (distance < vanillaColumn.length * (columnsAdded - 1)) { - // // pipe( - // // stretchData.vanillaColumnsAdded, - // // A.last, - // // O.map((x) => { - // // x.removeFromParent() - // // stretchData.columnsAdded-- - // // decrementColumnCount(layoutGroup) - // // // endColumnGroup.userData.columnIndex-- - // // stretchData.vanillaColumnsAdded.pop() - // // }) - // // ) - // // } - // break - // } - // } - // stretchData.lastDistance = distance - } - } - } - - const onDragStretch: Handler<"drag", ThreeEvent> = (state) => { - const { - first, - last, - event, - event: { object, point }, - } = state - - switch (true) { - case first: { - setCameraControlsEnabled(false) - dispatchPointerDown({ point, object }) - - dispatchOutline({ - hoveredObjects: [], - selectedObjects: [], - }) - - const { direction, axis } = object.userData as StretchHandleMeshUserData - - const handleGroup = handleColumnGroupParentQuery(object) - const houseTransformsGroup = getHouseTransformsGroupUp(object) - const { systemId, houseId, vanillaColumn, columnCount } = - getActiveHouseUserData(houseTransformsGroup) - const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) - - switch (axis) { - case "x": { - return - } - case "z": - { - switch (direction) { - case 1: { - const [penultimateColumnGroup, endColumnGroup] = pipe( - layoutGroup, - getLayoutGroupColumnGroups, - A.filter((x) => x.userData.columnIndex >= columnCount - 2), - columnSorter - ) - - const zUpTask = pipe( - T.of(vanillaColumn), - T.chain(({ gridGroups }) => - pipe( - createColumnGroup({ - systemId, - houseId, - gridGroups, - columnIndex: -1, - }), - T.map((vanillaColumnGroup) => { - const data: StretchZUpData = { - systemId, - houseId, - handleObject: object, - houseTransformsGroup: houseTransformsGroup, - handleGroup, - handleGroupPos0: handleGroup.position.clone(), - point0: point, - lastDistance: 0, - direction, - axis, - columnsAdded: 0, - vanillaColumn, - endColumnGroup, - penultimateColumnGroup, - layoutGroup, - vanillaColumnGroup, - vanillaColumnsAdded: [], - } - stretchDataRef.current = data - }) - ) - ) - ) - - zUpTask() - - break - } - case -1: - const [startColumnGroup, ...restColumnGroups] = pipe( - layoutGroup, - getLayoutGroupColumnGroups, - columnSorter - ) - - const zDownTask = pipe( - T.of(vanillaColumn), - T.chain(({ gridGroups }) => - pipe( - createColumnGroup({ - systemId, - houseId, - gridGroups, - columnIndex: -1, - }), - T.map((vanillaColumnGroup) => { - const data: StretchZDownData = { - systemId, - houseId, - handleObject: object, - houseTransformsGroup, - handleGroup, - handleGroupPos0: handleGroup.position.clone(), - point0: point, - lastDistance: 0, - direction, - axis, - columnsAdded: 0, - vanillaColumn, - layoutGroup, - vanillaColumnGroup, - startColumnGroup, - restColumnGroups, - vanillaColumnsAdded: [], - } - stretchDataRef.current = data - }) - ) - ) - ) - - zDownTask() - - break - } - } - - return - } - } - case last: { - if (stretchDataRef.current === null) - throw new Error("stretchData.current null unexpectedly") - dispatchPointerUp() - // so we need to... - stretchDataRef.current = null - setCameraControlsEnabled(true) - return - } - default: { - if (!stretchDataRef.current) - throw new Error(`onDragStretch first didn't set first`) - - const { axis } = stretchDataRef.current - - switch (axis) { - case "z": - onStretchZProgress(state) - return - case "x": - onStretchXProgress(state) - return - } - } - } - } - - return onDragStretch -} - -export default useOnDragStretch diff --git a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts index a2cb15d8..fa04236c 100644 --- a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts +++ b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts @@ -1,59 +1,69 @@ -import { invalidate } from "@react-three/fiber" -import { pipe } from "fp-ts/lib/function" +import { flow, pipe } from "fp-ts/lib/function" import { RefObject } from "react" import { Group, Material, Mesh } from "three" import { A, O } from "../../../../utils/functions" -import siteCtx, { - SiteCtxModeEnum, - useModeChangeListener, -} from "../../../state/siteCtx" import { GridGroupUserData, HouseTransformsGroupUserData, + isHouseTransformsGroup, UserDataTypeEnum, } from "../userData" -import { BIG_CLIP_NUMBER } from "./layouts" import { getActiveHouseUserData, getActiveLayoutGroup, - findHouseTransformsGroupDown, getLayoutGroupColumnGroups, - mapHouseTransformGroup, } from "./sceneQueries" const useClippingPlaneHelpers = (rootRef: RefObject) => { const initClippingPlanes = (houseId: string) => { - mapHouseTransformGroup(rootRef, houseId, (houseTransformGroup) => { - const { clippingPlanes } = - houseTransformGroup.userData as HouseTransformsGroupUserData + if (!rootRef.current) return - houseTransformGroup.traverse((x) => { - if (x.userData.type === UserDataTypeEnum.Enum.ElementMesh) { - ;((x as Mesh).material as Material).clippingPlanes = clippingPlanes - } + pipe( + rootRef.current.children, + A.filter(isHouseTransformsGroup), + A.findFirst((x) => x.userData.houseId === houseId), + O.map((houseTransformsGroup) => { + const { clippingPlanes } = + houseTransformsGroup.userData as HouseTransformsGroupUserData + + houseTransformsGroup.traverse((x) => { + if (x.userData.type === UserDataTypeEnum.Enum.ElementMesh) { + ;((x as Mesh).material as Material).clippingPlanes = clippingPlanes + } + }) }) - }) + ) } const setYCut = (houseId: string, y: number) => { - mapHouseTransformGroup(rootRef, houseId, (houseTransformGroup) => { - const { - clippingPlanes: [, cpy], - height, - } = getActiveHouseUserData(houseTransformGroup) - cpy.constant = y - }) + if (!rootRef.current) return + + pipe( + rootRef.current.children, + A.filter(isHouseTransformsGroup), + A.findFirst((x) => x.userData.houseId === houseId), + O.map((houseTransformsGroup) => { + const { + clippingPlanes: [, cpy], + height, + } = getActiveHouseUserData(houseTransformsGroup) + cpy.constant = y + }) + ) } const houseLevelIndexToCutHeight = ( houseId: string, levelIndex: number ): O.Option => { + if (!rootRef.current) return O.none + return pipe( - findHouseTransformsGroupDown(rootRef, houseId), - O.chain((houseTransformGroup) => - pipe( - houseTransformGroup, + rootRef.current.children, + A.filter(isHouseTransformsGroup), + A.findFirst((x) => x.userData.houseId === houseId), + O.chain( + flow( getActiveLayoutGroup, getLayoutGroupColumnGroups, A.head, @@ -66,15 +76,15 @@ const useClippingPlaneHelpers = (rootRef: RefObject) => { gridGroup.userData as GridGroupUserData return gridGroupUserData.levelIndex === levelIndex + }), + O.map((gridGroup) => { + const { height } = gridGroup.userData as GridGroupUserData + return gridGroup.position.y + height / 2 }) ) }) ) - ), - O.map((gridGroup) => { - const { height } = gridGroup.userData as GridGroupUserData - return gridGroup.position.y + height / 2 - }) + ) ) } diff --git a/app/design/ui-3d/fresh/helpers/handles.ts b/app/design/ui-3d/fresh/helpers/handles.ts deleted file mode 100644 index 4c48c97f..00000000 --- a/app/design/ui-3d/fresh/helpers/handles.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { pipe } from "fp-ts/lib/function" -import { CircleGeometry, Group, Mesh, Object3D, PlaneGeometry } from "three" -import { RoundedBoxGeometry } from "three-stdlib" -import { A } from "../../../../utils/functions" -import { PI } from "../../../../utils/math" -import { - setInvisibleNoRaycast, - setVisibleAndRaycast, -} from "../../../../utils/three" -import handleMaterial from "../handleMaterial" -import { - isStretchHandleMesh, - RotateHandleMeshUserData, - RotateHandlesGroupUserData, - StretchHandleMeshUserData, - UserDataTypeEnum, -} from "../userData" -import { takeWhileGuardDown } from "./sceneQueries" - -export const setStretchHandlesVisibility = ( - houseTransformGroup: Object3D, - value: boolean -) => { - pipe( - houseTransformGroup, - takeWhileGuardDown(isStretchHandleMesh, 2), - A.map((object) => { - if (value) setVisibleAndRaycast(object) - else setInvisibleNoRaycast(object) - }) - ) -} - -export const createStretchHandle = ({ - houseId, - width, - length, - axis, - direction, -}: { - houseId: string - width: number - length: number - axis: "z" | "x" - direction: 1 | -1 -}) => { - const OFFSET_XZ = 0.5 - const OFFSET_Y = 0.1 - - const position: [number, number, number] = - axis === "z" - ? direction === 1 - ? [0, OFFSET_Y, OFFSET_XZ] - : [0, OFFSET_Y, -OFFSET_XZ] - : direction === 1 - ? [width / 2 + OFFSET_XZ, OFFSET_Y, length / 2] - : [-(width / 2 + OFFSET_XZ), OFFSET_Y, length / 2] - - const rotation: [number, number, number] = - axis === "x" ? [0, PI / 2, 0] : [0, 0, 0] - - const geometry = new RoundedBoxGeometry( - ((axis === "z" ? width : length) * 2) / 1.5, - 1, - 1, - undefined, - 0.5 - ) - - const material = handleMaterial - - const mesh = new Mesh(geometry, material) - mesh.position.set(...position) - mesh.rotation.set(...rotation) - mesh.scale.set(0.4, 0.001, 0.4) - mesh.userData = { - type: UserDataTypeEnum.Enum.StretchHandleMesh, - axis, - direction, - houseId, - } as StretchHandleMeshUserData - - setInvisibleNoRaycast(mesh) - - return mesh -} - -const ROTATE_HANDLE_OFFSET = 5 -const ROTATE_HANDLE_SIZE = 0.3 -const rotateHandleCircleGeometry = new CircleGeometry(0.5, 16) - -export const createRotateHandles = ({ - width, - length, -}: { - width: number - length: number -}) => { - const meshUserData: RotateHandleMeshUserData = { - type: UserDataTypeEnum.Enum.RotateHandleMesh, - } - - const circleMesh1 = new Mesh(rotateHandleCircleGeometry, handleMaterial) - circleMesh1.position.set(0, 0, -ROTATE_HANDLE_OFFSET) - circleMesh1.rotation.x = -PI / 2 - circleMesh1.userData = meshUserData - - const planeMesh1 = new Mesh( - new PlaneGeometry(ROTATE_HANDLE_SIZE, ROTATE_HANDLE_OFFSET), - handleMaterial - ) - planeMesh1.rotation.x = -PI / 2 - planeMesh1.position.set(0, 0, -ROTATE_HANDLE_OFFSET / 2) - planeMesh1.userData = meshUserData - - const circleMesh2 = new Mesh(rotateHandleCircleGeometry, handleMaterial) - circleMesh2.rotation.x = -PI / 2 - circleMesh2.position.set(-ROTATE_HANDLE_OFFSET - width / 4, 0, length / 2) - circleMesh2.userData = meshUserData - - const planeMesh2 = new Mesh( - new PlaneGeometry(ROTATE_HANDLE_OFFSET, ROTATE_HANDLE_SIZE), - handleMaterial - ) - planeMesh2.rotation.x = -PI / 2 - planeMesh2.position.set(-width / 1.05, 0, length / 2) - planeMesh2.userData = meshUserData - - const handleGroup = new Group() - handleGroup.add(circleMesh1, planeMesh1, circleMesh2, planeMesh2) - - const groupUserData: RotateHandlesGroupUserData = { - type: UserDataTypeEnum.Enum.RotateHandlesGroup, - } - - handleGroup.userData = groupUserData - - handleGroup.visible = false - - // setInvisible(handleGroup) - - return handleGroup -} - -// export const setRotateHandleVisibility = ( -// houseTransformGroup: Object3D, -// value: boolean -// ) => { -// traverseDownUntil(houseTransformGroup, (object) => { -// if (object.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup) { -// object.visible = value -// o = object -// return true -// } -// return false -// }) -// } diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 0e04cab0..6be24d36 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -1,12 +1,12 @@ import { liveQuery } from "dexie" +import { cartesian } from "fp-ts-std/Array" import { pipe } from "fp-ts/lib/function" import { BufferGeometry, BufferGeometryLoader, Group, + Matrix3, Mesh, - MeshStandardMaterial, - Object3D, Plane, Vector3, } from "three" @@ -20,8 +20,18 @@ import layoutsDB, { VanillaColumn, VanillaColumnsKey, } from "../../../../db/layouts" -import { A, Num, O, Ord, R, S, T } from "../../../../utils/functions" +import { A, combineGuards, O, R, S, T } from "../../../../utils/functions" +import { + setInvisibleNoRaycast, + setVisibility, + setVisibleAndRaycast, + yAxis, +} from "../../../../utils/three" import { getLayoutsWorker } from "../../../../workers" +import siteCtx, { getModeBools } from "../../../state/siteCtx" +import { renderOBB } from "../dimensions" +import createRotateHandles from "../shapes/rotateHandles" +import createStretchHandle from "../shapes/stretchHandle" import { getMaterial } from "../systems" import { ColumnGroup, @@ -30,10 +40,14 @@ import { GridGroupUserData, HouseLayoutGroupUserData, HouseTransformsGroupUserData, + isRotateHandlesGroup, + isStretchHandleGroup, ModuleGroupUserData, UserDataTypeEnum, } from "../userData" -import { createRotateHandles, createStretchHandle } from "./handles" +import { findAllGuardDown } from "./sceneQueries" + +export const DEBUG = false export const BIG_CLIP_NUMBER = 999 @@ -259,7 +273,7 @@ export const createColumnGroups = ({ systemId: string houseId: string houseLayout: ColumnLayout -}): T.Task => +}): T.Task => pipe( houseLayout, A.traverseWithIndex(T.ApplicativeSeq)( @@ -364,118 +378,202 @@ export const createLayoutGroup = ({ modifiedMaterials: {}, vanillaColumn, } - layoutGroup.userData = userData + layoutGroup.position.setZ(-length / 2) layoutGroup.add(...columnGroups) - pipe( - columnGroups, - A.findFirst((x) => x.userData.columnIndex === 0), - O.map((firstColumn) => { - const stretchHandle = createStretchHandle({ - houseId, - axis: "z", - direction: -1, - length, - width, - }) - firstColumn.add(stretchHandle) - }) - ) + // const removeAllHandles = () => { + // pipe( + // layoutGroup, + // findAllGuardDown( + // combineGuards(isRotateHandlesGroup, isStretchHandleMesh) + // ) + // ).forEach((x) => { + // x.removeFromParent() + // }) + // } + + // const refreshHandles = () => { + // console.log("refreshing handles") + // removeAllHandles() + + const { buildingOrLevelMode, siteMode } = getModeBools(siteCtx.mode) + + // const stretchViz = (handle: StretchHandleMesh) => { + // if (buildingOrLevelMode) { + // setVisibleAndRaycast(handle) + // } else { + // setInvisibleNoRaycast(handle) + // } + // return handle + // } + + // const stretchXHandleUp = createStretchHandle({ + // axis: "x", + // direction: 1, + // houseId, + // length, + // width, + // }) + // stretchViz(stretchXHandleUp) + + // layoutGroup.add(stretchXHandleUp) + + // const stretchXHandleDown = createStretchHandle({ + // axis: "x", + // direction: -1, + // houseId, + // length, + // width, + // }) + // stretchViz(stretchXHandleDown) + // layoutGroup.add(stretchXHandleDown) + + // pipe( + // columnGroups, + // A.findFirst((x) => x.userData.columnIndex === 0), + // O.map((firstColumn) => { + // const handle = createStretchHandle({ + // houseId, + // axis: "z", + // direction: -1, + // length, + // width, + // }) + // firstColumn.add(handle) + // stretchViz(handle) + // }) + // ) + + // pipe( + // columnGroups, + // A.findFirst( + // (x) => x.userData.columnIndex === columnGroups.length - 1 + // ), + // O.map((lastColumn) => { + // const stretchHandle = createStretchHandle({ + // houseId, + // axis: "z", + // direction: 1, + // length, + // width, + // }) + // stretchHandle.position.z += lastColumn.userData.length + // lastColumn.add(stretchHandle) + // stretchViz(stretchHandle) + // }) + // ) + + // const rotateHandlesGroup = createRotateHandlesGroup({ + // width, + // length, + // }) + + // if (siteMode) { + // setVisibleAndRaycast(rotateHandlesGroup) + // } else { + // setInvisibleNoRaycast(rotateHandlesGroup) + // } + + // layoutGroup.add(rotateHandlesGroup) + // } + + // refreshHandles() + + const rotateHandles = createRotateHandles({ + houseWidth: width, + houseLength: length, + }) - layoutGroup.add( - createStretchHandle({ - axis: "x", - direction: 1, - houseId, - length, - width, - }) - ) + setVisibility(rotateHandles, siteMode) - layoutGroup.add( - createStretchHandle({ - axis: "x", - direction: -1, - houseId, - length, - width, - }) - ) + layoutGroup.add(rotateHandles) + + const { startColumnGroup, endColumnGroup } = + splitColumnGroups(columnGroups) - pipe( - columnGroups, - A.findFirst( - (x) => x.userData.columnIndex === columnGroups.length - 1 - ), - O.map((lastColumn) => { - const stretchHandle = createStretchHandle({ - houseId, - axis: "z", - direction: 1, - length, - width, + const stretchHandles = pipe( + [1, -1] as Array<1 | -1>, + cartesian(["x", "z"] as Array<"z" | "x">), + A.map(([axis, side]) => { + const stretchHandleGroup = createStretchHandle({ + axis, + side, + houseLength: length, + houseWidth: width, }) - stretchHandle.position.z += lastColumn.userData.length - lastColumn.add(stretchHandle) + setVisibility(stretchHandleGroup, buildingOrLevelMode) + if (axis === "z") { + if (side === 1) { + endColumnGroup.add(stretchHandleGroup) + } else { + startColumnGroup.add(stretchHandleGroup) + } + } else { + layoutGroup.add(stretchHandleGroup) + } + // return stretchHandleGroup }) ) + // layoutGroup.add(...stretchHandles) + + const userDataHandler: ProxyHandler = { + set: function (target: any, prop: any, value: any) { + if (prop === "length") { + const oldLength = target[prop] + const newLength = value + target[prop] = value + + console.log( + `doing stuff, oldLength: ${oldLength}; newLength: ${newLength}` + ) + + layoutGroup.position.setZ(-newLength / 2) + + layoutGroup.parent?.position.add( + new Vector3(0, 0, (newLength - oldLength) / 2).applyAxisAngle( + yAxis, + layoutGroup.parent.rotation.y + ) + ) + + const houseTransformsGroup = layoutGroup.parent! + + const { x, y, z } = houseTransformsGroup.position + + const center = new Vector3(x, y + height / 2, z) + const halfSize = new Vector3( + width / 2, + height / 2, + newLength / 2 + ) + const rotation = new Matrix3().setFromMatrix4( + houseTransformsGroup.matrix + ) + + layoutGroup.userData.obb.set(center, halfSize, rotation) + + if (DEBUG && houseTransformsGroup.parent) { + renderOBB( + layoutGroup.userData.obb, + houseTransformsGroup.parent + ) + } + + // refreshHandles() + } + + return true // Indicate assignment success + }, + } - layoutGroup.add(createRotateHandles({ width, length })) - + layoutGroup.userData = new Proxy(userData, userDataHandler) return layoutGroup }) ) }) ) -// export const createTransformsGroup = () => {} - -// export const houseLayoutToHouseGroup = ({ -// systemId, -// houseId, -// houseLayout, -// }: { -// systemId: string -// houseId: string -// houseLayout: ColumnLayout -// }): T.Task => -// pipe( -// createColumnGroups({ -// systemId, -// houseLayout, -// }), -// T.chain((columnGroups) => async () => { -// const transformsGroup = new Group() -// const layoutGroup = new Group() - -// const BIG_NUMBER = 999 - -// const clippingPlanes: Plane[] = [ -// new Plane(new Vector3(BIG_NUMBER, 0, 0), 0), -// new Plane(new Vector3(0, BIG_NUMBER, 0), 0), -// new Plane(new Vector3(0, 0, BIG_NUMBER), 0), -// ] - -// const houseTransformsGroupUserData: Partial = -// { -// type: UserDataTypeEnum.Enum.HouseTransformsGroup, -// systemId, -// houseId, -// clippingPlanes, -// } -// transformsGroup.userData = houseTransformsGroupUserData - -// layoutGroup.add(...columnGroups) -// layoutGroup.position.setZ(-length / 2) -// layoutGroup.userData.type = UserDataTypeEnum.Enum.HouseLayoutGroup - -// transformsGroup.add(layoutGroup) - -// return transformsGroup -// }) -// ) - export const getHouseLayout = ({ systemId, dnas, diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 770a8dde..9eaaee30 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -99,33 +99,6 @@ export const takeWhileGuardDown = return results } -export const findHouseTransformsGroupDown = ( - rootRef: RefObject, - houseId: string -) => - pipe( - rootRef.current?.children, - O.fromNullable, - O.chain(A.findFirst((x) => x.userData.houseId === houseId)) - ) as O.Option - -export const mapHouseTransformGroup = ( - rootRef: RefObject, - houseId: string, - f: (houseTransformGroup: Object3D) => void -) => pipe(findHouseTransformsGroupDown(rootRef, houseId), O.map(f)) - -export const mapAllHouseTransformGroups = ( - rootRef: RefObject, - f: (houseTransformGroup: Object3D) => void -): void => - pipe( - rootRef.current?.children ?? [], - A.filter(isHouseTransformsGroup), - A.map(f), - () => void null - ) - export const getHouseTransformsGroupUp = ( object: Object3D ): HouseTransformsGroup => { diff --git a/app/design/ui-3d/fresh/shapes/rotateHandles.ts b/app/design/ui-3d/fresh/shapes/rotateHandles.ts new file mode 100644 index 00000000..0b7d4625 --- /dev/null +++ b/app/design/ui-3d/fresh/shapes/rotateHandles.ts @@ -0,0 +1,67 @@ +import { CircleGeometry, Group, Mesh, PlaneGeometry } from "three" +import { PI } from "../../../../utils/math" +import handleMaterial from "../handleMaterial" +import { + RotateHandleMeshUserData, + RotateHandlesGroupUserData, + UserDataTypeEnum, +} from "../userData" + +const ROTATE_HANDLE_OFFSET = 5 +const ROTATE_HANDLE_SIZE = 0.3 +const rotateHandleCircleGeometry = new CircleGeometry(0.5, 16) + +const createRotateHandles = ({ + houseWidth, + houseLength, +}: { + houseWidth: number + houseLength: number +}) => { + const meshUserData: RotateHandleMeshUserData = { + type: UserDataTypeEnum.Enum.RotateHandleMesh, + } + + const circleMesh1 = new Mesh(rotateHandleCircleGeometry, handleMaterial) + circleMesh1.position.set(0, 0, -ROTATE_HANDLE_OFFSET) + circleMesh1.rotation.x = -PI / 2 + circleMesh1.userData = meshUserData + + const planeMesh1 = new Mesh( + new PlaneGeometry(ROTATE_HANDLE_SIZE, ROTATE_HANDLE_OFFSET), + handleMaterial + ) + planeMesh1.rotation.x = -PI / 2 + planeMesh1.position.set(0, 0, -ROTATE_HANDLE_OFFSET / 2) + planeMesh1.userData = meshUserData + + const circleMesh2 = new Mesh(rotateHandleCircleGeometry, handleMaterial) + circleMesh2.rotation.x = -PI / 2 + circleMesh2.position.set( + -ROTATE_HANDLE_OFFSET - houseWidth / 4, + 0, + houseLength / 2 + ) + circleMesh2.userData = meshUserData + + const planeMesh2 = new Mesh( + new PlaneGeometry(ROTATE_HANDLE_OFFSET, ROTATE_HANDLE_SIZE), + handleMaterial + ) + planeMesh2.rotation.x = -PI / 2 + planeMesh2.position.set(-houseWidth / 1.05, 0, houseLength / 2) + planeMesh2.userData = meshUserData + + const handleGroup = new Group() + handleGroup.add(circleMesh1, planeMesh1, circleMesh2, planeMesh2) + + const groupUserData: RotateHandlesGroupUserData = { + type: UserDataTypeEnum.Enum.RotateHandlesGroup, + } + + handleGroup.userData = groupUserData + + return handleGroup +} + +export default createRotateHandles diff --git a/app/design/ui-3d/fresh/shapes/stretchHandle.ts b/app/design/ui-3d/fresh/shapes/stretchHandle.ts new file mode 100644 index 00000000..85085dcb --- /dev/null +++ b/app/design/ui-3d/fresh/shapes/stretchHandle.ts @@ -0,0 +1,74 @@ +import { pipe } from "fp-ts/lib/function" +import { BoxGeometry, Group, Mesh, SphereGeometry } from "three" +import { A } from "../../../../utils/functions" +import handleMaterial from "../handleMaterial" +import { StretchHandleGroupUserData, UserDataTypeEnum } from "../userData" + +const OFFSET = 0.5 +const sphereGeom = new SphereGeometry(0.5) +const boxGeom = new BoxGeometry(1, 1, 1) + +const createStretchHandle = ({ + houseWidth, + houseLength, + axis, + side, +}: { + houseWidth: number + houseLength: number + axis: "z" | "x" + side: 1 | -1 +}) => { + const boxMesh = new Mesh(boxGeom, handleMaterial) + boxMesh.scale.setY(0.001) + boxMesh.userData.type = UserDataTypeEnum.Enum.StretchHandleMesh + + const sphereMeshes = pipe( + A.replicate(2, sphereGeom), + A.mapWithIndex((i, geom): Mesh => { + const mesh = new Mesh(geom, handleMaterial) + mesh.scale.setY(0.001) + mesh.userData.type = UserDataTypeEnum.Enum.StretchHandleMesh + return mesh + }) + ) + + const handleGroup = new Group() + + handleGroup.add(boxMesh, ...sphereMeshes) + + if (axis === "z") { + sphereMeshes.forEach((sphereMesh, i) => { + const sign = i === 0 ? 1 : -1 + sphereMesh.position.setX((houseWidth / 2) * sign) + }) + boxMesh.scale.setX(houseWidth) + + if (side === 1) { + handleGroup.position.setZ(houseLength + OFFSET) + } else if (side === -1) { + handleGroup.position.setZ(-OFFSET) + } + } else if (axis === "x") { + sphereMeshes.forEach((sphereMesh, i) => { + const sign = i === 0 ? 1 : -1 + sphereMesh.position.setZ((houseLength / 2) * sign) + }) + boxMesh.scale.setZ(houseLength) + handleGroup.position.setX((side * houseWidth) / 2 + OFFSET * side) + handleGroup.position.setZ(houseLength / 2) + } + + handleGroup.scale.setScalar(0.5) + handleGroup.position.setY(0.01) + + handleGroup.userData = { + axis, + side, + type: UserDataTypeEnum.Enum.StretchHandleGroup, + } as StretchHandleGroupUserData + + return handleGroup +} + +export default createStretchHandle diff --git a/app/design/ui-3d/fresh/useKeyTestInteractions.ts b/app/design/ui-3d/fresh/useKeyTestInteractions.ts deleted file mode 100644 index d3259782..00000000 --- a/app/design/ui-3d/fresh/useKeyTestInteractions.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { invalidate } from "@react-three/fiber" -import { liveQuery } from "dexie" -import { flow, pipe } from "fp-ts/lib/function" -import { RefObject } from "react" -import { useKey } from "react-use" -import { Group, Object3D, Vector3 } from "three" -import layoutsDB from "../../../db/layouts" -import { A, O, R, T } from "../../../utils/functions" -import { PI } from "../../../utils/math" -import { setInvisibleNoRaycast, yAxis } from "../../../utils/three" -import useClippingPlaneHelpers from "./helpers/clippingPlanes" -import { createLayoutGroup } from "./helpers/layouts" -import { debugNextLayout } from "./helpers/sceneChanges" -import { - findAllGuardDown, - getActiveHouseUserData, - getLayoutGroupBySectionType, -} from "./helpers/sceneQueries" -import { - insertVanillaColumn, - subtractPenultimateColumn, -} from "./helpers/stretchZ" -import { - HouseTransformsGroup, - isHouseTransformsGroup, - UserDataTypeEnum, -} from "./userData" - -const useKeyTestInteractions = (rootRef: RefObject) => { - const getHouseGroups = () => - pipe( - rootRef.current, - O.fromNullable, - O.map(flow(findAllGuardDown(isHouseTransformsGroup))), - O.getOrElse((): HouseTransformsGroup[] => []) - ) - - useKey("z", async () => { - for (let houseGroup of getHouseGroups()) { - await insertVanillaColumn(houseGroup, 1)() - // updateEverything(houseGroup) - } - }) - - useKey("Z", async () => { - for (let houseGroup of getHouseGroups()) { - await insertVanillaColumn(houseGroup, -1)() - // updateEverything(houseGroup) - } - }) - - useKey("d", () => { - for (let houseGroup of getHouseGroups()) { - subtractPenultimateColumn(houseGroup, 1) - // updateEverything(houseGroup) - } - }) - - useKey("D", () => { - for (let houseGroup of getHouseGroups()) { - subtractPenultimateColumn(houseGroup, -1) - // updateEverything(houseGroup) - } - }) - - useKey("t", () => { - for (let houseGroup of getHouseGroups()) { - houseGroup.position.add(new Vector3(1, 0, 1)) - // updateEverything(houseGroup) - } - }) - useKey("T", () => { - for (let houseGroup of getHouseGroups()) { - houseGroup.position.add(new Vector3(-1, 0, -1)) - // updateEverything(houseGroup) - } - }) - - useKey("r", () => { - for (let houseGroup of getHouseGroups()) { - houseGroup.rotateOnAxis(yAxis, PI / 8) - // updateEverything(houseGroup) - } - }) - - useKey("R", () => { - for (let houseGroup of getHouseGroups()) { - houseGroup.rotateOnAxis(yAxis, -PI / 8) - // updateEverything(houseGroup) - } - }) - - useKey("x", () => { - for (let houseGroup of getHouseGroups()) { - debugNextLayout(houseGroup) - invalidate() - - // @ts-ignore - window.houseGroup = houseGroup - } - }) - - const { setYCut } = useClippingPlaneHelpers(rootRef) - - useKey("c", () => { - setYCut(getHouseGroups()[0].userData.houseId, 0.5) - }) - - // toggle stretch width first pass - // useKey("x", () => { - // // console.log(liveHouses) - // for (let houseId of Object.keys(liveHouses)) { - // const liveHouseGroup: Group = liveHouses[houseId] - // const stretchXGroups: Record = stretchXHouses[houseId] - - // pipe( - // R.keys(stretchXGroups), - // A.head, - // O.map((k) => - // pipe( - // stretchXGroups, - // R.lookup(k), - // O.map((firstGroup) => { - // setVisibleAndRaycast(firstGroup) - // setInvisible(liveHouseGroup) - - // console.log("swapping groups") - - // stretchXGroups[ - // (liveHouseGroup.userData as HouseTransformsGroupUserData).sectionType - // ] = liveHouseGroup - - // liveHouses[houseId] = firstGroup - - // delete stretchXGroups[k] - - // updateEverything(firstGroup) - - // invalidate() - // }) - // ) - // ) - // ) - // } - // }) - - liveQuery(() => layoutsDB.altSectionTypeLayouts.toArray()).subscribe( - (data) => { - const houseGroups = getHouseGroups() - for (const { houseId, altSectionTypeLayouts } of data) { - const foo = pipe( - houseGroups, - A.findFirst((houseGroup) => houseGroup.userData.houseId === houseId), - O.map((houseTransformsGroup) => - pipe( - altSectionTypeLayouts, - R.map(({ layout: houseLayout, sectionType }) => { - const { systemId, dnas } = - getActiveHouseUserData(houseTransformsGroup) - - pipe( - getLayoutGroupBySectionType( - houseTransformsGroup, - sectionType.code - ), - O.map((maybeSectionTypeLayoutGroup) => { - houseTransformsGroup.remove(maybeSectionTypeLayoutGroup) - }) - ) - - const taskOut: T.Task = async () => { - const layoutGroup = await createLayoutGroup({ - systemId, - dnas, - houseId, - houseLayout, - })() - - setInvisibleNoRaycast(layoutGroup) - - houseTransformsGroup.add(layoutGroup) - } - - taskOut() - }) - ) - ) - ) - } - } - ) - // for (const { houseId, altSectionTypeLayouts } of data) { - // pipe( - // liveHouses, - // R.lookup(houseId), - // O.map((liveHouseGroup) => { - - // const layoutsTask = pipe( - // layoutTasks, - // R.traverse(T.ApplicativeSeq)(identity) - // ) - - // layoutsTask().then((groups) => { - // pipe( - // stretchXHouses[houseId], - // R.map((group) => rootRef.current?.remove(group)) - // ) - // pipe( - // groups, - // R.map((altHouseGroup) => { - // setInvisible(altHouseGroup) - - // liveHouseGroup.matrix.decompose( - // altHouseGroup.position, - // altHouseGroup.quaternion, - // altHouseGroup.scale - // ) - - // console.log(`adding altHouseGroup ${altHouseGroup.uuid}`) - - // rootRef.current?.add(altHouseGroup) - // }) - // ) - - // console.log( - // `stretchXHouses[houseId] = ${Object.values(groups).map( - // (x) => x.uuid - // )}` - // ) - // stretchXHouses[houseId] = groups - - // console.log(stretchXHouses) - // }) - // }) - // ) - // } -} - -export default useKeyTestInteractions diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 20c94856..4ebee722 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -32,6 +32,7 @@ export const UserDataTypeEnum = z.enum([ "GridGroup", "ModuleGroup", "ElementMesh", + "StretchHandleGroup", "StretchHandleMesh", "RotateHandlesGroup", "RotateHandleMesh", @@ -40,7 +41,6 @@ export const UserDataTypeEnum = z.enum([ export type UserDataTypeEnum = z.infer export type HouseTransformsGroupUserData = { - // all type: typeof UserDataTypeEnum.Enum.HouseTransformsGroup systemId: string houseId: string @@ -48,7 +48,6 @@ export type HouseTransformsGroupUserData = { friendlyName: string clippingPlanes: Plane[] activeChildUuid: string - // preview specific } export type HouseLayoutGroupUserData = { @@ -97,11 +96,14 @@ export type ElementMeshUserData = { // --- HANDLES --- +export type StretchHandleGroupUserData = { + type: typeof UserDataTypeEnum.Enum.StretchHandleGroup + axis: "z" | "x" + side: 1 | -1 +} + export type StretchHandleMeshUserData = { type: typeof UserDataTypeEnum.Enum.StretchHandleMesh - axis: "z" | "x" - direction: 1 | -1 - houseId: string } export type RotateHandlesGroupUserData = { @@ -162,6 +164,10 @@ export type RotateHandlesGroup = Group & { userData: RotateHandlesGroupUserData } +export type StretchHandleGroup = Group & { + userData: StretchHandleGroupUserData +} + // Type Guards export const isElementMesh = (node: Object3D): node is ElementMesh => node.userData?.type === UserDataTypeEnum.Enum.ElementMesh @@ -196,6 +202,11 @@ export const isRotateHandlesGroup = ( ): node is RotateHandlesGroup => node.userData?.type === UserDataTypeEnum.Enum.RotateHandlesGroup +export const isStretchHandleGroup = ( + node: Object3D +): node is StretchHandleGroup => + node.userData?.type === UserDataTypeEnum.Enum.StretchHandleGroup + export const incrementColumnCount = (layoutGroup: Object3D) => { const userData = layoutGroup.userData as HouseLayoutGroupUserData if (userData.type !== UserDataTypeEnum.Enum.HouseLayoutGroup) diff --git a/app/utils/functions.ts b/app/utils/functions.ts index aace8874..3803510e 100644 --- a/app/utils/functions.ts +++ b/app/utils/functions.ts @@ -132,3 +132,10 @@ export const headTail = (xs: T[]) => middle, }) ) + +type Guard = (obj: any) => obj is T + +export const combineGuards = + (guard1: Guard
, guard2: Guard): Guard => + (obj: any): obj is A | B => + guard1(obj) || guard2(obj) diff --git a/app/utils/three.ts b/app/utils/three.ts index 571cc831..4d95b14b 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -191,8 +191,8 @@ export const useRotations = () => { } export const setVisibleAndRaycast = (object: Object3D) => { - object.visible = true object.traverse((node) => { + node.visible = true node.layers.set(CameraLayer.VISIBLE) node.layers.enable(RaycasterLayer.ENABLED) }) @@ -219,6 +219,11 @@ export const setInvisibleButRaycast = (object: Object3D) => { }) } +export const setVisibility = (object: Object3D, bool: boolean) => { + if (bool) setVisibleAndRaycast(object) + else setInvisibleNoRaycast(object) +} + export const replicateObject = (n: number, obj: T): T[] => { const replicated: T[] = [] for (let i = 0; i < n; i++) { From 813645eb897c017815be81f7163489701adf6a58 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Sun, 20 Aug 2023 16:13:07 +0100 Subject: [PATCH 110/132] wip checkpoint --- app/design/state/routing.ts | 4 ++-- app/design/state/siteCtx.ts | 5 ----- app/design/ui-3d/fresh/FreshApp.tsx | 17 +++++++-------- app/design/ui-3d/fresh/events/houses.ts | 4 ++-- app/design/ui-3d/fresh/gestures/index.ts | 5 ++--- app/design/ui-3d/fresh/helpers/layouts.ts | 6 +++--- .../ui-3d/fresh/shapes/stretchHandle.ts | 2 +- app/design/ui/menu/ContextMenuEntry.tsx | 3 +-- app/design/ui/menu/FreshContextMenu.tsx | 20 +++++++----------- app/utils/three.ts | 21 +++++++++++++++++-- 10 files changed, 46 insertions(+), 41 deletions(-) diff --git a/app/design/state/routing.ts b/app/design/state/routing.ts index 0dc7ac8f..548cb751 100644 --- a/app/design/state/routing.ts +++ b/app/design/state/routing.ts @@ -22,11 +22,11 @@ export const useRouting = () => { if (urlChangingLock.current) return if (!location.startsWith("/design")) return - const { buildingOrLevelMode, levelMode } = getModeBools(siteCtx.mode) + const { siteMode, levelMode } = getModeBools(siteCtx.mode) let path = "/design" - if (siteCtx.houseId && buildingOrLevelMode) { + if (siteCtx.houseId && !siteMode) { path += `?houseId=${siteCtx.houseId}` if (levelMode) { path += `&levelIndex=${siteCtx.levelIndex}` diff --git a/app/design/state/siteCtx.ts b/app/design/state/siteCtx.ts index c1e60bbf..24234e6d 100644 --- a/app/design/state/siteCtx.ts +++ b/app/design/state/siteCtx.ts @@ -171,15 +171,10 @@ export const getModeBools = (_mode?: SiteCtxMode) => { const levelMode = mode === SiteCtxModeEnum.Enum.LEVEL - const buildingOrLevelMode = ( - [SiteCtxModeEnum.Enum.BUILDING, SiteCtxModeEnum.Enum.LEVEL] as SiteCtxMode[] - ).includes(mode) - return { siteMode, buildingMode, levelMode, - buildingOrLevelMode, } } diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index aaee68fd..12ffbfd3 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -65,7 +65,7 @@ const FreshApp = () => { const hideAllHandles = () => { rootRef.current?.traverse((node) => { if ( - node.userData.type === UserDataTypeEnum.Enum.StretchHandleMesh || + node.userData.type === UserDataTypeEnum.Enum.StretchHandleGroup || node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup ) { setInvisibleNoRaycast(node) @@ -103,11 +103,9 @@ const FreshApp = () => { const { houseId, levelIndex, mode } = siteCtx const { selected } = scope - const stretchModes: SiteCtxMode[] = [ - SiteCtxModeEnum.Enum.BUILDING, - SiteCtxModeEnum.Enum.LEVEL, - ] + const { buildingMode, levelMode, siteMode } = getModeBools() + console.log(`hide all handles`) hideAllHandles() const allHouseTransformGroups = pipe( @@ -124,11 +122,12 @@ const FreshApp = () => { }) ) - if (stretchModes.includes(mode)) { - if (houseId) showHouseStretchHandles(houseId) + if (houseId && (buildingMode || levelMode)) { + console.log("show stretch handles") + showHouseStretchHandles(houseId) } - if (mode === SiteCtxModeEnum.Enum.LEVEL) { + if (levelMode) { if (houseId !== null && levelIndex !== null) { pipe( houseLevelIndexToCutHeight(houseId, levelIndex), @@ -139,7 +138,7 @@ const FreshApp = () => { } } - if (mode === SiteCtxModeEnum.Enum.SITE) { + if (siteMode) { if (selected !== null) { const { houseId } = selected showHouseRotateHandles(houseId) diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index 4f2996e9..3ed1bea3 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -6,7 +6,7 @@ import { Group, Vector3 } from "three" import { HouseType } from "../../../../../server/data/houseTypes" import userDB, { House } from "../../../../db/user" import { A } from "../../../../utils/functions" -import { setVisibleAndRaycast } from "../../../../utils/three" +import { setRaycasting, setVisibleAndRaycast } from "../../../../utils/three" import { nanoid } from "nanoid" import { floor } from "../../../../utils/math" import { createInitialHouse } from "../helpers/layouts" @@ -75,7 +75,7 @@ export const useHousesEvents = (rootRef: RefObject) => { houseGroup.position.set(position.x, position.y, position.z) houseGroup.rotation.set(0, rotation, 0) - setVisibleAndRaycast(houseGroup) + setRaycasting(houseGroup, true) rootRef.current.add(houseGroup) // liveHouses[houseId] = houseGroup diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts index d90dc7e2..1fab2250 100644 --- a/app/design/ui-3d/fresh/gestures/index.ts +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -62,11 +62,10 @@ const useGestures = () => { const { object, point } = firstDragEventRef.current! - const { siteMode, buildingOrLevelMode } = getModeBools() + const { siteMode } = getModeBools() // stretch - if (buildingOrLevelMode && isStretchHandleMesh(object)) { - console.log("hello stretch") + if (!siteMode && isStretchHandleMesh(object)) { const handleGroup = object.parent as StretchHandleGroup const { userData: { axis }, diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 6be24d36..656a59a3 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -397,7 +397,7 @@ export const createLayoutGroup = ({ // console.log("refreshing handles") // removeAllHandles() - const { buildingOrLevelMode, siteMode } = getModeBools(siteCtx.mode) + const { siteMode } = getModeBools(siteCtx.mode) // const stretchViz = (handle: StretchHandleMesh) => { // if (buildingOrLevelMode) { @@ -492,7 +492,7 @@ export const createLayoutGroup = ({ const { startColumnGroup, endColumnGroup } = splitColumnGroups(columnGroups) - const stretchHandles = pipe( + pipe( [1, -1] as Array<1 | -1>, cartesian(["x", "z"] as Array<"z" | "x">), A.map(([axis, side]) => { @@ -502,7 +502,6 @@ export const createLayoutGroup = ({ houseLength: length, houseWidth: width, }) - setVisibility(stretchHandleGroup, buildingOrLevelMode) if (axis === "z") { if (side === 1) { endColumnGroup.add(stretchHandleGroup) @@ -512,6 +511,7 @@ export const createLayoutGroup = ({ } else { layoutGroup.add(stretchHandleGroup) } + setVisibility(stretchHandleGroup, !siteMode) // return stretchHandleGroup }) ) diff --git a/app/design/ui-3d/fresh/shapes/stretchHandle.ts b/app/design/ui-3d/fresh/shapes/stretchHandle.ts index 85085dcb..c95e2c48 100644 --- a/app/design/ui-3d/fresh/shapes/stretchHandle.ts +++ b/app/design/ui-3d/fresh/shapes/stretchHandle.ts @@ -45,7 +45,7 @@ const createStretchHandle = ({ boxMesh.scale.setX(houseWidth) if (side === 1) { - handleGroup.position.setZ(houseLength + OFFSET) + handleGroup.position.setZ(OFFSET * 1.75) } else if (side === -1) { handleGroup.position.setZ(-OFFSET) } diff --git a/app/design/ui/menu/ContextMenuEntry.tsx b/app/design/ui/menu/ContextMenuEntry.tsx index c6501cb9..b3f380b5 100644 --- a/app/design/ui/menu/ContextMenuEntry.tsx +++ b/app/design/ui/menu/ContextMenuEntry.tsx @@ -58,8 +58,7 @@ const ContextMenuEntry = ({ x: pageX, y: pageY }: { x: number; y: number }) => { props.onClose?.() } - const { siteMode, buildingMode, levelMode, buildingOrLevelMode } = - getModeBools(mode) + const { siteMode, buildingMode, levelMode } = getModeBools(mode) const houseTypes = useHouseTypes() diff --git a/app/design/ui/menu/FreshContextMenu.tsx b/app/design/ui/menu/FreshContextMenu.tsx index 4a0014c8..e1a9ba20 100644 --- a/app/design/ui/menu/FreshContextMenu.tsx +++ b/app/design/ui/menu/FreshContextMenu.tsx @@ -1,23 +1,19 @@ -import React, { Fragment, useMemo, useState } from "react" -import { Pencil, TextCursor } from "../../../ui/icons" +import { Fragment, useMemo, useState } from "react" import { ScopeItem } from "../../state/scope" import siteCtx, { downMode, getModeBools } from "../../state/siteCtx" -import RenameForm from "../RenameForm" import ContextMenu from "./ContextMenu" -import ContextMenuButton from "./ContextMenuButton" const FreshContextMenu = () => { const [item, setItem] = useState(null) const [{ pageX, pageY }, setPageXY] = useState({ pageX: 0, pageY: 0 }) - const { buildingMode, buildingOrLevelMode, levelMode, siteMode } = - useMemo(() => { - const modeBools = getModeBools(siteCtx.mode) - return { - ...modeBools, - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [item]) + const { siteMode } = useMemo(() => { + const modeBools = getModeBools(siteCtx.mode) + return { + ...modeBools, + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [item]) const editBuilding = () => { if (!item) return diff --git a/app/utils/three.ts b/app/utils/three.ts index 4d95b14b..38bc5f45 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -190,6 +190,18 @@ export const useRotations = () => { return { rotateV2, unrotateV2 } } +export const setRaycasting = (object: Object3D, bool: boolean) => { + object.traverse((node) => { + if (bool) { + node.layers.enable(RaycasterLayer.ENABLED) + node.layers.disable(RaycasterLayer.DISABLED) + } else { + node.layers.enable(RaycasterLayer.DISABLED) + node.layers.disable(RaycasterLayer.ENABLED) + } + }) +} + export const setVisibleAndRaycast = (object: Object3D) => { object.traverse((node) => { node.visible = true @@ -220,8 +232,13 @@ export const setInvisibleButRaycast = (object: Object3D) => { } export const setVisibility = (object: Object3D, bool: boolean) => { - if (bool) setVisibleAndRaycast(object) - else setInvisibleNoRaycast(object) + if (bool) { + console.log(`setting visible`) + setVisibleAndRaycast(object) + } else { + console.log(`setting invisible`) + setInvisibleNoRaycast(object) + } } export const replicateObject = (n: number, obj: T): T[] => { From 2afe5ce8d967fbfef1131dbd5d7046857c86e77c Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 21 Aug 2023 08:29:31 +0100 Subject: [PATCH 111/132] wip --- app/design/ui-3d/fresh/gestures/index.ts | 6 +- app/design/ui-3d/fresh/gestures/stretch.ts | 110 +++++++++- app/design/ui-3d/fresh/helpers/layouts.ts | 197 ++++++------------ .../ui-3d/fresh/helpers/sceneChanges.ts | 14 +- .../ui-3d/fresh/helpers/sceneQueries.ts | 21 +- .../ui-3d/fresh/shapes/stretchHandle.ts | 2 +- app/utils/three.ts | 20 ++ 7 files changed, 202 insertions(+), 168 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts index 1fab2250..e62b80ed 100644 --- a/app/design/ui-3d/fresh/gestures/index.ts +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -77,9 +77,9 @@ const useGestures = () => { if (last) onDragStretchZ.last() } if (axis === "x") { - // if (first) onDragStretchX.first(userData) - // if (!first && !last) onDragStretchX.mid(userData) - // if (last) onDragStretchX.last(userData) + if (first) onDragStretchX.first({ handleGroup, point }) + if (!first && !last) onDragStretchX.mid() + if (last) onDragStretchX.last() } } diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index f9af4378..8b557356 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,8 +1,10 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" -import { A, T } from "../../../../utils/functions" +import { A, someOrError, T } from "../../../../utils/functions" import { + addDebugLineAtX, + addDebugLineAtZ, setInvisibleNoRaycast, setVisibleAndRaycast, yAxis, @@ -12,9 +14,11 @@ import { updateLayoutGroupLength } from "../dimensions" import { dispatchOutline } from "../events/outlines" import { createColumnGroup, splitColumnGroups } from "../helpers/layouts" import { + findAllGuardDown, getActiveHouseUserData, getActiveLayoutGroup, getHouseTransformsGroupUp, + getPartitionedLayoutGroups, getSortedVisibleColumnGroups, getVisibleColumnGroups, handleColumnGroupParentQuery, @@ -23,6 +27,7 @@ import { ColumnGroup, HouseLayoutGroup, HouseTransformsGroup, + isStretchHandleGroup, StretchHandleGroup, StretchHandleMesh, StretchHandleMeshUserData, @@ -49,13 +54,13 @@ const useOnDragStretch = () => { midEndZ: number } | null>(null) - type Fence = { + type FenceZ = { z: number columnGroup: ColumnGroup } const stretchZProgressDataRef = useRef<{ - fences: Fence[] + fences: FenceZ[] lastDistance: number fenceIndex: number }>({ @@ -67,8 +72,6 @@ const useOnDragStretch = () => { const addVanilla = (side: 1 | -1) => { if (!stretchZInitialDataRef.current) return - console.log(`add vanilla`) - const { templateVanillaColumnGroup, layoutGroup } = stretchZInitialDataRef.current @@ -208,7 +211,7 @@ const useOnDragStretch = () => { direction, point0, houseTransformsGroup, - handleColumnGroup: handleGroup, + handleColumnGroup, handleGroupZ0, vanillaLength, templateVanillaColumnGroup, @@ -231,7 +234,7 @@ const useOnDragStretch = () => { ) const distance = distanceVector.z - handleGroup.position.set(0, 0, handleGroupZ0 + distance) + handleColumnGroup.position.set(0, 0, handleGroupZ0 + distance) // back side if (direction === 1) { @@ -249,6 +252,9 @@ const useOnDragStretch = () => { endColumnGroup.userData.columnIndex - 1 stretchZProgressDataRef.current.fenceIndex++ + // layoutGroup.userData.length += + // nextFence.columnGroup.userData.length + if (nextFence.z < maxLength) { addVanilla(direction) } @@ -266,6 +272,10 @@ const useOnDragStretch = () => { setInvisibleNoRaycast(lastVisibleFence.columnGroup) stretchZProgressDataRef.current.fenceIndex-- lastVisibleFence.columnGroup.userData.columnIndex = -1 + + // layoutGroup.userData.length += + // lastVisibleFence.columnGroup.userData.length + endColumnGroup.userData.columnIndex-- } } @@ -293,6 +303,9 @@ const useOnDragStretch = () => { nextFence.columnGroup.userData.columnIndex = 1 + // layoutGroup.userData.length += + // nextFence.columnGroup.userData.length + stretchZProgressDataRef.current.fenceIndex++ // naive @@ -310,6 +323,10 @@ const useOnDragStretch = () => { if (realDistance > lastVisibleFence.z) { setInvisibleNoRaycast(lastVisibleFence.columnGroup) + + // layoutGroup.userData.length -= + // lastVisibleFence.columnGroup.userData.length + lastVisibleFence.columnGroup.userData.columnIndex = -1 pipe( @@ -387,12 +404,83 @@ const useOnDragStretch = () => { }, } - const stretchXData = useRef<{} | null>(null) + type FenceX = { + x: number + layoutGroup: HouseLayoutGroup + } + + const stretchXData = useRef<{ + point0: Vector3 + houseTransformsGroup: HouseTransformsGroup + handleGroup: StretchHandleGroup + otherSideHandleGroup: StretchHandleGroup + } | null>(null) const onDragStretchX = { - first: (stretchHandleUserData: StretchHandleMeshUserData) => {}, - mid: (stretchHandleUserData: StretchHandleMeshUserData) => {}, - last: (stretchHandleUserData: StretchHandleMeshUserData) => {}, + first: ({ + point, + handleGroup, + }: { + handleGroup: StretchHandleGroup + point: Vector3 + }) => { + dispatchOutline({ + hoveredObjects: [], + selectedObjects: [], + }) + + const { side } = handleGroup.userData + const otherSide = side * -1 + + const houseTransformsGroup = getHouseTransformsGroupUp(handleGroup) + const { activeLayoutGroup, otherLayoutGroups } = + getPartitionedLayoutGroups(houseTransformsGroup) + + const otherSideHandleGroup = pipe( + activeLayoutGroup.children, + A.findFirst((x): x is StretchHandleGroup => { + return ( + isStretchHandleGroup(x) && + x.userData.axis === "x" && + x.userData.side === otherSide + ) + }), + someOrError(`other side handle group not found`) + ) + + const fences = pipe( + otherLayoutGroups, + A.map((x) => (x.userData.width / 2) * side) + ) + + console.log(fences) + + fences.forEach((fence) => { + console.log(`adding fence`) + addDebugLineAtX(activeLayoutGroup, fence) + }) + + stretchXData.current = { + handleGroup, + otherSideHandleGroup, + houseTransformsGroup, + point0: point, + } + }, + mid: () => { + if (!stretchXData.current) return + + const { point0, houseTransformsGroup } = stretchXData.current + const [x1, z1] = pointer.xz + const distanceVector = new Vector3(x1, 0, z1).sub(point0) + distanceVector.applyAxisAngle( + new Vector3(0, 1, 0), + -houseTransformsGroup.rotation.y + ) + const distance = distanceVector.x + // handleGroup.position.set(handleGroupX0 + distance, 0) + }, + last: () => {}, } return { onDragStretchZ, onDragStretchX } diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 656a59a3..00c39d56 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -21,12 +21,7 @@ import layoutsDB, { VanillaColumnsKey, } from "../../../../db/layouts" import { A, combineGuards, O, R, S, T } from "../../../../utils/functions" -import { - setInvisibleNoRaycast, - setVisibility, - setVisibleAndRaycast, - yAxis, -} from "../../../../utils/three" +import { addDebugLineAtZ, setVisibility, yAxis } from "../../../../utils/three" import { getLayoutsWorker } from "../../../../workers" import siteCtx, { getModeBools } from "../../../state/siteCtx" import { renderOBB } from "../dimensions" @@ -41,13 +36,13 @@ import { HouseLayoutGroupUserData, HouseTransformsGroupUserData, isRotateHandlesGroup, - isStretchHandleGroup, + isStretchHandleMesh, ModuleGroupUserData, UserDataTypeEnum, } from "../userData" import { findAllGuardDown } from "./sceneQueries" -export const DEBUG = false +export const DEBUG = true export const BIG_CLIP_NUMBER = 999 @@ -361,6 +356,8 @@ export const createLayoutGroup = ({ const obb = new OBB() const levelTypes = houseLayoutToLevelTypes(houseLayout) + const { siteMode } = getModeBools(siteCtx.mode) + return pipe( getVanillaColumn({ systemId, levelTypes }), T.map((vanillaColumn) => { @@ -382,140 +379,19 @@ export const createLayoutGroup = ({ layoutGroup.position.setZ(-length / 2) layoutGroup.add(...columnGroups) - // const removeAllHandles = () => { - // pipe( - // layoutGroup, - // findAllGuardDown( - // combineGuards(isRotateHandlesGroup, isStretchHandleMesh) - // ) - // ).forEach((x) => { - // x.removeFromParent() - // }) - // } - - // const refreshHandles = () => { - // console.log("refreshing handles") - // removeAllHandles() - - const { siteMode } = getModeBools(siteCtx.mode) - - // const stretchViz = (handle: StretchHandleMesh) => { - // if (buildingOrLevelMode) { - // setVisibleAndRaycast(handle) - // } else { - // setInvisibleNoRaycast(handle) - // } - // return handle - // } - - // const stretchXHandleUp = createStretchHandle({ - // axis: "x", - // direction: 1, - // houseId, - // length, - // width, - // }) - // stretchViz(stretchXHandleUp) - - // layoutGroup.add(stretchXHandleUp) - - // const stretchXHandleDown = createStretchHandle({ - // axis: "x", - // direction: -1, - // houseId, - // length, - // width, - // }) - // stretchViz(stretchXHandleDown) - // layoutGroup.add(stretchXHandleDown) - - // pipe( - // columnGroups, - // A.findFirst((x) => x.userData.columnIndex === 0), - // O.map((firstColumn) => { - // const handle = createStretchHandle({ - // houseId, - // axis: "z", - // direction: -1, - // length, - // width, - // }) - // firstColumn.add(handle) - // stretchViz(handle) - // }) - // ) - - // pipe( - // columnGroups, - // A.findFirst( - // (x) => x.userData.columnIndex === columnGroups.length - 1 - // ), - // O.map((lastColumn) => { - // const stretchHandle = createStretchHandle({ - // houseId, - // axis: "z", - // direction: 1, - // length, - // width, - // }) - // stretchHandle.position.z += lastColumn.userData.length - // lastColumn.add(stretchHandle) - // stretchViz(stretchHandle) - // }) - // ) - - // const rotateHandlesGroup = createRotateHandlesGroup({ - // width, - // length, - // }) - - // if (siteMode) { - // setVisibleAndRaycast(rotateHandlesGroup) - // } else { - // setInvisibleNoRaycast(rotateHandlesGroup) - // } - - // layoutGroup.add(rotateHandlesGroup) - // } - - // refreshHandles() - - const rotateHandles = createRotateHandles({ - houseWidth: width, - houseLength: length, - }) - - setVisibility(rotateHandles, siteMode) - - layoutGroup.add(rotateHandles) - const { startColumnGroup, endColumnGroup } = splitColumnGroups(columnGroups) - pipe( - [1, -1] as Array<1 | -1>, - cartesian(["x", "z"] as Array<"z" | "x">), - A.map(([axis, side]) => { - const stretchHandleGroup = createStretchHandle({ - axis, - side, - houseLength: length, - houseWidth: width, - }) - if (axis === "z") { - if (side === 1) { - endColumnGroup.add(stretchHandleGroup) - } else { - startColumnGroup.add(stretchHandleGroup) - } - } else { - layoutGroup.add(stretchHandleGroup) - } - setVisibility(stretchHandleGroup, !siteMode) - // return stretchHandleGroup + const removeAllHandles = () => { + pipe( + layoutGroup, + findAllGuardDown( + combineGuards(isRotateHandlesGroup, isStretchHandleMesh) + ) + ).forEach((x) => { + x.removeFromParent() }) - ) - // layoutGroup.add(...stretchHandles) + } const userDataHandler: ProxyHandler = { set: function (target: any, prop: any, value: any) { @@ -560,7 +436,7 @@ export const createLayoutGroup = ({ ) } - // refreshHandles() + refreshHandles() } return true // Indicate assignment success @@ -568,6 +444,49 @@ export const createLayoutGroup = ({ } layoutGroup.userData = new Proxy(userData, userDataHandler) + + const refreshHandles = () => { + removeAllHandles() + + const { length: houseLength, width: houseWidth } = + layoutGroup.userData + + const rotateHandles = createRotateHandles({ + houseWidth, + houseLength, + }) + + setVisibility(rotateHandles, siteMode) + + layoutGroup.add(rotateHandles) + + pipe( + [1, -1] as Array<1 | -1>, + cartesian(["x", "z"] as Array<"z" | "x">), + A.map(([axis, side]) => { + const stretchHandleGroup = createStretchHandle({ + axis, + side, + houseLength: length, + houseWidth: width, + }) + if (axis === "z") { + if (side === 1) { + endColumnGroup.add(stretchHandleGroup) + } else { + startColumnGroup.add(stretchHandleGroup) + } + } else { + stretchHandleGroup.position.setZ(houseLength / 2) + layoutGroup.add(stretchHandleGroup) + } + setVisibility(stretchHandleGroup, !siteMode) + }) + ) + } + + refreshHandles() + return layoutGroup }) ) diff --git a/app/design/ui-3d/fresh/helpers/sceneChanges.ts b/app/design/ui-3d/fresh/helpers/sceneChanges.ts index 3a8033a4..0bf047f8 100644 --- a/app/design/ui-3d/fresh/helpers/sceneChanges.ts +++ b/app/design/ui-3d/fresh/helpers/sceneChanges.ts @@ -1,24 +1,22 @@ import { pipe } from "fp-ts/lib/function" -import { Group } from "three" import { A, O } from "../../../../utils/functions" import { setInvisibleNoRaycast, setVisibleAndRaycast, } from "../../../../utils/three" +import { HouseTransformsGroup } from "../userData" import { getPartitionedLayoutGroups } from "./sceneQueries" -export const debugNextLayout = (houseTransformsGroup: Group) => { - const { - left: rest, - right: [active], - } = getPartitionedLayoutGroups(houseTransformsGroup) +export const debugNextLayout = (houseTransformsGroup: HouseTransformsGroup) => { + const { activeLayoutGroup, otherLayoutGroups } = + getPartitionedLayoutGroups(houseTransformsGroup) pipe( - rest, + otherLayoutGroups, A.head, O.map((nextLayout) => { setVisibleAndRaycast(nextLayout) - setInvisibleNoRaycast(active) + setInvisibleNoRaycast(activeLayoutGroup) houseTransformsGroup.userData.activeChildUuid = nextLayout.uuid }) ) diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 9eaaee30..7763f70d 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -9,6 +9,7 @@ import { HouseTransformsGroup, HouseTransformsGroupUserData, isColumnGroup, + isHouseLayoutGroup, isHouseTransformsGroup, UserDataTypeEnum, } from "../userData" @@ -149,10 +150,10 @@ export const getActiveHouseUserData = (houseTransformsGroup: Object3D) => someOrError(`getActiveHouseUserData failure`) ) -export const getLayoutGroups = (houseTransformsGroup: Group): Group[] => - houseTransformsGroup.children.filter( - (x) => x.userData.type === UserDataTypeEnum.Enum.HouseLayoutGroup - ) as Group[] +export const getLayoutGroups = ( + houseTransformsGroup: HouseTransformsGroup +): HouseLayoutGroup[] => + houseTransformsGroup.children.filter(isHouseLayoutGroup) export const getActiveLayoutGroup = ( houseTransformsGroup: HouseTransformsGroup @@ -166,11 +167,19 @@ export const getActiveLayoutGroup = ( someOrError(`getActiveLayoutGroup failure`) ) -export const getPartitionedLayoutGroups = (houseTransformsGroup: Group) => +export const getPartitionedLayoutGroups = ( + houseTransformsGroup: HouseTransformsGroup +) => pipe( houseTransformsGroup, getLayoutGroups, - A.partition((x) => x.uuid === houseTransformsGroup.userData.activeChildUuid) + A.partition( + (x) => x.uuid === houseTransformsGroup.userData.activeChildUuid + ), + ({ left: otherLayoutGroups, right: [activeLayoutGroup] }) => ({ + activeLayoutGroup, + otherLayoutGroups, + }) ) export const getLayoutGroupColumnGroups = ( diff --git a/app/design/ui-3d/fresh/shapes/stretchHandle.ts b/app/design/ui-3d/fresh/shapes/stretchHandle.ts index c95e2c48..636db505 100644 --- a/app/design/ui-3d/fresh/shapes/stretchHandle.ts +++ b/app/design/ui-3d/fresh/shapes/stretchHandle.ts @@ -56,7 +56,7 @@ const createStretchHandle = ({ }) boxMesh.scale.setZ(houseLength) handleGroup.position.setX((side * houseWidth) / 2 + OFFSET * side) - handleGroup.position.setZ(houseLength / 2) + // handleGroup.position.setZ(houseLength / 2) } handleGroup.scale.setScalar(0.5) diff --git a/app/utils/three.ts b/app/utils/three.ts index 38bc5f45..49237501 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -269,3 +269,23 @@ export const addDebugLineAtZ = ( line.position.set(0, 0, z) parent.add(line) } + +export const addDebugLineAtX = ( + parent: Object3D, + x: number, + length: number = 5, + color: Color | number | string = 0xff0000 +): void => { + // Step 1: Create geometry for the line segment. + const lineGeometry = new BufferGeometry().setFromPoints([ + new Vector3(x, 0, -length / 2), + new Vector3(x, 0, length / 2), + ]) + + // Step 2: Create a material for the line. + const lineMaterial = new LineBasicMaterial({ color: color }) + + // Step 3: Create the line object and add it to the parent. + const line = new Line(lineGeometry, lineMaterial) + parent.add(line) +} From 745ec5679bb9eb2f11ffc4a78bbbdfaee6cab366 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 21 Aug 2023 08:41:24 +0100 Subject: [PATCH 112/132] wip --- app/design/ui-3d/fresh/helpers/layouts.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts index 00c39d56..dcf6b173 100644 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ b/app/design/ui-3d/fresh/helpers/layouts.ts @@ -42,7 +42,7 @@ import { } from "../userData" import { findAllGuardDown } from "./sceneQueries" -export const DEBUG = true +export const DEBUG = false export const BIG_CLIP_NUMBER = 999 @@ -356,8 +356,6 @@ export const createLayoutGroup = ({ const obb = new OBB() const levelTypes = houseLayoutToLevelTypes(houseLayout) - const { siteMode } = getModeBools(siteCtx.mode) - return pipe( getVanillaColumn({ systemId, levelTypes }), T.map((vanillaColumn) => { @@ -448,6 +446,8 @@ export const createLayoutGroup = ({ const refreshHandles = () => { removeAllHandles() + const { siteMode } = getModeBools(siteCtx.mode) + const { length: houseLength, width: houseWidth } = layoutGroup.userData @@ -467,8 +467,8 @@ export const createLayoutGroup = ({ const stretchHandleGroup = createStretchHandle({ axis, side, - houseLength: length, - houseWidth: width, + houseLength, + houseWidth, }) if (axis === "z") { if (side === 1) { From 31f33cad66679941d42cdb90782e97378456bd36 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Mon, 21 Aug 2023 16:20:16 +0100 Subject: [PATCH 113/132] wip pre x handles to transforms group --- app/db/layouts.ts | 2 +- app/design/ui-3d/fresh/FreshApp.tsx | 143 +----------- app/design/ui-3d/fresh/dimensions.ts | 2 +- app/design/ui-3d/fresh/events/houses.ts | 10 +- app/design/ui-3d/fresh/events/modeChange.ts | 214 ++++++++++++++++++ app/design/ui-3d/fresh/gestures/stretch.ts | 97 ++++++-- .../ui-3d/fresh/helpers/sceneQueries.ts | 13 +- app/design/ui-3d/fresh/helpers/stretchZ.ts | 4 +- .../ui-3d/fresh/shapes/rotateHandles.ts | 1 + app/utils/three.ts | 2 - app/workers/layouts/worker.ts | 10 +- 11 files changed, 315 insertions(+), 183 deletions(-) create mode 100644 app/design/ui-3d/fresh/events/modeChange.ts diff --git a/app/db/layouts.ts b/app/db/layouts.ts index f9dc5d43..0d7d1541 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -116,7 +116,7 @@ type IndexedAltSectionTypeLayouts = { houseId: string altSectionTypeLayouts: Record< string, - { layout: ColumnLayout; sectionType: SectionType } + { layout: ColumnLayout; dnas: string[]; sectionType: SectionType } > } diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index 12ffbfd3..a1aa1731 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -1,156 +1,17 @@ -import { invalidate } from "@react-three/fiber" -import { pipe } from "fp-ts/lib/function" import { Fragment, useRef } from "react" -import { useKey } from "react-use" import { Group } from "three" -import { A, O } from "../../../utils/functions" -import { useSubscribeKey } from "../../../utils/hooks" -import { - setInvisibleNoRaycast, - setVisibility, - setVisibleAndRaycast, -} from "../../../utils/three" -import scope from "../../state/scope" -import siteCtx, { - getModeBools, - SiteCtxMode, - SiteCtxModeEnum, - useModeChangeListener, -} from "../../state/siteCtx" import XZPlane from "../XZPlane" import { useHousesEvents } from "./events/houses" +import useModeChange from "./events/modeChange" import useGestures from "./gestures" -import useClippingPlaneHelpers from "./helpers/clippingPlanes" -import { BIG_CLIP_NUMBER } from "./helpers/layouts" -import { - findAllGuardDown, - getActiveHouseUserData, -} from "./helpers/sceneQueries" -import createStretchHandle from "./shapes/stretchHandle" -import { - isHouseTransformsGroup, - isStretchHandleGroup, - UserDataTypeEnum, -} from "./userData" const FreshApp = () => { const rootRef = useRef(null) useHousesEvents(rootRef) - + useModeChange(rootRef) const bindAll = useGestures() - const showHouseStretchHandles = (houseId: string) => { - if (!rootRef.current) return - - const allHouseTransformGroups = pipe( - rootRef.current.children, - A.filter(isHouseTransformsGroup) - ) - - pipe( - allHouseTransformGroups, - A.findFirst((x) => x.userData.houseId === houseId), - O.map((houseTransformsGroup) => - pipe( - houseTransformsGroup, - findAllGuardDown(isStretchHandleGroup) - ).forEach((handleGroup) => { - setVisibleAndRaycast(handleGroup) - }) - ) - ) - } - - const hideAllHandles = () => { - rootRef.current?.traverse((node) => { - if ( - node.userData.type === UserDataTypeEnum.Enum.StretchHandleGroup || - node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup - ) { - setInvisibleNoRaycast(node) - } - }) - } - - const { houseLevelIndexToCutHeight, setYCut } = - useClippingPlaneHelpers(rootRef) - - const showHouseRotateHandles = (houseId: string) => { - if (!rootRef.current) return - - const allHouseTransformGroups = pipe( - rootRef.current.children, - A.filter(isHouseTransformsGroup) - ) - - pipe( - allHouseTransformGroups, - A.findFirst((x) => x.userData.houseId === houseId), - O.map((houseTransformsGroup) => - houseTransformsGroup.traverse((node) => { - if (node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup) { - setVisibleAndRaycast(node) - } - }) - ) - ) - } - - const f = () => { - if (!rootRef.current) return - - const { houseId, levelIndex, mode } = siteCtx - const { selected } = scope - - const { buildingMode, levelMode, siteMode } = getModeBools() - - console.log(`hide all handles`) - hideAllHandles() - - const allHouseTransformGroups = pipe( - rootRef.current.children, - A.filter(isHouseTransformsGroup) - ) - - pipe( - allHouseTransformGroups, - A.findFirst((x) => x.userData.houseId === houseId), - O.map((houseTransformsGroup) => { - const { houseId } = getActiveHouseUserData(houseTransformsGroup) - setYCut(houseId, BIG_CLIP_NUMBER) - }) - ) - - if (houseId && (buildingMode || levelMode)) { - console.log("show stretch handles") - showHouseStretchHandles(houseId) - } - - if (levelMode) { - if (houseId !== null && levelIndex !== null) { - pipe( - houseLevelIndexToCutHeight(houseId, levelIndex), - O.map((cutHeight) => { - setYCut(houseId, cutHeight) - }) - ) - } - } - - if (siteMode) { - if (selected !== null) { - const { houseId } = selected - showHouseRotateHandles(houseId) - } - } - - invalidate() - } - - useSubscribeKey(scope, "selected", f) - useModeChangeListener(f) - return ( diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 29aaeace..249f404b 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -15,7 +15,7 @@ import userDB from "../../../db/user" import { A, O } from "../../../utils/functions" import { yAxis } from "../../../utils/three" import { - columnSorter, + sortColumnsByIndex, findAllGuardDown, findFirstGuardDown, getActiveHouseUserData, diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index 3ed1bea3..8d7a429f 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -1,16 +1,18 @@ import { invalidate } from "@react-three/fiber" +import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" -import { MutableRefObject, RefObject, useEffect } from "react" +import { nanoid } from "nanoid" +import { RefObject, useEffect } from "react" import { useEvent } from "react-use" import { Group, Vector3 } from "three" import { HouseType } from "../../../../../server/data/houseTypes" +import layoutsDB from "../../../../db/layouts" import userDB, { House } from "../../../../db/user" import { A } from "../../../../utils/functions" -import { setRaycasting, setVisibleAndRaycast } from "../../../../utils/three" -import { nanoid } from "nanoid" import { floor } from "../../../../utils/math" -import { createInitialHouse } from "../helpers/layouts" +import { setRaycasting } from "../../../../utils/three" import useClippingPlaneHelpers from "../helpers/clippingPlanes" +import { createInitialHouse } from "../helpers/layouts" const ADD_HOUSE_INTENT_EVENT = "AddHouseIntentEvent" const ADD_HOUSE_EVENT = "AddHouseEvent" diff --git a/app/design/ui-3d/fresh/events/modeChange.ts b/app/design/ui-3d/fresh/events/modeChange.ts new file mode 100644 index 00000000..b41cb903 --- /dev/null +++ b/app/design/ui-3d/fresh/events/modeChange.ts @@ -0,0 +1,214 @@ +import { invalidate } from "@react-three/fiber" +import { pipe } from "fp-ts/lib/function" +import { A, O, R } from "~/utils/functions" +import { useSubscribeKey } from "~/utils/hooks" +import { + setInvisibleNoRaycast, + setVisibility, + setVisibleAndRaycast, +} from "~/utils/three" +import scope from "~/design/state/scope" +import siteCtx, { + getModeBools, + SiteCtxModeEnum, + useModeChangeListener, +} from "~/design/state/siteCtx" +import useClippingPlaneHelpers from "../helpers/clippingPlanes" +import { BIG_CLIP_NUMBER, createLayoutGroup } from "../helpers/layouts" +import { + findAllGuardDown, + findFirstGuardDown, + getActiveHouseUserData, + getActiveLayoutGroup, +} from "../helpers/sceneQueries" +import { + HouseTransformsGroup, + isHouseLayoutGroup, + isHouseTransformsGroup, + isStretchHandleGroup, + UserDataTypeEnum, +} from "../userData" +import { RefObject } from "react" +import { Group } from "three" +import layoutsDB from "../../../../db/layouts" + +const useModeChange = (rootRef: RefObject) => { + const showHouseStretchHandles = (houseId: string) => { + if (!rootRef.current) return + + const allHouseTransformGroups = pipe( + rootRef.current.children, + A.filter(isHouseTransformsGroup) + ) + + pipe( + allHouseTransformGroups, + A.findFirst((x) => x.userData.houseId === houseId), + O.map((houseTransformsGroup) => + pipe( + houseTransformsGroup, + findAllGuardDown(isStretchHandleGroup) + ).forEach((handleGroup) => { + setVisibleAndRaycast(handleGroup) + }) + ) + ) + } + + const hideAllHandles = () => { + rootRef.current?.traverse((node) => { + if ( + node.userData.type === UserDataTypeEnum.Enum.StretchHandleGroup || + node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup + ) { + setInvisibleNoRaycast(node) + } + }) + } + + const { houseLevelIndexToCutHeight, setYCut } = + useClippingPlaneHelpers(rootRef) + + const showHouseRotateHandles = (houseId: string) => { + if (!rootRef.current) return + + const allHouseTransformGroups = pipe( + rootRef.current.children, + A.filter(isHouseTransformsGroup) + ) + + pipe( + allHouseTransformGroups, + A.findFirst((x) => x.userData.houseId === houseId), + O.map((houseTransformsGroup) => + houseTransformsGroup.traverse((node) => { + if (node.userData.type === UserDataTypeEnum.Enum.RotateHandlesGroup) { + setVisibleAndRaycast(node) + } + }) + ) + ) + } + + const processHandles = () => { + if (!rootRef.current) return + + const { houseId } = siteCtx + const { selected } = scope + const { buildingMode, levelMode, siteMode } = getModeBools() + + hideAllHandles() + + if (houseId && (buildingMode || levelMode)) { + showHouseStretchHandles(houseId) + } + + if (siteMode) { + if (selected !== null) { + const { houseId } = selected + showHouseRotateHandles(houseId) + } + } + + invalidate() + } + + const processClippingPlanes = () => { + if (!rootRef.current) return + + const { houseId, levelIndex } = siteCtx + const { levelMode } = getModeBools() + + const allHouseTransformGroups = pipe( + rootRef.current.children, + A.filter(isHouseTransformsGroup) + ) + + pipe( + allHouseTransformGroups, + A.findFirst((x) => x.userData.houseId === houseId), + O.map((houseTransformsGroup) => { + const { houseId } = getActiveHouseUserData(houseTransformsGroup) + setYCut(houseId, BIG_CLIP_NUMBER) + }) + ) + + if (levelMode) { + if (houseId !== null && levelIndex !== null) { + pipe( + houseLevelIndexToCutHeight(houseId, levelIndex), + O.map((cutHeight) => { + setYCut(houseId, cutHeight) + }) + ) + } + } + } + + useSubscribeKey(scope, "selected", processHandles) + useModeChangeListener(async ({ previous, next }) => { + processHandles() + processClippingPlanes() + if ( + previous === SiteCtxModeEnum.Enum.SITE && + next === SiteCtxModeEnum.Enum.BUILDING + ) { + // house Id + // get the stretch x stuff out of the db? + // maybe clean out old stuff + // ultimately pop some alternative invisible layouts in there! + + const { houseId } = siteCtx + + if (!houseId) return + + const dbResult = await layoutsDB.altSectionTypeLayouts.get(houseId) + + if (!dbResult) return + + if (!rootRef.current) return + + pipe( + rootRef.current, + findFirstGuardDown((x): x is HouseTransformsGroup => { + if (!isHouseTransformsGroup(x)) return false + if (x.userData.houseId !== houseId) return false + return true + }), + O.map((houseTransformsGroup) => { + const { systemId } = getActiveHouseUserData(houseTransformsGroup) + + pipe( + dbResult.altSectionTypeLayouts, + R.map(({ layout, dnas }) => { + createLayoutGroup({ + systemId, + houseId, + houseLayout: layout, + dnas, + })().then((layoutGroup) => { + setVisibility(layoutGroup, false) + + // remove same dnas ones + pipe( + houseTransformsGroup, + findAllGuardDown(isHouseLayoutGroup), + A.filter( + (x) => x.userData.dnas.toString() === dnas.toString() + ) + ).forEach((layoutGroup) => { + console.log(`removing ${layoutGroup.userData.sectionType}`) + layoutGroup.removeFromParent() + }) + houseTransformsGroup.add(layoutGroup) + console.log(houseTransformsGroup.children) + }) + }) + ) + }) + ) + } + }) +} + +export default useModeChange diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 8b557356..b63fd8f5 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,11 +1,13 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" -import { A, someOrError, T } from "../../../../utils/functions" +import { A, O, pipeLog, someOrError, T } from "../../../../utils/functions" +import { abs } from "../../../../utils/math" import { addDebugLineAtX, addDebugLineAtZ, setInvisibleNoRaycast, + setVisibility, setVisibleAndRaycast, yAxis, } from "../../../../utils/three" @@ -22,6 +24,7 @@ import { getSortedVisibleColumnGroups, getVisibleColumnGroups, handleColumnGroupParentQuery, + sortLayoutGroupsByWidth, } from "../helpers/sceneQueries" import { ColumnGroup, @@ -234,7 +237,7 @@ const useOnDragStretch = () => { ) const distance = distanceVector.z - handleColumnGroup.position.set(0, 0, handleGroupZ0 + distance) + handleColumnGroup.position.setZ(handleGroupZ0 + distance) // back side if (direction === 1) { @@ -252,9 +255,6 @@ const useOnDragStretch = () => { endColumnGroup.userData.columnIndex - 1 stretchZProgressDataRef.current.fenceIndex++ - // layoutGroup.userData.length += - // nextFence.columnGroup.userData.length - if (nextFence.z < maxLength) { addVanilla(direction) } @@ -273,9 +273,6 @@ const useOnDragStretch = () => { stretchZProgressDataRef.current.fenceIndex-- lastVisibleFence.columnGroup.userData.columnIndex = -1 - // layoutGroup.userData.length += - // lastVisibleFence.columnGroup.userData.length - endColumnGroup.userData.columnIndex-- } } @@ -303,9 +300,6 @@ const useOnDragStretch = () => { nextFence.columnGroup.userData.columnIndex = 1 - // layoutGroup.userData.length += - // nextFence.columnGroup.userData.length - stretchZProgressDataRef.current.fenceIndex++ // naive @@ -324,9 +318,6 @@ const useOnDragStretch = () => { if (realDistance > lastVisibleFence.z) { setInvisibleNoRaycast(lastVisibleFence.columnGroup) - // layoutGroup.userData.length -= - // lastVisibleFence.columnGroup.userData.length - lastVisibleFence.columnGroup.userData.columnIndex = -1 pipe( @@ -411,9 +402,13 @@ const useOnDragStretch = () => { const stretchXData = useRef<{ point0: Vector3 + handleGroupX0: number houseTransformsGroup: HouseTransformsGroup handleGroup: StretchHandleGroup otherSideHandleGroup: StretchHandleGroup + fences: FenceX[] + fenceIndex: number + lastDistance: number } | null>(null) const onDragStretchX = { @@ -448,29 +443,49 @@ const useOnDragStretch = () => { someOrError(`other side handle group not found`) ) + let fenceIndex = 0 + const fences = pipe( - otherLayoutGroups, - A.map((x) => (x.userData.width / 2) * side) + [activeLayoutGroup, ...otherLayoutGroups], + sortLayoutGroupsByWidth, + A.mapWithIndex((i, layoutGroup): FenceX => { + if (layoutGroup.uuid === activeLayoutGroup.uuid) fenceIndex = i + return { + layoutGroup, + x: + (layoutGroup.userData.width - activeLayoutGroup.userData.width) / + 2, + } + }) ) - console.log(fences) - - fences.forEach((fence) => { - console.log(`adding fence`) - addDebugLineAtX(activeLayoutGroup, fence) - }) - stretchXData.current = { handleGroup, otherSideHandleGroup, houseTransformsGroup, point0: point, + fences, + handleGroupX0: handleGroup.position.x, + fenceIndex, + lastDistance: 0, } }, mid: () => { if (!stretchXData.current) return - const { point0, houseTransformsGroup } = stretchXData.current + const { + point0, + houseTransformsGroup, + handleGroup, + otherSideHandleGroup, + handleGroupX0, + fences, + fenceIndex, + lastDistance, + } = stretchXData.current + + const { side } = handleGroup.userData + const [x1, z1] = pointer.xz const distanceVector = new Vector3(x1, 0, z1).sub(point0) distanceVector.applyAxisAngle( @@ -478,7 +493,39 @@ const useOnDragStretch = () => { -houseTransformsGroup.rotation.y ) const distance = distanceVector.x - // handleGroup.position.set(handleGroupX0 + distance, 0) + handleGroup.position.setX(handleGroupX0 + distance) + + const adjustedDistance = side * distance + const adjustedLastDistance = side * lastDistance + + if (adjustedDistance > adjustedLastDistance) { + pipe( + fences, + A.lookup(fenceIndex + 1), + O.map(({ x }) => { + if (adjustedDistance >= x) { + // swap it + setVisibility(fences[fenceIndex + 1].layoutGroup, true) + setVisibility(fences[fenceIndex].layoutGroup, false) + stretchXData.current!.fenceIndex++ + } + }) + ) + } else if (adjustedDistance < adjustedLastDistance) { + pipe( + fences, + A.lookup(fenceIndex - 1), + O.map(({ x }) => { + if (adjustedDistance >= x) { + setVisibility(fences[fenceIndex].layoutGroup, true) + setVisibility(fences[fenceIndex + 1].layoutGroup, false) + stretchXData.current!.fenceIndex-- + } + }) + ) + // tbc + } + // if next fence up }, last: () => {}, } diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 7763f70d..02660c36 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -14,13 +14,20 @@ import { UserDataTypeEnum, } from "../userData" -export const columnSorter = A.sort( +export const sortColumnsByIndex = A.sort( pipe( Num.Ord, Ord.contramap((x: ColumnGroup) => x.userData.columnIndex) ) ) +export const sortLayoutGroupsByWidth = A.sort( + pipe( + Num.Ord, + Ord.contramap((x: HouseLayoutGroup) => x.userData.width) + ) +) + export const findFirstGuardUp = (guard: (o: Object3D) => o is T) => (object: Object3D): O.Option => { @@ -196,12 +203,12 @@ export const getVisibleColumnGroups = ( if (x.visible === false) return false return true }), - columnSorter + sortColumnsByIndex ) export const getSortedVisibleColumnGroups = flow( getVisibleColumnGroups, - columnSorter + sortColumnsByIndex ) export const getLayoutGroupColumnIndices = flow( diff --git a/app/design/ui-3d/fresh/helpers/stretchZ.ts b/app/design/ui-3d/fresh/helpers/stretchZ.ts index 10572ed6..12638773 100644 --- a/app/design/ui-3d/fresh/helpers/stretchZ.ts +++ b/app/design/ui-3d/fresh/helpers/stretchZ.ts @@ -8,7 +8,7 @@ import { } from "../userData" import { createColumnGroup, getVanillaColumn } from "./layouts" import { - columnSorter, + sortColumnsByIndex, getActiveHouseUserData, getActiveLayoutGroup, getLayoutGroupColumnGroups, @@ -69,7 +69,7 @@ export const insertVanillaColumn = ( } else if (direction === -1) { pipe( getLayoutGroupColumnGroups(layoutGroup), - columnSorter, + sortColumnsByIndex, ([startColumnGroup, ...restColumnGroups]) => { for (let columnGroup of restColumnGroups) { columnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) diff --git a/app/design/ui-3d/fresh/shapes/rotateHandles.ts b/app/design/ui-3d/fresh/shapes/rotateHandles.ts index 0b7d4625..81b41d0a 100644 --- a/app/design/ui-3d/fresh/shapes/rotateHandles.ts +++ b/app/design/ui-3d/fresh/shapes/rotateHandles.ts @@ -54,6 +54,7 @@ const createRotateHandles = ({ const handleGroup = new Group() handleGroup.add(circleMesh1, planeMesh1, circleMesh2, planeMesh2) + handleGroup.position.setY(0.01) const groupUserData: RotateHandlesGroupUserData = { type: UserDataTypeEnum.Enum.RotateHandlesGroup, diff --git a/app/utils/three.ts b/app/utils/three.ts index 49237501..ea10ff9f 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -233,10 +233,8 @@ export const setInvisibleButRaycast = (object: Object3D) => { export const setVisibility = (object: Object3D, bool: boolean) => { if (bool) { - console.log(`setting visible`) setVisibleAndRaycast(object) } else { - console.log(`setting invisible`) setInvisibleNoRaycast(object) } } diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 8a31731c..06a12b9b 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -439,7 +439,6 @@ export const columnLayoutToDnas = ( ) as string[] if (!isSSR()) { - // CONT liveQuery(async () => { const houses = await userDB.houses.toArray() const sectionTypes = await systemsDB.sectionTypes.toArray() @@ -447,7 +446,6 @@ if (!isSSR()) { return { houses, sectionTypes } }).subscribe(async ({ houses, sectionTypes }) => { // for each house, for each section type - for (const house of houses) { const { systemId, dnas, houseId: houseId } = house @@ -633,13 +631,17 @@ if (!isSSR()) { ( acc: Record< string, - { layout: ColumnLayout; sectionType: SectionType } + { layout: ColumnLayout; sectionType: SectionType; dnas: string[] } >, { layout, sectionType } ) => { return { ...acc, - [sectionType.code]: { layout, sectionType }, + [sectionType.code]: { + layout, + sectionType, + dnas: columnLayoutToDnas(layout), + }, } } ) From 66d72b0c3123fdb389406267a192b1572330da68 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 07:47:30 +0100 Subject: [PATCH 114/132] wip separate layout helpers --- app/design/ui-3d/fresh/events/houses.ts | 4 +- app/design/ui-3d/fresh/events/modeChange.ts | 5 +- app/design/ui-3d/fresh/gestures/stretch.ts | 10 +- app/design/ui-3d/fresh/helpers/layouts.ts | 576 ------------------ .../ui-3d/fresh/helpers/sceneChanges.ts | 2 +- .../ui-3d/fresh/helpers/sceneQueries.ts | 6 +- app/design/ui-3d/fresh/helpers/stretchZ.ts | 149 ----- app/design/ui-3d/fresh/scene/columnGroup.ts | 199 ++++++ .../ui-3d/fresh/scene/houseLayoutGroup.ts | 249 ++++++++ .../ui-3d/fresh/scene/houseTransformsGroup.ts | 52 ++ app/design/ui-3d/fresh/scene/moduleGroup.ts | 121 ++++ app/design/ui-3d/fresh/userData.ts | 2 +- 12 files changed, 633 insertions(+), 742 deletions(-) delete mode 100644 app/design/ui-3d/fresh/helpers/layouts.ts delete mode 100644 app/design/ui-3d/fresh/helpers/stretchZ.ts create mode 100644 app/design/ui-3d/fresh/scene/columnGroup.ts create mode 100644 app/design/ui-3d/fresh/scene/houseLayoutGroup.ts create mode 100644 app/design/ui-3d/fresh/scene/houseTransformsGroup.ts create mode 100644 app/design/ui-3d/fresh/scene/moduleGroup.ts diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index 8d7a429f..1b743ea1 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -12,7 +12,7 @@ import { A } from "../../../../utils/functions" import { floor } from "../../../../utils/math" import { setRaycasting } from "../../../../utils/three" import useClippingPlaneHelpers from "../helpers/clippingPlanes" -import { createInitialHouse } from "../helpers/layouts" +import { createHouseTransformsGroup } from "../scene/houseTransformsGroup" const ADD_HOUSE_INTENT_EVENT = "AddHouseIntentEvent" const ADD_HOUSE_EVENT = "AddHouseEvent" @@ -66,7 +66,7 @@ export const useHousesEvents = (rootRef: RefObject) => { houseTypeId, } = house - const houseGroup = await createInitialHouse({ + const houseGroup = await createHouseTransformsGroup({ systemId, houseId, dnas, diff --git a/app/design/ui-3d/fresh/events/modeChange.ts b/app/design/ui-3d/fresh/events/modeChange.ts index b41cb903..e7c6ac84 100644 --- a/app/design/ui-3d/fresh/events/modeChange.ts +++ b/app/design/ui-3d/fresh/events/modeChange.ts @@ -14,7 +14,6 @@ import siteCtx, { useModeChangeListener, } from "~/design/state/siteCtx" import useClippingPlaneHelpers from "../helpers/clippingPlanes" -import { BIG_CLIP_NUMBER, createLayoutGroup } from "../helpers/layouts" import { findAllGuardDown, findFirstGuardDown, @@ -31,6 +30,8 @@ import { import { RefObject } from "react" import { Group } from "three" import layoutsDB from "../../../../db/layouts" +import { BIG_CLIP_NUMBER } from "../scene/houseTransformsGroup" +import { createHouseLayoutGroup } from "../scene/houseLayoutGroup" const useModeChange = (rootRef: RefObject) => { const showHouseStretchHandles = (houseId: string) => { @@ -181,7 +182,7 @@ const useModeChange = (rootRef: RefObject) => { pipe( dbResult.altSectionTypeLayouts, R.map(({ layout, dnas }) => { - createLayoutGroup({ + createHouseLayoutGroup({ systemId, houseId, houseLayout: layout, diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index b63fd8f5..02170db2 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -1,11 +1,8 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Object3D, Vector3 } from "three" -import { A, O, pipeLog, someOrError, T } from "../../../../utils/functions" -import { abs } from "../../../../utils/math" +import { A, O, someOrError, T } from "../../../../utils/functions" import { - addDebugLineAtX, - addDebugLineAtZ, setInvisibleNoRaycast, setVisibility, setVisibleAndRaycast, @@ -14,9 +11,7 @@ import { import pointer from "../../../state/pointer" import { updateLayoutGroupLength } from "../dimensions" import { dispatchOutline } from "../events/outlines" -import { createColumnGroup, splitColumnGroups } from "../helpers/layouts" import { - findAllGuardDown, getActiveHouseUserData, getActiveLayoutGroup, getHouseTransformsGroupUp, @@ -26,14 +21,13 @@ import { handleColumnGroupParentQuery, sortLayoutGroupsByWidth, } from "../helpers/sceneQueries" +import { createColumnGroup, splitColumnGroups } from "../scene/columnGroup" import { ColumnGroup, HouseLayoutGroup, HouseTransformsGroup, isStretchHandleGroup, StretchHandleGroup, - StretchHandleMesh, - StretchHandleMeshUserData, } from "../userData" const TMP_MAX_LENGTH = 10 diff --git a/app/design/ui-3d/fresh/helpers/layouts.ts b/app/design/ui-3d/fresh/helpers/layouts.ts deleted file mode 100644 index dcf6b173..00000000 --- a/app/design/ui-3d/fresh/helpers/layouts.ts +++ /dev/null @@ -1,576 +0,0 @@ -import { liveQuery } from "dexie" -import { cartesian } from "fp-ts-std/Array" -import { pipe } from "fp-ts/lib/function" -import { - BufferGeometry, - BufferGeometryLoader, - Group, - Matrix3, - Mesh, - Plane, - Vector3, -} from "three" -import { OBB } from "three-stdlib" -import { Module } from "../../../../../server/data/modules" -import layoutsDB, { - ColumnLayout, - getHouseLayoutsKey, - getVanillaColumnsKey, - GridGroup, - VanillaColumn, - VanillaColumnsKey, -} from "../../../../db/layouts" -import { A, combineGuards, O, R, S, T } from "../../../../utils/functions" -import { addDebugLineAtZ, setVisibility, yAxis } from "../../../../utils/three" -import { getLayoutsWorker } from "../../../../workers" -import siteCtx, { getModeBools } from "../../../state/siteCtx" -import { renderOBB } from "../dimensions" -import createRotateHandles from "../shapes/rotateHandles" -import createStretchHandle from "../shapes/stretchHandle" -import { getMaterial } from "../systems" -import { - ColumnGroup, - ColumnGroupUserData, - ElementMeshUserData, - GridGroupUserData, - HouseLayoutGroupUserData, - HouseTransformsGroupUserData, - isRotateHandlesGroup, - isStretchHandleMesh, - ModuleGroupUserData, - UserDataTypeEnum, -} from "../userData" -import { findAllGuardDown } from "./sceneQueries" - -export const DEBUG = false - -export const BIG_CLIP_NUMBER = 999 - -// serialized layout key : column -export let vanillaColumns: Record = {} - -export const getVanillaColumn = ({ - systemId, - levelTypes, -}: VanillaColumnsKey): T.Task => { - const key = getVanillaColumnsKey({ systemId, levelTypes }) - - return pipe( - vanillaColumns, - R.lookup(key), - O.match( - () => { - const layoutsWorker = getLayoutsWorker() - if (!layoutsWorker) throw new Error(`no layouts worker`) - return () => - layoutsWorker.getVanillaColumn({ - systemId, - levelTypes, - }) - }, - (vanillaColumn) => T.of(vanillaColumn) - ) - ) -} - -liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( - (dbVanillaColumns) => { - for (let dbVanillaColumn of dbVanillaColumns) { - const { systemId, levelTypes, vanillaColumn } = dbVanillaColumn - vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] = - vanillaColumn - } - } -) - -const loader = new BufferGeometryLoader() - -// speckle branch url : geometry by ifc tag -export let models: Record> = {} - -const putModel = ({ - geometries, - speckleBranchUrl, -}: { - speckleBranchUrl: string - geometries: any -}) => { - const loadedModels: Record = pipe( - geometries, - R.map((x) => loader.parse(x) as BufferGeometry), - R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { - geometry.computeVertexNormals() - return { - ...acc, - [ifcTag]: geometry, - } - }) - ) - models[speckleBranchUrl] = loadedModels - - return loadedModels -} - -liveQuery(() => layoutsDB.models.toArray()).subscribe((dbModels) => { - for (let { speckleBranchUrl, geometries } of dbModels) { - if (!(speckleBranchUrl in models)) { - putModel({ speckleBranchUrl, geometries }) - } - } -}) - -export const getGeometry = ({ - speckleBranchUrl, - ifcTag, -}: { - speckleBranchUrl: string - ifcTag: string -}) => models[speckleBranchUrl][ifcTag] - -export const createModuleGroup = async ({ - systemId, - houseId, - gridGroupIndex, - module: { speckleBranchUrl, length, dna }, -}: { - systemId: string - houseId: string - gridGroupIndex: number - module: Module -}) => { - const moduleGroup = new Group() - - const processModel = ( - modelGeometriesByIfcTag: Record - ) => { - for (let ifcTag of Object.keys(modelGeometriesByIfcTag)) { - const geometry = getGeometry({ speckleBranchUrl, ifcTag }) - const material = getMaterial({ - systemId, - ifcTag, - houseId, - }) - material.wireframe = false - const mesh = new Mesh(geometry, material) - mesh.castShadow = true - - const elementMeshUserData: ElementMeshUserData = { - type: UserDataTypeEnum.Enum.ElementMesh, - ifcTag, - } - mesh.userData = elementMeshUserData - moduleGroup.add(mesh) - } - } - - await pipe( - models, - R.lookup(speckleBranchUrl), - O.match( - async () => { - const model = await layoutsDB.models.get(speckleBranchUrl) - if (model === undefined) - throw new Error(`no model for ${speckleBranchUrl}`) - const loadedModel = putModel(model) - processModel(loadedModel) - }, - async (loadedModel) => { - processModel(loadedModel) - } - ) - ) - - const moduleGroupUserData: ModuleGroupUserData = { - type: UserDataTypeEnum.Enum.ModuleGroup, - gridGroupIndex, - dna, - length, - } - - moduleGroup.userData = moduleGroupUserData - - return moduleGroup -} - -export const createColumnGroup = - ({ - systemId, - houseId, - gridGroups, - columnIndex, - startColumn = false, - endColumn = false, - }: { - systemId: string - houseId: string - gridGroups: GridGroup[] - columnIndex: number - startColumn?: boolean - endColumn?: boolean - }): T.Task => - async () => { - const columnGroup = new Group() - - for (let { modules, y, levelIndex } of gridGroups) { - const gridGroup = new Group() - let length = 0 - - for (let { z, module, gridGroupIndex } of modules) { - const moduleGroup = await createModuleGroup({ - systemId, - houseId, - module, - gridGroupIndex, - }) - - moduleGroup.scale.set(1, 1, endColumn ? 1 : -1) - moduleGroup.position.set( - 0, - 0, - endColumn ? z + module.length / 2 : z - module.length / 2 - ) - - gridGroup.position.setY(y) - gridGroup.add(moduleGroup) - - length += module.length - } - - const gridGroupUserData: GridGroupUserData = { - type: UserDataTypeEnum.Enum.GridGroup, - levelIndex, - length, - height: modules[0].module.height, - } - gridGroup.userData = gridGroupUserData - - columnGroup.add(gridGroup) - } - - const columnGroupUserData: ColumnGroupUserData = { - type: UserDataTypeEnum.Enum.ColumnGroup, - columnIndex, - length: gridGroups[0].length, - startColumn, - endColumn, - } - - columnGroup.userData = columnGroupUserData - - return columnGroup as ColumnGroup - } - -export const createColumnGroups = ({ - systemId, - houseId, - houseLayout, -}: { - systemId: string - houseId: string - houseLayout: ColumnLayout -}): T.Task => - pipe( - houseLayout, - A.traverseWithIndex(T.ApplicativeSeq)( - (i, { gridGroups, z, columnIndex }) => { - const startColumn = i === 0 - const endColumn = i === houseLayout.length - 1 - - const task = createColumnGroup({ - systemId, - houseId, - gridGroups, - startColumn, - endColumn, - columnIndex, - }) - - return pipe( - task, - T.map((columnGroup) => { - columnGroup.position.set(0, 0, z) - return columnGroup - }) - ) - } - ) - ) - -export let houseLayouts: Record = {} - -liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( - (dbHouseLayouts) => { - for (let { systemId, dnas, layout } of dbHouseLayouts) { - houseLayouts[getHouseLayoutsKey({ systemId, dnas })] = layout - } - } -) - -export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => - pipe( - columnLayout, - A.head, - O.map(({ gridGroups }) => - pipe( - gridGroups, - A.map(({ levelType }) => levelType) - ) - ), - O.getOrElse((): string[] => []) - ) - -export const createLayoutGroup = ({ - systemId, - houseId, - dnas, - houseLayout, -}: { - systemId: string - houseId: string - dnas: string[] - houseLayout: ColumnLayout -}) => - pipe( - createColumnGroups({ - systemId, - houseId, - houseLayout, - }), - T.chain((columnGroups) => { - const layoutGroup = new Group() - - const columnCount = columnGroups.length - - const sectionType = - houseLayout[0].gridGroups[0].modules[0].module.structuredDna.sectionType - - const width = houseLayout[0].gridGroups[0].modules[0].module.width - const height = houseLayout[0].gridGroups.reduce( - (acc, v) => acc + v.modules[0].module.height, - 0 - ) - const length = columnGroups.reduce( - (acc, columnGroup) => acc + columnGroup.userData.length, - 0 - ) - const obb = new OBB() - const levelTypes = houseLayoutToLevelTypes(houseLayout) - - return pipe( - getVanillaColumn({ systemId, levelTypes }), - T.map((vanillaColumn) => { - const userData: HouseLayoutGroupUserData = { - type: UserDataTypeEnum.Enum.HouseLayoutGroup, - dnas, - houseLayout, - columnCount, - sectionType, - levelTypes, - width, - height, - length, - obb, - modifiedMaterials: {}, - vanillaColumn, - } - - layoutGroup.position.setZ(-length / 2) - layoutGroup.add(...columnGroups) - - const { startColumnGroup, endColumnGroup } = - splitColumnGroups(columnGroups) - - const removeAllHandles = () => { - pipe( - layoutGroup, - findAllGuardDown( - combineGuards(isRotateHandlesGroup, isStretchHandleMesh) - ) - ).forEach((x) => { - x.removeFromParent() - }) - } - - const userDataHandler: ProxyHandler = { - set: function (target: any, prop: any, value: any) { - if (prop === "length") { - const oldLength = target[prop] - const newLength = value - target[prop] = value - - console.log( - `doing stuff, oldLength: ${oldLength}; newLength: ${newLength}` - ) - - layoutGroup.position.setZ(-newLength / 2) - - layoutGroup.parent?.position.add( - new Vector3(0, 0, (newLength - oldLength) / 2).applyAxisAngle( - yAxis, - layoutGroup.parent.rotation.y - ) - ) - - const houseTransformsGroup = layoutGroup.parent! - - const { x, y, z } = houseTransformsGroup.position - - const center = new Vector3(x, y + height / 2, z) - const halfSize = new Vector3( - width / 2, - height / 2, - newLength / 2 - ) - const rotation = new Matrix3().setFromMatrix4( - houseTransformsGroup.matrix - ) - - layoutGroup.userData.obb.set(center, halfSize, rotation) - - if (DEBUG && houseTransformsGroup.parent) { - renderOBB( - layoutGroup.userData.obb, - houseTransformsGroup.parent - ) - } - - refreshHandles() - } - - return true // Indicate assignment success - }, - } - - layoutGroup.userData = new Proxy(userData, userDataHandler) - - const refreshHandles = () => { - removeAllHandles() - - const { siteMode } = getModeBools(siteCtx.mode) - - const { length: houseLength, width: houseWidth } = - layoutGroup.userData - - const rotateHandles = createRotateHandles({ - houseWidth, - houseLength, - }) - - setVisibility(rotateHandles, siteMode) - - layoutGroup.add(rotateHandles) - - pipe( - [1, -1] as Array<1 | -1>, - cartesian(["x", "z"] as Array<"z" | "x">), - A.map(([axis, side]) => { - const stretchHandleGroup = createStretchHandle({ - axis, - side, - houseLength, - houseWidth, - }) - if (axis === "z") { - if (side === 1) { - endColumnGroup.add(stretchHandleGroup) - } else { - startColumnGroup.add(stretchHandleGroup) - } - } else { - stretchHandleGroup.position.setZ(houseLength / 2) - layoutGroup.add(stretchHandleGroup) - } - setVisibility(stretchHandleGroup, !siteMode) - }) - ) - } - - refreshHandles() - - return layoutGroup - }) - ) - }) - ) - -export const getHouseLayout = ({ - systemId, - dnas, -}: { - systemId: string - dnas: string[] -}): T.Task => - pipe( - houseLayouts, - R.lookup(getHouseLayoutsKey({ systemId, dnas })), - O.match( - (): T.Task => async () => { - const layoutsWorker = getLayoutsWorker() - if (!layoutsWorker) throw new Error(`no layouts worker`) - return await layoutsWorker.getLayout({ - systemId, - dnas, - }) - }, - (houseLayout) => T.of(houseLayout) - ) - ) - -export const createInitialHouse = ({ - systemId, - houseId, - dnas, - friendlyName, - houseTypeId, -}: { - systemId: string - houseId: string - dnas: string[] - friendlyName: string - houseTypeId: string -}): T.Task => - pipe( - getHouseLayout({ systemId, dnas }), - T.chain((houseLayout) => - createLayoutGroup({ houseLayout, dnas, systemId, houseId }) - ), - T.map((layoutGroup) => { - const transformsGroup = new Group() - - const NORMAL_DIRECTION = -1 - - const clippingPlanes: Plane[] = [ - new Plane(new Vector3(NORMAL_DIRECTION, 0, 0), BIG_CLIP_NUMBER), - new Plane(new Vector3(0, NORMAL_DIRECTION, 0), BIG_CLIP_NUMBER), - new Plane(new Vector3(0, 0, NORMAL_DIRECTION), BIG_CLIP_NUMBER), - ] - - const houseTransformsGroupUserData: HouseTransformsGroupUserData = { - type: UserDataTypeEnum.Enum.HouseTransformsGroup, - systemId, - houseId, - clippingPlanes, - friendlyName, - activeChildUuid: layoutGroup.uuid, - houseTypeId, - } - transformsGroup.userData = houseTransformsGroupUserData - transformsGroup.add(layoutGroup) - - return transformsGroup - }) - ) - -export const splitColumnGroups = (columnGroups: ColumnGroup[]) => - pipe( - columnGroups, - A.partition( - ({ userData: { columnIndex } }) => - columnIndex === 0 || columnIndex === columnGroups.length - 1 - ), - ({ left: midColumnGroups, right: [startColumnGroup, endColumnGroup] }) => ({ - startColumnGroup, - endColumnGroup, - midColumnGroups, - }) - ) diff --git a/app/design/ui-3d/fresh/helpers/sceneChanges.ts b/app/design/ui-3d/fresh/helpers/sceneChanges.ts index 0bf047f8..d16dc1db 100644 --- a/app/design/ui-3d/fresh/helpers/sceneChanges.ts +++ b/app/design/ui-3d/fresh/helpers/sceneChanges.ts @@ -17,7 +17,7 @@ export const debugNextLayout = (houseTransformsGroup: HouseTransformsGroup) => { O.map((nextLayout) => { setVisibleAndRaycast(nextLayout) setInvisibleNoRaycast(activeLayoutGroup) - houseTransformsGroup.userData.activeChildUuid = nextLayout.uuid + houseTransformsGroup.userData.activeLayoutUuid = nextLayout.uuid }) ) } diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 02660c36..243f6dbb 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -147,7 +147,7 @@ export const getActiveHouseUserData = (houseTransformsGroup: Object3D) => A.findFirstMap((x) => x.uuid === (houseTransformsGroup.userData as HouseTransformsGroupUserData) - .activeChildUuid + .activeLayoutUuid ? O.some({ ...(houseTransformsGroup.userData as HouseTransformsGroupUserData), ...(x.userData as HouseLayoutGroupUserData), @@ -169,7 +169,7 @@ export const getActiveLayoutGroup = ( houseTransformsGroup.children, A.findFirst( (x): x is HouseLayoutGroup => - x.uuid === houseTransformsGroup.userData.activeChildUuid + x.uuid === houseTransformsGroup.userData.activeLayoutUuid ), someOrError(`getActiveLayoutGroup failure`) ) @@ -181,7 +181,7 @@ export const getPartitionedLayoutGroups = ( houseTransformsGroup, getLayoutGroups, A.partition( - (x) => x.uuid === houseTransformsGroup.userData.activeChildUuid + (x) => x.uuid === houseTransformsGroup.userData.activeLayoutUuid ), ({ left: otherLayoutGroups, right: [activeLayoutGroup] }) => ({ activeLayoutGroup, diff --git a/app/design/ui-3d/fresh/helpers/stretchZ.ts b/app/design/ui-3d/fresh/helpers/stretchZ.ts deleted file mode 100644 index 12638773..00000000 --- a/app/design/ui-3d/fresh/helpers/stretchZ.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { pipe } from "fp-ts/lib/function" -import { Group, Object3D, Vector3 } from "three" -import { A, Num, Ord, T } from "../../../../utils/functions" -import { - decrementColumnCount, - HouseTransformsGroup, - incrementColumnCount, -} from "../userData" -import { createColumnGroup, getVanillaColumn } from "./layouts" -import { - sortColumnsByIndex, - getActiveHouseUserData, - getActiveLayoutGroup, - getLayoutGroupColumnGroups, -} from "./sceneQueries" - -export const insertVanillaColumn = ( - houseTransformsGroup: HouseTransformsGroup, - direction: 1 | -1 -) => { - const { systemId, houseId, levelTypes, columnCount } = - getActiveHouseUserData(houseTransformsGroup) - - return pipe( - getVanillaColumn({ systemId, levelTypes }), - T.chain(({ gridGroups }) => - pipe( - createColumnGroup({ systemId, houseId, gridGroups, columnIndex: -1 }) - ) - ), - T.map((vanillaColumnGroup) => { - const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) - const vanillaColumnLength = vanillaColumnGroup.userData.length - - if (direction === 1) { - pipe( - // get column groups - getLayoutGroupColumnGroups(layoutGroup), - // last two - A.filter((x) => x.userData.columnIndex >= columnCount - 2), - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), - ([penultimateColumnGroup, endColumnGroup]) => { - // put vanilla column group in place - vanillaColumnGroup.position.setZ( - penultimateColumnGroup.position.z + - penultimateColumnGroup.userData.length / 2 + - vanillaColumnLength / 2 - ) - // add it - layoutGroup.add(vanillaColumnGroup) - - // set its index - vanillaColumnGroup.userData.columnIndex = - penultimateColumnGroup.userData.columnIndex + 1 - - // adjust end's index - endColumnGroup.userData.columnIndex++ - - incrementColumnCount(layoutGroup) - // n.b. you're already adjusting the endColumnGroup's - // position in the handler - } - ) - } else if (direction === -1) { - pipe( - getLayoutGroupColumnGroups(layoutGroup), - sortColumnsByIndex, - ([startColumnGroup, ...restColumnGroups]) => { - for (let columnGroup of restColumnGroups) { - columnGroup.position.add(new Vector3(0, 0, vanillaColumnLength)) - columnGroup.userData.columnIndex++ - } - - vanillaColumnGroup.userData.columnIndex = 1 - vanillaColumnGroup.position.setZ( - startColumnGroup.position.z + - startColumnGroup.userData.length + - vanillaColumnLength / 2 - ) - layoutGroup.add(vanillaColumnGroup) - - incrementColumnCount(layoutGroup) - } - ) - } - }) - ) -} - -export const subtractPenultimateColumn = ( - houseTransformsGroup: HouseTransformsGroup, - direction: 1 | -1 -) => { - const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) - const columnGroups = getLayoutGroupColumnGroups(layoutGroup) - const { columnCount } = getActiveHouseUserData(houseTransformsGroup) - - if (columnCount <= 3) return - - if (direction === 1) { - pipe( - columnGroups, - A.filter((x) => x.userData.columnIndex >= columnCount - 2), - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), - ([penultimateColumnGroup, endColumnGroup]) => { - endColumnGroup.position.sub( - new Vector3(0, 0, penultimateColumnGroup.userData.length) - ) - - layoutGroup.remove(penultimateColumnGroup) - - decrementColumnCount(layoutGroup) - endColumnGroup.userData.columnIndex-- - } - ) - } else if (direction === -1) { - pipe( - columnGroups, - A.sort( - pipe( - Num.Ord, - Ord.contramap((x: Object3D) => x.userData.columnIndex) - ) - ), - ([_, secondColumnGroup, ...restColumnGroups]) => { - const subV = new Vector3(0, 0, secondColumnGroup.userData.length) - - restColumnGroups.forEach((columnGroup) => { - columnGroup.position.sub(subV) - columnGroup.userData.columnIndex-- - }) - - layoutGroup.remove(secondColumnGroup) - - decrementColumnCount(layoutGroup) - } - ) - } -} diff --git a/app/design/ui-3d/fresh/scene/columnGroup.ts b/app/design/ui-3d/fresh/scene/columnGroup.ts new file mode 100644 index 00000000..d7d64658 --- /dev/null +++ b/app/design/ui-3d/fresh/scene/columnGroup.ts @@ -0,0 +1,199 @@ +import { liveQuery } from "dexie" +import { cartesian } from "fp-ts-std/Array" +import { pipe } from "fp-ts/lib/function" +import { + BufferGeometry, + BufferGeometryLoader, + Group, + Matrix3, + Mesh, + Plane, + Vector3, +} from "three" +import { OBB } from "three-stdlib" +import { Module } from "../../../../../server/data/modules" +import layoutsDB, { + ColumnLayout, + getHouseLayoutsKey, + getVanillaColumnsKey, + GridGroup, + VanillaColumn, + VanillaColumnsKey, +} from "../../../../db/layouts" +import { A, combineGuards, O, R, S, T } from "../../../../utils/functions" +import { addDebugLineAtZ, setVisibility, yAxis } from "../../../../utils/three" +import { getLayoutsWorker } from "../../../../workers" +import siteCtx, { getModeBools } from "../../../state/siteCtx" +import { renderOBB } from "../dimensions" +import createRotateHandles from "../shapes/rotateHandles" +import createStretchHandle from "../shapes/stretchHandle" +import { getMaterial } from "../systems" +import { + ColumnGroup, + ColumnGroupUserData, + ElementMeshUserData, + GridGroupUserData, + HouseLayoutGroupUserData, + HouseTransformsGroupUserData, + isRotateHandlesGroup, + isStretchHandleMesh, + ModuleGroupUserData, + UserDataTypeEnum, +} from "../userData" +import { findAllGuardDown } from "../helpers/sceneQueries" +import { createModuleGroup } from "./moduleGroup" + +// serialized layout key : column +export let vanillaColumns: Record = {} + +liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( + (dbVanillaColumns) => { + for (let dbVanillaColumn of dbVanillaColumns) { + const { systemId, levelTypes, vanillaColumn } = dbVanillaColumn + vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] = + vanillaColumn + } + } +) + +export const getVanillaColumn = ({ + systemId, + levelTypes, +}: VanillaColumnsKey): T.Task => { + const key = getVanillaColumnsKey({ systemId, levelTypes }) + + return pipe( + vanillaColumns, + R.lookup(key), + O.match( + () => { + const layoutsWorker = getLayoutsWorker() + if (!layoutsWorker) throw new Error(`no layouts worker`) + return () => + layoutsWorker.getVanillaColumn({ + systemId, + levelTypes, + }) + }, + (vanillaColumn) => T.of(vanillaColumn) + ) + ) +} + +export const splitColumnGroups = (columnGroups: ColumnGroup[]) => + pipe( + columnGroups, + A.partition( + ({ userData: { columnIndex } }) => + columnIndex === 0 || columnIndex === columnGroups.length - 1 + ), + ({ left: midColumnGroups, right: [startColumnGroup, endColumnGroup] }) => ({ + startColumnGroup, + endColumnGroup, + midColumnGroups, + }) + ) + +export const createColumnGroup = + ({ + systemId, + houseId, + gridGroups, + columnIndex, + startColumn = false, + endColumn = false, + }: { + systemId: string + houseId: string + gridGroups: GridGroup[] + columnIndex: number + startColumn?: boolean + endColumn?: boolean + }): T.Task => + async () => { + const columnGroup = new Group() + + for (let { modules, y, levelIndex } of gridGroups) { + const gridGroup = new Group() + let length = 0 + + for (let { z, module, gridGroupIndex } of modules) { + const moduleGroup = await createModuleGroup({ + systemId, + houseId, + module, + gridGroupIndex, + }) + + moduleGroup.scale.set(1, 1, endColumn ? 1 : -1) + moduleGroup.position.set( + 0, + 0, + endColumn ? z + module.length / 2 : z - module.length / 2 + ) + + gridGroup.position.setY(y) + gridGroup.add(moduleGroup) + + length += module.length + } + + const gridGroupUserData: GridGroupUserData = { + type: UserDataTypeEnum.Enum.GridGroup, + levelIndex, + length, + height: modules[0].module.height, + } + gridGroup.userData = gridGroupUserData + + columnGroup.add(gridGroup) + } + + const columnGroupUserData: ColumnGroupUserData = { + type: UserDataTypeEnum.Enum.ColumnGroup, + columnIndex, + length: gridGroups[0].length, + startColumn, + endColumn, + } + + columnGroup.userData = columnGroupUserData + + return columnGroup as ColumnGroup + } + +export const createColumnGroups = ({ + systemId, + houseId, + houseLayout, +}: { + systemId: string + houseId: string + houseLayout: ColumnLayout +}): T.Task => + pipe( + houseLayout, + A.traverseWithIndex(T.ApplicativeSeq)( + (i, { gridGroups, z, columnIndex }) => { + const startColumn = i === 0 + const endColumn = i === houseLayout.length - 1 + + const task = createColumnGroup({ + systemId, + houseId, + gridGroups, + startColumn, + endColumn, + columnIndex, + }) + + return pipe( + task, + T.map((columnGroup) => { + columnGroup.position.set(0, 0, z) + return columnGroup + }) + ) + } + ) + ) diff --git a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts new file mode 100644 index 00000000..f0f4750a --- /dev/null +++ b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts @@ -0,0 +1,249 @@ +import { liveQuery } from "dexie" +import { cartesian } from "fp-ts-std/Array" +import { pipe } from "fp-ts/lib/function" +import { Group, Matrix3, Vector3 } from "three" +import { OBB } from "three-stdlib" +import layoutsDB, { + ColumnLayout, + getHouseLayoutsKey, +} from "../../../../db/layouts" +import { A, combineGuards, O, R, T } from "../../../../utils/functions" +import { setVisibility, yAxis } from "../../../../utils/three" +import { getLayoutsWorker } from "../../../../workers" +import siteCtx, { getModeBools } from "../../../state/siteCtx" +import { renderOBB } from "../dimensions" +import { findAllGuardDown } from "../helpers/sceneQueries" +import createRotateHandles from "../shapes/rotateHandles" +import createStretchHandle from "../shapes/stretchHandle" +import { + HouseLayoutGroupUserData, + isRotateHandlesGroup, + isStretchHandleMesh, + UserDataTypeEnum, +} from "../userData" +import { + createColumnGroups, + getVanillaColumn, + splitColumnGroups, +} from "./columnGroup" + +const DEBUG = false + +export let houseLayouts: Record = {} + +liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( + (dbHouseLayouts) => { + for (let { systemId, dnas, layout } of dbHouseLayouts) { + houseLayouts[getHouseLayoutsKey({ systemId, dnas })] = layout + } + } +) + +export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => + pipe( + columnLayout, + A.head, + O.map(({ gridGroups }) => + pipe( + gridGroups, + A.map(({ levelType }) => levelType) + ) + ), + O.getOrElse((): string[] => []) + ) + +export const getHouseLayout = ({ + systemId, + dnas, +}: { + systemId: string + dnas: string[] +}): T.Task => + pipe( + houseLayouts, + R.lookup(getHouseLayoutsKey({ systemId, dnas })), + O.match( + (): T.Task => async () => { + const layoutsWorker = getLayoutsWorker() + if (!layoutsWorker) throw new Error(`no layouts worker`) + return await layoutsWorker.getLayout({ + systemId, + dnas, + }) + }, + (houseLayout) => T.of(houseLayout) + ) + ) +export const createHouseLayoutGroup = ({ + systemId, + houseId, + dnas, + houseLayout, +}: { + systemId: string + houseId: string + dnas: string[] + houseLayout: ColumnLayout +}) => + pipe( + createColumnGroups({ + systemId, + houseId, + houseLayout, + }), + T.chain((columnGroups) => { + const layoutGroup = new Group() + + const columnCount = columnGroups.length + + const sectionType = + houseLayout[0].gridGroups[0].modules[0].module.structuredDna.sectionType + + const width = houseLayout[0].gridGroups[0].modules[0].module.width + const height = houseLayout[0].gridGroups.reduce( + (acc, v) => acc + v.modules[0].module.height, + 0 + ) + const length = columnGroups.reduce( + (acc, columnGroup) => acc + columnGroup.userData.length, + 0 + ) + const obb = new OBB() + const levelTypes = houseLayoutToLevelTypes(houseLayout) + + return pipe( + getVanillaColumn({ systemId, levelTypes }), + T.map((vanillaColumn) => { + const userData: HouseLayoutGroupUserData = { + type: UserDataTypeEnum.Enum.HouseLayoutGroup, + dnas, + houseLayout, + columnCount, + sectionType, + levelTypes, + width, + height, + length, + obb, + modifiedMaterials: {}, + vanillaColumn, + } + + layoutGroup.position.setZ(-length / 2) + layoutGroup.add(...columnGroups) + + const { startColumnGroup, endColumnGroup } = + splitColumnGroups(columnGroups) + + const removeAllHandles = () => { + pipe( + layoutGroup, + findAllGuardDown( + combineGuards(isRotateHandlesGroup, isStretchHandleMesh) + ) + ).forEach((x) => { + x.removeFromParent() + }) + } + + const userDataHandler: ProxyHandler = { + set: function (target: any, prop: any, value: any) { + if (prop === "length") { + const oldLength = target[prop] + const newLength = value + target[prop] = value + + console.log( + `doing stuff, oldLength: ${oldLength}; newLength: ${newLength}` + ) + + layoutGroup.position.setZ(-newLength / 2) + + layoutGroup.parent?.position.add( + new Vector3(0, 0, (newLength - oldLength) / 2).applyAxisAngle( + yAxis, + layoutGroup.parent.rotation.y + ) + ) + + const houseTransformsGroup = layoutGroup.parent! + + const { x, y, z } = houseTransformsGroup.position + + const center = new Vector3(x, y + height / 2, z) + const halfSize = new Vector3( + width / 2, + height / 2, + newLength / 2 + ) + const rotation = new Matrix3().setFromMatrix4( + houseTransformsGroup.matrix + ) + + layoutGroup.userData.obb.set(center, halfSize, rotation) + + if (DEBUG && houseTransformsGroup.parent) { + renderOBB( + layoutGroup.userData.obb, + houseTransformsGroup.parent + ) + } + + refreshHandles() + } + + return true // Indicate assignment success + }, + } + + layoutGroup.userData = new Proxy(userData, userDataHandler) + + const refreshHandles = () => { + removeAllHandles() + + const { siteMode } = getModeBools(siteCtx.mode) + + const { length: houseLength, width: houseWidth } = + layoutGroup.userData + + const rotateHandles = createRotateHandles({ + houseWidth, + houseLength, + }) + + setVisibility(rotateHandles, siteMode) + + layoutGroup.add(rotateHandles) + + pipe( + [1, -1] as Array<1 | -1>, + cartesian(["x", "z"] as Array<"z" | "x">), + A.map(([axis, side]) => { + const stretchHandleGroup = createStretchHandle({ + axis, + side, + houseLength, + houseWidth, + }) + if (axis === "z") { + if (side === 1) { + endColumnGroup.add(stretchHandleGroup) + } else { + startColumnGroup.add(stretchHandleGroup) + } + } else { + stretchHandleGroup.position.setZ(houseLength / 2) + layoutGroup.add(stretchHandleGroup) + } + setVisibility(stretchHandleGroup, !siteMode) + }) + ) + } + + refreshHandles() + + return layoutGroup + }) + ) + }) + ) diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts new file mode 100644 index 00000000..5f825b01 --- /dev/null +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -0,0 +1,52 @@ +import { pipe } from "fp-ts/lib/function" +import { Group, Plane, Vector3 } from "three" +import { T } from "../../../../utils/functions" +import { HouseTransformsGroupUserData, UserDataTypeEnum } from "../userData" +import { createHouseLayoutGroup, getHouseLayout } from "./houseLayoutGroup" + +export const BIG_CLIP_NUMBER = 999 + +export const createHouseTransformsGroup = ({ + systemId, + houseId, + dnas, + friendlyName, + houseTypeId, +}: { + systemId: string + houseId: string + dnas: string[] + friendlyName: string + houseTypeId: string +}): T.Task => + pipe( + getHouseLayout({ systemId, dnas }), + T.chain((houseLayout) => + createHouseLayoutGroup({ houseLayout, dnas, systemId, houseId }) + ), + T.map((layoutGroup) => { + const transformsGroup = new Group() + + const NORMAL_DIRECTION = -1 + + const clippingPlanes: Plane[] = [ + new Plane(new Vector3(NORMAL_DIRECTION, 0, 0), BIG_CLIP_NUMBER), + new Plane(new Vector3(0, NORMAL_DIRECTION, 0), BIG_CLIP_NUMBER), + new Plane(new Vector3(0, 0, NORMAL_DIRECTION), BIG_CLIP_NUMBER), + ] + + const houseTransformsGroupUserData: HouseTransformsGroupUserData = { + type: UserDataTypeEnum.Enum.HouseTransformsGroup, + systemId, + houseId, + clippingPlanes, + friendlyName, + activeLayoutUuid: layoutGroup.uuid, + houseTypeId, + } + transformsGroup.userData = houseTransformsGroupUserData + transformsGroup.add(layoutGroup) + + return transformsGroup + }) + ) diff --git a/app/design/ui-3d/fresh/scene/moduleGroup.ts b/app/design/ui-3d/fresh/scene/moduleGroup.ts new file mode 100644 index 00000000..da3047fe --- /dev/null +++ b/app/design/ui-3d/fresh/scene/moduleGroup.ts @@ -0,0 +1,121 @@ +import { liveQuery } from "dexie" +import { pipe } from "fp-ts/lib/function" +import { BufferGeometry, BufferGeometryLoader, Group, Mesh } from "three" +import { Module } from "../../../../../server/data/modules" +import layoutsDB from "../../../../db/layouts" +import { O, R, S } from "../../../../utils/functions" +import { getMaterial } from "../systems" +import { + ElementMeshUserData, + ModuleGroupUserData, + UserDataTypeEnum, +} from "../userData" + +// speckle branch url : geometry by ifc tag +export let models: Record> = {} + +export const getGeometry = ({ + speckleBranchUrl, + ifcTag, +}: { + speckleBranchUrl: string + ifcTag: string +}) => models[speckleBranchUrl][ifcTag] + +const loader = new BufferGeometryLoader() + +const putModel = ({ + geometries, + speckleBranchUrl, +}: { + speckleBranchUrl: string + geometries: any +}) => { + const loadedModels: Record = pipe( + geometries, + R.map((x) => loader.parse(x) as BufferGeometry), + R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { + geometry.computeVertexNormals() + return { + ...acc, + [ifcTag]: geometry, + } + }) + ) + models[speckleBranchUrl] = loadedModels + + return loadedModels +} + +liveQuery(() => layoutsDB.models.toArray()).subscribe((dbModels) => { + for (let { speckleBranchUrl, geometries } of dbModels) { + if (!(speckleBranchUrl in models)) { + putModel({ speckleBranchUrl, geometries }) + } + } +}) + +export const createModuleGroup = async ({ + systemId, + houseId, + gridGroupIndex, + module: { speckleBranchUrl, length, dna }, +}: { + systemId: string + houseId: string + gridGroupIndex: number + module: Module +}) => { + const moduleGroup = new Group() + + const processModel = ( + modelGeometriesByIfcTag: Record + ) => { + for (let ifcTag of Object.keys(modelGeometriesByIfcTag)) { + const geometry = getGeometry({ speckleBranchUrl, ifcTag }) + const material = getMaterial({ + systemId, + ifcTag, + houseId, + }) + material.wireframe = false + const mesh = new Mesh(geometry, material) + mesh.castShadow = true + + const elementMeshUserData: ElementMeshUserData = { + type: UserDataTypeEnum.Enum.ElementMesh, + ifcTag, + } + mesh.userData = elementMeshUserData + moduleGroup.add(mesh) + } + } + + await pipe( + models, + R.lookup(speckleBranchUrl), + O.match( + async () => { + const model = await layoutsDB.models.get(speckleBranchUrl) + if (model === undefined) + throw new Error(`no model for ${speckleBranchUrl}`) + const loadedModel = putModel(model) + processModel(loadedModel) + }, + async (loadedModel) => { + processModel(loadedModel) + } + ) + ) + + const moduleGroupUserData: ModuleGroupUserData = { + type: UserDataTypeEnum.Enum.ModuleGroup, + gridGroupIndex, + dna, + length, + } + + moduleGroup.userData = moduleGroupUserData + + return moduleGroup +} diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 4ebee722..940d02bc 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -47,7 +47,7 @@ export type HouseTransformsGroupUserData = { houseTypeId: string friendlyName: string clippingPlanes: Plane[] - activeChildUuid: string + activeLayoutUuid: string } export type HouseLayoutGroupUserData = { From b8dccc8171755ff215369168953034b1b6098c84 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 08:11:04 +0100 Subject: [PATCH 115/132] wip checkpoint --- .../ui-3d/fresh/scene/houseLayoutGroup.ts | 82 ++++++------------- .../ui-3d/fresh/scene/houseTransformsGroup.ts | 47 ++++++++++- 2 files changed, 67 insertions(+), 62 deletions(-) diff --git a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts index f0f4750a..cbe0051f 100644 --- a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts @@ -16,6 +16,7 @@ import { findAllGuardDown } from "../helpers/sceneQueries" import createRotateHandles from "../shapes/rotateHandles" import createStretchHandle from "../shapes/stretchHandle" import { + HouseLayoutGroup, HouseLayoutGroupUserData, isRotateHandlesGroup, isStretchHandleMesh, @@ -84,7 +85,7 @@ export const createHouseLayoutGroup = ({ houseId: string dnas: string[] houseLayout: ColumnLayout -}) => +}): T.Task => pipe( createColumnGroups({ systemId, @@ -135,17 +136,6 @@ export const createHouseLayoutGroup = ({ const { startColumnGroup, endColumnGroup } = splitColumnGroups(columnGroups) - const removeAllHandles = () => { - pipe( - layoutGroup, - findAllGuardDown( - combineGuards(isRotateHandlesGroup, isStretchHandleMesh) - ) - ).forEach((x) => { - x.removeFromParent() - }) - } - const userDataHandler: ProxyHandler = { set: function (target: any, prop: any, value: any) { if (prop === "length") { @@ -189,7 +179,7 @@ export const createHouseLayoutGroup = ({ ) } - refreshHandles() + // refreshHandles() } return true // Indicate assignment success @@ -198,51 +188,27 @@ export const createHouseLayoutGroup = ({ layoutGroup.userData = new Proxy(userData, userDataHandler) - const refreshHandles = () => { - removeAllHandles() - - const { siteMode } = getModeBools(siteCtx.mode) - - const { length: houseLength, width: houseWidth } = - layoutGroup.userData - - const rotateHandles = createRotateHandles({ - houseWidth, - houseLength, - }) - - setVisibility(rotateHandles, siteMode) - - layoutGroup.add(rotateHandles) - - pipe( - [1, -1] as Array<1 | -1>, - cartesian(["x", "z"] as Array<"z" | "x">), - A.map(([axis, side]) => { - const stretchHandleGroup = createStretchHandle({ - axis, - side, - houseLength, - houseWidth, - }) - if (axis === "z") { - if (side === 1) { - endColumnGroup.add(stretchHandleGroup) - } else { - startColumnGroup.add(stretchHandleGroup) - } - } else { - stretchHandleGroup.position.setZ(houseLength / 2) - layoutGroup.add(stretchHandleGroup) - } - setVisibility(stretchHandleGroup, !siteMode) - }) - ) - } - - refreshHandles() - - return layoutGroup + const { siteMode } = getModeBools(siteCtx.mode) + + const backStretchZHandleGroup = createStretchHandle({ + axis: "z", + side: 1, + houseLength: length, + houseWidth: width, + }) + endColumnGroup.add(backStretchZHandleGroup) + setVisibility(backStretchZHandleGroup, !siteMode) + + const frontStretchZHandleGroup = createStretchHandle({ + axis: "z", + side: -1, + houseLength: length, + houseWidth: width, + }) + startColumnGroup.add(frontStretchZHandleGroup) + setVisibility(frontStretchZHandleGroup, !siteMode) + + return layoutGroup as HouseLayoutGroup }) ) }) diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index 5f825b01..9aa16ae1 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -1,6 +1,11 @@ import { pipe } from "fp-ts/lib/function" import { Group, Plane, Vector3 } from "three" import { T } from "../../../../utils/functions" +import { setVisibility } from "../../../../utils/three" +import { getModeBools } from "../../../state/siteCtx" +import { getActiveHouseUserData } from "../helpers/sceneQueries" +import createRotateHandles from "../shapes/rotateHandles" +import createStretchHandle from "../shapes/stretchHandle" import { HouseTransformsGroupUserData, UserDataTypeEnum } from "../userData" import { createHouseLayoutGroup, getHouseLayout } from "./houseLayoutGroup" @@ -25,7 +30,7 @@ export const createHouseTransformsGroup = ({ createHouseLayoutGroup({ houseLayout, dnas, systemId, houseId }) ), T.map((layoutGroup) => { - const transformsGroup = new Group() + const houseTransformsGroup = new Group() const NORMAL_DIRECTION = -1 @@ -35,6 +40,40 @@ export const createHouseTransformsGroup = ({ new Plane(new Vector3(0, 0, NORMAL_DIRECTION), BIG_CLIP_NUMBER), ] + const { width: houseWidth, length: houseLength } = layoutGroup.userData + + const { siteMode } = getModeBools() + + const rotateHandles = createRotateHandles({ + houseWidth, + houseLength, + }) + + setVisibility(rotateHandles, siteMode) + + houseTransformsGroup.add(rotateHandles) + + const stretchXUpHandleGroup = createStretchHandle({ + axis: "x", + side: 1, + houseLength, + houseWidth, + }) + + const stretchXDownHandleGroup = createStretchHandle({ + axis: "x", + side: -1, + houseLength, + houseWidth, + }) + + ;[stretchXUpHandleGroup, stretchXDownHandleGroup].forEach((handle) => { + handle.position.setZ(houseLength / 2) + + houseTransformsGroup.add(handle) + setVisibility(handle, !siteMode) + }) + const houseTransformsGroupUserData: HouseTransformsGroupUserData = { type: UserDataTypeEnum.Enum.HouseTransformsGroup, systemId, @@ -44,9 +83,9 @@ export const createHouseTransformsGroup = ({ activeLayoutUuid: layoutGroup.uuid, houseTypeId, } - transformsGroup.userData = houseTransformsGroupUserData - transformsGroup.add(layoutGroup) + houseTransformsGroup.userData = houseTransformsGroupUserData + houseTransformsGroup.add(layoutGroup) - return transformsGroup + return houseTransformsGroup }) ) From 042bae21eadb29b55913375f67f59f698c59512d Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 08:22:21 +0100 Subject: [PATCH 116/132] wip --- .../ui-3d/fresh/scene/houseTransformsGroup.ts | 84 ++++++++++++------- app/design/ui-3d/fresh/userData.ts | 3 + 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index 9aa16ae1..7f1502ad 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -1,12 +1,20 @@ import { pipe } from "fp-ts/lib/function" import { Group, Plane, Vector3 } from "three" -import { T } from "../../../../utils/functions" +import { A, T } from "../../../../utils/functions" import { setVisibility } from "../../../../utils/three" import { getModeBools } from "../../../state/siteCtx" -import { getActiveHouseUserData } from "../helpers/sceneQueries" +import { + getActiveHouseUserData, + getActiveLayoutGroup, +} from "../helpers/sceneQueries" import createRotateHandles from "../shapes/rotateHandles" import createStretchHandle from "../shapes/stretchHandle" -import { HouseTransformsGroupUserData, UserDataTypeEnum } from "../userData" +import { + HouseTransformsGroupUserData, + isStretchHandleGroup, + StretchHandleGroup, + UserDataTypeEnum, +} from "../userData" import { createHouseLayoutGroup, getHouseLayout } from "./houseLayoutGroup" export const BIG_CLIP_NUMBER = 999 @@ -40,39 +48,56 @@ export const createHouseTransformsGroup = ({ new Plane(new Vector3(0, 0, NORMAL_DIRECTION), BIG_CLIP_NUMBER), ] - const { width: houseWidth, length: houseLength } = layoutGroup.userData + const initHandles = () => { + const { width: houseWidth, length: houseLength } = + getActiveHouseUserData(houseTransformsGroup) - const { siteMode } = getModeBools() + const { siteMode } = getModeBools() - const rotateHandles = createRotateHandles({ - houseWidth, - houseLength, - }) + const rotateHandles = createRotateHandles({ + houseWidth, + houseLength, + }) - setVisibility(rotateHandles, siteMode) + setVisibility(rotateHandles, siteMode) - houseTransformsGroup.add(rotateHandles) + houseTransformsGroup.add(rotateHandles) - const stretchXUpHandleGroup = createStretchHandle({ - axis: "x", - side: 1, - houseLength, - houseWidth, - }) + const stretchXUpHandleGroup = createStretchHandle({ + axis: "x", + side: 1, + houseLength, + houseWidth, + }) - const stretchXDownHandleGroup = createStretchHandle({ - axis: "x", - side: -1, - houseLength, - houseWidth, - }) + const stretchXDownHandleGroup = createStretchHandle({ + axis: "x", + side: -1, + houseLength, + houseWidth, + }) - ;[stretchXUpHandleGroup, stretchXDownHandleGroup].forEach((handle) => { - handle.position.setZ(houseLength / 2) + ;[stretchXUpHandleGroup, stretchXDownHandleGroup].forEach((handle) => { + handle.position.setZ(houseLength / 2) - houseTransformsGroup.add(handle) - setVisibility(handle, !siteMode) - }) + houseTransformsGroup.add(handle) + setVisibility(handle, !siteMode) + }) + } + + const syncRotateHandles = () => {} + const syncWidthHandles = () => { + const widthHandles = pipe( + houseTransformsGroup.children, + A.filter((x): x is StretchHandleGroup => { + return isStretchHandleGroup(x) && x.userData.axis === "x" + }) + ) + + widthHandles.forEach((handle) => { + // cont + }) + } const houseTransformsGroupUserData: HouseTransformsGroupUserData = { type: UserDataTypeEnum.Enum.HouseTransformsGroup, @@ -82,6 +107,9 @@ export const createHouseTransformsGroup = ({ friendlyName, activeLayoutUuid: layoutGroup.uuid, houseTypeId, + initHandles, + syncRotateHandles, + syncWidthHandles, } houseTransformsGroup.userData = houseTransformsGroupUserData houseTransformsGroup.add(layoutGroup) diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 940d02bc..04005474 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -48,6 +48,9 @@ export type HouseTransformsGroupUserData = { friendlyName: string clippingPlanes: Plane[] activeLayoutUuid: string + initHandles: () => void + syncWidthHandles: () => void + syncRotateHandles: () => void } export type HouseLayoutGroupUserData = { From 87982761fc266b630de25a01f450c44371cb8ba7 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 08:35:57 +0100 Subject: [PATCH 117/132] wip --- app/design/ui-3d/fresh/gestures/stretch.ts | 13 ++++++++++--- .../ui-3d/fresh/scene/houseTransformsGroup.ts | 5 ++++- app/design/ui-3d/fresh/shapes/stretchHandle.ts | 9 +++++++-- app/design/ui-3d/fresh/userData.ts | 1 + 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 02170db2..37b4c082 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -248,6 +248,7 @@ const useOnDragStretch = () => { nextFence.columnGroup.userData.columnIndex = endColumnGroup.userData.columnIndex - 1 stretchZProgressDataRef.current.fenceIndex++ + houseTransformsGroup.userData.syncWidthHandles() if (nextFence.z < maxLength) { addVanilla(direction) @@ -266,8 +267,8 @@ const useOnDragStretch = () => { setInvisibleNoRaycast(lastVisibleFence.columnGroup) stretchZProgressDataRef.current.fenceIndex-- lastVisibleFence.columnGroup.userData.columnIndex = -1 - endColumnGroup.userData.columnIndex-- + houseTransformsGroup.userData.syncWidthHandles() } } } @@ -295,6 +296,7 @@ const useOnDragStretch = () => { nextFence.columnGroup.userData.columnIndex = 1 stretchZProgressDataRef.current.fenceIndex++ + houseTransformsGroup.userData.syncWidthHandles() // naive if (nextFence.z < maxLength) { @@ -322,6 +324,7 @@ const useOnDragStretch = () => { columnGroup.userData.columnIndex-- }) stretchZProgressDataRef.current.fenceIndex-- + houseTransformsGroup.userData.syncWidthHandles() } } } @@ -400,6 +403,7 @@ const useOnDragStretch = () => { houseTransformsGroup: HouseTransformsGroup handleGroup: StretchHandleGroup otherSideHandleGroup: StretchHandleGroup + otherSideHandleGroupX0: number fences: FenceX[] fenceIndex: number lastDistance: number @@ -426,7 +430,7 @@ const useOnDragStretch = () => { getPartitionedLayoutGroups(houseTransformsGroup) const otherSideHandleGroup = pipe( - activeLayoutGroup.children, + houseTransformsGroup.children, A.findFirst((x): x is StretchHandleGroup => { return ( isStretchHandleGroup(x) && @@ -456,6 +460,7 @@ const useOnDragStretch = () => { stretchXData.current = { handleGroup, otherSideHandleGroup, + otherSideHandleGroupX0: otherSideHandleGroup.position.x, houseTransformsGroup, point0: point, fences, @@ -471,8 +476,9 @@ const useOnDragStretch = () => { point0, houseTransformsGroup, handleGroup, - otherSideHandleGroup, handleGroupX0, + otherSideHandleGroup, + otherSideHandleGroupX0, fences, fenceIndex, lastDistance, @@ -488,6 +494,7 @@ const useOnDragStretch = () => { ) const distance = distanceVector.x handleGroup.position.setX(handleGroupX0 + distance) + otherSideHandleGroup.position.setX(otherSideHandleGroupX0 - distance) const adjustedDistance = side * distance const adjustedLastDistance = side * lastDistance diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index 7f1502ad..150bf010 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -95,7 +95,8 @@ export const createHouseTransformsGroup = ({ ) widthHandles.forEach((handle) => { - // cont + const { length } = getActiveHouseUserData(houseTransformsGroup) + handle.userData.updateXHandleLength(length) }) } @@ -114,6 +115,8 @@ export const createHouseTransformsGroup = ({ houseTransformsGroup.userData = houseTransformsGroupUserData houseTransformsGroup.add(layoutGroup) + initHandles() + return houseTransformsGroup }) ) diff --git a/app/design/ui-3d/fresh/shapes/stretchHandle.ts b/app/design/ui-3d/fresh/shapes/stretchHandle.ts index 636db505..d2218e43 100644 --- a/app/design/ui-3d/fresh/shapes/stretchHandle.ts +++ b/app/design/ui-3d/fresh/shapes/stretchHandle.ts @@ -62,11 +62,16 @@ const createStretchHandle = ({ handleGroup.scale.setScalar(0.5) handleGroup.position.setY(0.01) - handleGroup.userData = { + const userData: StretchHandleGroupUserData = { axis, side, type: UserDataTypeEnum.Enum.StretchHandleGroup, - } as StretchHandleGroupUserData + updateXHandleLength: (length: number) => { + boxMesh.scale.setZ(length) + }, + } + + handleGroup.userData = userData return handleGroup } diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 04005474..7cc18ca3 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -103,6 +103,7 @@ export type StretchHandleGroupUserData = { type: typeof UserDataTypeEnum.Enum.StretchHandleGroup axis: "z" | "x" side: 1 | -1 + updateXHandleLength: (length: number) => void } export type StretchHandleMeshUserData = { From 6d8b1b492688f6adefae1633dbdc273061c0764d Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 09:18:35 +0100 Subject: [PATCH 118/132] wip pre sync st alts --- app/design/ui-3d/fresh/gestures/stretch.ts | 16 +++++---- .../ui-3d/fresh/helpers/sceneChanges.ts | 2 +- .../ui-3d/fresh/helpers/sceneQueries.ts | 6 ++-- .../ui-3d/fresh/scene/houseTransformsGroup.ts | 36 +++++++++++++++---- app/design/ui-3d/fresh/userData.ts | 3 +- 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts index 37b4c082..9c615e8c 100644 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ b/app/design/ui-3d/fresh/gestures/stretch.ts @@ -457,6 +457,8 @@ const useOnDragStretch = () => { }) ) + console.log(fences) + stretchXData.current = { handleGroup, otherSideHandleGroup, @@ -505,9 +507,10 @@ const useOnDragStretch = () => { A.lookup(fenceIndex + 1), O.map(({ x }) => { if (adjustedDistance >= x) { - // swap it - setVisibility(fences[fenceIndex + 1].layoutGroup, true) - setVisibility(fences[fenceIndex].layoutGroup, false) + console.log(1) + houseTransformsGroup.userData.setActiveLayoutGroup( + fences[fenceIndex + 1].layoutGroup + ) stretchXData.current!.fenceIndex++ } }) @@ -517,9 +520,10 @@ const useOnDragStretch = () => { fences, A.lookup(fenceIndex - 1), O.map(({ x }) => { - if (adjustedDistance >= x) { - setVisibility(fences[fenceIndex].layoutGroup, true) - setVisibility(fences[fenceIndex + 1].layoutGroup, false) + if (adjustedDistance <= x) { + houseTransformsGroup.userData.setActiveLayoutGroup( + fences[fenceIndex - 1].layoutGroup + ) stretchXData.current!.fenceIndex-- } }) diff --git a/app/design/ui-3d/fresh/helpers/sceneChanges.ts b/app/design/ui-3d/fresh/helpers/sceneChanges.ts index d16dc1db..fca46085 100644 --- a/app/design/ui-3d/fresh/helpers/sceneChanges.ts +++ b/app/design/ui-3d/fresh/helpers/sceneChanges.ts @@ -17,7 +17,7 @@ export const debugNextLayout = (houseTransformsGroup: HouseTransformsGroup) => { O.map((nextLayout) => { setVisibleAndRaycast(nextLayout) setInvisibleNoRaycast(activeLayoutGroup) - houseTransformsGroup.userData.activeLayoutUuid = nextLayout.uuid + houseTransformsGroup.userData.activeLayoutGroupUuid = nextLayout.uuid }) ) } diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 243f6dbb..639a45f2 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -147,7 +147,7 @@ export const getActiveHouseUserData = (houseTransformsGroup: Object3D) => A.findFirstMap((x) => x.uuid === (houseTransformsGroup.userData as HouseTransformsGroupUserData) - .activeLayoutUuid + .activeLayoutGroupUuid ? O.some({ ...(houseTransformsGroup.userData as HouseTransformsGroupUserData), ...(x.userData as HouseLayoutGroupUserData), @@ -169,7 +169,7 @@ export const getActiveLayoutGroup = ( houseTransformsGroup.children, A.findFirst( (x): x is HouseLayoutGroup => - x.uuid === houseTransformsGroup.userData.activeLayoutUuid + x.uuid === houseTransformsGroup.userData.activeLayoutGroupUuid ), someOrError(`getActiveLayoutGroup failure`) ) @@ -181,7 +181,7 @@ export const getPartitionedLayoutGroups = ( houseTransformsGroup, getLayoutGroups, A.partition( - (x) => x.uuid === houseTransformsGroup.userData.activeLayoutUuid + (x) => x.uuid === houseTransformsGroup.userData.activeLayoutGroupUuid ), ({ left: otherLayoutGroups, right: [activeLayoutGroup] }) => ({ activeLayoutGroup, diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index 150bf010..a3d39d39 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -1,16 +1,16 @@ import { pipe } from "fp-ts/lib/function" import { Group, Plane, Vector3 } from "three" -import { A, T } from "../../../../utils/functions" +import { A, O, pipeLog, T } from "../../../../utils/functions" import { setVisibility } from "../../../../utils/three" import { getModeBools } from "../../../state/siteCtx" -import { - getActiveHouseUserData, - getActiveLayoutGroup, -} from "../helpers/sceneQueries" +import { getActiveHouseUserData } from "../helpers/sceneQueries" import createRotateHandles from "../shapes/rotateHandles" import createStretchHandle from "../shapes/stretchHandle" import { + HouseLayoutGroup, + HouseTransformsGroup, HouseTransformsGroupUserData, + isHouseLayoutGroup, isStretchHandleGroup, StretchHandleGroup, UserDataTypeEnum, @@ -38,7 +38,7 @@ export const createHouseTransformsGroup = ({ createHouseLayoutGroup({ houseLayout, dnas, systemId, houseId }) ), T.map((layoutGroup) => { - const houseTransformsGroup = new Group() + const houseTransformsGroup = new Group() as HouseTransformsGroup const NORMAL_DIRECTION = -1 @@ -86,6 +86,7 @@ export const createHouseTransformsGroup = ({ } const syncRotateHandles = () => {} + const syncWidthHandles = () => { const widthHandles = pipe( houseTransformsGroup.children, @@ -100,19 +101,40 @@ export const createHouseTransformsGroup = ({ }) } + const setActiveLayoutGroup = (nextLayoutGroup: HouseLayoutGroup) => { + pipe( + houseTransformsGroup.children, + A.findFirst( + (x): x is HouseLayoutGroup => + isHouseLayoutGroup(x) && + x.uuid === houseTransformsGroup.userData.activeLayoutGroupUuid + ), + O.map((lastLayoutGroup) => { + console.log(`in ${nextLayoutGroup.uuid}`) + console.log(`out ${lastLayoutGroup.uuid}`) + setVisibility(nextLayoutGroup, true) + setVisibility(lastLayoutGroup, false) + houseTransformsGroup.userData.activeLayoutGroupUuid = + nextLayoutGroup.uuid + }) + ) + } + const houseTransformsGroupUserData: HouseTransformsGroupUserData = { type: UserDataTypeEnum.Enum.HouseTransformsGroup, systemId, houseId, clippingPlanes, friendlyName, - activeLayoutUuid: layoutGroup.uuid, + activeLayoutGroupUuid: layoutGroup.uuid, houseTypeId, initHandles, syncRotateHandles, syncWidthHandles, + setActiveLayoutGroup, } houseTransformsGroup.userData = houseTransformsGroupUserData + houseTransformsGroup.add(layoutGroup) initHandles() diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 7cc18ca3..7cd1cf10 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -47,10 +47,11 @@ export type HouseTransformsGroupUserData = { houseTypeId: string friendlyName: string clippingPlanes: Plane[] - activeLayoutUuid: string + activeLayoutGroupUuid: string initHandles: () => void syncWidthHandles: () => void syncRotateHandles: () => void + setActiveLayoutGroup: (layoutGroup: HouseLayoutGroup) => void } export type HouseLayoutGroupUserData = { From 97e2f0a2da68a04c900ebed0c11bfcfff5208abe Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 11:50:55 +0100 Subject: [PATCH 119/132] wip separate stretch handlers --- app/design/ui-3d/fresh/dimensions.ts | 2 +- app/design/ui-3d/fresh/gestures/index.ts | 7 +- app/design/ui-3d/fresh/gestures/stretch.ts | 541 -------------------- app/design/ui-3d/fresh/gestures/stretchX.ts | 164 ++++++ app/design/ui-3d/fresh/gestures/stretchZ.ts | 391 ++++++++++++++ app/design/ui-3d/fresh/userData.ts | 5 + 6 files changed, 566 insertions(+), 544 deletions(-) delete mode 100644 app/design/ui-3d/fresh/gestures/stretch.ts create mode 100644 app/design/ui-3d/fresh/gestures/stretchX.ts create mode 100644 app/design/ui-3d/fresh/gestures/stretchZ.ts diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 249f404b..3da3de9f 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -90,7 +90,7 @@ export const updateClippingPlanes = (houseGroup: Group) => { planeZ.applyMatrix4(houseGroup.matrix) } -const updateDnas = (houseTransformsGroup: HouseTransformsGroup) => { +export const updateDnas = (houseTransformsGroup: HouseTransformsGroup) => { let result: string[][] = [] pipe( houseTransformsGroup, diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts index e62b80ed..e33ff576 100644 --- a/app/design/ui-3d/fresh/gestures/index.ts +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -31,13 +31,16 @@ import { import { dispatchPointerDown, dispatchPointerUp } from "./events" import useOnDragMove from "./move" import useOnDragRotate from "./rotate" -import useOnDragStretch from "./stretch" +import useOnDragStretchX from "./stretchX" +import useOnDragStretchZ from "./stretchZ" const useGestures = () => { - const { onDragStretchZ, onDragStretchX } = useOnDragStretch() const onDragMove = useOnDragMove() const onDragRotate = useOnDragRotate() + const onDragStretchZ = useOnDragStretchZ() + const onDragStretchX = useOnDragStretchX() + const firstDragEventRef = useRef | null>(null) return useGesture<{ diff --git a/app/design/ui-3d/fresh/gestures/stretch.ts b/app/design/ui-3d/fresh/gestures/stretch.ts deleted file mode 100644 index 9c615e8c..00000000 --- a/app/design/ui-3d/fresh/gestures/stretch.ts +++ /dev/null @@ -1,541 +0,0 @@ -import { pipe } from "fp-ts/lib/function" -import { useRef } from "react" -import { Object3D, Vector3 } from "three" -import { A, O, someOrError, T } from "../../../../utils/functions" -import { - setInvisibleNoRaycast, - setVisibility, - setVisibleAndRaycast, - yAxis, -} from "../../../../utils/three" -import pointer from "../../../state/pointer" -import { updateLayoutGroupLength } from "../dimensions" -import { dispatchOutline } from "../events/outlines" -import { - getActiveHouseUserData, - getActiveLayoutGroup, - getHouseTransformsGroupUp, - getPartitionedLayoutGroups, - getSortedVisibleColumnGroups, - getVisibleColumnGroups, - handleColumnGroupParentQuery, - sortLayoutGroupsByWidth, -} from "../helpers/sceneQueries" -import { createColumnGroup, splitColumnGroups } from "../scene/columnGroup" -import { - ColumnGroup, - HouseLayoutGroup, - HouseTransformsGroup, - isStretchHandleGroup, - StretchHandleGroup, -} from "../userData" - -const TMP_MAX_LENGTH = 10 - -const useOnDragStretch = () => { - const stretchZInitialDataRef = useRef<{ - direction: number - point0: Vector3 - handleColumnGroup: Object3D - houseTransformsGroup: HouseTransformsGroup - layoutGroup: HouseLayoutGroup - handleGroupZ0: number - columnGroups: Object3D[] - startColumnGroup: Object3D - midColumnGroups: Object3D[] - endColumnGroup: ColumnGroup - templateVanillaColumnGroup: ColumnGroup - vanillaLength: number - maxLength: number - midStartZ: number - midEndZ: number - } | null>(null) - - type FenceZ = { - z: number - columnGroup: ColumnGroup - } - - const stretchZProgressDataRef = useRef<{ - fences: FenceZ[] - lastDistance: number - fenceIndex: number - }>({ - fences: [], - lastDistance: 0, - fenceIndex: 0, - }) - - const addVanilla = (side: 1 | -1) => { - if (!stretchZInitialDataRef.current) return - - const { templateVanillaColumnGroup, layoutGroup } = - stretchZInitialDataRef.current - - const { fences } = stretchZProgressDataRef.current - - const lastColumnGroup = fences[fences.length - 1].columnGroup - const columnGroup = templateVanillaColumnGroup.clone() - - let z = 0 - if (side === 1) { - z = lastColumnGroup.position.z + lastColumnGroup.userData.length - } else if (side === -1) { - z = lastColumnGroup.position.z - columnGroup.userData.length - } - - columnGroup.position.setZ(z) - - setInvisibleNoRaycast(columnGroup) - - layoutGroup.add(columnGroup) - - fences.push({ - columnGroup, - z: z + columnGroup.userData.length / 2, - }) - } - - const onDragStretchZ = { - first: ({ - handleGroup, - point, - }: { - handleGroup: StretchHandleGroup - point: Vector3 - }) => { - dispatchOutline({ - hoveredObjects: [], - selectedObjects: [], - }) - - const handleColumnGroup = handleColumnGroupParentQuery(handleGroup) - const houseTransformsGroup = getHouseTransformsGroupUp(handleColumnGroup) - - const { side } = handleGroup.userData - const { systemId, houseId, vanillaColumn } = - getActiveHouseUserData(houseTransformsGroup) - - const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) - const columnGroups = pipe(layoutGroup, getSortedVisibleColumnGroups) - - const task = pipe( - T.of(vanillaColumn), - T.chain(({ gridGroups }) => - pipe( - createColumnGroup({ - systemId, - houseId, - gridGroups, - columnIndex: -1, - }), - T.map((templateVanillaColumnGroup) => { - const { startColumnGroup, midColumnGroups, endColumnGroup } = - splitColumnGroups(columnGroups) - - const vanillaLength = templateVanillaColumnGroup.userData.length - - stretchZInitialDataRef.current = { - direction: side, - handleColumnGroup: handleColumnGroup, - layoutGroup, - houseTransformsGroup, - point0: point, - handleGroupZ0: handleColumnGroup.position.z, - templateVanillaColumnGroup, - vanillaLength, - columnGroups, - startColumnGroup, - midColumnGroups, - endColumnGroup, - maxLength: TMP_MAX_LENGTH, - midStartZ: startColumnGroup.userData.length, - midEndZ: endColumnGroup.position.z, - } - - if (side === 1) { - stretchZProgressDataRef.current.fences = pipe( - midColumnGroups, - A.map((columnGroup) => { - return { - columnGroup, - z: - columnGroup.position.z + - columnGroup.userData.length / 2, - } - }) - ) - stretchZProgressDataRef.current.fenceIndex = - stretchZProgressDataRef.current.fences.length - 1 - - for (let i = 0; i < 3; i++) { - addVanilla(side) - } - } - - if (side === -1) { - stretchZProgressDataRef.current.fences = pipe( - midColumnGroups, - A.reverse, - A.map((columnGroup) => { - const z = columnGroup.position.z - return { - columnGroup, - z, - } - }) - ) - stretchZProgressDataRef.current.fenceIndex = - stretchZProgressDataRef.current.fences.length - 1 - - for (let i = 0; i < 3; i++) { - addVanilla(side) - } - } - - console.log(stretchZProgressDataRef.current.fences) - }) - ) - ) - ) - - task() - }, - mid: () => { - if (!stretchZInitialDataRef.current) return - - const { - direction, - point0, - houseTransformsGroup, - handleColumnGroup, - handleGroupZ0, - vanillaLength, - templateVanillaColumnGroup, - columnGroups, - layoutGroup, - endColumnGroup, - maxLength, - midEndZ, - midStartZ, - } = stretchZInitialDataRef.current - - const { lastDistance, fences, fenceIndex } = - stretchZProgressDataRef.current - - const [x1, z1] = pointer.xz - const distanceVector = new Vector3(x1, 0, z1).sub(point0) - distanceVector.applyAxisAngle( - new Vector3(0, 1, 0), - -houseTransformsGroup.rotation.y - ) - const distance = distanceVector.z - - handleColumnGroup.position.setZ(handleGroupZ0 + distance) - - // back side - if (direction === 1) { - // const cl = clamp(lo, hi) - - // additive direction to back side - if (distance > lastDistance) { - if (fenceIndex + 1 < fences.length) { - const nextFence = fences[fenceIndex + 1] - const realDistance = midEndZ + distance - if (realDistance >= nextFence.z) { - setVisibleAndRaycast(nextFence.columnGroup) - endColumnGroup.userData.columnIndex++ - nextFence.columnGroup.userData.columnIndex = - endColumnGroup.userData.columnIndex - 1 - stretchZProgressDataRef.current.fenceIndex++ - houseTransformsGroup.userData.syncWidthHandles() - - if (nextFence.z < maxLength) { - addVanilla(direction) - } - } - } - } - - // subtractive direction to back side - if (distance < lastDistance) { - if (fenceIndex > 0) { - const realDistance = midEndZ + distance - const lastVisibleFence = fences[fenceIndex] - - if (realDistance < lastVisibleFence.z) { - setInvisibleNoRaycast(lastVisibleFence.columnGroup) - stretchZProgressDataRef.current.fenceIndex-- - lastVisibleFence.columnGroup.userData.columnIndex = -1 - endColumnGroup.userData.columnIndex-- - houseTransformsGroup.userData.syncWidthHandles() - } - } - } - } - - // front side - if (direction === -1) { - // const cl = clamp(lo, hi) - // additive direction to front side - if (distance < lastDistance) { - if (fenceIndex + 1 < fences.length) { - const nextFence = fences[fenceIndex + 1] - const realDistance = midStartZ + distance - if (realDistance <= nextFence.z) { - setVisibleAndRaycast(nextFence.columnGroup) - - pipe( - layoutGroup, - getVisibleColumnGroups, - A.filter((x) => x.userData.columnIndex >= 1) - ).forEach((columnGroup) => { - columnGroup.userData.columnIndex++ - }) - - nextFence.columnGroup.userData.columnIndex = 1 - - stretchZProgressDataRef.current.fenceIndex++ - houseTransformsGroup.userData.syncWidthHandles() - - // naive - if (nextFence.z < maxLength) { - addVanilla(direction) - } - } - } - } - // subtractive direction to front side - if (distance > lastDistance) { - if (fenceIndex > 0) { - const realDistance = midStartZ + distance - const lastVisibleFence = fences[fenceIndex] - - if (realDistance > lastVisibleFence.z) { - setInvisibleNoRaycast(lastVisibleFence.columnGroup) - - lastVisibleFence.columnGroup.userData.columnIndex = -1 - - pipe( - layoutGroup, - getVisibleColumnGroups, - A.filter((x) => x.userData.columnIndex > 1) - ).forEach((columnGroup) => { - columnGroup.userData.columnIndex-- - }) - stretchZProgressDataRef.current.fenceIndex-- - houseTransformsGroup.userData.syncWidthHandles() - } - } - } - } - - stretchZProgressDataRef.current.lastDistance = distance - }, - last: () => { - if (!stretchZInitialDataRef.current) return - - const { layoutGroup, direction, houseTransformsGroup } = - stretchZInitialDataRef.current - - const sortedVisibleColumnGroups = pipe( - layoutGroup, - getSortedVisibleColumnGroups - ) - - if (direction === 1) { - pipe( - sortedVisibleColumnGroups, - A.takeRight(2), - ([penultimateColumnGroup, endColumnGroup]) => { - endColumnGroup.position.setZ( - penultimateColumnGroup.position.z + - penultimateColumnGroup.userData.length - ) - } - ) - } - - if (direction === -1) { - pipe( - sortedVisibleColumnGroups, - A.takeLeft(2), - ([startColumnGroup, secondColumnGroup]) => { - startColumnGroup.position.setZ( - secondColumnGroup.position.z - startColumnGroup.userData.length - ) - } - ) - - const delta = -sortedVisibleColumnGroups[0].position.z - - sortedVisibleColumnGroups.forEach((columnGroup) => { - columnGroup.position.z += delta - }) - - houseTransformsGroup.position.sub( - new Vector3(0, 0, delta).applyAxisAngle( - yAxis, - houseTransformsGroup.rotation.y - ) - ) - } - - updateLayoutGroupLength(layoutGroup) - - stretchZInitialDataRef.current = null - stretchZProgressDataRef.current = { - lastDistance: 0, - fences: [], - fenceIndex: 0, - } - }, - } - - type FenceX = { - x: number - layoutGroup: HouseLayoutGroup - } - - const stretchXData = useRef<{ - point0: Vector3 - handleGroupX0: number - houseTransformsGroup: HouseTransformsGroup - handleGroup: StretchHandleGroup - otherSideHandleGroup: StretchHandleGroup - otherSideHandleGroupX0: number - fences: FenceX[] - fenceIndex: number - lastDistance: number - } | null>(null) - - const onDragStretchX = { - first: ({ - point, - handleGroup, - }: { - handleGroup: StretchHandleGroup - point: Vector3 - }) => { - dispatchOutline({ - hoveredObjects: [], - selectedObjects: [], - }) - - const { side } = handleGroup.userData - const otherSide = side * -1 - - const houseTransformsGroup = getHouseTransformsGroupUp(handleGroup) - const { activeLayoutGroup, otherLayoutGroups } = - getPartitionedLayoutGroups(houseTransformsGroup) - - const otherSideHandleGroup = pipe( - houseTransformsGroup.children, - A.findFirst((x): x is StretchHandleGroup => { - return ( - isStretchHandleGroup(x) && - x.userData.axis === "x" && - x.userData.side === otherSide - ) - }), - someOrError(`other side handle group not found`) - ) - - let fenceIndex = 0 - - const fences = pipe( - [activeLayoutGroup, ...otherLayoutGroups], - sortLayoutGroupsByWidth, - A.mapWithIndex((i, layoutGroup): FenceX => { - if (layoutGroup.uuid === activeLayoutGroup.uuid) fenceIndex = i - return { - layoutGroup, - x: - (layoutGroup.userData.width - activeLayoutGroup.userData.width) / - 2, - } - }) - ) - - console.log(fences) - - stretchXData.current = { - handleGroup, - otherSideHandleGroup, - otherSideHandleGroupX0: otherSideHandleGroup.position.x, - houseTransformsGroup, - point0: point, - fences, - handleGroupX0: handleGroup.position.x, - fenceIndex, - lastDistance: 0, - } - }, - mid: () => { - if (!stretchXData.current) return - - const { - point0, - houseTransformsGroup, - handleGroup, - handleGroupX0, - otherSideHandleGroup, - otherSideHandleGroupX0, - fences, - fenceIndex, - lastDistance, - } = stretchXData.current - - const { side } = handleGroup.userData - - const [x1, z1] = pointer.xz - const distanceVector = new Vector3(x1, 0, z1).sub(point0) - distanceVector.applyAxisAngle( - new Vector3(0, 1, 0), - -houseTransformsGroup.rotation.y - ) - const distance = distanceVector.x - handleGroup.position.setX(handleGroupX0 + distance) - otherSideHandleGroup.position.setX(otherSideHandleGroupX0 - distance) - - const adjustedDistance = side * distance - const adjustedLastDistance = side * lastDistance - - if (adjustedDistance > adjustedLastDistance) { - pipe( - fences, - A.lookup(fenceIndex + 1), - O.map(({ x }) => { - if (adjustedDistance >= x) { - console.log(1) - houseTransformsGroup.userData.setActiveLayoutGroup( - fences[fenceIndex + 1].layoutGroup - ) - stretchXData.current!.fenceIndex++ - } - }) - ) - } else if (adjustedDistance < adjustedLastDistance) { - pipe( - fences, - A.lookup(fenceIndex - 1), - O.map(({ x }) => { - if (adjustedDistance <= x) { - houseTransformsGroup.userData.setActiveLayoutGroup( - fences[fenceIndex - 1].layoutGroup - ) - stretchXData.current!.fenceIndex-- - } - }) - ) - // tbc - } - // if next fence up - }, - last: () => {}, - } - - return { onDragStretchZ, onDragStretchX } -} - -export default useOnDragStretch diff --git a/app/design/ui-3d/fresh/gestures/stretchX.ts b/app/design/ui-3d/fresh/gestures/stretchX.ts new file mode 100644 index 00000000..99549ff1 --- /dev/null +++ b/app/design/ui-3d/fresh/gestures/stretchX.ts @@ -0,0 +1,164 @@ +import { pipe } from "fp-ts/lib/function" +import { useRef } from "react" +import { Vector3 } from "three" +import { A, O, someOrError } from "../../../../utils/functions" +import pointer from "../../../state/pointer" +import { dispatchOutline } from "../events/outlines" +import { + getHouseTransformsGroupUp, + getPartitionedLayoutGroups, + sortLayoutGroupsByWidth, +} from "../helpers/sceneQueries" +import { + HouseLayoutGroup, + HouseTransformsGroup, + isStretchHandleGroup, + StretchHandleGroup, +} from "../userData" + +type FenceX = { + x: number + layoutGroup: HouseLayoutGroup +} + +const useOnDragStretchX = () => { + const stretchXData = useRef<{ + point0: Vector3 + handleGroupX0: number + houseTransformsGroup: HouseTransformsGroup + handleGroup: StretchHandleGroup + otherSideHandleGroup: StretchHandleGroup + otherSideHandleGroupX0: number + fences: FenceX[] + fenceIndex: number + lastDistance: number + } | null>(null) + + const first = ({ + point, + handleGroup, + }: { + handleGroup: StretchHandleGroup + point: Vector3 + }) => { + dispatchOutline({ + hoveredObjects: [], + selectedObjects: [], + }) + + const { side } = handleGroup.userData + const otherSide = side * -1 + + const houseTransformsGroup = getHouseTransformsGroupUp(handleGroup) + const { activeLayoutGroup, otherLayoutGroups } = + getPartitionedLayoutGroups(houseTransformsGroup) + + const otherSideHandleGroup = pipe( + houseTransformsGroup.children, + A.findFirst((x): x is StretchHandleGroup => { + return ( + isStretchHandleGroup(x) && + x.userData.axis === "x" && + x.userData.side === otherSide + ) + }), + someOrError(`other side handle group not found`) + ) + + let fenceIndex = 0 + + const fences = pipe( + [activeLayoutGroup, ...otherLayoutGroups], + sortLayoutGroupsByWidth, + A.mapWithIndex((i, layoutGroup): FenceX => { + if (layoutGroup.uuid === activeLayoutGroup.uuid) fenceIndex = i + return { + layoutGroup, + x: + (layoutGroup.userData.width - activeLayoutGroup.userData.width) / 2, + } + }) + ) + + console.log(fences) + + stretchXData.current = { + handleGroup, + otherSideHandleGroup, + otherSideHandleGroupX0: otherSideHandleGroup.position.x, + houseTransformsGroup, + point0: point, + fences, + handleGroupX0: handleGroup.position.x, + fenceIndex, + lastDistance: 0, + } + } + const mid = () => { + if (!stretchXData.current) return + + const { + point0, + houseTransformsGroup, + handleGroup, + handleGroupX0, + otherSideHandleGroup, + otherSideHandleGroupX0, + fences, + fenceIndex, + lastDistance, + } = stretchXData.current + + const { side } = handleGroup.userData + + const [x1, z1] = pointer.xz + const distanceVector = new Vector3(x1, 0, z1).sub(point0) + distanceVector.applyAxisAngle( + new Vector3(0, 1, 0), + -houseTransformsGroup.rotation.y + ) + const distance = distanceVector.x + handleGroup.position.setX(handleGroupX0 + distance) + otherSideHandleGroup.position.setX(otherSideHandleGroupX0 - distance) + + const adjustedDistance = side * distance + const adjustedLastDistance = side * lastDistance + + if (adjustedDistance > adjustedLastDistance) { + pipe( + fences, + A.lookup(fenceIndex + 1), + O.map(({ x }) => { + if (adjustedDistance >= x) { + console.log(1) + houseTransformsGroup.userData.setActiveLayoutGroup( + fences[fenceIndex + 1].layoutGroup + ) + stretchXData.current!.fenceIndex++ + } + }) + ) + } else if (adjustedDistance < adjustedLastDistance) { + pipe( + fences, + A.lookup(fenceIndex - 1), + O.map(({ x }) => { + if (adjustedDistance <= x) { + houseTransformsGroup.userData.setActiveLayoutGroup( + fences[fenceIndex - 1].layoutGroup + ) + stretchXData.current!.fenceIndex-- + } + }) + ) + // tbc + } + // if next fence up + } + + const last = () => {} + + return { first, mid, last } +} + +export default useOnDragStretchX diff --git a/app/design/ui-3d/fresh/gestures/stretchZ.ts b/app/design/ui-3d/fresh/gestures/stretchZ.ts new file mode 100644 index 00000000..18eb62c6 --- /dev/null +++ b/app/design/ui-3d/fresh/gestures/stretchZ.ts @@ -0,0 +1,391 @@ +import { pipe } from "fp-ts/lib/function" +import { useRef } from "react" +import { Object3D, Vector3 } from "three" +import { A, T } from "../../../../utils/functions" +import { + setInvisibleNoRaycast, + setVisibleAndRaycast, + yAxis, +} from "../../../../utils/three" +import pointer from "../../../state/pointer" +import { updateLayoutGroupLength } from "../dimensions" +import { dispatchOutline } from "../events/outlines" +import { + getActiveHouseUserData, + getActiveLayoutGroup, + getHouseTransformsGroupUp, + getSortedVisibleColumnGroups, + getVisibleColumnGroups, + handleColumnGroupParentQuery, +} from "../helpers/sceneQueries" +import { createColumnGroup, splitColumnGroups } from "../scene/columnGroup" +import { + ColumnGroup, + HouseLayoutGroup, + HouseTransformsGroup, + StretchHandleGroup, +} from "../userData" + +const TMP_MAX_LENGTH = 10 + +const useOnDragStretchZ = () => { + const stretchZInitialDataRef = useRef<{ + direction: number + point0: Vector3 + handleColumnGroup: Object3D + houseTransformsGroup: HouseTransformsGroup + layoutGroup: HouseLayoutGroup + handleGroupZ0: number + columnGroups: Object3D[] + startColumnGroup: Object3D + midColumnGroups: Object3D[] + endColumnGroup: ColumnGroup + templateVanillaColumnGroup: ColumnGroup + vanillaLength: number + maxLength: number + midStartZ: number + midEndZ: number + } | null>(null) + + type FenceZ = { + z: number + columnGroup: ColumnGroup + } + + const stretchZProgressDataRef = useRef<{ + fences: FenceZ[] + lastDistance: number + fenceIndex: number + }>({ + fences: [], + lastDistance: 0, + fenceIndex: 0, + }) + + const addVanilla = (side: 1 | -1) => { + if (!stretchZInitialDataRef.current) return + + const { templateVanillaColumnGroup, layoutGroup } = + stretchZInitialDataRef.current + + const { fences } = stretchZProgressDataRef.current + + const lastColumnGroup = fences[fences.length - 1].columnGroup + const columnGroup = templateVanillaColumnGroup.clone() + + let z = 0 + if (side === 1) { + z = lastColumnGroup.position.z + lastColumnGroup.userData.length + } else if (side === -1) { + z = lastColumnGroup.position.z - columnGroup.userData.length + } + + columnGroup.position.setZ(z) + + setInvisibleNoRaycast(columnGroup) + + layoutGroup.add(columnGroup) + + fences.push({ + columnGroup, + z: z + columnGroup.userData.length / 2, + }) + } + + const first = ({ + handleGroup, + point, + }: { + handleGroup: StretchHandleGroup + point: Vector3 + }) => { + dispatchOutline({ + hoveredObjects: [], + selectedObjects: [], + }) + + const handleColumnGroup = handleColumnGroupParentQuery(handleGroup) + const houseTransformsGroup = getHouseTransformsGroupUp(handleColumnGroup) + + const { side } = handleGroup.userData + const { systemId, houseId, vanillaColumn } = + getActiveHouseUserData(houseTransformsGroup) + + const layoutGroup = getActiveLayoutGroup(houseTransformsGroup) + const columnGroups = pipe(layoutGroup, getSortedVisibleColumnGroups) + + const task = pipe( + T.of(vanillaColumn), + T.chain(({ gridGroups }) => + pipe( + createColumnGroup({ + systemId, + houseId, + gridGroups, + columnIndex: -1, + }), + T.map((templateVanillaColumnGroup) => { + const { startColumnGroup, midColumnGroups, endColumnGroup } = + splitColumnGroups(columnGroups) + + const vanillaLength = templateVanillaColumnGroup.userData.length + + stretchZInitialDataRef.current = { + direction: side, + handleColumnGroup: handleColumnGroup, + layoutGroup, + houseTransformsGroup, + point0: point, + handleGroupZ0: handleColumnGroup.position.z, + templateVanillaColumnGroup, + vanillaLength, + columnGroups, + startColumnGroup, + midColumnGroups, + endColumnGroup, + maxLength: TMP_MAX_LENGTH, + midStartZ: startColumnGroup.userData.length, + midEndZ: endColumnGroup.position.z, + } + + if (side === 1) { + stretchZProgressDataRef.current.fences = pipe( + midColumnGroups, + A.map((columnGroup) => { + return { + columnGroup, + z: columnGroup.position.z + columnGroup.userData.length / 2, + } + }) + ) + stretchZProgressDataRef.current.fenceIndex = + stretchZProgressDataRef.current.fences.length - 1 + + for (let i = 0; i < 3; i++) { + addVanilla(side) + } + } + + if (side === -1) { + stretchZProgressDataRef.current.fences = pipe( + midColumnGroups, + A.reverse, + A.map((columnGroup) => { + const z = columnGroup.position.z + return { + columnGroup, + z, + } + }) + ) + stretchZProgressDataRef.current.fenceIndex = + stretchZProgressDataRef.current.fences.length - 1 + + for (let i = 0; i < 3; i++) { + addVanilla(side) + } + } + + console.log(stretchZProgressDataRef.current.fences) + }) + ) + ) + ) + + task() + } + + const mid = () => { + if (!stretchZInitialDataRef.current) return + + const { + direction, + point0, + houseTransformsGroup, + handleColumnGroup, + handleGroupZ0, + vanillaLength, + templateVanillaColumnGroup, + columnGroups, + layoutGroup, + endColumnGroup, + maxLength, + midEndZ, + midStartZ, + } = stretchZInitialDataRef.current + + const { lastDistance, fences, fenceIndex } = stretchZProgressDataRef.current + + const [x1, z1] = pointer.xz + const distanceVector = new Vector3(x1, 0, z1).sub(point0) + distanceVector.applyAxisAngle( + new Vector3(0, 1, 0), + -houseTransformsGroup.rotation.y + ) + const distance = distanceVector.z + + handleColumnGroup.position.setZ(handleGroupZ0 + distance) + + // back side + if (direction === 1) { + // const cl = clamp(lo, hi) + + // additive direction to back side + if (distance > lastDistance) { + if (fenceIndex + 1 < fences.length) { + const nextFence = fences[fenceIndex + 1] + const realDistance = midEndZ + distance + if (realDistance >= nextFence.z) { + setVisibleAndRaycast(nextFence.columnGroup) + endColumnGroup.userData.columnIndex++ + nextFence.columnGroup.userData.columnIndex = + endColumnGroup.userData.columnIndex - 1 + stretchZProgressDataRef.current.fenceIndex++ + houseTransformsGroup.userData.syncWidthHandles() + + if (nextFence.z < maxLength) { + addVanilla(direction) + } + } + } + } + + // subtractive direction to back side + if (distance < lastDistance) { + if (fenceIndex > 0) { + const realDistance = midEndZ + distance + const lastVisibleFence = fences[fenceIndex] + + if (realDistance < lastVisibleFence.z) { + setInvisibleNoRaycast(lastVisibleFence.columnGroup) + stretchZProgressDataRef.current.fenceIndex-- + lastVisibleFence.columnGroup.userData.columnIndex = -1 + endColumnGroup.userData.columnIndex-- + houseTransformsGroup.userData.syncWidthHandles() + } + } + } + } + + // front side + if (direction === -1) { + // const cl = clamp(lo, hi) + // additive direction to front side + if (distance < lastDistance) { + if (fenceIndex + 1 < fences.length) { + const nextFence = fences[fenceIndex + 1] + const realDistance = midStartZ + distance + if (realDistance <= nextFence.z) { + setVisibleAndRaycast(nextFence.columnGroup) + + pipe( + layoutGroup, + getVisibleColumnGroups, + A.filter((x) => x.userData.columnIndex >= 1) + ).forEach((columnGroup) => { + columnGroup.userData.columnIndex++ + }) + + nextFence.columnGroup.userData.columnIndex = 1 + + stretchZProgressDataRef.current.fenceIndex++ + houseTransformsGroup.userData.syncWidthHandles() + + // naive + if (nextFence.z < maxLength) { + addVanilla(direction) + } + } + } + } + // subtractive direction to front side + if (distance > lastDistance) { + if (fenceIndex > 0) { + const realDistance = midStartZ + distance + const lastVisibleFence = fences[fenceIndex] + + if (realDistance > lastVisibleFence.z) { + setInvisibleNoRaycast(lastVisibleFence.columnGroup) + + lastVisibleFence.columnGroup.userData.columnIndex = -1 + + pipe( + layoutGroup, + getVisibleColumnGroups, + A.filter((x) => x.userData.columnIndex > 1) + ).forEach((columnGroup) => { + columnGroup.userData.columnIndex-- + }) + stretchZProgressDataRef.current.fenceIndex-- + houseTransformsGroup.userData.syncWidthHandles() + } + } + } + } + + stretchZProgressDataRef.current.lastDistance = distance + } + + const last = () => { + if (!stretchZInitialDataRef.current) return + + const { layoutGroup, direction, houseTransformsGroup } = + stretchZInitialDataRef.current + + const sortedVisibleColumnGroups = pipe( + layoutGroup, + getSortedVisibleColumnGroups + ) + + if (direction === 1) { + pipe( + sortedVisibleColumnGroups, + A.takeRight(2), + ([penultimateColumnGroup, endColumnGroup]) => { + endColumnGroup.position.setZ( + penultimateColumnGroup.position.z + + penultimateColumnGroup.userData.length + ) + } + ) + } + + if (direction === -1) { + pipe( + sortedVisibleColumnGroups, + A.takeLeft(2), + ([startColumnGroup, secondColumnGroup]) => { + startColumnGroup.position.setZ( + secondColumnGroup.position.z - startColumnGroup.userData.length + ) + } + ) + + const delta = -sortedVisibleColumnGroups[0].position.z + + sortedVisibleColumnGroups.forEach((columnGroup) => { + columnGroup.position.z += delta + }) + + houseTransformsGroup.position.sub( + new Vector3(0, 0, delta).applyAxisAngle( + yAxis, + houseTransformsGroup.rotation.y + ) + ) + } + + updateLayoutGroupLength(layoutGroup) + + stretchZInitialDataRef.current = null + stretchZProgressDataRef.current = { + lastDistance: 0, + fences: [], + fenceIndex: 0, + } + } + + return { first, mid, last } +} + +export default useOnDragStretchZ diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/userData.ts index 7cd1cf10..662b08a0 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/userData.ts @@ -197,6 +197,11 @@ export const isColumnGroup = (node: Object3D): node is ColumnGroup => export const isHouseLayoutGroup = (node: Object3D): node is HouseLayoutGroup => node.userData?.type === UserDataTypeEnum.Enum.HouseLayoutGroup +export const isActiveLayoutGroup = (node: Object3D): node is HouseLayoutGroup => + isHouseLayoutGroup(node) && + node.uuid === + (node.parent as HouseTransformsGroup).userData.activeLayoutGroupUuid + export const isHouseTransformsGroup = ( node: Object3D ): node is HouseTransformsGroup => From 961d222432d7c8d8af94327b7e73d8b1e62dc1b6 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 12:23:46 +0100 Subject: [PATCH 120/132] wip --- app/design/state/gestures/index.ts | 2 +- app/design/ui-3d/fresh/dimensions.ts | 2 +- app/design/ui-3d/fresh/events/modeChange.ts | 6 +- app/design/ui-3d/fresh/gestures/index.ts | 2 +- app/design/ui-3d/fresh/gestures/move.ts | 2 +- app/design/ui-3d/fresh/gestures/rotate.ts | 2 +- app/design/ui-3d/fresh/gestures/stretchX.ts | 2 +- app/design/ui-3d/fresh/gestures/stretchZ.ts | 9 +- .../ui-3d/fresh/helpers/clippingPlanes.ts | 2 +- .../ui-3d/fresh/helpers/sceneChanges.ts | 2 +- .../ui-3d/fresh/helpers/sceneQueries.ts | 2 +- app/design/ui-3d/fresh/scene/columnGroup.ts | 4 +- .../ui-3d/fresh/scene/houseLayoutGroup.ts | 200 +++++++++++------- .../ui-3d/fresh/scene/houseTransformsGroup.ts | 29 ++- app/design/ui-3d/fresh/scene/moduleGroup.ts | 2 +- .../ui-3d/fresh/{ => scene}/userData.ts | 15 +- .../ui-3d/fresh/shapes/rotateHandles.ts | 2 +- .../ui-3d/fresh/shapes/stretchHandle.ts | 2 +- app/design/ui-3d/fresh/useCuts.ts | 2 +- app/utils/three.ts | 2 +- 20 files changed, 177 insertions(+), 114 deletions(-) rename app/design/ui-3d/fresh/{ => scene}/userData.ts (93%) diff --git a/app/design/state/gestures/index.ts b/app/design/state/gestures/index.ts index f7436983..6922f27b 100644 --- a/app/design/state/gestures/index.ts +++ b/app/design/state/gestures/index.ts @@ -11,7 +11,7 @@ import { GridGroupUserData, HouseTransformsGroupUserData, UserDataTypeEnum, -} from "../../ui-3d/fresh/userData" +} from "../../ui-3d/fresh/scene/userData" import { setCameraControlsEnabled } from "../camera" import { getHouseCenter } from "../dimensions" import { dispatchMoveHouseIntent } from "../events/moveRotate" diff --git a/app/design/ui-3d/fresh/dimensions.ts b/app/design/ui-3d/fresh/dimensions.ts index 3da3de9f..c2b8162f 100644 --- a/app/design/ui-3d/fresh/dimensions.ts +++ b/app/design/ui-3d/fresh/dimensions.ts @@ -31,7 +31,7 @@ import { isHouseLayoutGroup, ModuleGroupUserData, UserDataTypeEnum, -} from "./userData" +} from "./scene/userData" export const DEBUG = false diff --git a/app/design/ui-3d/fresh/events/modeChange.ts b/app/design/ui-3d/fresh/events/modeChange.ts index e7c6ac84..87e56192 100644 --- a/app/design/ui-3d/fresh/events/modeChange.ts +++ b/app/design/ui-3d/fresh/events/modeChange.ts @@ -4,7 +4,7 @@ import { A, O, R } from "~/utils/functions" import { useSubscribeKey } from "~/utils/hooks" import { setInvisibleNoRaycast, - setVisibility, + setVisible, setVisibleAndRaycast, } from "~/utils/three" import scope from "~/design/state/scope" @@ -26,7 +26,7 @@ import { isHouseTransformsGroup, isStretchHandleGroup, UserDataTypeEnum, -} from "../userData" +} from "../scene/userData" import { RefObject } from "react" import { Group } from "three" import layoutsDB from "../../../../db/layouts" @@ -188,7 +188,7 @@ const useModeChange = (rootRef: RefObject) => { houseLayout: layout, dnas, })().then((layoutGroup) => { - setVisibility(layoutGroup, false) + setVisible(layoutGroup, false) // remove same dnas ones pipe( diff --git a/app/design/ui-3d/fresh/gestures/index.ts b/app/design/ui-3d/fresh/gestures/index.ts index e33ff576..35aee095 100644 --- a/app/design/ui-3d/fresh/gestures/index.ts +++ b/app/design/ui-3d/fresh/gestures/index.ts @@ -27,7 +27,7 @@ import { isStretchHandleMesh, StretchHandleGroup, UserDataTypeEnum, -} from "../userData" +} from "../scene/userData" import { dispatchPointerDown, dispatchPointerUp } from "./events" import useOnDragMove from "./move" import useOnDragRotate from "./rotate" diff --git a/app/design/ui-3d/fresh/gestures/move.ts b/app/design/ui-3d/fresh/gestures/move.ts index b8dec13b..18dd3104 100644 --- a/app/design/ui-3d/fresh/gestures/move.ts +++ b/app/design/ui-3d/fresh/gestures/move.ts @@ -14,7 +14,7 @@ import { HouseTransformsGroup, isHouseTransformsGroup, UserDataTypeEnum, -} from "../userData" +} from "../scene/userData" const useOnDragMove = () => { const moveData = useRef<{ diff --git a/app/design/ui-3d/fresh/gestures/rotate.ts b/app/design/ui-3d/fresh/gestures/rotate.ts index 4f993b01..525c9a93 100644 --- a/app/design/ui-3d/fresh/gestures/rotate.ts +++ b/app/design/ui-3d/fresh/gestures/rotate.ts @@ -15,7 +15,7 @@ import { HouseTransformsGroup, isHouseTransformsGroup, UserDataTypeEnum, -} from "../userData" +} from "../scene/userData" const useOnDragRotate = () => { const rotateData = useRef<{ diff --git a/app/design/ui-3d/fresh/gestures/stretchX.ts b/app/design/ui-3d/fresh/gestures/stretchX.ts index 99549ff1..561c796b 100644 --- a/app/design/ui-3d/fresh/gestures/stretchX.ts +++ b/app/design/ui-3d/fresh/gestures/stretchX.ts @@ -14,7 +14,7 @@ import { HouseTransformsGroup, isStretchHandleGroup, StretchHandleGroup, -} from "../userData" +} from "../scene/userData" type FenceX = { x: number diff --git a/app/design/ui-3d/fresh/gestures/stretchZ.ts b/app/design/ui-3d/fresh/gestures/stretchZ.ts index 18eb62c6..cdee166b 100644 --- a/app/design/ui-3d/fresh/gestures/stretchZ.ts +++ b/app/design/ui-3d/fresh/gestures/stretchZ.ts @@ -24,7 +24,7 @@ import { HouseLayoutGroup, HouseTransformsGroup, StretchHandleGroup, -} from "../userData" +} from "../scene/userData" const TMP_MAX_LENGTH = 10 @@ -106,6 +106,7 @@ const useOnDragStretchZ = () => { const handleColumnGroup = handleColumnGroupParentQuery(handleGroup) const houseTransformsGroup = getHouseTransformsGroupUp(handleColumnGroup) + houseTransformsGroup.userData.setWidthHandlesVisible(false) const { side } = handleGroup.userData const { systemId, houseId, vanillaColumn } = @@ -185,8 +186,6 @@ const useOnDragStretchZ = () => { addVanilla(side) } } - - console.log(stretchZProgressDataRef.current.fences) }) ) ) @@ -375,7 +374,9 @@ const useOnDragStretchZ = () => { ) } - updateLayoutGroupLength(layoutGroup) + layoutGroup.userData.updateLength() + houseTransformsGroup.userData.syncWidthHandles() + houseTransformsGroup.userData.setWidthHandlesVisible(true) stretchZInitialDataRef.current = null stretchZProgressDataRef.current = { diff --git a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts index fa04236c..cf86c8c4 100644 --- a/app/design/ui-3d/fresh/helpers/clippingPlanes.ts +++ b/app/design/ui-3d/fresh/helpers/clippingPlanes.ts @@ -7,7 +7,7 @@ import { HouseTransformsGroupUserData, isHouseTransformsGroup, UserDataTypeEnum, -} from "../userData" +} from "../scene/userData" import { getActiveHouseUserData, getActiveLayoutGroup, diff --git a/app/design/ui-3d/fresh/helpers/sceneChanges.ts b/app/design/ui-3d/fresh/helpers/sceneChanges.ts index fca46085..f0f334b5 100644 --- a/app/design/ui-3d/fresh/helpers/sceneChanges.ts +++ b/app/design/ui-3d/fresh/helpers/sceneChanges.ts @@ -4,7 +4,7 @@ import { setInvisibleNoRaycast, setVisibleAndRaycast, } from "../../../../utils/three" -import { HouseTransformsGroup } from "../userData" +import { HouseTransformsGroup } from "../scene/userData" import { getPartitionedLayoutGroups } from "./sceneQueries" export const debugNextLayout = (houseTransformsGroup: HouseTransformsGroup) => { diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 639a45f2..21a27fde 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -12,7 +12,7 @@ import { isHouseLayoutGroup, isHouseTransformsGroup, UserDataTypeEnum, -} from "../userData" +} from "../scene/userData" export const sortColumnsByIndex = A.sort( pipe( diff --git a/app/design/ui-3d/fresh/scene/columnGroup.ts b/app/design/ui-3d/fresh/scene/columnGroup.ts index d7d64658..ff02a48d 100644 --- a/app/design/ui-3d/fresh/scene/columnGroup.ts +++ b/app/design/ui-3d/fresh/scene/columnGroup.ts @@ -21,7 +21,7 @@ import layoutsDB, { VanillaColumnsKey, } from "../../../../db/layouts" import { A, combineGuards, O, R, S, T } from "../../../../utils/functions" -import { addDebugLineAtZ, setVisibility, yAxis } from "../../../../utils/three" +import { addDebugLineAtZ, setVisible, yAxis } from "../../../../utils/three" import { getLayoutsWorker } from "../../../../workers" import siteCtx, { getModeBools } from "../../../state/siteCtx" import { renderOBB } from "../dimensions" @@ -39,7 +39,7 @@ import { isStretchHandleMesh, ModuleGroupUserData, UserDataTypeEnum, -} from "../userData" +} from "./userData" import { findAllGuardDown } from "../helpers/sceneQueries" import { createModuleGroup } from "./moduleGroup" diff --git a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts index cbe0051f..e0c5aadf 100644 --- a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts @@ -8,25 +8,32 @@ import layoutsDB, { getHouseLayoutsKey, } from "../../../../db/layouts" import { A, combineGuards, O, R, T } from "../../../../utils/functions" -import { setVisibility, yAxis } from "../../../../utils/three" +import { setVisible, yAxis } from "../../../../utils/three" import { getLayoutsWorker } from "../../../../workers" import siteCtx, { getModeBools } from "../../../state/siteCtx" import { renderOBB } from "../dimensions" -import { findAllGuardDown } from "../helpers/sceneQueries" +import { + findAllGuardDown, + getLayoutGroupColumnGroups, + getSortedVisibleColumnGroups, +} from "../helpers/sceneQueries" import createRotateHandles from "../shapes/rotateHandles" import createStretchHandle from "../shapes/stretchHandle" import { HouseLayoutGroup, HouseLayoutGroupUserData, + isModuleGroup, isRotateHandlesGroup, isStretchHandleMesh, + ModuleGroupUserData, UserDataTypeEnum, -} from "../userData" +} from "./userData" import { createColumnGroups, getVanillaColumn, splitColumnGroups, } from "./columnGroup" +import { columnLayoutToDnas } from "../../../../workers/layouts/worker" const DEBUG = false @@ -93,7 +100,7 @@ export const createHouseLayoutGroup = ({ houseLayout, }), T.chain((columnGroups) => { - const layoutGroup = new Group() + const layoutGroup = new Group() as HouseLayoutGroup const columnCount = columnGroups.length @@ -115,6 +122,109 @@ export const createHouseLayoutGroup = ({ return pipe( getVanillaColumn({ systemId, levelTypes }), T.map((vanillaColumn) => { + const updateLength = () => { + const { length: oldLength } = layoutGroup.userData + + pipe( + layoutGroup, + getLayoutGroupColumnGroups, + A.filter((columnGroup) => !columnGroup.visible) + ).forEach((columnGroup) => { + columnGroup.removeFromParent() + }) + + const nextLength = layoutGroup.children + .filter( + (x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup + ) + .reduce((acc, v) => acc + v.userData.length, 0) + + layoutGroup.userData.length = nextLength + + layoutGroup.position.setZ(-nextLength / 2) + + layoutGroup.parent?.position.add( + new Vector3(0, 0, (nextLength - oldLength) / 2).applyAxisAngle( + yAxis, + layoutGroup.parent.rotation.y + ) + ) + + const houseTransformsGroup = layoutGroup.parent! + + const { x, y, z } = houseTransformsGroup.position + + const center = new Vector3(x, y + height / 2, z) + const halfSize = new Vector3(width / 2, height / 2, nextLength / 2) + const rotation = new Matrix3().setFromMatrix4( + houseTransformsGroup.matrix + ) + + layoutGroup.userData.obb.set(center, halfSize, rotation) + + // if (DEBUG && houseTransformsGroup.parent) { + // renderOBB( + // layoutGroup.userData.obb, + // houseTransformsGroup.parent + // ) + // } + } + + const updateDnas = () => { + let result: string[][] = [] + pipe( + layoutGroup, + getSortedVisibleColumnGroups, + // -> findAllGuardDown + A.map((v) => { + v.traverse((node) => { + if (isModuleGroup(node)) { + const { dna } = node.userData as ModuleGroupUserData + if ( + node.parent?.userData.type !== + UserDataTypeEnum.Enum.GridGroup + ) + throw new Error("non-GridGroup parent of ModuleGroup") + + const levelIndex = node.parent!.userData.levelIndex + if (!result[levelIndex]) { + result[levelIndex] = [] + } + result[levelIndex].push(dna) + } + }) + }) + ) + layoutGroup.userData.dnas = result.flat() + } + + const initStretchZHandles = () => { + const { startColumnGroup, endColumnGroup } = + splitColumnGroups(columnGroups) + + const { siteMode } = getModeBools(siteCtx.mode) + + const { length: houseLength, width: houseWidth } = + layoutGroup.userData + const backStretchZHandleGroup = createStretchHandle({ + axis: "z", + side: 1, + houseLength, + houseWidth, + }) + endColumnGroup.add(backStretchZHandleGroup) + setVisible(backStretchZHandleGroup, !siteMode) + + const frontStretchZHandleGroup = createStretchHandle({ + axis: "z", + side: -1, + houseLength, + houseWidth, + }) + startColumnGroup.add(frontStretchZHandleGroup) + setVisible(frontStretchZHandleGroup, !siteMode) + } + const userData: HouseLayoutGroupUserData = { type: UserDataTypeEnum.Enum.HouseLayoutGroup, dnas, @@ -128,85 +238,15 @@ export const createHouseLayoutGroup = ({ obb, modifiedMaterials: {}, vanillaColumn, + initStretchZHandles, + updateLength, + updateDnas, } - layoutGroup.position.setZ(-length / 2) + layoutGroup.userData = userData layoutGroup.add(...columnGroups) - - const { startColumnGroup, endColumnGroup } = - splitColumnGroups(columnGroups) - - const userDataHandler: ProxyHandler = { - set: function (target: any, prop: any, value: any) { - if (prop === "length") { - const oldLength = target[prop] - const newLength = value - target[prop] = value - - console.log( - `doing stuff, oldLength: ${oldLength}; newLength: ${newLength}` - ) - - layoutGroup.position.setZ(-newLength / 2) - - layoutGroup.parent?.position.add( - new Vector3(0, 0, (newLength - oldLength) / 2).applyAxisAngle( - yAxis, - layoutGroup.parent.rotation.y - ) - ) - - const houseTransformsGroup = layoutGroup.parent! - - const { x, y, z } = houseTransformsGroup.position - - const center = new Vector3(x, y + height / 2, z) - const halfSize = new Vector3( - width / 2, - height / 2, - newLength / 2 - ) - const rotation = new Matrix3().setFromMatrix4( - houseTransformsGroup.matrix - ) - - layoutGroup.userData.obb.set(center, halfSize, rotation) - - if (DEBUG && houseTransformsGroup.parent) { - renderOBB( - layoutGroup.userData.obb, - houseTransformsGroup.parent - ) - } - - // refreshHandles() - } - - return true // Indicate assignment success - }, - } - - layoutGroup.userData = new Proxy(userData, userDataHandler) - - const { siteMode } = getModeBools(siteCtx.mode) - - const backStretchZHandleGroup = createStretchHandle({ - axis: "z", - side: 1, - houseLength: length, - houseWidth: width, - }) - endColumnGroup.add(backStretchZHandleGroup) - setVisibility(backStretchZHandleGroup, !siteMode) - - const frontStretchZHandleGroup = createStretchHandle({ - axis: "z", - side: -1, - houseLength: length, - houseWidth: width, - }) - startColumnGroup.add(frontStretchZHandleGroup) - setVisibility(frontStretchZHandleGroup, !siteMode) + layoutGroup.position.setZ(-length / 2) + layoutGroup.userData.initStretchZHandles() return layoutGroup as HouseLayoutGroup }) diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index a3d39d39..2c0e50be 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -1,9 +1,12 @@ import { pipe } from "fp-ts/lib/function" import { Group, Plane, Vector3 } from "three" import { A, O, pipeLog, T } from "../../../../utils/functions" -import { setVisibility } from "../../../../utils/three" +import { setVisible } from "../../../../utils/three" import { getModeBools } from "../../../state/siteCtx" -import { getActiveHouseUserData } from "../helpers/sceneQueries" +import { + findAllGuardDown, + getActiveHouseUserData, +} from "../helpers/sceneQueries" import createRotateHandles from "../shapes/rotateHandles" import createStretchHandle from "../shapes/stretchHandle" import { @@ -12,9 +15,10 @@ import { HouseTransformsGroupUserData, isHouseLayoutGroup, isStretchHandleGroup, + isStretchXHandleGroup, StretchHandleGroup, UserDataTypeEnum, -} from "../userData" +} from "./userData" import { createHouseLayoutGroup, getHouseLayout } from "./houseLayoutGroup" export const BIG_CLIP_NUMBER = 999 @@ -59,7 +63,7 @@ export const createHouseTransformsGroup = ({ houseLength, }) - setVisibility(rotateHandles, siteMode) + setVisible(rotateHandles, siteMode) houseTransformsGroup.add(rotateHandles) @@ -81,7 +85,7 @@ export const createHouseTransformsGroup = ({ handle.position.setZ(houseLength / 2) houseTransformsGroup.add(handle) - setVisibility(handle, !siteMode) + setVisible(handle, !siteMode) }) } @@ -112,14 +116,22 @@ export const createHouseTransformsGroup = ({ O.map((lastLayoutGroup) => { console.log(`in ${nextLayoutGroup.uuid}`) console.log(`out ${lastLayoutGroup.uuid}`) - setVisibility(nextLayoutGroup, true) - setVisibility(lastLayoutGroup, false) + setVisible(nextLayoutGroup, true) + setVisible(lastLayoutGroup, false) houseTransformsGroup.userData.activeLayoutGroupUuid = nextLayoutGroup.uuid }) ) } + const setWidthHandlesVisible = (bool: boolean = true) => { + pipe( + houseTransformsGroup, + findAllGuardDown(isStretchXHandleGroup), + A.map((x) => void setVisible(x, bool)) + ) + } + const houseTransformsGroupUserData: HouseTransformsGroupUserData = { type: UserDataTypeEnum.Enum.HouseTransformsGroup, systemId, @@ -128,10 +140,11 @@ export const createHouseTransformsGroup = ({ friendlyName, activeLayoutGroupUuid: layoutGroup.uuid, houseTypeId, - initHandles, + initRotateAndStretchXHandles: initHandles, syncRotateHandles, syncWidthHandles, setActiveLayoutGroup, + setWidthHandlesVisible, } houseTransformsGroup.userData = houseTransformsGroupUserData diff --git a/app/design/ui-3d/fresh/scene/moduleGroup.ts b/app/design/ui-3d/fresh/scene/moduleGroup.ts index da3047fe..cb15a123 100644 --- a/app/design/ui-3d/fresh/scene/moduleGroup.ts +++ b/app/design/ui-3d/fresh/scene/moduleGroup.ts @@ -9,7 +9,7 @@ import { ElementMeshUserData, ModuleGroupUserData, UserDataTypeEnum, -} from "../userData" +} from "./userData" // speckle branch url : geometry by ifc tag export let models: Record> = {} diff --git a/app/design/ui-3d/fresh/userData.ts b/app/design/ui-3d/fresh/scene/userData.ts similarity index 93% rename from app/design/ui-3d/fresh/userData.ts rename to app/design/ui-3d/fresh/scene/userData.ts index 662b08a0..4030a193 100644 --- a/app/design/ui-3d/fresh/userData.ts +++ b/app/design/ui-3d/fresh/scene/userData.ts @@ -8,8 +8,8 @@ import { } from "three" import { OBB } from "three-stdlib" import { z } from "zod" -import { ColumnLayout, VanillaColumn } from "../../../db/layouts" -import { ScopeItem } from "../../state/scope" +import { ColumnLayout, VanillaColumn } from "../../../../db/layouts" +import { ScopeItem } from "../../../state/scope" // HouseTransformsGroup has @@ -48,10 +48,11 @@ export type HouseTransformsGroupUserData = { friendlyName: string clippingPlanes: Plane[] activeLayoutGroupUuid: string - initHandles: () => void + initRotateAndStretchXHandles: () => void syncWidthHandles: () => void syncRotateHandles: () => void setActiveLayoutGroup: (layoutGroup: HouseLayoutGroup) => void + setWidthHandlesVisible: (bool?: boolean) => void } export type HouseLayoutGroupUserData = { @@ -67,6 +68,9 @@ export type HouseLayoutGroupUserData = { columnCount: number sectionType: string modifiedMaterials: Record + initStretchZHandles: () => void + updateLength: () => void + updateDnas: () => void } export type ColumnGroupUserData = { @@ -217,6 +221,11 @@ export const isStretchHandleGroup = ( ): node is StretchHandleGroup => node.userData?.type === UserDataTypeEnum.Enum.StretchHandleGroup +export const isStretchXHandleGroup = ( + node: Object3D +): node is StretchHandleGroup => + isStretchHandleGroup(node) && node.userData.axis === "x" + export const incrementColumnCount = (layoutGroup: Object3D) => { const userData = layoutGroup.userData as HouseLayoutGroupUserData if (userData.type !== UserDataTypeEnum.Enum.HouseLayoutGroup) diff --git a/app/design/ui-3d/fresh/shapes/rotateHandles.ts b/app/design/ui-3d/fresh/shapes/rotateHandles.ts index 81b41d0a..6769a8c4 100644 --- a/app/design/ui-3d/fresh/shapes/rotateHandles.ts +++ b/app/design/ui-3d/fresh/shapes/rotateHandles.ts @@ -5,7 +5,7 @@ import { RotateHandleMeshUserData, RotateHandlesGroupUserData, UserDataTypeEnum, -} from "../userData" +} from "../scene/userData" const ROTATE_HANDLE_OFFSET = 5 const ROTATE_HANDLE_SIZE = 0.3 diff --git a/app/design/ui-3d/fresh/shapes/stretchHandle.ts b/app/design/ui-3d/fresh/shapes/stretchHandle.ts index d2218e43..1c542a1a 100644 --- a/app/design/ui-3d/fresh/shapes/stretchHandle.ts +++ b/app/design/ui-3d/fresh/shapes/stretchHandle.ts @@ -2,7 +2,7 @@ import { pipe } from "fp-ts/lib/function" import { BoxGeometry, Group, Mesh, SphereGeometry } from "three" import { A } from "../../../../utils/functions" import handleMaterial from "../handleMaterial" -import { StretchHandleGroupUserData, UserDataTypeEnum } from "../userData" +import { StretchHandleGroupUserData, UserDataTypeEnum } from "../scene/userData" const OFFSET = 0.5 const sphereGeom = new SphereGeometry(0.5) diff --git a/app/design/ui-3d/fresh/useCuts.ts b/app/design/ui-3d/fresh/useCuts.ts index 746dc1f2..794f44dd 100644 --- a/app/design/ui-3d/fresh/useCuts.ts +++ b/app/design/ui-3d/fresh/useCuts.ts @@ -4,7 +4,7 @@ import { Group } from "three" import { useSubscribe } from "../../../utils/hooks" import settings from "../../state/settings" import siteCtx from "../../state/siteCtx" -import { UserData, UserDataTypeEnum } from "./userData" +import { UserData, UserDataTypeEnum } from "./scene/userData" const useCuts = (rootRef: RefObject) => { useSubscribe( diff --git a/app/utils/three.ts b/app/utils/three.ts index ea10ff9f..b46a77fb 100644 --- a/app/utils/three.ts +++ b/app/utils/three.ts @@ -231,7 +231,7 @@ export const setInvisibleButRaycast = (object: Object3D) => { }) } -export const setVisibility = (object: Object3D, bool: boolean) => { +export const setVisible = (object: Object3D, bool: boolean) => { if (bool) { setVisibleAndRaycast(object) } else { From 3105e57a623e4beb7daf0b7f21b68f16ba57ade4 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 12:41:14 +0100 Subject: [PATCH 121/132] wip --- app/design/ui-3d/fresh/gestures/stretchZ.ts | 1 + .../ui-3d/fresh/scene/houseLayoutGroup.ts | 29 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretchZ.ts b/app/design/ui-3d/fresh/gestures/stretchZ.ts index cdee166b..a45fe0fe 100644 --- a/app/design/ui-3d/fresh/gestures/stretchZ.ts +++ b/app/design/ui-3d/fresh/gestures/stretchZ.ts @@ -377,6 +377,7 @@ const useOnDragStretchZ = () => { layoutGroup.userData.updateLength() houseTransformsGroup.userData.syncWidthHandles() houseTransformsGroup.userData.setWidthHandlesVisible(true) + layoutGroup.userData.updateDnas() stretchZInitialDataRef.current = null stretchZProgressDataRef.current = { diff --git a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts index e0c5aadf..8f0835d3 100644 --- a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts @@ -122,22 +122,27 @@ export const createHouseLayoutGroup = ({ return pipe( getVanillaColumn({ systemId, levelTypes }), T.map((vanillaColumn) => { - const updateLength = () => { - const { length: oldLength } = layoutGroup.userData - + const computeLength = () => pipe( layoutGroup, getLayoutGroupColumnGroups, - A.filter((columnGroup) => !columnGroup.visible) - ).forEach((columnGroup) => { - columnGroup.removeFromParent() - }) + A.partition((columnGroup) => columnGroup.visible), + ({ left: hiddenColumnGroups, right: activeColumnGroups }) => { + hiddenColumnGroups.forEach((columnGroup) => { + columnGroup.removeFromParent() + }) - const nextLength = layoutGroup.children - .filter( - (x) => x.userData.type === UserDataTypeEnum.Enum.ColumnGroup - ) - .reduce((acc, v) => acc + v.userData.length, 0) + return activeColumnGroups.reduce( + (acc, v) => acc + v.userData.length, + 0 + ) + } + ) + + const updateLength = (maybeLength?: number) => { + const { length: oldLength } = layoutGroup.userData + + const nextLength = maybeLength ?? computeLength() layoutGroup.userData.length = nextLength From d0c75f9df7d49f58c3585f9d7852fa5636842b8c Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 13:03:31 +0100 Subject: [PATCH 122/132] wip --- app/design/ui-3d/fresh/gestures/stretchZ.ts | 6 +--- .../ui-3d/fresh/scene/houseTransformsGroup.ts | 33 +++++++++++-------- app/design/ui-3d/fresh/scene/userData.ts | 25 ++++++++++---- .../ui-3d/fresh/shapes/stretchHandle.ts | 8 +++-- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretchZ.ts b/app/design/ui-3d/fresh/gestures/stretchZ.ts index a45fe0fe..d62cc903 100644 --- a/app/design/ui-3d/fresh/gestures/stretchZ.ts +++ b/app/design/ui-3d/fresh/gestures/stretchZ.ts @@ -240,7 +240,6 @@ const useOnDragStretchZ = () => { nextFence.columnGroup.userData.columnIndex = endColumnGroup.userData.columnIndex - 1 stretchZProgressDataRef.current.fenceIndex++ - houseTransformsGroup.userData.syncWidthHandles() if (nextFence.z < maxLength) { addVanilla(direction) @@ -260,7 +259,6 @@ const useOnDragStretchZ = () => { stretchZProgressDataRef.current.fenceIndex-- lastVisibleFence.columnGroup.userData.columnIndex = -1 endColumnGroup.userData.columnIndex-- - houseTransformsGroup.userData.syncWidthHandles() } } } @@ -288,7 +286,6 @@ const useOnDragStretchZ = () => { nextFence.columnGroup.userData.columnIndex = 1 stretchZProgressDataRef.current.fenceIndex++ - houseTransformsGroup.userData.syncWidthHandles() // naive if (nextFence.z < maxLength) { @@ -316,7 +313,6 @@ const useOnDragStretchZ = () => { columnGroup.userData.columnIndex-- }) stretchZProgressDataRef.current.fenceIndex-- - houseTransformsGroup.userData.syncWidthHandles() } } } @@ -375,7 +371,7 @@ const useOnDragStretchZ = () => { } layoutGroup.userData.updateLength() - houseTransformsGroup.userData.syncWidthHandles() + houseTransformsGroup.userData.syncLength() houseTransformsGroup.userData.setWidthHandlesVisible(true) layoutGroup.userData.updateDnas() diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index 2c0e50be..a232c7c1 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -13,6 +13,7 @@ import { HouseLayoutGroup, HouseTransformsGroup, HouseTransformsGroupUserData, + HouseTransformsHandlesGroup, isHouseLayoutGroup, isStretchHandleGroup, isStretchXHandleGroup, @@ -52,6 +53,9 @@ export const createHouseTransformsGroup = ({ new Plane(new Vector3(0, 0, NORMAL_DIRECTION), BIG_CLIP_NUMBER), ] + const handlesGroup = new Group() as HouseTransformsHandlesGroup + handlesGroup.position.setZ(-layoutGroup.userData.length / 2) + const initHandles = () => { const { width: houseWidth, length: houseLength } = getActiveHouseUserData(houseTransformsGroup) @@ -65,7 +69,7 @@ export const createHouseTransformsGroup = ({ setVisible(rotateHandles, siteMode) - houseTransformsGroup.add(rotateHandles) + handlesGroup.add(rotateHandles) const stretchXUpHandleGroup = createStretchHandle({ axis: "x", @@ -84,24 +88,26 @@ export const createHouseTransformsGroup = ({ ;[stretchXUpHandleGroup, stretchXDownHandleGroup].forEach((handle) => { handle.position.setZ(houseLength / 2) - houseTransformsGroup.add(handle) + handlesGroup.add(handle) setVisible(handle, !siteMode) }) + + houseTransformsGroup.add(handlesGroup) } - const syncRotateHandles = () => {} + const syncLength = () => { + const { length: houseLength } = + getActiveHouseUserData(houseTransformsGroup) + + // handlesGroup.position.setZ(-houseLength / 2) - const syncWidthHandles = () => { - const widthHandles = pipe( - houseTransformsGroup.children, - A.filter((x): x is StretchHandleGroup => { - return isStretchHandleGroup(x) && x.userData.axis === "x" - }) + const xStretchHandles = pipe( + handlesGroup, + findAllGuardDown(isStretchXHandleGroup) ) - widthHandles.forEach((handle) => { - const { length } = getActiveHouseUserData(houseTransformsGroup) - handle.userData.updateXHandleLength(length) + xStretchHandles.forEach((handle) => { + handle.userData.updateXHandleLength(houseLength) }) } @@ -141,8 +147,7 @@ export const createHouseTransformsGroup = ({ activeLayoutGroupUuid: layoutGroup.uuid, houseTypeId, initRotateAndStretchXHandles: initHandles, - syncRotateHandles, - syncWidthHandles, + syncLength, setActiveLayoutGroup, setWidthHandlesVisible, } diff --git a/app/design/ui-3d/fresh/scene/userData.ts b/app/design/ui-3d/fresh/scene/userData.ts index 4030a193..c24f33d6 100644 --- a/app/design/ui-3d/fresh/scene/userData.ts +++ b/app/design/ui-3d/fresh/scene/userData.ts @@ -12,23 +12,28 @@ import { ColumnLayout, VanillaColumn } from "../../../../db/layouts" import { ScopeItem } from "../../../state/scope" // HouseTransformsGroup has - +// -> HouseTransformsHandlesGroup (rotate and X-Stretch handles) +// -> Stretch X Handle groups +// -> Stretch X Handle meshes +// -> Rotate Handles group +// -> Rotate Handles meshes // -> HouseLayoutGroup's as children // (alternative layouts; // visibility/raycasting disabled // except 1) // -> ColumnsGroup's as children have // -> GridGroup's as children have +// -> Z-Stretch handles (special case) // -> ModuleGroup's as children have // -> ElementMesh's as children -// -> Stretch Handles meshes -// -> Rotate Handles group -// -> Rotate Handles meshes export const UserDataTypeEnum = z.enum([ "HouseTransformsGroup", + "HouseTransformsHandlesGroup", "HouseLayoutGroup", "ColumnGroup", + // layout group handles go in start/end column groups + // this is a special case for stretch Z handles "GridGroup", "ModuleGroup", "ElementMesh", @@ -49,12 +54,15 @@ export type HouseTransformsGroupUserData = { clippingPlanes: Plane[] activeLayoutGroupUuid: string initRotateAndStretchXHandles: () => void - syncWidthHandles: () => void - syncRotateHandles: () => void + syncLength: () => void setActiveLayoutGroup: (layoutGroup: HouseLayoutGroup) => void setWidthHandlesVisible: (bool?: boolean) => void } +export type HouseTransformsHandlesGroupUserData = { + type: typeof UserDataTypeEnum.Enum.HouseTransformsHandlesGroup +} + export type HouseLayoutGroupUserData = { type: typeof UserDataTypeEnum.Enum.HouseLayoutGroup dnas: string[] @@ -132,6 +140,7 @@ export type UserData = | ColumnGroupUserData | HouseLayoutGroupUserData | HouseTransformsGroupUserData + | HouseTransformsHandlesGroupUserData | StretchHandleMeshUserData | RotateHandlesGroupUserData @@ -177,6 +186,10 @@ export type StretchHandleGroup = Group & { userData: StretchHandleGroupUserData } +export type HouseTransformsHandlesGroup = Group & { + userData: HouseTransformsHandlesGroupUserData +} + // Type Guards export const isElementMesh = (node: Object3D): node is ElementMesh => node.userData?.type === UserDataTypeEnum.Enum.ElementMesh diff --git a/app/design/ui-3d/fresh/shapes/stretchHandle.ts b/app/design/ui-3d/fresh/shapes/stretchHandle.ts index 1c542a1a..bbdd96f5 100644 --- a/app/design/ui-3d/fresh/shapes/stretchHandle.ts +++ b/app/design/ui-3d/fresh/shapes/stretchHandle.ts @@ -66,8 +66,12 @@ const createStretchHandle = ({ axis, side, type: UserDataTypeEnum.Enum.StretchHandleGroup, - updateXHandleLength: (length: number) => { - boxMesh.scale.setZ(length) + updateXHandleLength: (nextLength: number) => { + boxMesh.scale.setZ(nextLength) + sphereMeshes.forEach((sphereMesh, i) => { + const sign = i === 0 ? 1 : -1 + sphereMesh.position.setZ((nextLength / 2) * sign) + }) }, } From 6759419b9abb16a76040a5b5c40cb70c45d1e19b Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 13:25:52 +0100 Subject: [PATCH 123/132] wip breadth vs depth guarding --- app/design/ui-3d/fresh/gestures/stretchX.ts | 19 ++++++++------- .../ui-3d/fresh/helpers/sceneQueries.ts | 24 +++++++++++++++++-- app/design/ui-3d/fresh/scene/userData.ts | 5 ++++ 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretchX.ts b/app/design/ui-3d/fresh/gestures/stretchX.ts index 561c796b..884a2435 100644 --- a/app/design/ui-3d/fresh/gestures/stretchX.ts +++ b/app/design/ui-3d/fresh/gestures/stretchX.ts @@ -5,6 +5,9 @@ import { A, O, someOrError } from "../../../../utils/functions" import pointer from "../../../state/pointer" import { dispatchOutline } from "../events/outlines" import { + findAllGuardDown, + findFirstGuardAcross, + findFirstGuardDown, getHouseTransformsGroupUp, getPartitionedLayoutGroups, sortLayoutGroupsByWidth, @@ -12,7 +15,10 @@ import { import { HouseLayoutGroup, HouseTransformsGroup, + isHouseTransformsGroup, + isHouseTransformsHandlesGroup, isStretchHandleGroup, + isStretchXHandleGroup, StretchHandleGroup, } from "../scene/userData" @@ -54,14 +60,11 @@ const useOnDragStretchX = () => { getPartitionedLayoutGroups(houseTransformsGroup) const otherSideHandleGroup = pipe( - houseTransformsGroup.children, - A.findFirst((x): x is StretchHandleGroup => { - return ( - isStretchHandleGroup(x) && - x.userData.axis === "x" && - x.userData.side === otherSide - ) - }), + houseTransformsGroup, + findFirstGuardAcross( + (x): x is StretchHandleGroup => + isStretchXHandleGroup(x) && x.userData.side === otherSide + ), someOrError(`other side handle group not found`) ) diff --git a/app/design/ui-3d/fresh/helpers/sceneQueries.ts b/app/design/ui-3d/fresh/helpers/sceneQueries.ts index 21a27fde..2affcab8 100644 --- a/app/design/ui-3d/fresh/helpers/sceneQueries.ts +++ b/app/design/ui-3d/fresh/helpers/sceneQueries.ts @@ -44,6 +44,25 @@ export const findFirstGuardUp = return O.none } +export const findFirstGuardAcross = + (guard: (o: Object3D) => o is T) => + (object: Object3D): O.Option => { + const queue: Object3D[] = [object] + + while (queue.length > 0) { + const current = queue.shift() + if (!current) continue + + if (guard(current)) { + return O.some(current) + } + + queue.push(...current.children) + } + + return O.none + } + export const findFirstGuardDown = (guard: (o: Object3D) => o is T) => (object: Object3D): O.Option => { @@ -55,8 +74,9 @@ export const findFirstGuardDown = for (let i = 0, l = children.length; i < l; i++) { const child = children[i] - if (guard(child)) { - return O.some(child) + const result = findFirstGuardDown(guard)(child) + if (O.isSome(result)) { + return result } } diff --git a/app/design/ui-3d/fresh/scene/userData.ts b/app/design/ui-3d/fresh/scene/userData.ts index c24f33d6..ac9cbf5c 100644 --- a/app/design/ui-3d/fresh/scene/userData.ts +++ b/app/design/ui-3d/fresh/scene/userData.ts @@ -224,6 +224,11 @@ export const isHouseTransformsGroup = ( ): node is HouseTransformsGroup => node.userData?.type === UserDataTypeEnum.Enum.HouseTransformsGroup +export const isHouseTransformsHandlesGroup = ( + node: Object3D +): node is HouseTransformsHandlesGroup => + node.userData?.type === UserDataTypeEnum.Enum.HouseTransformsHandlesGroup + export const isRotateHandlesGroup = ( node: Object3D ): node is RotateHandlesGroup => From 59d92348497f5dd23760d243459a7fc54a021b22 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 21:14:16 +0100 Subject: [PATCH 124/132] wip --- app/design/ui-3d/fresh/events/modeChange.ts | 187 ++++++++++++------ .../ui-3d/fresh/scene/houseLayoutGroup.ts | 17 ++ .../ui-3d/fresh/scene/houseTransformsGroup.ts | 24 ++- app/design/ui-3d/fresh/scene/userData.ts | 2 + app/workers/layouts/worker.ts | 12 ++ 5 files changed, 183 insertions(+), 59 deletions(-) diff --git a/app/design/ui-3d/fresh/events/modeChange.ts b/app/design/ui-3d/fresh/events/modeChange.ts index 87e56192..8aede409 100644 --- a/app/design/ui-3d/fresh/events/modeChange.ts +++ b/app/design/ui-3d/fresh/events/modeChange.ts @@ -1,6 +1,6 @@ import { invalidate } from "@react-three/fiber" import { pipe } from "fp-ts/lib/function" -import { A, O, R } from "~/utils/functions" +import { A, O, R, T } from "~/utils/functions" import { useSubscribeKey } from "~/utils/hooks" import { setInvisibleNoRaycast, @@ -16,22 +16,25 @@ import siteCtx, { import useClippingPlaneHelpers from "../helpers/clippingPlanes" import { findAllGuardDown, + findFirstGuardAcross, findFirstGuardDown, getActiveHouseUserData, getActiveLayoutGroup, } from "../helpers/sceneQueries" import { + HouseLayoutGroup, HouseTransformsGroup, isHouseLayoutGroup, isHouseTransformsGroup, isStretchHandleGroup, UserDataTypeEnum, } from "../scene/userData" -import { RefObject } from "react" +import { RefObject, useEffect } from "react" import { Group } from "three" import layoutsDB from "../../../../db/layouts" import { BIG_CLIP_NUMBER } from "../scene/houseTransformsGroup" import { createHouseLayoutGroup } from "../scene/houseLayoutGroup" +import { liveQuery } from "dexie" const useModeChange = (rootRef: RefObject) => { const showHouseStretchHandles = (houseId: string) => { @@ -146,69 +149,143 @@ const useModeChange = (rootRef: RefObject) => { } } + // set up a separate listener for when houses dnas changes in the db + + useEffect(() => { + const { unsubscribe } = liveQuery(() => + layoutsDB.altSectionTypeLayouts.toArray() + ).subscribe((dbAltSectionTypeLayouts) => { + if (!rootRef.current) return + + for (let { houseId, altSectionTypeLayouts } of dbAltSectionTypeLayouts) { + pipe( + rootRef.current, + findFirstGuardAcross( + (x): x is HouseTransformsGroup => + isHouseTransformsGroup(x) && x.userData.houseId === houseId + ), + O.map((houseTransformsGroup) => { + // delete non-active layout groups + // return active layout group + const maybeActiveLayoutGroup = pipe( + houseTransformsGroup.children, + A.partition( + (x): x is HouseLayoutGroup => + isHouseLayoutGroup(x) && + x.uuid !== houseTransformsGroup.userData.activeLayoutGroupUuid + ), + ({ right: activeLayoutGroups, right: otherLayoutGroups }) => { + otherLayoutGroups.forEach((x) => { + console.log(`removing for st ${x.userData.sectionType}`) + x.removeFromParent() + }) + return pipe(activeLayoutGroups, A.head) + } + ) + + if (houseId === siteCtx.houseId) { + pipe( + maybeActiveLayoutGroup, + O.map((activeLayoutGroup) => { + pipe( + altSectionTypeLayouts, + R.filterMap(({ sectionType, layout, dnas }) => { + // maybe add the active section type to the house transforms group? + if ( + sectionType.code === + activeLayoutGroup.userData.sectionType + ) + return O.none + + createHouseLayoutGroup({ + systemId: houseTransformsGroup.userData.systemId, + dnas, + houseId, + houseLayout: layout, + })().then((layoutGroup) => { + console.log(`posting for st ${sectionType.code}`) + setInvisibleNoRaycast(layoutGroup) + houseTransformsGroup.add(layoutGroup) + }) + + return O.none + }) + ) + }) + ) + } + }) + ) + } + }) + + return unsubscribe + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + useSubscribeKey(scope, "selected", processHandles) useModeChangeListener(async ({ previous, next }) => { processHandles() processClippingPlanes() - if ( - previous === SiteCtxModeEnum.Enum.SITE && - next === SiteCtxModeEnum.Enum.BUILDING - ) { - // house Id - // get the stretch x stuff out of the db? - // maybe clean out old stuff - // ultimately pop some alternative invisible layouts in there! + // if ( + // previous === SiteCtxModeEnum.Enum.SITE && + // next === SiteCtxModeEnum.Enum.BUILDING + // ) { + // // house Id + // // get the stretch x stuff out of the db? + // // maybe clean out old stuff + // // ultimately pop some alternative invisible layouts in there! - const { houseId } = siteCtx + // const { houseId } = siteCtx - if (!houseId) return + // if (!houseId) return - const dbResult = await layoutsDB.altSectionTypeLayouts.get(houseId) + // const dbResult = await layoutsDB.altSectionTypeLayouts.get(houseId) - if (!dbResult) return + // if (!dbResult) return - if (!rootRef.current) return + // if (!rootRef.current) return - pipe( - rootRef.current, - findFirstGuardDown((x): x is HouseTransformsGroup => { - if (!isHouseTransformsGroup(x)) return false - if (x.userData.houseId !== houseId) return false - return true - }), - O.map((houseTransformsGroup) => { - const { systemId } = getActiveHouseUserData(houseTransformsGroup) - - pipe( - dbResult.altSectionTypeLayouts, - R.map(({ layout, dnas }) => { - createHouseLayoutGroup({ - systemId, - houseId, - houseLayout: layout, - dnas, - })().then((layoutGroup) => { - setVisible(layoutGroup, false) - - // remove same dnas ones - pipe( - houseTransformsGroup, - findAllGuardDown(isHouseLayoutGroup), - A.filter( - (x) => x.userData.dnas.toString() === dnas.toString() - ) - ).forEach((layoutGroup) => { - console.log(`removing ${layoutGroup.userData.sectionType}`) - layoutGroup.removeFromParent() - }) - houseTransformsGroup.add(layoutGroup) - console.log(houseTransformsGroup.children) - }) - }) - ) - }) - ) - } + // pipe( + // rootRef.current, + // findFirstGuardDown((x): x is HouseTransformsGroup => { + // if (!isHouseTransformsGroup(x)) return false + // if (x.userData.houseId !== houseId) return false + // return true + // }), + // O.map((houseTransformsGroup) => { + // const { systemId } = getActiveHouseUserData(houseTransformsGroup) + + // pipe( + // dbResult.altSectionTypeLayouts, + // R.map(({ layout, dnas }) => { + // createHouseLayoutGroup({ + // systemId, + // houseId, + // houseLayout: layout, + // dnas, + // })().then((layoutGroup) => { + // setVisible(layoutGroup, false) + + // // remove same dnas ones + // pipe( + // houseTransformsGroup, + // findAllGuardDown(isHouseLayoutGroup), + // A.filter( + // (x) => x.userData.dnas.toString() === dnas.toString() + // ) + // ).forEach((layoutGroup) => { + // console.log(`removing ${layoutGroup.userData.sectionType}`) + // layoutGroup.removeFromParent() + // }) + // houseTransformsGroup.add(layoutGroup) + // console.log(houseTransformsGroup.children) + // }) + // }) + // ) + // }) + // ) + // } }) } diff --git a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts index 8f0835d3..b2fa1229 100644 --- a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts @@ -22,6 +22,8 @@ import createStretchHandle from "../shapes/stretchHandle" import { HouseLayoutGroup, HouseLayoutGroupUserData, + HouseTransformsGroup, + isHouseLayoutGroup, isModuleGroup, isRotateHandlesGroup, isStretchHandleMesh, @@ -34,6 +36,7 @@ import { splitColumnGroups, } from "./columnGroup" import { columnLayoutToDnas } from "../../../../workers/layouts/worker" +import userDB from "../../../../db/user" const DEBUG = false @@ -201,6 +204,20 @@ export const createHouseLayoutGroup = ({ }) ) layoutGroup.userData.dnas = result.flat() + + const houseTransformsGroup = + layoutGroup.parent as HouseTransformsGroup + + houseTransformsGroup.userData.refreshAltLayouts() + + if ( + houseTransformsGroup.userData.activeLayoutGroupUuid === + layoutGroup.uuid + ) { + userDB.houses.update(houseId, { + dnas, + }) + } } const initStretchZHandles = () => { diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index a232c7c1..fbe2bc7e 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -120,12 +120,12 @@ export const createHouseTransformsGroup = ({ x.uuid === houseTransformsGroup.userData.activeLayoutGroupUuid ), O.map((lastLayoutGroup) => { - console.log(`in ${nextLayoutGroup.uuid}`) - console.log(`out ${lastLayoutGroup.uuid}`) setVisible(nextLayoutGroup, true) setVisible(lastLayoutGroup, false) houseTransformsGroup.userData.activeLayoutGroupUuid = nextLayoutGroup.uuid + houseTransformsGroup.userData.activeLayoutDnas = + nextLayoutGroup.userData.dnas }) ) } @@ -138,18 +138,34 @@ export const createHouseTransformsGroup = ({ ) } + const refreshAltLayouts = () => { + // remove old alts + // pipe( + // houseTransformsGroup.children, + // A.filter( + // (x): x is HouseLayoutGroup => + // isHouseLayoutGroup(x) && x.uuid !== houseTransformsGroup.uuid + // ) + // ).forEach((x) => { + // x.removeFromParent() + // }) + // section type layouts + } + const houseTransformsGroupUserData: HouseTransformsGroupUserData = { type: UserDataTypeEnum.Enum.HouseTransformsGroup, systemId, + houseTypeId, houseId, + activeLayoutGroupUuid: layoutGroup.uuid, + activeLayoutDnas: layoutGroup.userData.dnas, clippingPlanes, friendlyName, - activeLayoutGroupUuid: layoutGroup.uuid, - houseTypeId, initRotateAndStretchXHandles: initHandles, syncLength, setActiveLayoutGroup, setWidthHandlesVisible, + refreshAltLayouts, } houseTransformsGroup.userData = houseTransformsGroupUserData diff --git a/app/design/ui-3d/fresh/scene/userData.ts b/app/design/ui-3d/fresh/scene/userData.ts index ac9cbf5c..f31f95a4 100644 --- a/app/design/ui-3d/fresh/scene/userData.ts +++ b/app/design/ui-3d/fresh/scene/userData.ts @@ -53,10 +53,12 @@ export type HouseTransformsGroupUserData = { friendlyName: string clippingPlanes: Plane[] activeLayoutGroupUuid: string + activeLayoutDnas: string[] initRotateAndStretchXHandles: () => void syncLength: () => void setActiveLayoutGroup: (layoutGroup: HouseLayoutGroup) => void setWidthHandlesVisible: (bool?: boolean) => void + refreshAltLayouts: () => void } export type HouseTransformsHandlesGroupUserData = { diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 06a12b9b..da6ebd08 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -438,6 +438,18 @@ export const columnLayoutToDnas = ( RA.flatten ) as string[] +const foo = ({ + systemId, + houseId, + dnas, +}: { + systemId: string + houseId: string + dnas: string[] +}) => { + console.log({ systemId, houseId, dnas }) +} + if (!isSSR()) { liveQuery(async () => { const houses = await userDB.houses.toArray() From d8c1e7b5c2e69b73bdaec020c4a6194498c5d46f Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Tue, 22 Aug 2023 21:38:38 +0100 Subject: [PATCH 125/132] wip half fixed --- app/design/ui-3d/fresh/events/modeChange.ts | 21 +++++++++++++++---- .../ui-3d/fresh/scene/houseLayoutGroup.ts | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/design/ui-3d/fresh/events/modeChange.ts b/app/design/ui-3d/fresh/events/modeChange.ts index 8aede409..03669591 100644 --- a/app/design/ui-3d/fresh/events/modeChange.ts +++ b/app/design/ui-3d/fresh/events/modeChange.ts @@ -155,6 +155,7 @@ const useModeChange = (rootRef: RefObject) => { const { unsubscribe } = liveQuery(() => layoutsDB.altSectionTypeLayouts.toArray() ).subscribe((dbAltSectionTypeLayouts) => { + console.log(`db alt section layouts type sub run`) if (!rootRef.current) return for (let { houseId, altSectionTypeLayouts } of dbAltSectionTypeLayouts) { @@ -165,16 +166,22 @@ const useModeChange = (rootRef: RefObject) => { isHouseTransformsGroup(x) && x.userData.houseId === houseId ), O.map((houseTransformsGroup) => { + console.log(`house transforms group`, houseTransformsGroup) // delete non-active layout groups // return active layout group const maybeActiveLayoutGroup = pipe( houseTransformsGroup.children, + A.filter(isHouseLayoutGroup), A.partition( - (x): x is HouseLayoutGroup => - isHouseLayoutGroup(x) && - x.uuid !== houseTransformsGroup.userData.activeLayoutGroupUuid + (x) => + x.uuid === houseTransformsGroup.userData.activeLayoutGroupUuid ), - ({ right: activeLayoutGroups, right: otherLayoutGroups }) => { + ({ left: otherLayoutGroups, right: activeLayoutGroups }) => { + console.log({ + uuid: houseTransformsGroup.userData.activeLayoutGroupUuid, + activeLayoutGroups, + otherLayoutGroups, + }) otherLayoutGroups.forEach((x) => { console.log(`removing for st ${x.userData.sectionType}`) x.removeFromParent() @@ -183,6 +190,10 @@ const useModeChange = (rootRef: RefObject) => { } ) + console.log(maybeActiveLayoutGroup) + + console.log(siteCtx.houseId) + if (houseId === siteCtx.houseId) { pipe( maybeActiveLayoutGroup, @@ -197,6 +208,8 @@ const useModeChange = (rootRef: RefObject) => { ) return O.none + console.log(`hello ${houseId} ${sectionType.code}`) + createHouseLayoutGroup({ systemId: houseTransformsGroup.userData.systemId, dnas, diff --git a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts index b2fa1229..1fc1ec44 100644 --- a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts @@ -214,6 +214,7 @@ export const createHouseLayoutGroup = ({ houseTransformsGroup.userData.activeLayoutGroupUuid === layoutGroup.uuid ) { + console.log(`updating dnas in db`) userDB.houses.update(houseId, { dnas, }) From 15c6bd2fdda25b1914e5b972f2bb2a1c88cd5f26 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 23 Aug 2023 10:20:29 +0100 Subject: [PATCH 126/132] wip --- app/design/ui-3d/fresh/events/modeChange.ts | 2 - app/workers/layouts/worker.ts | 361 ++++++++++++-------- 2 files changed, 211 insertions(+), 152 deletions(-) diff --git a/app/design/ui-3d/fresh/events/modeChange.ts b/app/design/ui-3d/fresh/events/modeChange.ts index 03669591..618cb2cc 100644 --- a/app/design/ui-3d/fresh/events/modeChange.ts +++ b/app/design/ui-3d/fresh/events/modeChange.ts @@ -208,8 +208,6 @@ const useModeChange = (rootRef: RefObject) => { ) return O.none - console.log(`hello ${houseId} ${sectionType.code}`) - createHouseLayoutGroup({ systemId: houseTransformsGroup.userData.systemId, dnas, diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index da6ebd08..90a01055 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -438,7 +438,7 @@ export const columnLayoutToDnas = ( RA.flatten ) as string[] -const foo = ({ +export const foo = ({ systemId, houseId, dnas, @@ -450,166 +450,166 @@ const foo = ({ console.log({ systemId, houseId, dnas }) } -if (!isSSR()) { - liveQuery(async () => { - const houses = await userDB.houses.toArray() - const sectionTypes = await systemsDB.sectionTypes.toArray() +const changeLayoutSectionType = ({ + systemId, + layout, + sectionType: st, +}: { + systemId: string + layout: ColumnLayout + sectionType: SectionType +}) => { + const { code: sectionType } = st - return { houses, sectionTypes } - }).subscribe(async ({ houses, sectionTypes }) => { - // for each house, for each section type - for (const house of houses) { - const { systemId, dnas, houseId: houseId } = house + return pipe( + layout, + A.traverse(TO.ApplicativeSeq)((positionedColumn) => + pipe( + positionedColumn.gridGroups, + A.traverse(TO.ApplicativeSeq)((gridGroup) => { + const { + modules, + modules: [ + { + module: { + structuredDna: { levelType, positionType, gridType }, + }, + }, + ], + } = gridGroup - const layout = await getLayout({ systemId, dnas }) + const vanillaModuleTask: TO.TaskOption = pipe( + TO.fromTask(() => + getIndexedVanillaModule({ + systemId, + sectionType, + positionType, + levelType, + gridType, + }) + ), + TO.chainOptionK( + flow( + O.fromNullable, + O.chain((a) => + pipe( + modulesCache, + A.findFirst( + (b) => a.systemId === b.systemId && a.moduleDna === b.dna + ) + ) + ) + ) + ) + ) - const changeLayoutSectionType = ( - layout: ColumnLayout, - st: SectionType - ) => { - const { code: sectionType } = st - - return pipe( - layout, - A.traverse(TO.ApplicativeSeq)((positionedColumn) => - pipe( - positionedColumn.gridGroups, - A.traverse(TO.ApplicativeSeq)((gridGroup) => { - const { - modules, - modules: [ - { - module: { - structuredDna: { levelType, positionType, gridType }, + return pipe( + vanillaModuleTask, + TO.chain((vanillaModule) => + pipe( + modules, + reduceToOption( + O.some([]), + (i, acc: O.Option, positionedModule) => { + // target is existent module with target section type + const target = { + systemId, + structuredDna: { + ...positionedModule.module.structuredDna, + sectionType: st.code, }, - }, - ], - } = gridGroup + } as Module - const vanillaModuleTask: TO.TaskOption = pipe( - TO.fromTask(() => - getIndexedVanillaModule({ - systemId, - sectionType, - positionType, - levelType, - gridType, - }) - ), - TO.chainOptionK( - flow( - O.fromNullable, - O.chain((a) => - pipe( - modulesCache, - A.findFirst( - (b) => - a.systemId === b.systemId && a.moduleDna === b.dna - ) - ) - ) + const compatModules = pipe( + modulesCache, + filterCompatibleModules()(target) ) - ) - ) - return pipe( - vanillaModuleTask, - TO.chain((vanillaModule) => - pipe( - modules, - reduceToOption( - O.some([]), - ( - i, - acc: O.Option, - positionedModule - ) => { - // target is existent module with target section type - const target = { - systemId, - structuredDna: { - ...positionedModule.module.structuredDna, - sectionType: st.code, - }, - } as Module - - const compatModules = pipe( - modulesCache, - filterCompatibleModules()(target) - ) - - if (compatModules.length === 0) return O.none - - return pipe( - compatModules, - topCandidateByHamming(target), - O.map((bestModule) => { - const distanceToTarget = - target.structuredDna.gridUnits - - bestModule.structuredDna.gridUnits - switch (true) { - case sign(distanceToTarget) > 0: - // fill in some vanilla - return [ - bestModule, - ...A.replicate( - distanceToTarget / vanillaModule.length, - vanillaModule - ), - ] - case sign(distanceToTarget) < 0: - // abort and only vanilla - return A.replicate( - positionedModule.module.length / - vanillaModule.length, - vanillaModule - ) - - case sign(distanceToTarget) === 0: - default: - return [bestModule] - // swap the module - } - }), - O.map((nextModules) => - pipe( - acc, - O.map((positionedModules) => [ - ...positionedModules, - ...nextModules.map( - (module, i) => - ({ - module, - z: positionedModule.z, - gridGroupIndex: i, - } as PositionedModule) - ), - ]) - ) - ), - O.flatten - ) + if (compatModules.length === 0) return O.none + + return pipe( + compatModules, + topCandidateByHamming(target), + O.map((bestModule) => { + const distanceToTarget = + target.structuredDna.gridUnits - + bestModule.structuredDna.gridUnits + switch (true) { + case sign(distanceToTarget) > 0: + // fill in some vanilla + return [ + bestModule, + ...A.replicate( + distanceToTarget / vanillaModule.length, + vanillaModule + ), + ] + case sign(distanceToTarget) < 0: + // abort and only vanilla + return A.replicate( + positionedModule.module.length / + vanillaModule.length, + vanillaModule + ) + + case sign(distanceToTarget) === 0: + default: + return [bestModule] + // swap the module } + }), + O.map((nextModules) => + pipe( + acc, + O.map((positionedModules) => [ + ...positionedModules, + ...nextModules.map( + (module, i) => + ({ + module, + z: positionedModule.z, + gridGroupIndex: i, + } as PositionedModule) + ), + ]) + ) ), - O.map( - (modules): GridGroup => ({ - ...gridGroup, - modules, - }) - ), - TO.fromOption + O.flatten ) - ) - ) - }), - TO.map((gridGroups) => ({ - ...positionedColumn, - gridGroups, - })) + } + ), + O.map( + (modules): GridGroup => ({ + ...gridGroup, + modules, + }) + ), + TO.fromOption + ) ) ) - )() - } + }), + TO.map((gridGroups) => ({ + ...positionedColumn, + gridGroups, + })) + ) + ) + )() +} + +if (!isSSR()) { + liveQuery(async () => { + const houses = await userDB.houses.toArray() + const sectionTypes = await systemsDB.sectionTypes.toArray() + + return { houses, sectionTypes } + }).subscribe(async ({ houses, sectionTypes }) => { + // for each house, for each section type + for (const house of houses) { + const { systemId, dnas, houseId: houseId } = house + + const layout = await getLayout({ systemId, dnas }) const otherSectionTypes = sectionTypes.filter( (x) => @@ -627,7 +627,7 @@ if (!isSSR()) { sectionType: SectionType }> => () => - changeLayoutSectionType(layout, sectionType).then( + changeLayoutSectionType({ systemId, layout, sectionType }).then( O.map((layout) => ({ layout, sectionType })) ) ), @@ -664,6 +664,66 @@ if (!isSSR()) { }) } +// const updateAltSectionTypeLayouts = async ({ +// houseId, +// currentSectionTypeCode +// }: { +// houseId: string +// currentSectionTypeCode: string +// }) => { +// layoutsDB.altSectionTypeLayouts.delete(houseId) + +// const dbSectionTypes = await systemsDB.sectionTypes.toArray() + +// const otherSectionTypes = dbSectionTypes.filter( +// (x) => +// x.code !== +// currentSectionTypeCode +// ) + +// const otherLayouts = await pipe( +// otherSectionTypes, +// A.map( +// ( +// sectionType +// ): TO.TaskOption<{ +// layout: ColumnLayout +// sectionType: SectionType +// }> => +// () => +// changeLayoutSectionType({ systemId, layout, sectionType }).then( +// O.map((layout) => ({ layout, sectionType })) +// ) +// ), +// A.sequence(T.ApplicativeSeq), +// unwrapSome, +// (x) => x +// )() +// const altSectionTypeLayouts = pipe( +// otherLayouts, +// A.reduce( +// {}, +// ( +// acc: Record< +// string, +// { layout: ColumnLayout; sectionType: SectionType; dnas: string[] } +// >, +// { layout, sectionType } +// ) => { +// return { +// ...acc, +// [sectionType.code]: { +// layout, +// sectionType, +// dnas: columnLayoutToDnas(layout), +// }, +// } +// } +// ) +// ) +// layoutsDB.altSectionTypeLayouts.put({ houseId, altSectionTypeLayouts }) +// } + const getVanillaColumn = async (key: VanillaColumnsKey) => { const maybeVanillaColumn = await layoutsDB.vanillaColumns.get( getVanillaColumnsKey(key) @@ -682,6 +742,7 @@ const api = { getLayout, syncModels, getVanillaColumn, + // updateAltSectionTypeLayouts, } export type LayoutsAPI = typeof api From 34aefb7fcbd5d15abef3838b255188f812e0b910 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 23 Aug 2023 11:59:00 +0100 Subject: [PATCH 127/132] wip width then length working --- app/db/layouts.ts | 11 +- app/design/state/vanilla.ts | 6 +- app/design/ui-3d/fresh/events/modeChange.ts | 5 - app/design/ui-3d/fresh/scene/columnGroup.ts | 12 +- .../ui-3d/fresh/scene/houseLayoutGroup.ts | 69 ++-------- .../ui-3d/fresh/scene/houseTransformsGroup.ts | 52 ++++++- app/design/ui-3d/fresh/scene/userData.ts | 1 + app/workers/layouts/worker.ts | 128 +++++++++++------- 8 files changed, 154 insertions(+), 130 deletions(-) diff --git a/app/db/layouts.ts b/app/db/layouts.ts index 0d7d1541..69070d70 100644 --- a/app/db/layouts.ts +++ b/app/db/layouts.ts @@ -92,24 +92,27 @@ export type VanillaColumn = Omit export type IndexedVanillaColumn = { systemId: string + sectionType: string levelTypes: string[] vanillaColumn: VanillaColumn } export type VanillaColumnsKey = { systemId: string + sectionType: string levelTypes: string[] } export const getVanillaColumnsKey = ({ systemId, + sectionType, levelTypes, -}: VanillaColumnsKey): string => `${systemId}:${levelTypes}` +}: VanillaColumnsKey): string => `${systemId}:${sectionType}:${levelTypes}` export const invertVanillaColumnsKey = (key: string): VanillaColumnsKey => { - const [systemId, levelTypesString] = key.split(":") + const [systemId, sectionType, levelTypesString] = key.split(":") const levelTypes = levelTypesString.split(",") - return { systemId, levelTypes } + return { systemId, sectionType, levelTypes } } type IndexedAltSectionTypeLayouts = { @@ -133,7 +136,7 @@ class LayoutsDatabase extends Dexie { models: "speckleBranchUrl,systemId", houseLayouts: "[systemId+dnas]", vanillaModules: "[systemId+sectionType+positionType+levelType+gridType]", - vanillaColumns: "[systemId+levelTypes]", + vanillaColumns: "[systemId+sectionType+levelTypes]", altSectionTypeLayouts: "houseId", }) this.houseLayouts = this.table("houseLayouts") diff --git a/app/design/state/vanilla.ts b/app/design/state/vanilla.ts index 8537f68a..f0a55573 100644 --- a/app/design/state/vanilla.ts +++ b/app/design/state/vanilla.ts @@ -43,7 +43,11 @@ if (!isSSR()) { liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( (dbVanillaColumns) => { for (let { systemId, levelTypes, vanillaColumn } of dbVanillaColumns) { - const vanillaColumnsKey = getVanillaColumnsKey({ systemId, levelTypes }) + const vanillaColumnsKey = getVanillaColumnsKey({ + systemId, + sectionType: "", + levelTypes, + }) vanillaColumns[vanillaColumnsKey] = vanillaColumn } } diff --git a/app/design/ui-3d/fresh/events/modeChange.ts b/app/design/ui-3d/fresh/events/modeChange.ts index 618cb2cc..fe9f5e65 100644 --- a/app/design/ui-3d/fresh/events/modeChange.ts +++ b/app/design/ui-3d/fresh/events/modeChange.ts @@ -166,7 +166,6 @@ const useModeChange = (rootRef: RefObject) => { isHouseTransformsGroup(x) && x.userData.houseId === houseId ), O.map((houseTransformsGroup) => { - console.log(`house transforms group`, houseTransformsGroup) // delete non-active layout groups // return active layout group const maybeActiveLayoutGroup = pipe( @@ -190,10 +189,6 @@ const useModeChange = (rootRef: RefObject) => { } ) - console.log(maybeActiveLayoutGroup) - - console.log(siteCtx.houseId) - if (houseId === siteCtx.houseId) { pipe( maybeActiveLayoutGroup, diff --git a/app/design/ui-3d/fresh/scene/columnGroup.ts b/app/design/ui-3d/fresh/scene/columnGroup.ts index ff02a48d..9520dae1 100644 --- a/app/design/ui-3d/fresh/scene/columnGroup.ts +++ b/app/design/ui-3d/fresh/scene/columnGroup.ts @@ -49,18 +49,21 @@ export let vanillaColumns: Record = {} liveQuery(() => layoutsDB.vanillaColumns.toArray()).subscribe( (dbVanillaColumns) => { for (let dbVanillaColumn of dbVanillaColumns) { - const { systemId, levelTypes, vanillaColumn } = dbVanillaColumn - vanillaColumns[getVanillaColumnsKey({ systemId, levelTypes })] = - vanillaColumn + const { systemId, sectionType, levelTypes, vanillaColumn } = + dbVanillaColumn + vanillaColumns[ + getVanillaColumnsKey({ systemId, sectionType, levelTypes }) + ] = vanillaColumn } } ) export const getVanillaColumn = ({ systemId, + sectionType, levelTypes, }: VanillaColumnsKey): T.Task => { - const key = getVanillaColumnsKey({ systemId, levelTypes }) + const key = getVanillaColumnsKey({ systemId, sectionType, levelTypes }) return pipe( vanillaColumns, @@ -72,6 +75,7 @@ export const getVanillaColumn = ({ return () => layoutsWorker.getVanillaColumn({ systemId, + sectionType, levelTypes, }) }, diff --git a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts index 1fc1ec44..126c8090 100644 --- a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts @@ -1,54 +1,28 @@ -import { liveQuery } from "dexie" -import { cartesian } from "fp-ts-std/Array" import { pipe } from "fp-ts/lib/function" import { Group, Matrix3, Vector3 } from "three" import { OBB } from "three-stdlib" -import layoutsDB, { - ColumnLayout, - getHouseLayoutsKey, -} from "../../../../db/layouts" -import { A, combineGuards, O, R, T } from "../../../../utils/functions" +import { ColumnLayout } from "../../../../db/layouts" +import { A, O, T } from "../../../../utils/functions" import { setVisible, yAxis } from "../../../../utils/three" -import { getLayoutsWorker } from "../../../../workers" import siteCtx, { getModeBools } from "../../../state/siteCtx" -import { renderOBB } from "../dimensions" import { - findAllGuardDown, getLayoutGroupColumnGroups, getSortedVisibleColumnGroups, } from "../helpers/sceneQueries" -import createRotateHandles from "../shapes/rotateHandles" import createStretchHandle from "../shapes/stretchHandle" +import { + createColumnGroups, + getVanillaColumn, + splitColumnGroups, +} from "./columnGroup" import { HouseLayoutGroup, HouseLayoutGroupUserData, HouseTransformsGroup, - isHouseLayoutGroup, isModuleGroup, - isRotateHandlesGroup, - isStretchHandleMesh, ModuleGroupUserData, UserDataTypeEnum, } from "./userData" -import { - createColumnGroups, - getVanillaColumn, - splitColumnGroups, -} from "./columnGroup" -import { columnLayoutToDnas } from "../../../../workers/layouts/worker" -import userDB from "../../../../db/user" - -const DEBUG = false - -export let houseLayouts: Record = {} - -liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( - (dbHouseLayouts) => { - for (let { systemId, dnas, layout } of dbHouseLayouts) { - houseLayouts[getHouseLayoutsKey({ systemId, dnas })] = layout - } - } -) export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => pipe( @@ -63,28 +37,6 @@ export const houseLayoutToLevelTypes = (columnLayout: ColumnLayout) => O.getOrElse((): string[] => []) ) -export const getHouseLayout = ({ - systemId, - dnas, -}: { - systemId: string - dnas: string[] -}): T.Task => - pipe( - houseLayouts, - R.lookup(getHouseLayoutsKey({ systemId, dnas })), - O.match( - (): T.Task => async () => { - const layoutsWorker = getLayoutsWorker() - if (!layoutsWorker) throw new Error(`no layouts worker`) - return await layoutsWorker.getLayout({ - systemId, - dnas, - }) - }, - (houseLayout) => T.of(houseLayout) - ) - ) export const createHouseLayoutGroup = ({ systemId, houseId, @@ -123,7 +75,7 @@ export const createHouseLayoutGroup = ({ const levelTypes = houseLayoutToLevelTypes(houseLayout) return pipe( - getVanillaColumn({ systemId, levelTypes }), + getVanillaColumn({ systemId, sectionType, levelTypes }), T.map((vanillaColumn) => { const computeLength = () => pipe( @@ -214,10 +166,7 @@ export const createHouseLayoutGroup = ({ houseTransformsGroup.userData.activeLayoutGroupUuid === layoutGroup.uuid ) { - console.log(`updating dnas in db`) - userDB.houses.update(houseId, { - dnas, - }) + houseTransformsGroup.userData.updateActiveLayoutDnas(dnas) } } diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index fbe2bc7e..c6931ea6 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -1,6 +1,7 @@ import { pipe } from "fp-ts/lib/function" import { Group, Plane, Vector3 } from "three" -import { A, O, pipeLog, T } from "../../../../utils/functions" +import userDB from "../../../../db/user" +import { A, O, T } from "../../../../utils/functions" import { setVisible } from "../../../../utils/three" import { getModeBools } from "../../../state/siteCtx" import { @@ -9,21 +10,57 @@ import { } from "../helpers/sceneQueries" import createRotateHandles from "../shapes/rotateHandles" import createStretchHandle from "../shapes/stretchHandle" +import { createHouseLayoutGroup } from "./houseLayoutGroup" import { HouseLayoutGroup, HouseTransformsGroup, HouseTransformsGroupUserData, HouseTransformsHandlesGroup, isHouseLayoutGroup, - isStretchHandleGroup, isStretchXHandleGroup, - StretchHandleGroup, UserDataTypeEnum, } from "./userData" -import { createHouseLayoutGroup, getHouseLayout } from "./houseLayoutGroup" - +import layoutsDB, { + ColumnLayout, + getHouseLayoutsKey, +} from "../../../../db/layouts" +import { R } from "../../../../utils/functions" +import { getLayoutsWorker } from "../../../../workers" +import { liveQuery } from "dexie" export const BIG_CLIP_NUMBER = 999 +let houseLayouts: Record = {} +liveQuery(() => layoutsDB.houseLayouts.toArray()).subscribe( + (dbHouseLayouts) => { + for (let { systemId, dnas, layout } of dbHouseLayouts) { + houseLayouts[getHouseLayoutsKey({ systemId, dnas })] = layout + } + } +) + +const getHouseLayout = ({ + systemId, + dnas, +}: { + systemId: string + dnas: string[] +}): T.Task => + pipe( + houseLayouts, + R.lookup(getHouseLayoutsKey({ systemId, dnas })), + O.match( + (): T.Task => async () => { + const layoutsWorker = getLayoutsWorker() + if (!layoutsWorker) throw new Error(`no layouts worker`) + return await layoutsWorker.getLayout({ + systemId, + dnas, + }) + }, + (houseLayout) => T.of(houseLayout) + ) + ) + export const createHouseTransformsGroup = ({ systemId, houseId, @@ -152,6 +189,10 @@ export const createHouseTransformsGroup = ({ // section type layouts } + const updateActiveLayoutDnas = (nextDnas: string[]) => { + userDB.houses.update(houseId, { dnas }) + } + const houseTransformsGroupUserData: HouseTransformsGroupUserData = { type: UserDataTypeEnum.Enum.HouseTransformsGroup, systemId, @@ -161,6 +202,7 @@ export const createHouseTransformsGroup = ({ activeLayoutDnas: layoutGroup.userData.dnas, clippingPlanes, friendlyName, + updateActiveLayoutDnas, initRotateAndStretchXHandles: initHandles, syncLength, setActiveLayoutGroup, diff --git a/app/design/ui-3d/fresh/scene/userData.ts b/app/design/ui-3d/fresh/scene/userData.ts index f31f95a4..2a0c5a0a 100644 --- a/app/design/ui-3d/fresh/scene/userData.ts +++ b/app/design/ui-3d/fresh/scene/userData.ts @@ -54,6 +54,7 @@ export type HouseTransformsGroupUserData = { clippingPlanes: Plane[] activeLayoutGroupUuid: string activeLayoutDnas: string[] + updateActiveLayoutDnas: (x: string[]) => void initRotateAndStretchXHandles: () => void syncLength: () => void setActiveLayoutGroup: (layoutGroup: HouseLayoutGroup) => void diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index 90a01055..b36260ea 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -278,6 +278,79 @@ const modulesToColumnLayout = (modules: Module[]) => { ) } +const postVanillaColumn = (arbitraryColumn: PositionedColumn) => { + const getVanillaModule = createVanillaModuleGetter(modulesCache)({ + constrainGridType: false, + positionType: "MID", + }) + + pipe( + arbitraryColumn.gridGroups, + A.traverse(O.Applicative)( + ({ + levelIndex, + levelType, + y, + modules: [{ module }], + }): O.Option => + pipe( + module, + getVanillaModule, + O.map((vanillaModule) => ({ + modules: [ + { + module: vanillaModule, + gridGroupIndex: 0, + // TODO: document me (quirk) + z: vanillaModule.length / 2, + }, + ], + length: vanillaModule.length, + y, + levelIndex, + levelType, + })) + ) + ), + O.map((gridGroups) => { + const levelTypes = pipe( + gridGroups, + A.map((gridGroup) => gridGroup.levelType) + ) + + pipe( + gridGroups, + A.head, + O.chain((gridGroup) => + pipe( + gridGroup.modules, + A.head, + O.map((firstModule) => { + const { + module: { + systemId, + structuredDna: { sectionType }, + length, + }, + } = firstModule + + layoutsDB.vanillaColumns.put({ + systemId, + levelTypes, + sectionType, + vanillaColumn: { + gridGroups, + length, + }, + }) + }) + ) + ) + ) + }) + ) +} + export const splitColumns = (layout: ColumnLayout) => pipe( layout, @@ -324,57 +397,7 @@ const getLayout = async ({ dnas, }) - const getVanillaModule = createVanillaModuleGetter(modulesCache)({ - constrainGridType: false, - positionType: "MID", - }) - - const { startColumn } = splitColumns(layout) - - pipe( - startColumn.gridGroups, - A.traverse(O.Applicative)( - ({ - levelIndex, - levelType, - y, - modules: [{ module }], - }): O.Option => - pipe( - module, - getVanillaModule, - O.map((vanillaModule) => ({ - modules: [ - { - module: vanillaModule, - gridGroupIndex: 0, - // TODO: document me (quirk) - z: vanillaModule.length / 2, - }, - ], - length: vanillaModule.length, - y, - levelIndex, - levelType, - })) - ) - ), - O.map((gridGroups) => { - const levelTypes = pipe( - gridGroups, - A.map((gridGroup) => gridGroup.levelType) - ) - - layoutsDB.vanillaColumns.put({ - systemId, - levelTypes, - vanillaColumn: { - gridGroups, - length: gridGroups[0].length, - }, - }) - }) - ) + postVanillaColumn(splitColumns(layout).startColumn) return layout } @@ -647,6 +670,9 @@ if (!isSSR()) { >, { layout, sectionType } ) => { + const { startColumn } = splitColumns(layout) + postVanillaColumn(startColumn) + return { ...acc, [sectionType.code]: { From 9ba35c68ee1938a76cb139b97a6f658496561793 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 23 Aug 2023 12:44:49 +0100 Subject: [PATCH 128/132] wip when it started working --- app/design/ui-3d/fresh/events/modeChange.ts | 26 +++++++------------ .../ui-3d/fresh/scene/houseLayoutGroup.ts | 5 ++-- .../ui-3d/fresh/scene/houseTransformsGroup.ts | 2 +- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/app/design/ui-3d/fresh/events/modeChange.ts b/app/design/ui-3d/fresh/events/modeChange.ts index fe9f5e65..c8318798 100644 --- a/app/design/ui-3d/fresh/events/modeChange.ts +++ b/app/design/ui-3d/fresh/events/modeChange.ts @@ -1,40 +1,32 @@ import { invalidate } from "@react-three/fiber" +import { liveQuery } from "dexie" import { pipe } from "fp-ts/lib/function" -import { A, O, R, T } from "~/utils/functions" -import { useSubscribeKey } from "~/utils/hooks" -import { - setInvisibleNoRaycast, - setVisible, - setVisibleAndRaycast, -} from "~/utils/three" +import { RefObject, useEffect } from "react" +import { Group } from "three" import scope from "~/design/state/scope" import siteCtx, { getModeBools, - SiteCtxModeEnum, useModeChangeListener, } from "~/design/state/siteCtx" +import { A, O, R } from "~/utils/functions" +import { useSubscribeKey } from "~/utils/hooks" +import { setInvisibleNoRaycast, setVisibleAndRaycast } from "~/utils/three" +import layoutsDB from "../../../../db/layouts" import useClippingPlaneHelpers from "../helpers/clippingPlanes" import { findAllGuardDown, findFirstGuardAcross, - findFirstGuardDown, getActiveHouseUserData, - getActiveLayoutGroup, } from "../helpers/sceneQueries" +import { createHouseLayoutGroup } from "../scene/houseLayoutGroup" +import { BIG_CLIP_NUMBER } from "../scene/houseTransformsGroup" import { - HouseLayoutGroup, HouseTransformsGroup, isHouseLayoutGroup, isHouseTransformsGroup, isStretchHandleGroup, UserDataTypeEnum, } from "../scene/userData" -import { RefObject, useEffect } from "react" -import { Group } from "three" -import layoutsDB from "../../../../db/layouts" -import { BIG_CLIP_NUMBER } from "../scene/houseTransformsGroup" -import { createHouseLayoutGroup } from "../scene/houseLayoutGroup" -import { liveQuery } from "dexie" const useModeChange = (rootRef: RefObject) => { const showHouseStretchHandles = (houseId: string) => { diff --git a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts index 126c8090..8d6e6f80 100644 --- a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts @@ -155,7 +155,8 @@ export const createHouseLayoutGroup = ({ }) }) ) - layoutGroup.userData.dnas = result.flat() + const nextDnas = result.flat() + layoutGroup.userData.dnas = nextDnas const houseTransformsGroup = layoutGroup.parent as HouseTransformsGroup @@ -166,7 +167,7 @@ export const createHouseLayoutGroup = ({ houseTransformsGroup.userData.activeLayoutGroupUuid === layoutGroup.uuid ) { - houseTransformsGroup.userData.updateActiveLayoutDnas(dnas) + houseTransformsGroup.userData.updateActiveLayoutDnas(nextDnas) } } diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index c6931ea6..71cf836c 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -190,7 +190,7 @@ export const createHouseTransformsGroup = ({ } const updateActiveLayoutDnas = (nextDnas: string[]) => { - userDB.houses.update(houseId, { dnas }) + userDB.houses.update(houseId, { dnas: nextDnas }) } const houseTransformsGroupUserData: HouseTransformsGroupUserData = { From c1517e5f5db7639f0adbe8836d0fe5441e585d14 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 23 Aug 2023 13:11:35 +0100 Subject: [PATCH 129/132] wip maybe x clamp working --- app/design/ui-3d/fresh/gestures/stretchX.ts | 37 +++++++++++++++------ app/utils/functions.ts | 9 +++-- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/app/design/ui-3d/fresh/gestures/stretchX.ts b/app/design/ui-3d/fresh/gestures/stretchX.ts index 884a2435..9918103b 100644 --- a/app/design/ui-3d/fresh/gestures/stretchX.ts +++ b/app/design/ui-3d/fresh/gestures/stretchX.ts @@ -1,13 +1,11 @@ import { pipe } from "fp-ts/lib/function" import { useRef } from "react" import { Vector3 } from "three" -import { A, O, someOrError } from "../../../../utils/functions" +import { A, clamp, O, someOrError } from "../../../../utils/functions" import pointer from "../../../state/pointer" import { dispatchOutline } from "../events/outlines" import { - findAllGuardDown, findFirstGuardAcross, - findFirstGuardDown, getHouseTransformsGroupUp, getPartitionedLayoutGroups, sortLayoutGroupsByWidth, @@ -15,9 +13,6 @@ import { import { HouseLayoutGroup, HouseTransformsGroup, - isHouseTransformsGroup, - isHouseTransformsHandlesGroup, - isStretchHandleGroup, isStretchXHandleGroup, StretchHandleGroup, } from "../scene/userData" @@ -83,8 +78,6 @@ const useOnDragStretchX = () => { }) ) - console.log(fences) - stretchXData.current = { handleGroup, otherSideHandleGroup, @@ -112,6 +105,23 @@ const useOnDragStretchX = () => { lastDistance, } = stretchXData.current + // const minX = pipe( + // fences, + // A.head, + // O.match( + // () => activeLayoutGroup.userData.width, + // (x) => activeLayoutGroup.userData.width + x.x + // ) + // ) + // const maxX = pipe( + // fences, + // A.last, + // O.match( + // () => activeLayoutGroup.userData.width, + // (x) => activeLayoutGroup.userData.width + x.x + // ) + // ) + const { side } = handleGroup.userData const [x1, z1] = pointer.xz @@ -121,8 +131,14 @@ const useOnDragStretchX = () => { -houseTransformsGroup.rotation.y ) const distance = distanceVector.x - handleGroup.position.setX(handleGroupX0 + distance) - otherSideHandleGroup.position.setX(otherSideHandleGroupX0 - distance) + + const lo = handleGroupX0 + fences[0].x + const hi = handleGroupX0 + fences[fences.length - 1].x + handleGroup.position.setX(clamp(lo, hi)(handleGroupX0 + distance)) + + otherSideHandleGroup.position.setX( + clamp(-hi, -lo)(otherSideHandleGroupX0 - distance) + ) const adjustedDistance = side * distance const adjustedLastDistance = side * lastDistance @@ -133,7 +149,6 @@ const useOnDragStretchX = () => { A.lookup(fenceIndex + 1), O.map(({ x }) => { if (adjustedDistance >= x) { - console.log(1) houseTransformsGroup.userData.setActiveLayoutGroup( fences[fenceIndex + 1].layoutGroup ) diff --git a/app/utils/functions.ts b/app/utils/functions.ts index 3803510e..1abf59a2 100644 --- a/app/utils/functions.ts +++ b/app/utils/functions.ts @@ -3,16 +3,14 @@ import { flow, identity, pipe } from "fp-ts/lib/function" import * as Mon from "fp-ts/Monoid" import * as Num from "fp-ts/number" import * as O from "fp-ts/Option" -import { clamp } from "fp-ts/Ord" +import * as Ord from "fp-ts/Ord" import * as RA from "fp-ts/ReadonlyArray" import * as R from "fp-ts/Record" import * as S from "fp-ts/string" import * as T from "fp-ts/Task" -const clamp_ = clamp(Num.Ord) export * as M from "fp-ts/Map" export * as NEA from "fp-ts/NonEmptyArray" -export * as Ord from "fp-ts/Ord" export * as RM from "fp-ts/ReadonlyMap" export * as RNEA from "fp-ts/ReadonlyNonEmptyArray" export * as RR from "fp-ts/ReadonlyRecord" @@ -20,8 +18,9 @@ export * as SG from "fp-ts/Semigroup" export * as TO from "fp-ts/TaskOption" export * as TE from "fp-ts/TaskEither" -export { clamp_ as clamp } -export { A, Num, O, R, RA, S, T } +const clamp = Ord.clamp(Num.Ord) + +export { A, Num, O, Ord, R, RA, S, T, clamp } export const any = (...args: boolean[]) => args.reduce((acc, v) => acc || v, false) From 80ccab251df7db9530e74b4dc4002c82982a2338 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Wed, 23 Aug 2023 13:52:25 +0100 Subject: [PATCH 130/132] yay --- app/design/ui-3d/fresh/events/modeChange.ts | 67 ----------------- app/design/ui-3d/fresh/gestures/stretchX.ts | 75 +++++++++++-------- .../ui-3d/fresh/scene/houseLayoutGroup.ts | 50 ++++++++----- .../ui-3d/fresh/scene/houseTransformsGroup.ts | 21 +++--- app/design/ui-3d/fresh/scene/userData.ts | 8 +- 5 files changed, 94 insertions(+), 127 deletions(-) diff --git a/app/design/ui-3d/fresh/events/modeChange.ts b/app/design/ui-3d/fresh/events/modeChange.ts index c8318798..235c50b3 100644 --- a/app/design/ui-3d/fresh/events/modeChange.ts +++ b/app/design/ui-3d/fresh/events/modeChange.ts @@ -147,7 +147,6 @@ const useModeChange = (rootRef: RefObject) => { const { unsubscribe } = liveQuery(() => layoutsDB.altSectionTypeLayouts.toArray() ).subscribe((dbAltSectionTypeLayouts) => { - console.log(`db alt section layouts type sub run`) if (!rootRef.current) return for (let { houseId, altSectionTypeLayouts } of dbAltSectionTypeLayouts) { @@ -168,13 +167,7 @@ const useModeChange = (rootRef: RefObject) => { x.uuid === houseTransformsGroup.userData.activeLayoutGroupUuid ), ({ left: otherLayoutGroups, right: activeLayoutGroups }) => { - console.log({ - uuid: houseTransformsGroup.userData.activeLayoutGroupUuid, - activeLayoutGroups, - otherLayoutGroups, - }) otherLayoutGroups.forEach((x) => { - console.log(`removing for st ${x.userData.sectionType}`) x.removeFromParent() }) return pipe(activeLayoutGroups, A.head) @@ -201,7 +194,6 @@ const useModeChange = (rootRef: RefObject) => { houseId, houseLayout: layout, })().then((layoutGroup) => { - console.log(`posting for st ${sectionType.code}`) setInvisibleNoRaycast(layoutGroup) houseTransformsGroup.add(layoutGroup) }) @@ -225,65 +217,6 @@ const useModeChange = (rootRef: RefObject) => { useModeChangeListener(async ({ previous, next }) => { processHandles() processClippingPlanes() - // if ( - // previous === SiteCtxModeEnum.Enum.SITE && - // next === SiteCtxModeEnum.Enum.BUILDING - // ) { - // // house Id - // // get the stretch x stuff out of the db? - // // maybe clean out old stuff - // // ultimately pop some alternative invisible layouts in there! - - // const { houseId } = siteCtx - - // if (!houseId) return - - // const dbResult = await layoutsDB.altSectionTypeLayouts.get(houseId) - - // if (!dbResult) return - - // if (!rootRef.current) return - - // pipe( - // rootRef.current, - // findFirstGuardDown((x): x is HouseTransformsGroup => { - // if (!isHouseTransformsGroup(x)) return false - // if (x.userData.houseId !== houseId) return false - // return true - // }), - // O.map((houseTransformsGroup) => { - // const { systemId } = getActiveHouseUserData(houseTransformsGroup) - - // pipe( - // dbResult.altSectionTypeLayouts, - // R.map(({ layout, dnas }) => { - // createHouseLayoutGroup({ - // systemId, - // houseId, - // houseLayout: layout, - // dnas, - // })().then((layoutGroup) => { - // setVisible(layoutGroup, false) - - // // remove same dnas ones - // pipe( - // houseTransformsGroup, - // findAllGuardDown(isHouseLayoutGroup), - // A.filter( - // (x) => x.userData.dnas.toString() === dnas.toString() - // ) - // ).forEach((layoutGroup) => { - // console.log(`removing ${layoutGroup.userData.sectionType}`) - // layoutGroup.removeFromParent() - // }) - // houseTransformsGroup.add(layoutGroup) - // console.log(houseTransformsGroup.children) - // }) - // }) - // ) - // }) - // ) - // } }) } diff --git a/app/design/ui-3d/fresh/gestures/stretchX.ts b/app/design/ui-3d/fresh/gestures/stretchX.ts index 9918103b..9430f0a5 100644 --- a/app/design/ui-3d/fresh/gestures/stretchX.ts +++ b/app/design/ui-3d/fresh/gestures/stretchX.ts @@ -13,7 +13,7 @@ import { import { HouseLayoutGroup, HouseTransformsGroup, - isStretchXHandleGroup, + isXStretchHandleGroup, StretchHandleGroup, } from "../scene/userData" @@ -33,6 +33,8 @@ const useOnDragStretchX = () => { fences: FenceX[] fenceIndex: number lastDistance: number + lo: number + hi: number } | null>(null) const first = ({ @@ -54,11 +56,15 @@ const useOnDragStretchX = () => { const { activeLayoutGroup, otherLayoutGroups } = getPartitionedLayoutGroups(houseTransformsGroup) + ;[activeLayoutGroup, ...otherLayoutGroups].forEach((x) => { + x.userData.setLengthHandlesVisible(false) + }) + const otherSideHandleGroup = pipe( houseTransformsGroup, findFirstGuardAcross( (x): x is StretchHandleGroup => - isStretchXHandleGroup(x) && x.userData.side === otherSide + isXStretchHandleGroup(x) && x.userData.side === otherSide ), someOrError(`other side handle group not found`) ) @@ -78,6 +84,9 @@ const useOnDragStretchX = () => { }) ) + const lo = side * fences[0].x + const hi = side * fences[fences.length - 1].x + stretchXData.current = { handleGroup, otherSideHandleGroup, @@ -88,6 +97,8 @@ const useOnDragStretchX = () => { handleGroupX0: handleGroup.position.x, fenceIndex, lastDistance: 0, + lo, + hi, } } const mid = () => { @@ -103,25 +114,10 @@ const useOnDragStretchX = () => { fences, fenceIndex, lastDistance, + lo, + hi, } = stretchXData.current - // const minX = pipe( - // fences, - // A.head, - // O.match( - // () => activeLayoutGroup.userData.width, - // (x) => activeLayoutGroup.userData.width + x.x - // ) - // ) - // const maxX = pipe( - // fences, - // A.last, - // O.match( - // () => activeLayoutGroup.userData.width, - // (x) => activeLayoutGroup.userData.width + x.x - // ) - // ) - const { side } = handleGroup.userData const [x1, z1] = pointer.xz @@ -132,12 +128,25 @@ const useOnDragStretchX = () => { ) const distance = distanceVector.x - const lo = handleGroupX0 + fences[0].x - const hi = handleGroupX0 + fences[fences.length - 1].x - handleGroup.position.setX(clamp(lo, hi)(handleGroupX0 + distance)) + const distanceWithSide = side * distance + + const clampedDistance = clamp(side * lo, side * hi)(distanceWithSide) + + // const lo = handleGroupX0 + side * fences[0].x + // const hi = handleGroupX0 + side * fences[fences.length - 1].x + + // const c = clamp(lo, hi) + + console.log({ lo, hi, distance, distanceWithSide }) + + handleGroup.position.setX(handleGroupX0 + side * clampedDistance) + + console.log({ side: handleGroup.userData.side }) + + // const c2 = clamp(-hi, -lo) otherSideHandleGroup.position.setX( - clamp(-hi, -lo)(otherSideHandleGroupX0 - distance) + otherSideHandleGroupX0 - side * clampedDistance ) const adjustedDistance = side * distance @@ -149,9 +158,9 @@ const useOnDragStretchX = () => { A.lookup(fenceIndex + 1), O.map(({ x }) => { if (adjustedDistance >= x) { - houseTransformsGroup.userData.setActiveLayoutGroup( - fences[fenceIndex + 1].layoutGroup - ) + const layoutGroup = fences[fenceIndex + 1].layoutGroup + houseTransformsGroup.userData.setActiveLayoutGroup(layoutGroup) + layoutGroup.userData.setLengthHandlesVisible(false) stretchXData.current!.fenceIndex++ } }) @@ -162,9 +171,9 @@ const useOnDragStretchX = () => { A.lookup(fenceIndex - 1), O.map(({ x }) => { if (adjustedDistance <= x) { - houseTransformsGroup.userData.setActiveLayoutGroup( - fences[fenceIndex - 1].layoutGroup - ) + const layoutGroup = fences[fenceIndex - 1].layoutGroup + houseTransformsGroup.userData.setActiveLayoutGroup(layoutGroup) + layoutGroup.userData.setLengthHandlesVisible(false) stretchXData.current!.fenceIndex-- } }) @@ -174,7 +183,13 @@ const useOnDragStretchX = () => { // if next fence up } - const last = () => {} + const last = () => { + const { fences, fenceIndex } = stretchXData.current! + const activeLayoutGroup = fences[fenceIndex].layoutGroup + activeLayoutGroup.userData.setLengthHandlesVisible(true) + + stretchXData.current = null + } return { first, mid, last } } diff --git a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts index 8d6e6f80..5d883dc5 100644 --- a/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseLayoutGroup.ts @@ -6,6 +6,7 @@ import { A, O, T } from "../../../../utils/functions" import { setVisible, yAxis } from "../../../../utils/three" import siteCtx, { getModeBools } from "../../../state/siteCtx" import { + findAllGuardDown, getLayoutGroupColumnGroups, getSortedVisibleColumnGroups, } from "../helpers/sceneQueries" @@ -20,6 +21,8 @@ import { HouseLayoutGroupUserData, HouseTransformsGroup, isModuleGroup, + isXStretchHandleGroup, + isZStretchHandleGroup, ModuleGroupUserData, UserDataTypeEnum, } from "./userData" @@ -55,7 +58,7 @@ export const createHouseLayoutGroup = ({ houseLayout, }), T.chain((columnGroups) => { - const layoutGroup = new Group() as HouseLayoutGroup + const houseLayoutGroup = new Group() as HouseLayoutGroup const columnCount = columnGroups.length @@ -79,7 +82,7 @@ export const createHouseLayoutGroup = ({ T.map((vanillaColumn) => { const computeLength = () => pipe( - layoutGroup, + houseLayoutGroup, getLayoutGroupColumnGroups, A.partition((columnGroup) => columnGroup.visible), ({ left: hiddenColumnGroups, right: activeColumnGroups }) => { @@ -95,22 +98,22 @@ export const createHouseLayoutGroup = ({ ) const updateLength = (maybeLength?: number) => { - const { length: oldLength } = layoutGroup.userData + const { length: oldLength } = houseLayoutGroup.userData const nextLength = maybeLength ?? computeLength() - layoutGroup.userData.length = nextLength + houseLayoutGroup.userData.length = nextLength - layoutGroup.position.setZ(-nextLength / 2) + houseLayoutGroup.position.setZ(-nextLength / 2) - layoutGroup.parent?.position.add( + houseLayoutGroup.parent?.position.add( new Vector3(0, 0, (nextLength - oldLength) / 2).applyAxisAngle( yAxis, - layoutGroup.parent.rotation.y + houseLayoutGroup.parent.rotation.y ) ) - const houseTransformsGroup = layoutGroup.parent! + const houseTransformsGroup = houseLayoutGroup.parent! const { x, y, z } = houseTransformsGroup.position @@ -120,7 +123,7 @@ export const createHouseLayoutGroup = ({ houseTransformsGroup.matrix ) - layoutGroup.userData.obb.set(center, halfSize, rotation) + houseLayoutGroup.userData.obb.set(center, halfSize, rotation) // if (DEBUG && houseTransformsGroup.parent) { // renderOBB( @@ -133,7 +136,7 @@ export const createHouseLayoutGroup = ({ const updateDnas = () => { let result: string[][] = [] pipe( - layoutGroup, + houseLayoutGroup, getSortedVisibleColumnGroups, // -> findAllGuardDown A.map((v) => { @@ -156,16 +159,16 @@ export const createHouseLayoutGroup = ({ }) ) const nextDnas = result.flat() - layoutGroup.userData.dnas = nextDnas + houseLayoutGroup.userData.dnas = nextDnas const houseTransformsGroup = - layoutGroup.parent as HouseTransformsGroup + houseLayoutGroup.parent as HouseTransformsGroup houseTransformsGroup.userData.refreshAltLayouts() if ( houseTransformsGroup.userData.activeLayoutGroupUuid === - layoutGroup.uuid + houseLayoutGroup.uuid ) { houseTransformsGroup.userData.updateActiveLayoutDnas(nextDnas) } @@ -178,7 +181,7 @@ export const createHouseLayoutGroup = ({ const { siteMode } = getModeBools(siteCtx.mode) const { length: houseLength, width: houseWidth } = - layoutGroup.userData + houseLayoutGroup.userData const backStretchZHandleGroup = createStretchHandle({ axis: "z", side: 1, @@ -198,6 +201,14 @@ export const createHouseLayoutGroup = ({ setVisible(frontStretchZHandleGroup, !siteMode) } + const setLengthHandlesVisible = (bool: boolean = true) => { + pipe( + houseLayoutGroup, + findAllGuardDown(isZStretchHandleGroup), + A.map((x) => void setVisible(x, bool)) + ) + } + const userData: HouseLayoutGroupUserData = { type: UserDataTypeEnum.Enum.HouseLayoutGroup, dnas, @@ -214,14 +225,15 @@ export const createHouseLayoutGroup = ({ initStretchZHandles, updateLength, updateDnas, + setLengthHandlesVisible, } - layoutGroup.userData = userData - layoutGroup.add(...columnGroups) - layoutGroup.position.setZ(-length / 2) - layoutGroup.userData.initStretchZHandles() + houseLayoutGroup.userData = userData + houseLayoutGroup.add(...columnGroups) + houseLayoutGroup.position.setZ(-length / 2) + houseLayoutGroup.userData.initStretchZHandles() - return layoutGroup as HouseLayoutGroup + return houseLayoutGroup as HouseLayoutGroup }) ) }) diff --git a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts index 71cf836c..c6434875 100644 --- a/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts +++ b/app/design/ui-3d/fresh/scene/houseTransformsGroup.ts @@ -17,7 +17,7 @@ import { HouseTransformsGroupUserData, HouseTransformsHandlesGroup, isHouseLayoutGroup, - isStretchXHandleGroup, + isXStretchHandleGroup, UserDataTypeEnum, } from "./userData" import layoutsDB, { @@ -93,7 +93,7 @@ export const createHouseTransformsGroup = ({ const handlesGroup = new Group() as HouseTransformsHandlesGroup handlesGroup.position.setZ(-layoutGroup.userData.length / 2) - const initHandles = () => { + const initRotateAndStretchXHandles = () => { const { width: houseWidth, length: houseLength } = getActiveHouseUserData(houseTransformsGroup) @@ -140,7 +140,7 @@ export const createHouseTransformsGroup = ({ const xStretchHandles = pipe( handlesGroup, - findAllGuardDown(isStretchXHandleGroup) + findAllGuardDown(isXStretchHandleGroup) ) xStretchHandles.forEach((handle) => { @@ -148,6 +148,11 @@ export const createHouseTransformsGroup = ({ }) } + const updateActiveLayoutDnas = (nextDnas: string[]) => { + houseTransformsGroup.userData.activeLayoutDnas = nextDnas + userDB.houses.update(houseId, { dnas: nextDnas }) + } + const setActiveLayoutGroup = (nextLayoutGroup: HouseLayoutGroup) => { pipe( houseTransformsGroup.children, @@ -170,7 +175,7 @@ export const createHouseTransformsGroup = ({ const setWidthHandlesVisible = (bool: boolean = true) => { pipe( houseTransformsGroup, - findAllGuardDown(isStretchXHandleGroup), + findAllGuardDown(isXStretchHandleGroup), A.map((x) => void setVisible(x, bool)) ) } @@ -189,10 +194,6 @@ export const createHouseTransformsGroup = ({ // section type layouts } - const updateActiveLayoutDnas = (nextDnas: string[]) => { - userDB.houses.update(houseId, { dnas: nextDnas }) - } - const houseTransformsGroupUserData: HouseTransformsGroupUserData = { type: UserDataTypeEnum.Enum.HouseTransformsGroup, systemId, @@ -203,7 +204,7 @@ export const createHouseTransformsGroup = ({ clippingPlanes, friendlyName, updateActiveLayoutDnas, - initRotateAndStretchXHandles: initHandles, + initRotateAndStretchXHandles, syncLength, setActiveLayoutGroup, setWidthHandlesVisible, @@ -213,7 +214,7 @@ export const createHouseTransformsGroup = ({ houseTransformsGroup.add(layoutGroup) - initHandles() + initRotateAndStretchXHandles() return houseTransformsGroup }) diff --git a/app/design/ui-3d/fresh/scene/userData.ts b/app/design/ui-3d/fresh/scene/userData.ts index 2a0c5a0a..e2c2c9fa 100644 --- a/app/design/ui-3d/fresh/scene/userData.ts +++ b/app/design/ui-3d/fresh/scene/userData.ts @@ -80,6 +80,7 @@ export type HouseLayoutGroupUserData = { sectionType: string modifiedMaterials: Record initStretchZHandles: () => void + setLengthHandlesVisible: (bool?: boolean) => void updateLength: () => void updateDnas: () => void } @@ -242,11 +243,16 @@ export const isStretchHandleGroup = ( ): node is StretchHandleGroup => node.userData?.type === UserDataTypeEnum.Enum.StretchHandleGroup -export const isStretchXHandleGroup = ( +export const isXStretchHandleGroup = ( node: Object3D ): node is StretchHandleGroup => isStretchHandleGroup(node) && node.userData.axis === "x" +export const isZStretchHandleGroup = ( + node: Object3D +): node is StretchHandleGroup => + isStretchHandleGroup(node) && node.userData.axis === "z" + export const incrementColumnCount = (layoutGroup: Object3D) => { const userData = layoutGroup.userData as HouseLayoutGroupUserData if (userData.type !== UserDataTypeEnum.Enum.HouseLayoutGroup) From 78bd397c41757bed8f038778a3bb69a25541813b Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 24 Aug 2023 12:39:47 +0100 Subject: [PATCH 131/132] wip --- app/design/app.tsx | 11 +- app/design/ui-3d/fresh/FreshApp.tsx | 1 + app/design/ui-3d/fresh/events/houses.ts | 10 +- app/design/ui-3d/fresh/gestures/stretchX.ts | 13 -- app/design/ui-3d/fresh/scene/moduleGroup.ts | 150 ++++++++++++++------ app/workers/index.ts | 25 +++- app/workers/layouts/models.ts | 51 ------- app/workers/layouts/vanilla.ts | 4 +- app/workers/layouts/worker.ts | 113 +++------------ app/workers/models.ts | 116 +++++++++++++++ 10 files changed, 283 insertions(+), 211 deletions(-) delete mode 100644 app/workers/layouts/models.ts create mode 100644 app/workers/models.ts diff --git a/app/design/app.tsx b/app/design/app.tsx index 58a54c74..d2083dd7 100644 --- a/app/design/app.tsx +++ b/app/design/app.tsx @@ -1,14 +1,19 @@ "use client" import DataInit from "~/data/DataInit" import { TrpcProvider } from "../ui/TrpcProvider" -import { getLayoutsWorker, getSystemsWorker } from "../workers" +import { + initLayoutsWorker, + initModelsWorker, + initSystemsWorker, +} from "../workers" import { Routing } from "./state/routing" import FreshApp from "./ui-3d/fresh/FreshApp" import AppInit from "./ui-3d/init/AppInit" const App = () => { - getSystemsWorker() - getLayoutsWorker() + initSystemsWorker() + initModelsWorker() + initLayoutsWorker() return ( diff --git a/app/design/ui-3d/fresh/FreshApp.tsx b/app/design/ui-3d/fresh/FreshApp.tsx index a1aa1731..22cb048d 100644 --- a/app/design/ui-3d/fresh/FreshApp.tsx +++ b/app/design/ui-3d/fresh/FreshApp.tsx @@ -10,6 +10,7 @@ const FreshApp = () => { useHousesEvents(rootRef) useModeChange(rootRef) + const bindAll = useGestures() return ( diff --git a/app/design/ui-3d/fresh/events/houses.ts b/app/design/ui-3d/fresh/events/houses.ts index 1b743ea1..c3941ca8 100644 --- a/app/design/ui-3d/fresh/events/houses.ts +++ b/app/design/ui-3d/fresh/events/houses.ts @@ -66,7 +66,7 @@ export const useHousesEvents = (rootRef: RefObject) => { houseTypeId, } = house - const houseGroup = await createHouseTransformsGroup({ + const houseTransformsGroup = await createHouseTransformsGroup({ systemId, houseId, dnas, @@ -74,12 +74,12 @@ export const useHousesEvents = (rootRef: RefObject) => { houseTypeId, })() - houseGroup.position.set(position.x, position.y, position.z) - houseGroup.rotation.set(0, rotation, 0) + houseTransformsGroup.position.set(position.x, position.y, position.z) + houseTransformsGroup.rotation.set(0, rotation, 0) - setRaycasting(houseGroup, true) + setRaycasting(houseTransformsGroup, true) - rootRef.current.add(houseGroup) + rootRef.current.add(houseTransformsGroup) // liveHouses[houseId] = houseGroup invalidate() diff --git a/app/design/ui-3d/fresh/gestures/stretchX.ts b/app/design/ui-3d/fresh/gestures/stretchX.ts index 9430f0a5..e4198e04 100644 --- a/app/design/ui-3d/fresh/gestures/stretchX.ts +++ b/app/design/ui-3d/fresh/gestures/stretchX.ts @@ -132,19 +132,8 @@ const useOnDragStretchX = () => { const clampedDistance = clamp(side * lo, side * hi)(distanceWithSide) - // const lo = handleGroupX0 + side * fences[0].x - // const hi = handleGroupX0 + side * fences[fences.length - 1].x - - // const c = clamp(lo, hi) - - console.log({ lo, hi, distance, distanceWithSide }) - handleGroup.position.setX(handleGroupX0 + side * clampedDistance) - console.log({ side: handleGroup.userData.side }) - - // const c2 = clamp(-hi, -lo) - otherSideHandleGroup.position.setX( otherSideHandleGroupX0 - side * clampedDistance ) @@ -178,9 +167,7 @@ const useOnDragStretchX = () => { } }) ) - // tbc } - // if next fence up } const last = () => { diff --git a/app/design/ui-3d/fresh/scene/moduleGroup.ts b/app/design/ui-3d/fresh/scene/moduleGroup.ts index cb15a123..d5c28170 100644 --- a/app/design/ui-3d/fresh/scene/moduleGroup.ts +++ b/app/design/ui-3d/fresh/scene/moduleGroup.ts @@ -1,9 +1,11 @@ import { liveQuery } from "dexie" -import { pipe } from "fp-ts/lib/function" +import { flow, pipe } from "fp-ts/lib/function" import { BufferGeometry, BufferGeometryLoader, Group, Mesh } from "three" import { Module } from "../../../../../server/data/modules" import layoutsDB from "../../../../db/layouts" -import { O, R, S } from "../../../../utils/functions" +import { LastFetchStamped } from "../../../../db/systems" +import { O, R, S, someOrError, T } from "../../../../utils/functions" +import { getModelsWorker } from "../../../../workers" import { getMaterial } from "../systems" import { ElementMeshUserData, @@ -14,17 +16,92 @@ import { // speckle branch url : geometry by ifc tag export let models: Record> = {} +export const getModelGeometriesTask = ({ + systemId, + speckleBranchUrl, +}: { + systemId: string + speckleBranchUrl: string +}): T.Task> => { + const onNoModel = (): T.Task> => async () => { + const { geometries } = await getModelsWorker().getModuleModel({ + systemId, + speckleBranchUrl, + }) + const loadedModels: Record = pipe( + geometries, + R.map((x) => loader.parse(x) as BufferGeometry), + R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { + geometry.computeVertexNormals() + return { + ...acc, + [ifcTag]: geometry, + } + }) + ) + + models[speckleBranchUrl] = loadedModels + return loadedModels + } + + return pipe( + models, + R.lookup(speckleBranchUrl), + O.match(onNoModel, flow(T.of)) + ) +} + export const getGeometry = ({ speckleBranchUrl, ifcTag, -}: { +}: // systemId, +// lastFetched, +{ speckleBranchUrl: string ifcTag: string -}) => models[speckleBranchUrl][ifcTag] + // systemId: string + // lastFetched: number +}): BufferGeometry => { + // const onNoModelGeometries = (): T.Task => () => + // getModelsWorker() + // .putModuleModel({ + // systemId, + // speckleBranchUrl, + // lastFetched, + // }) + // .then(({ geometries }) => { + // const loadedModels: Record = pipe( + // geometries, + // R.map((x) => loader.parse(x) as BufferGeometry), + // R.reduceWithIndex(S.Ord)({}, (ifcTag, acc, geometry) => { + // geometry.computeVertexNormals() + // return { + // ...acc, + // [ifcTag]: geometry, + // } + // }) + // ) + // models[speckleBranchUrl] = loadedModels + + // return pipe( + // models, + // R.lookup(speckleBranchUrl), + // O.chain(R.lookup(ifcTag)), + // someOrError(`onNoModelGeometries error`) + // ) + // }) + + return pipe( + models, + R.lookup(speckleBranchUrl), + O.chain(R.lookup(ifcTag)), + someOrError(`no geometry found for ${ifcTag} in ${speckleBranchUrl}`) + ) +} const loader = new BufferGeometryLoader() -const putModel = ({ +const cacheModel = ({ geometries, speckleBranchUrl, }: { @@ -50,7 +127,7 @@ const putModel = ({ liveQuery(() => layoutsDB.models.toArray()).subscribe((dbModels) => { for (let { speckleBranchUrl, geometries } of dbModels) { if (!(speckleBranchUrl in models)) { - putModel({ speckleBranchUrl, geometries }) + cacheModel({ speckleBranchUrl, geometries }) } } }) @@ -68,46 +145,35 @@ export const createModuleGroup = async ({ }) => { const moduleGroup = new Group() - const processModel = ( - modelGeometriesByIfcTag: Record - ) => { - for (let ifcTag of Object.keys(modelGeometriesByIfcTag)) { - const geometry = getGeometry({ speckleBranchUrl, ifcTag }) - const material = getMaterial({ - systemId, - ifcTag, - houseId, - }) - material.wireframe = false - const mesh = new Mesh(geometry, material) - mesh.castShadow = true + const modelGeometries = await getModelGeometriesTask({ + systemId, + speckleBranchUrl, + })() - const elementMeshUserData: ElementMeshUserData = { - type: UserDataTypeEnum.Enum.ElementMesh, - ifcTag, - } - mesh.userData = elementMeshUserData - moduleGroup.add(mesh) + for (let ifcTag of Object.keys(modelGeometries)) { + const geometry = getGeometry({ + speckleBranchUrl, + ifcTag, + // systemId, + // lastFetched, + }) + const material = getMaterial({ + systemId, + ifcTag, + houseId, + }) + material.wireframe = false + const mesh = new Mesh(geometry, material) + mesh.castShadow = true + + const elementMeshUserData: ElementMeshUserData = { + type: UserDataTypeEnum.Enum.ElementMesh, + ifcTag, } + mesh.userData = elementMeshUserData + moduleGroup.add(mesh) } - await pipe( - models, - R.lookup(speckleBranchUrl), - O.match( - async () => { - const model = await layoutsDB.models.get(speckleBranchUrl) - if (model === undefined) - throw new Error(`no model for ${speckleBranchUrl}`) - const loadedModel = putModel(model) - processModel(loadedModel) - }, - async (loadedModel) => { - processModel(loadedModel) - } - ) - ) - const moduleGroupUserData: ModuleGroupUserData = { type: UserDataTypeEnum.Enum.ModuleGroup, gridGroupIndex, diff --git a/app/workers/index.ts b/app/workers/index.ts index 1546964d..21d9ccbe 100644 --- a/app/workers/index.ts +++ b/app/workers/index.ts @@ -2,22 +2,43 @@ import { Remote, wrap } from "comlink" import { isSSR } from "../utils/next" import type { LayoutsAPI } from "./layouts/worker" +import type { ModelsAPI } from "./models" let systemsWorker: Worker | null = null let layoutsWorker: Remote | null = null +let modelsWorker: Remote | null = null -export const getSystemsWorker = () => { +export const initSystemsWorker = () => { if (!isSSR() && systemsWorker === null) { systemsWorker = new Worker(new URL("./systems.ts", import.meta.url)) } +} + +export const getSystemsWorker = () => { + if (systemsWorker === null) throw new Error(`couldn't get systemsWorker`) return systemsWorker } -export const getLayoutsWorker = () => { +export const initLayoutsWorker = () => { if (!isSSR() && layoutsWorker === null) { layoutsWorker = wrap( new Worker(new URL("./layouts/worker.ts", import.meta.url)) ) } +} + +export const getLayoutsWorker = () => { + if (layoutsWorker === null) throw new Error(`couldn't get layoutsWorker`) return layoutsWorker } + +export const initModelsWorker = () => { + if (!isSSR() && modelsWorker === null) { + modelsWorker = wrap(new Worker(new URL("./models.ts", import.meta.url))) + } +} + +export const getModelsWorker = () => { + if (modelsWorker === null) throw new Error(`couldn't get modelsWorker`) + return modelsWorker +} diff --git a/app/workers/layouts/models.ts b/app/workers/layouts/models.ts deleted file mode 100644 index a641bc2b..00000000 --- a/app/workers/layouts/models.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Module } from "../../../server/data/modules" -import layoutsDB from "../../db/layouts" -import { LastFetchStamped } from "../../db/systems" -import { pipe } from "fp-ts/lib/function" -import produce from "immer" -import { BufferGeometry } from "three" -import { mergeBufferGeometries } from "three-stdlib" -import { getSpeckleObject } from "../../../server/data/speckleModel" -import { A, R } from "../../utils/functions" -import speckleIfcParser from "../../utils/speckle/speckleIfcParser" - -const getSpeckleModelGeometries = async (speckleBranchUrl: string) => { - const speckleObjectData = await getSpeckleObject(speckleBranchUrl) - const speckleObject = speckleIfcParser.parse(speckleObjectData) - - return pipe( - speckleObject, - A.reduce( - {}, - (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { - return produce(acc, (draft) => { - if (ifcTag in draft) draft[ifcTag].push(geometry) - else draft[ifcTag] = [geometry] - }) - } - ), - R.map((geoms) => mergeBufferGeometries(geoms)), - R.filter((bg: BufferGeometry | null): bg is BufferGeometry => Boolean(bg)), - R.map((x) => x.toJSON()) - ) -} - -export const syncModels = (modules: LastFetchStamped[]) => { - modules.map(async (nextModule) => { - const { speckleBranchUrl, lastFetched } = nextModule - const maybeModel = await layoutsDB.models.get(speckleBranchUrl) - - if (maybeModel && maybeModel.lastFetched === lastFetched) { - return - } - - const geometries = await getSpeckleModelGeometries(speckleBranchUrl) - - layoutsDB.models.put({ - speckleBranchUrl, - lastFetched, - geometries, - systemId: nextModule.systemId, - }) - }) -} diff --git a/app/workers/layouts/vanilla.ts b/app/workers/layouts/vanilla.ts index d7c1e4cd..82d9c929 100644 --- a/app/workers/layouts/vanilla.ts +++ b/app/workers/layouts/vanilla.ts @@ -6,7 +6,7 @@ import systemsDB, { LastFetchStamped } from "../../db/systems" import { A, all, O, Ord, S } from "../../utils/functions" export const createVanillaModuleGetter = - (modulesCache: LastFetchStamped[]) => + (modules: LastFetchStamped[]) => ( opts: { positionType?: string @@ -24,7 +24,7 @@ export const createVanillaModuleGetter = } = opts return pipe( - modulesCache, + modules, A.filter((sysModule) => all( sysModule.systemId === module.systemId, diff --git a/app/workers/layouts/worker.ts b/app/workers/layouts/worker.ts index b36260ea..f4888a00 100644 --- a/app/workers/layouts/worker.ts +++ b/app/workers/layouts/worker.ts @@ -26,10 +26,15 @@ import userDB from "../../db/user" import { A, O, reduceToOption, T, TO, unwrapSome } from "../../utils/functions" import { sign } from "../../utils/math" import { isSSR } from "../../utils/next" -import { syncModels } from "./models" import { createVanillaModuleGetter, getIndexedVanillaModule } from "./vanilla" let modulesCache: LastFetchStamped[] = [] +const getModules = async () => { + if (modulesCache.length > 0) return modulesCache + modulesCache = await systemsDB.modules.toArray() + return modulesCache +} + let layoutsQueue: HouseLayoutsKey[] = [] const modulesToRows = (modules: Module[]): Module[][] => { @@ -278,8 +283,10 @@ const modulesToColumnLayout = (modules: Module[]) => { ) } -const postVanillaColumn = (arbitraryColumn: PositionedColumn) => { - const getVanillaModule = createVanillaModuleGetter(modulesCache)({ +const postVanillaColumn = async (arbitraryColumn: PositionedColumn) => { + const modules = await getModules() + + const getVanillaModule = createVanillaModuleGetter(modules)({ constrainGridType: false, positionType: "MID", }) @@ -370,6 +377,7 @@ const getLayout = async ({ systemId, dnas, }: HouseLayoutsKey): Promise => { + const allModules = await getModules() const maybeLayout = await layoutsDB.houseLayouts .get(getHouseLayoutsKey({ systemId, dnas })) .then((x) => x?.layout) @@ -381,7 +389,7 @@ const getLayout = async ({ dnas, A.filterMap((dna) => pipe( - modulesCache, + allModules, A.findFirst( (systemModule: Module) => systemModule.systemId === systemId && systemModule.dna === dna @@ -397,17 +405,15 @@ const getLayout = async ({ dnas, }) - postVanillaColumn(splitColumns(layout).startColumn) + const { startColumn } = splitColumns(layout) + + postVanillaColumn(startColumn) return layout } } const processLayoutsQueue = async () => { - if (modulesCache.length === 0) { - return - } - // Process queue one item at a time while (layoutsQueue.length > 0) { const layoutsKey = layoutsQueue.shift() @@ -417,14 +423,6 @@ const processLayoutsQueue = async () => { } } -if (!isSSR()) { - liveQuery(() => systemsDB.modules.toArray()).subscribe((modules) => { - syncModels(modules) - modulesCache = modules - processLayoutsQueue() - }) -} - const postLayout = (key: HouseLayoutsKey) => { layoutsQueue.push(key) } @@ -461,19 +459,7 @@ export const columnLayoutToDnas = ( RA.flatten ) as string[] -export const foo = ({ - systemId, - houseId, - dnas, -}: { - systemId: string - houseId: string - dnas: string[] -}) => { - console.log({ systemId, houseId, dnas }) -} - -const changeLayoutSectionType = ({ +const changeLayoutSectionType = async ({ systemId, layout, sectionType: st, @@ -484,6 +470,8 @@ const changeLayoutSectionType = ({ }) => { const { code: sectionType } = st + const allModules = await getModules() + return pipe( layout, A.traverse(TO.ApplicativeSeq)((positionedColumn) => @@ -516,7 +504,7 @@ const changeLayoutSectionType = ({ O.fromNullable, O.chain((a) => pipe( - modulesCache, + allModules, A.findFirst( (b) => a.systemId === b.systemId && a.moduleDna === b.dna ) @@ -544,7 +532,7 @@ const changeLayoutSectionType = ({ } as Module const compatModules = pipe( - modulesCache, + allModules, filterCompatibleModules()(target) ) @@ -690,66 +678,6 @@ if (!isSSR()) { }) } -// const updateAltSectionTypeLayouts = async ({ -// houseId, -// currentSectionTypeCode -// }: { -// houseId: string -// currentSectionTypeCode: string -// }) => { -// layoutsDB.altSectionTypeLayouts.delete(houseId) - -// const dbSectionTypes = await systemsDB.sectionTypes.toArray() - -// const otherSectionTypes = dbSectionTypes.filter( -// (x) => -// x.code !== -// currentSectionTypeCode -// ) - -// const otherLayouts = await pipe( -// otherSectionTypes, -// A.map( -// ( -// sectionType -// ): TO.TaskOption<{ -// layout: ColumnLayout -// sectionType: SectionType -// }> => -// () => -// changeLayoutSectionType({ systemId, layout, sectionType }).then( -// O.map((layout) => ({ layout, sectionType })) -// ) -// ), -// A.sequence(T.ApplicativeSeq), -// unwrapSome, -// (x) => x -// )() -// const altSectionTypeLayouts = pipe( -// otherLayouts, -// A.reduce( -// {}, -// ( -// acc: Record< -// string, -// { layout: ColumnLayout; sectionType: SectionType; dnas: string[] } -// >, -// { layout, sectionType } -// ) => { -// return { -// ...acc, -// [sectionType.code]: { -// layout, -// sectionType, -// dnas: columnLayoutToDnas(layout), -// }, -// } -// } -// ) -// ) -// layoutsDB.altSectionTypeLayouts.put({ houseId, altSectionTypeLayouts }) -// } - const getVanillaColumn = async (key: VanillaColumnsKey) => { const maybeVanillaColumn = await layoutsDB.vanillaColumns.get( getVanillaColumnsKey(key) @@ -766,7 +694,6 @@ const api = { postLayout, postLayouts, getLayout, - syncModels, getVanillaColumn, // updateAltSectionTypeLayouts, } diff --git a/app/workers/models.ts b/app/workers/models.ts new file mode 100644 index 00000000..6b9ba6cc --- /dev/null +++ b/app/workers/models.ts @@ -0,0 +1,116 @@ +import { Module } from "../../server/data/modules" +import layoutsDB from "../db/layouts" +import systemsDB, { LastFetchStamped } from "../db/systems" +import { pipe } from "fp-ts/lib/function" +import produce from "immer" +import { BufferGeometry } from "three" +import { mergeBufferGeometries } from "three-stdlib" +import { getSpeckleObject } from "../../server/data/speckleModel" +import { A, R } from "../utils/functions" +import speckleIfcParser from "../utils/speckle/speckleIfcParser" +import { expose } from "comlink" +import { isSSR } from "../utils/next" +import { liveQuery } from "dexie" + +const getSpeckleModelGeometries = async (speckleBranchUrl: string) => { + const speckleObjectData = await getSpeckleObject(speckleBranchUrl) + const speckleObject = speckleIfcParser.parse(speckleObjectData) + + return pipe( + speckleObject, + A.reduce( + {}, + (acc: { [e: string]: BufferGeometry[] }, { ifcTag, geometry }) => { + return produce(acc, (draft) => { + if (ifcTag in draft) draft[ifcTag].push(geometry) + else draft[ifcTag] = [geometry] + }) + } + ), + R.map((geoms) => mergeBufferGeometries(geoms)), + R.filter((bg: BufferGeometry | null): bg is BufferGeometry => Boolean(bg)), + R.map((x) => x.toJSON()) + ) +} + +const putModuleModel = async ({ + systemId, + lastFetched, + speckleBranchUrl, +}: Pick< + LastFetchStamped, + "systemId" | "lastFetched" | "speckleBranchUrl" +>) => { + const geometries = await getSpeckleModelGeometries(speckleBranchUrl) + + const payload = { + speckleBranchUrl, + lastFetched, + geometries, + systemId, + } + + layoutsDB.models.put(payload) + + return payload +} + +const getModuleModel = async ({ + speckleBranchUrl, + systemId, +}: { + speckleBranchUrl: string + systemId: string +}) => { + const maybeModuleModel = await layoutsDB.models.get(speckleBranchUrl) + if (maybeModuleModel) return maybeModuleModel + return await putModuleModel({ + systemId, + speckleBranchUrl, + lastFetched: new Date().getTime(), + }) +} + +if (!isSSR()) { + liveQuery(() => systemsDB.modules.toArray()).subscribe((modules) => + pipe(modules, A.map(putModuleModel)) + ) +} + +export const syncModuleModels = (modules: LastFetchStamped[]) => { + modules.map(async (nextModule) => { + const { speckleBranchUrl, lastFetched } = nextModule + const maybeModel = await layoutsDB.models.get(speckleBranchUrl) + + if (maybeModel && maybeModel.lastFetched === lastFetched) { + return + } + + const geometries = await getSpeckleModelGeometries(speckleBranchUrl) + + layoutsDB.models.put({ + speckleBranchUrl, + lastFetched, + geometries, + systemId: nextModule.systemId, + }) + }) +} + +if (!isSSR()) { + liveQuery(() => systemsDB.modules.toArray()).subscribe((modules) => { + syncModuleModels(modules) + // modulesCache = modules + // processLayoutsQueue() + }) +} + +const api = { + syncModuleModels, + putModuleModel, + getModuleModel, +} + +export type ModelsAPI = typeof api + +expose(api) From 91846d1683fbbd1bdf18eeaf927810d94e12d393 Mon Sep 17 00:00:00 2001 From: Thomas O'Neill Date: Thu, 24 Aug 2023 12:50:32 +0100 Subject: [PATCH 132/132] wip --- app/workers/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/workers/index.ts b/app/workers/index.ts index 21d9ccbe..0c3802ad 100644 --- a/app/workers/index.ts +++ b/app/workers/index.ts @@ -15,6 +15,7 @@ export const initSystemsWorker = () => { } export const getSystemsWorker = () => { + if (isSSR()) return undefined as any if (systemsWorker === null) throw new Error(`couldn't get systemsWorker`) return systemsWorker } @@ -28,6 +29,7 @@ export const initLayoutsWorker = () => { } export const getLayoutsWorker = () => { + if (isSSR()) return undefined as any if (layoutsWorker === null) throw new Error(`couldn't get layoutsWorker`) return layoutsWorker } @@ -39,6 +41,7 @@ export const initModelsWorker = () => { } export const getModelsWorker = () => { + if (isSSR()) return undefined as any if (modelsWorker === null) throw new Error(`couldn't get modelsWorker`) return modelsWorker }