Compare commits

...
Author SHA1 Message Date
bflatastic 9f621e75c7 refactor drag and drop for cleaner code 2025-05-23 09:16:29 +07:00
bflatastic e599aecc62 enable double-sided image plane and signal removal 2025-05-23 08:55:52 +07:00
bflatastic 7b1bbc4a0e bug fix test 2 2025-05-23 08:36:24 +07:00
bflatastic 1a174345bb bug fix test 1 2025-05-23 08:26:47 +07:00
4 changed files with 72 additions and 77 deletions
@@ -206,6 +206,7 @@ class Scene {
color: 0xffffff,
map: texture,
transparent: true,
side: THREE.DoubleSide,
});
obj = new THREE.Mesh(geometry, image_material);
obj.userData["media_id"] = name;
@@ -54,15 +54,18 @@ import {
topNavMediaId,
topNavMediaUrl,
} from "~/components/signaled/TopBar/TopBar";
import { setOnImageDrop } from "../../../../../../libs/components/gallery-modal/src/lib/galleryDnd";
import { uploadPlaneFromMediaToken } from "~/components/reusable/UploadModalMedia/uploadPlane";
import { addObject } from "./signals/objectGroup/addObject";
import { AssetType } from "~/enums";
import { v4 as uuidv4 } from "uuid";
import { MediaItem } from "~/pages/PageEnigma/models";
import { GalleryItem } from "../../../../../../libs/components/gallery-modal/src/lib/gallery-modal";
import { UploaderState } from "~/models";
import * as THREE from "three";
import {
GalleryItem,
onImageDrop,
removeImageDropListener,
} from "@storyteller/ui-gallery-modal";
export const PageEditor = () => {
useSignals();
@@ -247,89 +250,63 @@ export const PageEditor = () => {
// Image drop from gallery/library modal
useEffect(() => {
setOnImageDrop(
async (item: GalleryItem, _position: { x: number; y: number }) => {
console.log("Drop debug:", {
const handler = onImageDrop(
(item: GalleryItem, position: { x: number; y: number }) => {
console.log("Drop debug (event):", {
editorEngine,
camera: editorEngine?.camera,
renderer: editorEngine?.renderer,
_position,
position,
});
// Diagnostic log for received drop position and canvas rect
if (editorEngine?.renderer) {
const rect = editorEngine.renderer.domElement.getBoundingClientRect();
const canvasX = _position.x - rect.left;
const canvasY = _position.y - rect.top;
console.log(
"[Drop Debug] Received:",
_position,
"Canvas rect:",
rect,
"canvasX:",
canvasX,
"canvasY:",
canvasY,
);
}
// Calculate world position from cursor immediately on drop
let worldPosition = undefined;
if (editorEngine?.camera && editorEngine?.renderer && _position) {
if (editorEngine?.camera && editorEngine?.renderer && position) {
const rect = editorEngine.renderer.domElement.getBoundingClientRect();
// Convert client coordinates to canvas coordinates
const canvasX = _position.x - rect.left;
const canvasY = _position.y - rect.top;
const canvasX = position.x - rect.left;
const canvasY = position.y - rect.top;
const ndcX = (canvasX / rect.width) * 2 - 1;
const ndcY = -(canvasY / rect.height) * 2 + 1;
const vector = new THREE.Vector3(ndcX, ndcY, 0.5);
vector.unproject(editorEngine.camera);
worldPosition = vector;
// Diagnostic logging
console.log({
dropPosition: _position,
rect: {
left: rect.left,
top: rect.top,
width: rect.width,
height: rect.height,
},
canvasX,
canvasY,
ndcX,
ndcY,
worldPosition,
});
}
try {
// Place the object immediately to avoid upload delay affecting placement
const mediaItem: MediaItem = {
version: 1,
type: AssetType.OBJECT,
media_id: item.id || uuidv4(),
name: item.label || "Image Plane",
...(worldPosition && {
position: {
x: worldPosition.x,
y: worldPosition.y,
z: worldPosition.z,
(async () => {
try {
// Place the object immediately to avoid upload delay affecting placement
const mediaItem: MediaItem = {
version: 1,
type: AssetType.OBJECT,
media_id: item.id || uuidv4(),
name: item.label || "Image Plane",
...(worldPosition && {
position: {
x: worldPosition.x,
y: worldPosition.y,
z: worldPosition.z,
},
}),
};
addObject(mediaItem);
await uploadPlaneFromMediaToken({
title: item.label || "Image Plane",
mediaToken: item.id,
progressCallback: (state: UploaderState) => {
if (state.status) console.log("Upload status:", state.status);
},
}),
};
addObject(mediaItem);
await uploadPlaneFromMediaToken({
title: item.label || "Image Plane",
mediaToken: item.id,
progressCallback: (state: UploaderState) => {
if (state.status) console.log("Upload status:", state.status);
},
});
console.log("Upload complete");
} catch (err) {
console.error("Failed to upload and add image plane:", err);
}
});
} catch (err) {
console.error("Failed to add image plane:", err);
}
})();
},
);
return () => {
removeImageDropListener(handler);
};
}, []);
const display3d = appTabId.value === "3D" ? "block" : "none";
@@ -1,3 +1,4 @@
export * from "./lib/gallery-modal";
export * from "./lib/galleryModalSignals";
export * from "./lib/GalleryDragComponent";
export * from "./lib/galleryDnd";
@@ -24,13 +24,6 @@ const dragState: DragState = {
const dragThreshold = 5;
export let onImageDrop:
| ((item: GalleryItem, position: { x: number; y: number }) => void)
| null = null;
export function setOnImageDrop(cb: typeof onImageDrop) {
onImageDrop = cb;
}
function onPointerDown(event: React.PointerEvent, item: GalleryItem) {
if (event.button !== 0) return;
dragState.item = item;
@@ -59,15 +52,38 @@ function onPointerMove(event: PointerEvent) {
dragState.currY = event.pageY;
}
export const IMAGE_DROP_EVENT = "gallery-image-drop";
export function emitImageDrop(
item: GalleryItem,
position: { x: number; y: number }
) {
window.dispatchEvent(
new CustomEvent(IMAGE_DROP_EVENT, { detail: { item, position } })
);
}
export function onImageDrop(
callback: (item: GalleryItem, position: { x: number; y: number }) => void
) {
const handler = (e: any) => {
callback(e.detail.item, e.detail.position);
};
window.addEventListener(IMAGE_DROP_EVENT, handler);
return handler;
}
export function removeImageDropListener(handler: (e: any) => void) {
window.removeEventListener(IMAGE_DROP_EVENT, handler);
}
function onPointerUp(event: PointerEvent) {
if (
dragState.item &&
dragState.isDragging &&
dragState.item.mediaClass === "image"
) {
if (onImageDrop) {
onImageDrop(dragState.item, { x: event.pageX, y: event.pageY });
}
emitImageDrop(dragState.item, { x: event.pageX, y: event.pageY });
}
dragState.item = null;
dragState.isDragging = false;