let canvas, ctx; let currentChannel = 'r'; let points = []; let isDragging = false; let draggedPointIndex = -1; function initCurveEditor() { canvas = document.getElementById('curveCanvas'); ctx = canvas.getContext('2d'); // 初期ポイントを設定 resetPoints(); drawCurve(); // イベントリスナーを設定 canvas.addEventListener('mousedown', handleMouseDown); canvas.addEventListener('mousemove', handleMouseMove); canvas.addEventListener('mouseup', handleMouseUp); canvas.addEventListener('mouseleave', handleMouseUp); // チャンネル選択 document.querySelectorAll('.channel h3').forEach(header => { header.addEventListener('click', () => { currentChannel = header.textContent.toLowerCase().charAt(0); if (currentChannel === '明') currentChannel = 'y'; resetPoints(); drawCurve(); }); }); } function resetPoints() { points = [ { x: 0, y: 0 }, { x: 64, y: parseInt(document.getElementById(`${currentChannel}1`).value) }, { x: 128, y: parseInt(document.getElementById(`${currentChannel}2`).value) }, { x: 192, y: parseInt(document.getElementById(`${currentChannel}3`).value) }, { x: 255, y: 255 } ]; } function drawCurve() { // キャンバスをクリア ctx.clearRect(0, 0, canvas.width, canvas.height); // グリッドを描画 drawGrid(); // 軸を描画 ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(0, canvas.height); ctx.lineTo(0, 0); ctx.lineTo(canvas.width, 0); ctx.stroke(); // カーブを描画 ctx.strokeStyle = getChannelColor(); ctx.lineWidth = 3; ctx.beginPath(); // ベジェ曲線用の制御点を計算 for (let i = 0; i < points.length; i++) { const x = (points[i].x / 255) * canvas.width; const y = canvas.height - (points[i].y / 255) * canvas.height; if (i === 0) { ctx.moveTo(x, y); } else { const prevX = (points[i-1].x / 255) * canvas.width; const prevY = canvas.height - (points[i-1].y / 255) * canvas.height; // 制御点は前後の点の中点 const ctrlX = (prevX + x) / 2; const ctrlY1 = prevY; const ctrlY2 = y; ctx.bezierCurveTo(ctrlX, ctrlY1, ctrlX, ctrlY2, x, y); } } ctx.stroke(); // ポイントを描画 points.forEach((point, index) => { if (index === 0 || index === points.length - 1) return; // 端点は描画しない const x = (point.x / 255) * canvas.width; const y = canvas.height - (point.y / 255) * canvas.height; ctx.fillStyle = getChannelColor(); ctx.beginPath(); ctx.arc(x, y, 6, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#fff'; ctx.lineWidth = 2; ctx.stroke(); }); } function drawGrid() { ctx.strokeStyle = '#ddd'; ctx.lineWidth = 1; // 縦線 for (let i = 0; i <= 10; i++) { const x = (i / 10) * canvas.width; ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); } // 横線 for (let i = 0; i <= 10; i++) { const y = (i / 10) * canvas.height; ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); } } function getChannelColor() { switch(currentChannel) { case 'r': return '#f00'; case 'g': return '#0f0'; case 'b': return '#00f'; case 'y': return '#ff0'; default: return '#000'; } } function handleMouseDown(e) { const rect = canvas.getBoundingClientRect(); const x = ((e.clientX - rect.left) / canvas.width) * 255; const y = 255 - ((e.clientY - rect.top) / canvas.height) * 255; // どのポイントがクリックされたかチェック for (let i = 1; i < points.length - 1; i++) { const dx = Math.abs(points[i].x - x); const dy = Math.abs(points[i].y - y); if (dx < 15 && dy < 15) { isDragging = true; draggedPointIndex = i; return; } } } function handleMouseMove(e) { if (!isDragging) return; const rect = canvas.getBoundingClientRect(); const x = ((e.clientX - rect.left) / canvas.width) * 255; const y = 255 - ((e.clientY - rect.top) / canvas.height) * 255; // X座標は固定、Y座標のみ変更可能 points[draggedPointIndex].y = Math.max(0, Math.min(255, Math.round(y))); // スライダーを更新 updateSlidersFromPoints(); drawCurve(); } function handleMouseUp() { isDragging = false; draggedPointIndex = -1; } function updateSlidersFromPoints() { for (let i = 1; i < points.length - 1; i++) { const slider = document.getElementById(`${currentChannel}${i}`); const valueSpan = slider.parentElement.querySelector('.slider-value'); slider.value = points[i].y; valueSpan.textContent = points[i].y; } } function setLinearCurve() { points = [ { x: 0, y: 0 }, { x: 64, y: 64 }, { x: 128, y: 128 }, { x: 192, y: 192 }, { x: 255, y: 255 } ]; updateSlidersFromPoints(); drawCurve(); } function setContrastCurve() { points = [ { x: 0, y: 0 }, { x: 64, y: 32 }, { x: 128, y: 128 }, { x: 192, y: 224 }, { x: 255, y: 255 } ]; updateSlidersFromPoints(); drawCurve(); } function setInverseCurve() { points = [ { x: 0, y: 255 }, { x: 64, y: 192 }, { x: 128, y: 128 }, { x: 192, y: 64 }, { x: 255, y: 0 } ]; updateSlidersFromPoints(); drawCurve(); } function updateCurve() { resetPoints(); drawCurve(); }