function fitViewBox (domainX, domainY, rangeX, rangeY) {
const lenDomainX = Math.abs(domainX[1] - domainX[0])
const lenDomainY = Math.abs(domainY[1] - domainY[0])
const lenRangeX = Math.abs(rangeX[1] - rangeX[0])
const lenRangeY = Math.abs(rangeY[1] - rangeY[0])
const factorX = lenDomainX / lenRangeX
const factorY = lenDomainY / lenRangeY
const factor = Math.max(factorX, factorY)
const contentX = lenDomainX / factor
const contentY = lenDomainY / factor
const marginX = (lenRangeX - contentX) / 2
const marginY = (lenRangeY - contentY) / 2
let x0 = d3.min(domainX) - marginX * factor
let x1 = d3.max(domainX) + marginX * factor
if (domainX[0] > domainX[1]) {
[x0, x1] = [x1, x0]
}
let y0 = d3.min(domainY) - marginY * factor
let y1 = d3.max(domainY) + marginY * factor
if (domainY[0] > domainY[1]) {
[y0, y1] = [y1, y0]
}
const scaleX = d3.scaleLinear()
.domain([x0, x1])
.range(rangeX)
const scaleY = d3.scaleLinear()
.domain([y0, y1])
.range(rangeY)
const scale = (x, y) => [scaleX(x), scaleY(y)]
const lengthX = value => Math.abs(scaleX(value) - scaleX(0))
const lengthY = value => Math.abs(scaleY(value) - scaleY(0))
scale.x = scaleX
scale.y = scaleY
scale.lengthX = lengthX
scale.lengthY = lengthY
scale.points = coordinates => coordinates.map((x, i) => i % 2 === 0 ? scaleX(x) : scaleY(x))
scale.ellipse = (selection, options) => {
selection.attr('cx', scaleX(options.cx))
selection.attr('cy', scaleY(options.cy))
selection.attr('rx', lengthX(options.rx))
selection.attr('ry', lengthY(options.ry))
}
scale.line = (selection, options) => {
selection.attr('x1', scaleX(options.x1))
selection.attr('y1', scaleY(options.y1))
selection.attr('x2', scaleX(options.x2))
selection.attr('y2', scaleY(options.y2))
}
return scale
}