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




























    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


























      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

      127k15148412




      127k15148412










      asked Apr 28 at 20:58









      geofflittle

      825




      825






















          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

                  5497




                  5497
























                      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




















































                              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%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

                              Terni

                              A new problem with tex4ht and tikz

                              Sun Ra