import * as THREE from 'https://unpkg.com/three/build/three.module.js'; export function drawRoundedRect(ctx, x, y, width, height, radius = 5, fill, stroke = true) { let corners; if (typeof radius === 'number') { corners = { tl: radius, tr: radius, br: radius, bl: radius }; } else { corners = { tl: 0, tr: 0, br: 0, bl: 0, ...radius }; } if (width < 2 * corners.tl) corners.tl = width / 2; if (width < 2 * corners.tr) corners.tr = width / 2; if (width < 2 * corners.bl) corners.bl = width / 2; if (width < 2 * corners.br) corners.br = width / 2; if (height < 2 * corners.tl) corners.tl = height / 2; if (height < 2 * corners.tr) corners.tr = height / 2; if (height < 2 * corners.bl) corners.bl = height / 2; if (height < 2 * corners.br) corners.br = height / 2; ctx.beginPath(); ctx.moveTo(x + corners.tl, y); ctx.lineTo(x + width - corners.tr, y); ctx.quadraticCurveTo(x + width, y, x + width, y + corners.tr); ctx.lineTo(x + width, y + height - corners.br); ctx.quadraticCurveTo(x + width, y + height, x + width - corners.br, y + height); ctx.lineTo(x + corners.bl, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - corners.bl); ctx.lineTo(x, y + corners.tl); ctx.quadraticCurveTo(x, y, x + corners.tl, y); ctx.closePath(); if (fill) { if (typeof fill === 'string') ctx.fillStyle = fill; ctx.fill(); } if (stroke) { if (typeof stroke === 'string') ctx.strokeStyle = stroke; ctx.stroke(); } } export function createButtonTexture(textOrPathData, textColor = 'white', backgroundColor = 'rgba(0,0,0,0)', textureSize = 128, fontSize = 48, isSvgPath = false, svgViewBoxSize = 44) { const canvas = document.createElement('canvas'); canvas.width = textureSize; canvas.height = textureSize; const ctx = canvas.getContext('2d'); if (!ctx) { throw new Error('Unable to create 2D canvas context for button texture.'); } if (backgroundColor !== 'rgba(0,0,0,0)') { ctx.fillStyle = backgroundColor; ctx.fillRect(0, 0, textureSize, textureSize); } ctx.fillStyle = textColor; if (isSvgPath) { const path = new Path2D(textOrPathData); const iconTargetSize = textureSize; const scale = iconTargetSize / svgViewBoxSize; const offsetX = (textureSize - (svgViewBoxSize * scale)) / 2; const offsetY = (textureSize - (svgViewBoxSize * scale)) / 2; ctx.save(); ctx.translate(offsetX, offsetY); ctx.scale(scale, scale); ctx.fill(path); ctx.restore(); } else { ctx.font = `bold ${fontSize}px Helvetica, Arial, sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(textOrPathData, textureSize / 2, textureSize / 2); } const texture = new THREE.CanvasTexture(canvas); texture.minFilter = THREE.LinearFilter; texture.needsUpdate = true; return texture; } export function createVideoTexture(video) { const texture = new THREE.VideoTexture(video); texture.minFilter = THREE.LinearFilter; texture.magFilter = THREE.LinearFilter; texture.colorSpace = THREE.SRGBColorSpace; return texture; }