function setOffsetAndWidth(gd, pa, sieve, opts) {
    var fullLayout = gd._fullLayout;
    var positions = sieve.positions;
    var distinctPositions = sieve.distinctPositions;
    var minDiff = sieve.minDiff;
    var calcTraces = sieve.traces;
    var nTraces = calcTraces.length;

    // if there aren't any overlapping positions,
    // let them have full width even if mode is group
    var overlap = (positions.length !== distinctPositions.length);

    var barGroupWidth = minDiff * (1 - opts.gap);
    var barWidthPlusGap;
    var barWidth;
    var offsetFromCenter;
    var alignmentGroups;
    if(pa._id === 'angularaxis') {
        barWidthPlusGap = barGroupWidth;
        barWidth = barWidthPlusGap * (1 - (opts.groupgap || 0));
        offsetFromCenter = -barWidth / 2;
    } else { // collect groups and calculate values in loop below
        var groupId = getAxisGroup(fullLayout, pa._id) + calcTraces[0][0].trace.orientation;
        alignmentGroups = fullLayout._alignmentOpts[groupId] || {};
    }

    for(var i = 0; i < nTraces; i++) {
        var calcTrace = calcTraces[i];
        var trace = calcTrace[0].trace;
        if(pa._id !== 'angularaxis') {
            var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {};
            var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length;

            if(nOffsetGroups) {
                barWidthPlusGap = barGroupWidth / nOffsetGroups;
            } else {
                barWidthPlusGap = overlap ? barGroupWidth / nTraces : barGroupWidth;
            }

            barWidth = barWidthPlusGap * (1 - (opts.groupgap || 0));

            if(nOffsetGroups) {
                offsetFromCenter = ((2 * trace._offsetIndex + 1 - nOffsetGroups) * barWidthPlusGap - barWidth) / 2;
            } else {
                offsetFromCenter = overlap ?
                    ((2 * i + 1 - nTraces) * barWidthPlusGap - barWidth) / 2 :
                    -barWidth / 2;
            }
        }

        var t = calcTrace[0].t;
        t.barwidth = barWidth;
        t.offsetindex = trace._offsetIndex || 0;
        t.poffset = offsetFromCenter;
        t.bargroupwidth = barGroupWidth;
        t.bardelta = minDiff;
    }

    // stack bars that only differ by rounding
    sieve.binWidth = calcTraces[0][0].t.barwidth / 100;

    // if defined, apply trace width
    applyAttributes(sieve);

    // store the bar center in each calcdata item
    setBarCenterAndWidth(pa, sieve);

    // update position axes
    if(pa._id === 'angularaxis') {
        updatePositionAxis(pa, sieve);
    } else {
        updatePositionAxis(pa, sieve, overlap);
    }
}
Mode group: Traces should be offsetted to other traces at the same position if they have a
     different offsetgroup or if no offsetgroups are specified.
     If there are no other traces at the same position, the trace will not be offsetted and it
     can occupy the whole width.
     If two traces share an offsetgroup, they should overlap.
Mode overlay/stack/relative: Traces should be offseted to other traces at the same position if
     they have a different offsetgroup.
     If two traces share an offsetgroup or if no offsetgroups are specified, they should instead
     overlap/stack.
Angular axes (for barpolar type) don't support group offsets.