Skip to content

Commit b0f1d83

Browse files
committed
SamplerType is a subset of the only params neeeded from the Chart
1 parent 590b7cc commit b0f1d83

File tree

8 files changed

+114
-88
lines changed

8 files changed

+114
-88
lines changed

src/chart.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { line as d3Line, Line } from 'd3-shape'
22
import { format as d3Format } from 'd3-format'
3-
import { scaleLinear as d3ScaleLinear, scaleLog as d3ScaleLog, ScaleLinear, ScaleLogarithmic } from 'd3-scale'
3+
import { scaleLinear as d3ScaleLinear, scaleLog as d3ScaleLog } from 'd3-scale'
44
import { axisLeft as d3AxisLeft, axisBottom as d3AxisBottom, Axis } from 'd3-axis'
55
import { zoom as d3Zoom } from 'd3-zoom'
66
// @ts-ignore
77
import { select as d3Select, pointer as d3Pointer } from 'd3-selection'
88
import { interpolateRound as d3InterpolateRound } from 'd3-interpolate'
99
import EventEmitter from 'events'
1010

11-
import { FunctionPlotOptions, FunctionPlotDatum } from './types'
11+
import { FunctionPlotOptions, FunctionPlotDatum, FunctionPlotScale } from './types'
1212

1313
import annotations from './helpers/annotations'
1414
import mousetip from './tip'
@@ -39,8 +39,8 @@ export interface ChartMeta {
3939
*/
4040
height?: number
4141
zoomBehavior?: any
42-
xScale?: ScaleLinear<number, number> | ScaleLogarithmic<number, number>
43-
yScale?: ScaleLinear<number, number> | ScaleLogarithmic<number, number>
42+
xScale?: FunctionPlotScale
43+
yScale?: FunctionPlotScale
4444
xAxis?: Axis<any>
4545
yAxis?: Axis<any>
4646
xDomain?: number[]

src/evaluate.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import globals from './globals'
2-
import { interval, builtIn } from './samplers'
2+
import interval from './samplers/interval'
3+
import builtIn from './samplers/builtIn'
34

45
import { Chart } from './index'
56
import { FunctionPlotDatum } from './types'
67

7-
const evalTypeFn = {
8-
interval,
9-
builtIn
10-
}
8+
type SamplerTypeFn = typeof interval | typeof builtIn
119

1210
/**
1311
* Computes the endpoints x_lo, x_hi of the range
@@ -34,10 +32,27 @@ function computeEndpoints(scale: any, d: any): [number, number] {
3432
*/
3533
function evaluate(chart: Chart, d: FunctionPlotDatum) {
3634
const range = computeEndpoints(chart.meta.xScale, d)
37-
const evalFn = evalTypeFn[d.sampler]
35+
36+
let samplerFn: SamplerTypeFn
37+
if (d.sampler === 'builtIn') {
38+
samplerFn = builtIn
39+
} else if (d.sampler === 'interval') {
40+
samplerFn = interval
41+
} else {
42+
throw new Error(`Invalid sampler function ${d.sampler}`)
43+
}
44+
3845
const nSamples = d.nSamples || Math.min(globals.MAX_ITERATIONS, globals.DEFAULT_ITERATIONS || chart.meta.width * 2)
3946

40-
const data = evalFn(chart, d, range, nSamples)
47+
const data = samplerFn({
48+
d,
49+
range,
50+
xScale: chart.meta.xScale,
51+
yScale: chart.meta.yScale,
52+
xAxis: chart.options.xAxis,
53+
yAxis: chart.options.yAxis,
54+
nSamples
55+
})
4156
// NOTE: it's impossible to listen for the first eval event
4257
// as the event is already fired when a listener is attached
4358
chart.emit('eval', data, d.index, d.isHelper)

src/helpers/secant.ts

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import {select as d3Select, Selection} from 'd3-selection'
1+
import { select as d3Select, Selection } from 'd3-selection'
22

33
import { builtIn as builtInEvaluator } from './eval'
44
import datumDefaults from '../datum-defaults'
55
import { polyline } from '../graph-types/'
66

7-
import { Chart } from "../index";
7+
import { Chart } from '../index'
88
import { FunctionPlotDatumScope, FunctionPlotDatum, FunctionPlotDatumSecant } from '../types'
99

10-
export default function secant (chart: Chart) {
10+
export default function secant(chart: Chart) {
1111
const secantDefaults = datumDefaults({
1212
isHelper: true,
1313
skipTip: true,
@@ -16,11 +16,11 @@ export default function secant (chart: Chart) {
1616
graphType: 'polyline'
1717
})
1818

19-
function computeSlope (scope: FunctionPlotDatumScope) {
19+
function computeSlope(scope: FunctionPlotDatumScope) {
2020
scope.m = (scope.y1 - scope.y0) / (scope.x1 - scope.x0)
2121
}
2222

23-
function updateLine (d: FunctionPlotDatum, secant: FunctionPlotDatumSecant) {
23+
function updateLine(d: FunctionPlotDatum, secant: FunctionPlotDatumSecant) {
2424
if (!('x0' in secant)) {
2525
throw Error('secant must have the property `x0` defined')
2626
}
@@ -29,20 +29,20 @@ export default function secant (chart: Chart) {
2929
const x0 = secant.x0
3030
const x1 = typeof secant.x1 === 'number' ? secant.x1 : Infinity
3131
Object.assign(secant.scope, {
32-
x0: x0,
33-
x1: x1,
32+
x0,
33+
x1,
3434
y0: builtInEvaluator(d, 'fn', { x: x0 }),
3535
y1: builtInEvaluator(d, 'fn', { x: x1 })
3636
})
3737
computeSlope(secant.scope)
3838
}
3939

40-
function setFn (d: FunctionPlotDatum, secant: FunctionPlotDatumSecant) {
40+
function setFn(d: FunctionPlotDatum, secant: FunctionPlotDatumSecant) {
4141
updateLine(d, secant)
4242
secant.fn = 'm * (x - x0) + y0'
4343
}
4444

45-
function setMouseListener (d: FunctionPlotDatum, secantObject: FunctionPlotDatumSecant) {
45+
function setMouseListener(d: FunctionPlotDatum, secantObject: FunctionPlotDatumSecant) {
4646
const self = this
4747
if (secantObject.updateOnMouseMove && !secantObject.$$mouseListener) {
4848
secantObject.$$mouseListener = function ({ x }: any) {
@@ -54,12 +54,12 @@ export default function secant (chart: Chart) {
5454
}
5555
}
5656

57-
function computeLines (d: FunctionPlotDatum) {
57+
function computeLines(d: FunctionPlotDatum) {
5858
const self = this
5959
const data = []
6060
d.secants = d.secants || []
6161
for (let i = 0; i < d.secants.length; i += 1) {
62-
const secant = d.secants[i] = Object.assign({}, secantDefaults, d.secants[i])
62+
const secant = (d.secants[i] = Object.assign({}, secantDefaults, d.secants[i]))
6363
// necessary to make the secant have the same color as d
6464
secant.index = d.index
6565
if (!secant.fn) {
@@ -71,25 +71,19 @@ export default function secant (chart: Chart) {
7171
return data
7272
}
7373

74-
const secant = function (selection: Selection<any, FunctionPlotDatum, any, any>) {
74+
function secant(selection: Selection<any, FunctionPlotDatum, any, any>) {
7575
selection.each(function (d) {
7676
const el = d3Select(this)
7777
const data = computeLines.call(selection, d)
78-
const innerSelection = el.selectAll('g.secant')
79-
.data(data)
78+
const innerSelection = el.selectAll('g.secant').data(data)
8079

81-
const innerSelectionEnter = innerSelection.enter()
82-
.append('g')
83-
.attr('class', 'secant')
80+
const innerSelectionEnter = innerSelection.enter().append('g').attr('class', 'secant')
8481

8582
// enter + update
86-
innerSelection.merge(innerSelectionEnter)
87-
.call(polyline(chart))
83+
innerSelection.merge(innerSelectionEnter).call(polyline(chart))
8884

8985
// change the opacity of the secants
90-
innerSelection.merge(innerSelectionEnter)
91-
.selectAll('path')
92-
.attr('opacity', 0.5)
86+
innerSelection.merge(innerSelectionEnter).selectAll('path').attr('opacity', 0.5)
9387

9488
// exit
9589
innerSelection.exit().remove()

src/samplers/builtIn.ts

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import clamp from 'clamp'
33
import utils from '../utils'
44
import { builtIn as evaluate } from '../helpers/eval'
55

6-
import { Chart } from '../chart'
7-
import { FunctionPlotDatum } from '../types'
6+
import { FunctionPlotDatum, FunctionPlotScale } from '../types'
7+
import { SamplerParams, SamplerFn } from './types'
88

99
function checkAsymptote(
1010
d0: number[],
@@ -45,17 +45,15 @@ function checkAsymptote(
4545
/**
4646
* Splits the evaluated data into arrays, each array is separated by any asymptote found
4747
* through the process of detecting slope/sign brusque changes
48-
* @param chart
49-
* @param d
50-
* @param data
48+
*
5149
* @returns {Array[]}
5250
*/
53-
function split(chart: Chart, d: FunctionPlotDatum, data: number[][]) {
51+
function split(d: FunctionPlotDatum, data: number[][], yScale: FunctionPlotScale): Array<any> {
5452
let i, oldSign
5553
let deltaX
5654
let st = []
5755
const sets = []
58-
const domain = chart.meta.yScale.domain()
56+
const domain = yScale.domain()
5957
const yMin = domain[0]
6058
const yMax = domain[1]
6159

@@ -105,77 +103,78 @@ function split(chart: Chart, d: FunctionPlotDatum, data: number[][]) {
105103
return sets
106104
}
107105

108-
function linear(chart: Chart, d: FunctionPlotDatum, range: [number, number], n: number) {
109-
const allX = utils.space(chart.options.xAxis.type, range, n)
110-
const yDomain = chart.meta.yScale.domain()
106+
function linear(samplerParams: SamplerParams): Array<any> {
107+
const allX = utils.space(samplerParams.xAxis, samplerParams.range, samplerParams.nSamples)
108+
const yDomain = samplerParams.yScale.domain()
111109
const yDomainMargin = yDomain[1] - yDomain[0]
112110
const yMin = yDomain[0] - yDomainMargin * 1e5
113111
const yMax = yDomain[1] + yDomainMargin * 1e5
114112
let data = []
115113
for (let i = 0; i < allX.length; i += 1) {
116114
const x = allX[i]
117-
const y = evaluate(d, 'fn', { x })
115+
const y = evaluate(samplerParams.d, 'fn', { x })
118116
if (utils.isValidNumber(x) && utils.isValidNumber(y)) {
119117
data.push([x, clamp(y, yMin, yMax)])
120118
}
121119
}
122-
data = split(chart, d, data)
120+
data = split(samplerParams.d, data, samplerParams.yScale)
123121
return data
124122
}
125123

126-
function parametric(chart: Chart, d: FunctionPlotDatum, range: [number, number], nSamples: number) {
124+
function parametric(samplerParams: SamplerParams): Array<any> {
127125
// range is mapped to canvas coordinates from the input
128126
// for parametric plots the range will tell the start/end points of the `t` param
129-
const parametricRange = d.range || [0, 2 * Math.PI]
130-
const tCoords = utils.space(chart.options.xAxis.type, parametricRange, nSamples)
127+
const parametricRange = samplerParams.d.range || [0, 2 * Math.PI]
128+
const tCoords = utils.space(samplerParams.xAxis, parametricRange, samplerParams.nSamples)
131129
const samples = []
132130
for (let i = 0; i < tCoords.length; i += 1) {
133131
const t = tCoords[i]
134-
const x = evaluate(d, 'x', { t })
135-
const y = evaluate(d, 'y', { t })
132+
const x = evaluate(samplerParams.d, 'x', { t })
133+
const y = evaluate(samplerParams.d, 'y', { t })
136134
samples.push([x, y])
137135
}
138136
return [samples]
139137
}
140138

141-
function polar(chart: Chart, d: FunctionPlotDatum, range: [number, number], nSamples: number) {
139+
function polar(samplerParams: SamplerParams): Array<any> {
142140
// range is mapped to canvas coordinates from the input
143141
// for polar plots the range will tell the start/end points of the `theta` param
144-
const polarRange = d.range || [-Math.PI, Math.PI]
145-
const thetaSamples = utils.space(chart.options.xAxis.type, polarRange, nSamples)
142+
const polarRange = samplerParams.d.range || [-Math.PI, Math.PI]
143+
const thetaSamples = utils.space(samplerParams.xAxis, polarRange, samplerParams.nSamples)
146144
const samples = []
147145
for (let i = 0; i < thetaSamples.length; i += 1) {
148146
const theta = thetaSamples[i]
149-
const r = evaluate(d, 'r', { theta })
147+
const r = evaluate(samplerParams.d, 'r', { theta })
150148
const x = r * Math.cos(theta)
151149
const y = r * Math.sin(theta)
152150
samples.push([x, y])
153151
}
154152
return [samples]
155153
}
156154

157-
function points(chart: Chart, d: FunctionPlotDatum, range: [number, number], nSamples: number) {
158-
return [d.points]
155+
function points(samplerParams: SamplerParams): Array<any> {
156+
return [samplerParams.d.points]
159157
}
160158

161-
function vector(chart: Chart, d: FunctionPlotDatum, range: [number, number], nSamples: number) {
159+
function vector(sampleParams: SamplerParams): Array<any> {
160+
const d = sampleParams.d
162161
d.offset = d.offset || [0, 0]
163162
return [[d.offset, [d.vector[0] + d.offset[0], d.vector[1] + d.offset[1]]]]
164163
}
165164

166-
const sampler = function (chart: Chart, d: FunctionPlotDatum, range: [number, number], nSamples: number) {
165+
const sampler: SamplerFn = function sampler(samplerParams: SamplerParams): Array<any> {
167166
const fnTypes = {
168167
parametric,
169168
polar,
170169
points,
171170
vector,
172171
linear
173172
}
174-
if (!(d.fnType in fnTypes)) {
175-
throw Error(d.fnType + ' is not supported in the `builtIn` sampler')
173+
if (!(samplerParams.d.fnType in fnTypes)) {
174+
throw Error(samplerParams.d.fnType + ' is not supported in the `builtIn` sampler')
176175
}
177176
// @ts-ignore
178-
return fnTypes[d.fnType].apply(null, arguments)
177+
return fnTypes[samplerParams.d.fnType].apply(null, arguments)
179178
}
180179

181180
export default sampler

0 commit comments

Comments
 (0)