Computing bezier curves of degree n with recursive functions












1














I have been looking at homemade CNC and have been wondering how curves are drawn, so I looked into it and found this cool article. I then decided to try coming up with a bezier curve algorithm in C. It seems to work ok, and while I haven't tried plotting points with this exact implementation, it seems to match up with results from a previous implementation that I did plot points with.



#include <stdint.h>

static long double bbezier(long double t, long double p0, long double p1){
return ((p1 - p0) * t) + p0;
}

long double bezier(long double t, uint64_t *points, uint64_t n){
long double p0 = points[0], p1 = points[1];
if(n == 1) return bbezier(t, p0, p1);

long double q0 = bezier(t, points, n - 1),
q1 = bezier(t, points + 1, n - 1);
return bbezier(t, q0, q1);
}


I then quickly wrote this test program in C++.



#include <iostream>

extern "C" long double bezier(long double, uint64_t *, uint64_t);

uint64_t pointsx = {
0, 40, 100, 200
};
uint64_t pointsy = {
0, 150, 60, 100
};

int main(){
for(uint64_t i = 0; i <= 10000; ++i){
long double ii = i;
long double j = ii/10000;

long double x = bezier(j, pointsx, 3);
long double y = bezier(j, pointsy, 3);

std::cout << "X: " << x << ", Y: " << y << 'n';
}
return 0;
}


I wrote an implementation running in javascript from a lightly modified w3 schools canvas tutorial here to understand how bezier curves work but it only supports 3rd degree curves. It does plot the points though, and that's what I based the above implementation on.



It doesn't make any checks to ensure t is between 0 and 1 and n != 0 but I'm not too worried. The only thing I'm worried about is segfaults in cases where n is so high that you get a stack overflow but that will be a pretty crazy curve. Anyway, how does it look?










share|improve this question



























    1














    I have been looking at homemade CNC and have been wondering how curves are drawn, so I looked into it and found this cool article. I then decided to try coming up with a bezier curve algorithm in C. It seems to work ok, and while I haven't tried plotting points with this exact implementation, it seems to match up with results from a previous implementation that I did plot points with.



    #include <stdint.h>

    static long double bbezier(long double t, long double p0, long double p1){
    return ((p1 - p0) * t) + p0;
    }

    long double bezier(long double t, uint64_t *points, uint64_t n){
    long double p0 = points[0], p1 = points[1];
    if(n == 1) return bbezier(t, p0, p1);

    long double q0 = bezier(t, points, n - 1),
    q1 = bezier(t, points + 1, n - 1);
    return bbezier(t, q0, q1);
    }


    I then quickly wrote this test program in C++.



    #include <iostream>

    extern "C" long double bezier(long double, uint64_t *, uint64_t);

    uint64_t pointsx = {
    0, 40, 100, 200
    };
    uint64_t pointsy = {
    0, 150, 60, 100
    };

    int main(){
    for(uint64_t i = 0; i <= 10000; ++i){
    long double ii = i;
    long double j = ii/10000;

    long double x = bezier(j, pointsx, 3);
    long double y = bezier(j, pointsy, 3);

    std::cout << "X: " << x << ", Y: " << y << 'n';
    }
    return 0;
    }


    I wrote an implementation running in javascript from a lightly modified w3 schools canvas tutorial here to understand how bezier curves work but it only supports 3rd degree curves. It does plot the points though, and that's what I based the above implementation on.



    It doesn't make any checks to ensure t is between 0 and 1 and n != 0 but I'm not too worried. The only thing I'm worried about is segfaults in cases where n is so high that you get a stack overflow but that will be a pretty crazy curve. Anyway, how does it look?










    share|improve this question

























      1












      1








      1







      I have been looking at homemade CNC and have been wondering how curves are drawn, so I looked into it and found this cool article. I then decided to try coming up with a bezier curve algorithm in C. It seems to work ok, and while I haven't tried plotting points with this exact implementation, it seems to match up with results from a previous implementation that I did plot points with.



      #include <stdint.h>

      static long double bbezier(long double t, long double p0, long double p1){
      return ((p1 - p0) * t) + p0;
      }

      long double bezier(long double t, uint64_t *points, uint64_t n){
      long double p0 = points[0], p1 = points[1];
      if(n == 1) return bbezier(t, p0, p1);

      long double q0 = bezier(t, points, n - 1),
      q1 = bezier(t, points + 1, n - 1);
      return bbezier(t, q0, q1);
      }


      I then quickly wrote this test program in C++.



      #include <iostream>

      extern "C" long double bezier(long double, uint64_t *, uint64_t);

      uint64_t pointsx = {
      0, 40, 100, 200
      };
      uint64_t pointsy = {
      0, 150, 60, 100
      };

      int main(){
      for(uint64_t i = 0; i <= 10000; ++i){
      long double ii = i;
      long double j = ii/10000;

      long double x = bezier(j, pointsx, 3);
      long double y = bezier(j, pointsy, 3);

      std::cout << "X: " << x << ", Y: " << y << 'n';
      }
      return 0;
      }


      I wrote an implementation running in javascript from a lightly modified w3 schools canvas tutorial here to understand how bezier curves work but it only supports 3rd degree curves. It does plot the points though, and that's what I based the above implementation on.



      It doesn't make any checks to ensure t is between 0 and 1 and n != 0 but I'm not too worried. The only thing I'm worried about is segfaults in cases where n is so high that you get a stack overflow but that will be a pretty crazy curve. Anyway, how does it look?










      share|improve this question













      I have been looking at homemade CNC and have been wondering how curves are drawn, so I looked into it and found this cool article. I then decided to try coming up with a bezier curve algorithm in C. It seems to work ok, and while I haven't tried plotting points with this exact implementation, it seems to match up with results from a previous implementation that I did plot points with.



      #include <stdint.h>

      static long double bbezier(long double t, long double p0, long double p1){
      return ((p1 - p0) * t) + p0;
      }

      long double bezier(long double t, uint64_t *points, uint64_t n){
      long double p0 = points[0], p1 = points[1];
      if(n == 1) return bbezier(t, p0, p1);

      long double q0 = bezier(t, points, n - 1),
      q1 = bezier(t, points + 1, n - 1);
      return bbezier(t, q0, q1);
      }


      I then quickly wrote this test program in C++.



      #include <iostream>

      extern "C" long double bezier(long double, uint64_t *, uint64_t);

      uint64_t pointsx = {
      0, 40, 100, 200
      };
      uint64_t pointsy = {
      0, 150, 60, 100
      };

      int main(){
      for(uint64_t i = 0; i <= 10000; ++i){
      long double ii = i;
      long double j = ii/10000;

      long double x = bezier(j, pointsx, 3);
      long double y = bezier(j, pointsy, 3);

      std::cout << "X: " << x << ", Y: " << y << 'n';
      }
      return 0;
      }


      I wrote an implementation running in javascript from a lightly modified w3 schools canvas tutorial here to understand how bezier curves work but it only supports 3rd degree curves. It does plot the points though, and that's what I based the above implementation on.



      It doesn't make any checks to ensure t is between 0 and 1 and n != 0 but I'm not too worried. The only thing I'm worried about is segfaults in cases where n is so high that you get a stack overflow but that will be a pretty crazy curve. Anyway, how does it look?







      c recursion






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked yesterday









      user233009user233009

      1406




      1406






















          2 Answers
          2






          active

          oldest

          votes


















          2















          • I don't like bbezier. It collided too much with bezier, and is not very informative. It performs a linear interpolation, so why not call it interpolate?



          • The p0 and p1 just add noise. Consider



                if (n == 1) {
            return interpolate(t, points[0], points[1]);
            }


            I would seriously consider getting rid of q0 and q1:



                return interpolate(t,
            bezier(t, points, n - 1),
            bezier(t, points + 1, n - 1));


            Don't take it as a recommendation.



          • The recursion leads to the exponential time complexity. Way before you start having memory problems you'd face a performance problem. Consider computing the Bernstein form instead. It gives you linear time, and no memory problems.







          share|improve this answer





















          • "It gives you linear time, and no memory problems." Evaluating them in Bernstein form through de Casteljau's algorithm is inefficient: $n(n+1)/2$ additions and $n(n+1)$ multiplications to calculate a point on a curve of degree $n$. Rather, I'd prefer Wang-Ball form. For reference: "Efficient algorithms for Bézier curves."
            – esote
            23 hours ago












          • @esote Did I ever mention de Casteljau? A not-so-naive implementation is linear. Two multiplications and two divisions per term. It will have some numerical issues with large n, sure.
            – vnp
            23 hours ago












          • Fair enough, I should've read your answer more carefully.
            – esote
            23 hours ago





















          2














          Recursion



          [Edit]

          In bezier(), the 2 recursive calls to bezier() is inefficient as it exponential grows with O(2n) and only O(n2) operations are needed. I suspect better efficiency (linear) can be had with a pre-computed weighing of the d terms below.



          The concern about seg faulting due to excessive recursion would be mitigated with the above improvement.



          I also change function bbezier() to code.



          long double bezier_alt1(long double t, const uint64_t *points, size_t n) {
          assert(n);
          long double omt = 1.0 - t;
          long double d[n]; // Save in between calculations.

          for (size_t i = 0; i < n; i++) {
          d[i] = omt * points[i] + t * points[i + 1];
          }
          while (n > 1) {
          n--;
          for (size_t i = 0; i < n; i++) {
          d[i] = omt * d[i] + t * d[i + 1];
          }
          }
          return d[0];
          }


          [Edit2]



          A linear solution O(n) is possible with O(1) additional memory.



          long double bezier_alt2(long double t, const uint64_t *points, size_t n) {
          assert(n);
          long double omt = 1.0 - t;
          long double power_t = 1.0;
          long double power_omt = powl(omt,n);
          long double omt_div = omt != 0.0 ? 1.0/omt : 0.0;

          long double sum = 0.0;
          unsigned long term_n = 1;
          unsigned long term_d = 1;
          for (size_t i = 0; i < n; i++) {
          long double y = power_omt*power_t*points[i]*term_n/term_d;
          sum += y;
          power_t *= t;
          power_omt *= omt_div;
          term_n *= (n-i);
          term_d *= (i+1);
          }
          power_omt = 1.0;
          long double y = power_omt*power_t*points[n]*term_n/term_d;
          sum += y;
          return sum;
          }


          Additional linear simplifications possible - perhaps another day.





          Minor stuff



          Use const



          A const in the referenced date allows for some optimizations, wider application and better conveys code's intent.



          // long double bezier(long double t, uint64_t *points, uint64_t n){
          long double bezier(long double t, const uint64_t *points, uint64_t n){


          Excessive wide type



          With uint64_t n, there is no reasonable expectation that such an iteration will finish for large n.



          Fortunately, n indicates the size of the array. For array indexing and sizing, using size_t. It is the right size - not too narrow, nor too wide a type.



          // long double bezier(long double t, uint64_t *points, uint64_t n){
          long double bezier(long double t, uint64_t *points, size_t n){


          For this application, certainly unsigned would alway suffice.



          static



          Good use of static in static long double bbezier() to keep that function local.



          Missing "bezier.h"



          I'd expect a bezier() declarations in a .h file and implementation in the .c file instead of extern "C" long double bezier(long double, uint64_t *, uint64_t); in main.c



          // extern "C" long double bezier(long double, uint64_t *, uint64_t);
          #include "bezier.h".


          n range check



          Perhaps in a debug build, test n.



          long double bezier(long double t, const uint64_t *points, uint64_t n){
          assert( n > 0); // Low bound
          assert( n < 1000); // Maybe an sane upper limit too





          share|improve this answer























            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211157%2fcomputing-bezier-curves-of-degree-n-with-recursive-functions%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            2















            • I don't like bbezier. It collided too much with bezier, and is not very informative. It performs a linear interpolation, so why not call it interpolate?



            • The p0 and p1 just add noise. Consider



                  if (n == 1) {
              return interpolate(t, points[0], points[1]);
              }


              I would seriously consider getting rid of q0 and q1:



                  return interpolate(t,
              bezier(t, points, n - 1),
              bezier(t, points + 1, n - 1));


              Don't take it as a recommendation.



            • The recursion leads to the exponential time complexity. Way before you start having memory problems you'd face a performance problem. Consider computing the Bernstein form instead. It gives you linear time, and no memory problems.







            share|improve this answer





















            • "It gives you linear time, and no memory problems." Evaluating them in Bernstein form through de Casteljau's algorithm is inefficient: $n(n+1)/2$ additions and $n(n+1)$ multiplications to calculate a point on a curve of degree $n$. Rather, I'd prefer Wang-Ball form. For reference: "Efficient algorithms for Bézier curves."
              – esote
              23 hours ago












            • @esote Did I ever mention de Casteljau? A not-so-naive implementation is linear. Two multiplications and two divisions per term. It will have some numerical issues with large n, sure.
              – vnp
              23 hours ago












            • Fair enough, I should've read your answer more carefully.
              – esote
              23 hours ago


















            2















            • I don't like bbezier. It collided too much with bezier, and is not very informative. It performs a linear interpolation, so why not call it interpolate?



            • The p0 and p1 just add noise. Consider



                  if (n == 1) {
              return interpolate(t, points[0], points[1]);
              }


              I would seriously consider getting rid of q0 and q1:



                  return interpolate(t,
              bezier(t, points, n - 1),
              bezier(t, points + 1, n - 1));


              Don't take it as a recommendation.



            • The recursion leads to the exponential time complexity. Way before you start having memory problems you'd face a performance problem. Consider computing the Bernstein form instead. It gives you linear time, and no memory problems.







            share|improve this answer





















            • "It gives you linear time, and no memory problems." Evaluating them in Bernstein form through de Casteljau's algorithm is inefficient: $n(n+1)/2$ additions and $n(n+1)$ multiplications to calculate a point on a curve of degree $n$. Rather, I'd prefer Wang-Ball form. For reference: "Efficient algorithms for Bézier curves."
              – esote
              23 hours ago












            • @esote Did I ever mention de Casteljau? A not-so-naive implementation is linear. Two multiplications and two divisions per term. It will have some numerical issues with large n, sure.
              – vnp
              23 hours ago












            • Fair enough, I should've read your answer more carefully.
              – esote
              23 hours ago
















            2












            2








            2







            • I don't like bbezier. It collided too much with bezier, and is not very informative. It performs a linear interpolation, so why not call it interpolate?



            • The p0 and p1 just add noise. Consider



                  if (n == 1) {
              return interpolate(t, points[0], points[1]);
              }


              I would seriously consider getting rid of q0 and q1:



                  return interpolate(t,
              bezier(t, points, n - 1),
              bezier(t, points + 1, n - 1));


              Don't take it as a recommendation.



            • The recursion leads to the exponential time complexity. Way before you start having memory problems you'd face a performance problem. Consider computing the Bernstein form instead. It gives you linear time, and no memory problems.







            share|improve this answer













            • I don't like bbezier. It collided too much with bezier, and is not very informative. It performs a linear interpolation, so why not call it interpolate?



            • The p0 and p1 just add noise. Consider



                  if (n == 1) {
              return interpolate(t, points[0], points[1]);
              }


              I would seriously consider getting rid of q0 and q1:



                  return interpolate(t,
              bezier(t, points, n - 1),
              bezier(t, points + 1, n - 1));


              Don't take it as a recommendation.



            • The recursion leads to the exponential time complexity. Way before you start having memory problems you'd face a performance problem. Consider computing the Bernstein form instead. It gives you linear time, and no memory problems.








            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered 23 hours ago









            vnpvnp

            38.7k13097




            38.7k13097












            • "It gives you linear time, and no memory problems." Evaluating them in Bernstein form through de Casteljau's algorithm is inefficient: $n(n+1)/2$ additions and $n(n+1)$ multiplications to calculate a point on a curve of degree $n$. Rather, I'd prefer Wang-Ball form. For reference: "Efficient algorithms for Bézier curves."
              – esote
              23 hours ago












            • @esote Did I ever mention de Casteljau? A not-so-naive implementation is linear. Two multiplications and two divisions per term. It will have some numerical issues with large n, sure.
              – vnp
              23 hours ago












            • Fair enough, I should've read your answer more carefully.
              – esote
              23 hours ago




















            • "It gives you linear time, and no memory problems." Evaluating them in Bernstein form through de Casteljau's algorithm is inefficient: $n(n+1)/2$ additions and $n(n+1)$ multiplications to calculate a point on a curve of degree $n$. Rather, I'd prefer Wang-Ball form. For reference: "Efficient algorithms for Bézier curves."
              – esote
              23 hours ago












            • @esote Did I ever mention de Casteljau? A not-so-naive implementation is linear. Two multiplications and two divisions per term. It will have some numerical issues with large n, sure.
              – vnp
              23 hours ago












            • Fair enough, I should've read your answer more carefully.
              – esote
              23 hours ago


















            "It gives you linear time, and no memory problems." Evaluating them in Bernstein form through de Casteljau's algorithm is inefficient: $n(n+1)/2$ additions and $n(n+1)$ multiplications to calculate a point on a curve of degree $n$. Rather, I'd prefer Wang-Ball form. For reference: "Efficient algorithms for Bézier curves."
            – esote
            23 hours ago






            "It gives you linear time, and no memory problems." Evaluating them in Bernstein form through de Casteljau's algorithm is inefficient: $n(n+1)/2$ additions and $n(n+1)$ multiplications to calculate a point on a curve of degree $n$. Rather, I'd prefer Wang-Ball form. For reference: "Efficient algorithms for Bézier curves."
            – esote
            23 hours ago














            @esote Did I ever mention de Casteljau? A not-so-naive implementation is linear. Two multiplications and two divisions per term. It will have some numerical issues with large n, sure.
            – vnp
            23 hours ago






            @esote Did I ever mention de Casteljau? A not-so-naive implementation is linear. Two multiplications and two divisions per term. It will have some numerical issues with large n, sure.
            – vnp
            23 hours ago














            Fair enough, I should've read your answer more carefully.
            – esote
            23 hours ago






            Fair enough, I should've read your answer more carefully.
            – esote
            23 hours ago















            2














            Recursion



            [Edit]

            In bezier(), the 2 recursive calls to bezier() is inefficient as it exponential grows with O(2n) and only O(n2) operations are needed. I suspect better efficiency (linear) can be had with a pre-computed weighing of the d terms below.



            The concern about seg faulting due to excessive recursion would be mitigated with the above improvement.



            I also change function bbezier() to code.



            long double bezier_alt1(long double t, const uint64_t *points, size_t n) {
            assert(n);
            long double omt = 1.0 - t;
            long double d[n]; // Save in between calculations.

            for (size_t i = 0; i < n; i++) {
            d[i] = omt * points[i] + t * points[i + 1];
            }
            while (n > 1) {
            n--;
            for (size_t i = 0; i < n; i++) {
            d[i] = omt * d[i] + t * d[i + 1];
            }
            }
            return d[0];
            }


            [Edit2]



            A linear solution O(n) is possible with O(1) additional memory.



            long double bezier_alt2(long double t, const uint64_t *points, size_t n) {
            assert(n);
            long double omt = 1.0 - t;
            long double power_t = 1.0;
            long double power_omt = powl(omt,n);
            long double omt_div = omt != 0.0 ? 1.0/omt : 0.0;

            long double sum = 0.0;
            unsigned long term_n = 1;
            unsigned long term_d = 1;
            for (size_t i = 0; i < n; i++) {
            long double y = power_omt*power_t*points[i]*term_n/term_d;
            sum += y;
            power_t *= t;
            power_omt *= omt_div;
            term_n *= (n-i);
            term_d *= (i+1);
            }
            power_omt = 1.0;
            long double y = power_omt*power_t*points[n]*term_n/term_d;
            sum += y;
            return sum;
            }


            Additional linear simplifications possible - perhaps another day.





            Minor stuff



            Use const



            A const in the referenced date allows for some optimizations, wider application and better conveys code's intent.



            // long double bezier(long double t, uint64_t *points, uint64_t n){
            long double bezier(long double t, const uint64_t *points, uint64_t n){


            Excessive wide type



            With uint64_t n, there is no reasonable expectation that such an iteration will finish for large n.



            Fortunately, n indicates the size of the array. For array indexing and sizing, using size_t. It is the right size - not too narrow, nor too wide a type.



            // long double bezier(long double t, uint64_t *points, uint64_t n){
            long double bezier(long double t, uint64_t *points, size_t n){


            For this application, certainly unsigned would alway suffice.



            static



            Good use of static in static long double bbezier() to keep that function local.



            Missing "bezier.h"



            I'd expect a bezier() declarations in a .h file and implementation in the .c file instead of extern "C" long double bezier(long double, uint64_t *, uint64_t); in main.c



            // extern "C" long double bezier(long double, uint64_t *, uint64_t);
            #include "bezier.h".


            n range check



            Perhaps in a debug build, test n.



            long double bezier(long double t, const uint64_t *points, uint64_t n){
            assert( n > 0); // Low bound
            assert( n < 1000); // Maybe an sane upper limit too





            share|improve this answer




























              2














              Recursion



              [Edit]

              In bezier(), the 2 recursive calls to bezier() is inefficient as it exponential grows with O(2n) and only O(n2) operations are needed. I suspect better efficiency (linear) can be had with a pre-computed weighing of the d terms below.



              The concern about seg faulting due to excessive recursion would be mitigated with the above improvement.



              I also change function bbezier() to code.



              long double bezier_alt1(long double t, const uint64_t *points, size_t n) {
              assert(n);
              long double omt = 1.0 - t;
              long double d[n]; // Save in between calculations.

              for (size_t i = 0; i < n; i++) {
              d[i] = omt * points[i] + t * points[i + 1];
              }
              while (n > 1) {
              n--;
              for (size_t i = 0; i < n; i++) {
              d[i] = omt * d[i] + t * d[i + 1];
              }
              }
              return d[0];
              }


              [Edit2]



              A linear solution O(n) is possible with O(1) additional memory.



              long double bezier_alt2(long double t, const uint64_t *points, size_t n) {
              assert(n);
              long double omt = 1.0 - t;
              long double power_t = 1.0;
              long double power_omt = powl(omt,n);
              long double omt_div = omt != 0.0 ? 1.0/omt : 0.0;

              long double sum = 0.0;
              unsigned long term_n = 1;
              unsigned long term_d = 1;
              for (size_t i = 0; i < n; i++) {
              long double y = power_omt*power_t*points[i]*term_n/term_d;
              sum += y;
              power_t *= t;
              power_omt *= omt_div;
              term_n *= (n-i);
              term_d *= (i+1);
              }
              power_omt = 1.0;
              long double y = power_omt*power_t*points[n]*term_n/term_d;
              sum += y;
              return sum;
              }


              Additional linear simplifications possible - perhaps another day.





              Minor stuff



              Use const



              A const in the referenced date allows for some optimizations, wider application and better conveys code's intent.



              // long double bezier(long double t, uint64_t *points, uint64_t n){
              long double bezier(long double t, const uint64_t *points, uint64_t n){


              Excessive wide type



              With uint64_t n, there is no reasonable expectation that such an iteration will finish for large n.



              Fortunately, n indicates the size of the array. For array indexing and sizing, using size_t. It is the right size - not too narrow, nor too wide a type.



              // long double bezier(long double t, uint64_t *points, uint64_t n){
              long double bezier(long double t, uint64_t *points, size_t n){


              For this application, certainly unsigned would alway suffice.



              static



              Good use of static in static long double bbezier() to keep that function local.



              Missing "bezier.h"



              I'd expect a bezier() declarations in a .h file and implementation in the .c file instead of extern "C" long double bezier(long double, uint64_t *, uint64_t); in main.c



              // extern "C" long double bezier(long double, uint64_t *, uint64_t);
              #include "bezier.h".


              n range check



              Perhaps in a debug build, test n.



              long double bezier(long double t, const uint64_t *points, uint64_t n){
              assert( n > 0); // Low bound
              assert( n < 1000); // Maybe an sane upper limit too





              share|improve this answer


























                2












                2








                2






                Recursion



                [Edit]

                In bezier(), the 2 recursive calls to bezier() is inefficient as it exponential grows with O(2n) and only O(n2) operations are needed. I suspect better efficiency (linear) can be had with a pre-computed weighing of the d terms below.



                The concern about seg faulting due to excessive recursion would be mitigated with the above improvement.



                I also change function bbezier() to code.



                long double bezier_alt1(long double t, const uint64_t *points, size_t n) {
                assert(n);
                long double omt = 1.0 - t;
                long double d[n]; // Save in between calculations.

                for (size_t i = 0; i < n; i++) {
                d[i] = omt * points[i] + t * points[i + 1];
                }
                while (n > 1) {
                n--;
                for (size_t i = 0; i < n; i++) {
                d[i] = omt * d[i] + t * d[i + 1];
                }
                }
                return d[0];
                }


                [Edit2]



                A linear solution O(n) is possible with O(1) additional memory.



                long double bezier_alt2(long double t, const uint64_t *points, size_t n) {
                assert(n);
                long double omt = 1.0 - t;
                long double power_t = 1.0;
                long double power_omt = powl(omt,n);
                long double omt_div = omt != 0.0 ? 1.0/omt : 0.0;

                long double sum = 0.0;
                unsigned long term_n = 1;
                unsigned long term_d = 1;
                for (size_t i = 0; i < n; i++) {
                long double y = power_omt*power_t*points[i]*term_n/term_d;
                sum += y;
                power_t *= t;
                power_omt *= omt_div;
                term_n *= (n-i);
                term_d *= (i+1);
                }
                power_omt = 1.0;
                long double y = power_omt*power_t*points[n]*term_n/term_d;
                sum += y;
                return sum;
                }


                Additional linear simplifications possible - perhaps another day.





                Minor stuff



                Use const



                A const in the referenced date allows for some optimizations, wider application and better conveys code's intent.



                // long double bezier(long double t, uint64_t *points, uint64_t n){
                long double bezier(long double t, const uint64_t *points, uint64_t n){


                Excessive wide type



                With uint64_t n, there is no reasonable expectation that such an iteration will finish for large n.



                Fortunately, n indicates the size of the array. For array indexing and sizing, using size_t. It is the right size - not too narrow, nor too wide a type.



                // long double bezier(long double t, uint64_t *points, uint64_t n){
                long double bezier(long double t, uint64_t *points, size_t n){


                For this application, certainly unsigned would alway suffice.



                static



                Good use of static in static long double bbezier() to keep that function local.



                Missing "bezier.h"



                I'd expect a bezier() declarations in a .h file and implementation in the .c file instead of extern "C" long double bezier(long double, uint64_t *, uint64_t); in main.c



                // extern "C" long double bezier(long double, uint64_t *, uint64_t);
                #include "bezier.h".


                n range check



                Perhaps in a debug build, test n.



                long double bezier(long double t, const uint64_t *points, uint64_t n){
                assert( n > 0); // Low bound
                assert( n < 1000); // Maybe an sane upper limit too





                share|improve this answer














                Recursion



                [Edit]

                In bezier(), the 2 recursive calls to bezier() is inefficient as it exponential grows with O(2n) and only O(n2) operations are needed. I suspect better efficiency (linear) can be had with a pre-computed weighing of the d terms below.



                The concern about seg faulting due to excessive recursion would be mitigated with the above improvement.



                I also change function bbezier() to code.



                long double bezier_alt1(long double t, const uint64_t *points, size_t n) {
                assert(n);
                long double omt = 1.0 - t;
                long double d[n]; // Save in between calculations.

                for (size_t i = 0; i < n; i++) {
                d[i] = omt * points[i] + t * points[i + 1];
                }
                while (n > 1) {
                n--;
                for (size_t i = 0; i < n; i++) {
                d[i] = omt * d[i] + t * d[i + 1];
                }
                }
                return d[0];
                }


                [Edit2]



                A linear solution O(n) is possible with O(1) additional memory.



                long double bezier_alt2(long double t, const uint64_t *points, size_t n) {
                assert(n);
                long double omt = 1.0 - t;
                long double power_t = 1.0;
                long double power_omt = powl(omt,n);
                long double omt_div = omt != 0.0 ? 1.0/omt : 0.0;

                long double sum = 0.0;
                unsigned long term_n = 1;
                unsigned long term_d = 1;
                for (size_t i = 0; i < n; i++) {
                long double y = power_omt*power_t*points[i]*term_n/term_d;
                sum += y;
                power_t *= t;
                power_omt *= omt_div;
                term_n *= (n-i);
                term_d *= (i+1);
                }
                power_omt = 1.0;
                long double y = power_omt*power_t*points[n]*term_n/term_d;
                sum += y;
                return sum;
                }


                Additional linear simplifications possible - perhaps another day.





                Minor stuff



                Use const



                A const in the referenced date allows for some optimizations, wider application and better conveys code's intent.



                // long double bezier(long double t, uint64_t *points, uint64_t n){
                long double bezier(long double t, const uint64_t *points, uint64_t n){


                Excessive wide type



                With uint64_t n, there is no reasonable expectation that such an iteration will finish for large n.



                Fortunately, n indicates the size of the array. For array indexing and sizing, using size_t. It is the right size - not too narrow, nor too wide a type.



                // long double bezier(long double t, uint64_t *points, uint64_t n){
                long double bezier(long double t, uint64_t *points, size_t n){


                For this application, certainly unsigned would alway suffice.



                static



                Good use of static in static long double bbezier() to keep that function local.



                Missing "bezier.h"



                I'd expect a bezier() declarations in a .h file and implementation in the .c file instead of extern "C" long double bezier(long double, uint64_t *, uint64_t); in main.c



                // extern "C" long double bezier(long double, uint64_t *, uint64_t);
                #include "bezier.h".


                n range check



                Perhaps in a debug build, test n.



                long double bezier(long double t, const uint64_t *points, uint64_t n){
                assert( n > 0); // Low bound
                assert( n < 1000); // Maybe an sane upper limit too






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 20 hours ago

























                answered 23 hours ago









                chuxchux

                12.8k21344




                12.8k21344






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211157%2fcomputing-bezier-curves-of-degree-n-with-recursive-functions%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    How to reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

                    is 'sed' thread safe

                    How to make a Squid Proxy server?