UNPKG

@visactor/vscale

Version:

Scales for visual encoding, used in VGrammar, VTable

184 lines (179 loc) 10.8 kB
import { toNumber, isGreater, isLess, isNumber, isValid, isNil, clamp } from "@visactor/vutils"; import { OrdinalScale, implicit } from "./ordinal-scale"; import { bandSpace, calculateBandwidthFromWholeRangeSize, calculateWholeRangeFromRangeFactor, scaleWholeRangeSize } from "./utils/utils"; import { ScaleEnum } from "./type"; import { stepTicks, ticks } from "./utils/tick-sample-int"; export class BandScale extends OrdinalScale { constructor(slience) { super(), this.type = ScaleEnum.Band, this._range = [ 0, 1 ], this._step = void 0, this._bandwidth = void 0, this._isFixed = !1, this._round = !1, this._paddingInner = 0, this._paddingOuter = 0, this._align = .5, this._unknown = void 0, delete this.unknown, this.rescale(slience); } rescale(slience, changeProperty) { if (slience) return this; this._wholeRange = null; const wholeRange = this._calculateWholeRange(this._range, changeProperty), n = super.domain().length, reverse = wholeRange[1] < wholeRange[0]; let start = wholeRange[Number(reverse) - 0]; const stop = wholeRange[1 - Number(reverse)], space = bandSpace(n, this._paddingInner, this._paddingOuter); return this._step = (stop - start) / Math.max(1, space || 1), this._round && (this._step = Math.floor(this._step)), start += (stop - start - this._step * (n - this._paddingInner)) * this._align, this.isBandwidthFixed() || (this._bandwidth = this._step * (1 - this._paddingInner)), this._round && (start = Math.round(start), this.isBandwidthFixed() || (this._bandwidth = Math.round(this._bandwidth))), this._bandRangeState = { reverse: reverse, start: reverse ? clamp(start + this._step * (n - 1), wholeRange[1], wholeRange[0]) : clamp(start, wholeRange[0], wholeRange[1]), min: reverse ? wholeRange[1] : wholeRange[0], max: stop, count: n }, this.generateFishEyeTransform(), this; } scale(d) { if (!this._bandRangeState) return; const key = `${d}`, special = this._getSpecifiedValue(key); if (void 0 !== special) return special; let i = this._index.get(key); if (!i) { if (this._unknown !== implicit) return this._unknown; i = this._domain.push(d), this._index.set(key, i); } const {count: count, start: start, reverse: reverse, min: min, max: max} = this._bandRangeState, output = start + (reverse ? -1 : 1) * ((i - 1) % count) * this._step; return clamp(this._fishEyeTransform ? this._fishEyeTransform(output) : output, min, max); } _calculateWholeRange(range, changeProperty) { if (this._wholeRange) return this._wholeRange; if ((this._minBandwidth || this._maxBandwidth) && !this._isBandwidthFixedByUser()) { let wholeSize; if (isValid(this._rangeFactorStart) && isValid(this._rangeFactorEnd) && 2 === range.length) { const wholeRange = calculateWholeRangeFromRangeFactor(range, [ this._rangeFactorStart, this._rangeFactorEnd ]); wholeSize = Math.abs(wholeRange[1] - wholeRange[0]); } else wholeSize = Math.abs(range[1] - range[0]); const autoBandwidth = calculateBandwidthFromWholeRangeSize(super.domain().length, wholeSize, this._paddingInner, this._paddingOuter, this._round); autoBandwidth < this._minBandwidth ? (this._bandwidth = this._minBandwidth, this._isFixed = !0) : autoBandwidth > this._maxBandwidth ? (this._bandwidth = this._maxBandwidth, this._isFixed = !0) : (this._bandwidth = autoBandwidth, this._isFixed = !1); } if (this.isBandwidthFixed()) { const wholeLength = scaleWholeRangeSize(super.domain().length, this._bandwidth, this._paddingInner, this._paddingOuter) * Math.sign(range[1] - range[0]), rangeFactorSize = Math.min((range[1] - range[0]) / wholeLength, 1); if (isValid(this._rangeFactorStart) && isValid(this._rangeFactorEnd)) { const canAlignStart = this._rangeFactorStart + rangeFactorSize <= 1, canAlignEnd = this._rangeFactorEnd - rangeFactorSize >= 0; if ("rangeFactorStart" === changeProperty && canAlignStart ? this._rangeFactorEnd = this._rangeFactorStart + rangeFactorSize : "rangeFactorEnd" === changeProperty && canAlignEnd ? this._rangeFactorStart = this._rangeFactorEnd - rangeFactorSize : range[0] <= range[1] ? canAlignStart ? this._rangeFactorEnd = this._rangeFactorStart + rangeFactorSize : canAlignEnd ? this._rangeFactorStart = this._rangeFactorEnd - rangeFactorSize : (this._rangeFactorStart = 0, this._rangeFactorEnd = rangeFactorSize) : canAlignEnd ? this._rangeFactorStart = this._rangeFactorEnd - rangeFactorSize : canAlignStart ? this._rangeFactorEnd = this._rangeFactorStart + rangeFactorSize : (this._rangeFactorStart = 1 - rangeFactorSize, this._rangeFactorEnd = 1), wholeLength > 0) { const r0 = range[0] - wholeLength * this._rangeFactorStart, r1 = r0 + wholeLength; this._wholeRange = [ r0, r1 ]; } else { const r1 = range[1] + wholeLength * (1 - this._rangeFactorEnd), r0 = r1 - wholeLength; this._wholeRange = [ r0, r1 ]; } } else this._rangeFactorStart = 0, this._rangeFactorEnd = rangeFactorSize, this._wholeRange = [ range[0], range[0] + wholeLength ]; return this._wholeRange; } return super._calculateWholeRange(range); } calculateWholeRangeSize() { const wholeRange = this._calculateWholeRange(this._range); return Math.abs(wholeRange[1] - wholeRange[0]); } calculateVisibleDomain(range) { const domain = this._domain; if (isValid(this._rangeFactorStart) && isValid(this._rangeFactorEnd) && domain.length) { const d0 = this._getInvertIndex(range[0]), d1 = this._getInvertIndex(range[1]); return domain.slice(Math.min(d0, d1), Math.max(d0, d1) + 1); } return domain; } domain(_, slience) { return _ ? (super.domain(_), this.rescale(slience)) : super.domain(); } range(_, slience) { return _ ? (this._range = [ toNumber(_[0]), toNumber(_[1]) ], this.rescale(slience)) : this._range; } rangeRound(_, slience) { return this._range = [ toNumber(_[0]), toNumber(_[1]) ], this._round = !0, this.rescale(slience); } ticks(count = 10) { const d = this.calculateVisibleDomain(this._range); if (-1 === count) return d; return ticks(0, d.length - 1, count, !1).map((i => d[i])); } tickData(count = 10) { return this.ticks(count).map(((tick, index) => ({ index: index, tick: tick, value: (this.scale(tick) - this._range[0] + this._bandwidth / 2) / (this._range[1] - this._range[0]) }))); } forceTicks(count = 10) { const d = this.calculateVisibleDomain(this._range); return ticks(0, d.length - 1, count, !0).filter((i => i < d.length)).map((i => d[i])); } stepTicks(step) { const d = this.calculateVisibleDomain(this._range); return stepTicks(0, d.length - 1, step).map((i => d[i])); } _getInvertIndex(d) { let i = 0; const halfStep = this.step() / 2, halfBandwidth = this.bandwidth() / 2, len = this._domain.length, range = this.range(), reverse = range[0] > range[range.length - 1]; for (i = 0; i < len; i++) { const r = this.scale(this._domain[i]) + halfBandwidth; if (0 === i && (!reverse && !isGreater(d, r + halfStep) || reverse && !isLess(d, r - halfStep))) break; if (i === len - 1) break; if (!isLess(d, r - halfStep) && !isGreater(d, r + halfStep)) break; } return i >= 0 && i <= len - 1 ? i : len - 1; } invert(d) { return this._domain[this._getInvertIndex(d)]; } padding(p, slience) { return void 0 !== p ? (this._paddingOuter = Math.max(0, Math.min(Array.isArray(p) ? Math.min.apply(null, p) : p)), this._paddingInner = this._paddingOuter, this.rescale(slience)) : this._paddingInner; } paddingInner(_, slience) { return void 0 !== _ ? (this._paddingInner = Math.max(0, Math.min(1, _)), this.rescale(slience)) : this._paddingInner; } paddingOuter(_, slience) { return void 0 !== _ ? (this._paddingOuter = Math.max(0, Math.min(1, _)), this.rescale(slience)) : this._paddingOuter; } step() { return this._step; } round(_, slience) { return void 0 !== _ ? (this._round = _, this.rescale(slience)) : this._round; } align(_, slience) { return void 0 !== _ ? (this._align = Math.max(0, Math.min(1, _)), this.rescale(slience)) : this._align; } rangeFactor(_, slience) { return _ ? (super.rangeFactor(_), this.rescale(slience)) : super.rangeFactor(); } rangeFactorStart(_, slience) { return isNil(_) ? super.rangeFactorStart() : (super.rangeFactorStart(_), this.rescale(slience, "rangeFactorStart")); } rangeFactorEnd(_, slience) { return isNil(_) ? super.rangeFactorEnd() : (super.rangeFactorEnd(_), this.rescale(slience, "rangeFactorEnd")); } bandwidth(_, slience) { return _ ? ("auto" === _ ? (this._bandwidth = void 0, this._isFixed = !1) : (this._bandwidth = _, this._isFixed = !0), this._userBandwidth = _, this.rescale(slience)) : this._bandwidth; } maxBandwidth(_, slience) { return _ ? (this._maxBandwidth = "auto" === _ ? void 0 : _, this.rescale(slience)) : this._maxBandwidth; } minBandwidth(_, slience) { return _ ? (this._minBandwidth = "auto" === _ ? void 0 : _, this.rescale(slience)) : this._minBandwidth; } fishEye(options, slience, clear) { return options || clear ? (this._fishEyeOptions = options, this._fishEyeTransform = null, this.rescale(slience)) : this._fishEyeOptions; } isBandwidthFixed() { return this._isFixed && !!this._bandwidth; } _isBandwidthFixedByUser() { return this._isFixed && this._userBandwidth && isNumber(this._userBandwidth); } clone() { var _a, _b, _c; return new BandScale(!0).domain(this._domain, !0).range(this._range, !0).round(this._round, !0).paddingInner(this._paddingInner, !0).paddingOuter(this._paddingOuter, !0).align(this._align, !0).bandwidth(null !== (_a = this._userBandwidth) && void 0 !== _a ? _a : "auto", !0).maxBandwidth(null !== (_b = this._maxBandwidth) && void 0 !== _b ? _b : "auto", !0).minBandwidth(null !== (_c = this._maxBandwidth) && void 0 !== _c ? _c : "auto"); } }