0

I need help with the solution provided here.

Create easy function 40% off set

I need to modify it so that the returned left and rights are extrapolated to 1,1 after splitting. This is because if I don't extrapolate, I can't use the returned split cubic-bezier as a css transition.

So this is the test I did. Please help because real does not match mike way :( I think the issue is I need to extrapolate the result to 1,1. I can't simply double the values though I'm pretty sure.

Code used for getting it the Mike way is this:

var result = split({
    z: .5,
    x: [0, 0.42, 0.58, 1],
    y: [0, 0, 1, 1]
});
alert(result.toSource());
Community
  • 1
  • 1
Noitidart
  • 35,443
  • 37
  • 154
  • 323

2 Answers2

1

first half is ease-in which is cubic-bezier(.42,0,1,1) and graphically is http://cubic-bezier.com/#.42,0,1,1

Please verify this assumption. (curves original end points are 0,0, and 1,1 in a css timing function) The first half of the bezier curve [0,0, .42,0, .58,1, 1,1] should not be [0,0 .42,0, 1,1, 1,1] The end points are correct (after scaling to 1,1), but you have lost continuity there.

The values returned by Mike's algorithm is correct.

Try this visualisation for an explanation on why your assumption might be wrong.

The algorithm you are using to split is a well known algorithm called de Casteljau algorithm. This method can be geometrically expressed in a very simple manner. Check out the animated visualisations on how this splitting at any arbitrary point is possible https://en.wikipedia.org/wiki/B%C3%A9zier_curve.

However, You may soon hit an issue trying to correctly scale a split portion of a bezier curve to fit in a unit square, with endpoints fixed at 0,0 and 1,1. This probably you can try out quite easily on paper. The easiest way probably is to just linearly scale the control points of the bezier, you will get a squashed curve in most cases though.

hkrish
  • 1,259
  • 1
  • 10
  • 11
  • amusingly, it wasn't actually de Casteljau's algorithm - the code I provided was based on the direct formulae that come rolling out of solving the "splitting" using matrix representations, rather than the iterative de Casteljau approach. Of course, the result will be the same, but the number of steps differ =) – Mike 'Pomax' Kamermans May 07 '14 at 23:26
  • 1
    In fact in de Casteljau's original paper, he wrote it in the expanded form ("courbes et surfaces `a pˆoles"). He didn't exactly advise loops or any iteration! :P The exact same thing can of course be derived by writing the bernstein basis in Matrix form as well. – hkrish May 08 '14 at 11:08
  • I need to get my hands on that paper =P (also, SO uses modern html, accented letters should work fine =) – Mike 'Pomax' Kamermans May 08 '14 at 15:24
  • 1
    The paper, sadly, is in the form of unpublished notes. I am not sure if we can find it online. You can find scans of relevant parts of the original —including the expanded form of cubic and derivation of the recurrence relation, from Farouki's or Farin's papers on the subject. Interesting part is that de Casteljau didn't establish explicit relation with Bernstein basis. Even though his ideas for evaluating the curves were more general, for lower degrees he just wrote down the recurrence relations as is. (regarding accents, I copied it from a pdf, may be that's why :)). – hkrish May 08 '14 at 20:06
  • Thanks @Mike'Pomax'Kamermans and hkrish for the help. Im working on this again and will report back what i come up with :) Thanks especially hkrish for that visualization it is awesome!! – Noitidart Oct 13 '14 at 07:46
  • Super thanks @hkrish i just studied your visualization and you got it by doubling the points, the visualization is absolutely spectacular!!! I accepted your answer now, thank you very very much!! – Noitidart Oct 13 '14 at 07:55
  • Question please, how did you scale the right function? For the left one we doubled all the values, but on the second one I'm not sure. You went from here `[0.5, 0.5, 0.645, 0.75, 0.79, 1, 1, 1]` to `[0, 0, 0.29, 0.5, 0.58, 1, 1, 1]`. – Noitidart Oct 13 '14 at 08:51
  • Much thanks @hkrish I figured out how to extrapolate it, its coordinate normalization :) figured it out from here: http://math.stackexchange.com/questions/971454/how-to-scale-this-set-of-points/971473 I created a modified version of Mike's split function so it fits it to a unit square :) Ill post below – Noitidart Oct 15 '14 at 03:22
0

I created a modified version of Mike's split function so it fits it to a unit square :) It uses hkrish's pointers to do coordinate normalization.

Just set parameter fitUnitCell to true. :)

function splitCubicBezier(options) {
  var z = options.z,
      cz = z-1,
      z2 = z*z,
      cz2 = cz*cz,
      z3 = z2*z,
      cz3 = cz2*cz,
      x = options.x,
      y = options.y;

  var left = [
    x[0],
    y[0],
    z*x[1] - cz*x[0], 
    z*y[1] - cz*y[0], 
    z2*x[2] - 2*z*cz*x[1] + cz2*x[0],
    z2*y[2] - 2*z*cz*y[1] + cz2*y[0],
    z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
    z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0]];

  var right = [
    z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
    z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0],
                    z2*x[3] - 2*z*cz*x[2] + cz2*x[1],
                    z2*y[3] - 2*z*cz*y[2] + cz2*y[1],
                                    z*x[3] - cz*x[2], 
                                    z*y[3] - cz*y[2], 
                                                x[3],
                                                y[3]];

  if (options.fitUnitSquare) {
    return {
      left: left.map(function(el, i) {
        if (i % 2 == 0) {
          //return el * (1 / left[6])
          var Xmin = left[0];
          var Xmax = left[6]; //should be 1
          var Sx = 1 / (Xmax - Xmin);
          return (el - Xmin) * Sx;
        } else {
          //return el * (1 / left[7])
          var Ymin = left[1];
          var Ymax = left[7]; //should be 1
          var Sy = 1 / (Ymax - Ymin);
          return (el - Ymin) * Sy;
        }
      }),
      right: right.map(function(el, i) {
        if (i % 2 == 0) {
          //xval
          var Xmin = right[0]; //should be 0
          var Xmax = right[6];
          var Sx = 1 / (Xmax - Xmin);
          return (el - Xmin) * Sx;
        } else {
          //yval
          var Ymin = right[1]; //should be 0
          var Ymax = right[7];
          var Sy = 1 / (Ymax - Ymin);
          return (el - Ymin) * Sy;
        }
      })
    }
  } else {
   return { left: left, right: right};
  }
}

var easeInOut = {
  xs: [0, .42, .58, 1],
  ys: [0,   0,   1, 1]
};


var splitRes = splitCubicBezier({
  z: .5,
  x: easeInOut.xs,
  y: easeInOut.ys,
  fitUnitSquare: false
});

alert(splitRes.toSource())
Noitidart
  • 35,443
  • 37
  • 154
  • 323