mirror of
https://github.com/YuzuZensai/Pien-Studio.git
synced 2026-09-02 14:18:35 +00:00
✨ feat: more tests, refactored
This commit is contained in:
@@ -12,8 +12,14 @@ export type BrushStroke = {
|
||||
height: number;
|
||||
};
|
||||
|
||||
function hexToRgb(hex: string): { r: number; g: number; b: number } {
|
||||
const clean = hex.replace("#", "");
|
||||
export type BrushDab = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export function hexToRgb(hex: string): { r: number; g: number; b: number } {
|
||||
const clean = hex.replace("#", "").trim();
|
||||
if (!/^[0-9a-fA-F]{6}$/.test(clean)) return { r: 0, g: 0, b: 0 };
|
||||
return {
|
||||
r: parseInt(clean.slice(0, 2), 16),
|
||||
g: parseInt(clean.slice(2, 4), 16),
|
||||
@@ -21,20 +27,45 @@ function hexToRgb(hex: string): { r: number; g: number; b: number } {
|
||||
};
|
||||
}
|
||||
|
||||
export function clampBrushOptions(options: BrushOptions): BrushOptions {
|
||||
return {
|
||||
color: options.color,
|
||||
size: Math.max(1, Number.isFinite(options.size) ? options.size : 1),
|
||||
opacity: Math.max(0, Math.min(1, Number.isFinite(options.opacity) ? options.opacity : 1)),
|
||||
hardness: Math.max(0, Math.min(1, Number.isFinite(options.hardness) ? options.hardness : 1)),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildBrushDabs(x0: number, y0: number, x1: number, y1: number, size: number): BrushDab[] {
|
||||
const dx = x1 - x0;
|
||||
const dy = y1 - y0;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
const step = Math.max(1, size * 0.25);
|
||||
const steps = Math.max(1, Math.ceil(dist / step));
|
||||
const dabs: BrushDab[] = [];
|
||||
|
||||
for (let i = 0; i <= steps; i++) {
|
||||
const t = i / steps;
|
||||
dabs.push({ x: x0 + dx * t, y: y0 + dy * t });
|
||||
}
|
||||
|
||||
return dabs;
|
||||
}
|
||||
|
||||
function drawDab(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
x: number,
|
||||
y: number,
|
||||
options: BrushOptions,
|
||||
) {
|
||||
const r = options.size / 2;
|
||||
const { r: cr, g: cg, b: cb } = hexToRgb(options.color);
|
||||
const normalized = clampBrushOptions(options);
|
||||
const r = normalized.size / 2;
|
||||
const { r: cr, g: cg, b: cb } = hexToRgb(normalized.color);
|
||||
|
||||
const gradient = ctx.createRadialGradient(x, y, 0, x, y, r);
|
||||
const innerStop = Math.max(0, Math.min(1, options.hardness));
|
||||
|
||||
gradient.addColorStop(0, `rgba(${cr},${cg},${cb},${options.opacity})`);
|
||||
gradient.addColorStop(innerStop, `rgba(${cr},${cg},${cb},${options.opacity})`);
|
||||
gradient.addColorStop(0, `rgba(${cr},${cg},${cb},${normalized.opacity})`);
|
||||
gradient.addColorStop(normalized.hardness, `rgba(${cr},${cg},${cb},${normalized.opacity})`);
|
||||
gradient.addColorStop(1, `rgba(${cr},${cg},${cb},0)`);
|
||||
|
||||
ctx.beginPath();
|
||||
@@ -62,15 +93,9 @@ export function paintSegment(
|
||||
y1: number,
|
||||
options: BrushOptions,
|
||||
) {
|
||||
const dx = x1 - x0;
|
||||
const dy = y1 - y0;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
const step = Math.max(1, options.size * 0.25);
|
||||
const steps = Math.max(1, Math.ceil(dist / step));
|
||||
|
||||
for (let i = 0; i <= steps; i++) {
|
||||
const t = steps === 0 ? 0 : i / steps;
|
||||
drawDab(stroke.ctx, x0 + dx * t, y0 + dy * t, options);
|
||||
const normalized = clampBrushOptions(options);
|
||||
for (const dab of buildBrushDabs(x0, y0, x1, y1, normalized.size)) {
|
||||
drawDab(stroke.ctx, dab.x, dab.y, normalized);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user