Source: visual-layer/src/layers/bar/bar-helper.js

import { FieldType, DimensionSubtype } from 'muze-utils';
import * as PROPS from '../../enums/props';
import { STACK } from '../../enums/constants';
import { getLayerColor, positionPoints, getIndividualClassName } from '../../helpers';

/**
 *
 *
 * @param {*} type
 * @param {*} fieldInfo
 * @param {*} config
 * @param {*} data
 *
 */
const resolveDimByField = (type, axesInfo, config, data) => {
    const spaceType = type === 'x' ? 'width' : 'height';
    const [fieldType, axis] = [config[`${type}FieldType`], axesInfo[`${type}Axis`]];
    const {
        transformType,
        sizeEncoding,
        sizeConfig,
        measurement
    } = config;

    const sizeValue = sizeEncoding.value;
    let enter = 0;
    let pos;
    let space = 0;
    let enterSpace = 0;
    if (fieldType !== undefined) {
        if (config[`${type}0Field`]) {
            const minVal = data[type];
            const maxVal = data[`${type}0`];
            let min;
            let max;
            if (minVal === null || maxVal === null) {
                return {
                    enterSpace: undefined,
                    enter: undefined,
                    pos: undefined,
                    space: undefined
                };
            } else if (fieldType === FieldType.MEASURE || fieldType === DimensionSubtype.TEMPORAL) {
                min = Math.min(minVal, maxVal);
                max = Math.max(minVal, maxVal);
            } else {
                min = minVal;
                max = maxVal;
            }
            const scales = type === 'x' ? [min, max] : [max, min];
            pos = axis.getScaleValue(scales[0]) + axis.getUnitWidth() / 2;
            space = Math.abs(axis.getScaleValue(scales[1]) - pos) + axis.getUnitWidth() / 2;

            enter = pos;
            enterSpace = 0;
        } else if (fieldType === DimensionSubtype.CATEGORICAL || fieldType === DimensionSubtype.TEMPORAL) {
            pos = axis.getScaleValue(data[type]) +
                (sizeConfig[type === 'x' ? 'barWidthOffset' : 'barHeightOffset'] || 0);

            space = sizeConfig[type === 'x' ? 'barWidth' : 'barHeight'];
            if (sizeValue !== undefined) {
                const diffPx = sizeValue * space;
                space -= diffPx;
                pos += diffPx / 2;
            }
            enter = pos;
            enterSpace = space;
        } else {
            const zeroPos = axis.getScaleValue(0);
            const axisType = axis.getScaleValue(data[type]);
            const axisType0 = axis.getScaleValue(data[`${type}0`]);

            enterSpace = 0;
            if (type === 'x') {
                pos = data[type] < 0 || transformType === STACK ? axisType : zeroPos;
                space = Math.abs(pos - (transformType === STACK ? axisType0 : (data[type] >= 0 ? axisType : zeroPos)));
            } else {
                pos = transformType === STACK || data[type] >= 0 ? axisType : zeroPos;
                space = Math.abs(pos - (transformType === STACK ? axisType0 : (data[type] >= 0 ? zeroPos : axisType)));
            }
            enter = zeroPos;
        }
    } else {
        pos = 0;
        space = measurement[spaceType];
    }

    return {
        enterSpace,
        enter,
        pos,
        space
    };
};

/**
 *
 *
 * @param {*} data
 * @param {*} config
 * @param {*} axes
 *
 */
const resolveDimensions = (data, config, axes) => {
    const axesInfo = {
        xAxis: axes.x,
        yAxis: axes.y
    };
    const {
        enterSpace: enterWidth,
        enter: enterX,
        pos: xPos,
        space: width
    } = resolveDimByField('x', axesInfo, config, data);

    const {
        enterSpace: enterHeight,
        enter: enterY,
        pos: yPos,
        space: height
    } = resolveDimByField('y', axesInfo, config, data);
    return {
        enter: {
            x: enterX,
            y: enterY,
            width: enterWidth,
            height: enterHeight
        },
        update: {
            x: xPos,
            y: yPos,
            width,
            height
        }
    };
};

/**
 * Generates an array of objects containing x, y, width and height of the bars from the data
 * @param  {Array.<Array>} data Data Array
 * @param  {Object} encoding  Config
 * @param  {Object} axes     Axes object
 * @param {Object} conf config object for point generation
 * @return {Array.<Object>}  Array of points
 */
export const getTranslatedPoints = (context, data, sizeConfig) => {
    let points = [];
    const encoding = context.config().encoding;
    const axes = context.axes();
    const colorAxis = axes.color;
    const fieldsConfig = context.data().getFieldsConfig();
    const colorEncoding = encoding.color;
    const colorField = colorEncoding.field;
    const sizeEncoding = encoding.size || {};
    const {
            x0Field,
            y0Field,
            xFieldSubType,
            yFieldSubType
        } = context.encodingFieldsInf();
    const measurement = context._store.get(PROPS.MEASUREMENT);
    const isXDim = xFieldSubType === DimensionSubtype.CATEGORICAL || xFieldSubType === DimensionSubtype.TEMPORAL;
    const isYDim = yFieldSubType === DimensionSubtype.CATEGORICAL || yFieldSubType === DimensionSubtype.TEMPORAL;
    const key = isXDim ? 'x' : (isYDim ? 'y' : null);
    const transformType = context.transformType();
    const colorFieldIndex = colorField && fieldsConfig[colorField] && fieldsConfig[colorField].index;

    for (let i = 0, len = data.length; i < len; i++) {
        const d = data[i];
        const style = {};
        const meta = {};
        const dimensions = resolveDimensions(d, {
            xFieldType: xFieldSubType,
            yFieldType: yFieldSubType,
            x0Field,
            y0Field,
            transformType,
            measurement,
            sizeConfig,
            sizeEncoding
        }, axes);

        const { color, rawColor } = getLayerColor({ datum: d, index: i },
            { colorEncoding, colorAxis, colorFieldIndex });

        style.fill = color;
        meta.stateColor = {};
        meta.originalColor = rawColor;
        meta.colorTransform = {};

        const update = dimensions.update;

        if (!isNaN(update.x) && !isNaN(update.y) && d._id !== undefined) {
            let point = null;
            point = {
                enter: dimensions.enter,
                update,
                style,
                _data: d._data,
                _id: d._id,
                source: d._data,
                rowId: d._id,
                meta
            };
            point.className = getIndividualClassName(d, i, data, context);
            points.push(point);
            // Store each point in a hashmap with key as the dimensional or temporal field value
            context.cachePoint(d[key], point);
        }
    }

    points = positionPoints(context, points);
    return points;
};