import { Drawing } from '../lib/image.js' export function render (x: number, y: number, drawing: Drawing, canvas: HTMLCanvasElement) { const ctx = canvas.getContext('2d')! switch (drawing.tag) { case 'ellipse': { ctx.fillStyle = drawing.color ctx.strokeStyle = drawing.color const radiusX = drawing.width / 2 const radiusY = drawing.height / 2 const centerX = x + radiusX const centerY = y + radiusY ctx.beginPath() ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI) if (drawing.mode === 'solid') { ctx.fill() } else if (drawing.mode === 'outline') { ctx.stroke() } break } case 'rectangle': { ctx.fillStyle = drawing.color ctx.strokeStyle = drawing.color if (drawing.mode === 'solid') { ctx.fillRect(x, y, drawing.width, drawing.height) } else if (drawing.mode === 'outline') { ctx.strokeRect(x, y, drawing.width, drawing.height) } break } case 'triangle': { ctx.fillStyle = drawing.color ctx.strokeStyle = drawing.color ctx.beginPath() // Start in the bottom-left corner of the triangle... ctx.moveTo(x, y + drawing.height) // Then go to the top corner... ctx.lineTo(x + drawing.width / 2, y) // And then the bottom-right corner... ctx.lineTo(x + drawing.width, y + drawing.height) // And back! ctx.lineTo(x, y + drawing.height) if (drawing.mode === 'solid') { ctx.fill() } else if (drawing.mode === 'outline') { ctx.stroke() } break } case 'path': { if (drawing.points.length === 0) { break } ctx.fillStyle = drawing.color ctx.strokeStyle = drawing.color ctx.beginPath() ctx.moveTo(x + drawing.points[0][0], y + drawing.points[0][1]) drawing.points.slice(1).forEach(p => { ctx.lineTo(x + p[0], y + p[1]) }) if (drawing.mode === 'solid') { ctx.fill() } else { ctx.stroke() } break } case 'beside': { drawing.drawings.forEach(d => { render( x, drawing.align === 'top' ? y : drawing.align === 'bottom' ? y + drawing.height - d.height // N.B., assumed to be 'center' : y + (drawing.height - d.height) / 2, d, canvas) x += d.width }) break } case 'above': { drawing.drawings.forEach(d => { render( drawing.align === 'left' ? x : drawing.align === 'right' ? x + drawing.width - d.width // N.B., assumed to be 'middle' : x + (drawing.width - d.width) / 2, y, d, canvas) y += d.height }) break } case 'overlay': { // N.B., need to draw in reverse order to get the overlay effect to work [...drawing.drawings].reverse().forEach(d => { render( drawing.xAlign === 'left' ? x : drawing.xAlign === 'right' ? x + drawing.width - d.width // N.B., assumed to be 'middle' : x + (drawing.width - d.width) / 2, drawing.yAlign === 'top' ? y : drawing.yAlign === 'bottom' ? y + drawing.height - d.height // N.B., assumed to be 'center' : y + (drawing.height - d.height) / 2, d, canvas) }) break } case 'overlayOffset': { const x1 = drawing.dx > 0 ? x : x + Math.abs(drawing.dx) const y1 = drawing.dy > 0 ? y : y + Math.abs(drawing.dy) const x2 = drawing.dx > 0 ? x + drawing.dx : x const y2 = drawing.dy > 0 ? y + drawing.dy : y // N.B., render d2 first so d1 is on top render(x2, y2, drawing.d2, canvas) render(x1, y1, drawing.d1, canvas) break } case 'rotate': { const centerX = x + drawing.width / 2 const centerY = y + drawing.height / 2 const angle = drawing.angle * Math.PI / 180 // N.B., need to move the canvas from the origin to the // center of the drawing to rotate and then move back to // the origin. ctx.translate(centerX, centerY) ctx.rotate(angle) ctx.translate(-centerX, -centerY) render(x, y, drawing.drawing, canvas) ctx.translate(centerX, centerY) ctx.rotate(-angle) ctx.translate(-centerX, -centerY) break } case 'withDash': { ctx.setLineDash(drawing.dashSpec) render(x, y, drawing.drawing, canvas) ctx.setLineDash([]) } } } function clearDrawing (canvas: HTMLCanvasElement) { const ctx = canvas.getContext('2d')! ctx.fillStyle = 'white' ctx.strokeStyle = 'black' ctx.fillRect(0, 0, canvas.width, canvas.height) } export function renderer (obj: object): HTMLElement { const drawing = obj as Drawing const canvas = document.createElement('canvas') canvas.width = drawing.width canvas.height = drawing.height clearDrawing(canvas) render(0, 0, drawing, canvas) return canvas }