Files
topos/src/API/DOM/Canvas.ts

420 lines
13 KiB
TypeScript

import { OscilloscopeConfig } from "../../DOM/Visuals/Oscilloscope";
import { ShapeObject, createConicGradient, createLinearGradient, createRadialGradient, drawBackground, drawBox, drawBall, drawBalloid, drawDonut, drawEquilateral, drawImage, drawPie, drawSmiley, drawStar, drawStroke, drawText, drawTriangular } from "../DOM/Visuals/CanvasVisuals";
import { Editor } from "../../main";
export const loadHydra = (app: Editor) => (): void => {
app.ensureHydraLoaded()
}
export const w = (app: Editor) => (): number => {
const canvas: HTMLCanvasElement = app.interface.feedback as HTMLCanvasElement;
return canvas.clientWidth;
};
export const pulseLocation = (app: Editor) => (): number => {
return ((app.api.epulse() / app.api.pulsesForBar()) * w(app)()) % w(app)();
};
export const clear = (app: Editor) => (): boolean => {
const canvas: HTMLCanvasElement = app.interface.feedback as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
ctx.clearRect(0, 0, canvas.width, canvas.height);
return true;
};
export const h = (app: Editor) => (): number => {
const canvas: HTMLCanvasElement = app.interface.feedback as HTMLCanvasElement;
return canvas.clientHeight;
};
export const hc = (app: Editor) => (): number => {
return h(app)() / 2;
};
export const wc = (app: Editor) => (): number => {
return w(app)() / 2;
};
export const background = (app: Editor) => (color: string | number, ...gb: number[]): boolean => {
drawBackground(app.interface.feedback as HTMLCanvasElement, color, ...gb);
return true;
};
export const bg = background;
export const linearGradient = (app: Editor) => (x1: number, y1: number, x2: number, y2: number, ...stops: (number | string)[]): CanvasGradient => {
return createLinearGradient(app.interface.feedback as HTMLCanvasElement, x1, y1, x2, y2, ...stops);
};
export const radialGradient = (app: Editor) => (x1: number, y1: number, r1: number, x2: number, y2: number, r2: number, ...stops: (number | string)[]) => {
return createRadialGradient(app.interface.feedback as HTMLCanvasElement, x1, y1, r1, x2, y2, r2, ...stops);
};
export const conicGradient = (app: Editor) => (x: number, y: number, angle: number, ...stops: (number | string)[]) => {
return createConicGradient(app.interface.feedback as HTMLCanvasElement, x, y, angle, ...stops);
};
export const draw = (app: Editor) => (func: Function): boolean => {
if (typeof func === "string") {
drawText(app.interface.feedback as HTMLCanvasElement, func, 24, 0, "Arial", wc(app)(), hc(app)(), "white", "none");
} else {
const canvas: HTMLCanvasElement = app.interface.feedback as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
func(ctx);
}
return true;
};
// Additional drawing and utility functions in canvas.ts
export const balloid = (app: Editor) => (
curves: number | ShapeObject = 6,
radius: number = hc(app)() / 2,
curve: number = 1.5,
fillStyle: string = "white",
secondary: string = "black",
x: number = wc(app)(),
y: number = hc(app)(),
): boolean => {
if (typeof curves === "object") {
fillStyle = curves.fillStyle || "white";
x = curves.x || wc(app)();
y = curves.y || hc(app)();
curve = curves.curve || 1.5;
radius = curves.radius || hc(app)() / 2;
curves = curves.curves || 6;
}
drawBalloid(app.interface.feedback as HTMLCanvasElement, curves, radius, curve, fillStyle, secondary, x, y);
return true;
};
export const equilateral = (app: Editor) => (
radius: number | ShapeObject = hc(app)() / 3,
fillStyle: string = "white",
rotation: number = 0,
x: number = wc(app)(),
y: number = hc(app)(),
): boolean => {
if (typeof radius === "object") {
fillStyle = radius.fillStyle || "white";
x = radius.x || wc(app)();
y = radius.y || hc(app)();
rotation = radius.rotation || 0;
radius = radius.radius || hc(app)() / 3;
}
drawEquilateral(app.interface.feedback as HTMLCanvasElement, radius, fillStyle, rotation, x, y);
return true;
};
export const triangular = (app: Editor) => (
width: number | ShapeObject = hc(app)() / 3,
height: number = hc(app)() / 3,
fillStyle: string = "white",
rotation: number = 0,
x: number = wc(app)(),
y: number = hc(app)(),
): boolean => {
if (typeof width === "object") {
fillStyle = width.fillStyle || "white";
x = width.x || wc(app)();
y = width.y || hc(app)();
rotation = width.rotation || 0;
height = width.height || hc(app)() / 3;
width = width.width || hc(app)() / 3;
}
drawTriangular(app.interface.feedback as HTMLCanvasElement, width, height, fillStyle, rotation, x, y);
return true;
};
export const pointy = triangular;
export const ball = (app: Editor) => (
radius: number | ShapeObject = hc(app)() / 3,
fillStyle: string = "white",
x: number = wc(app)(),
y: number = hc(app)(),
): boolean => {
if (typeof radius === "object") {
fillStyle = radius.fillStyle || "white";
x = radius.x || wc(app)();
y = radius.y || hc(app)();
radius = radius.radius || hc(app)() / 3;
}
drawBall(app.interface.feedback as HTMLCanvasElement, radius, fillStyle, x, y);
return true;
};
export const circle = ball;
export const donut = (app: Editor) => (
slices: number | ShapeObject = 3,
eaten: number = 0,
radius: number = hc(app)() / 3,
hole: number = hc(app)() / 12,
fillStyle: string = "white",
secondary: string = "black",
stroke: string = "black",
rotation: number = 0,
x: number = wc(app)(),
y: number = hc(app)(),
): boolean => {
if (typeof slices === "object") {
fillStyle = slices.fillStyle || "white";
x = slices.x || wc(app)();
y = slices.y || hc(app)();
rotation = slices.rotation || 0;
radius = slices.radius || hc(app)() / 3;
eaten = slices.eaten || 0;
hole = slices.hole || hc(app)() / 12;
secondary = slices.secondary || "black";
stroke = slices.stroke || "black";
slices = slices.slices || 3;
}
drawDonut(app.interface.feedback as HTMLCanvasElement, slices, eaten, radius, hole, fillStyle, secondary, stroke, rotation, x, y);
return true;
};
export const pie = (app: Editor) => (
slices: number | ShapeObject = 3,
eaten: number = 0,
radius: number = hc(app)() / 3,
fillStyle: string = "white",
secondary: string = "black",
stroke: string = "black",
rotation: number = 0,
x: number = wc(app)(),
y: number = hc(app)(),
): boolean => {
if (typeof slices === "object") {
fillStyle = slices.fillStyle || "white";
x = slices.x || wc(app)();
y = slices.y || hc(app)();
rotation = slices.rotation || 0;
radius = slices.radius || hc(app)() / 3;
secondary = slices.secondary || "black";
stroke = slices.stroke || "black";
eaten = slices.eaten || 0;
slices = slices.slices || 3;
}
drawPie(app.interface.feedback as HTMLCanvasElement, slices, eaten, radius, fillStyle, secondary, stroke, rotation, x, y);
return true;
};
export const star = (app: Editor) => (
points: number | ShapeObject = 5,
radius: number = hc(app)() / 3,
fillStyle: string = "white",
rotation: number = 0,
outerRadius: number = radius / 100,
x: number = wc(app)(),
y: number = hc(app)(),
): boolean => {
if (typeof points === "object") {
radius = points.radius || hc(app)() / 3;
fillStyle = points.fillStyle || "white";
x = points.x || wc(app)();
y = points.y || hc(app)();
rotation = points.rotation || 0;
outerRadius = points.outerRadius || radius / 100;
points = points.points || 5;
}
drawStar(app.interface.feedback as HTMLCanvasElement, points, radius, fillStyle, rotation, outerRadius, x, y);
return true;
};
export const stroke = (app: Editor) => (
width: number | ShapeObject = 1,
strokeStyle: string = "white",
rotation: number = 0,
x1: number = wc(app)() - wc(app)() / 10,
y1: number = hc(app)(),
x2: number = wc(app)() + wc(app)() / 5,
y2: number = hc(app)(),
): boolean => {
if (typeof width === "object") {
strokeStyle = width.strokeStyle || "white";
x1 = width.x1 || wc(app)() - wc(app)() / 10;
y1 = width.y1 || hc(app)();
x2 = width.x2 || wc(app)() + wc(app)() / 5;
y2 = width.y2 || hc(app)();
rotation = width.rotation || 0;
width = width.width || 1;
}
drawStroke(app.interface.feedback as HTMLCanvasElement, width, strokeStyle, rotation, x1, y1, x2, y2);
return true;
};
export const box = (app: Editor) => (
width: number | ShapeObject = wc(app)() / 4,
height: number = wc(app)() / 4,
fillStyle: string = "white",
rotation: number = 0,
x: number = wc(app)() - wc(app)() / 8,
y: number = hc(app)() - hc(app)() / 8,
): boolean => {
if (typeof width === "object") {
fillStyle = width.fillStyle || "white";
x = width.x || wc(app)() - wc(app)() / 4;
y = width.y || hc(app)() - hc(app)() / 2;
rotation = width.rotation || 0;
height = width.height || wc(app)() / 4;
width = width.width || wc(app)() / 4;
}
drawBox(app.interface.feedback as HTMLCanvasElement, width, height, fillStyle, rotation, x, y);
return true;
};
export const smiley = (app: Editor) => (
happiness: number | ShapeObject = 0,
radius: number = hc(app)() / 3,
eyeSize: number = 3.0,
fillStyle: string = "yellow",
rotation: number = 0,
x: number = wc(app)(),
y: number = hc(app)(),
): boolean => {
if (typeof happiness === "object") {
fillStyle = happiness.fillStyle || "yellow";
x = happiness.x || wc(app)();
y = happiness.y || hc(app)();
rotation = happiness.rotation || 0;
eyeSize = happiness.eyeSize || 3.0;
radius = happiness.radius || hc(app)() / 3;
happiness = happiness.happiness || 0;
}
drawSmiley(app.interface.feedback as HTMLCanvasElement, happiness, radius, eyeSize, fillStyle, rotation, x, y);
return true;
};
export const text = (app: Editor) => (
text: string | ShapeObject,
fontSize: number = 24,
rotation: number = 0,
font: string = "Arial",
x: number = wc(app)(),
y: number = hc(app)(),
fillStyle: string = "white",
filter: string = "none",
): boolean => {
if (typeof text === "object") {
fillStyle = text.fillStyle || "white";
x = text.x || wc(app)();
y = text.y || hc(app)();
rotation = text.rotation || 0;
font = text.font || "Arial";
fontSize = text.fontSize || 24;
filter = text.filter || "none";
text = text.text || "";
}
drawText(app.interface.feedback as HTMLCanvasElement, text, fontSize, rotation, font, x, y, fillStyle, filter);
return true;
};
export const image = (app: Editor) => (
url: string | ShapeObject,
width: number = wc(app)() / 2,
height: number = hc(app)() / 2,
rotation: number = 0,
x: number = wc(app)(),
y: number = hc(app)(),
filter: string = "none",
): boolean => {
if (typeof url === "object") {
if (!url.url) return true;
x = url.x || wc(app)();
y = url.y || hc(app)();
rotation = url.rotation || 0;
width = url.width || 100;
height = url.height || 100;
filter = url.filter || "none";
url = url.url || "";
}
drawImage(app.interface.feedback as HTMLCanvasElement, url, width, height, rotation, x, y, filter);
return true;
};
export const randomChar = () => (length: number = 1, min: number = 0, max: number = 65536): string => {
return Array.from(
{ length }, () => String.fromCodePoint(Math.floor(Math.random() * (max - min) + min))
).join('');
};
export const randomFromRange = () => (min: number, max: number): string => {
const codePoint = Math.floor(Math.random() * (max - min) + min);
return String.fromCodePoint(codePoint);
};
export const emoji = () => (n: number = 1): string => {
return randomChar()(n, 0x1f600, 0x1f64f);
};
export const food = () => (n: number = 1): string => {
return randomChar()(n, 0x1f32d, 0x1f37f);
};
export const animals = () => (n: number = 1): string => {
return randomChar()(n, 0x1f400, 0x1f4d3);
};
export const expressions = () => (n: number = 1): string => {
return randomChar()(n, 0x1f910, 0x1f92f);
};
export const gif = (app: Editor) => (options: any): void => {
const {
url,
posX = 0,
posY = 0,
opacity = 1,
size = "auto",
center = false,
rotation = 0,
filter = 'none',
duration = 10
} = options;
let real_duration = duration * app.clock.pulse_duration * app.clock.ppqn;
let fadeOutDuration = real_duration * 0.1;
let visibilityDuration = real_duration - fadeOutDuration;
const gifElement = document.createElement("img");
gifElement.src = url;
gifElement.style.position = "fixed";
gifElement.style.left = center ? "50%" : `${posX}px`;
gifElement.style.top = center ? "50%" : `${posY}px`;
gifElement.style.opacity = `${opacity}`;
gifElement.style.zIndex = "1000"; // Ensure it's on top, fixed zIndex
if (size !== "auto") {
gifElement.style.width = size;
gifElement.style.height = size;
}
const transformRules = [`rotate(${rotation}deg)`];
if (center) {
transformRules.unshift("translate(-50%, -50%)");
}
gifElement.style.transform = transformRules.join(" ");
gifElement.style.filter = filter;
gifElement.style.transition = `opacity ${fadeOutDuration}s ease`;
document.body.appendChild(gifElement);
// Start the fade-out at the end of the visibility duration
setTimeout(() => {
gifElement.style.opacity = "0";
}, visibilityDuration * 1000);
// Remove the GIF from the DOM after the fade-out duration
setTimeout(() => {
if (document.body.contains(gifElement)) {
document.body.removeChild(gifElement);
}
}, real_duration * 1000);
};
export const scope = (app: Editor) => (config: OscilloscopeConfig): void => {
/**
* Configures the oscilloscope.
* @param config - The configuration object for the oscilloscope.
*/
app.osc = {
...app.osc,
...config,
};
};