forked from EXT/VR180-Web-Player
118 lines
3.4 KiB
TypeScript
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);
|
|
}
|