You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* From an image, generates a sewing pattern for a simple pet coat with specified measurements.
* The pattern includes the user's image as a texture, diamond grid lines for quilting,
* and annotations for construction.
*
* @param {Image} originalImg - The javascript Image object to use as the fabric pattern.
* @param {number} chest - The chest girth measurement in inches. Default is 27.5".
* @param {number} neck - The neck circumference measurement in inches. Default is 16.5".
* @param {number} backLength - The length from collar to tail in inches. Default is 16".
* @param {number} gridSize - The size of the diamond grid lines in inches. Default is 2".
* @returns {HTMLCanvasElement} A canvas element displaying the generated sewing pattern.
*/
async function processImage(originalImg, chest = 27.5, neck = 16.5, backLength = 16, gridSize = 2) {
// A constant to convert inches to pixels for drawing. Increase for higher print resolution.
const scale = 30; // 30 pixels per inch
const padding = 3 * scale; // 3-inch padding around the pattern
// Convert all inch measurements to pixel dimensions
const back_px = backLength * scale;
const chest_half_px = (chest / 2) * scale;
// A reasonable approximation for the flat pattern neck opening is ~1/3 of the circumference
const neck_opening_width_px = (neck / 3) * scale;
// Taper the pattern slightly towards the tail for a better fit
const tail_half_px = chest_half_px * 0.85;
// Calculate overall canvas dimensions
const canvasWidth = chest_half_px * 2 + padding * 2;
const canvasHeight = back_px + padding * 2;
// Create and setup the canvas
const canvas = document.createElement('canvas');
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// --- Define key coordinates for the pattern shape ---
const top_y = padding;
const chest_level_y = padding + back_px * 0.3; // Y-coordinate for the widest chest part
const tail_y = padding + back_px;
const center_x = canvas.width / 2;
const p_neck_l = { x: center_x - neck_opening_width_px / 2, y: top_y };
const p_neck_r = { x: center_x + neck_opening_width_px / 2, y: top_y };
const p_chest_l = { x: center_x - chest_half_px, y: chest_level_y };
const p_chest_r = { x: center_x + chest_half_px, y: chest_level_y };
const p_tail_l = { x: center_x - tail_half_px, y: tail_y };
const p_tail_r = { x: center_x + tail_half_px, y: tail_y };
// --- Draw the main pattern piece path using smooth curves ---
ctx.beginPath();
ctx.moveTo(p_neck_l.x, p_neck_l.y); // Start at top-left neck
// Left side curve from neck, out to chest, and down to tail
ctx.bezierCurveTo(
p_neck_l.x, chest_level_y, // Control point 1 (shapes shoulder)
p_chest_l.x, chest_level_y, // Control point 2 (defines chest width)
p_tail_l.x, p_tail_l.y // End point at tail
);
// Rounded Tail
ctx.quadraticCurveTo(center_x, tail_y + 1.5 * scale, p_tail_r.x, p_tail_r.y);
// Right side curve (symmetric to the left side)
ctx.bezierCurveTo(
p_chest_r.x, chest_level_y, // Control point 1 (mirror)
p_neck_r.x, chest_level_y, // Control point 2 (mirror)
p_neck_r.x, p_neck_r.y // End point at neck
);
// Scooped neckline
ctx.quadraticCurveTo(center_x, top_y + (neck / 5) * scale, p_neck_l.x, p_neck_l.y);
ctx.closePath();
// --- Fill the pattern and draw grid lines within it ---
ctx.save();
ctx.clip(); // Restrict all subsequent drawing to within the path
// Fill with the provided image, tiled
const imagePattern = ctx.createPattern(originalImg, 'repeat');
if (imagePattern) {
ctx.fillStyle = imagePattern;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// Draw the diamond grid
const grid_step_px = gridSize * scale;
ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)'; // Semi-transparent white for visibility
ctx.lineWidth = 1;
ctx.beginPath();
// Lines from top-left to bottom-right (\)
for (let i = -canvas.height; i < canvas.width; i += grid_step_px) {
ctx.moveTo(i, 0);
ctx.lineTo(i + canvas.height, canvas.height);
}
// Lines from top-right to bottom-left (/)
for (let i = 0; i < canvas.width + canvas.height; i += grid_step_px) {
ctx.moveTo(i, 0);
ctx.lineTo(i - canvas.height, canvas.height);
}
ctx.stroke();
ctx.restore(); // Stop clipping
// --- Stroke the final pattern outline ---
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
// --- Add Annotations ---
// Grainline Arrow indicates fabric direction
const arrowLength = back_px * 0.6;
const arrowYStart = top_y + back_px * 0.2;
const arrowYEnd = arrowYStart + arrowLength;
ctx.beginPath();
ctx.moveTo(center_x, arrowYStart);
ctx.lineTo(center_x, arrowYEnd);
// Arrowhead top
ctx.moveTo(center_x, arrowYStart);
ctx.lineTo(center_x - 0.2 * scale, arrowYStart + 0.4 * scale);
ctx.moveTo(center_x, arrowYStart);
ctx.lineTo(center_x + 0.2 * scale, arrowYStart + 0.4 * scale);
// Arrowhead bottom
ctx.moveTo(center_x, arrowYEnd);
ctx.lineTo(center_x - 0.2 * scale, arrowYEnd - 0.4 * scale);
ctx.moveTo(center_x, arrowYEnd);
ctx.lineTo(center_x + 0.2 * scale, arrowYEnd - 0.4 * scale);
ctx.strokeStyle = '#333333';
ctx.setLineDash([5, 3]);
ctx.stroke();
ctx.setLineDash([]);
// Information Text
const textOriginX = padding / 2;
let textY = padding / 2;
ctx.fillStyle = 'black';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.font = `bold ${0.6 * scale}px sans-serif`;
ctx.fillText(`Custom Coat Pattern`, textOriginX, textY);
textY += 0.9 * scale;
ctx.font = `${0.5 * scale}px sans-serif`;
ctx.fillText(`Chest: ${chest}" Neck: ${neck}" Back: ${backLength}"`, textOriginX, textY);
textY += 0.7 * scale;
ctx.fillText(`Materials: Ripstop Shell, Fleece Lining`, textOriginX, textY);
textY += 0.7 * scale;
ctx.fillText(`Grid: ${gridSize}" Diamond`, textOriginX, textY);
textY += 1.0 * scale;
ctx.font = `italic ${0.4 * scale}px sans-serif`;
ctx.fillText(`* seam allowance not included. Cut 1 Shell, Cut 1 Lining.`, textOriginX, textY);
// Scale Box for printing verification
const scaleBoxSize = 1 * scale;
const scaleBoxX = canvasWidth - padding / 2 - scaleBoxSize;
const scaleBoxY = padding / 2;
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.strokeRect(scaleBoxX, scaleBoxY, scaleBoxSize, scaleBoxSize);
ctx.font = `${0.4 * scale}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('1" x 1"', scaleBoxX + scaleBoxSize / 2, scaleBoxY + scaleBoxSize / 2);
return canvas;
}
Apply Changes