function gradientWithBounds(sel, gd, gradientID, type, colorscale, prop, start, stop, inUserSpace, reversed) {
    var len = colorscale.length;

    var info;
    if(type === 'linear') {
        info = {
            node: 'linearGradient',
            attrs: {
                x1: start.x,
                y1: start.y,
                x2: stop.x,
                y2: stop.y,
                gradientUnits: inUserSpace ? 'userSpaceOnUse' : 'objectBoundingBox',
            },
            reversed: reversed,
        };
    } else if(type === 'radial') {
        info = {
            node: 'radialGradient',
            reversed: reversed,
        };
    }

    var colorStops = new Array(len);
    for(var i = 0; i < len; i++) {
        if(info.reversed) {
            colorStops[len - 1 - i] = [stopFormatter((1 - colorscale[i][0]) * 100), colorscale[i][1]];
        } else {
            colorStops[i] = [stopFormatter(colorscale[i][0] * 100), colorscale[i][1]];
        }
    }

    var fullLayout = gd._fullLayout;
    var fullID = 'g' + fullLayout._uid + '-' + gradientID;

    var gradient = fullLayout._defs.select('.gradients')
        .selectAll('#' + fullID)
        .data([type + colorStops.join(';')], Lib.identity);

    gradient.exit().remove();

    gradient.enter()
        .append(info.node)
        .each(function() {
            var el = d3.select(this);
            if(info.attrs) el.attr(info.attrs);

            el.attr('id', fullID);

            var stops = el.selectAll('stop')
                .data(colorStops);
            stops.exit().remove();
            stops.enter().append('stop');

            stops.each(function(d) {
                var tc = tinycolor(d[1]);
                d3.select(this).attr({
                    offset: d[0] + '%',
                    'stop-color': Color.tinyRGB(tc),
                    'stop-opacity': tc.getAlpha()
                });
            });
        });

    sel.style(prop, getFullUrl(fullID, gd))
        .style(prop + '-opacity', null);

    sel.classed('gradient_filled', true);
}
gradient_with_bounds: create and apply a gradient fill for defined start and stop positions

@param {object} sel: d3 selection to apply this gradient to
    You can use `selection.call(Drawing.gradient, ...)`
@param {DOM element} gd: the graph div `sel` is part of
@param {string} gradientID: a unique (within this plot) identifier
    for this gradient, so that we don't create unnecessary definitions
@param {string} type: 'radial' or 'linear'. Radial goes center to edge,
    horizontal goes as defined by start and stop
@param {array} colorscale: as in attribute values, [[fraction, color], ...]
@param {string} prop: the property to apply to, 'fill' or 'stroke'
@param {object} start: start point for linear gradients, { x: number, y: number }.
    Ignored if type is 'radial'.
@param {object} stop: stop point for linear gradients, { x: number, y: number }.
    Ignored if type is 'radial'.
@param {boolean} inUserSpace: If true, start and stop give absolute values in the plot.
    If false, start and stop are fractions of the traces extent along each axis.
@param {boolean} reversed: If true, the gradient is reversed between normal start and stop,
    i.e., the colorscale is applied in order from stop to start for linear, from edge
    to center for radial gradients.