Calculate next position and velocity after “bounce”











up vote
1
down vote

favorite












Intro and code



I have the following function that calculates an objects "next" position and velocity (in 1 dimension) given a boundary [0, max].



public TwoVector<Double> getNextPositionAndVelocity(double position, double velocity, double max) {
if (position + velocity < 0) {
return TwoVector.<Double>builder()
.x(Math.abs(position + velocity))
.y(Math.abs(velocity))
.build();
}
if (position + velocity > max) {
return TwoVector.<Double>builder()
.x(max - ((position + velocity) % max))
.y(-Math.abs(velocity))
.build();
}
return TwoVector.<Double>builder()
.x(position + velocity)
.y(velocity)
.build();
}


Explanation




  • The first condition checks if adding the current velocity to the current position would cause the object to be beyond the "left" boundary. If that's the case, the next position should be equal to the amount the object would be beyond the left boundary added to the left boundary and the next velocity should point the object to the "right".

  • The second condition checks if adding the current velocity to the current position would cause the object to beyond the "right" boundary. If that's the case, the next position should be equal to the amount the object would be beyond the boundary subtracted from the boundary and the next velocity should point the object to the "left".

  • If neither condition is true, we can add the current velocity to the current position to get the next position without issue.


Question



Are there more straightforward functions to calculate each of these "next" values? Perhaps a function that "collapses" these branches?










share|improve this question
















bumped to the homepage by Community 2 days ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.



















    up vote
    1
    down vote

    favorite












    Intro and code



    I have the following function that calculates an objects "next" position and velocity (in 1 dimension) given a boundary [0, max].



    public TwoVector<Double> getNextPositionAndVelocity(double position, double velocity, double max) {
    if (position + velocity < 0) {
    return TwoVector.<Double>builder()
    .x(Math.abs(position + velocity))
    .y(Math.abs(velocity))
    .build();
    }
    if (position + velocity > max) {
    return TwoVector.<Double>builder()
    .x(max - ((position + velocity) % max))
    .y(-Math.abs(velocity))
    .build();
    }
    return TwoVector.<Double>builder()
    .x(position + velocity)
    .y(velocity)
    .build();
    }


    Explanation




    • The first condition checks if adding the current velocity to the current position would cause the object to be beyond the "left" boundary. If that's the case, the next position should be equal to the amount the object would be beyond the left boundary added to the left boundary and the next velocity should point the object to the "right".

    • The second condition checks if adding the current velocity to the current position would cause the object to beyond the "right" boundary. If that's the case, the next position should be equal to the amount the object would be beyond the boundary subtracted from the boundary and the next velocity should point the object to the "left".

    • If neither condition is true, we can add the current velocity to the current position to get the next position without issue.


    Question



    Are there more straightforward functions to calculate each of these "next" values? Perhaps a function that "collapses" these branches?










    share|improve this question
















    bumped to the homepage by Community 2 days ago


    This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.

















      up vote
      1
      down vote

      favorite









      up vote
      1
      down vote

      favorite











      Intro and code



      I have the following function that calculates an objects "next" position and velocity (in 1 dimension) given a boundary [0, max].



      public TwoVector<Double> getNextPositionAndVelocity(double position, double velocity, double max) {
      if (position + velocity < 0) {
      return TwoVector.<Double>builder()
      .x(Math.abs(position + velocity))
      .y(Math.abs(velocity))
      .build();
      }
      if (position + velocity > max) {
      return TwoVector.<Double>builder()
      .x(max - ((position + velocity) % max))
      .y(-Math.abs(velocity))
      .build();
      }
      return TwoVector.<Double>builder()
      .x(position + velocity)
      .y(velocity)
      .build();
      }


      Explanation




      • The first condition checks if adding the current velocity to the current position would cause the object to be beyond the "left" boundary. If that's the case, the next position should be equal to the amount the object would be beyond the left boundary added to the left boundary and the next velocity should point the object to the "right".

      • The second condition checks if adding the current velocity to the current position would cause the object to beyond the "right" boundary. If that's the case, the next position should be equal to the amount the object would be beyond the boundary subtracted from the boundary and the next velocity should point the object to the "left".

      • If neither condition is true, we can add the current velocity to the current position to get the next position without issue.


      Question



      Are there more straightforward functions to calculate each of these "next" values? Perhaps a function that "collapses" these branches?










      share|improve this question















      Intro and code



      I have the following function that calculates an objects "next" position and velocity (in 1 dimension) given a boundary [0, max].



      public TwoVector<Double> getNextPositionAndVelocity(double position, double velocity, double max) {
      if (position + velocity < 0) {
      return TwoVector.<Double>builder()
      .x(Math.abs(position + velocity))
      .y(Math.abs(velocity))
      .build();
      }
      if (position + velocity > max) {
      return TwoVector.<Double>builder()
      .x(max - ((position + velocity) % max))
      .y(-Math.abs(velocity))
      .build();
      }
      return TwoVector.<Double>builder()
      .x(position + velocity)
      .y(velocity)
      .build();
      }


      Explanation




      • The first condition checks if adding the current velocity to the current position would cause the object to be beyond the "left" boundary. If that's the case, the next position should be equal to the amount the object would be beyond the left boundary added to the left boundary and the next velocity should point the object to the "right".

      • The second condition checks if adding the current velocity to the current position would cause the object to beyond the "right" boundary. If that's the case, the next position should be equal to the amount the object would be beyond the boundary subtracted from the boundary and the next velocity should point the object to the "left".

      • If neither condition is true, we can add the current velocity to the current position to get the next position without issue.


      Question



      Are there more straightforward functions to calculate each of these "next" values? Perhaps a function that "collapses" these branches?







      java coordinate-system physics






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Apr 28 at 21:37









      200_success

      127k15148411




      127k15148411










      asked Apr 28 at 20:58









      geofflittle

      825




      825





      bumped to the homepage by Community 2 days ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.







      bumped to the homepage by Community 2 days ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
























          2 Answers
          2






          active

          oldest

          votes

















          up vote
          0
          down vote













          I.



          x(max - ((position + velocity) % max))


          doesn't look quite right. % max does not help here much (see below) it is better replaced with - max, and thus the whole formula turns into 2 * max - (position + velocity).



          II. Do you assume large velocities are impossible, in other words, in the first alternative, can you guarantee that Math.abs(position + velocity) is always LE than max?






          share|improve this answer




























            up vote
            0
            down vote















            I assume "next" in getNextPositionAndVelocity means "after one unit of whatever temporal unit you use in the unit of the velocity", since the change of a position based on a velocity only makes sense in the context of a time span.



            As for your code, you could simply model the path of the object by breaking it up into subpaths where the direction does not change, like this:



            public double getNextPositionAndVelocity(double position, double velocity, double max) {
            double remainingDistance = Math.abs(velocity);
            double currentVelocity = velocity;
            double currentPosition = position;

            while (remainingDistance > 0.0) {
            double nextBoundary;
            if (currentVelocity < 0.0) {
            nextBoundary = 0.0;
            } else {
            assert currentVelocity > 0.0; //cannot be 0 or NaN, not if we're in the loop
            nextBoundary = max;
            }

            double maximumDistanceToTravelInCurrentDirection = Math.abs(nextBoundary - currentPosition);

            if (maximumDistanceToTravelInCurrentDirection <= remainingDistance) {
            currentPosition = nextBoundary;
            remainingDistance -= maximumDistanceToTravelInCurrentDirection;
            currentVelocity *= -1;
            } else {
            currentPosition += remainingDistance * Math.signum(currentVelocity);
            remainingDistance = 0;
            }
            }

            return new double{currentPosition, currentVelocity};
            }


            Admittedly, this is more code than your version, but then, you don't consider the possibility that the range is smaller than the velocity, and if this is the case, an object might bounce off an edge more than once.



            The above code could be optimized by first calculating the velocity modulo twice the range, because after two range lenghts, the position and velocity of the object will be identical to its initial position and velocity.



            Also, you should validate the arguments. This not only entails checking whether the arguments are valid with respect to each other (e.g. whether position lies within the permitted range), but also handling special cases like the infinities or Double.NaN. E.g., if you check whether position >= 0 && position <= max, then you automatically have NaN covered for position and max, because the comparison operators and the equality operator == always return false if one operand is NaN (even Double.NaN == Double.NaN returns false, which is why there's a method Double.isNaN(double)), but position might still be Double.POSITIVE_INFINITY if max is also Double.POSITIVE_INFINITY. Also, max should probably be greater than 0 (which would not be covered by the aforementioned condition either).






            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',
              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%2f193165%2fcalculate-next-position-and-velocity-after-bounce%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








              up vote
              0
              down vote













              I.



              x(max - ((position + velocity) % max))


              doesn't look quite right. % max does not help here much (see below) it is better replaced with - max, and thus the whole formula turns into 2 * max - (position + velocity).



              II. Do you assume large velocities are impossible, in other words, in the first alternative, can you guarantee that Math.abs(position + velocity) is always LE than max?






              share|improve this answer

























                up vote
                0
                down vote













                I.



                x(max - ((position + velocity) % max))


                doesn't look quite right. % max does not help here much (see below) it is better replaced with - max, and thus the whole formula turns into 2 * max - (position + velocity).



                II. Do you assume large velocities are impossible, in other words, in the first alternative, can you guarantee that Math.abs(position + velocity) is always LE than max?






                share|improve this answer























                  up vote
                  0
                  down vote










                  up vote
                  0
                  down vote









                  I.



                  x(max - ((position + velocity) % max))


                  doesn't look quite right. % max does not help here much (see below) it is better replaced with - max, and thus the whole formula turns into 2 * max - (position + velocity).



                  II. Do you assume large velocities are impossible, in other words, in the first alternative, can you guarantee that Math.abs(position + velocity) is always LE than max?






                  share|improve this answer












                  I.



                  x(max - ((position + velocity) % max))


                  doesn't look quite right. % max does not help here much (see below) it is better replaced with - max, and thus the whole formula turns into 2 * max - (position + velocity).



                  II. Do you assume large velocities are impossible, in other words, in the first alternative, can you guarantee that Math.abs(position + velocity) is always LE than max?







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Apr 28 at 22:30









                  bipll

                  5397




                  5397
























                      up vote
                      0
                      down vote















                      I assume "next" in getNextPositionAndVelocity means "after one unit of whatever temporal unit you use in the unit of the velocity", since the change of a position based on a velocity only makes sense in the context of a time span.



                      As for your code, you could simply model the path of the object by breaking it up into subpaths where the direction does not change, like this:



                      public double getNextPositionAndVelocity(double position, double velocity, double max) {
                      double remainingDistance = Math.abs(velocity);
                      double currentVelocity = velocity;
                      double currentPosition = position;

                      while (remainingDistance > 0.0) {
                      double nextBoundary;
                      if (currentVelocity < 0.0) {
                      nextBoundary = 0.0;
                      } else {
                      assert currentVelocity > 0.0; //cannot be 0 or NaN, not if we're in the loop
                      nextBoundary = max;
                      }

                      double maximumDistanceToTravelInCurrentDirection = Math.abs(nextBoundary - currentPosition);

                      if (maximumDistanceToTravelInCurrentDirection <= remainingDistance) {
                      currentPosition = nextBoundary;
                      remainingDistance -= maximumDistanceToTravelInCurrentDirection;
                      currentVelocity *= -1;
                      } else {
                      currentPosition += remainingDistance * Math.signum(currentVelocity);
                      remainingDistance = 0;
                      }
                      }

                      return new double{currentPosition, currentVelocity};
                      }


                      Admittedly, this is more code than your version, but then, you don't consider the possibility that the range is smaller than the velocity, and if this is the case, an object might bounce off an edge more than once.



                      The above code could be optimized by first calculating the velocity modulo twice the range, because after two range lenghts, the position and velocity of the object will be identical to its initial position and velocity.



                      Also, you should validate the arguments. This not only entails checking whether the arguments are valid with respect to each other (e.g. whether position lies within the permitted range), but also handling special cases like the infinities or Double.NaN. E.g., if you check whether position >= 0 && position <= max, then you automatically have NaN covered for position and max, because the comparison operators and the equality operator == always return false if one operand is NaN (even Double.NaN == Double.NaN returns false, which is why there's a method Double.isNaN(double)), but position might still be Double.POSITIVE_INFINITY if max is also Double.POSITIVE_INFINITY. Also, max should probably be greater than 0 (which would not be covered by the aforementioned condition either).






                      share|improve this answer

























                        up vote
                        0
                        down vote















                        I assume "next" in getNextPositionAndVelocity means "after one unit of whatever temporal unit you use in the unit of the velocity", since the change of a position based on a velocity only makes sense in the context of a time span.



                        As for your code, you could simply model the path of the object by breaking it up into subpaths where the direction does not change, like this:



                        public double getNextPositionAndVelocity(double position, double velocity, double max) {
                        double remainingDistance = Math.abs(velocity);
                        double currentVelocity = velocity;
                        double currentPosition = position;

                        while (remainingDistance > 0.0) {
                        double nextBoundary;
                        if (currentVelocity < 0.0) {
                        nextBoundary = 0.0;
                        } else {
                        assert currentVelocity > 0.0; //cannot be 0 or NaN, not if we're in the loop
                        nextBoundary = max;
                        }

                        double maximumDistanceToTravelInCurrentDirection = Math.abs(nextBoundary - currentPosition);

                        if (maximumDistanceToTravelInCurrentDirection <= remainingDistance) {
                        currentPosition = nextBoundary;
                        remainingDistance -= maximumDistanceToTravelInCurrentDirection;
                        currentVelocity *= -1;
                        } else {
                        currentPosition += remainingDistance * Math.signum(currentVelocity);
                        remainingDistance = 0;
                        }
                        }

                        return new double{currentPosition, currentVelocity};
                        }


                        Admittedly, this is more code than your version, but then, you don't consider the possibility that the range is smaller than the velocity, and if this is the case, an object might bounce off an edge more than once.



                        The above code could be optimized by first calculating the velocity modulo twice the range, because after two range lenghts, the position and velocity of the object will be identical to its initial position and velocity.



                        Also, you should validate the arguments. This not only entails checking whether the arguments are valid with respect to each other (e.g. whether position lies within the permitted range), but also handling special cases like the infinities or Double.NaN. E.g., if you check whether position >= 0 && position <= max, then you automatically have NaN covered for position and max, because the comparison operators and the equality operator == always return false if one operand is NaN (even Double.NaN == Double.NaN returns false, which is why there's a method Double.isNaN(double)), but position might still be Double.POSITIVE_INFINITY if max is also Double.POSITIVE_INFINITY. Also, max should probably be greater than 0 (which would not be covered by the aforementioned condition either).






                        share|improve this answer























                          up vote
                          0
                          down vote










                          up vote
                          0
                          down vote











                          I assume "next" in getNextPositionAndVelocity means "after one unit of whatever temporal unit you use in the unit of the velocity", since the change of a position based on a velocity only makes sense in the context of a time span.



                          As for your code, you could simply model the path of the object by breaking it up into subpaths where the direction does not change, like this:



                          public double getNextPositionAndVelocity(double position, double velocity, double max) {
                          double remainingDistance = Math.abs(velocity);
                          double currentVelocity = velocity;
                          double currentPosition = position;

                          while (remainingDistance > 0.0) {
                          double nextBoundary;
                          if (currentVelocity < 0.0) {
                          nextBoundary = 0.0;
                          } else {
                          assert currentVelocity > 0.0; //cannot be 0 or NaN, not if we're in the loop
                          nextBoundary = max;
                          }

                          double maximumDistanceToTravelInCurrentDirection = Math.abs(nextBoundary - currentPosition);

                          if (maximumDistanceToTravelInCurrentDirection <= remainingDistance) {
                          currentPosition = nextBoundary;
                          remainingDistance -= maximumDistanceToTravelInCurrentDirection;
                          currentVelocity *= -1;
                          } else {
                          currentPosition += remainingDistance * Math.signum(currentVelocity);
                          remainingDistance = 0;
                          }
                          }

                          return new double{currentPosition, currentVelocity};
                          }


                          Admittedly, this is more code than your version, but then, you don't consider the possibility that the range is smaller than the velocity, and if this is the case, an object might bounce off an edge more than once.



                          The above code could be optimized by first calculating the velocity modulo twice the range, because after two range lenghts, the position and velocity of the object will be identical to its initial position and velocity.



                          Also, you should validate the arguments. This not only entails checking whether the arguments are valid with respect to each other (e.g. whether position lies within the permitted range), but also handling special cases like the infinities or Double.NaN. E.g., if you check whether position >= 0 && position <= max, then you automatically have NaN covered for position and max, because the comparison operators and the equality operator == always return false if one operand is NaN (even Double.NaN == Double.NaN returns false, which is why there's a method Double.isNaN(double)), but position might still be Double.POSITIVE_INFINITY if max is also Double.POSITIVE_INFINITY. Also, max should probably be greater than 0 (which would not be covered by the aforementioned condition either).






                          share|improve this answer














                          I assume "next" in getNextPositionAndVelocity means "after one unit of whatever temporal unit you use in the unit of the velocity", since the change of a position based on a velocity only makes sense in the context of a time span.



                          As for your code, you could simply model the path of the object by breaking it up into subpaths where the direction does not change, like this:



                          public double getNextPositionAndVelocity(double position, double velocity, double max) {
                          double remainingDistance = Math.abs(velocity);
                          double currentVelocity = velocity;
                          double currentPosition = position;

                          while (remainingDistance > 0.0) {
                          double nextBoundary;
                          if (currentVelocity < 0.0) {
                          nextBoundary = 0.0;
                          } else {
                          assert currentVelocity > 0.0; //cannot be 0 or NaN, not if we're in the loop
                          nextBoundary = max;
                          }

                          double maximumDistanceToTravelInCurrentDirection = Math.abs(nextBoundary - currentPosition);

                          if (maximumDistanceToTravelInCurrentDirection <= remainingDistance) {
                          currentPosition = nextBoundary;
                          remainingDistance -= maximumDistanceToTravelInCurrentDirection;
                          currentVelocity *= -1;
                          } else {
                          currentPosition += remainingDistance * Math.signum(currentVelocity);
                          remainingDistance = 0;
                          }
                          }

                          return new double{currentPosition, currentVelocity};
                          }


                          Admittedly, this is more code than your version, but then, you don't consider the possibility that the range is smaller than the velocity, and if this is the case, an object might bounce off an edge more than once.



                          The above code could be optimized by first calculating the velocity modulo twice the range, because after two range lenghts, the position and velocity of the object will be identical to its initial position and velocity.



                          Also, you should validate the arguments. This not only entails checking whether the arguments are valid with respect to each other (e.g. whether position lies within the permitted range), but also handling special cases like the infinities or Double.NaN. E.g., if you check whether position >= 0 && position <= max, then you automatically have NaN covered for position and max, because the comparison operators and the equality operator == always return false if one operand is NaN (even Double.NaN == Double.NaN returns false, which is why there's a method Double.isNaN(double)), but position might still be Double.POSITIVE_INFINITY if max is also Double.POSITIVE_INFINITY. Also, max should probably be greater than 0 (which would not be covered by the aforementioned condition either).







                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Apr 28 at 23:13









                          Stingy

                          1,913212




                          1,913212






























                               

                              draft saved


                              draft discarded



















































                               


                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193165%2fcalculate-next-position-and-velocity-after-bounce%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

                              Quarter-circle Tiles

                              build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

                              Mont Emei