|
|
import { appState } from '../state.js';
|
|
|
import { DETECTION_CONFIG } from '../config.js';
|
|
|
import { loadImage } from '../utils/imageUtils.js';
|
|
|
|
|
|
let cropState = {
|
|
|
originalBox: null,
|
|
|
currentBox: null,
|
|
|
isDragging: false,
|
|
|
isResizing: false,
|
|
|
resizeHandle: null,
|
|
|
dragStartX: 0,
|
|
|
dragStartY: 0,
|
|
|
imageWidth: 0,
|
|
|
imageHeight: 0,
|
|
|
loadedImage: null,
|
|
|
canvas: null,
|
|
|
scale: 1
|
|
|
};
|
|
|
|
|
|
export function initializeCropEditor() {
|
|
|
const editCropBtn = document.getElementById('editCropBtn');
|
|
|
const applyCropBtn = document.getElementById('applyCropBtn');
|
|
|
const resetCropBtn = document.getElementById('resetCropBtn');
|
|
|
const cropControls = document.getElementById('cropControls');
|
|
|
|
|
|
if (editCropBtn) {
|
|
|
editCropBtn.addEventListener('click', () => {
|
|
|
showManualCropEditor();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
if (resetCropBtn) {
|
|
|
resetCropBtn.addEventListener('click', () => {
|
|
|
resetCropBox();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
if (applyCropBtn) {
|
|
|
applyCropBtn.addEventListener('click', () => {
|
|
|
applyCropSettings();
|
|
|
hideManualCropEditor();
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
export function showCropEditor(embryoIndex) {
|
|
|
const editCropBtn = document.getElementById('editCropBtn');
|
|
|
const cropControls = document.getElementById('cropControls');
|
|
|
const embryo = appState.croppedEmbryos[embryoIndex];
|
|
|
|
|
|
if (!embryo || !embryo.box) {
|
|
|
editCropBtn.style.display = 'none';
|
|
|
if (cropControls) cropControls.style.display = 'none';
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
|
|
|
cropState.originalBox = { ...embryo.box };
|
|
|
cropState.currentBox = { ...embryo.box };
|
|
|
|
|
|
editCropBtn.style.display = 'block';
|
|
|
|
|
|
|
|
|
if (cropControls) cropControls.style.display = 'none';
|
|
|
|
|
|
|
|
|
const croppedImage = document.getElementById('croppedImage');
|
|
|
if (croppedImage) croppedImage.style.display = 'block';
|
|
|
|
|
|
|
|
|
const cropCanvas = document.getElementById('manualCropCanvas');
|
|
|
if (cropCanvas) cropCanvas.style.display = 'none';
|
|
|
}
|
|
|
|
|
|
export function hideCropEditor() {
|
|
|
const editCropBtn = document.getElementById('editCropBtn');
|
|
|
const cropControls = document.getElementById('cropControls');
|
|
|
editCropBtn.style.display = 'none';
|
|
|
cropControls.style.display = 'none';
|
|
|
hideManualCropEditor();
|
|
|
}
|
|
|
|
|
|
function showManualCropEditor() {
|
|
|
const cropControls = document.getElementById('cropControls');
|
|
|
const editCropBtn = document.getElementById('editCropBtn');
|
|
|
|
|
|
cropControls.style.display = 'block';
|
|
|
editCropBtn.style.display = 'none';
|
|
|
|
|
|
|
|
|
const touchHint = document.querySelector('.touch-hint');
|
|
|
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
|
if (touchHint && isTouchDevice) {
|
|
|
touchHint.style.display = 'block';
|
|
|
}
|
|
|
|
|
|
|
|
|
initializeInteractiveCrop();
|
|
|
}
|
|
|
|
|
|
function hideManualCropEditor() {
|
|
|
const cropControls = document.getElementById('cropControls');
|
|
|
const editCropBtn = document.getElementById('editCropBtn');
|
|
|
const cropCanvas = document.getElementById('manualCropCanvas');
|
|
|
const croppedImage = document.getElementById('croppedImage');
|
|
|
|
|
|
cropControls.style.display = 'none';
|
|
|
editCropBtn.style.display = 'block';
|
|
|
|
|
|
if (cropCanvas) cropCanvas.style.display = 'none';
|
|
|
if (croppedImage) croppedImage.style.display = 'block';
|
|
|
}
|
|
|
|
|
|
async function initializeInteractiveCrop() {
|
|
|
if (!appState.currentImage || !cropState.originalBox) {
|
|
|
console.error('Missing image or box data');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const container = document.getElementById('croppedImagePreview');
|
|
|
const croppedImage = document.getElementById('croppedImage');
|
|
|
|
|
|
if (!container) {
|
|
|
console.error('Container not found');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (croppedImage) croppedImage.style.display = 'none';
|
|
|
|
|
|
|
|
|
let canvas = document.getElementById('manualCropCanvas');
|
|
|
if (!canvas) {
|
|
|
canvas = document.createElement('canvas');
|
|
|
canvas.id = 'manualCropCanvas';
|
|
|
canvas.className = 'manual-crop-canvas';
|
|
|
container.appendChild(canvas);
|
|
|
}
|
|
|
|
|
|
canvas.style.display = 'block';
|
|
|
|
|
|
try {
|
|
|
|
|
|
const img = await loadImage(appState.currentImage);
|
|
|
|
|
|
|
|
|
cropState.loadedImage = img;
|
|
|
|
|
|
|
|
|
const containerWidth = Math.max(container.clientWidth - 40, 300);
|
|
|
const containerHeight = 400;
|
|
|
const scale = Math.min(containerWidth / img.width, containerHeight / img.height, 1);
|
|
|
|
|
|
canvas.width = Math.floor(img.width * scale);
|
|
|
canvas.height = Math.floor(img.height * scale);
|
|
|
cropState.imageWidth = canvas.width;
|
|
|
cropState.imageHeight = canvas.height;
|
|
|
cropState.scale = scale;
|
|
|
|
|
|
console.log('Crop editor initialized:', {
|
|
|
imageSize: `${img.width}x${img.height}`,
|
|
|
canvasSize: `${canvas.width}x${canvas.height}`,
|
|
|
scale: scale,
|
|
|
originalBox: cropState.originalBox
|
|
|
});
|
|
|
|
|
|
|
|
|
cropState.currentBox = {
|
|
|
x1: Math.floor(cropState.originalBox.x1 * scale),
|
|
|
y1: Math.floor(cropState.originalBox.y1 * scale),
|
|
|
x2: Math.floor(cropState.originalBox.x2 * scale),
|
|
|
y2: Math.floor(cropState.originalBox.y2 * scale)
|
|
|
};
|
|
|
|
|
|
console.log('Scaled crop box:', cropState.currentBox);
|
|
|
|
|
|
|
|
|
drawCropInterface(canvas);
|
|
|
|
|
|
|
|
|
setupCanvasEventListeners(canvas);
|
|
|
} catch (error) {
|
|
|
console.error('Error initializing crop editor:', error);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function drawCropInterface(canvas) {
|
|
|
if (!canvas || !cropState.loadedImage || !cropState.currentBox) {
|
|
|
console.error('Canvas, image, or crop box not available', {
|
|
|
canvas: !!canvas,
|
|
|
image: !!cropState.loadedImage,
|
|
|
box: !!cropState.currentBox
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
const img = cropState.loadedImage;
|
|
|
|
|
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
|
|
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
|
|
|
const box = cropState.currentBox;
|
|
|
const x1 = Math.max(0, Math.min(box.x1, canvas.width));
|
|
|
const y1 = Math.max(0, Math.min(box.y1, canvas.height));
|
|
|
const x2 = Math.max(0, Math.min(box.x2, canvas.width));
|
|
|
const y2 = Math.max(0, Math.min(box.y2, canvas.height));
|
|
|
|
|
|
|
|
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
|
|
|
|
|
|
|
|
|
ctx.fillRect(0, 0, canvas.width, y1);
|
|
|
|
|
|
ctx.fillRect(0, y2, canvas.width, canvas.height - y2);
|
|
|
|
|
|
ctx.fillRect(0, y1, x1, y2 - y1);
|
|
|
|
|
|
ctx.fillRect(x2, y1, canvas.width - x2, y2 - y1);
|
|
|
|
|
|
|
|
|
ctx.strokeStyle = '#00B8D4';
|
|
|
ctx.lineWidth = 3;
|
|
|
ctx.strokeRect(
|
|
|
x1,
|
|
|
y1,
|
|
|
x2 - x1,
|
|
|
y2 - y1
|
|
|
);
|
|
|
|
|
|
|
|
|
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
|
const handleSize = isTouchDevice ? 20 : 12;
|
|
|
|
|
|
|
|
|
ctx.fillStyle = '#00B8D4';
|
|
|
ctx.strokeStyle = 'white';
|
|
|
ctx.lineWidth = 2;
|
|
|
|
|
|
|
|
|
const corners = [
|
|
|
{ x: x1, y: y1 },
|
|
|
{ x: x2, y: y1 },
|
|
|
{ x: x1, y: y2 },
|
|
|
{ x: x2, y: y2 }
|
|
|
];
|
|
|
|
|
|
corners.forEach(corner => {
|
|
|
ctx.fillRect(corner.x - handleSize / 2, corner.y - handleSize / 2, handleSize, handleSize);
|
|
|
ctx.strokeRect(corner.x - handleSize / 2, corner.y - handleSize / 2, handleSize, handleSize);
|
|
|
});
|
|
|
|
|
|
|
|
|
const edges = [
|
|
|
{ x: (x1 + x2) / 2, y: y1 },
|
|
|
{ x: (x1 + x2) / 2, y: y2 },
|
|
|
{ x: x1, y: (y1 + y2) / 2 },
|
|
|
{ x: x2, y: (y1 + y2) / 2 }
|
|
|
];
|
|
|
|
|
|
edges.forEach(edge => {
|
|
|
ctx.fillRect(edge.x - handleSize / 2, edge.y - handleSize / 2, handleSize, handleSize);
|
|
|
ctx.strokeRect(edge.x - handleSize / 2, edge.y - handleSize / 2, handleSize, handleSize);
|
|
|
});
|
|
|
|
|
|
|
|
|
const centerX = (x1 + x2) / 2;
|
|
|
const centerY = (y1 + y2) / 2;
|
|
|
const centerRadius = isTouchDevice ? 20 : 15;
|
|
|
ctx.fillStyle = 'rgba(0, 184, 212, 0.3)';
|
|
|
ctx.beginPath();
|
|
|
ctx.arc(centerX, centerY, centerRadius, 0, Math.PI * 2);
|
|
|
ctx.fill();
|
|
|
|
|
|
|
|
|
ctx.strokeStyle = 'white';
|
|
|
ctx.lineWidth = 2;
|
|
|
const crosshairSize = isTouchDevice ? 10 : 8;
|
|
|
ctx.beginPath();
|
|
|
ctx.moveTo(centerX - crosshairSize, centerY);
|
|
|
ctx.lineTo(centerX + crosshairSize, centerY);
|
|
|
ctx.moveTo(centerX, centerY - crosshairSize);
|
|
|
ctx.lineTo(centerX, centerY + crosshairSize);
|
|
|
ctx.stroke();
|
|
|
}
|
|
|
|
|
|
function setupCanvasEventListeners(canvas) {
|
|
|
|
|
|
cropState.canvas = canvas;
|
|
|
|
|
|
|
|
|
canvas.addEventListener('mousedown', handleMouseDown);
|
|
|
canvas.addEventListener('mousemove', handleMouseMove);
|
|
|
canvas.addEventListener('mouseup', handleMouseUp);
|
|
|
canvas.addEventListener('mouseleave', handleMouseUp);
|
|
|
|
|
|
|
|
|
canvas.addEventListener('touchstart', handleTouchStart, { passive: false });
|
|
|
canvas.addEventListener('touchmove', handleTouchMove, { passive: false });
|
|
|
canvas.addEventListener('touchend', handleTouchEnd, { passive: false });
|
|
|
canvas.addEventListener('touchcancel', handleTouchEnd, { passive: false });
|
|
|
}
|
|
|
|
|
|
function handleMouseDown(e) {
|
|
|
const canvas = e.target;
|
|
|
const rect = canvas.getBoundingClientRect();
|
|
|
const x = e.clientX - rect.left;
|
|
|
const y = e.clientY - rect.top;
|
|
|
|
|
|
|
|
|
const handle = getResizeHandle(x, y);
|
|
|
if (handle) {
|
|
|
cropState.isResizing = true;
|
|
|
cropState.resizeHandle = handle;
|
|
|
cropState.dragStartX = x;
|
|
|
cropState.dragStartY = y;
|
|
|
e.preventDefault();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (isInsideCropBox(x, y)) {
|
|
|
cropState.isDragging = true;
|
|
|
cropState.dragStartX = x;
|
|
|
cropState.dragStartY = y;
|
|
|
e.preventDefault();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function handleMouseMove(e) {
|
|
|
const canvas = e.target;
|
|
|
const rect = canvas.getBoundingClientRect();
|
|
|
const x = e.clientX - rect.left;
|
|
|
const y = e.clientY - rect.top;
|
|
|
|
|
|
|
|
|
if (!cropState.isDragging && !cropState.isResizing) {
|
|
|
const handle = getResizeHandle(x, y);
|
|
|
if (handle) {
|
|
|
canvas.style.cursor = getCursorStyle(handle);
|
|
|
} else if (isInsideCropBox(x, y)) {
|
|
|
canvas.style.cursor = 'move';
|
|
|
} else {
|
|
|
canvas.style.cursor = 'default';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (cropState.isResizing) {
|
|
|
handleResize(x, y);
|
|
|
drawCropInterface(canvas);
|
|
|
e.preventDefault();
|
|
|
} else if (cropState.isDragging) {
|
|
|
handleDrag(x, y);
|
|
|
drawCropInterface(canvas);
|
|
|
e.preventDefault();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function handleMouseUp(e) {
|
|
|
cropState.isDragging = false;
|
|
|
cropState.isResizing = false;
|
|
|
cropState.resizeHandle = null;
|
|
|
}
|
|
|
|
|
|
|
|
|
function handleTouchStart(e) {
|
|
|
|
|
|
if (e.touches.length !== 1) return;
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
const canvas = e.target;
|
|
|
const rect = canvas.getBoundingClientRect();
|
|
|
const touch = e.touches[0];
|
|
|
const x = touch.clientX - rect.left;
|
|
|
const y = touch.clientY - rect.top;
|
|
|
|
|
|
|
|
|
const handle = getResizeHandle(x, y, true);
|
|
|
if (handle) {
|
|
|
cropState.isResizing = true;
|
|
|
cropState.resizeHandle = handle;
|
|
|
cropState.dragStartX = x;
|
|
|
cropState.dragStartY = y;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (isInsideCropBox(x, y)) {
|
|
|
cropState.isDragging = true;
|
|
|
cropState.dragStartX = x;
|
|
|
cropState.dragStartY = y;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function handleTouchMove(e) {
|
|
|
|
|
|
if (e.touches.length !== 1) return;
|
|
|
|
|
|
|
|
|
if (cropState.isDragging || cropState.isResizing) {
|
|
|
e.preventDefault();
|
|
|
}
|
|
|
|
|
|
const canvas = e.target;
|
|
|
const rect = canvas.getBoundingClientRect();
|
|
|
const touch = e.touches[0];
|
|
|
const x = touch.clientX - rect.left;
|
|
|
const y = touch.clientY - rect.top;
|
|
|
|
|
|
if (cropState.isResizing) {
|
|
|
handleResize(x, y);
|
|
|
drawCropInterface(canvas);
|
|
|
} else if (cropState.isDragging) {
|
|
|
handleDrag(x, y);
|
|
|
drawCropInterface(canvas);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function handleTouchEnd(e) {
|
|
|
e.preventDefault();
|
|
|
cropState.isDragging = false;
|
|
|
cropState.isResizing = false;
|
|
|
cropState.resizeHandle = null;
|
|
|
}
|
|
|
|
|
|
function getResizeHandle(x, y, isTouch = false) {
|
|
|
|
|
|
const threshold = isTouch ? 25 : 15;
|
|
|
|
|
|
|
|
|
if (Math.abs(x - cropState.currentBox.x1) < threshold && Math.abs(y - cropState.currentBox.y1) < threshold) return 'nw';
|
|
|
if (Math.abs(x - cropState.currentBox.x2) < threshold && Math.abs(y - cropState.currentBox.y1) < threshold) return 'ne';
|
|
|
if (Math.abs(x - cropState.currentBox.x1) < threshold && Math.abs(y - cropState.currentBox.y2) < threshold) return 'sw';
|
|
|
if (Math.abs(x - cropState.currentBox.x2) < threshold && Math.abs(y - cropState.currentBox.y2) < threshold) return 'se';
|
|
|
|
|
|
|
|
|
const centerX = (cropState.currentBox.x1 + cropState.currentBox.x2) / 2;
|
|
|
const centerY = (cropState.currentBox.y1 + cropState.currentBox.y2) / 2;
|
|
|
|
|
|
if (Math.abs(x - centerX) < threshold && Math.abs(y - cropState.currentBox.y1) < threshold) return 'n';
|
|
|
if (Math.abs(x - centerX) < threshold && Math.abs(y - cropState.currentBox.y2) < threshold) return 's';
|
|
|
if (Math.abs(x - cropState.currentBox.x1) < threshold && Math.abs(y - centerY) < threshold) return 'w';
|
|
|
if (Math.abs(x - cropState.currentBox.x2) < threshold && Math.abs(y - centerY) < threshold) return 'e';
|
|
|
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
function getCursorStyle(handle) {
|
|
|
const cursors = {
|
|
|
'nw': 'nw-resize',
|
|
|
'ne': 'ne-resize',
|
|
|
'sw': 'sw-resize',
|
|
|
'se': 'se-resize',
|
|
|
'n': 'n-resize',
|
|
|
's': 's-resize',
|
|
|
'w': 'w-resize',
|
|
|
'e': 'e-resize'
|
|
|
};
|
|
|
return cursors[handle] || 'default';
|
|
|
}
|
|
|
|
|
|
function isInsideCropBox(x, y) {
|
|
|
return x >= cropState.currentBox.x1 && x <= cropState.currentBox.x2 &&
|
|
|
y >= cropState.currentBox.y1 && y <= cropState.currentBox.y2;
|
|
|
}
|
|
|
|
|
|
function handleResize(x, y) {
|
|
|
const minSize = 50;
|
|
|
|
|
|
switch (cropState.resizeHandle) {
|
|
|
case 'nw':
|
|
|
cropState.currentBox.x1 = Math.max(0, Math.min(x, cropState.currentBox.x2 - minSize));
|
|
|
cropState.currentBox.y1 = Math.max(0, Math.min(y, cropState.currentBox.y2 - minSize));
|
|
|
break;
|
|
|
case 'ne':
|
|
|
cropState.currentBox.x2 = Math.min(cropState.imageWidth, Math.max(x, cropState.currentBox.x1 + minSize));
|
|
|
cropState.currentBox.y1 = Math.max(0, Math.min(y, cropState.currentBox.y2 - minSize));
|
|
|
break;
|
|
|
case 'sw':
|
|
|
cropState.currentBox.x1 = Math.max(0, Math.min(x, cropState.currentBox.x2 - minSize));
|
|
|
cropState.currentBox.y2 = Math.min(cropState.imageHeight, Math.max(y, cropState.currentBox.y1 + minSize));
|
|
|
break;
|
|
|
case 'se':
|
|
|
cropState.currentBox.x2 = Math.min(cropState.imageWidth, Math.max(x, cropState.currentBox.x1 + minSize));
|
|
|
cropState.currentBox.y2 = Math.min(cropState.imageHeight, Math.max(y, cropState.currentBox.y1 + minSize));
|
|
|
break;
|
|
|
case 'n':
|
|
|
cropState.currentBox.y1 = Math.max(0, Math.min(y, cropState.currentBox.y2 - minSize));
|
|
|
break;
|
|
|
case 's':
|
|
|
cropState.currentBox.y2 = Math.min(cropState.imageHeight, Math.max(y, cropState.currentBox.y1 + minSize));
|
|
|
break;
|
|
|
case 'w':
|
|
|
cropState.currentBox.x1 = Math.max(0, Math.min(x, cropState.currentBox.x2 - minSize));
|
|
|
break;
|
|
|
case 'e':
|
|
|
cropState.currentBox.x2 = Math.min(cropState.imageWidth, Math.max(x, cropState.currentBox.x1 + minSize));
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function handleDrag(x, y) {
|
|
|
const dx = x - cropState.dragStartX;
|
|
|
const dy = y - cropState.dragStartY;
|
|
|
|
|
|
const boxWidth = cropState.currentBox.x2 - cropState.currentBox.x1;
|
|
|
const boxHeight = cropState.currentBox.y2 - cropState.currentBox.y1;
|
|
|
|
|
|
let newX1 = cropState.currentBox.x1 + dx;
|
|
|
let newY1 = cropState.currentBox.y1 + dy;
|
|
|
|
|
|
|
|
|
if (newX1 < 0) newX1 = 0;
|
|
|
if (newY1 < 0) newY1 = 0;
|
|
|
if (newX1 + boxWidth > cropState.imageWidth) newX1 = cropState.imageWidth - boxWidth;
|
|
|
if (newY1 + boxHeight > cropState.imageHeight) newY1 = cropState.imageHeight - boxHeight;
|
|
|
|
|
|
cropState.currentBox.x1 = newX1;
|
|
|
cropState.currentBox.y1 = newY1;
|
|
|
cropState.currentBox.x2 = newX1 + boxWidth;
|
|
|
cropState.currentBox.y2 = newY1 + boxHeight;
|
|
|
|
|
|
cropState.dragStartX = x;
|
|
|
cropState.dragStartY = y;
|
|
|
}
|
|
|
|
|
|
function resetCropBox() {
|
|
|
if (!cropState.originalBox || !cropState.scale) return;
|
|
|
|
|
|
const canvas = document.getElementById('manualCropCanvas');
|
|
|
if (!canvas) return;
|
|
|
|
|
|
|
|
|
cropState.currentBox = {
|
|
|
x1: cropState.originalBox.x1 * cropState.scale,
|
|
|
y1: cropState.originalBox.y1 * cropState.scale,
|
|
|
x2: cropState.originalBox.x2 * cropState.scale,
|
|
|
y2: cropState.originalBox.y2 * cropState.scale
|
|
|
};
|
|
|
|
|
|
drawCropInterface(canvas);
|
|
|
}
|
|
|
|
|
|
async function applyCropSettings() {
|
|
|
if (appState.currentEmbryoIndex === undefined || !cropState.currentBox) return;
|
|
|
|
|
|
const embryo = appState.croppedEmbryos[appState.currentEmbryoIndex];
|
|
|
if (!embryo) return;
|
|
|
|
|
|
|
|
|
const img = await loadImage(appState.currentImage);
|
|
|
|
|
|
|
|
|
const scaleX = img.width / cropState.imageWidth;
|
|
|
const scaleY = img.height / cropState.imageHeight;
|
|
|
|
|
|
const x1 = cropState.currentBox.x1 * scaleX;
|
|
|
const y1 = cropState.currentBox.y1 * scaleY;
|
|
|
const x2 = cropState.currentBox.x2 * scaleX;
|
|
|
const y2 = cropState.currentBox.y2 * scaleY;
|
|
|
|
|
|
|
|
|
const canvas = document.createElement('canvas');
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
canvas.width = x2 - x1;
|
|
|
canvas.height = y2 - y1;
|
|
|
|
|
|
ctx.drawImage(img, x1, y1, x2 - x1, y2 - y1, 0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
const croppedImageData = canvas.toDataURL();
|
|
|
|
|
|
|
|
|
embryo.imageData = croppedImageData;
|
|
|
|
|
|
|
|
|
embryo.box = {
|
|
|
x1: x1,
|
|
|
y1: y1,
|
|
|
x2: x2,
|
|
|
y2: y2
|
|
|
};
|
|
|
|
|
|
|
|
|
const croppedImage = document.getElementById('croppedImage');
|
|
|
if (croppedImage) {
|
|
|
croppedImage.src = croppedImageData;
|
|
|
croppedImage.style.display = 'block';
|
|
|
}
|
|
|
|
|
|
|
|
|
updateThumbnail(appState.currentEmbryoIndex, croppedImageData);
|
|
|
}
|
|
|
|
|
|
function updateThumbnail(index, imageData) {
|
|
|
const thumbnail = document.querySelector(`[data-embryo-index="${index}"] img`);
|
|
|
if (thumbnail) {
|
|
|
thumbnail.src = imageData;
|
|
|
}
|
|
|
}
|
|
|
|