Skip to content

Commit

Permalink
fix(scales): get code to compile
Browse files Browse the repository at this point in the history
  • Loading branch information
wyze committed Jun 22, 2021
1 parent 27d717a commit 2d5ebe7
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 159 deletions.
180 changes: 100 additions & 80 deletions packages/scales/src/compute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,93 @@ import sortBy from 'lodash/sortBy'
import last from 'lodash/last'
import isDate from 'lodash/isDate'
import { createDateNormalizer } from './timeHelpers'
import { ScaleAxis, ScaleSpec, Series, ScaleValue, SerieAxis, OtherScaleAxis } from './types'
import { ScaleAxis, ScaleSpec, Series, ScaleValue, SerieAxis } from './types'
import { computeScale } from './computeScale'

export const getOtherAxis = (axis: ScaleAxis): OtherScaleAxis<typeof axis> =>
axis === 'x' ? 'y' : 'x'
type XY = ReturnType<typeof generateSeriesXY>

const a = getOtherAxis('x')
if (a === 'x') {
console.log('crap')
type StackedXY = {
[K in keyof XY]: XY[K] & {
maxStacked: number
minStacked: number
}
}

type InputXYSeries = Record<'x' | 'y', number | string | Date | null>

interface Data {
x: number
xStacked: number | null
y: number
yStacked: number | null

// Allow template literal `xStacked/yStacked` to be set on line 213
[key: string]: number | null
}

type XYSeries = InputXYSeries & {
data: Data[]
}

interface ComputedXYSeries extends InputXYSeries {
data: Array<{
data: Data
position: {
x: ScaleValue | null
y: ScaleValue | null
}
}>
}

type Compare = <T>(a: T, b: T) => boolean

export const getOtherAxis = (axis: ScaleAxis): ScaleAxis => (axis === 'x' ? 'y' : 'x')

export const compareValues = (a: string | number, b: string | number) => a === b
export const compareDateValues = (a: Date, b: Date) => a.getTime() === b.getTime()

export const computeXYScalesForSeries = (_series, xScaleSpec, yScaleSpec, width, height) => {
export const computeXYScalesForSeries = (
_series: XYSeries[],
xScaleSpec: ScaleSpec,
yScaleSpec: ScaleSpec,
width: number,
height: number
) => {
const series = _series.map(serie => ({
...serie,
data: serie.data.map(d => ({ data: { ...d } })),
}))
})) as ComputedXYSeries[]

let xy = generateSeriesXY(series, xScaleSpec, yScaleSpec)
if (xScaleSpec.stacked === true) {
stackX(yScaleSpec.type, xy, series)
const xy = generateSeriesXY(series, xScaleSpec, yScaleSpec)
if ('stacked' in xScaleSpec && xScaleSpec.stacked === true) {
stackX(xy as StackedXY, series)
}
if (yScaleSpec.stacked === true) {
stackY(xScaleSpec.type, xy, series)
if ('stacked' in yScaleSpec && yScaleSpec.stacked === true) {
stackY(xy as StackedXY, series)
}

const xScale = computeScale({ ...xScaleSpec, axis: 'x' }, xy.x, width, 'x')
const yScale = computeScale({ ...yScaleSpec, axis: 'y' }, xy.y, height, 'y')
const xScale = computeScale(xScaleSpec, xy.x, width, 'x')
const yScale = computeScale(yScaleSpec, xy.y, height, 'y')

series.forEach(serie => {
serie.data.forEach(d => {
d.position = {
x:
xScale.stacked === true
'stacked' in xScale && xScale.stacked === true
? d.data.xStacked === null
? null
: xScale(d.data.xStacked)
: d.data.x === null
? null
: xScale(d.data.x),
: xScale(d.data.x) ?? null,
y:
yScale.stacked === true
'stacked' in yScale && yScale.stacked === true
? d.data.yStacked === null
? null
: yScale(d.data.yStacked)
: d.data.y === null
? null
: yScale(d.data.y),
: yScale(d.data.y) ?? null,
}
})
})
Expand Down Expand Up @@ -97,64 +134,71 @@ export const generateSeriesAxis = <Axis extends ScaleAxis, Value extends ScaleVa
series.forEach(serie => {
serie.data.forEach(d => {
const value = getValue(d)
if (value !== null) {
setValue(d, parseFloat(value))

if (value) {
setValue(d, (parseFloat(String(value)) as unknown) as Value)
}
})
})
} else if (scaleSpec.type === 'time' && scaleSpec.format !== 'native') {
// `native` means we already have Date instances,
// otherwise we have to convert the values to Date.
const parseTime = createDateNormalizer(scaleSpec)

series.forEach(serie => {
serie.data.forEach(d => {
const value = getValue(d)
setValue(d, getValue(d) === null ? null : parseTime(getValue(d)))

if (value) {
setValue(d, (parseTime(value as Date) as unknown) as Value)
}
})
})
}

let all = []
const values: unknown[] = []

series.forEach(serie => {
serie.data.forEach(d => {
all.push(getValue(d))
values.push(getValue(d))
})
})

let min, max
if (scaleSpec.type === 'linear') {
all = uniq(all)
all = sortBy(all, v => v)
min = Math.min(...all)
max = Math.max(...all)
} else if (scaleSpec.type === 'time') {
all = uniqBy(all, v => v.getTime())
all = all
.slice(0)
.sort((a, b) => b - a)
.reverse()
min = all[0]
max = last(all)
} else {
all = uniq(all)
min = all[0]
max = last(all)
}
switch (scaleSpec.type) {
case 'linear': {
const all = sortBy(uniq(values as number[]), v => v)

return { all, min: Math.min(...all), max: Math.max(...all) }
}
case 'time': {
const all = uniqBy(values as Date[], v => v.getTime())
.slice(0)
.sort((a, b) => b.getTime() - a.getTime())
.reverse()

return { all, min: all[0], max: last(all) }
}
default: {
const all = uniq(values)

return { all, min, max }
return { all, min: all[0], max: last(all) }
}
}
}

export const stackAxis = (axis: ScaleAxis, otherType: ScaleAxis, xy: any, series: any) => {
export const stackAxis = (axis: ScaleAxis, xy: StackedXY, series: ComputedXYSeries[]) => {
const otherAxis = getOtherAxis(axis)
const all: number[] = []

let all = []
xy[otherAxis].all.forEach(v => {
const compare = isDate(v) ? compareDateValues : compareValues
const stack = []
const compare = (isDate(v) ? compareDateValues : compareValues) as Compare
const stack: Array<number | null> = []

series.forEach(serie => {
const datum = serie.data.find(d => compare(d.data[otherAxis], v))
let value = null
let stackValue = null

if (datum !== undefined) {
value = datum.data[axis]
if (value !== null) {
Expand All @@ -165,45 +209,21 @@ export const stackAxis = (axis: ScaleAxis, otherType: ScaleAxis, xy: any, series
stackValue = head + value
}
}

datum.data[`${axis}Stacked`] = stackValue
}

stack.push(stackValue)
all.push(stackValue)

if (stackValue !== null) {
all.push(stackValue)
}
})
})
all = all.filter(v => v !== null)

xy[axis].minStacked = Math.min(...all)
xy[axis].maxStacked = Math.max(...all)
}

export const stackX = (xy, otherType: ScaleAxis, series) => stackAxis('x', xy, otherType, series)
export const stackY = (xy, otherType: ScaleAxis, series) => stackAxis('y', xy, otherType, series)

export const computeAxisSlices = (axis: ScaleAxis, data) => {
const otherAxis = getOtherAxis(axis)

return data[otherAxis].all.map(v => {
const slice = {
id: v,
[otherAxis]: data[`${otherAxis}Scale`](v),
data: [],
}
const compare = isDate(v) ? compareDateValues : compareValues
data.series.forEach(serie => {
const datum = serie.data.find(d => compare(d.data[otherAxis], v))
if (datum !== undefined) {
slice.data.push({
...datum,
serie,
})
}
})
slice.data.reverse()

return slice
})
}

export const computeXSlices = data => computeAxisSlices('x', data)
export const computeYSlices = data => computeAxisSlices('y', data)
const stackX = (xy: StackedXY, series: ComputedXYSeries[]) => stackAxis('x', xy, series)
const stackY = (xy: StackedXY, series: ComputedXYSeries[]) => stackAxis('y', xy, series)
91 changes: 18 additions & 73 deletions packages/scales/src/computeScale.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,31 @@
import {
ScaleAxis,
ScaleBand,
ScaleBandSpec,
ScalePoint,
ScalePointSpec,
ScaleSpec,
ComputedSerieAxis,
ScaleValue,
StringValue,
NumericValue,
ScaleLinear,
ScaleLinearSpec,
ScaleLogSpec,
ScaleLog,
ScaleSymLogSpec,
} from './types'
import { ScaleAxis, ScaleSpec, ComputedSerieAxis, ScaleValue } from './types'
import { createLinearScale } from './linearScale'
import { createPointScale } from './pointScale'
import { createBandScale } from './bandScale'
import { createTimeScale } from './timeScale'
import { createLogScale } from './logScale'
import { createSymLogScale } from './symLogScale'

// override for linear scale
export function computeScale<Input extends NumericValue>(
spec: ScaleLinearSpec,
data: ComputedSerieAxis<Input>,
size: number,
axis: ScaleAxis
): ScaleLinear<Input>

// override for point scale
export function computeScale<Input extends StringValue>(
spec: ScalePointSpec,
data: ComputedSerieAxis<Input>,
size: number,
axis: ScaleAxis
): ScalePoint<Input>

// override for band scale
export function computeScale<Input extends StringValue>(
spec: ScaleBandSpec,
data: ComputedSerieAxis<Input>,
size: number,
axis: ScaleAxis
): ScaleBand<Input>

// override for log scale
export function computeScale(
spec: ScaleLogSpec,
data: ComputedSerieAxis<number>,
size: number,
axis: ScaleAxis
): ScaleLog

// override for symlog scale
export function computeScale(
spec: ScaleSymLogSpec,
data: ComputedSerieAxis<number>,
size: number,
axis: ScaleAxis
): ScaleLog

export function computeScale<Input extends ScaleValue, Output>(
export function computeScale<Input extends ScaleValue>(
spec: ScaleSpec,
data: ComputedSerieAxis<Input>,
data: ComputedSerieAxis<any>,
size: number,
axis: ScaleAxis
) {
if (spec.type === 'linear') {
return createLinearScale(spec, data, size, axis)
} else if (spec.type === 'point') {
return createPointScale<Input>(spec, data, size)
} else if (spec.type === 'band') {
return createBandScale<Input>(spec, data, size)
} else if (spec.type === 'time') {
return createTimeScale(spec, data, size)
} else if (spec.type === 'log') {
return createLogScale(spec, data, size, axis)
} else if (spec.type === 'symlog') {
return createSymLogScale(spec, data, size, axis)
switch (spec.type) {
case 'linear':
return createLinearScale(spec, data, size, axis)
case 'point':
return createPointScale<Input>(spec, data, size)
case 'band':
return createBandScale<Input>(spec, data, size)
case 'time':
return createTimeScale(spec, data, size)
case 'log':
return createLogScale(spec, data, size, axis)
case 'symlog':
return createSymLogScale(spec, data, size, axis)
default:
throw new Error('invalid scale spec')
}

throw new Error('invalid scale spec')
}
4 changes: 2 additions & 2 deletions packages/scales/src/timeScale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { NumberValue, scaleTime, scaleUtc } from 'd3-scale'
import { createDateNormalizer } from './timeHelpers'
import { ComputedSerieAxis, ScaleTime, ScaleTimeSpec } from './types'

export const createTimeScale = <Input extends Date | NumberValue, Output>(
export const createTimeScale = <Input extends Date | NumberValue>(
{
format = 'native',
precision = 'millisecond',
Expand Down Expand Up @@ -41,7 +41,7 @@ export const createTimeScale = <Input extends Date | NumberValue, Output>(
if (nice === true) scale.nice()
else if (typeof nice === 'object' || typeof nice === 'number') scale.nice(nice)

const typedScale = (scale as unknown) as ScaleTime<Input, Output>
const typedScale = (scale as unknown) as ScaleTime<Input>

typedScale.type = 'time'
typedScale.useUTC = useUTC
Expand Down
Loading

0 comments on commit 2d5ebe7

Please sign in to comment.