Skip to content

Commit

Permalink
refactor: simplify scale range parameters and tests (#5145)
Browse files Browse the repository at this point in the history
  • Loading branch information
kanitw authored and domoritz committed Jul 5, 2019
1 parent 79ecb26 commit a9f33b4
Show file tree
Hide file tree
Showing 9 changed files with 431 additions and 638 deletions.
14 changes: 8 additions & 6 deletions src/compile/layoutsize/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ export function initLayoutSize({
const sizeType = getSizeType(channel);
const fieldDef = getFieldDef(encoding[channel]) as PositionFieldDef<string>;
if (isStep(size[sizeType])) {
if (isContinuous(fieldDef)) {
delete size[sizeType];
log.warn(log.message.cannotUseStepWithContinuous(sizeType));
} else if (isDiscrete(fieldDef) && fit) {
delete size[sizeType];
log.warn(log.message.cannotUseStepWithFit(sizeType));
if (fieldDef) {
if (isContinuous(fieldDef)) {
delete size[sizeType];
log.warn(log.message.stepDropped(sizeType, 'continuous'));
} else if (isDiscrete(fieldDef) && fit) {
delete size[sizeType];
log.warn(log.message.stepDropped(sizeType, 'fit'));
}
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/compile/scale/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ import {parseScaleDomain} from './domain';
import {parseScaleProperty, parseScaleRange} from './properties';
import {scaleType} from './type';

export function parseScales(model: Model) {
export function parseScales(model: Model, {ignoreRange}: {ignoreRange?: boolean} = {}) {
parseScaleCore(model);
parseScaleDomain(model);
for (const prop of NON_TYPE_DOMAIN_RANGE_VEGA_SCALE_PROPERTIES) {
parseScaleProperty(model, prop);
}
// range depends on zero
parseScaleRange(model);
if (!ignoreRange) {
// range depends on zero
parseScaleRange(model);
}
}

export function parseScaleCore(model: Model) {
Expand Down
114 changes: 35 additions & 79 deletions src/compile/scale/range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,14 @@ import {
isContinuousToDiscrete,
isExtendedScheme,
Scale,
ScaleType,
scaleTypeSupportProperty,
Scheme
} from '../../scale';
import {isStep, LayoutSizeMixins} from '../../spec/base';
import {Type} from '../../type';
import * as util from '../../util';
import {isSignalRef, SchemeConfig, VgRange} from '../../vega.schema';
import {getBinSignalName} from '../data/bin';
import {Rename, SignalRefWrapper} from '../signal';
import {SignalRefWrapper} from '../signal';
import {Explicit, makeExplicit, makeImplicit} from '../split';
import {UnitModel} from '../unit';
import {ScaleComponentIndex} from './component';
Expand All @@ -57,31 +55,8 @@ export function parseUnitScaleRange(model: UnitModel) {
if (!localScaleCmpt) {
return;
}
const mergedScaleCmpt = model.getScaleComponent(channel);

const specifiedScale = model.specifiedScales[channel];
const fieldDef = model.fieldDef(channel);

const scaleType = mergedScaleCmpt.get('type');
const sizeType = getSizeType(channel);

const rangeWithExplicit = parseRangeForChannel(
channel,
model.getSignalName.bind(model),
scaleType,
fieldDef.type,
specifiedScale,
model.config,
localScaleCmpt.get('zero'),
model.mark,
model.getName(sizeType),
model.size,
model.fit,
{
x: getBinStepSignal(model, 'x'),
y: getBinStepSignal(model, 'y')
}
);
const rangeWithExplicit = parseRangeForChannel(channel, model);

localScaleCmpt.setWithExplicit('range', rangeWithExplicit);
});
Expand All @@ -108,20 +83,13 @@ function getBinStepSignal(model: UnitModel, channel: 'x' | 'y'): SignalRefWrappe
/**
* Return mixins that includes one of the Vega range types (explicit range, range.step, range.scheme).
*/
export function parseRangeForChannel(
channel: Channel,
getSignalName: Rename,
scaleType: ScaleType,
type: Type,
specifiedScale: Scale,
config: Config,
zero: boolean,
mark: Mark,
sizeSignal: string,
size: LayoutSizeMixins,
fit: boolean = false,
xyStepSignals: {x?: SignalRefWrapper; y?: SignalRefWrapper} = {}
): Explicit<VgRange> {
export function parseRangeForChannel(channel: ScaleChannel, model: UnitModel): Explicit<VgRange> {
const specifiedScale = model.specifiedScales[channel];
const {size, fit} = model;

const mergedScaleCmpt = model.getScaleComponent(channel);
const scaleType = mergedScaleCmpt.get('type');

// Check if any of the range properties is specified.
// If so, check if it is compatible and make sure that we only output one of the properties
for (const property of RANGE_PROPERTIES) {
Expand Down Expand Up @@ -161,22 +129,7 @@ export function parseRangeForChannel(
}
}

return makeImplicit(
defaultRange(
channel,
getSignalName,
scaleType,
type,
config,
zero,
mark,
sizeSignal,
size,
xyStepSignals,
specifiedScale.domain,
fit
)
);
return makeImplicit(defaultRange(channel, model));
}

function parseScheme(scheme: Scheme): SchemeConfig {
Expand All @@ -189,23 +142,21 @@ function parseScheme(scheme: Scheme): SchemeConfig {
return {scheme: scheme};
}

function defaultRange(
channel: Channel,
getSignalName: Rename,
scaleType: ScaleType,
type: Type,
config: Config,
zero: boolean,
mark: Mark,
sizeSignal: string,
size: LayoutSizeMixins,
xyStepSignals: {x?: SignalRefWrapper; y?: SignalRefWrapper},
domain: Domain,
fit: boolean
): VgRange {
function defaultRange(channel: ScaleChannel, model: UnitModel): VgRange {
const {size, config, fit, mark} = model;

const getSignalName = model.getSignalName.bind(model);

const {type} = model.fieldDef(channel);

const mergedScaleCmpt = model.getScaleComponent(channel);
const scaleType = mergedScaleCmpt.get('type');

const {domain} = model.specifiedScales[channel];

switch (channel) {
case X:
case Y:
case Y: {
// If there is no explicit width/height for discrete x/y scales
if (util.contains(['point', 'band'], scaleType)) {
if (channel === X && !size.width) {
Expand All @@ -228,16 +179,21 @@ function defaultRange(
// We will later replace these temporary names with
// the final name in assembleScaleRange()

const sizeType = getSizeType(channel);
const sizeSignal = model.getName(sizeType);

if (channel === Y && hasContinuousDomain(scaleType)) {
// For y continuous scale, we have to start from the height as the bottom part has the max value.
return [SignalRefWrapper.fromName(getSignalName, sizeSignal), 0];
} else {
return [0, SignalRefWrapper.fromName(getSignalName, sizeSignal)];
}
}
case SIZE: {
// TODO: support custom rangeMin, rangeMax
const zero = model.component.scales[channel].get('zero');
const rangeMin = sizeRangeMin(mark, zero, config);
const rangeMax = sizeRangeMax(mark, size, xyStepSignals, config);
const rangeMax = sizeRangeMax(mark, size, model, config);
if (isContinuousToDiscrete(scaleType)) {
return interpolateRange(
rangeMin,
Expand Down Expand Up @@ -341,12 +297,12 @@ function sizeRangeMin(mark: Mark, zero: boolean, config: Config) {

export const MAX_SIZE_RANGE_STEP_RATIO = 0.95;

function sizeRangeMax(
mark: Mark,
size: LayoutSizeMixins,
xyStepSignals: {x?: SignalRefWrapper; y?: SignalRefWrapper},
config: Config
): number | SignalRef {
function sizeRangeMax(mark: Mark, size: LayoutSizeMixins, model: UnitModel, config: Config): number | SignalRef {
const xyStepSignals = {
x: getBinStepSignal(model, 'x'),
y: getBinStepSignal(model, 'y')
};

switch (mark) {
case 'bar':
case 'tick': {
Expand Down
8 changes: 0 additions & 8 deletions src/log/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ export const INVALID_SPEC = 'Invalid spec';
// FIT
export const FIT_NON_SINGLE = 'Autosize "fit" only works for single views and layered views.';

export function cannotUseStepWithFit(sizeType: 'width' | 'height') {
return `Cannot use a "step" ${sizeType} when "autosize" is "fit".`;
}

export function cannotUseStepWithContinuous(sizeType: 'width' | 'height') {
return `Cannot use a "step" ${sizeType} when the ${sizeType === 'width' ? 'x' : 'y'}-field is continuous.`;
}

// SELECTION
export function cannotProjectOnChannelWithoutField(channel: Channel) {
return `Cannot project a selection on encoding channel "${channel}", which has no field.`;
Expand Down
2 changes: 2 additions & 0 deletions src/scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ export function scaleTypePrecedence(scaleType: ScaleType): number {
export const CONTINUOUS_TO_CONTINUOUS_SCALES: ScaleType[] = ['linear', 'log', 'pow', 'sqrt', 'symlog', 'time', 'utc'];
const CONTINUOUS_TO_CONTINUOUS_INDEX = toSet(CONTINUOUS_TO_CONTINUOUS_SCALES);

export const QUANTITATIVE_SCALES: ScaleType[] = ['linear', 'log', 'pow', 'sqrt', 'symlog'];

export const CONTINUOUS_TO_DISCRETE_SCALES: ScaleType[] = ['quantile', 'quantize', 'threshold'];
const CONTINUOUS_TO_DISCRETE_INDEX = toSet(CONTINUOUS_TO_DISCRETE_SCALES);

Expand Down
2 changes: 1 addition & 1 deletion test/compile/compile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ describe('compile/compile', () => {
x: {field: 'b', type: 'quantitative'}
}
}).spec;
expect(localLogger.warns[0]).toEqual(log.message.cannotUseStepWithFit('height'));
expect(localLogger.warns[0]).toEqual(log.message.stepDropped('height', 'fit'));
expect(spec.width).toEqual(200);
expect(spec.height).toEqual(200);
})
Expand Down
2 changes: 1 addition & 1 deletion test/compile/layoutsize/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('compile/layout', () => {
}
});

expect(localLogger.warns[0]).toEqual(log.message.cannotUseStepWithContinuous('width'));
expect(localLogger.warns[0]).toEqual(log.message.stepDropped('width', 'continuous'));

expect(model.component.layoutSize.get('width')).toBe(200);
})
Expand Down
Loading

0 comments on commit a9f33b4

Please sign in to comment.