You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Creates a diagram illustrating either asexual or sexual reproduction using a source image.
* The diagram visually represents the concept by showing parent(s) and offspring.
* For sexual reproduction, it depicts two distinct parents producing a blended offspring.
* For asexual reproduction, it shows a single parent producing an identical offspring.
*
* @param {HTMLImageElement} originalImg The source image object to represent the parent organism(s).
* @param {string} [reproductionType='sexual'] The type of reproduction to illustrate. Can be 'sexual' or 'asexual'. The default is 'sexual' to visually answer the question implied in the tool's description.
* @param {string} [textColor='black'] The hexadecimal or named color for text labels and arrows.
* @param {string} [backgroundColor='white'] The hexadecimal or named color for the background of the diagram.
* @returns {Promise<HTMLCanvasElement>} A promise that resolves with a canvas element containing the generated diagram.
*/
async function processImage(originalImg, reproductionType = 'sexual', textColor = 'black', backgroundColor = 'white') {
// Sanitize input: ensure reproductionType is a valid string, otherwise default to 'sexual'.
reproductionType = typeof reproductionType === 'string' ? reproductionType.toLowerCase() : 'sexual';
if (reproductionType !== 'sexual' && reproductionType !== 'asexual') {
reproductionType = 'sexual';
}
const w = originalImg.width;
const h = originalImg.height;
const mainCanvas = document.createElement('canvas');
const ctx = mainCanvas.getContext('2d');
const fontName = 'Arial'; // Use a web-safe font
/**
* Helper function to draw an arrow from one point to another on a canvas context.
* @param {CanvasRenderingContext2D} context The canvas context to draw on.
* @param {number} fromx The starting x-coordinate.
* @param {number} fromy The starting y-coordinate.
* @param {number} tox The ending x-coordinate.
* @param {number} toy The ending y-coordinate.
*/
const drawArrow = (context, fromx, fromy, tox, toy) => {
const headlen = 10; // length of head in pixels
const dx = tox - fromx;
const dy = toy - fromy;
const angle = Math.atan2(dy, dx);
context.beginPath();
context.moveTo(fromx, fromy);
context.lineTo(tox, toy);
context.lineTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
context.moveTo(tox, toy);
context.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
context.strokeStyle = textColor;
context.lineWidth = 2;
context.stroke();
};
if (reproductionType === 'sexual') {
/****************************************
* SEXUAL REPRODUCTION VISUALIZATION *
****************************************/
// Layout constants
const parentScale = 0.6;
const offspringScale = 0.45;
const pW = w * parentScale;
const pH = h * parentScale;
const oW = w * offspringScale;
const oH = h * offspringScale;
const hSpacing = 50;
const vSpacing = 40;
const titleHeight = 50;
const labelHeight = 40;
const padding = 30;
// Set canvas dimensions
mainCanvas.width = pW * 2 + hSpacing + padding * 2;
mainCanvas.height = titleHeight + pH + vSpacing + oH + labelHeight + padding;
// Fill background
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, mainCanvas.width, mainCanvas.height);
// Create Parent 2 (color-shifted version) in an offscreen canvas
const parent2Canvas = document.createElement('canvas');
parent2Canvas.width = w;
parent2Canvas.height = h;
const p2Ctx = parent2Canvas.getContext('2d');
p2Ctx.filter = 'hue-rotate(120deg)'; // Apply a filter to differentiate from Parent 1
p2Ctx.drawImage(originalImg, 0, 0);
// Create Offspring (blended version) in an offscreen canvas
const offspringCanvas = document.createElement('canvas');
offspringCanvas.width = w;
offspringCanvas.height = h;
const offCtx = offspringCanvas.getContext('2d');
offCtx.globalAlpha = 0.5; // Set transparency for blending
offCtx.drawImage(originalImg, 0, 0); // Draw Parent 1
offCtx.drawImage(parent2Canvas, 0, 0); // Draw Parent 2 on top
offCtx.globalAlpha = 1.0; // Reset transparency
// --- Draw all elements onto the main canvas ---
ctx.fillStyle = textColor;
ctx.textAlign = 'center';
// Draw Title
ctx.font = `bold 22px ${fontName}`;
ctx.fillText('Sexual Reproduction', mainCanvas.width / 2, padding + 15);
// Draw Parents
const p1x = padding;
const p1y = titleHeight;
const p2x = p1x + pW + hSpacing;
const p2y = p1y;
ctx.drawImage(originalImg, p1x, p1y, pW, pH);
ctx.drawImage(parent2Canvas, p2x, p2y, pW, pH);
// Draw Offspring
const ox = mainCanvas.width / 2 - oW / 2;
const oy = p1y + pH + vSpacing;
ctx.drawImage(offspringCanvas, ox, oy, oW, oH);
// Draw Labels
ctx.font = `18px ${fontName}`;
ctx.fillText('Parent 1', p1x + pW / 2, p1y + pH + 25);
ctx.fillText('Parent 2', p2x + pW / 2, p2y + pH + 25);
ctx.fillText('Offspring', ox + oW / 2, oy + oH + 25);
// Draw Arrows from parents to offspring
drawArrow(ctx, p1x + pW / 2, p1y + pH, ox + oW * 0.25, oy);
drawArrow(ctx, p2x + pW / 2, p2y + pH, ox + oW * 0.75, oy);
} else { // 'asexual'
/****************************************
* ASEXUAL REPRODUCTION VISUALIZATION *
****************************************/
// Layout constants
const parentScale = 0.6;
const offspringScale = 0.45;
const pW = w * parentScale;
const pH = h * parentScale;
const oW = w * offspringScale;
const oH = h * offspringScale;
const hSpacing = 40;
const titleHeight = 50;
const labelHeight = 40;
const padding = 30;
// Set canvas dimensions
mainCanvas.width = pW + hSpacing + oW + padding * 2;
mainCanvas.height = titleHeight + pH + labelHeight + padding;
// Fill background
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, mainCanvas.width, mainCanvas.height);
// --- Draw all elements onto the main canvas ---
ctx.fillStyle = textColor;
ctx.textAlign = 'center';
// Draw Title
ctx.font = `bold 22px ${fontName}`;
ctx.fillText('Asexual Reproduction', mainCanvas.width / 2, padding + 15);
// Positions for Parent and Offspring
const imageRowY = titleHeight;
const pX = padding;
const pY = imageRowY;
const oX = pX + pW + hSpacing;
const oY = pY + (pH - oH) / 2; // Vertically center the smaller offspring
// Draw Parent
ctx.drawImage(originalImg, pX, pY, pW, pH);
// Draw Offspring (a smaller, identical copy)
ctx.drawImage(originalImg, oX, oY, oW, oH);
// Draw Labels
ctx.font = `18px ${fontName}`;
const labelY = pY + pH + 25;
ctx.fillText('Parent', pX + pW / 2, labelY);
ctx.fillText('Offspring', oX + oW / 2, labelY);
// Draw Arrow from parent to offspring
drawArrow(ctx, pX + pW, pY + pH / 2, oX, oY + oH / 2);
}
return mainCanvas;
}
Apply Changes