From ca6fc13171219ec86bf7fa5d00a399055ca83084 Mon Sep 17 00:00:00 2001 From: BFlat Date: Thu, 3 Apr 2025 12:23:54 +0700 Subject: [PATCH] aspect ratio update (#468) - added 3:2, 2:3 ratios (defaulted to 3:2 [1200 x 800 px]) - added letterbox accordingly - aspect ratio buttons to show icons for each ratio - popover to close when item selected --- .../components/reusable/Popover/Popover.tsx | 124 ++++++++++-------- .../app/pages/PageEnigma/Editor/editor.ts | 34 ++++- .../app/pages/PageEnigma/PageEditor.tsx | 1 - .../comps/AspectRatioMenu/AspectRatioMenu.tsx | 32 ++++- .../PageEnigma/comps/PromptBox/PromptBox.tsx | 107 ++++++++++++++- .../comps/SceneContainer/helpers.ts | 22 ++++ .../app/pages/PageEnigma/enums/EditorState.ts | 2 + frontend/editor3d/package-lock.json | 12 ++ frontend/editor3d/package.json | 1 + 9 files changed, 267 insertions(+), 68 deletions(-) diff --git a/frontend/editor3d/app/components/reusable/Popover/Popover.tsx b/frontend/editor3d/app/components/reusable/Popover/Popover.tsx index 41ad4e363..a37bb816c 100644 --- a/frontend/editor3d/app/components/reusable/Popover/Popover.tsx +++ b/frontend/editor3d/app/components/reusable/Popover/Popover.tsx @@ -42,11 +42,13 @@ export const PopoverMenu = ({ }: PopoverMenuProps) => { const selectedItem = items.find((item) => item.selected); - const handleItemClick = (item: PopoverItem) => { + const handleItemClick = (item: PopoverItem, close: () => void) => { if (mode === "button" && item.action && onAction) { onAction(item.action); + close(); } else { onSelect?.(item); + close(); } }; @@ -60,67 +62,75 @@ export const PopoverMenu = ({ return ( - - {triggerIcon} - {mode === "toggle" && selectedItem ? selectedItem.label : null} - {mode === "default" && triggerLabel ? triggerLabel : null} - + {({ close }) => ( + <> + + {triggerIcon} + {mode === "toggle" && selectedItem ? selectedItem.label : null} + {mode === "default" && triggerLabel ? triggerLabel : null} + -
- - -
+ - {panelTitle && ( -
- {panelTitle} -
- )} - {mode === "default" && children ? ( -
{children}
- ) : ( -
- {items.map((item, index) => ( - + ))} + {showAddButton && onAdd && ( + )} - - ))} - {showAddButton && onAdd && ( - +
)}
- )} -
- - - + + + + + )}
); }; diff --git a/frontend/editor3d/app/pages/PageEnigma/Editor/editor.ts b/frontend/editor3d/app/pages/PageEnigma/Editor/editor.ts index d685aca0a..3eaaf218e 100644 --- a/frontend/editor3d/app/pages/PageEnigma/Editor/editor.ts +++ b/frontend/editor3d/app/pages/PageEnigma/Editor/editor.ts @@ -334,6 +334,20 @@ class Editor { aspectRatio: 16 / 9, }; } + case CameraAspectRatio.HORIZONTAL_3_2: { + return { + width: 1200, + height: 800, + aspectRatio: 3 / 2, + }; + } + case CameraAspectRatio.VERTICAL_2_3: { + return { + width: 800, + height: 1200, + aspectRatio: 2 / 3, + }; + } case CameraAspectRatio.VERTICAL_9_16: { return { width: 720, @@ -948,16 +962,28 @@ class Editor { this.render_camera_aspect_ratio === CameraAspectRatio.HORIZONTAL_16_9 ? 1024 : this.render_camera_aspect_ratio === - CameraAspectRatio.VERTICAL_9_16 + CameraAspectRatio.VERTICAL_9_16 ? 576 - : 1000; + : this.render_camera_aspect_ratio === + CameraAspectRatio.HORIZONTAL_3_2 + ? 900 + : this.render_camera_aspect_ratio === + CameraAspectRatio.VERTICAL_2_3 + ? 600 + : 1000; const height = this.render_camera_aspect_ratio === CameraAspectRatio.HORIZONTAL_16_9 ? 576 : this.render_camera_aspect_ratio === - CameraAspectRatio.VERTICAL_9_16 + CameraAspectRatio.VERTICAL_9_16 ? 1024 - : 1000; + : this.render_camera_aspect_ratio === + CameraAspectRatio.HORIZONTAL_3_2 + ? 600 + : this.render_camera_aspect_ratio === + CameraAspectRatio.VERTICAL_2_3 + ? 900 + : 1000; this.rawRenderer.setSize(width, height); diff --git a/frontend/editor3d/app/pages/PageEnigma/PageEditor.tsx b/frontend/editor3d/app/pages/PageEnigma/PageEditor.tsx index 2ada8208c..ba5ae88c5 100644 --- a/frontend/editor3d/app/pages/PageEnigma/PageEditor.tsx +++ b/frontend/editor3d/app/pages/PageEnigma/PageEditor.tsx @@ -113,7 +113,6 @@ export const PageEditor = () => {
-
diff --git a/frontend/editor3d/app/pages/PageEnigma/comps/AspectRatioMenu/AspectRatioMenu.tsx b/frontend/editor3d/app/pages/PageEnigma/comps/AspectRatioMenu/AspectRatioMenu.tsx index 84abe7e18..2065290cb 100644 --- a/frontend/editor3d/app/pages/PageEnigma/comps/AspectRatioMenu/AspectRatioMenu.tsx +++ b/frontend/editor3d/app/pages/PageEnigma/comps/AspectRatioMenu/AspectRatioMenu.tsx @@ -18,13 +18,21 @@ export const AspectRatioMenu = () => { ? faRectangle : cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16 ? faRectangleVertical - : faSquare; + : cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_3_2 + ? faRectangle + : cameraAspectRatio.value === CameraAspectRatio.VERTICAL_2_3 + ? faRectangleVertical + : faSquare; const buttonText = cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_16_9 ? "16:9 Horizontal" : cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16 ? "9:16 Vertical" - : "1:1 Squared"; + : cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_3_2 + ? "3:2 Horizontal" + : cameraAspectRatio.value === CameraAspectRatio.VERTICAL_2_3 + ? "2:3 Vertical" + : "1:1 Squared"; const handleChangeAspectRatio = (newRatio: CameraAspectRatio) => { Queue.publish({ @@ -52,6 +60,26 @@ export const AspectRatioMenu = () => { onClick: () => handleChangeAspectRatio(CameraAspectRatio.HORIZONTAL_16_9), }, + { + label: "3:2", + icon: faRectangle, + className: "pl-4", + description: "Horizontal", + selected: + cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_3_2, + onClick: () => + handleChangeAspectRatio(CameraAspectRatio.HORIZONTAL_3_2), + }, + { + label: "2:3", + icon: faRectangleVertical, + className: "pl-4", + description: "Vertical", + selected: + cameraAspectRatio.value === CameraAspectRatio.VERTICAL_2_3, + onClick: () => + handleChangeAspectRatio(CameraAspectRatio.VERTICAL_2_3), + }, { label: "9:16", icon: faRectangleVertical, diff --git a/frontend/editor3d/app/pages/PageEnigma/comps/PromptBox/PromptBox.tsx b/frontend/editor3d/app/pages/PageEnigma/comps/PromptBox/PromptBox.tsx index 6229bb199..6dfb06cf8 100644 --- a/frontend/editor3d/app/pages/PageEnigma/comps/PromptBox/PromptBox.tsx +++ b/frontend/editor3d/app/pages/PageEnigma/comps/PromptBox/PromptBox.tsx @@ -1,4 +1,5 @@ import { useState, useRef, useEffect } from "react"; +import { useSignals } from "@preact/signals-react/runtime"; import { faPlus, @@ -10,18 +11,46 @@ import { faDownload, faSpinnerThird, } from "@fortawesome/pro-solid-svg-icons"; +import { + faRectangleWide, + faRectangleVertical, + faSquare, + faRectangle, +} from "@fortawesome/pro-regular-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { PopoverItem, PopoverMenu } from "~/components/reusable/Popover"; import { Button } from "~/components"; +import { CameraAspectRatio } from "~/pages/PageEnigma/enums"; +import { cameraAspectRatio } from "~/pages/PageEnigma/signals"; +import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; +import Queue from "~/pages/PageEnigma/Queue/Queue"; +import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; export const PromptBox = () => { + useSignals(); const [prompt, setPrompt] = useState(""); const [isEnqueueing, setisEnqueueing] = useState(false); const [aspectRatioList, setAspectRatioList] = useState([ - { label: "16:9", selected: true }, - { label: "3:2", selected: false }, - { label: "2:3", selected: false }, - { label: "1:1", selected: false }, + { + label: "3:2", + selected: true, + icon: , + }, + { + label: "2:3", + selected: false, + icon: , + }, + { + label: "1:1", + selected: false, + icon: , + }, + { + label: "16:9", + selected: false, + icon: , + }, ]); const [cameraList, setCameraList] = useState([ { @@ -45,6 +74,25 @@ export const PromptBox = () => { } }, [prompt]); + // Update aspect ratio list based on the current cameraAspectRatio signal + useEffect(() => { + setAspectRatioList((prev) => + prev.map((item) => ({ + ...item, + selected: + (item.label === "16:9" && + cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_16_9) || + (item.label === "3:2" && + cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_3_2) || + (item.label === "2:3" && + cameraAspectRatio.value === CameraAspectRatio.VERTICAL_2_3) || + (item.label === "1:1" && + cameraAspectRatio.value === CameraAspectRatio.SQUARE_1_1), + })), + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cameraAspectRatio.value]); + const handleCameraSelect = (selectedItem: PopoverItem) => { setCameraList((prev) => prev.map((item) => ({ @@ -73,6 +121,32 @@ export const PromptBox = () => { selected: item.label === selectedItem.label, })), ); + + // Map the selected label to the corresponding CameraAspectRatio enum value + let newRatio: CameraAspectRatio; + switch (selectedItem.label) { + case "16:9": + newRatio = CameraAspectRatio.HORIZONTAL_16_9; + break; + case "3:2": + newRatio = CameraAspectRatio.HORIZONTAL_3_2; + break; + case "2:3": + newRatio = CameraAspectRatio.VERTICAL_2_3; + break; + case "1:1": + newRatio = CameraAspectRatio.SQUARE_1_1; + break; + default: + newRatio = CameraAspectRatio.HORIZONTAL_16_9; + } + + // Publish the change to the engine + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.CHANGE_CAMERA_ASPECT_RATIO, + data: newRatio, + }); }; const handleFileUpload = (event: React.ChangeEvent) => { @@ -133,6 +207,24 @@ export const PromptBox = () => { } }; + // Get the current aspect ratio icon based on the cameraAspectRatio signal + const getCurrentAspectRatioIcon = () => { + switch (cameraAspectRatio.value) { + case CameraAspectRatio.HORIZONTAL_16_9: + return faRectangleWide; + case CameraAspectRatio.HORIZONTAL_3_2: + return faRectangle; + case CameraAspectRatio.VERTICAL_2_3: + return faRectangleVertical; + case CameraAspectRatio.VERTICAL_9_16: + return faRectangleVertical; + case CameraAspectRatio.SQUARE_1_1: + return faSquare; + default: + return faRectangleWide; + } + }; + return (
{ onSelect={handleAspectRatioSelect} mode="toggle" panelTitle="Aspect Ratio" + showIconsInList + triggerIcon={ + + } /> = 0 + ? MatteOrientation.TOP_BOTTOM + : MatteOrientation.LEFT_RIGHT; + } + case CameraAspectRatio.HORIZONTAL_3_2: { + return (width / 3) * 2 - height >= 0 + ? MatteOrientation.LEFT_RIGHT + : MatteOrientation.TOP_BOTTOM; + } case CameraAspectRatio.HORIZONTAL_16_9: default: { return (width / 16) * 9 - height >= 0 @@ -58,6 +68,12 @@ export const calcMatteWidth = ({ case CameraAspectRatio.VERTICAL_9_16: { return (width - (height / 16) * 9) / 2; } + case CameraAspectRatio.HORIZONTAL_3_2: { + return (width - (height / 2) * 3) / 2; + } + case CameraAspectRatio.VERTICAL_2_3: { + return (width - (height / 3) * 2) / 2; + } default: { return undefined; } @@ -88,6 +104,12 @@ export const calcMatteHeight = ({ case CameraAspectRatio.VERTICAL_9_16: { return (height - (width / 9) * 16) / 2; } + case CameraAspectRatio.HORIZONTAL_3_2: { + return (height - (width / 3) * 2) / 2; + } + case CameraAspectRatio.VERTICAL_2_3: { + return (height - (width / 2) * 3) / 2; + } default: { return undefined; } diff --git a/frontend/editor3d/app/pages/PageEnigma/enums/EditorState.ts b/frontend/editor3d/app/pages/PageEnigma/enums/EditorState.ts index 70375c046..12c1af5b9 100644 --- a/frontend/editor3d/app/pages/PageEnigma/enums/EditorState.ts +++ b/frontend/editor3d/app/pages/PageEnigma/enums/EditorState.ts @@ -8,4 +8,6 @@ export enum CameraAspectRatio { HORIZONTAL_16_9 = "horizontal_16_9", VERTICAL_9_16 = "vertical_9_16", SQUARE_1_1 = "SQUARE_1_1", + HORIZONTAL_3_2 = "horizontal_3_2", + VERTICAL_2_3 = "vertical_2_3", } diff --git a/frontend/editor3d/package-lock.json b/frontend/editor3d/package-lock.json index 7cdc02361..293a635c7 100644 --- a/frontend/editor3d/package-lock.json +++ b/frontend/editor3d/package-lock.json @@ -9,6 +9,7 @@ "@awesome.me/kit-fde2be5eb0": "^1.0.7", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.2", + "@fortawesome/pro-regular-svg-icons": "^6.5.1", "@fortawesome/pro-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.18", @@ -1423,6 +1424,17 @@ "node": ">=6" } }, + "node_modules/@fortawesome/pro-regular-svg-icons": { + "version": "6.7.2", + "resolved": "https://npm.fontawesome.com/@fortawesome/pro-regular-svg-icons/-/6.7.2/pro-regular-svg-icons-6.7.2.tgz", + "integrity": "sha512-cJolK4K/s56mHAqOyno24SmjviHqSqG9ZfckBKCpWjJlgDexi61EfET5wWbmmEot6/BqasFjw9S6tudFQUL/PQ==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@fortawesome/pro-solid-svg-icons": { "version": "6.7.2", "resolved": "https://npm.fontawesome.com/@fortawesome/pro-solid-svg-icons/-/6.7.2/pro-solid-svg-icons-6.7.2.tgz", diff --git a/frontend/editor3d/package.json b/frontend/editor3d/package.json index 09be8ba37..a58ba3976 100644 --- a/frontend/editor3d/package.json +++ b/frontend/editor3d/package.json @@ -21,6 +21,7 @@ "@awesome.me/kit-fde2be5eb0": "^1.0.7", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.2", + "@fortawesome/pro-regular-svg-icons": "^6.5.1", "@fortawesome/pro-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.18",