From a327106b19cc75f65097fd9353fb59eddd907b37 Mon Sep 17 00:00:00 2001 From: AI Bot Date: Thu, 30 Jul 2026 17:43:48 +0530 Subject: [PATCH] Make UV Editor canvas dimensions strictly follow the base texture aspect ratio --- src/components/UVEditor.tsx | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/components/UVEditor.tsx b/src/components/UVEditor.tsx index 17e847c..3b9d0d0 100644 --- a/src/components/UVEditor.tsx +++ b/src/components/UVEditor.tsx @@ -40,6 +40,8 @@ export function UVEditor({ onClose, onSave, targetObject, loadedModel, sceneObje const [mappingType, setMappingType] = useState(targetObject?.materialProps?.mappingType || 'uv'); const [bgColor, setBgColor] = useState(initBg); const [bgTransparent, setBgTransparent] = useState(initTrans); + const [canvasW, setCanvasW] = useState(1024); + const [canvasH, setCanvasH] = useState(1024); const containerRef = useRef(null); const fileInputRef = useRef(null); @@ -90,9 +92,9 @@ export function UVEditor({ onClose, onSave, targetObject, loadedModel, sceneObje const renderCanvas = useCallback((lrs: Layer[], transparent: boolean, bg: string): string => { const c = document.createElement('canvas'); - c.width = c.height = CANVAS_SIZE; + c.width = canvasW; c.height = canvasH; const ctx = c.getContext('2d')!; - if (!transparent) { ctx.fillStyle = bg; ctx.fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE); } + if (!transparent) { ctx.fillStyle = bg; ctx.fillRect(0, 0, canvasW, canvasH); } lrs.forEach(layer => { if (!layer.img || layer.visible === false) return; const iw = layer.img.width, ih = layer.img.height; @@ -125,7 +127,7 @@ export function UVEditor({ onClose, onSave, targetObject, loadedModel, sceneObje const url = ev.target?.result as string; const img = new Image(); img.onload = () => { - const nl: Layer = { id: `l-${Date.now()}`, url, img, x: CANVAS_SIZE / 2, y: CANVAS_SIZE / 2, scaleX: 1, scaleY: 1, rotation: 0, cropL: 0, cropT: 0, cropR: 0, cropB: 0, visible: true }; + const nl: Layer = { id: `l-${Date.now()}`, url, img, x: canvasW / 2, y: canvasH / 2, scaleX: 1, scaleY: 1, rotation: 0, cropL: 0, cropT: 0, cropR: 0, cropB: 0, visible: true }; setLayers(p => [...p, nl]); setSelectedId(nl.id); }; @@ -140,7 +142,7 @@ export function UVEditor({ onClose, onSave, targetObject, loadedModel, sceneObje const getSF = () => { const r = containerRef.current?.getBoundingClientRect(); - return r ? CANVAS_SIZE / r.width : 1; + return r ? canvasW / r.width : 1; }; const onHandleDown = (e: React.MouseEvent, handle: Handle, layer: Layer) => { @@ -166,13 +168,13 @@ export function UVEditor({ onClose, onSave, targetObject, loadedModel, sceneObje let nx = d.layer.x + dx; let ny = d.layer.y + dy; // Clamp to canvas bounds with soft margin - nx = Math.max(0, Math.min(CANVAS_SIZE, nx)); - ny = Math.max(0, Math.min(CANVAS_SIZE, ny)); + nx = Math.max(0, Math.min(canvasW, nx)); + ny = Math.max(0, Math.min(canvasH, ny)); // Hold Shift to snap to grid if (isShift) { nx = snap(nx, SNAP_PX); ny = snap(ny, SNAP_PX); } // Center snapping: snap near canvas center - if (Math.abs(nx - CANVAS_SIZE / 2) < SNAP_PX) nx = CANVAS_SIZE / 2; - if (Math.abs(ny - CANVAS_SIZE / 2) < SNAP_PX) ny = CANVAS_SIZE / 2; + if (Math.abs(nx - canvasW / 2) < SNAP_PX) nx = canvasW / 2; + if (Math.abs(ny - canvasH / 2) < SNAP_PX) ny = canvasH / 2; updateLayer(d.layer.id, { x: nx, y: ny }); } else if (d.handle === 'rotate') { let rot = d.layer.rotation + dx / 2; @@ -228,11 +230,11 @@ export function UVEditor({ onClose, onSave, targetObject, loadedModel, sceneObje const ih = (layer.img?.height ?? 200) * (1 - layer.cropT - layer.cropB); const rect = containerRef.current?.getBoundingClientRect(); if (!rect) return null; - const pct = (v: number) => `${(v / CANVAS_SIZE) * 100}%`; - const w = (iw * layer.scaleX / CANVAS_SIZE) * 100; - const h = (ih * layer.scaleY / CANVAS_SIZE) * 100; - const lx = (layer.x / CANVAS_SIZE) * 100; - const ly = (layer.y / CANVAS_SIZE) * 100; + + const w = (iw * layer.scaleX / canvasW) * 100; + const h = (ih * layer.scaleY / canvasH) * 100; + const lx = (layer.x / canvasW) * 100; + const ly = (layer.y / canvasH) * 100; const cornerStyle = (cx: string, cy: string, cursor: string): React.CSSProperties => ({ position: 'absolute', left: cx, top: cy, width: 10, height: 10, @@ -411,7 +413,7 @@ export function UVEditor({ onClose, onSave, targetObject, loadedModel, sceneObje onClick={() => setSelectedId(null)}>
{ e.stopPropagation(); onHandleDown(e, 'move', layer); }} className="absolute cursor-move" style={{ - left: `${(layer.x / CANVAS_SIZE) * 100}%`, - top: `${(layer.y / CANVAS_SIZE) * 100}%`, - width: `${(iw / CANVAS_SIZE) * 100}%`, - height: `${(ih / CANVAS_SIZE) * 100}%`, + left: `${(layer.x / canvasW) * 100}%`, + top: `${(layer.y / canvasH) * 100}%`, + width: `${(iw / canvasW) * 100}%`, + height: `${(ih / canvasH) * 100}%`, transform: `translate(-50%,-50%) rotate(${layer.rotation}deg) scale(${layer.scaleX},${layer.scaleY})`, zIndex: isSelected ? 4 : 2, transition: 'transform 0.04s linear',