UNPKG

color-math

Version:
572 lines (432 loc) 15.8 kB
import * as Utils from '../utils' import ValueType from '../ValueType' import {ParenthesesExpr} from '../nodes' export default class EvaluatorBase { constructor($type) { if (this.constructor === EvaluatorBase) { throw new Error("Can't instantiate abstract class") } this.$type = `evaluator.${$type}` } get core() { throw new Error('Core evaluator should be returned in a derived class') } evalProgram(/*node*/) { notImpl() } evalStatement(/*node*/) { notImpl() } evalParentheses(/*node*/) { notImpl() } evalNumberLiteral(/*node*/) { notImpl() } evalPercent(/*node*/) { notImpl() } evalArrayLiteral(/*node*/) { notImpl() } evalArrayElement(/*node*/) { notImpl() } evalColorNameLiteral(/*node*/) { notImpl() } evalColorHexLiteral(/*node*/) { notImpl() } evalColorByNumber(/*node*/) { notImpl() } evalColorByTemperature(/*node*/) { notImpl() } evalColorByWavelength(/*node*/) { notImpl() } evalColorBySpaceParams(/*node*/) { notImpl() } evalRandomColor(/*node*/) { notImpl() } evalScale(/*node*/) { notImpl() } evalBezier(/*node*/) { notImpl() } evalCubehelix(/*node*/) { notImpl() } evalBrewerConst(/*node*/) { notImpl() } evalParam(node) { const obj = node.obj.evaluate(this.core) const objType = Utils.getType(obj) let defs = [] switch (objType) { case ValueType.Color: defs = this._getColorParamDefs(); break case ValueType.ColorScale: defs = this._getColorScaleParamDefs(obj.name); break } if (!defs.length && (objType & ValueType.Array)) { defs.push({ re: /^\d+$/i, get: node => this.evalArrayElement(node), }) } const result = this._manageParam(node, defs) return result } evalManageColorNumber(/*node*/) { notImpl() } evalManageColorTemperature(/*node*/) { notImpl() } evalManageColorLuminance(/*node*/) { notImpl() } evalManageColorAlpha(/*node*/) { notImpl() } evalManageColorCompRgbR(/*node*/) { notImpl() } evalManageColorCompRgbG(/*node*/) { notImpl() } evalManageColorCompRgbB(/*node*/) { notImpl() } evalManageColorCompCmykC(/*node*/) { notImpl() } evalManageColorCompCmykM(/*node*/) { notImpl() } evalManageColorCompCmykY(/*node*/) { notImpl() } evalManageColorCompCmykK(/*node*/) { notImpl() } evalManageColorCompHslH(/*node*/) { notImpl() } evalManageColorCompHslS(/*node*/) { notImpl() } evalManageColorCompHslL(/*node*/) { notImpl() } evalManageColorCompHsvH(/*node*/) { notImpl() } evalManageColorCompHsvS(/*node*/) { notImpl() } evalManageColorCompHsvV(/*node*/) { notImpl() } evalManageColorCompHsiH(/*node*/) { notImpl() } evalManageColorCompHsiS(/*node*/) { notImpl() } evalManageColorCompHsiI(/*node*/) { notImpl() } evalManageColorCompLabL(/*node*/) { notImpl() } evalManageColorCompLabA(/*node*/) { notImpl() } evalManageColorCompLabB(/*node*/) { notImpl() } evalManageColorCompLchL(/*node*/) { notImpl() } evalManageColorCompLchC(/*node*/) { notImpl() } evalManageColorCompLchH(/*node*/) { notImpl() } evalSetColorScalePadding(/*node*/) { notImpl() } evalSetScaleDomain(/*node*/) { notImpl() } evalSetCubehelixStart(/*node*/) { notImpl() } evalSetCubehelixRotations(/*node*/) { notImpl() } evalSetCubehelixHue(/*node*/) { notImpl() } evalSetCubehelixGamma(/*node*/) { notImpl() } evalSetCubehelixLightness(/*node*/) { notImpl() } evalUnaryOperation(node) { switch (node.operator) { case '-': return this.evalUnaryMinus(node) case '~': return this.evalColorInverse(node) case '+': return this.evalCorrectLightness(node) default: throw `invalid operator: ${node.operator}` } } evalUnaryMinus(/*node*/) { notImpl() } evalColorInverse(/*node*/) { notImpl() } evalCorrectLightness(/*node*/) { notImpl() } evalBinaryOperation(node) { const left = node.left.evaluate(this.core) const right = node.right.evaluate(this.core) const isNumbers = [left, right].every(v => Utils.getType(v) === ValueType.Number) const isColors = [left, right].every(v => Utils.getType(v) === ValueType.Color) const isColorAndNumber = Utils.getType(left) === ValueType.Color && Utils.getType(right) === ValueType.Number const isNumberAndColor = Utils.getType(left) === ValueType.Number && Utils.getType(right) === ValueType.Color switch (node.operator) { case '+': if (isNumbers) { return this.evalNumbersAddition(node) } else if (isColors) { return this.evalAddBlend(node) } else if (isColorAndNumber || isNumberAndColor) { return this.evalColorAndNumberAddition(node) } else { break } case '-': if (isNumbers) { return this.evalNumbersSubtraction(node) } else if (isColors) { return this.evalSubtractBlend(node) } else if (isColorAndNumber) { return this.evalColorAndNumberSubtraction(node) } else { break } case '*': if (isNumbers) { return this.evalNumbersMultiplication(node) } else if (isColors) { return this.evalMultiplyBlend(node) } else if (isColorAndNumber || isNumberAndColor) { return this.evalColorAndNumberMultiplication(node) } else { break } case '/': if (isNumbers) { return this.evalNumbersDivision(node) } else if (isColors) { return this.evalDivideBlend(node) } else if (isColorAndNumber) { return this.evalColorAndNumberDivision(node) } else { break } case '^': if (isNumbers) { return this.evalNumberPower(node) } else { break } case '%%': if (isColors) { return this.evalColorsContrast(node) } else { break } case '|': if (isColors) { return this.evalColorsMix(node) } else { break } case '->': if (Utils.getType(left) === ValueType.ColorScale && Utils.getType(right) === ValueType.Number) { return this.evalColorsFromScaleProduction(node) } else { break } case '<<': if (isColorAndNumber) { return this.evalColorDesaturate(node) } else if (isColors) { return this.evalColorBurnBlend(node) } else { break } case '>>': if (isColorAndNumber) { return this.evalColorSaturate(node) } else if (isColors) { return this.evalColorDodgeBlend(node) } else { break } case '<<<': if (isColorAndNumber) { return this.evalColorDarken(node) } else if (isColors) { return this.evalDarkenBlend(node) } else { break } case '>>>': if (isColorAndNumber) { return this.evalColorLighten(node) } else if (isColors) { return this.evalLightenBlend(node) } else { break } case '!*': if (isColors) { return this.evalScreenBlend(node) } else { break } case '**': if (isColors) { return this.evalOverlayBlend(node) } else { break } case '<*': if (isColors) { return this.evalHardLightBlend(node) } else { break } case '*>': if (isColors) { return this.evalSoftLightBlend(node) } else { break } case '^*': if (isColors) { return this.evalDifferenceBlend(node) } else { break } case '^^': if (isColors) { return this.evalExclusionBlend(node) } else { break } case '!^': if (isColors) { return this.evalNegateBlend(node) } else { break } default: Utils.throwError(`invalid operator '${node.operator}'`) } Utils.throwError(`${Utils.getObjKey(ValueType, Utils.getType(left))} ` + `and ${Utils.getObjKey(ValueType, Utils.getType(right))} ` + `is invalid operand types or sequence for operator '${node.operator}'`, node.$loc) } evalNumbersAddition(/*node*/) { notImpl() } evalNumbersSubtraction(/*node*/) { notImpl() } evalNumbersMultiplication(/*node*/) { notImpl() } evalNumbersDivision(/*node*/) { notImpl() } evalColorAndNumberAddition(/*node*/) { notImpl() } evalColorAndNumberSubtraction(/*node*/) { notImpl() } evalColorAndNumberMultiplication(/*node*/) { notImpl() } evalColorAndNumberDivision(/*node*/) { notImpl() } evalNumberPower(/*node*/) { notImpl() } evalColorsContrast(/*node*/) { notImpl() } evalColorsMix(/*node*/) { notImpl() } evalColorsFromScaleProduction(/*node*/) { notImpl() } evalColorDesaturate(/*node*/) { notImpl() } evalColorSaturate(/*node*/) { notImpl() } evalColorDarken(/*node*/) { notImpl() } evalColorLighten(/*node*/) { notImpl() } evalAddBlend(/*node*/) { notImpl() } evalSubtractBlend(/*node*/) { notImpl() } evalMultiplyBlend(/*node*/) { notImpl() } evalDivideBlend(/*node*/) { notImpl() } evalColorBurnBlend(/*node*/) { notImpl() } evalColorDodgeBlend(/*node*/) { notImpl() } evalDarkenBlend(/*node*/) { notImpl() } evalLightenBlend(/*node*/) { notImpl() } evalScreenBlend(/*node*/) { notImpl() } evalOverlayBlend(/*node*/) { notImpl() } evalHardLightBlend(/*node*/) { notImpl() } evalSoftLightBlend(/*node*/) { notImpl() } evalDifferenceBlend(/*node*/) { notImpl() } evalExclusionBlend(/*node*/) { notImpl() } evalNegateBlend(/*node*/) { notImpl() } evalGetVar(/*node*/) { notImpl() } evalSetVar(/*node*/) { notImpl() } _getNumberArithmeticFunc(operator) { if (!['+', '-', '*', '/'].includes(operator)) { Utils.throwError(`invalid arithmetic operator provided: '${operator}'`) } return eval(`(function(a, b) { return a ${operator} b; })`) } _manageParam(node, defs) { const opName = (() => { if (node.value === void 0) { return 'get' } else if (!node.operator) { return 'set' } else { return 'relative set' } })() for (let i = 0; i < defs.length; i++) { const def = defs[i] if (node.name.match(def.re)) { let method = def.manage if (!method) { method = node.value === void 0 ? def.get : def.set if (node.operator) { method = def.setRel } } if (method) { const result = method.call(def, node) if (result === void 0) { Utils.throwError(`operation '${opName}' for parameter '${node.name}' is not supported by '${this.$type}'`, node.$loc) } return result } else { Utils.throwError(`operation '${opName}' is not supported for parameter '${node.name}'`, node.$loc) } break } } Utils.throwError(`unknown parameter name '${node.name}'`, node.$loc) } _getColorParamDefs() { return [ { re: /^(number|num|n)$/i, manage: node => this.evalManageColorNumber(node), }, { re: /^(temperature|temp|t)$/i, manage: node => this.evalManageColorTemperature(node), }, { re: /^((rgb|cmyk|hsl|hsv|hsi|lab|lch|hcl)\.)?(luminance|lum)$/i, manage: node => { if (node.value === void 0 && node.name.match(/\./)) { Utils.throwError('color space should not be specified when retrieving luminance', node.$loc) } return this.evalManageColorLuminance(node) }, }, { re: /^(alpha|a)$/i, manage: node => this.evalManageColorAlpha(node), }, { re: /^(rgb\.)?(red|r)$/i, manage: node => this.evalManageColorCompRgbR(node), }, { re: /^(rgb\.)?(green|g)$/i, manage: node => this.evalManageColorCompRgbG(node), }, { re: /^(rgb\.)?(blue|b)$/i, manage: node => this.evalManageColorCompRgbB(node), }, { re: /^(cmyk\.)?(cyan|c)$/i, manage: node => this.evalManageColorCompCmykC(node), }, { re: /^(cmyk\.)?(magenta|mag|m)$/i, manage: node => this.evalManageColorCompCmykM(node), }, { re: /^(cmyk\.)?(yellow|yel|y)$/i, manage: node => this.evalManageColorCompCmykY(node), }, { re: /^(cmyk\.)?(key|k)$/i, manage: node => this.evalManageColorCompCmykK(node), }, { re: /^((hsl|hsv|hsi|lch|hcl)\.)?(hue|h)$/i, manage: node => node.name.match(/hsv/i) ? this.evalManageColorCompHsvH(node) : (node.name.match(/hsi/i) ? this.evalManageColorCompHsiH(node) : (node.name.match(/lch|hcl/i) ? this.evalManageColorCompLchH(node) : this.evalManageColorCompHslH(node))), }, { re: /^((hsl|hsv|hsi)\.)?(saturation|sat|s)$/i, manage: node => node.name.match(/hsv/i) ? this.evalManageColorCompHsvS(node) : (node.name.match(/hsi/i) ? this.evalManageColorCompHsiS(node) : this.evalManageColorCompHslS(node)), }, { re: /^((hsl|lab|lch|hcl)\.)?(lightness|ltns|lt|l)$/i, manage: node => node.name.match(/lab/i) ? this.evalManageColorCompLabL(node) : (node.name.match(/lch|hcl/i) ? this.evalManageColorCompLchL(node) : this.evalManageColorCompHslL(node)), }, { re: /^(hsv\.)?(value|val|v)$/i, manage: node => this.evalManageColorCompHsvV(node), }, { re: /^(hsi\.)?(intensity|int|i)$/i, manage: node => this.evalManageColorCompHsiI(node), }, { re: /^lab\.a$/i, manage: node => this.evalManageColorCompLabA(node), }, { re: /^lab\.b$/i, manage: node => this.evalManageColorCompLabB(node), }, { re: /^((((lch|hcl)\.)?(chroma|chr|ch))|lch\.c|hcl\.c)$/i, manage: node => this.evalManageColorCompLchC(node), }, ] } _getColorScaleParamDefs(scaleName) { const defs = [{ re: /^(padding|pad|p)$/i, set: node => this.evalSetColorScalePadding(node), }] if (scaleName === 'scale') { defs.push.apply(defs, [{ re: /^(domain|dom|d)$/i, set: node => this.evalSetScaleDomain(node), }]) } if (scaleName === 'cubehelix') { defs.push.apply(defs, [{ re: /^(start|s)$/i, set: node => this.evalSetCubehelixStart(node), }, { re: /^(rotations|rot|r)$/i, set: node => this.evalSetCubehelixRotations(node), }, { re: /^(hue|h)$/i, set: node => this.evalSetCubehelixHue(node), }, { re: /^(gamma|g)$/i, set: node => this.evalSetCubehelixGamma(node), }, { re: /^(lightness|lt|l)$/i, set: node => this.evalSetCubehelixLightness(node), }]) } return defs } _unwrapParens(node) { while (node instanceof ParenthesesExpr) { node = node.expr } return node } } function notImpl() { throw new Error('Not implemented') }