Comments (9)
Are you imagining something like d3.interpolateMeta(a, b) that takes two interpolators and returns an interpolator that linearly interpolates the output of a and b?
from d3-interpolate.
Maybe. I was looking at my hand-crafted function and thinking about how I would generalize it to allow the functions themselves to be interpolators (and so, to allow things to gel with non-number output types.)
I think it works cleanly? (i.e. without excessive object creation) But if not, I would be happy with a function that only to interpolated between unary functions from reals to reals.
from d3-interpolate.
Right, so here's a general, but possibly unperformant implementation:
function interpolateMeta(a, b) {
return (alpha) => interpolate(a(1 - alpha), b(alpha));
}
Here we do the interpolation in the domain, since we don't want to assume the codomain is numeric. This leads directly calling the (possibly expensive?) factory on each step.
If we know the codomain is numeric, we can do the interpolation there instead. Note this won't in general result in the same interpolator as above, but that's ok: I just want a continuous, linearish deformation.
function interpolateFunction(a, b) {
return (alpha) => (d) => (1 - alpha) * a(d) + alpha * b(d);
}
from d3-interpolate.
Err, excuse me. My interpolateMeta
doesn't look that bad on performance, but it does assume the domains of a
and b
are in the unit interval. The thing I was afraid of was this:
function interpolateAnyDomainAndCodomain(a, b) {
return (alpha) => (d) => interpolate(a(d), b(d))(alpha);
}
Here a
and b
are general functions, but the interpolate
call is stuck under the (d) =>
.
from d3-interpolate.
Sorry, last one. Upon third reading, my interpolateMeta
is simply wrong (for example, interpolate(a, b)(0) != a
.) interpolateAnyDomainAndCodomain
does what was intended.
from d3-interpolate.
I was thinking this:
function interpolateMeta(a, b) {
return (t) => interpolate(a(t), b(t))(t);
}
Which, as you point out, isn’t ideal from a performance perspective because it creates as new closure for each invocation of the meta-interpolator. This is an unfortunate consequence of the design of interpolators in this library. But, since the behavior of d3.interpolate is explicitly defined, it could use private internal methods to implement the above behavior more efficiently, after restructuring the implementation of the other interpolators, e.g.,
// A private function that interpolates directly rather than returning an interpolator.
function interpolateNumber(a, b, t) {
return (a = +a) + (b - a) * t;
}
A more extreme option would be to have separate methods, i.e., to make the above reusable implementation public:
- d3.interpolateNumber(a, b, t) - returns the interpolated value
- d3.interpolatorNumber(a, b) - returns an interpolator that takes a t
- d3.interpolateString(a, b, t) -
- d3.interpolatorString(a, b) -
- etc.
That might be a good long-term strategy although it’s highly inconvenient in terms of backwards-compatibility.
Technically, it doesn’t require that the domains of the two interpolators a and b are the unit interval—that’s just the convention. But it does require that they have the same domain, and that this domain is also the domain that will be used with the returned “meta” interpolator.
from d3-interpolate.
Oh also, as far as the dynamically-typed nature of d3.interpolate goes, the behavior of d3.interpolateMeta could be defined such that the first time the returned interpolator is invoked, it determines the type of interpolator to use subsequently based on the return value of b(t). So if b(t) is a string, then it henceforth uses d3.interpolateString every time (even if subsequently the behavior of b changes—though in practice it’s hard to imagine a good reason for an interpolator to return inconsistent types).
from d3-interpolate.
Lastly if you could elaborate on some practical use cases for this feature it would be helpful in establishing motivation. Thanks!
from d3-interpolate.
The motivation is visually intuitive non-linear transforms of unit square patches (linear transforms are already easy directly with SVG.)
The unit square restriction may look odd, but it makes what comes next easier. Such patches are also easy to make from arbitrary rectangles using the existing d3.scale*
functions.
So as an example, imagine transforming a time-series line graph (given by function d
) by mapping its base and top lines onto a pair of arbitrary paths (a
and b
), such as those made by d3.line
or d3.radialLine
. Assuming I've already scaled d
by sending both its domain and range to unit intervals, what should I do to transform it onto the patch defined by a
and b
?
I think one answer is:
function transformedD(u) {
return interpolate(a(u), b(u))(d(u));
}
For our given u
, we see where both a
and b
land, and choosing a point on the resulting line segment that is d(u)
of the way towards the b
end (which is where we sent the top line.)
Let's see if we can tease the bits apart a little.
function interpolateMeta(a, b) {
return (u) => (t) => interpolate(a(u), b(u))(t);
}
var transform = interpolateMeta(a, b);
var transformedDPrime = (v) => transform(v)(d(v));
Make sure I'm not nuts:
transformedDPrime
(v) => transform(v)(d(v)) // subst transformedDPrime
(v) => interpolateMeta(a, b)(v)(d(v)) // subst transform
(v) => ( (u) => (t) => interpolate(a(u), b(u))(t) )(v)(d(v)) // eval interpolateMeta(a, b)
(v) => ( (t) => interpolate(a(v), b(v))(t) )(d(v)) // beta
(v) => interpolate(a(v), b(v))(d(v)) // beta
(u) => interpolate(a(u), b(u))(d(u)) // alpha
transformedD // unsubst transformedD
I get the feeling we can play with binder order to avoid making closures, but I need to run for the moment.
from d3-interpolate.
Related Issues (20)
- Images in readme not loading due to Content Security Policy
- Simple zoom-in with interpolateZoom gives negative duration
- Interpolated data spike value problem HOT 1
- d3-color vulnerable to ReDoS HOT 1
- d3-color dependency update due to ReDoS HOT 1
- d3-color version issue HOT 2
- [bug] require() of ES modules is not supported HOT 3
- NaN RGB values if passing zero to interpolateRgb
- Meteor app crashes HOT 1
- d3-color version issue
- interpolateBalanced? HOT 3
- d3.piecewise(values)?
- Non-uniform d3.interpolateRgbBasis HOT 2
- array.js sometimes reports errors HOT 1
- Catmull-Rom interpolator? HOT 1
- Interpolate with t=Infinity returns NaN instead of Infinity HOT 6
- d3.interpolateHcl(a,b)(1) doesn't return 'b' HOT 3
- 3.0.1 doesn't work with Jest HOT 11
- Require Cycle with object.js and value.js HOT 1
- Require cycle: `value.js` -> `object.js` -> `value.js` HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from d3-interpolate.