1
0
Files
VR-Web-Player/src/vr180player/rendering/three-utils.ts
2026-06-10 12:48:36 +10:00

118 lines
3.4 KiB
TypeScript

import * as THREE from 'https://unpkg.com/three/build/three.module.js';
import { drawLucideIcon, type LucideIconName } from '../dom/icons.js';
type Radius = number | {
tl?: number;
tr?: number;
br?: number;
bl?: number;
};
export function drawRoundedRect(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
width: number,
height: number,
radius: Radius = 5,
fill: boolean | string,
stroke: boolean | string = true
): void {
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 createLucideButtonTexture(
iconName: LucideIconName,
color = '#ffffff',
textureSize = 128,
iconSize = 82,
skipLabel?: string
) {
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 Lucide button texture.');
}
const iconOffset = (textureSize - iconSize) / 2;
drawLucideIcon(ctx, iconName, iconOffset, iconOffset, iconSize, color, 2);
if (skipLabel) {
ctx.fillStyle = color;
ctx.font = `700 ${Math.round(textureSize * 0.18)}px Helvetica, Arial, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(skipLabel, textureSize / 2, textureSize / 2);
}
const texture = new THREE.CanvasTexture(canvas);
texture.minFilter = THREE.LinearFilter;
texture.needsUpdate = true;
return texture;
}
export function createVideoTexture(video: HTMLVideoElement) {
const texture = new THREE.VideoTexture(video);
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.colorSpace = THREE.SRGBColorSpace;
return texture;
}
export function createImageTexture(image: HTMLImageElement) {
const texture = new THREE.Texture(image);
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.colorSpace = THREE.SRGBColorSpace;
texture.needsUpdate = true;
return texture;
}
export function createMediaTexture(source: HTMLImageElement | HTMLVideoElement) {
if (source.tagName.toLowerCase() === 'img') {
return createImageTexture(source as HTMLImageElement);
}
return createVideoTexture(source as HTMLVideoElement);
}