Spaces:
Running
Running
File size: 6,986 Bytes
5f07a23 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
// Get the correct coordinates based on canvas scaling
export const getCoordinates = (e, canvas) => {
const rect = canvas.getBoundingClientRect();
// Calculate the scaling factor between the internal canvas size and displayed size
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
// Apply the scaling to get accurate coordinates
return {
x: (e.nativeEvent.offsetX || (e.nativeEvent.touches?.[0]?.clientX - rect.left)) * scaleX,
y: (e.nativeEvent.offsetY || (e.nativeEvent.touches?.[0]?.clientY - rect.top)) * scaleY
};
};
// Initialize canvas with white background
export const initializeCanvas = (canvas) => {
const ctx = canvas.getContext("2d");
// Fill canvas with white background
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
};
// Draw the background image to the canvas
export const drawImageToCanvas = (canvas, backgroundImage) => {
if (!canvas || !backgroundImage) return;
const ctx = canvas.getContext("2d");
// Fill with white background first
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw the background image
ctx.drawImage(
backgroundImage,
0, 0,
canvas.width, canvas.height
);
};
// Draw bezier curve
export const drawBezierCurve = (canvas, points) => {
const ctx = canvas.getContext('2d');
if (!points || points.length < 2) {
console.error('Need at least 2 points to draw a path');
return;
}
ctx.beginPath();
ctx.strokeStyle = '#000000';
ctx.lineWidth = 4;
// Start at the first anchor point
ctx.moveTo(points[0].x, points[0].y);
// For each pair of anchor points (and their control points)
for (let i = 0; i < points.length - 1; i++) {
const current = points[i];
const next = points[i + 1];
if (current.handleOut && next.handleIn) {
// If both points have handles, draw a cubic bezier
ctx.bezierCurveTo(
current.x + (current.handleOut?.x || 0), current.y + (current.handleOut?.y || 0),
next.x + (next.handleIn?.x || 0), next.y + (next.handleIn?.y || 0),
next.x, next.y
);
} else {
// If no handles, draw a straight line
ctx.lineTo(next.x, next.y);
}
}
ctx.stroke();
};
// Draw bezier guides (control points and lines)
export const drawBezierGuides = (ctx, points) => {
if (!points || points.length === 0) return;
// Draw the path itself first (as a light preview)
ctx.save();
ctx.globalAlpha = 0.3;
ctx.strokeStyle = '#888888';
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
// For each pair of anchor points (and their control points)
for (let i = 0; i < points.length - 1; i++) {
const current = points[i];
const next = points[i + 1];
if (current.handleOut && next.handleIn) {
// If both points have handles, draw a cubic bezier
ctx.bezierCurveTo(
current.x + (current.handleOut?.x || 0), current.y + (current.handleOut?.y || 0),
next.x + (next.handleIn?.x || 0), next.y + (next.handleIn?.y || 0),
next.x, next.y
);
} else {
// If no handles, draw a straight line
ctx.lineTo(next.x, next.y);
}
}
ctx.stroke();
ctx.restore();
// Draw guide lines between anchor points and their handles
ctx.strokeStyle = 'rgba(100, 100, 255, 0.5)';
ctx.lineWidth = 1;
for (const point of points) {
// Draw line from anchor to in-handle if it exists
if (point.handleIn) {
ctx.beginPath();
ctx.moveTo(point.x, point.y);
ctx.lineTo(point.x + point.handleIn.x, point.y + point.handleIn.y);
ctx.stroke();
}
// Draw line from anchor to out-handle if it exists
if (point.handleOut) {
ctx.beginPath();
ctx.moveTo(point.x, point.y);
ctx.lineTo(point.x + point.handleOut.x, point.y + point.handleOut.y);
ctx.stroke();
}
}
// Draw anchor points (main points of the path)
for (const point of points) {
// Draw the main anchor point
ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
ctx.strokeStyle = 'rgba(0, 0, 0, 0.8)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.arc(point.x, point.y, 5, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Draw the handle points if they exist
if (point.handleIn) {
ctx.fillStyle = 'rgba(100, 100, 255, 0.8)';
ctx.beginPath();
ctx.arc(point.x + point.handleIn.x, point.y + point.handleIn.y, 4, 0, Math.PI * 2);
ctx.fill();
}
if (point.handleOut) {
ctx.fillStyle = 'rgba(100, 100, 255, 0.8)';
ctx.beginPath();
ctx.arc(point.x + point.handleOut.x, point.y + point.handleOut.y, 4, 0, Math.PI * 2);
ctx.fill();
}
}
};
// Helper to create a new anchor point with handles
export const createAnchorPoint = (x, y, prevPoint = null) => {
// By default, create a point with no handles
const point = { x, y, handleIn: null, handleOut: null };
// If there's a previous point, automatically add symmetric handles
if (prevPoint) {
// Calculate the default handle length (as a percentage of distance to previous point)
const dx = x - prevPoint.x;
const dy = y - prevPoint.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const handleLength = distance * 0.3; // 30% of distance between points
// Create handles perpendicular to the line between points
// For a smooth curve, make the previous point's out handle opposite to this point's in handle
const angle = Math.atan2(dy, dx);
// Add an out handle to the previous point (if it doesn't already have one)
if (!prevPoint.handleOut) {
prevPoint.handleOut = {
x: Math.cos(angle) * -handleLength,
y: Math.sin(angle) * -handleLength
};
}
// Add an in handle to the current point
point.handleIn = {
x: Math.cos(angle) * -handleLength,
y: Math.sin(angle) * -handleLength
};
}
return point;
};
// Helper to check if a point is near a handle
export const isNearHandle = (point, handleType, x, y, radius = 10) => {
if (!point || !point[handleType]) return false;
const handleX = point.x + point[handleType].x;
const handleY = point.y + point[handleType].y;
const dx = handleX - x;
const dy = handleY - y;
return (dx * dx + dy * dy) <= radius * radius;
};
// Helper to update a handle position
export const updateHandle = (point, handleType, dx, dy, symmetric = true) => {
if (!point || !point[handleType]) return;
// Update the target handle
point[handleType].x += dx;
point[handleType].y += dy;
// If symmetric and the other handle exists, update it to be symmetrical
if (symmetric) {
const otherType = handleType === 'handleIn' ? 'handleOut' : 'handleIn';
if (point[otherType]) {
point[otherType].x = -point[handleType].x;
point[otherType].y = -point[handleType].y;
}
}
}; |