0

first I should mention, because I couldn't translate the mathematical notation on Wikipedia to code, I copied this method from Rosetta Code*, refactored and made it a generator iterator:

def cubic_bezier(t, p0, p1, p2, p3):
  x_0, y_0 = p0
  x_1, y_1 = p1
  x_2, y_2 = p2
  x_3, y_3 = p3
  while True:
    a = (1.0 - t) ** 3
    b = 3.0 * t * (1.0 - t) ** 2
    c = 3.0 * t ** 2 * (1.0 - t)
    d = t ** 3
    x = a * x_0 + b * x_1 + c * x_2 + d * x_3
    y = a * y_0 + b * y_1 + c * y_2 + d * y_3
    t = yield (x, y)

usage:

bc = cubic_bezier(0, (0, 4), (0, -1.35), (12.03, 2.59), (17, 0.1))
next(bc) # you call next once, then use generator.send(t) after https://stackoverflow.com/a/19302694/4178053

for i in range(18):
  print(f"t = {i}/17", bc.send(i/17))

output:

t = 0/17 (0.0, 4.0)
t = 1/17 (0.12099328312639934, 3.1491186647669447)
t = 2/17 (0.46843069407693866, 2.4719112558518215)
t = 3/17 (1.0189985752086304, 1.9491797272542233)
t = 4/17 (1.7493832688784856, 1.5617260329737426)
t = 5/17 (2.6362711174435174, 1.2903521270099731)
t = 6/17 (3.656348463260737, 1.1158599633625073)
t = 7/17 (4.786301648687156, 1.0190514960309383)
t = 8/17 (6.0028170160797885, 0.9807286790148585)
t = 9/17 (7.282580907795644, 0.981693466313861)
t = 10/17 (8.602279666191738, 1.0027478119275395)
t = 11/17 (9.938599633625078, 1.0246936698554854)
t = 12/17 (11.268227152452678, 1.028332994097293)
t = 13/17 (12.567848565031547, 0.9944677386525546)
t = 14/17 (13.814150213718706, 0.9038998575208631)
t = 15/17 (14.983818440871158, 0.7374313047018115)
t = 16/17 (16.05353958884592, 0.4758640341949929)
t = 17/17 (17.0, 0.1)

expected output:

t = 0/17 (0.0, 4.0) // same
t = 1/17 (0.12099328312639934, ~2)
t = 2/17 (0.46843069407693866, ~1.5)
t = 3/17 (1.0189985752086304, ~1.25)
t = 4/17 (1.7493832688784856, ~1.1)
t = 5/17 (2.6362711174435174, ~1)
t = 6/17 (3.656348463260737, ~1)
// the rest is the same
t = 7/17 (4.786301648687156, 1.0190514960309383)
t = 8/17 (6.0028170160797885, 0.9807286790148585)
t = 9/17 (7.282580907795644, 0.981693466313861)
t = 10/17 (8.602279666191738, 1.0027478119275395)
t = 11/17 (9.938599633625078, 1.0246936698554854)
t = 12/17 (11.268227152452678, 1.028332994097293)
t = 13/17 (12.567848565031547, 0.9944677386525546)
t = 14/17 (13.814150213718706, 0.9038998575208631)
t = 15/17 (14.983818440871158, 0.7374313047018115)
t = 16/17 (16.05353958884592, 0.4758640341949929)
t = 17/17 (17.0, 0.1)

screenshot of the cubic bezier curve graph from the graphing calculator Desmos

this is a screenshot of the online graphing calculator Desmos which I used to plot a cubic Bézier curve graph in order to make whatever parameters/points I need for my programs, here's the permalink to it: desmos.com/calculator/tc955zllcc

*(Rosetta Code is a wiki-based programming chrestomathy website with implementations of common algorithms and solutions to various programming problems in many different programming languages.)

Wis
  • 484
  • 7
  • 22
  • Why is the order p1, p2, p0, p3 instead of the normal p0, p1, p2, p3? Also, note that copy-pasting your code doesn't work, as your comment is not a proper Python comment, and the rest of the code has syntax errors. – Mike 'Pomax' Kamermans Mar 18 '20 at 22:26
  • @Mike'Pomax'Kamermans I'm more used to c-style comments, I typed it in the question editor in order to link to that SO answer to explain what that next call is for. as to why the args are ordered that way, it's because optional args in Python are last, I wanted to make P0 and P3 args optional, with the values that are used in the browser's cubic Bezier easing function https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function, I'll remove them for sake of clarity and to prevent confusion from the question's topic, what other syntax errors? can you help me help you on helping me, please? – Wis Mar 19 '20 at 07:14
  • @Mike'Pomax'Kamermans I just realized that it's you that wrote that awesome resouce on Beziers that I started reading and bookmarked to finish reading later https://pomax.github.io/bezierinfo I love finding this type of articles, that are extensive and very well written, it reminded me of an other site that I bookmarked because I love how high quality and delightful its articles are: https://magcius.github.io/xplain/article/index.html – Wis Mar 19 '20 at 07:38
  • I am indeed. So here's the fun part: the mathematical computation and results that you're showing for your code are fine (but your code _as posted_ can't run, which you should verify by trying to run it using python =). The problem is your deduction based on looking at the graph: the graph shows _coordinate_ values, e.g. at `x=1`, `y=~2`, at `x=2`, `y=~1.75`,etc. However, cubic Beziers are non-linear (by definition) and the `x` axis values obviously do not correspond to `t` values (which run from 0 to 1) but also not `i` values: you don't get `x=1` at `i=1`, you get `x=~0.1207`, where `y=~3` – Mike 'Pomax' Kamermans Mar 19 '20 at 15:53

0 Answers0