UNPKG

molstar

Version:

A comprehensive macromolecular library.

137 lines (136 loc) 5.17 kB
/** * Copyright (c) 2026 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Zach Charlop-Powers <zach.charlop.powers@gmail.com> */ import { Color } from './color.js'; import { ColorNames } from './names.js'; const hexColorRegex = /^#([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$/i; const rgbColorRegex = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i; export function decodeColor(colorString) { if (colorString === undefined || colorString === null) return undefined; let result; if (hexColorRegex.test(colorString)) { if (colorString.length === 4) { // convert short form to full form (#f0f -> #ff00ff) colorString = `#${colorString[1]}${colorString[1]}${colorString[2]}${colorString[2]}${colorString[3]}${colorString[3]}`; } else if (colorString.length === 5) { // convert short form with alpha to full form, ignoring alpha (#f0fa -> #ff00ff) colorString = `#${colorString[1]}${colorString[1]}${colorString[2]}${colorString[2]}${colorString[3]}${colorString[3]}`; } else if (colorString.length === 9) { // strip alpha channel from 8-character hex (#ff00ffaa -> #ff00ff) colorString = colorString.substring(0, 7); } result = Color.fromHexStyle(colorString); if (result !== undefined && !isNaN(result)) return result; } result = ColorNames[colorString.toLowerCase()]; if (result !== undefined) return result; const rgbMatch = rgbColorRegex.exec(colorString); if (rgbMatch) { const r = parseInt(rgbMatch[1], 10); const g = parseInt(rgbMatch[2], 10); const b = parseInt(rgbMatch[3], 10); if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) { return Color.fromRgb(r, g, b); } } return undefined; } export function getColorGradientBanded(colors) { const n = colors.length; const styles = []; const hasOffsets = colors.every(c => Array.isArray(c)); if (hasOffsets) { const off = [...colors]; // 0 colors present if (!off[0]) { return 'linear-gradient(to right, #000 0%, #000 100%)'; } off.sort((a, b) => a[1] - b[1]); styles.push(`${Color.toStyle(off[0][0])} ${(100 * off[0][1]).toFixed(2)}%`); for (let i = 0, il = off.length - 1; i < il; ++i) { const [c0, o0] = off[i]; const [c1, o1] = off[i + 1]; const o = o0 + (o1 - o0) / 2; styles.push(`${Color.toStyle(c0)} ${(100 * o).toFixed(2)}%`, `${Color.toStyle(c1)} ${(100 * o).toFixed(2)}%`); } styles.push(`${Color.toStyle(off[off.length - 1][0])} ${(100 * off[off.length - 1][1]).toFixed(2)}%`); } else { styles.push(`${colorEntryToStyle(colors[0])} ${100 * (1 / n)}%`); for (let i = 1, il = n - 1; i < il; ++i) { styles.push(`${colorEntryToStyle(colors[i])} ${100 * (i / n)}%`, `${colorEntryToStyle(colors[i])} ${100 * ((i + 1) / n)}%`); } styles.push(`${colorEntryToStyle(colors[n - 1])} ${100 * ((n - 1) / n)}%`); } return `linear-gradient(to right, ${styles.join(', ')})`; } export function getColorGradient(colors) { if (colors.length === 0) return 'linear-gradient(to right, #000 0%, #000 100%)'; const hasOffsets = colors.every(c => Array.isArray(c)); let styles; if (hasOffsets) { const off = [...colors]; off.sort((a, b) => a[1] - b[1]); styles = off.map(c => colorEntryToStyle(c, true)); } else { styles = colors.map(c => colorEntryToStyle(c)); } return `linear-gradient(to right, ${styles.join(', ')})`; } function colorEntryToStyle(e, includeOffset = false) { if (Array.isArray(e)) { if (includeOffset) return `${Color.toStyle(e[0])} ${(100 * e[1]).toFixed(2)}%`; return Color.toStyle(e[0]); } return Color.toStyle(e); } export function parseColorList(input, separator = /,/) { const ret = []; const trimmed = input.replace(/\s+/g, ''); let tokenStart = 0; let bracketLevel = 0; for (let i = 0, il = trimmed.length; i < il; ++i) { const c = trimmed[i]; if (c === '(') { bracketLevel++; continue; } else if (c === ')') { if (bracketLevel > 0) { bracketLevel--; } continue; } if (bracketLevel > 0) continue; if (!separator.test(c)) { continue; } const color = trimmed.substring(tokenStart, i); tokenStart = i + 1; const decoded = decodeColor(color); if (decoded !== undefined) { ret.push(decoded); } } if (tokenStart < trimmed.length) { const color = trimmed.substring(tokenStart); const decoded = decodeColor(color); console.log(color, decoded); if (decoded !== undefined) { ret.push(decoded); } } return ret; }