diff --git a/index.html b/index.html
index 116b64c..c556c7d 100644
--- a/index.html
+++ b/index.html
@@ -5,7 +5,7 @@
rel="stylesheet"
href="https://app.autodeskforma.eu/design-system/v2/forma/styles/base.css"
/>
-
+
+
Shadow study
diff --git a/package.json b/package.json
index 73da9cd..a4bebef 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
},
"dependencies": {
"file-saver": "^2.0.5",
- "forma-embedded-view-sdk": "0.59.1",
+ "forma-embedded-view-sdk": "^0.60.1",
"jszip": "3.10.1",
"lodash": "^4.17.21",
"luxon": "^3.4.4",
diff --git a/src/app.tsx b/src/app.tsx
index e8e213f..006a9d7 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -5,6 +5,7 @@ import IntervalSelector from "./components/IntervalSelector";
import ResolutionSelector from "./components/ResolutionSelector";
import TimeSelector from "./components/TimeSelector";
import PreviewButton from "./components/PreviewButton";
+import GeometryColorSelector from "./components/GeometryColorSelector";
export default function App() {
const [month, setMonth] = useState(6);
@@ -32,6 +33,7 @@ export default function App() {
/>
+
,
+ filterPredicate: (element: FormaElement) => boolean,
+ path: string = "root",
+): string[] {
+ const element = elements[urn];
+ const paths = [];
+ if (filterPredicate(element)) {
+ paths.push(path);
+ }
+
+ if (element.children) {
+ for (const child of element?.children) {
+ paths.push(
+ ...getPathOfElements(child.urn, elements, filterPredicate, `${path}/${child.key}`),
+ );
+ }
+ }
+
+ return paths;
+}
+
+/**
+ * Color the ground texture with a given color
+ */
+async function colorGround(color: string) {
+ const bbox = await Forma.terrain.getBbox();
+ const canvas = document.createElement("canvas");
+ const width = bbox.max.x - bbox.min.x;
+ const height = bbox.max.y - bbox.min.y;
+ canvas.width = width;
+ canvas.height = height;
+ const context = canvas.getContext("2d");
+ if (!context) {
+ return;
+ }
+ context.fillStyle = color;
+ context.fillRect(0, 0, width, height);
+ return await Forma.terrain.groundTexture.add({
+ name: "shadow-study",
+ canvas: canvas,
+ position: { x: 0, y: 0, z: 0 },
+ scale: { x: 1, y: 1 },
+ });
+}
+
+/**
+ * Color elements with a given color
+ */
+function colorPaths(elementPaths: string[], color: string) {
+ const pathsToColor = new Map();
+ for (const path of elementPaths) {
+ pathsToColor.set(path, color);
+ }
+
+ Forma.render.elementColors.set({ pathsToColor });
+}
+
+/**
+ * Will debounce the function call to avoid calling it too often.
+ * Useful for avoiding color input events to be called too often.
+ */
+export const debounce = ReturnType>(func: F, waitFor: number) => {
+ let timeout: number | undefined;
+
+ return (...args: Parameters): Promise> =>
+ new Promise((resolve) => {
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+
+ timeout = setTimeout(() => resolve(func(...args)), waitFor);
+ });
+};
+
+export default function GeometryColorSelector() {
+ const [shouldPaintGeometry, setShouldPaintGeometry] = useState(false);
+ const [shouldPaintTerrain, setShouldPaintTerrain] = useState(false);
+
+ const [elementPaths, setElementPaths] = useState([]);
+ const [rootUrn, setRootUrn] = useState();
+ const [geometryColor, setGeometryColor] = useState("#ffffff");
+ const [terrainColor, setTerrainColor] = useState("#ffffff");
+
+ useEffect(() => {
+ Forma.proposal.getRootUrn().then((rootUrn) => {
+ setRootUrn(rootUrn as Urn);
+ });
+ Forma.proposal.subscribe(
+ ({ rootUrn }) => {
+ setRootUrn(rootUrn as Urn);
+ },
+ { debouncedPersistedOnly: true },
+ );
+ }, []);
+
+ useEffect(() => {
+ if (rootUrn != null) {
+ Forma.elements.get({ urn: rootUrn as Urn, recursive: true }).then(({ elements }) => {
+ const paths = getPathOfElements(rootUrn as Urn, elements, () => true);
+ setElementPaths(paths);
+ });
+ }
+ }, [rootUrn]);
+
+ useEffect(() => {
+ if (shouldPaintTerrain && shouldPaintGeometry) {
+ colorGround(terrainColor).then(() => {
+ colorPaths(elementPaths, geometryColor);
+ });
+ } else if (shouldPaintTerrain) {
+ colorGround(terrainColor);
+ Forma.render.elementColors.clearAll();
+ } else if (shouldPaintGeometry) {
+ colorPaths(elementPaths, geometryColor);
+ Forma.terrain.groundTexture.remove({ name: "shadow-study" });
+ } else {
+ Forma.render.elementColors.clearAll();
+ Forma.terrain.groundTexture.remove({ name: "shadow-study" });
+ }
+ }, [shouldPaintTerrain, shouldPaintGeometry, geometryColor, elementPaths, terrainColor]);
+
+ return (
+ <>
+
+
+ Color geometry:
+
+
+ setShouldPaintGeometry(e.detail.checked)}
+ >
+ {
+ if (e.target instanceof HTMLInputElement) setGeometryColor(e.target?.value);
+ }, 50)}
+ />
+
+
+
+
+ Color terrain:
+
+
+ setShouldPaintTerrain(e.detail.checked)}
+ >
+ {
+ if (e.target instanceof HTMLInputElement) setTerrainColor(e.target?.value);
+ }, 50)}
+ />
+
+
+ >
+ );
+}
diff --git a/src/lib/weave.d.ts b/src/lib/weave.d.ts
index fef76f0..385c66b 100644
--- a/src/lib/weave.d.ts
+++ b/src/lib/weave.d.ts
@@ -1,23 +1,32 @@
-export declare module "preact/src/jsx" {
- namespace JSXInternal {
- interface IntrinsicElements {
- "weave-button": JSX.HTMLAttributes & {
- type?: "button" | "submit" | "reset";
- variant?: "outlined" | "flat" | "solid";
- density?: "high" | "medium";
- iconposition?: "left" | "right";
- };
- "weave-select": JSX.HTMLAttributes & {
- placeholder?: any;
- value: any;
- children: JSX.Element[];
- onChange: (e: CustomEvent<{ value: string; text: string }>) => void;
- };
- "weave-select-option": JSX.HTMLAttributes & {
- disabled?: true;
- value: any;
- children?: JSX.Element | string;
- };
- }
+namespace JSX {
+ interface IntrinsicElements {
+ "weave-button": JSX.HTMLAttributes & {
+ type?: "button" | "submit" | "reset";
+ variant?: "outlined" | "flat" | "solid";
+ density?: "high" | "medium";
+ iconposition?: "left" | "right";
+ };
+ "weave-select": JSX.HTMLAttributes & {
+ placeholder?: any;
+ value: any;
+ children: JSX.Element[];
+ onChange: (e: CustomEvent<{ value: string; text: string }>) => void;
+ };
+ "weave-select-option": JSX.HTMLAttributes & {
+ disabled?: true;
+ value: any;
+ children?: JSX.Element | string;
+ };
+ "weave-checkbox": {
+ onChange?: (e: CustomEvent) => void;
+ children?: JSX.Element | string;
+ style?: string;
+ checked: boolean;
+ showlabel?: boolean;
+ label?: string;
+ value?: string;
+ key?: string;
+ disabled?: boolean;
+ };
}
}
diff --git a/src/styles.css b/src/styles.css
index c08e2f2..a992798 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -58,3 +58,13 @@ weave-select {
color: #3c3c3cb2;
}
+
+.color-picker {
+ margin-left: 10px;
+ width: 40%;
+ cursor: pointer;
+ border: none;
+ block-size: 20px;
+ padding: 0;
+ background-color: transparent;
+}
diff --git a/tsconfig.json b/tsconfig.json
index b52d3d2..8ce2a2b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,20 +7,19 @@
"skipLibCheck": true,
/* Bundler mode */
- "moduleResolution": "node",
+ "moduleResolution": "bundler",
"allowImportingTsExtensions": true,
- "allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"jsxImportSource": "preact",
- /* Linting */
+ /* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
-}
\ No newline at end of file
+}
diff --git a/yarn.lock b/yarn.lock
index 836b755..72f26cf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -706,10 +706,10 @@ forma-elements@^1.5.2:
resolved "https://registry.yarnpkg.com/forma-elements/-/forma-elements-1.5.3.tgz#9413acb1fa0e8b88a5cc6da2cfb42545c9d75895"
integrity sha512-f5xSHypP5eETa1oFy8GYeRjmVI0/82ARuRtYTe8Smy0cZdGkRaDd5WFwNZT252KqxyOu/phUjDegZxZQxayIfA==
-forma-embedded-view-sdk@0.59.1:
- version "0.59.1"
- resolved "https://registry.yarnpkg.com/forma-embedded-view-sdk/-/forma-embedded-view-sdk-0.59.1.tgz#7692a7e543e4c7fc695be91bf19f7d7022d28b30"
- integrity sha512-UmFQJLFZYf+Vwg71CTmMW6/b8ybc1Xd+v3AFQ0CfofaAVNUhBDjUP4I+v97tk6Jpl0vM0Pt0CxPl8hOElwJRJg==
+forma-embedded-view-sdk@^0.60.1:
+ version "0.60.1"
+ resolved "https://registry.yarnpkg.com/forma-embedded-view-sdk/-/forma-embedded-view-sdk-0.60.1.tgz#c6619dcc93d7709e920694b15a2e80eb14ecff41"
+ integrity sha512-G8v5PBvi4OFIUhU6+S5U0dPod/hVFAvatJciq2Cw0GPDt28voX0GRpq6JXrSXHIYdfYsnc6zRfwERyIHzZ1Isw==
dependencies:
"@types/geojson" "^7946.0.13"
forma-elements "^1.5.2"