diff --git a/app/src/components/footer/Footer.tsx b/app/src/components/footer/Footer.tsx index 19fc6c5..697c182 100644 --- a/app/src/components/footer/Footer.tsx +++ b/app/src/components/footer/Footer.tsx @@ -1,25 +1,7 @@ import React from "react"; import { HelpIcon } from "../icons/DashboardIcon"; -import { - LogInfoIcon, - ErrorIcon, - WarningIcon, -} from "../icons/ExportCommonIcons"; // Adjust path as needed import { useLogger } from "../ui/log/LoggerContext"; - -const getLogIcon = (type: string) => { - switch (type) { - case "info": - return ; - case "error": - return ; - case "warning": - return ; - case "log": - default: - return ; - } -}; +import { GetLogIcon } from "./getLogIcons"; const Footer: React.FC = () => { const { logs, setIsLogListVisible } = useLogger(); @@ -43,15 +25,22 @@ const Footer: React.FC = () => {
-
setIsLogListVisible(true)}> - {lastLog ? ( - <> - {getLogIcon(lastLog.type)} - {lastLog.message} - - ) : ( - "No logs yet." - )} +
+
+
+
V 0.01 diff --git a/app/src/components/footer/getLogIcons.tsx b/app/src/components/footer/getLogIcons.tsx new file mode 100644 index 0000000..7a26496 --- /dev/null +++ b/app/src/components/footer/getLogIcons.tsx @@ -0,0 +1,19 @@ +import { ErrorIcon, LogIcon, LogInfoIcon, SucessIcon, WarningIcon } from "../icons/LogIcons"; +import { LogType } from "../ui/log/LoggerContext"; + +export const GetLogIcon = (type: LogType): JSX.Element => { + switch (type) { + case "info": + return ; + case "log": + return ; + case "error": + return ; + case "warning": + return ; + case "success": + return ; + default: + return ; + } +}; diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index b4ec2cf..9b66849 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -826,7 +826,7 @@ export const LogListIcon = () => { > { xmlns="http://www.w3.org/2000/svg" > diff --git a/app/src/components/icons/LogIcons.tsx b/app/src/components/icons/LogIcons.tsx new file mode 100644 index 0000000..abee971 --- /dev/null +++ b/app/src/components/icons/LogIcons.tsx @@ -0,0 +1,98 @@ +export const LogInfoIcon = () => { + return ( + + + + + ); +}; + +export const WarningIcon = () => { + return ( + + + + + ); +}; + +export const ErrorIcon = () => { + return ( + + + + + ); +}; + +export const SucessIcon = () => { + return ( + + + + + ); +}; + +export const LogIcon = () => { + return ( + + + + + ); +}; diff --git a/app/src/components/icons/analysis.tsx b/app/src/components/icons/analysis.tsx index ee2fcc6..001787e 100644 --- a/app/src/components/icons/analysis.tsx +++ b/app/src/components/icons/analysis.tsx @@ -1,41 +1,138 @@ export function ThroughputSummaryIcon() { return ( - + + + + + + + + + + + + + + + ); } + export function ProductionCapacityIcon() { return ( - - + + + + + + + + + + + + + + + + + + + + + ); } + export function ROISummaryIcon() { return ( - + + - ); } + export function PowerIcon() { return ( ); } + +export function ROISummaryProductName() { + return ( + + + + + ); +} + +export function SonarCrownIcon() { + return ( + + + + + ); +} + +export function CostBreakDownIcon() { + return ( + + + + ); +} + +export function LightBulpIcon() { + return ( + + + + ); +} diff --git a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx index 1280693..7799858 100644 --- a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx +++ b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx @@ -1,15 +1,15 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useRef, useState } from "react"; import { - AddIcon, - ArrowIcon, - RemoveIcon, - ResizeHeightIcon, + AddIcon, + ArrowIcon, + RemoveIcon, + ResizeHeightIcon, } from "../../../icons/ExportCommonIcons"; import RenameInput from "../../../ui/inputs/RenameInput"; import { handleResize } from "../../../../functions/handleResizePannel"; import { - useSelectedAsset, - useSelectedProduct, + useSelectedAsset, + useSelectedProduct, } from "../../../../store/simulation/useSimulationStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { generateUUID } from "three/src/math/MathUtils"; @@ -22,206 +22,222 @@ import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertP import { deleteProductApi } from "../../../../services/simulation/deleteProductApi"; interface Event { - pathName: string; + pathName: string; } interface ListProps { - val: Event; + val: Event; } const List: React.FC = ({ val }) => { - return ( -
-
{val.pathName}
-
- ); + return ( +
+
{val.pathName}
+
+ ); }; const Simulations: React.FC = () => { - const productsContainerRef = useRef(null); - const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent, } = useProductStore(); - const { selectedProduct, setSelectedProduct } = useSelectedProduct(); - const { getEventByModelUuid } = useEventsStore(); - const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - const [openObjects, setOpenObjects] = useState(true); + const productsContainerRef = useRef(null); + const { + products, + addProduct, + removeProduct, + renameProduct, + addEvent, + removeEvent, + } = useProductStore(); + const { selectedProduct, setSelectedProduct } = useSelectedProduct(); + const { getEventByModelUuid } = useEventsStore(); + const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + const [openObjects, setOpenObjects] = useState(true); - const handleAddProduct = () => { - const id = generateUUID(); - const name = `Product ${products.length + 1}`; - addProduct(name, id); - upsertProductOrEventApi({ productName: name, productId: id, organization: organization }); - }; + const handleAddProduct = () => { + const id = generateUUID(); + const name = `Product ${products.length + 1}`; + addProduct(name, id); + upsertProductOrEventApi({ + productName: name, + productId: id, + organization: organization, + }); + }; - const handleRemoveProduct = (productId: string) => { - const currentIndex = products.findIndex((p) => p.productId === productId); - const isSelected = selectedProduct.productId === productId; + const handleRemoveProduct = (productId: string) => { + const currentIndex = products.findIndex((p) => p.productId === productId); + const isSelected = selectedProduct.productId === productId; - const updatedProducts = products.filter((p) => p.productId !== productId); + const updatedProducts = products.filter((p) => p.productId !== productId); - if (isSelected) { - if (updatedProducts.length > 0) { - let newSelectedIndex = currentIndex; - if (currentIndex >= updatedProducts.length) { - newSelectedIndex = updatedProducts.length - 1; - } - setSelectedProduct( - updatedProducts[newSelectedIndex].productId, - updatedProducts[newSelectedIndex].productName - ); - } else { - setSelectedProduct("", ""); - } + if (isSelected) { + if (updatedProducts.length > 0) { + let newSelectedIndex = currentIndex; + if (currentIndex >= updatedProducts.length) { + newSelectedIndex = updatedProducts.length - 1; } + setSelectedProduct( + updatedProducts[newSelectedIndex].productId, + updatedProducts[newSelectedIndex].productName + ); + } else { + setSelectedProduct("", ""); + } + } - removeProduct(productId); - deleteProductApi(productId, organization); - }; + removeProduct(productId); + deleteProductApi(productId, organization); + }; - const handleRenameProduct = (productId: string, newName: string) => { - renameProduct(productId, newName); - if (selectedProduct.productId === productId) { - setSelectedProduct(productId, newName); - } - }; + const handleRenameProduct = (productId: string, newName: string) => { + renameProduct(productId, newName); + if (selectedProduct.productId === productId) { + setSelectedProduct(productId, newName); + } + }; - const handleRemoveEventFromProduct = () => { - if (selectedAsset) { - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - deleteEventDataApi({ - productId: selectedProduct.productId, - modelUuid: selectedAsset.modelUuid, - organization: organization - }); - removeEvent(selectedProduct.productId, selectedAsset.modelUuid); - clearSelectedAsset(); - } - }; + const handleRemoveEventFromProduct = () => { + if (selectedAsset) { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + deleteEventDataApi({ + productId: selectedProduct.productId, + modelUuid: selectedAsset.modelUuid, + organization: organization, + }); + removeEvent(selectedProduct.productId, selectedAsset.modelUuid); + clearSelectedAsset(); + } + }; - const selectedProductData = products.find( - (product) => product.productId === selectedProduct.productId - ); + const selectedProductData = products.find( + (product) => product.productId === selectedProduct.productId + ); - const events: Event[] = selectedProductData?.eventDatas.map((event) => ({ - pathName: event.modelName, + const events: Event[] = + selectedProductData?.eventDatas.map((event) => ({ + pathName: event.modelName, })) || []; - return ( -
-
Simulations
-
-
-
-
Products
-
- Add -
-
-
-
- {products.map((product, index) => ( -
-
- setSelectedProduct(product.productId, product.productName) - } - > - - - handleRenameProduct(product.productId, newName) - } - /> -
- {products.length > 1 && ( -
handleRemoveProduct(product.productId)} - > - -
- )} -
- ))} -
-
handleResize(e, productsContainerRef)} - > - -
-
-
- -
- - {openObjects && - events.map((event, index) => )} -
- -
-
- Need to Compare Layout? -
-
- Click 'Compare' to review and analyze the layout - differences between them. -
-
- -
-
-
- - {selectedAsset && ( - - { - if (option === "Add to Product") { - handleAddEventToProduct({ - event: getEventByModelUuid(selectedAsset.modelUuid), - addEvent, - selectedProduct, - clearSelectedAsset - }); - } else { - handleRemoveEventFromProduct(); - } - }} + return ( +
+
Simulations
+
+
+
+
Products
+ +
+
+
+ {products.map((product, index) => ( +
+ {/* eslint-disable-next-line */} +
+ setSelectedProduct(product.productId, product.productName) + } + > + - - )} + + handleRenameProduct(product.productId, newName) + } + /> +
+ {products.length > 1 && ( + + )} +
+ ))} +
+ +
- ) + +
+ + {openObjects && + events.map((event, index) => ( + + ))} +
+ +
+
+ Need to Compare Layout? +
+
+ Click 'Compare' to review and analyze the layout + differences between them. +
+
+ +
+
+
+ + {selectedAsset && ( + + { + if (option === "Add to Product") { + handleAddEventToProduct({ + event: getEventByModelUuid(selectedAsset.modelUuid), + addEvent, + selectedProduct, + clearSelectedAsset, + }); + } else { + handleRemoveEventFromProduct(); + } + }} + /> + + )} +
+ ); }; export default Simulations; diff --git a/app/src/components/ui/analysis/ProductionCapacity.tsx b/app/src/components/ui/analysis/ProductionCapacity.tsx index 31c3a02..1cdf105 100644 --- a/app/src/components/ui/analysis/ProductionCapacity.tsx +++ b/app/src/components/ui/analysis/ProductionCapacity.tsx @@ -1,21 +1,81 @@ -import React, { useState } from "react"; -import { ProductionCapacityIcon } from "../../icons/analysis"; +import React from "react"; +import { Line } from "react-chartjs-2"; +import { + Chart as ChartJS, + LineElement, + CategoryScale, + LinearScale, + PointElement, +} from "chart.js"; +import { PowerIcon, ProductionCapacityIcon } from "../../icons/analysis"; -const ProductionCapacity = ({ - progressPercent = 50, - avgProcessTime = "28.4 Secs/unit", - machineUtilization = "78%", - throughputValue = 128, - timeRange = { startTime: "08:00 AM", endTime: "09:00 AM" }, -}) => { - const totalBars = 6; - const barsToFill = Math.floor((progressPercent / 100) * totalBars); - const partialFillPercent = - ((progressPercent / 100) * totalBars - barsToFill) * 100; +ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement); + +const ThroughputSummary = () => { + // Define all data internally within the component + const timeRange = { + startTime: "08:00 AM", + endTime: "09:00 AM", + }; + + const throughputData = { + labels: ["08:00", "08:10", "08:20", "08:30", "08:40", "08:50"], + data: [5, 10, 8, 10, 12, 10], + totalThroughput: 1240, + assetUsage: 85, + }; + + const energyConsumption = { + energyConsumed: 456, + unit: "KWH", + }; + + // Dynamic shift data + const shiftUtilization = [ + { shift: 1, percentage: 30, color: "#F3C64D" }, + { shift: 2, percentage: 40, color: "#67B3F4" }, + { shift: 3, percentage: 30, color: "#7981F5" }, + ]; + + // Chart data configuration + const chartData = { + labels: throughputData.labels, + datasets: [ + { + label: "Units/hour", + data: throughputData.data, + borderColor: "#B392F0", + tension: 0.4, + pointRadius: 0, // Hide points + }, + ], + }; + + const chartOptions = { + responsive: true, + scales: { + x: { + display: false, // hide X axis completely + }, + y: { + display: false, // hide Y axis completely + min: 0, // force Y axis to start at 0 + }, + }, + plugins: { + legend: { + display: false, + }, + tooltip: { + enabled: true, + }, + }, + }; + return ( -
-
+
+
Production Capacity
@@ -30,34 +90,63 @@ const ProductionCapacity = ({
- {throughputValue} Units/hour + {throughputData.totalThroughput}{" "} + Units/hour
- - {/* Dynamic Progress Bar */} -
- {[...Array(totalBars)].map((_, i) => ( -
- {i < barsToFill ? ( -
- ) : i === barsToFill ? ( -
- ) : null} -
- ))} +
+
+
Asset usage
+
{throughputData.assetUsage}%
+
+
-
-
- Avg. Process Time - {avgProcessTime} +
+
+
Energy Consumption
+
+
+ +
+
+
{energyConsumption.energyConsumed}
+
{energyConsumption.unit}
+
+
-
- Machine Utilization - {machineUtilization} +
+
Shift Utilization
+
+
{throughputData.assetUsage}%
+ +
+ {/* Dynamically create progress bars based on shiftUtilization array */} + {shiftUtilization.map((shift, index) => ( +
+ ))} +
+ +
+ {/* Dynamically create shift indicators with random colors */} + {shiftUtilization.map((shift, index) => ( +
+ + +
+ ))} +
+
@@ -65,4 +154,6 @@ const ProductionCapacity = ({ ); }; -export default ProductionCapacity; +export default ThroughputSummary; + +// Can we add dot pattern to background of lineChart and remove axis diff --git a/app/src/components/ui/analysis/ROISummary.tsx b/app/src/components/ui/analysis/ROISummary.tsx index 63ae707..81441a2 100644 --- a/app/src/components/ui/analysis/ROISummary.tsx +++ b/app/src/components/ui/analysis/ROISummary.tsx @@ -1,5 +1,11 @@ import React, { useState } from "react"; -import { ROISummaryIcon } from "../../icons/analysis"; +import { + CostBreakDownIcon, + LightBulpIcon, + ROISummaryIcon, + ROISummaryProductName, + SonarCrownIcon, +} from "../../icons/analysis"; import SemiCircleProgress from "./SemiCircleProgress"; import { ArrowIcon } from "../../icons/ExportCommonIcons"; @@ -77,10 +83,12 @@ const ROISummary = ({
+
Product :
{roiSummaryData.productName}
+
+{roiSummaryData.roiPercentage}% ROI with payback in @@ -109,7 +117,9 @@ const ROISummary = ({
- Net Profit + + Net Profit + {roiSummaryData.netProfit}
@@ -117,7 +127,7 @@ const ROISummary = ({
- + Cost Breakdown
@@ -169,13 +179,19 @@ const ROISummary = ({
- 💡 + + + How to improve ROI?
Increase CNC utilization by 10%{" "} to shave 0.5 months of payback period +
+
+
+
-
{/* Tabs */} -
- {["all", "info", "warning", "error"].map((type) => ( -
setSelectedTab(type as any)} - > - {`${type.charAt(0).toUpperCase() + type.slice(1)} Logs`} -
- ))} +
+
+ {["all", "info", "warning", "error"].map((type) => ( + + ))} +
+
{/* Log Entries */}
- {filteredLogs.map((log) => ( -
-
{getLogIcon(log.type)}
-
-
{log.message}
-
- {formatTimestamp(log.timestamp)} + {filteredLogs.length > 0 ? ( + filteredLogs.map((log) => ( +
+
{GetLogIcon(log.type)}
+
+
{log.message}
+
+ {formatTimestamp(log.timestamp)} +
-
- ))} + )) + ) : ( +
There are no logs to display at the moment.
+ )}
diff --git a/app/src/components/ui/log/LoggerContext.tsx b/app/src/components/ui/log/LoggerContext.tsx index f323b93..6e63a55 100644 --- a/app/src/components/ui/log/LoggerContext.tsx +++ b/app/src/components/ui/log/LoggerContext.tsx @@ -1,6 +1,7 @@ -import React, { createContext, useContext, useState, useCallback } from "react"; +import React, { createContext, useContext, useState, useCallback, useMemo } from "react"; +import { MathUtils } from "three"; -export type LogType = "log" | "info" | "warning" | "error"; +export type LogType = "log" | "info" | "warning" | "error" | "success"; export interface LogEntry { id: string; @@ -18,6 +19,7 @@ interface LoggerContextValue { info: (message: string) => void; warn: (message: string) => void; error: (message: string) => void; + success: (message: string) => void; clear: () => void; } @@ -29,37 +31,34 @@ export const LoggerProvider: React.FC<{ children: React.ReactNode }> = ({ const [logs, setLogs] = useState([]); const [isLogListVisible, setIsLogListVisible] = useState(false); - const generateId = useCallback( - () => Math.random().toString(36).substring(2, 9), - [] - ); - const addLog = useCallback( (type: LogType, message: string) => { const newLog: LogEntry = { - id: generateId(), + id: MathUtils.generateUUID(), type, message, timestamp: new Date(), }; setLogs((prevLogs) => [...prevLogs, newLog]); }, - [generateId] + [] ); - const loggerMethods: LoggerContextValue = { - logs, - setLogs, - isLogListVisible, - setIsLogListVisible, - log: (message: string) => addLog("log", message), - info: (message: string) => addLog("info", message), - warn: (message: string) => addLog("warning", message), - error: (message: string) => addLog("error", message), - clear: () => { - setLogs([]); - }, - }; + const loggerMethods: LoggerContextValue = useMemo( + () => ({ + logs, + setLogs, + isLogListVisible, + setIsLogListVisible, + log: (message: string) => addLog("log", message), + info: (message: string) => addLog("info", message), + warn: (message: string) => addLog("warning", message), + error: (message: string) => addLog("error", message), + success: (message: string) => addLog("success", message), + clear: () => setLogs([]), + }), + [logs, setLogs, isLogListVisible, setIsLogListVisible, addLog] + ); return ( diff --git a/app/src/components/ui/log/logger.ts b/app/src/components/ui/log/logger.ts deleted file mode 100644 index 1781e26..0000000 --- a/app/src/components/ui/log/logger.ts +++ /dev/null @@ -1,71 +0,0 @@ -type LogType = 'log' | 'info' | 'warning' | 'error'; - -interface LogEntry { - type: LogType; - message: string; - timestamp: Date; - context?: string; -} - -class Logger { - private static instance: Logger; - private logs: LogEntry[] = []; - private subscribers: Array<(log: LogEntry) => void> = []; - - private constructor() {} - - public static getInstance(): Logger { - if (!Logger.instance) { - Logger.instance = new Logger(); - } - return Logger.instance; - } - - private notifySubscribers(log: LogEntry) { - this.subscribers.forEach(callback => callback(log)); - } - - private addLog(type: LogType, message: string, context?: string) { - const log: LogEntry = { type, message, timestamp: new Date(), context }; - this.logs.push(log); - this.notifySubscribers(log); - - if (process.env.NODE_ENV === 'development') { - const logMessage = context ? `[${context}] ${message}` : message; - console[type === 'warning' ? 'warn' : type](logMessage); - } - } - - public log(message: string, context?: string) { - this.addLog('log', message, context); - } - - public info(message: string, context?: string) { - this.addLog('info', message, context); - } - - public warning(message: string, context?: string) { - this.addLog('warning', message, context); - } - - public error(message: string, context?: string) { - this.addLog('error', message, context); - } - - public getLogs(): LogEntry[] { - return [...this.logs]; - } - - public clear() { - this.logs = []; - } - - public subscribe(callback: (log: LogEntry) => void) { - this.subscribers.push(callback); - return () => { - this.subscribers = this.subscribers.filter(sub => sub !== callback); - }; - } -} - -export const logger = Logger.getInstance(); diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index eb937c8..558957b 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React from "react"; import { useLeftData, useTopData, @@ -16,29 +16,25 @@ const EditWidgetOption: React.FC = ({ const { top } = useTopData(); const { left } = useLeftData(); - useEffect(() => { - - }, [top, left]); - return (
-
+
{options.map((option, index) => ( -
onClick(option)} > {option} -
+ ))}
diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index e569250..2d9d4e3 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -19,8 +19,8 @@ import { } from "../../icons/ExportCommonIcons"; import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; import { useSubModuleStore } from "../../../store/useModuleStore"; -import ProductionCapacity from "../analysis/ProductionCapacity"; -import ThroughputSummary from "../analysis/ThroughputSummary"; +import ProductionCapacity from "../analysis/ThroughputSummary"; +import ThroughputSummary from "../analysis/ProductionCapacity"; import ROISummary from "../analysis/ROISummary"; const SimulationPlayer: React.FC = () => { @@ -64,8 +64,6 @@ const SimulationPlayer: React.FC = () => { const handleMouseDown = () => { isDragging.current = true; - document.addEventListener("mousemove", handleMouseMove); - document.addEventListener("mouseup", handleMouseUp); }; const handleMouseMove = (e: MouseEvent) => { @@ -80,15 +78,16 @@ const SimulationPlayer: React.FC = () => { const handleMouseUp = () => { isDragging.current = false; - document.removeEventListener("mousemove", handleMouseMove); - document.removeEventListener("mouseup", handleMouseUp); }; useEffect(() => { + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); return () => { document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Slider function ends @@ -109,24 +108,6 @@ const SimulationPlayer: React.FC = () => { { name: "process 9", completed: 90 }, // 90% completed { name: "process 10", completed: 30 }, // 30% completed ]; - // Move getRandomColor out of render - const getRandomColor = () => { - const letters = "0123456789ABCDEF"; - let color = "#"; - for (let i = 0; i < 6; i++) { - color += letters[Math.floor(Math.random() * 16)]; - } - return color; - }; - - // Store colors for each process item - const [_, setProcessColors] = useState([]); - - // Generate colors on mount or when process changes - useEffect(() => { - const generatedColors = process.map(() => getRandomColor()); - setProcessColors(generatedColors); - }, []); const intervals = [10, 20, 30, 40, 50, 60]; // in minutes const totalSegments = intervals.length; @@ -218,7 +199,7 @@ const SimulationPlayer: React.FC = () => {
)} - {subModule === "simulations" && ( + {subModule !== "analysis" && (
{playSimulation @@ -281,7 +262,7 @@ const SimulationPlayer: React.FC = () => { const segmentProgress = (index / totalSegments) * 100; const isFilled = progress >= segmentProgress; return ( - +
{label} mins
{
-
- - +
+
+ + +
+
4x
-
4x
@@ -360,6 +345,7 @@ const SimulationPlayer: React.FC = () => { className="process-wrapper" style={{ padding: expand ? "0px" : "5px 35px" }} > + {/* eslint-disable-next-line */}
{ > {process.map((item, index) => (
{ )}
-
-
- - + {/* {subModule === "analysis" && ( */} + {subModule === "analysis" && ( +
+
+ + +
+
- -
+ )} + {/* )} */} ); }; diff --git a/app/src/index.css b/app/src/index.css deleted file mode 100644 index bd5bd6d..0000000 --- a/app/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/app/src/index.tsx b/app/src/index.tsx index f241ca6..ba329e8 100644 --- a/app/src/index.tsx +++ b/app/src/index.tsx @@ -1,6 +1,5 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import './index.css'; import App from './app'; import reportWebVitals from './reportWebVitals'; diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index d367493..c0a3267 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -22,7 +22,6 @@ import { useDeletePointOrLine, useMovePoint, useActiveLayer, - useSocketStore, useWallVisibility, useRoofVisibility, useShadows, @@ -48,15 +47,11 @@ import FloorGroupAilse from "./groups/floorGroupAisle"; import Draw from "./functions/draw"; import WallsAndWallItems from "./groups/wallsAndWallItems"; import Ground from "../scene/environment/ground"; -// import ZoneGroup from "../groups/zoneGroup1"; import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment"; import Layer2DVisibility from "./geomentries/layers/layer2DVisibility"; -import DrieHtmlTemp from "../visualization/mqttTemp/drieHtmlTemp"; import ZoneGroup from "./groups/zoneGroup"; -import useModuleStore from "../../store/useModuleStore"; import MeasurementTool from "../scene/tools/measurementTool"; import NavMesh from "../simulation/vehicle/navMesh/navMesh"; -import ProductionCapacity from "../../components/ui/analysis/ProductionCapacity"; export default function Builder() { const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. @@ -113,21 +108,19 @@ export default function Builder() { const [selectedItemsIndex, setSelectedItemsIndex] = useState(null); // State for tracking the index of the selected item. - const { activeLayer, setActiveLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx. - const { toggleView, setToggleView } = useToggleView(); // State for toggling between 2D and 3D. + const { activeLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx. + const { toggleView } = useToggleView(); // State for toggling between 2D and 3D. const { toolMode, setToolMode } = useToolMode(); - const { movePoint, setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not. - const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); - const { socket } = useSocketStore(); - const { roofVisibility, setRoofVisibility } = useRoofVisibility(); - const { wallVisibility, setWallVisibility } = useWallVisibility(); - const { shadows, setShadows } = useShadows(); - const { renderDistance, setRenderDistance } = useRenderDistance(); - const { limitDistance, setLimitDistance } = useLimitDistance(); - const { updateScene, setUpdateScene } = useUpdateScene(); - const { walls, setWalls } = useWalls(); + const { setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not. + const { setDeletePointOrLine } = useDeletePointOrLine(); + const { setRoofVisibility } = useRoofVisibility(); + const { setWallVisibility } = useWallVisibility(); + const { setShadows } = useShadows(); + const { setRenderDistance } = useRenderDistance(); + const { setLimitDistance } = useLimitDistance(); + const { setUpdateScene } = useUpdateScene(); + const { setWalls } = useWalls(); const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); - const { activeModule } = useModuleStore(); // const loader = new GLTFLoader(); // const dracoLoader = new DRACOLoader(); diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index f3f5050..d5ca04f 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -1,11 +1,25 @@ import { useFrame, useThree } from "@react-three/fiber"; -import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store"; +import { + useActiveTool, + useAsset3dWidget, + useCamMode, + useDeletableFloorItem, + useDeleteTool, + useFloorItems, + useLoadingProgress, + useRenderDistance, + useSelectedFloorItem, + useSelectedItem, + useSocketStore, + useToggleView, + useTransformMode, +} from "../../../store/store"; import assetVisibility from "../geomentries/assets/assetVisibility"; import { useEffect } from "react"; import * as THREE from "three"; import * as Types from "../../../types/world/worldTypes"; import assetManager, { - cancelOngoingTasks, + cancelOngoingTasks, } from "../geomentries/assets/assetManager"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; @@ -18,313 +32,422 @@ import useModuleStore from "../../../store/useModuleStore"; // import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; import { useEventsStore } from "../../../store/simulation/useEventsStore"; -const assetManagerWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", import.meta.url)); -const gltfLoaderWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", import.meta.url)); +const assetManagerWorker = new Worker( + new URL( + "../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", + import.meta.url + ) +); +const gltfLoaderWorker = new Worker( + new URL( + "../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", + import.meta.url + ) +); -const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane, }: any) => { - const state: Types.ThreeState = useThree(); - const { raycaster, controls }: any = state; - const { renderDistance } = useRenderDistance(); - const { toggleView } = useToggleView(); - const { floorItems, setFloorItems } = useFloorItems(); - const { camMode } = useCamMode(); - const { deleteTool } = useDeleteTool(); - const { setDeletableFloorItem } = useDeletableFloorItem(); - const { transformMode } = useTransformMode(); - const { setSelectedFloorItem } = useSelectedFloorItem(); - const { activeTool } = useActiveTool(); - const { selectedItem, setSelectedItem } = useSelectedItem(); - const { setLoadingProgress } = useLoadingProgress(); - const { activeModule } = useModuleStore(); - const { socket } = useSocketStore(); - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - const { addEvent } = useEventsStore(); +const FloorItemsGroup = ({ + itemsGroup, + hoveredDeletableFloorItem, + AttachedObject, + floorGroup, + tempLoader, + isTempLoader, + plane, +}: any) => { + const state: Types.ThreeState = useThree(); + const { raycaster, controls }: any = state; + const { renderDistance } = useRenderDistance(); + const { toggleView } = useToggleView(); + const { floorItems, setFloorItems } = useFloorItems(); + const { camMode } = useCamMode(); + const { deleteTool } = useDeleteTool(); + const { setDeletableFloorItem } = useDeletableFloorItem(); + const { transformMode } = useTransformMode(); + const { setSelectedFloorItem } = useSelectedFloorItem(); + const { activeTool } = useActiveTool(); + const { selectedItem, setSelectedItem } = useSelectedItem(); + const { setLoadingProgress } = useLoadingProgress(); + const { activeModule } = useModuleStore(); + const { socket } = useSocketStore(); + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); + const { addEvent } = useEventsStore(); - dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"); - loader.setDRACOLoader(dracoLoader); + dracoLoader.setDecoderPath( + "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" + ); + loader.setDRACOLoader(dracoLoader); - useEffect(() => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + useEffect(() => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - let totalAssets = 0; - let loadedAssets = 0; + let totalAssets = 0; + let loadedAssets = 0; - const updateLoadingProgress = (progress: number) => { - if (progress < 100) { - setLoadingProgress(progress); - } else if (progress === 100) { - setTimeout(() => { - setLoadingProgress(100); - setTimeout(() => { - setLoadingProgress(0); - }, 1500); - }, 1000); - } - }; + const updateLoadingProgress = (progress: number) => { + if (progress < 100) { + setLoadingProgress(progress); + } else if (progress === 100) { + setTimeout(() => { + setLoadingProgress(100); + setTimeout(() => { + setLoadingProgress(0); + }, 1500); + }, 1000); + } + }; - getFloorAssets(organization).then((data) => { - if (data.length > 0) { - const uniqueItems = (data as Types.FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.modelfileID === item.modelfileID)); - totalAssets = uniqueItems.length; - if (totalAssets === 0) { - updateLoadingProgress(100); - return; - } - gltfLoaderWorker.postMessage({ floorItems: data }); - } else { - gltfLoaderWorker.postMessage({ floorItems: [] }); - loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance); - updateLoadingProgress(100); - } + getFloorAssets(organization).then((data) => { + if (data.length > 0) { + const uniqueItems = (data as Types.FloorItems).filter( + (item, index, self) => + index === self.findIndex((t) => t.modelfileID === item.modelfileID) + ); + totalAssets = uniqueItems.length; + if (totalAssets === 0) { + updateLoadingProgress(100); + return; + } + gltfLoaderWorker.postMessage({ floorItems: data }); + } else { + gltfLoaderWorker.postMessage({ floorItems: [] }); + loadInitialFloorItems( + itemsGroup, + setFloorItems, + addEvent, + renderDistance + ); + updateLoadingProgress(100); + } + }); + + gltfLoaderWorker.onmessage = async (event) => { + if (event.data.message === "gltfLoaded" && event.data.modelBlob) { + const blobUrl = URL.createObjectURL(event.data.modelBlob); + + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(event.data.modelID, gltf); + + loadedAssets++; + const progress = Math.round((loadedAssets / totalAssets) * 100); + updateLoadingProgress(progress); + + if (loadedAssets === totalAssets) { + loadInitialFloorItems( + itemsGroup, + setFloorItems, + addEvent, + renderDistance + ); + updateLoadingProgress(100); + } }); + } + }; + }, []); - gltfLoaderWorker.onmessage = async (event) => { - if (event.data.message === "gltfLoaded" && event.data.modelBlob) { - const blobUrl = URL.createObjectURL(event.data.modelBlob); + useEffect(() => { + assetManagerWorker.onmessage = async (event) => { + cancelOngoingTasks(); // Cancel the ongoing process + await assetManager(event.data, itemsGroup, loader); + }; + }, [assetManagerWorker]); - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(event.data.modelID, gltf); + useEffect(() => { + if (toggleView) return; - loadedAssets++; - const progress = Math.round((loadedAssets / totalAssets) * 100); - updateLoadingProgress(progress); + const uuids: string[] = []; + itemsGroup.current?.children.forEach((child: any) => { + uuids.push(child.uuid); + }); + const cameraPosition = state.camera.position; - if (loadedAssets === totalAssets) { - loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance); - updateLoadingProgress(100); - } - }); - } - }; - }, []); + assetManagerWorker.postMessage({ + floorItems, + cameraPosition, + uuids, + renderDistance, + }); + }, [camMode, renderDistance]); - useEffect(() => { - assetManagerWorker.onmessage = async (event) => { - cancelOngoingTasks(); // Cancel the ongoing process - await assetManager(event.data, itemsGroup, loader); - }; - }, [assetManagerWorker]); + useEffect(() => { + const controls: any = state.controls; + const camera: any = state.camera; - useEffect(() => { + if (controls) { + let intervalId: NodeJS.Timeout | null = null; + + const handleChange = () => { if (toggleView) return; const uuids: string[] = []; - itemsGroup.current?.children.forEach((child: any) => { uuids.push(child.uuid); }); - const cameraPosition = state.camera.position; + itemsGroup.current?.children.forEach((child: any) => { + uuids.push(child.uuid); + }); + const cameraPosition = camera.position; - assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance, }); - }, [camMode, renderDistance]); + assetManagerWorker.postMessage({ + floorItems, + cameraPosition, + uuids, + renderDistance, + }); + }; - useEffect(() => { - const controls: any = state.controls; - const camera: any = state.camera; - - if (controls) { - let intervalId: NodeJS.Timeout | null = null; - - const handleChange = () => { - if (toggleView) return; - - const uuids: string[] = []; - itemsGroup.current?.children.forEach((child: any) => { uuids.push(child.uuid); }); - const cameraPosition = camera.position; - - assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance, }); - }; - - const startInterval = () => { - if (!intervalId) { - intervalId = setInterval(handleChange, 50); - } - }; - - const stopInterval = () => { - handleChange(); - if (intervalId) { - clearInterval(intervalId); - intervalId = null; - } - }; - - controls.addEventListener("rest", handleChange); - controls.addEventListener("rest", stopInterval); - controls.addEventListener("control", startInterval); - controls.addEventListener("controlend", stopInterval); - - return () => { - controls.removeEventListener("rest", handleChange); - controls.removeEventListener("rest", stopInterval); - controls.removeEventListener("control", startInterval); - controls.removeEventListener("controlend", stopInterval); - if (intervalId) { - clearInterval(intervalId); - } - }; + const startInterval = () => { + if (!intervalId) { + intervalId = setInterval(handleChange, 50); } - }, [state.controls, floorItems, toggleView, renderDistance]); + }; - useEffect(() => { - const canvasElement = state.gl.domElement; - let drag = false; - let isLeftMouseDown = false; - - const onMouseDown = (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = true; - drag = false; - } - }; - - const onMouseMove = () => { - if (isLeftMouseDown) { - drag = true; - } - }; - - const onMouseUp = async (evt: any) => { - if (controls) { - (controls as any).enabled = true; - } - if (evt.button === 0) { - isLeftMouseDown = false; - if (drag) return; - - if (deleteTool) { - DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, socket); - } - const Mode = transformMode; - - if (Mode !== null || activeTool === "cursor") { - if (!itemsGroup.current) return; - let intersects = raycaster.intersectObjects( - itemsGroup.current.children, - true - ); - if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { - // let currentObject = intersects[0].object; - // while (currentObject) { - // if (currentObject.name === "Scene") { - // break; - // } - // currentObject = currentObject.parent as THREE.Object3D; - // } - // if (currentObject) { - // AttachedObject.current = currentObject as any; - // setSelectedFloorItem(AttachedObject.current!); - // } - } else { - const target = controls.getTarget(new THREE.Vector3()); - await controls.setTarget(target.x, 0, target.z, true); - setSelectedFloorItem(null); - } - } - } - }; - - const onDblClick = async (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = false; - if (drag) return; - - const Mode = transformMode; - - if (Mode !== null || activeTool === "cursor") { - if (!itemsGroup.current) return; - let intersects = raycaster.intersectObjects(itemsGroup.current.children, true); - if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { - let currentObject = intersects[0].object; - - while (currentObject) { - if (currentObject.name === "Scene") { - break; - } - currentObject = currentObject.parent as THREE.Object3D; - } - if (currentObject) { - AttachedObject.current = currentObject as any; - // controls.fitToSphere(AttachedObject.current!, true); - - const bbox = new THREE.Box3().setFromObject(AttachedObject.current); - const size = bbox.getSize(new THREE.Vector3()); - const center = bbox.getCenter(new THREE.Vector3()); - - const front = new THREE.Vector3(0, 0, 1); - AttachedObject.current.localToWorld(front); - front.sub(AttachedObject.current.position).normalize(); - - const distance = Math.max(size.x, size.y, size.z) * 2; - const newPosition = center.clone().addScaledVector(front, distance); - - controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true); - controls.setTarget(center.x, center.y, center.z, true); - controls.fitToBox(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, }); - - setSelectedFloorItem(AttachedObject.current!); - } - } else { - const target = controls.getTarget(new THREE.Vector3()); - await controls.setTarget(target.x, 0, target.z, true); - setSelectedFloorItem(null); - } - } - } - }; - - const onDrop = (event: any) => { - if (!event.dataTransfer?.files[0]) return; - - if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { - addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, addEvent, plane); - } - }; - - const onDragOver = (event: any) => { - event.preventDefault(); - }; - - if (activeModule === "builder") { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("dblclick", onDblClick); - canvasElement.addEventListener("drop", onDrop); - canvasElement.addEventListener("dragover", onDragOver); - } else { - if (controls) { - const target = controls.getTarget(new THREE.Vector3()); - controls.setTarget(target.x, 0, target.z, true); - setSelectedFloorItem(null); - } + const stopInterval = () => { + handleChange(); + if (intervalId) { + clearInterval(intervalId); + intervalId = null; } + }; - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("dblclick", onDblClick); - canvasElement.removeEventListener("drop", onDrop); - canvasElement.removeEventListener("dragover", onDragOver); - }; - }, [deleteTool, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]); + controls.addEventListener("rest", handleChange); + controls.addEventListener("rest", stopInterval); + controls.addEventListener("control", startInterval); + controls.addEventListener("controlend", stopInterval); - useFrame(() => { - if (controls) - // assetVisibility(itemsGroup, state.camera.position, renderDistance); - if (deleteTool && activeModule === "builder") { - DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem); - } else if (!deleteTool) { - if (hoveredDeletableFloorItem.current) { - hoveredDeletableFloorItem.current = undefined; - setDeletableFloorItem(null); - } + return () => { + controls.removeEventListener("rest", handleChange); + controls.removeEventListener("rest", stopInterval); + controls.removeEventListener("control", startInterval); + controls.removeEventListener("controlend", stopInterval); + if (intervalId) { + clearInterval(intervalId); + } + }; + } + }, [state.controls, floorItems, toggleView, renderDistance]); + + useEffect(() => { + const canvasElement = state.gl.domElement; + let drag = false; + let isLeftMouseDown = false; + + const onMouseDown = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = true; + drag = false; + } + }; + + const onMouseMove = () => { + console.log('isLeftMouseDown: ', isLeftMouseDown); + if (isLeftMouseDown) { + drag = true; + } + }; + + const onMouseUp = async (evt: any) => { + if (controls) { + (controls as any).enabled = true; + } + if (evt.button === 0) { + isLeftMouseDown = false; + if (drag) return; + + if (deleteTool) { + DeleteFloorItems( + itemsGroup, + hoveredDeletableFloorItem, + setFloorItems, + socket + ); + } + const Mode = transformMode; + + if (Mode !== null || activeTool === "cursor") { + if (!itemsGroup.current) return; + let intersects = raycaster.intersectObjects( + itemsGroup.current.children, + true + ); + if ( + intersects.length > 0 && + intersects[0]?.object?.parent?.parent?.position && + intersects[0]?.object?.parent?.parent?.scale && + intersects[0]?.object?.parent?.parent?.rotation + ) { + // let currentObject = intersects[0].object; + // while (currentObject) { + // if (currentObject.name === "Scene") { + // break; + // } + // currentObject = currentObject.parent as THREE.Object3D; + // } + // if (currentObject) { + // AttachedObject.current = currentObject as any; + // setSelectedFloorItem(AttachedObject.current!); + // } + } else { + const target = controls.getTarget(new THREE.Vector3()); + await controls.setTarget(target.x, 0, target.z, true); + setSelectedFloorItem(null); + } + } + } + }; + + const onDblClick = async (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = false; + if (drag) return; + + const Mode = transformMode; + + if (Mode !== null || activeTool === "cursor") { + if (!itemsGroup.current) return; + let intersects = raycaster.intersectObjects( + itemsGroup.current.children, + true + ); + if ( + intersects.length > 0 && + intersects[0]?.object?.parent?.parent?.position && + intersects[0]?.object?.parent?.parent?.scale && + intersects[0]?.object?.parent?.parent?.rotation + ) { + let currentObject = intersects[0].object; + + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as THREE.Object3D; } - }); + if (currentObject) { + AttachedObject.current = currentObject as any; + // controls.fitToSphere(AttachedObject.current!, true); - return ; + const bbox = new THREE.Box3().setFromObject( + AttachedObject.current + ); + const size = bbox.getSize(new THREE.Vector3()); + const center = bbox.getCenter(new THREE.Vector3()); + + const front = new THREE.Vector3(0, 0, 1); + AttachedObject.current.localToWorld(front); + front.sub(AttachedObject.current.position).normalize(); + + const distance = Math.max(size.x, size.y, size.z) * 2; + const newPosition = center + .clone() + .addScaledVector(front, distance); + + controls.setPosition( + newPosition.x, + newPosition.y, + newPosition.z, + true + ); + controls.setTarget(center.x, center.y, center.z, true); + controls.fitToBox(AttachedObject.current!, true, { + cover: true, + paddingTop: 5, + paddingLeft: 5, + paddingBottom: 5, + paddingRight: 5, + }); + + setSelectedFloorItem(AttachedObject.current!); + } + } else { + const target = controls.getTarget(new THREE.Vector3()); + await controls.setTarget(target.x, 0, target.z, true); + setSelectedFloorItem(null); + } + } + } + }; + + const onDrop = (event: any) => { + if (!event.dataTransfer?.files[0]) return; + + if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { + addAssetModel( + raycaster, + state.camera, + state.pointer, + floorGroup, + setFloorItems, + itemsGroup, + isTempLoader, + tempLoader, + socket, + selectedItem, + setSelectedItem, + addEvent, + plane + ); + } + }; + + const onDragOver = (event: any) => { + event.preventDefault(); + }; + + if (activeModule === "builder") { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener("dblclick", onDblClick); + canvasElement.addEventListener("drop", onDrop); + canvasElement.addEventListener("dragover", onDragOver); + } else { + if (controls) { + const target = controls.getTarget(new THREE.Vector3()); + controls.setTarget(target.x, 0, target.z, true); + setSelectedFloorItem(null); + } + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("dblclick", onDblClick); + canvasElement.removeEventListener("drop", onDrop); + canvasElement.removeEventListener("dragover", onDragOver); + }; + }, [ + deleteTool, + transformMode, + controls, + selectedItem, + state.camera, + state.pointer, + activeTool, + activeModule, + ]); + + useFrame(() => { + if (controls) + if (deleteTool && activeModule === "builder") { + // assetVisibility(itemsGroup, state.camera.position, renderDistance); + DeletableHoveredFloorItems( + state, + itemsGroup, + hoveredDeletableFloorItem, + setDeletableFloorItem + ); + } else if (!deleteTool) { + if (hoveredDeletableFloorItem.current) { + hoveredDeletableFloorItem.current = undefined; + setDeletableFloorItem(null); + } + } + }); + + return ; }; export default FloorItemsGroup; diff --git a/app/src/modules/scene/environment/ground.tsx b/app/src/modules/scene/environment/ground.tsx index 8d2fa22..ef3e690 100644 --- a/app/src/modules/scene/environment/ground.tsx +++ b/app/src/modules/scene/environment/ground.tsx @@ -3,7 +3,6 @@ import * as CONSTANTS from "../../../types/world/worldConstants"; const Ground = ({ grid, plane }: any) => { const { toggleView } = useToggleView(); - const savedTheme: string | null = localStorage.getItem("theme"); const { planeValue, gridValue } = useTileDistance(); return ( diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index af6767a..29a6db7 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -1,255 +1,276 @@ import React, { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; -import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore"; +import useModuleStore, { + useSubModuleStore, +} from "../../../../../store/useModuleStore"; import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { - useSelectedEventSphere, - useSelectedEventData, + useSelectedEventSphere, + useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore"; import { useThree } from "@react-three/fiber"; function PointsCreator() { - const { gl, raycaster, scene, pointer, camera } = useThree(); - const { subModule } = useSubModuleStore(); - const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore(); - const { activeModule } = useModuleStore(); - const transformRef = useRef(null); - const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); - const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); - const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere(); - const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); + const { gl, raycaster, scene, pointer, camera } = useThree(); + const { subModule } = useSubModuleStore(); + const { events, updatePoint, getPointByUuid, getEventByModelUuid } = + useEventsStore(); + const { activeModule } = useModuleStore(); + const transformRef = useRef(null); + const [transformMode, setTransformMode] = useState< + "translate" | "rotate" | null + >(null); + const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); + const { + selectedEventSphere, + setSelectedEventSphere, + clearSelectedEventSphere, + } = useSelectedEventSphere(); + const { setSelectedEventData, clearSelectedEventData } = + useSelectedEventData(); - useEffect(() => { - if (selectedEventSphere) { - const eventData = getEventByModelUuid( - selectedEventSphere.userData.modelUuid - ); + useEffect(() => { + if (selectedEventSphere) { + const eventData = getEventByModelUuid( + selectedEventSphere.userData.modelUuid + ); - if (eventData) { - setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); - } else { - clearSelectedEventData(); - } - } else { - clearSelectedEventData(); - } - }, [selectedEventSphere]); + if (eventData) { + setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); + } else { + clearSelectedEventData(); + } + } else { + clearSelectedEventData(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedEventSphere]); - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - const keyCombination = detectModifierKeys(e); - if (!selectedEventSphere) return; - if (keyCombination === "G") { - setTransformMode((prev) => (prev === "translate" ? null : "translate")); - } - if (keyCombination === "R") { - setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); - } - }; - - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [selectedEventSphere]); - - const updatePointToState = (selectedEventSphere: THREE.Mesh) => { - let point = JSON.parse( - JSON.stringify(getPointByUuid(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid)) - ); - if (point) { - point.position = [selectedEventSphere.position.x, selectedEventSphere.position.y, selectedEventSphere.position.z,]; - updatePoint(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid, point); - } + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + const keyCombination = detectModifierKeys(e); + if (!selectedEventSphere) return; + if (keyCombination === "G") { + setTransformMode((prev) => (prev === "translate" ? null : "translate")); + } + if (keyCombination === "R") { + setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); + } }; - useEffect(() => { - const canvasElement = gl.domElement; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [selectedEventSphere]); - let drag = false; - let isMouseDown = false; - - const onMouseDown = () => { - isMouseDown = true; - drag = false; - }; - - const onMouseUp = () => { - if (selectedEventSphere && !drag) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster - .intersectObjects(scene.children, true) - .filter( - (intersect) => - intersect.object.name === ('Event-Sphere') - ); - if (intersects.length === 0) { - clearSelectedEventSphere(); - setTransformMode(null); - } - } - } - - const onMouseMove = () => { - if (isMouseDown) { - drag = true; - } - }; - - if (subModule === 'mechanics') { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - }; - - }, [gl, subModule, selectedEventSphere]); - - return ( - <> - {activeModule === "simulation" && ( - <> - - {events.map((event, i) => { - if (event.type === "transfer") { - return ( - - {event.points.map((point, j) => ( - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[point.uuid] - ); - }} - key={`${i}-${j}`} - position={new THREE.Vector3(...point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: point.uuid, - }} - > - - - - ))} - - ); - } else if (event.type === "vehicle") { - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[event.point.uuid] - ); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, - }} - > - - - - - ); - } else if (event.type === "roboticArm") { - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[event.point.uuid] - ); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, - }} - > - - - - - ); - } else if (event.type === "machine") { - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[event.point.uuid] - ); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, - }} - > - - - - - ); - } else { - return null; - } - })} - - {selectedEventSphere && transformMode && ( - { - updatePointToState(selectedEventSphere); - }} - /> - )} - - )} - + const updatePointToState = (selectedEventSphere: THREE.Mesh) => { + let point = JSON.parse( + JSON.stringify( + getPointByUuid( + selectedEventSphere.userData.modelUuid, + selectedEventSphere.userData.pointUuid + ) + ) ); + if (point) { + point.position = [ + selectedEventSphere.position.x, + selectedEventSphere.position.y, + selectedEventSphere.position.z, + ]; + updatePoint( + selectedEventSphere.userData.modelUuid, + selectedEventSphere.userData.pointUuid, + point + ); + } + }; + + useEffect(() => { + const canvasElement = gl.domElement; + + let drag = false; + let isMouseDown = false; + + const onMouseDown = () => { + isMouseDown = true; + drag = false; + }; + + const onMouseUp = () => { + if (selectedEventSphere && !drag) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter((intersect) => intersect.object.name === "Event-Sphere"); + if (intersects.length === 0) { + clearSelectedEventSphere(); + setTransformMode(null); + } + } + }; + + const onMouseMove = () => { + if (isMouseDown) { + drag = true; + } + }; + + if (subModule === "mechanics") { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [gl, subModule, selectedEventSphere]); + + return ( + <> + {activeModule === "simulation" && ( + <> + + {events.map((event, index) => { + if (event.type === "transfer") { + return ( + + {event.points.map((point, j) => ( + (sphereRefs.current[point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[point.uuid] + ); + }} + key={`${index}-${point.uuid}`} + position={new THREE.Vector3(...point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: point.uuid, + }} + > + + + + ))} + + ); + } else if (event.type === "vehicle") { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[event.point.uuid] + ); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} + > + + + + + ); + } else if (event.type === "roboticArm") { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[event.point.uuid] + ); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} + > + + + + + ); + } else if (event.type === "machine") { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[event.point.uuid] + ); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} + > + + + + + ); + } else { + return null; + } + })} + + {selectedEventSphere && transformMode && ( + { + updatePointToState(selectedEventSphere); + }} + /> + )} + + )} + + ); } -export default PointsCreator; \ No newline at end of file +export default PointsCreator; diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index 74cdd92..06d50a1 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -1,220 +1,261 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { useFrame } from '@react-three/fiber'; -import * as THREE from 'three'; -import { Line } from '@react-three/drei'; +import React, { useEffect, useRef, useState } from "react"; +import { useFrame } from "@react-three/fiber"; +import * as THREE from "three"; +import { Line } from "@react-three/drei"; import { - useAnimationPlaySpeed, - usePauseButtonStore, - usePlayButtonStore, - useResetButtonStore -} from '../../../../../store/usePlayButtonStore'; + useAnimationPlaySpeed, + usePauseButtonStore, + usePlayButtonStore, + useResetButtonStore, +} from "../../../../../store/usePlayButtonStore"; function RoboticArmAnimator({ - HandleCallback, - restPosition, - ikSolver, - targetBone, - armBot, - logStatus, - path + HandleCallback, + restPosition, + ikSolver, + targetBone, + armBot, + logStatus, + path, }: any) { - const progressRef = useRef(0); - const curveRef = useRef(null); - const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); - const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); - const [customCurvePoints, setCustomCurvePoints] = useState(null); + const progressRef = useRef(0); + const curveRef = useRef(null); + const [currentPath, setCurrentPath] = useState<[number, number, number][]>( + [] + ); + const [circlePoints, setCirclePoints] = useState<[number, number, number][]>( + [] + ); + const [customCurvePoints, setCustomCurvePoints] = useState< + THREE.Vector3[] | null + >(null); - // Zustand stores - const { isPlaying } = usePlayButtonStore(); - const { isPaused } = usePauseButtonStore(); - const { isReset } = useResetButtonStore(); - const { speed } = useAnimationPlaySpeed(); + // Zustand stores + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); + const { isReset } = useResetButtonStore(); + const { speed } = useAnimationPlaySpeed(); - // Update path state whenever `path` prop changes - useEffect(() => { - setCurrentPath(path); - }, [path]); + // Update path state whenever `path` prop changes + useEffect(() => { + setCurrentPath(path); + }, [path]); - // Reset logic when `isPlaying` changes - useEffect(() => { - if (!isPlaying) { - setCurrentPath([]); - curveRef.current = null; - } - }, [isPlaying]); - - // Handle circle points based on armBot position - useEffect(() => { - const points = generateRingPoints(1.6, 64) - setCirclePoints(points); - }, [armBot.position]); - - - function generateRingPoints(radius: any, segments: any) { - const points: [number, number, number][] = []; - for (let i = 0; i < segments; i++) { - // Calculate angle for current segment - const angle = (i / segments) * Math.PI * 2; - // Calculate x and z coordinates (y remains the same for a flat ring) - const x = Math.cos(angle) * radius; - const z = Math.sin(angle) * radius; - points.push([x, 1.5, z]); - } - return points; + // Reset logic when `isPlaying` changes + useEffect(() => { + if (!isPlaying) { + setCurrentPath([]); + curveRef.current = null; } + }, [isPlaying]); - const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => { - for (let i = 0; i < points.length; i++) { - const [x, y, z] = points[i]; - if ( - Math.abs(x - nearestPoint[0]) < epsilon && - Math.abs(y - nearestPoint[1]) < epsilon && - Math.abs(z - nearestPoint[2]) < epsilon - ) { - return i; // Found the matching index - } + // Handle circle points based on armBot position + useEffect(() => { + const points = generateRingPoints(1.6, 64); + setCirclePoints(points); + }, [armBot.position]); + + function generateRingPoints(radius: any, segments: any) { + const points: [number, number, number][] = []; + for (let i = 0; i < segments; i++) { + // Calculate angle for current segment + const angle = (i / segments) * Math.PI * 2; + // Calculate x and z coordinates (y remains the same for a flat ring) + const x = Math.cos(angle) * radius; + const z = Math.sin(angle) * radius; + points.push([x, 1.5, z]); + } + return points; + } + + const findNearestIndex = ( + nearestPoint: [number, number, number], + points: [number, number, number][], + epsilon = 1e-6 + ) => { + for (let i = 0; i < points.length; i++) { + const [x, y, z] = points[i]; + if ( + Math.abs(x - nearestPoint[0]) < epsilon && + Math.abs(y - nearestPoint[1]) < epsilon && + Math.abs(z - nearestPoint[2]) < epsilon + ) { + return i; // Found the matching index + } + } + return -1; // Not found + }; + + // Handle nearest points and final path (including arc points) + useEffect(() => { + if (circlePoints.length > 0 && currentPath.length > 0) { + const start = currentPath[0]; + const end = currentPath[currentPath.length - 1]; + + const raisedStart = [start[0], start[1] + 0.5, start[2]] as [ + number, + number, + number + ]; + const raisedEnd = [end[0], end[1] + 0.5, end[2]] as [ + number, + number, + number + ]; + + const findNearest = (target: [number, number, number]) => { + return circlePoints.reduce((nearest, point) => { + const distance = Math.hypot( + target[0] - point[0], + target[1] - point[1], + target[2] - point[2] + ); + const nearestDistance = Math.hypot( + target[0] - nearest[0], + target[1] - nearest[1], + target[2] - nearest[2] + ); + return distance < nearestDistance ? point : nearest; + }, circlePoints[0]); + }; + + const nearestToStart = findNearest(raisedStart); + + const nearestToEnd = findNearest(raisedEnd); + + const indexOfNearestStart = findNearestIndex( + nearestToStart, + circlePoints + ); + + const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints); + + // Find clockwise and counter-clockwise distances + const clockwiseDistance = + (indexOfNearestEnd - indexOfNearestStart + 64) % 64; + + const counterClockwiseDistance = + (indexOfNearestStart - indexOfNearestEnd + 64) % 64; + + const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance; + + // Collect arc points between start and end + let arcPoints: [number, number, number][] = []; + + if (clockwiseIsShorter) { + if (indexOfNearestStart <= indexOfNearestEnd) { + arcPoints = circlePoints.slice( + indexOfNearestStart, + indexOfNearestEnd + 1 + ); + } else { + // Wrap around + arcPoints = [ + ...circlePoints.slice(indexOfNearestStart, 64), + ...circlePoints.slice(0, indexOfNearestEnd + 1), + ]; } - return -1; // Not found - }; - - - // Handle nearest points and final path (including arc points) - useEffect(() => { - if (circlePoints.length > 0 && currentPath.length > 0) { - const start = currentPath[0]; - const end = currentPath[currentPath.length - 1]; - - const raisedStart = [start[0], start[1] + 0.5, start[2]] as [number, number, number]; - const raisedEnd = [end[0], end[1] + 0.5, end[2]] as [number, number, number]; - - const findNearest = (target: [number, number, number]) => { - return circlePoints.reduce((nearest, point) => { - const distance = Math.hypot( - target[0] - point[0], - target[1] - point[1], - target[2] - point[2] - ); - const nearestDistance = Math.hypot( - target[0] - nearest[0], - target[1] - nearest[1], - target[2] - nearest[2] - ); - return distance < nearestDistance ? point : nearest; - }, circlePoints[0]); - }; - - const nearestToStart = findNearest(raisedStart); - - const nearestToEnd = findNearest(raisedEnd); - - const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints); - - const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints); - - // Find clockwise and counter-clockwise distances - const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64; - - const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64; - - const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance; - - // Collect arc points between start and end - let arcPoints: [number, number, number][] = []; - - if (clockwiseIsShorter) { - if (indexOfNearestStart <= indexOfNearestEnd) { - arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1); - } else { - // Wrap around - arcPoints = [ - ...circlePoints.slice(indexOfNearestStart, 64), - ...circlePoints.slice(0, indexOfNearestEnd + 1) - ]; - } - } else { - if (indexOfNearestStart >= indexOfNearestEnd) { - for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) { - arcPoints.push(circlePoints[i]); - } - } else { - for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) { - arcPoints.push(circlePoints[i]); - } - } - } - - // Continue your custom path logic - const pathVectors = [ - new THREE.Vector3(start[0], start[1], start[2]), // start - new THREE.Vector3(raisedStart[0], raisedStart[1], raisedStart[2]), // lift up - new THREE.Vector3(nearestToStart[0], raisedStart[1], nearestToStart[2]), // move to arc start - ...arcPoints.map(point => new THREE.Vector3(point[0], raisedStart[1], point[2])), - new THREE.Vector3(nearestToEnd[0], raisedEnd[1], nearestToEnd[2]), // move from arc end - new THREE.Vector3(raisedEnd[0], raisedEnd[1], raisedEnd[2]), // lowered end - new THREE.Vector3(end[0], end[1], end[2]) // end - ]; - - const customCurve = new THREE.CatmullRomCurve3(pathVectors, false, 'centripetal', 1); - const generatedPoints = customCurve.getPoints(100); - setCustomCurvePoints(generatedPoints); + } else if (indexOfNearestStart >= indexOfNearestEnd) { + for ( + let i = indexOfNearestStart; + i !== (indexOfNearestEnd - 1 + 64) % 64; + i = (i - 1 + 64) % 64 + ) { + arcPoints.push(circlePoints[i]); } - }, [circlePoints, currentPath]); + } - // Frame update for animation - useFrame((_, delta) => { - if (!ikSolver) return; + // Continue your custom path logic + const pathVectors = [ + new THREE.Vector3(start[0], start[1], start[2]), // start + new THREE.Vector3(raisedStart[0], raisedStart[1], raisedStart[2]), // lift up + new THREE.Vector3(nearestToStart[0], raisedStart[1], nearestToStart[2]), // move to arc start + ...arcPoints.map( + (point) => new THREE.Vector3(point[0], raisedStart[1], point[2]) + ), + new THREE.Vector3(nearestToEnd[0], raisedEnd[1], nearestToEnd[2]), // move from arc end + new THREE.Vector3(raisedEnd[0], raisedEnd[1], raisedEnd[2]), // lowered end + new THREE.Vector3(end[0], end[1], end[2]), // end + ]; - const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); - if (!bone) return; + const customCurve = new THREE.CatmullRomCurve3( + pathVectors, + false, + "centripetal", + 1 + ); + const generatedPoints = customCurve.getPoints(100); + setCustomCurvePoints(generatedPoints); + } + }, [circlePoints, currentPath]); - if (isPlaying) { - if (!isPaused && customCurvePoints && currentPath.length > 0) { - const curvePoints = customCurvePoints; - const speedAdjustedProgress = progressRef.current + (speed * armBot.speed); - const index = Math.floor(speedAdjustedProgress); + // Frame update for animation + useFrame((_, delta) => { + if (!ikSolver) return; - if (index >= curvePoints.length) { - // Reached the end of the curve - HandleCallback(); - setCurrentPath([]); - curveRef.current = null; - progressRef.current = 0; - } else { - const point = curvePoints[index]; - bone.position.copy(point); - progressRef.current = speedAdjustedProgress; - } - } else if (isPaused) { - logStatus(armBot.modelUuid, 'Simulation Paused'); - } - - ikSolver.update(); - } else if (!isPlaying && currentPath.length === 0) { - // Not playing anymore, reset to rest - bone.position.copy(restPosition); - ikSolver.update(); - } - }); - - - return ( - <> - {/* {customCurvePoints && currentPath && isPlaying && ( - - [p.x, p.y, p.z] as [number, number, number])} - color="green" - lineWidth={5} - dashed={false} - /> - - )} */} - {/* - - - */} - + const bone = ikSolver.mesh.skeleton.bones.find( + (b: any) => b.name === targetBone ); + if (!bone) return; + + if (isPlaying) { + if (!isPaused && customCurvePoints && currentPath.length > 0) { + const curvePoints = customCurvePoints; + const speedAdjustedProgress = + progressRef.current + speed * armBot.speed; + const index = Math.floor(speedAdjustedProgress); + + if (index >= curvePoints.length) { + // Reached the end of the curve + HandleCallback(); + setCurrentPath([]); + curveRef.current = null; + progressRef.current = 0; + } else { + const point = curvePoints[index]; + bone.position.copy(point); + progressRef.current = speedAdjustedProgress; + } + } else if (isPaused) { + logStatus(armBot.modelUuid, "Simulation Paused"); + } + + ikSolver.update(); + } else if (!isPlaying && currentPath.length === 0) { + // Not playing anymore, reset to rest + bone.position.copy(restPosition); + ikSolver.update(); + } + }); + + return ( + <> + {customCurvePoints && currentPath && isPlaying && ( + + [p.x, p.y, p.z] as [number, number, number] + )} + color="green" + lineWidth={5} + dashed={false} + /> + + )} + + + + + + ); } export default RoboticArmAnimator; diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx index 3070c3a..4a8947b 100644 --- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -67,13 +67,12 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns setIkSolver(solver); const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05) - // groupRef.current.add(helper); setSelectedArm(OOI.Target_Bone); // scene.add(helper); - }, [gltf]); + }, [cloned, gltf, setIkSolver]); return ( <> diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 17a6315..4fe26b3 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -58,7 +58,7 @@ const Project: React.FC = () => { setOrganization(Organization); setUserName(name); } - echo.info("Log in success full"); + echo.warn("Log in success full"); } else { navigate("/"); } diff --git a/app/src/styles/abstracts/variables.scss b/app/src/styles/abstracts/variables.scss index 29976dc..3545151 100644 --- a/app/src/styles/abstracts/variables.scss +++ b/app/src/styles/abstracts/variables.scss @@ -115,17 +115,23 @@ $color5: #c7a8ff; // log indication colors // ------------ text ------------- $log-default-text-color: #6f42c1; -$log-info-text-color: #488ef6; +$log-info-text-color: #1773fd; $log-warn-text-color: #f3a50c; -$log-error-text-color: #f65648; -$log-success-text-color: #43c06d; +$log-error-text-color: #fc230f; +$log-success-text-color: #23a84f; +// ----------- dark --------------- +$log-default-text-color-dark: #b18ef1; +$log-info-text-color-dark: #7eb0fa; +$log-warn-text-color-dark: #ffaa00; +$log-error-text-color-dark: #ff887d; +$log-success-text-color-dark: #43ff81; // ------------ background ------------- $log-default-backgroung-color: #6e42c133; -$log-info-background-color: #488ef633; +$log-info-background-color: #1773fd5d; $log-warn-background-color: #f3a50c33; -$log-error-background-color: #f6564833; -$log-success-background-color: #43c06d33; +$log-error-background-color: #fc230f33; +$log-success-background-color: #0ef75b33; // old variables $accent-color: #6f42c1; diff --git a/app/src/styles/base/base.scss b/app/src/styles/base/base.scss index 4555ed9..8843bfe 100644 --- a/app/src/styles/base/base.scss +++ b/app/src/styles/base/base.scss @@ -35,6 +35,18 @@ --icon-default-color: #{$icon-default-color}; --icon-default-color-active: #{$icon-default-color-active}; + // log colors + --default-text-color: #{$log-default-text-color}; + --log-info-text-color: #{$log-info-text-color}; + --log-warn-text-color: #{$log-warn-text-color}; + --log-error-text-color: #{$log-error-text-color}; + --log-success-text-color: #{$log-success-text-color}; + --log-default-background-color: #{$log-default-backgroung-color}; + --log-info-background-color: #{$log-info-background-color}; + --log-warn-background-color: #{$log-warn-background-color}; + --log-error-background-color: #{$log-error-background-color}; + --log-success-background-color: #{$log-success-background-color}; + // old colors --accent-color: #{$accent-color}; --accent-gradient-color: #{$acent-gradient}; @@ -85,6 +97,18 @@ --icon-default-color: #{$icon-default-color-dark}; --icon-default-color-active: #{$icon-default-color-active-dark}; + // log colors + --default-text-color: #{$log-default-text-color-dark}; + --log-info-text-color: #{$log-info-text-color-dark}; + --log-warn-text-color: #{$log-warn-text-color-dark}; + --log-error-text-color: #{$log-error-text-color-dark}; + --log-success-text-color: #{$log-success-text-color-dark}; + --log-default-background-color: #{$log-default-backgroung-color}; + --log-info-background-color: #{$log-info-background-color}; + --log-warn-background-color: #{$log-warn-background-color}; + --log-error-background-color: #{$log-error-background-color}; + --log-success-background-color: #{$log-success-background-color}; + // old colors --accent-color: #{$accent-color-dark}; --accent-gradient-color: #{$acent-gradient-dark}; @@ -115,7 +139,6 @@ } body { - background: var(--background-color); --font-size-tiny: #{$tiny}; --font-size-small: #{$small}; --font-size-regular: #{$regular}; diff --git a/app/src/styles/components/footer/footer.scss b/app/src/styles/components/footer/footer.scss index b2d85d0..a457557 100644 --- a/app/src/styles/components/footer/footer.scss +++ b/app/src/styles/components/footer/footer.scss @@ -23,6 +23,7 @@ padding: 3px 6px; border-radius: 12px; color: var(--text-color); + backdrop-filter: blur(14px); .selector { color: var(--text-color); @@ -33,24 +34,55 @@ .logs-wrapper { display: flex; gap: 6px; + position: relative; + // dummy + .bg-dummy{ + background: var(--background-color-solid); + position: absolute; + z-index: -1; + } + .bg-dummy.left-top{ + top: 1px; + left: 4px; + width: 60%; + height: 16px; + border-radius: 20px; + } + .bg-dummy.right-bottom{ + right: 68px; + bottom: 0; + width: 20%; + height: 100%; + border-radius: 50%; + } + + .log-container { + background: var(--background-color); + backdrop-filter: blur(20px); + border-radius: 12px; + @include flex-center; + overflow: hidden; + } .logs-detail, .version { + @include flex-center; border-radius: 12px; - background: var(--background-color); padding: 3px 6px; + height: 100%; color: var(--text-color); - display: flex; - align-items: center; gap: 6px; } .logs-detail { padding: 2px 12px; cursor: pointer; + outline: 0 solid var(--border-color); + outline-offset: -1px; .log-icon { @include flex-center; } + .log-message { max-width: 40vw; white-space: nowrap; @@ -60,12 +92,51 @@ } .version { + background: var(--background-color); font-size: var(--font-size-tiny); - display: flex; - gap: 6px; - .icon{ + + .icon { @include flex-center; } } + + .log { + background: var(--log-default-background-color); + outline-color: var(--default-text-color); + .log-message { + color: var(--default-text-color); + } + } + + .info { + background: var(--log-info-background-color); + outline-color: var(--log-info-text-color); + .log-message { + color: var(--log-info-text-color); + } + } + + .error { + background: var(--log-error-background-color); + outline-color: var(--log-error-text-color); + .log-message { + color: var(--log-error-text-color); + } + } + + .warning { + background: var(--log-warn-background-color); + outline-color: var(--log-warn-text-color); + .log-message { + color: var(--log-warn-text-color); + } + } + + .success { + background: var(--log-success-background-color); + .log-message { + color: var(--log-success-text-color); + } + } } } diff --git a/app/src/styles/components/form.scss b/app/src/styles/components/form.scss index e69de29..67accd2 100644 --- a/app/src/styles/components/form.scss +++ b/app/src/styles/components/form.scss @@ -0,0 +1,26 @@ +@use "../abstracts/variables" as *; +@use "../abstracts/mixins" as *; + +.rename-tool-tip { + position: absolute; + background: var(--background-color); + padding: 10px 16px; + width: 260px; + border-radius: #{$border-radius-large}; + outline: 1px solid var(--border-color); + z-index: 100; + .header { + @include flex-center; + gap: 8px; + .icon { + @include flex-center; + } + .name { + color: var(--text-color); + } + input { + width: 100%; + margin-top: 6px; + } + } +} diff --git a/app/src/styles/components/lists.scss b/app/src/styles/components/lists.scss index 894eac1..27ccda8 100644 --- a/app/src/styles/components/lists.scss +++ b/app/src/styles/components/lists.scss @@ -2,8 +2,6 @@ @use "../abstracts/mixins" as *; .dropdown-list-container { - border-bottom: 1px solid var(--border-color); - .lists-container { margin-bottom: 6px; } @@ -44,7 +42,7 @@ text-align: center; padding: 4px 8px; border-radius: #{$border-radius-large}; - .zone-header{ + .zone-header { @include flex-center; .value { width: 100%; @@ -62,16 +60,10 @@ cursor: pointer; } } - &:first-child{ - background: var(--highlight-accent-color); - .input-value{ - color: var(--highlight-text-color); - } - } } .active { background: var(--highlight-accent-color); - .input-value{ + .input-value { color: var(--highlight-text-color); } } diff --git a/app/src/styles/components/logs/logs.scss b/app/src/styles/components/logs/logs.scss index f07708b..b7c1363 100644 --- a/app/src/styles/components/logs/logs.scss +++ b/app/src/styles/components/logs/logs.scss @@ -5,14 +5,11 @@ width: 100vw; height: 100vh; background: var(--background-color-secondary); + @include flex-center; .log-list-wrapper { - height: 50%; - min-width: 50%; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); + height: 60%; + min-width: 55%; z-index: 5; background: var(--background-color); padding: 14px 12px; @@ -31,6 +28,10 @@ display: flex; align-items: center; gap: 6px; + .icon { + @include flex-center; + scale: 0.8; + } } .close { @@ -38,47 +39,66 @@ height: 28px; width: 28px; cursor: pointer; + border-radius: #{$border-radius-medium}; svg { scale: 1.6; } + &:hover { + background: var(--background-color); + } + } + } + .log-nav-container { + @include flex-space-between; + align-items: flex-end; + .log-nav-wrapper { + display: flex; + gap: 6px; + + .log-nav { + padding: 8px 16px; + border-radius: 19px; + } + + .log-nav.active { + background-color: var(--background-color-accent); + color: var(--text-button-color); + } + } + .clear-button{ + margin: 0 6px; + padding: 4px 12px; + color: var(--text-disabled); + border-radius: #{$border-radius-large}; + &:hover{ + font-weight: 300; + color: var(--text-color); + background: var(--background-color-solid-gradient); + } } } - .log-nav-wrapper { - display: flex; - gap: 6px; - - .log-nav { - padding: 8px 16px; - border-radius: 19px; - } - - .log-nav.active { - background-color: var(--background-color-accent); - color: var(--text-button-color); - } - } - .log-entry-wrapper { - height: 100%; + height: calc(100% - 80px); display: flex; flex-direction: column; gap: 4px; background: var(--background-color); - padding: 18px 10px; + padding: 10px; border-radius: 16px; outline: 1px solid var(--border-color); outline-offset: -1px; + overflow: auto; .log-entry { padding: 4px; border-radius: 4px; font-size: var(--font-size-small); display: flex; - align-items: center; - gap: 6px; .log-icon { + height: 24px; + width: 24px; @include flex-center; } .log-entry-message-container { @@ -90,8 +110,9 @@ font-weight: 300; opacity: 0.8; text-wrap: nowrap; + height: 100%; } - .log-entry-message{ + .log-entry-message { width: 100%; } } @@ -101,5 +122,10 @@ } } } + .no-log{ + padding: 20px; + text-align: center; + color: var(--text-color); + } } } diff --git a/app/src/styles/components/simulation/analysis.scss b/app/src/styles/components/simulation/analysis.scss index 9aed5c8..b838b0b 100644 --- a/app/src/styles/components/simulation/analysis.scss +++ b/app/src/styles/components/simulation/analysis.scss @@ -11,18 +11,12 @@ width: 100%; height: 100vh; pointer-events: none; - padding: 10px; z-index: 2; - .analysis-wrapper { - display: flex; - flex-direction: column; - gap: 12px; - } .analysis-card { min-width: 333px; border-radius: 20px; - padding: 8px; + margin: 8px; pointer-events: all; .analysis-card-wrapper { @@ -47,6 +41,13 @@ line-height: 20px; font-size: var(--font-size-regular); } + + .sub-header { + font-weight: 300; + font-size: var(--font-size-tiny); + color: var(--text-color); + opacity: 0.8; + } } .process-container { @@ -97,7 +98,7 @@ .metrics-section { padding-top: 16px; - border-top: 1px solid var(--background-color-gray); + border-top: 1px solid var(--border-color); .metric { display: flex; @@ -116,7 +117,8 @@ } } } - .throughoutSummary-wrapper { + + .production-wrapper { .process-container { display: flex; flex-direction: row; @@ -127,7 +129,7 @@ .throughput-value { font-size: var(--font-size-small); - flex: 1; + flex: 0.8; display: flex; flex-direction: column; @@ -139,15 +141,24 @@ } .lineChart { + flex: 1.2; max-width: 200px; height: 100px; position: relative; + background-image: radial-gradient(#8d8d8da4 1px, transparent 1px); + background-size: 10px 10px; + border-radius: 8px; .assetUsage { text-align: right; position: absolute; right: 0; top: 0; + + .key, + .value { + font-size: var(--font-size-regular); + } } canvas { @@ -163,12 +174,13 @@ .footer-card { width: 100%; - background: var(--background-color-gray); - border-radius: 6px; - padding: 8px; + background: var(--background-color); + border-radius: #{$border-radius-large}; + padding: 12px; display: flex; flex-direction: column; - gap: 6px; + gap: 12px; + outline: 1px solid var(--border-color); &:first-child { width: 85%; @@ -225,7 +237,7 @@ justify-content: space-between; width: 100%; gap: 6px; - + padding-top: 3px; .shift-wrapper { display: flex; align-items: center; @@ -251,7 +263,8 @@ } label { - font-size: var(--font-size-small); + font-weight: 200; + font-size: var(--font-size-tiny); position: relative; } @@ -268,19 +281,29 @@ } } } + .roiSummary-wrapper { max-width: 470px; background-color: var(--background-color); .product-info { display: flex; + align-items: center; + gap: 6px; } .playBack { display: flex; - background-color: var(--background-color); - border-radius: 12px; - padding: 6px; + align-items: center; + gap: 2px; + border-radius: 66px; + background: var(--background-color); + padding: 6px 12px; + border: 1px solid var(--border-color); + + svg { + scale: 0.8; + } .info { span { @@ -313,10 +336,12 @@ flex-direction: column; gap: 3px; align-items: center; + font-weight: 300; .key { - font-size: var(--font-size-xlarge); - color: var(--accent-color); + font-weight: 500; + font-size: var(--font-size-large); + color: #28b9f3; } } } @@ -363,8 +388,9 @@ gap: 6px; .metric-item { + padding: 8px; border-radius: #{$border-radius-large}; - background-color: var(--background-color); + background: var(--background-color); border: 1px solid var(--border-color); } } @@ -372,10 +398,12 @@ } .cost-breakdown { - background-color: var(--background-color); + background: var(--background-color); border: 1px solid var(--border-color); border-radius: #{$border-radius-extra-large}; + max-height: 20vh; padding: 16px; + overflow: auto; .breakdown-header { display: flex; @@ -385,7 +413,7 @@ .section-wrapper { display: flex; - gap: 4px; + gap: 6px; align-items: center; } @@ -427,6 +455,7 @@ text-align: left; border: 1px solid var(--border-color); } + th { background-color: var(--background-color); } @@ -434,8 +463,8 @@ } .tips-section { - background-color: var(--background-color); - border-radius: #{$border-radius-large}; + background: var(--background-color); + border-radius: #{$border-radius-extra-large}; outline: 1px solid var(--border-color); display: flex; flex-direction: column; @@ -445,7 +474,10 @@ .tip-header { display: flex; align-items: center; - + gap: 4px; + .lightbulb-icon{ + @include flex-center; + } .tip-title { color: var(--text-color); font-weight: 600; @@ -490,43 +522,22 @@ } } - .semi-circle-wrapper { - width: 100%; - height: 125px; - overflow-y: hidden; + .svg-half-donut { position: relative; - .semi-circle { + + .label-wrapper { width: 100%; - height: 250px; - border-radius: 50%; - position: relative; - } - .progress-cover { position: absolute; - width: 75%; - height: 75%; - top: 12.5%; - left: 12.5%; - border-radius: 50%; - } - } + bottom: 2px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; - .label-wrapper { - .label { - font-size: var(--font-size-xxxlarge); + .label { + font-size: var(--font-size-xxlarge); + } } - - position: absolute; - bottom: 0%; - left: 50%; - transform: translate(-50%, 0%); - font-weight: bold; - font-size: 1.2rem; - color: #333; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; } } @@ -554,4 +565,3 @@ } // Breakdown Table Open/Close Logic - diff --git a/app/src/styles/components/simulation/simulation.scss b/app/src/styles/components/simulation/simulation.scss index 88f85dc..37a87a8 100644 --- a/app/src/styles/components/simulation/simulation.scss +++ b/app/src/styles/components/simulation/simulation.scss @@ -3,7 +3,7 @@ .simulation-player-wrapper { position: fixed; - bottom: 12px; + bottom: 32px; left: 50%; z-index: 2; transform: translate(-50%, 0); @@ -35,11 +35,11 @@ @include flex-space-between; gap: 12px; justify-content: space-between; - .header{ + .header { @include flex-center; gap: 6px; padding: 0 8px; - svg{ + svg { scale: 1.3; } } @@ -144,14 +144,21 @@ &::after { content: ""; - background-color: #e5e5ea; + background: var(--background-color-solid); + opacity: 0.4; position: absolute; top: 50%; transform: translate(0, -50%); width: 100%; height: 3px; } - + .custom-slider-wrapper { + height: 100%; + width: 100%; + padding: 0 26px; + background: transparent; + border-radius: #{$border-radius-large}; + } .custom-slider { height: 100%; width: 100%; @@ -185,11 +192,13 @@ .marker { position: absolute; - background-color: var(--text-disabled); + background: var(--background-color-solid); + opacity: 0.6; width: 2px; height: 12px; border-radius: 1px; top: 8px; + z-index: 1; } .marker.marker-10 { @@ -299,6 +308,37 @@ } } } + + .open { + .start-displayer, + .end-displayer { + display: none; + } + + .timmer { + display: none; + } + .progresser-wrapper { + padding-top: 4px; + } + + .time-displayer { + height: 0; + opacity: 0; + pointer-events: none; + display: none; + } + + .processDisplayer { + padding: 0 8px; + background: transparent; + + .process-player { + width: 0; + display: none !important; + } + } + } } .processDisplayer { @@ -314,22 +354,6 @@ font-size: var(--font-size-tiny); } - .timmer { - width: auto; - position: absolute; - bottom: 0; - font-size: var(--font-size-tiny); - } - - .start-displayer { - left: 8px; - } - - .end-displayer { - width: auto; - right: 8px; - } - .start-displayer { bottom: 4px; left: 16px; @@ -351,12 +375,12 @@ border-width: 1px; background: var(--background-color-accent, #6f42c1); } - .process-wrapper{ + .process-wrapper { .process-container { position: relative; display: flex; width: 100%; - + .process { height: 5px; border-radius: 4px; @@ -368,35 +392,3 @@ } } } - -.simulation-player-container.open { - - .start-displayer, - .end-displayer { - display: none; - } - - .timmer { - display: none; - } - .progresser-wrapper { - padding-top: 4px; - } - - .time-displayer { - height: 0; - opacity: 0; - pointer-events: none; - display: none; - } - - .processDisplayer { - padding: 0 8px; - background: transparent; - - .process-player { - width: 0; - display: none !important; - } - } -} diff --git a/app/src/styles/components/tools.scss b/app/src/styles/components/tools.scss index 29d37b4..03813a4 100644 --- a/app/src/styles/components/tools.scss +++ b/app/src/styles/components/tools.scss @@ -14,7 +14,7 @@ width: fit-content; transition: width 0.2s; background: var(--background-color); - backdrop-filter: blur(8px); + backdrop-filter: blur(20px); z-index: 2; outline: 1px solid var(--border-color); outline-offset: -1px; @@ -31,10 +31,12 @@ .activeDropicon { @include flex-center; gap: 2px; + // stylelint-disable-next-line interpolate-size: allow-keywords; width: 0; opacity: 0; animation: expandWidth 0.2s ease-in-out forwards; + will-change: width; .tool-button { @include flex-center; @@ -44,9 +46,11 @@ border-radius: #{$border-radius-medium}; &:hover { - background: color-mix(in srgb, - var(--highlight-accent-color) 60%, - transparent); + background: color-mix( + in srgb, + var(--highlight-accent-color) 60%, + transparent + ); } } @@ -70,9 +74,11 @@ position: relative; &:hover { - background: color-mix(in srgb, - var(--highlight-accent-color) 60%, - transparent); + background: color-mix( + in srgb, + var(--highlight-accent-color) 60%, + transparent + ); } .drop-down-container { @@ -179,11 +185,11 @@ font-weight: 500; background: var(--accent-color); color: var(--highlight-accent-color); - &::after{ + &::after { animation: pulse 1s ease-out infinite; } } - &::after{ + &::after { content: ""; position: absolute; height: 100%; @@ -195,14 +201,14 @@ } @keyframes pulse { - 0%{ + 0% { opacity: 0; - scale: .5; + scale: 0.5; } - 50%{ + 50% { opacity: 1; } - 100%{ + 100% { opacity: 0; scale: 2; } @@ -218,4 +224,4 @@ width: fit-content; opacity: 1; } -} \ No newline at end of file +} diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index f8c022f..cffee1e 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -7,7 +7,7 @@ top: 32px; left: 8px; background: var(--background-color); - backdrop-filter: blur(15px); + backdrop-filter: blur(20px); border-radius: #{$border-radius-extra-large}; outline: 1px solid var(--border-color); box-shadow: #{$box-shadow-medium}; @@ -250,7 +250,7 @@ top: 32px; right: 8px; background: var(--background-color); - backdrop-filter: blur(15px); + backdrop-filter: blur(20px); border-radius: #{$border-radius-extra-large}; outline: 1px solid var(--border-color); box-shadow: #{$box-shadow-medium}; @@ -363,7 +363,7 @@ height: 34px; width: 34px; border-radius: #{$border-radius-circle}; - background: var(--background-color-secondary); + background: var(--background-color-solid-gradient); backdrop-filter: blur(12px); outline: 1px solid var(--border-color); outline-offset: -1px; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index abc626a..09b73a9 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -67,8 +67,8 @@ min-width: 150px; z-index: 3; transform: translate(-50%, -10%); - transition: transform 0.5s linear; pointer-events: all; + transition: all 0.3s linear; &::-webkit-scrollbar { display: none; @@ -243,6 +243,7 @@ &:hover { background: var(--highlight-accent-color); width: 100%; + .label { color: var(--accent-color); } @@ -619,6 +620,7 @@ .label { color: var(--accent-color); } + background: var(--highlight-accent-color); width: 100%; @@ -738,10 +740,10 @@ outline-color: #ffe3e0; } -.editWidgetOptions { +.context-menu-options { position: absolute; background: var(--background-color); - backdrop-filter: blur(10px); + backdrop-filter: blur(20px); z-index: 3; display: flex; flex-direction: column; @@ -749,6 +751,7 @@ overflow: hidden; padding: 4px; min-width: 150px; + outline: 1px solid var(--border-color); .option { padding: 4px 10px; @@ -933,4 +936,4 @@ opacity: 0; transform: scaleY(0); } -} +} \ No newline at end of file diff --git a/app/src/styles/pages/userAuth.scss b/app/src/styles/pages/userAuth.scss index 9d07dcf..cb45c13 100644 --- a/app/src/styles/pages/userAuth.scss +++ b/app/src/styles/pages/userAuth.scss @@ -48,16 +48,16 @@ max-width: 350px; padding: 10px; margin-bottom: 20px; - border: 1px solid var(--accent-color); + border: 1px solid var(--highlight-text-color); border-radius: #{$border-radius-extra-large}; background: transparent; - color: var(--accent-color); + color: var(--highlight-text-color); font-size: 14px; outline: none; cursor: pointer; .google-icon { - color: var(--accent-color); + color: var(--highlight-text-color); font-weight: bold; font-size: 16px; margin-right: 10px; @@ -89,7 +89,7 @@ border-radius: #{$border-radius-extra-large}; background: var(--background-color); font-size: 14px; - color: var(--input-text-color); + color: var(--text-button-color); &:focus { border-color: var(--accent-color); @@ -120,8 +120,8 @@ .continue-button { width: 100%; padding: 10px; - background: var(--accent-gradient-color); - color: var(--background-color); + background: var(--background-color-button); + color: var(--text-button-color); font-size: 14px; border: none; outline: none; @@ -158,7 +158,7 @@ text-align: center; .link { - color: var(--accent-color); + color: var(--highlight-text-color); text-decoration: none; &:hover { text-decoration: underline;