v2-ui #88
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React from "react";
|
||||||
import { HelpIcon } from "../icons/DashboardIcon";
|
import { HelpIcon } from "../icons/DashboardIcon";
|
||||||
import { useLogger } from "../ui/log/LoggerContext";
|
import { useLogger } from "../ui/log/LoggerContext";
|
||||||
import { GetLogIcon } from "./getLogIcons";
|
import { GetLogIcon } from "./getLogIcons";
|
||||||
@@ -16,35 +16,14 @@ const Footer: React.FC = () => {
|
|||||||
const { logs, setIsLogListVisible } = useLogger();
|
const { logs, setIsLogListVisible } = useLogger();
|
||||||
const lastLog = logs.length > 0 ? logs[logs.length - 1] : null;
|
const lastLog = logs.length > 0 ? logs[logs.length - 1] : null;
|
||||||
|
|
||||||
|
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { showShortcuts, setShowShortcuts } = useShortcutStore();
|
const { showShortcuts, setShowShortcuts } = useShortcutStore();
|
||||||
|
|
||||||
// Listen for Ctrl + Shift + ?
|
|
||||||
useEffect(() => {
|
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
|
||||||
if (
|
|
||||||
e.ctrlKey &&
|
|
||||||
e.shiftKey &&
|
|
||||||
(e.key === "?" || e.key === "/") // for some keyboards ? and / share the same key
|
|
||||||
) {
|
|
||||||
e.preventDefault();
|
|
||||||
setShowShortcuts(!showShortcuts); // toggle visibility directly
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
setShowShortcuts(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
|
||||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
||||||
}, [showShortcuts, setShowShortcuts]);
|
|
||||||
|
|
||||||
OuterClick({
|
OuterClick({
|
||||||
contextClassName: ["shortcut-helper-overlay"],
|
contextClassName: ["shortcut-helper-overlay"],
|
||||||
setMenuVisible: () => setShowShortcuts(false),
|
setMenuVisible: () => setShowShortcuts(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="footer-container">
|
<div className="footer-container">
|
||||||
<div className="footer-wrapper">
|
<div className="footer-wrapper">
|
||||||
|
|||||||
31
app/src/components/temporary/SelectFloorPlan.tsx
Normal file
31
app/src/components/temporary/SelectFloorPlan.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import React from "react";
|
||||||
|
import useLayoutStore from "../../store/builder/uselayoutStore";
|
||||||
|
|
||||||
|
const SelectFloorPlan: React.FC = () => {
|
||||||
|
const { currentLayout, setLayout } = useLayoutStore();
|
||||||
|
return (
|
||||||
|
<div className="select-floorplane-wrapper">
|
||||||
|
Preset Layouts
|
||||||
|
<div className="presets-container">
|
||||||
|
<button
|
||||||
|
className={`preset ${currentLayout === "layout1" ? "active" : ""}`}
|
||||||
|
onClick={() => {
|
||||||
|
setLayout("layout1");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Preset 1
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`preset ${currentLayout === "layout2" ? "active" : ""}`}
|
||||||
|
onClick={() => {
|
||||||
|
setLayout("layout2");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Preset 2
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectFloorPlan;
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
import React, { useEffect } from 'react';
|
import { useRoomsState, useToggleView } from "../../../store/builder/store";
|
||||||
import { useRoomsState, useToggleView } from '../../../store/builder/store';
|
import { computeArea } from "../functions/computeArea";
|
||||||
import { computeArea } from '../functions/computeArea';
|
import { Html } from "@react-three/drei";
|
||||||
import { Html } from '@react-three/drei';
|
|
||||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||||
import * as turf from '@turf/turf';
|
import * as turf from "@turf/turf";
|
||||||
import * as THREE from "three"
|
import * as THREE from "three";
|
||||||
|
|
||||||
const CalculateAreaGroup = () => {
|
const CalculateAreaGroup = () => {
|
||||||
const { roomsState } = useRoomsState();
|
const { roomsState } = useRoomsState();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const savedTheme: string | null = localStorage.getItem('theme');
|
const savedTheme: string | null = localStorage.getItem("theme");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group name="roomArea" visible={toggleView}>
|
<group name="roomArea" visible={toggleView}>
|
||||||
@@ -19,9 +18,9 @@ const CalculateAreaGroup = () => {
|
|||||||
const coordinates = room.coordinates;
|
const coordinates = room.coordinates;
|
||||||
if (!coordinates || coordinates.length < 3) return null;
|
if (!coordinates || coordinates.length < 3) return null;
|
||||||
|
|
||||||
const yPos = (room.layer || 0) * CONSTANTS.zoneConfig.height;
|
const coords2D = coordinates.map(
|
||||||
const coords2D = coordinates.map((p: any) => new THREE.Vector2(p.position.x, p.position.z));
|
(p: any) => new THREE.Vector2(p.position.x, p.position.z)
|
||||||
// console.log('coords2D: ', coords2D);
|
);
|
||||||
|
|
||||||
if (!coords2D[0].equals(coords2D[coords2D.length - 1])) {
|
if (!coords2D[0].equals(coords2D[coords2D.length - 1])) {
|
||||||
coords2D.push(coords2D[0]);
|
coords2D.push(coords2D[0]);
|
||||||
@@ -37,7 +36,7 @@ const CalculateAreaGroup = () => {
|
|||||||
geometry.rotateX(Math.PI / 2);
|
geometry.rotateX(Math.PI / 2);
|
||||||
|
|
||||||
const material = new THREE.MeshBasicMaterial({
|
const material = new THREE.MeshBasicMaterial({
|
||||||
color: savedTheme === "dark" ? "#d2baff" : '#6f42c1',
|
color: savedTheme === "dark" ? "#d2baff" : "#6f42c1",
|
||||||
side: THREE.DoubleSide,
|
side: THREE.DoubleSide,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 0.4,
|
opacity: 0.4,
|
||||||
@@ -46,7 +45,11 @@ const CalculateAreaGroup = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<group key={`roomFill-${index}`}>
|
<group key={`roomFill-${index}`}>
|
||||||
<mesh geometry={geometry} material={material} position={[0, yPos, 0]} />
|
<mesh
|
||||||
|
geometry={geometry}
|
||||||
|
material={material}
|
||||||
|
position={[0, 0.12, 0]}
|
||||||
|
/>
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -58,7 +61,10 @@ const CalculateAreaGroup = () => {
|
|||||||
|
|
||||||
if (!coordinates || coordinates.length < 3) return null;
|
if (!coordinates || coordinates.length < 3) return null;
|
||||||
|
|
||||||
let coords2D = coordinates.map((p: any) => [p.position.x, p.position.z]);
|
let coords2D = coordinates.map((p: any) => [
|
||||||
|
p.position.x,
|
||||||
|
p.position.z,
|
||||||
|
]);
|
||||||
|
|
||||||
const first = coords2D[0];
|
const first = coords2D[0];
|
||||||
const last = coords2D[coords2D.length - 1];
|
const last = coords2D[coords2D.length - 1];
|
||||||
@@ -69,7 +75,10 @@ const CalculateAreaGroup = () => {
|
|||||||
const polygon = turf.polygon([coords2D]);
|
const polygon = turf.polygon([coords2D]);
|
||||||
const center2D = turf.center(polygon).geometry.coordinates;
|
const center2D = turf.center(polygon).geometry.coordinates;
|
||||||
|
|
||||||
const sumY = coordinates.reduce((sum: number, p: any) => sum + p.position.y, 0);
|
const sumY = coordinates.reduce(
|
||||||
|
(sum: number, p: any) => sum + p.position.y,
|
||||||
|
0
|
||||||
|
);
|
||||||
const avgY = sumY / coordinates.length;
|
const avgY = sumY / coordinates.length;
|
||||||
|
|
||||||
const area = computeArea(room, "rooms");
|
const area = computeArea(room, "rooms");
|
||||||
@@ -100,6 +109,6 @@ const CalculateAreaGroup = () => {
|
|||||||
})}
|
})}
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CalculateAreaGroup;
|
export default CalculateAreaGroup;
|
||||||
|
|||||||
@@ -363,6 +363,10 @@ const FloorItemsGroup = ({
|
|||||||
if (!event.dataTransfer?.files[0]) return;
|
if (!event.dataTransfer?.files[0]) return;
|
||||||
|
|
||||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') {
|
if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') {
|
||||||
|
|
||||||
|
state.pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||||
|
state.pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||||
|
|
||||||
addAssetModel(
|
addAssetModel(
|
||||||
raycaster,
|
raycaster,
|
||||||
state.camera,
|
state.camera,
|
||||||
|
|||||||
@@ -13,22 +13,21 @@ const modelPaths: Record<string, string> = {
|
|||||||
function LayoutModel() {
|
function LayoutModel() {
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const { currentLayout } = useLayoutStore();
|
const { currentLayout } = useLayoutStore();
|
||||||
if (!currentLayout) return null;
|
|
||||||
|
|
||||||
const path = modelPaths[currentLayout];
|
// Always call the hook, but ensure it has a valid fallback
|
||||||
const gltf = useGLTF(path);
|
const path = currentLayout ? modelPaths[currentLayout] : modelPaths.layout1;
|
||||||
const cloned = useMemo(() => gltf?.scene?.clone(), [gltf]);
|
|
||||||
|
// Explicitly cast the return type to ensure it's not an array
|
||||||
|
const gltf = useGLTF(path) as any;
|
||||||
|
|
||||||
|
const cloned = useMemo(() => gltf.scene.clone(), [gltf]);
|
||||||
|
|
||||||
|
if (!currentLayout || !toggleView) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<group position={[0, 0, 0]} dispose={null}>
|
||||||
{toggleView &&
|
<primitive object={cloned} />
|
||||||
<group position={[0,0,0]} scale={[0,0,0]} dispose={null}>
|
|
||||||
<primitive
|
|
||||||
object={cloned}
|
|
||||||
/>
|
|
||||||
</group>
|
</group>
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { useLogger } from "../components/ui/log/LoggerContext";
|
|||||||
import RenderOverlay from "../components/templates/Overlay";
|
import RenderOverlay from "../components/templates/Overlay";
|
||||||
import LogList from "../components/ui/log/LogList";
|
import LogList from "../components/ui/log/LogList";
|
||||||
import Footer from "../components/footer/Footer";
|
import Footer from "../components/footer/Footer";
|
||||||
|
import SelectFloorPlan from "../components/temporary/SelectFloorPlan";
|
||||||
|
|
||||||
const Project: React.FC = () => {
|
const Project: React.FC = () => {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
@@ -98,6 +99,8 @@ const Project: React.FC = () => {
|
|||||||
{activeModule === "market" && <MarketPlace />}
|
{activeModule === "market" && <MarketPlace />}
|
||||||
{activeModule !== "market" && <Tools />}
|
{activeModule !== "market" && <Tools />}
|
||||||
{isPlaying && activeModule === "simulation" && <SimulationPlayer />}
|
{isPlaying && activeModule === "simulation" && <SimulationPlayer />}
|
||||||
|
{/* remove this later */}
|
||||||
|
{activeModule === "builder" && !toggleThreeD && <SelectFloorPlan />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
|
import * as CONSTANTS from '../../types/world/worldConstants';
|
||||||
|
|
||||||
export const useSocketStore = create<any>((set: any, get: any) => ({
|
export const useSocketStore = create<any>((set: any, get: any) => ({
|
||||||
socket: null,
|
socket: null,
|
||||||
@@ -386,8 +387,8 @@ export const useLimitDistance = create<any>((set: any) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const useTileDistance = create<any>((set: any) => ({
|
export const useTileDistance = create<any>((set: any) => ({
|
||||||
gridValue: { size: 300, divisions: 75 },
|
gridValue: { size: CONSTANTS.gridConfig.size, divisions: CONSTANTS.gridConfig.divisions },
|
||||||
planeValue: { height: 300, width: 300 },
|
planeValue: { height: CONSTANTS.planeConfig.height, width: CONSTANTS.planeConfig.width },
|
||||||
|
|
||||||
setGridValue: (value: any) =>
|
setGridValue: (value: any) =>
|
||||||
set((state: any) => ({
|
set((state: any) => ({
|
||||||
|
|||||||
@@ -42,17 +42,16 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: #{$border-radius-large};
|
border-radius: #{$border-radius-large};
|
||||||
|
|
||||||
.zone-header {
|
|
||||||
@include flex-center;
|
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: start;
|
|
||||||
max-width: 180px;
|
max-width: 180px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-align: start;
|
||||||
}
|
}
|
||||||
|
.zone-header {
|
||||||
|
@include flex-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.options-container {
|
.options-container {
|
||||||
@include flex-center;
|
@include flex-center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
@@ -74,9 +73,11 @@
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
.list-item {
|
||||||
background: var(--highlight-accent-color);
|
background: var(--highlight-accent-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.asset-list {
|
.asset-list {
|
||||||
border-left: 2px solid var(--border-color);
|
border-left: 2px solid var(--border-color);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
border-radius: #{$border-radius-medium};
|
border-radius: #{$border-radius-medium};
|
||||||
box-shadow: var(--box-shadow-light);
|
box-shadow: var(--box-shadow-light);
|
||||||
}
|
}
|
||||||
.area{
|
.area {
|
||||||
background: #008cff;
|
background: #008cff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,3 +27,34 @@
|
|||||||
.pointer-none {
|
.pointer-none {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// temp
|
||||||
|
.select-floorplane-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
@include flex-center;
|
||||||
|
gap: 12px;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
padding: 8px;
|
||||||
|
padding-left: 14px;
|
||||||
|
background: var(--background-color);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
border-radius: #{$border-radius-large};
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
transform: translate(-50%, 12px);
|
||||||
|
z-index: 100;
|
||||||
|
.presets-container {
|
||||||
|
@include flex-center;
|
||||||
|
gap: 4px;
|
||||||
|
.preset {
|
||||||
|
background: var(--background-color);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: #{$border-radius-large};
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
.active{
|
||||||
|
background: var(--background-color-accent);
|
||||||
|
color: var(--text-button-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -227,22 +227,10 @@ export const twoDimension: TwoDimension = {
|
|||||||
rightMouse: 0, // Mouse button for no action
|
rightMouse: 0, // Mouse button for no action
|
||||||
};
|
};
|
||||||
|
|
||||||
export const threeDimension: ThreeDimension = {
|
|
||||||
defaultPosition: [0, 40, 30], // Default position of the camera
|
|
||||||
defaultTarget: [0, 0, 0], // Default target of the camera
|
|
||||||
defaultRotation: [0, 0, 0], // Default rotation of the camera
|
|
||||||
defaultAzimuth: 0, // Default azimuth of the camera
|
|
||||||
boundaryBottom: [-150, 0, -150], // Bottom boundary of the camera movement
|
|
||||||
boundaryTop: [150, 100, 150], // Top boundary of the camera movement
|
|
||||||
minDistance: 1, // Minimum distance from the target
|
|
||||||
leftMouse: 2, // Mouse button for panning
|
|
||||||
rightMouse: 1, // Mouse button for rotation
|
|
||||||
};
|
|
||||||
|
|
||||||
export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position
|
export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position
|
||||||
|
|
||||||
export const gridConfig: GridConfig = {
|
export const gridConfig: GridConfig = {
|
||||||
size: 300, // Size of the grid
|
size: 150, // Size of the grid
|
||||||
divisions: 75, // Number of divisions in the grid
|
divisions: 75, // Number of divisions in the grid
|
||||||
primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid
|
primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid
|
||||||
secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid
|
secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid
|
||||||
@@ -251,13 +239,25 @@ export const gridConfig: GridConfig = {
|
|||||||
position3D: [0, -0.5, 0], // Position of the grid in 3D view
|
position3D: [0, -0.5, 0], // Position of the grid in 3D view
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const threeDimension: ThreeDimension = {
|
||||||
|
defaultPosition: [0, 40, 30], // Default position of the camera
|
||||||
|
defaultTarget: [0, 0, 0], // Default target of the camera
|
||||||
|
defaultRotation: [0, 0, 0], // Default rotation of the camera
|
||||||
|
defaultAzimuth: 0, // Default azimuth of the camera
|
||||||
|
boundaryBottom: [-gridConfig.size / 2, 0, -gridConfig.size / 2], // Bottom boundary of the camera movement
|
||||||
|
boundaryTop: [gridConfig.size / 2, 100, gridConfig.size / 2], // Top boundary of the camera movement
|
||||||
|
minDistance: 1, // Minimum distance from the target
|
||||||
|
leftMouse: 2, // Mouse button for panning
|
||||||
|
rightMouse: 1, // Mouse button for rotation
|
||||||
|
};
|
||||||
|
|
||||||
export const planeConfig: PlaneConfig = {
|
export const planeConfig: PlaneConfig = {
|
||||||
position2D: [0, -0.5, 0], // Position of the plane
|
position2D: [0, -0.5, 0], // Position of the plane
|
||||||
position3D: [0, -0.65, 0], // Position of the plane
|
position3D: [0, -0.65, 0], // Position of the plane
|
||||||
rotation: -Math.PI / 2, // Rotation of the plane
|
rotation: -Math.PI / 2, // Rotation of the plane
|
||||||
|
|
||||||
width: 300, // Width of the plane
|
width: 150, // Width of the plane
|
||||||
height: 300, // Height of the plane
|
height: 150, // Height of the plane
|
||||||
color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane
|
color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user