Files

249 lines
8.8 KiB
JavaScript

// Allgemeine Symbole (Ordner, Pfeile, Einstellungen ...) fuer das Icon-Pack, in Einheitskoordinaten [-1, 1].
const TAU = Math.PI * 2;
export const EXTRA_GLYPHS = [
'folder', 'file', 'home', 'gear', 'plus', 'minus', 'close', 'check', 'star', 'search', 'power', 'mic',
'headphones', 'lock', 'bell', 'clock', 'calendar', 'mail', 'globe', 'download', 'upload', 'trash', 'eye',
'stop', 'refresh', 'wifi', 'grid', 'menu', 'more', 'info', 'warning', 'monitor', 'sun', 'moon',
'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'chevron-up', 'chevron-down', 'chevron-left',
'chevron-right',
];
const ARROW_ANGLES = { 'arrow-right': 0, 'arrow-down': 90, 'arrow-left': 180, 'arrow-up': 270 };
const CHEVRON_ANGLES = { 'chevron-right': 0, 'chevron-down': 90, 'chevron-left': 180, 'chevron-up': 270 };
function starPoints() {
const points = [];
for (let i = 0; i < 10; i += 1) {
const angle = -Math.PI / 2 + (i * Math.PI) / 5;
const radius = i % 2 === 0 ? 0.98 : 0.42;
points.push([Math.cos(angle) * radius, Math.sin(angle) * radius + 0.05]);
}
return points;
}
const STAR = starPoints();
/**
* @param {import('./raster.mjs').Canvas} canvas
* @param {string} name - Eintrag aus EXTRA_GLYPHS
* @param {number} cx
* @param {number} cy
* @param {number} r - halbe Symbolgroesse in Pixeln
* @param {string|Function} color
* @param {string|Function} background - Farbe fuer "ausgeschnittene" Teile
*/
export function drawExtra(canvas, name, cx, cy, r, color, background) {
const p = (x, y) => [cx + x * r, cy + y * r];
const rot = (x, y, degrees) => {
const a = (degrees * Math.PI) / 180;
return [x * Math.cos(a) - y * Math.sin(a), x * Math.sin(a) + y * Math.cos(a)];
};
const stroke = (x1, y1, x2, y2, w = 0.2, paint = color, degrees = 0) => {
const [ax, ay] = p(...rot(x1, y1, degrees));
const [bx, by] = p(...rot(x2, y2, degrees));
canvas.line(ax, ay, bx, by, w * r, paint);
};
const path = (points, w = 0.2, degrees = 0) => {
points.slice(1).forEach((pt, i) => stroke(...points[i], ...pt, w, color, degrees));
};
const poly = (points, paint = color) => canvas.polygon(points.map(([x, y]) => p(x, y)), paint);
const disc = (x, y, radius, paint = color) => canvas.circle(...p(x, y), radius * r, paint);
const ring = (x, y, outer, inner, from = 0, to = TAU) => canvas.ring(...p(x, y), outer * r, inner * r, color, from, to);
const box = (x, y, w, h, radius, paint = color) => canvas.roundRect(...p(x, y), w * r, h * r, radius * r, paint);
// Ellipse; thickness = Randstaerke relativ zum Radius, null = gefuellt.
const ellipse = (rx, ry, thickness) => {
const [ex, ey] = p(0, 0);
canvas.fill((px, py) => {
const d = Math.hypot((px - ex) / (rx * r), (py - ey) / (ry * r));
return d <= 1 && (thickness === null || d >= 1 - thickness);
}, color, [ex - rx * r, ey - ry * r, ex + rx * r, ey + ry * r]);
};
if (name in ARROW_ANGLES) {
const a = ARROW_ANGLES[name];
stroke(-0.8, 0, 0.8, 0, 0.2, color, a);
stroke(0.8, 0, 0.2, -0.6, 0.2, color, a);
stroke(0.8, 0, 0.2, 0.6, 0.2, color, a);
return;
}
if (name in CHEVRON_ANGLES) {
path([[-0.3, -0.75], [0.35, 0], [-0.3, 0.75]], 0.24, CHEVRON_ANGLES[name]);
return;
}
switch (name) {
case 'folder':
poly([[-0.92, -0.5], [-0.92, -0.78], [-0.15, -0.78], [0.1, -0.5]]);
box(-0.92, -0.5, 1.84, 1.3, 0.12);
break;
case 'file':
poly([[-0.62, -0.92], [0.18, -0.92], [0.68, -0.42], [0.68, 0.92], [-0.62, 0.92]]);
poly([[0.18, -0.92], [0.18, -0.42], [0.68, -0.42]], background);
stroke(-0.3, 0.05, 0.36, 0.05, 0.14, background);
stroke(-0.3, 0.45, 0.36, 0.45, 0.14, background);
break;
case 'home':
poly([[-1, 0], [0, -0.9], [1, 0]]);
box(-0.62, -0.05, 1.24, 0.9, 0.06);
box(-0.16, 0.25, 0.32, 0.6, 0.04, background);
break;
case 'gear':
ring(0, 0, 0.66, 0.3);
for (let i = 0; i < 8; i += 1) {
const a = (i * 45 * Math.PI) / 180;
stroke(Math.cos(a) * 0.6, Math.sin(a) * 0.6, Math.cos(a) * 0.9, Math.sin(a) * 0.9, 0.3);
}
break;
case 'plus':
stroke(-0.8, 0, 0.8, 0, 0.24);
stroke(0, -0.8, 0, 0.8, 0.24);
break;
case 'minus':
stroke(-0.8, 0, 0.8, 0, 0.24);
break;
case 'close':
stroke(-0.7, -0.7, 0.7, 0.7, 0.24);
stroke(-0.7, 0.7, 0.7, -0.7, 0.24);
break;
case 'check':
path([[-0.8, 0.05], [-0.25, 0.6], [0.85, -0.55]], 0.26);
break;
case 'star':
poly(STAR);
break;
case 'search':
ring(-0.2, -0.2, 0.68, 0.44);
stroke(0.3, 0.3, 0.82, 0.82, 0.28);
break;
case 'power':
ring(0, 0.08, 0.8, 0.58, (3 * Math.PI) / 2 + 0.65, TAU);
ring(0, 0.08, 0.8, 0.58, 0, (3 * Math.PI) / 2 - 0.65);
stroke(0, -0.9, 0, -0.1, 0.24);
break;
case 'mic':
box(-0.28, -0.9, 0.56, 1.1, 0.28);
ring(0, -0.05, 0.6, 0.42, 0, Math.PI);
stroke(0, 0.55, 0, 0.9, 0.16);
stroke(-0.35, 0.9, 0.35, 0.9, 0.16);
break;
case 'headphones':
ring(0, 0.05, 0.85, 0.62, Math.PI, TAU);
box(-0.95, 0.05, 0.4, 0.7, 0.12);
box(0.55, 0.05, 0.4, 0.7, 0.12);
break;
case 'lock':
ring(0, -0.15, 0.45, 0.25, Math.PI, TAU);
stroke(-0.35, -0.15, -0.35, 0.05, 0.2);
stroke(0.35, -0.15, 0.35, 0.05, 0.2);
box(-0.65, 0, 1.3, 0.9, 0.12);
disc(0, 0.4, 0.13, background);
break;
case 'bell':
disc(0, -0.15, 0.55);
poly([[-0.55, -0.15], [0.55, -0.15], [0.82, 0.5], [-0.82, 0.5]]);
disc(0, 0.72, 0.16);
stroke(0, -0.85, 0, -0.65, 0.16);
break;
case 'clock':
ring(0, 0, 0.92, 0.7);
stroke(0, 0, 0, -0.5, 0.15);
stroke(0, 0, 0.36, 0.22, 0.15);
break;
case 'calendar':
box(-0.88, -0.68, 1.76, 1.58, 0.12);
box(-0.72, -0.22, 1.44, 1.0, 0.05, background);
stroke(-0.4, -0.92, -0.4, -0.5, 0.18);
stroke(0.4, -0.92, 0.4, -0.5, 0.18);
break;
case 'mail':
box(-0.92, -0.62, 1.84, 1.24, 0.12);
stroke(-0.8, -0.5, 0, 0.12, 0.15, background);
stroke(0, 0.12, 0.8, -0.5, 0.15, background);
break;
case 'globe':
ring(0, 0, 0.92, 0.75);
ellipse(0.42, 0.92, 0.18);
stroke(-0.92, 0, 0.92, 0, 0.13);
break;
case 'download':
stroke(0, -0.9, 0, 0.3, 0.22);
path([[-0.5, -0.2], [0, 0.4], [0.5, -0.2]], 0.22);
path([[-0.85, 0.35], [-0.85, 0.85], [0.85, 0.85], [0.85, 0.35]], 0.2);
break;
case 'upload':
stroke(0, 0.3, 0, -0.9, 0.22);
path([[-0.5, -0.4], [0, -0.95], [0.5, -0.4]], 0.22);
path([[-0.85, 0.35], [-0.85, 0.85], [0.85, 0.85], [0.85, 0.35]], 0.2);
break;
case 'trash':
box(-0.6, -0.3, 1.2, 1.2, 0.1);
stroke(-0.85, -0.5, 0.85, -0.5, 0.2);
stroke(-0.25, -0.75, 0.25, -0.75, 0.16);
stroke(-0.2, -0.05, -0.2, 0.65, 0.13, background);
stroke(0.2, -0.05, 0.2, 0.65, 0.13, background);
break;
case 'eye':
ellipse(0.98, 0.58, 0.22);
disc(0, 0, 0.3);
break;
case 'stop':
box(-0.72, -0.72, 1.44, 1.44, 0.16);
break;
case 'refresh': {
const end = 5.5;
ring(0, 0, 0.85, 0.6, 0.7, end);
const bx = Math.cos(end) * 0.725;
const by = Math.sin(end) * 0.725;
poly([[bx - Math.sin(end) * 0.55, by + Math.cos(end) * 0.55],
[bx + Math.cos(end) * 0.5, by + Math.sin(end) * 0.5],
[bx - Math.cos(end) * 0.5, by - Math.sin(end) * 0.5]]);
break;
}
case 'wifi':
for (const radius of [0.45, 0.8, 1.15]) ring(0, 0.6, radius + 0.09, radius - 0.09, 1.25 * Math.PI, 1.75 * Math.PI);
disc(0, 0.6, 0.13);
break;
case 'grid':
for (const x of [-0.85, 0.08]) for (const y of [-0.85, 0.08]) box(x, y, 0.77, 0.77, 0.14);
break;
case 'menu':
stroke(-0.8, -0.5, 0.8, -0.5, 0.22);
stroke(-0.8, 0, 0.8, 0, 0.22);
stroke(-0.8, 0.5, 0.8, 0.5, 0.22);
break;
case 'more':
for (const x of [-0.6, 0, 0.6]) disc(x, 0, 0.17);
break;
case 'info':
ring(0, 0, 0.92, 0.72);
disc(0, -0.42, 0.11);
stroke(0, -0.12, 0, 0.5, 0.18);
break;
case 'warning':
poly([[0, -0.9], [0.98, 0.78], [-0.98, 0.78]]);
stroke(0, -0.3, 0, 0.25, 0.16, background);
disc(0, 0.52, 0.09, background);
break;
case 'monitor':
box(-0.92, -0.72, 1.84, 1.2, 0.12);
box(-0.78, -0.58, 1.56, 0.92, 0.05, background);
stroke(0, 0.5, 0, 0.8, 0.2);
stroke(-0.45, 0.85, 0.45, 0.85, 0.2);
break;
case 'sun':
disc(0, 0, 0.42);
for (let i = 0; i < 8; i += 1) {
const a = (i * 45 * Math.PI) / 180;
stroke(Math.cos(a) * 0.68, Math.sin(a) * 0.68, Math.cos(a) * 0.95, Math.sin(a) * 0.95, 0.17);
}
break;
case 'moon':
disc(0, 0, 0.8);
disc(0.38, -0.3, 0.68, background);
break;
default:
throw new Error(`Unbekanntes Symbol: ${name}`);
}
}