

Show HN: Smooth.js - Turn arrays into smooth functions - mistercow
https://github.com/osuushi/Smooth.js

======
binarymax
Beautiful. Will definitely use this for a project I'm working on -
specifically to get around the common issue that many canvas based drawing
tools run into. Im curious to see how it performs. Thanks!

~~~
mistercow
I'd love any feedback you have. I haven't focused on performance much yet
(although I think I've made reasonable design decisions in general). I think
there's some caching I can do on cubic splines which might be beneficial, but
I'll obviously need to do some benchmarks to see if they're worthwhile.

------
Cushman
Neat looking library, but I have a little syntax question-- what's the reason
for using ugly Cish constants like Smooth.METHOD_CUBIC? Surely it would work
just as well (and be more JSish) to say something like

    
    
      Smooth points,
        method: 'cubic' 
        clip: 'periodic'
        cubicTension: 'catmull-rom'

~~~
mistercow
I can't give any really strong justification, besides to say that C-style
constants make me feel warm and safe. My weak justifications are that I find
having my constants declared in one place helps me to remember to handle all
of them, and that it helps slightly with auto-complete.

But there is a good reason in the case of the cubicTension constants: those
aren't enums, but actual values. You can put any cubicTension parameter you
want between 0 and 1.

~~~
Cushman
Totally get that-- if it's the style you're comfortable with, go for it. Hope
I didn't sound critical, just curious.

Although if I may, there is a potential benefit to using strings there in that
it lets you get rid of the switch statement entirely:

    
    
      unless config.clip of ClipHelpers
        throw new Error "No clip mode '#{config.clip}'. Available modes are #{(k for k of ClipHelpers).join(', ')}"
      clipHelper = ClipHelpers[config.clip]
    

This could also make it easy for a user to add helpers at runtime. Maybe
overkill for this, just something to keep in mind.

------
mikehuffman
Very well done. Simple and effective. This will be useful on a surprising
number of my projects.

------
dmvaldman
Cool. You may want to add more interpolants, for instance a Fourier (for
regularly spaced points) or (even better, but not regularly spaced) Chebyshev
interpolation would be useful, highly accurate, and can be computed fast (N
log(N))

~~~
mistercow
I definitely want to add more interpolants. The only limitation really is that
it has to be possible to do the calculation lazily. As I understand Fourier
interpolation, this should be fine with that. I am less familiar with
Chebyshev interpolation.

~~~
dmvaldman
For Fourier, given an N-dim vector you would compute the N Fourier
coefficients in Nlog(N) time, stored in memory, then whenever a new x-value is
requested it takes linear time to evaluate the point (add up the basis vectors
with the stored coefficients).

Chebyshev polynomials are more accurate than Fourier. In fact they are the
most accurate interpolating polynomials in general. They're not as wildly
known, but often used in numerical analysis. The problem with Fourier is that
if your function is not periodic, the coefficients do not converge quickly,
making the interpolant less accurate for a given number of nodes. Chebyshev
gets around this problem, but instead of using equally spaced points, points
are spaced out along the uneven "Chebyshev nodes". So to get an interpolated
point, say 2.5, 2.5 no longer falls halfway between the 2nd and 3rd Cheb node,
you would first have to figure out the value of 2.5 in the nonuniform
Chebyshev distribution of points, and then compute the interpolating value in
linear time at this point.[1]

Both Chebyshev and Fourier are "global" interpolants. As opposed to cubic
splines and your other ones. If you change a single value in your vector, the
entire interpolant is affected and needs to be recomputed.

[1] <http://en.wikipedia.org/wiki/Chebyshev_polynomials>

~~~
mistercow
> For Fourier, given an N-dim vector you would compute the N Fourier
> coefficients in Nlog(N) time

But Nlog(N) only works for certain values of N, right (like powers of two)? I
guess for other cases, we could use the clipping method to dictate behavior,
but that doesn't feel great.

------
peter_l_downs
Here's a python version, if anyone wants it. I know that I've had to do
interpolation in the past, and this would have been nice to have.

<http://pastebin.com/fqNRNcBP>

------
tantalor
Sorry to nitpick, but it's not smooth if it has sharp corners (not
differentiable).

~~~
mistercow
Yes, that's true, but I feel that the nearest neighbor and linear modes are
useful even if they do clash slightly with the name. You'll note that the
default configuration does, in fact, produce a smooth function.

~~~
xyzzyz
Even cubic splines are not smooth. "Smooth" has very well defined meaning in
math -- it means precisely "infinitely many times differentiable". Cubic
splines are differentiable only finitely many times (only twice, actually).

~~~
mistercow
Yes, but "smooth" also has a well defined meaning in everyday language. The
word "smooth" is commonly used when talking about splines and interpolation.

------
catshirt
really cool. recently was frustrated doing similar interpolation- this is a
cool api though, wish i had done it this way. would be fun to support other
data as well, ie. hex math for gradients. thanks!

~~~
mistercow
> hex math for gradients

Ooo that's a good idea. Of course, you can use 3D vectors for rgb values to
get much the same effect.

------
peregrine
Very cool, this reminds me of D3.scales
<https://github.com/mbostock/d3/wiki/Scales> good work!

------
sturob
Nicely done. I have implemented the Linear part of this already but this will
save me from having to do the rest. Thanks!

------
blake8086
I feel like the .js suffix is meant to imply this is JavaScript code, but it
is actually CoffeeScript. They are not the same.

Why is it not named Smooth.coffee?

~~~
mistercow
I considered naming it that, but ultimately decided that the fact that it's
written in CoffeeScript is irrelevant. It's a JavaScript library; it's meant
to be used with JavaScript (which of course implies that it's meant to be used
with CoffeeScript), but it happens to be written in CoffeeScript.

And there is every possibility that it could get rewritten in JavaScript. Or
it could be rewritten in something else that compiles to JS, if something
comes along to which this task is well-suited. The implementation can change,
and the name should remain appropriate. What will not certainly change,
however, is the environment in which this is intended to be used.

Hope that makes sense.

~~~
naugtur
My first thought was "Awww, it's in CoffeeScript" :) It looks like there in
fact is an issue with how people (at least me ;) ) react to CoffeeScript... I
wonder if you have real control over the performance. Anyway - it's a great
idea and I think the name is appropriate.

BTW. Yesterday I needed to mirror some sequence and I ended up using
Math.sin()

~~~
mistercow
> I wonder if you have real control over the performance.

In general, the mapping between CoffeeScript and JS is so trivial that it's
hard to think of places where there _could_ be a lack of control. Of course,
if that were ever an issue, CoffeeScript lets you directly embed JS by
surrounding it in backticks.

I also had a bad gut reaction to CoffeeScript initially, and I won't pretend
it doesn't have drawbacks; the powerful, in-browser debugger that it
desperately craves still hasn't yet been written, after all. But it really
gets right what so many compile-to-javascript languages have gotten wrong in
the past. At the very least, it is hands down the easiest way to sketch an
idea for JavaScript. I just never end up feeling like I would gain anything
significant by porting that sketch to native JS.

