Skip to content

Commit

Permalink
fix: handle stepped scales for autosized facets
Browse files Browse the repository at this point in the history
  • Loading branch information
Derek Gould committed Jul 13, 2020
1 parent e53bf2b commit bac6abd
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 32 deletions.
53 changes: 23 additions & 30 deletions src/compile/layoutsize/assemble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {getFirstDefined} from '../../util';
import {isVgRangeStep, VgRangeStep} from '../../vega.schema';
import {isFacetModel, Model} from '../model';
import {ScaleComponent} from '../scale/component';
import {LayoutSizeType} from './component';
import {getSizeTypeFromLayoutSizeType, LayoutSizeType} from './component';
import {isFacetMapping} from '../../spec/facet';
import {FacetModel} from "../facet";

export function assembleLayoutSignals(model: Model): NewSignal[] {
return [
Expand All @@ -26,7 +28,14 @@ export function sizeSignals(model: Model, sizeType: LayoutSizeType): (NewSignal
// Read size signal name from name map, just in case it is the top-level size signal that got renamed.
const name = model.getSizeSignalRef(sizeType).signal;

if (size === 'step') {
if (isFacetModel(model.parent) && model.parent.hasStaticOuterDimension(getSizeTypeFromLayoutSizeType(sizeType))) {
return [
{
name,
update: autosizedFacetExpr(model.parent, sizeType)
}
];
} else if (size === 'step') {
const scaleComponent = model.getScaleComponent(channel);

if (scaleComponent) {
Expand Down Expand Up @@ -63,34 +72,6 @@ export function sizeSignals(model: Model, sizeType: LayoutSizeType): (NewSignal
const defaultValue = getViewConfigContinuousSize(model.config.view, isWidth ? 'width' : 'height');
const safeExpr = `isFinite(${expr}) ? ${expr} : ${defaultValue}`;
return [{name, init: safeExpr, on: [{update: safeExpr, events: 'window:resize'}]}];
} else if (isFacetModel(model.parent) && sizeType === 'width' && model.parent.size.width) {
return [
{
name,
// if the facet operator defines a column channel, the compiled vega spec includes the 'column_domain' data
// if however the facet operator is itself is a facet field definition, the compiled vega spec instead includes the 'facet_domain_column' data
// otherwise there is no column faceting so the width should be passed through
update: model.parent.facet.column
? "width / length(data('column_domain'))"
: model.parent.facet.facet
? "width / length(data('facet_domain_column'))"
: 'width'
}
];
} else if (isFacetModel(model.parent) && sizeType === 'height' && model.parent.size.height) {
return [
{
name,
// if the facet operator defines a row channel, the compiled vega spec includes the 'row_domain' data
// if however the facet operator is itself is a facet field definition, the compiled vega spec instead includes the 'facet_domain_row' data
// otherwise there is no row faceting so the height should be passed through
update: model.parent.facet.row
? "height / length(data('row_domain'))"
: model.parent.facet.facet
? "height / length(data('facet_domain_row'))"
: 'height'
}
];
} else {
return [
{
Expand Down Expand Up @@ -125,3 +106,15 @@ export function sizeExpr(scaleName: string, scaleComponent: ScaleComponent, card
1;
return `bandspace(${cardinality}, ${paddingInner}, ${paddingOuter}) * ${scaleName}_step`;
}

export function autosizedFacetExpr(model: FacetModel, sizeType: LayoutSizeType) {
const channel = sizeType === 'width' ? 'column' : 'row';
if (!model.facet[channel] && isFacetMapping(model.facet)) {
// no faceting on this channel so just pass through the overall dimension of this channel
return sizeType;
}
// if the facet operator defines an explicit row or column channel, the compiled vega spec includes the '(row|column)_domain' data
// otherwise the facet operator is itself is a facet field definition, the compiled vega spec instead includes the 'facet_domain_(row|column)' data
const domain = !isFacetMapping(model.facet) ? `facet_domain_${channel}` : `${channel}_domain`;
return `${sizeType} / length(data('${domain}'))`;
}
9 changes: 9 additions & 0 deletions src/compile/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {isFacetSpec} from '../spec';
import {
extractCompositionLayout,
GenericCompositionLayoutWithColumns,
isStep,
LayoutSizeField,
LayoutSizeMixins,
SpecType,
ViewBackground
Expand Down Expand Up @@ -658,6 +660,13 @@ export abstract class Model {
this.component.axes.y?.some(a => a.hasOrientSignalRef())
);
}

/**
* Returns true if the model has a static value (i.e. not a step) set for the given size dimension.
*/
public hasStaticOuterDimension(dimension: LayoutSizeField) {
return this.size[dimension] && !isStep(this.size[dimension]);
}
}

/** Abstract class for UnitModel and FacetModel. Both of which can contain fieldDefs as a part of its own specification. */
Expand Down
5 changes: 3 additions & 2 deletions src/compile/scale/range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {SignalRefWrapper} from '../signal';
import {Explicit, makeExplicit, makeImplicit} from '../split';
import {UnitModel} from '../unit';
import {ScaleComponentIndex} from './component';
import {isFacetModel} from "../model";

export const RANGE_PROPERTIES: (keyof Scale)[] = ['range', 'scheme'];

Expand Down Expand Up @@ -203,12 +204,12 @@ function defaultRange(channel: ScaleChannel, model: UnitModel): VgRange {
if (util.contains(['point', 'band'], scaleType)) {
if (channel === X && !size.width) {
const w = getViewConfigDiscreteSize(config.view, 'width');
if (isStep(w)) {
if (isStep(w) && (!isFacetModel(model.parent) || !model.parent.hasStaticOuterDimension('width'))) {
return w;
}
} else if (channel === Y && !size.height) {
const h = getViewConfigDiscreteSize(config.view, 'height');
if (isStep(h)) {
if (isStep(h) && (!isFacetModel(model.parent) || !model.parent.hasStaticOuterDimension('height'))) {
return h;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/spec/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ export interface LayoutSizeMixins {
height?: number | 'container' | Step; // Vega also supports SignalRef for width and height. However, we need to know if width is a step or not in VL and it's very difficult to check this at runtime, so we intentionally do not support SignalRef here.
}

export type LayoutSizeField = keyof LayoutSizeMixins;

export function isFrameMixins(o: any): o is FrameMixins {
return o['view'] || o['width'] || o['height'];
}
Expand Down

0 comments on commit bac6abd

Please sign in to comment.