You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
3.2 KiB
128 lines
3.2 KiB
import {ticks} from "d3-array"; |
|
import {format} from "d3-format"; |
|
import constant from "./constant"; |
|
import nice from "./nice"; |
|
import {default as continuous, copy} from "./continuous"; |
|
|
|
function deinterpolate(a, b) { |
|
return (b = Math.log(b / a)) |
|
? function(x) { return Math.log(x / a) / b; } |
|
: constant(b); |
|
} |
|
|
|
function reinterpolate(a, b) { |
|
return a < 0 |
|
? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); } |
|
: function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); }; |
|
} |
|
|
|
function pow10(x) { |
|
return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x; |
|
} |
|
|
|
function powp(base) { |
|
return base === 10 ? pow10 |
|
: base === Math.E ? Math.exp |
|
: function(x) { return Math.pow(base, x); }; |
|
} |
|
|
|
function logp(base) { |
|
return base === Math.E ? Math.log |
|
: base === 10 && Math.log10 |
|
|| base === 2 && Math.log2 |
|
|| (base = Math.log(base), function(x) { return Math.log(x) / base; }); |
|
} |
|
|
|
function reflect(f) { |
|
return function(x) { |
|
return -f(-x); |
|
}; |
|
} |
|
|
|
export default function log() { |
|
var scale = continuous(deinterpolate, reinterpolate).domain([1, 10]), |
|
domain = scale.domain, |
|
base = 10, |
|
logs = logp(10), |
|
pows = powp(10); |
|
|
|
function rescale() { |
|
logs = logp(base), pows = powp(base); |
|
if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows); |
|
return scale; |
|
} |
|
|
|
scale.base = function(_) { |
|
return arguments.length ? (base = +_, rescale()) : base; |
|
}; |
|
|
|
scale.domain = function(_) { |
|
return arguments.length ? (domain(_), rescale()) : domain(); |
|
}; |
|
|
|
scale.ticks = function(count) { |
|
var d = domain(), |
|
u = d[0], |
|
v = d[d.length - 1], |
|
r; |
|
|
|
if (r = v < u) i = u, u = v, v = i; |
|
|
|
var i = logs(u), |
|
j = logs(v), |
|
p, |
|
k, |
|
t, |
|
n = count == null ? 10 : +count, |
|
z = []; |
|
|
|
if (!(base % 1) && j - i < n) { |
|
i = Math.round(i) - 1, j = Math.round(j) + 1; |
|
if (u > 0) for (; i < j; ++i) { |
|
for (k = 1, p = pows(i); k < base; ++k) { |
|
t = p * k; |
|
if (t < u) continue; |
|
if (t > v) break; |
|
z.push(t); |
|
} |
|
} else for (; i < j; ++i) { |
|
for (k = base - 1, p = pows(i); k >= 1; --k) { |
|
t = p * k; |
|
if (t < u) continue; |
|
if (t > v) break; |
|
z.push(t); |
|
} |
|
} |
|
} else { |
|
z = ticks(i, j, Math.min(j - i, n)).map(pows); |
|
} |
|
|
|
return r ? z.reverse() : z; |
|
}; |
|
|
|
scale.tickFormat = function(count, specifier) { |
|
if (specifier == null) specifier = base === 10 ? ".0e" : ","; |
|
if (typeof specifier !== "function") specifier = format(specifier); |
|
if (count === Infinity) return specifier; |
|
if (count == null) count = 10; |
|
var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate? |
|
return function(d) { |
|
var i = d / pows(Math.round(logs(d))); |
|
if (i * base < base - 0.5) i *= base; |
|
return i <= k ? specifier(d) : ""; |
|
}; |
|
}; |
|
|
|
scale.nice = function() { |
|
return domain(nice(domain(), { |
|
floor: function(x) { return pows(Math.floor(logs(x))); }, |
|
ceil: function(x) { return pows(Math.ceil(logs(x))); } |
|
})); |
|
}; |
|
|
|
scale.copy = function() { |
|
return copy(scale, log().base(base)); |
|
}; |
|
|
|
return scale; |
|
}
|
|
|