From: A-Larsen Date: Thu, 15 Oct 2026 02:34:59 +0000 (-0400) Subject: first commit X-Git-Url: https://nyquistdev.net/git/?a=commitdiff_plain;h=refs%2Fheads%2Fmain;p=balls.git first commit --- 5ac44c7448548064590f696dda3fd48ce92a508e diff --git a/2d-projectile-motion/Makefile b/2d-projectile-motion/Makefile new file mode 100644 index 0000000..0ddb263 --- /dev/null +++ b/2d-projectile-motion/Makefile @@ -0,0 +1,11 @@ +LIBS = -lSDL2 -lSDL2_ttf -lm + +PROG = balls + +build: $(PROG).c + gcc -g -o $(PROG) $(PROG).c $(LIBS) + +clean: + rm -rf $(PROG) + +.PHONY: clean diff --git a/2d-projectile-motion/balls.c b/2d-projectile-motion/balls.c new file mode 100644 index 0000000..0846ca3 --- /dev/null +++ b/2d-projectile-motion/balls.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2024 Austin Larsen + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include + +#define SCREEN_WIDTH_PX 1600 +#define SCREEN_HEIGHT_PX 800 +#define ACC_GRAVITY_MPS 9.81f +#define GROUND_HEIGHT_PX 750 + +typedef struct _Mouse { + int x; + int y; + uint32_t button; +} Mouse; + +enum /* color */ {COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_ORANGE, COLOR_GREY, + COLOR_WHITE, COLOR_BLACK, COLOR_SIZE}; + +void getMouse(Mouse *mouse); +void update(SDL_Renderer *renderer, uint64_t frame, float seconds, + SDL_KeyCode key, Mouse *mouse); +void +setColor(SDL_Renderer *renderer, uint8_t color); + + + +void +drawBall(SDL_Renderer *renderer, int px, int py, uint8_t radius, uint8_t color) +// (x - h)^2 + (y - k)^2 = r^2 +// Drawing a circle with the standard circle formula. +// There may be a more performant way to do this... +{ + setColor(renderer, color); + + // diameter is radius * 2 + for (int x = 0; x < radius * 2; x++) { + for (int y = 0; y < radius * 2; y++) { + if ( ((x - radius) * (x - radius)) + + ((y - radius) * (y - radius)) <= + radius * radius) { + int tx = px - radius + x; + int ty = py - radius + y; + + // TODO: wrap circle around screen + SDL_RenderDrawPoint(renderer, tx, ty); + + } + } + } +} + +int main(void) +{ + SDL_Window *window; + SDL_Renderer *renderer; + + printf("\033[2J"); // clear entire screen escape sequence + + { // SDL Initialization + if (SDL_Init(SDL_INIT_VIDEO) != 0) + fprintf(stderr, "could not init video\n%s", SDL_GetError()); + + window = SDL_CreateWindow("Projectile", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX, + SDL_WINDOW_SHOWN); + + renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_SOFTWARE); + } // SDL Initialization + + + { // game loop + bool quit = false; + const uint16_t fps = 60; + const float mspd = (1.0f / (float)fps) * 1000.0f; + uint64_t frame = 0; + float seconds = 0; + + while (!quit) { + uint32_t loop_start = SDL_GetTicks(); + + SDL_Event event; + SDL_KeyCode key = 0; + Mouse mouse; + bool keydown = false; + + getMouse(&mouse); + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_KEYDOWN: { + if (event.key.repeat == 0) { + key = event.key.keysym.sym; + keydown = true; + } + break; + } + case SDL_KEYUP: keydown = false; break; + case SDL_QUIT: quit = true; break; + } + } + + setColor(renderer, COLOR_BLACK); + SDL_RenderClear(renderer); + update(renderer, frame, seconds, key, &mouse); + SDL_RenderPresent(renderer); + frame++; + seconds = ((float)frame / (float)fps); + + uint32_t loop_end = SDL_GetTicks(); + uint32_t elapsed_time = loop_end - loop_start; + + uint32_t delay = ceilf(mspd - (float)elapsed_time); + + if (delay > 0) { + SDL_Delay(delay); + } + } + + } // game loop + + + { // quit + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + } // quit + +} + +void getMouse(Mouse * mouse) +{ + mouse->button = SDL_GetMouseState(&mouse->x, &mouse->y); +} + +void drawPath(SDL_Renderer *renderer, SDL_Point *point, float velocity, + float angle, float seconds) + // velocity in meters per second +{ + // REMEMBER! negative is upward and positive is downward. That is why + // the initial velocity for y (vi_y) is negative and acceleration due to + // gravity (ACC_GRAVITY_MPS) is positive + float vi_y = -velocity * sinf(angle); + float vi_x = velocity * cosf(angle); + float delta_x = (float)vi_x * (float)seconds; + float delta_y = (float)vi_y * (float)seconds + (0.5f * ACC_GRAVITY_MPS + * (seconds * seconds)); + + // SDL_Rect rect = { + // .x = point->x + delta_x, + // .y = point->y + delta_y, + // .w = 10, + // .h = 10 + // }; + int tx = point->x + delta_x; + int ty = point->y + delta_y; + + if (delta_y <= -1) { + printf("\033[H"); // clear and set to home position escape sequence + printf("seconds: %f\n", seconds); + printf("y displacement: %f\n", delta_y); + printf("x displacement: %f\n", delta_x); + printf("x: %d\n", tx); + printf("y: %d\n", ty); + // SDL_RenderFillRect(renderer, &rect); + drawBall(renderer, tx, ty, 20, COLOR_BLUE); + } + +} + +void update(SDL_Renderer *renderer, uint64_t frame, float seconds, + SDL_KeyCode key, Mouse *mouse) +{ + static float launch_start = 0; + static float launch_angle = 0; + static float launch_initial = 0; + + SDL_Point point = { + .x = 10, + .y = GROUND_HEIGHT_PX + }; + + // 0 to 90 degrees (1/2 pi) + setColor(renderer, COLOR_BLUE); + SDL_RenderDrawLine(renderer, point.x, point.y, mouse->x, mouse->y); + + + int opposite = (mouse->y - point.y); + int adjacent = (mouse->x - point.x); + float angle = -atanf((float)opposite / (float)adjacent); + + setColor(renderer, COLOR_RED); + SDL_RenderDrawLine(renderer, 0, GROUND_HEIGHT_PX, SCREEN_WIDTH_PX, + GROUND_HEIGHT_PX); + + if (mouse->button == 1) { + launch_start = seconds; + launch_angle = angle; + // hypotenuse is velocity + launch_initial = sqrtf((opposite * opposite) + (adjacent * adjacent)); + } + + if (launch_start > 0) { + drawPath(renderer, &point, launch_initial, launch_angle, seconds - + launch_start); + } +} + +void +setColor(SDL_Renderer *renderer, uint8_t color) +{ + const SDL_Color colors[] = { + [COLOR_RED] = {.r = 217, .g = 100, .b = 89, .a = 255}, + [COLOR_WHITE] = {.r = 255, .g = 255, .b = 255, .a = 255}, + [COLOR_GREEN] = {.r = 88, .g = 140, .b = 126, .a = 255}, + [COLOR_BLUE] = {.r = 146, .g = 161, .b = 185, .a = 255}, + [COLOR_ORANGE] = {.r = 242, .g = 174, .b = 114, .a = 255}, + [COLOR_GREY] = {.r = 89, .g = 89, .b = 89, .a = 89}, + [COLOR_BLACK] = {.r = 0, .g = 0, .b = 0, .a = 0}, + }; + + SDL_SetRenderDrawColor(renderer, colors[color].r, colors[color].g, + colors[color].b, colors[color].a); +} + diff --git a/2d-projectile-motion/docs/formulas.adoc b/2d-projectile-motion/docs/formulas.adoc new file mode 100644 index 0000000..735bb81 --- /dev/null +++ b/2d-projectile-motion/docs/formulas.adoc @@ -0,0 +1,28 @@ +:stem: +:source-highlighter: pygments +:pygments-style: monokai +:wrap: normal + += Formulas + +* Velocity of y + - stem:[\Deltay = V_{0}t + 1/2at^2] + +* Velocity of x + - stem:[\Deltax = V_{0}t] + +* Angle of trojectory + - stem:[\theta = \arctan(frac{opposite}{adjactent})] + +* Getting initial y velocity (going downward increases in value) + - stem:[y_{0} = -l_0 * \sin(\theta)] + +* Getting initial x velocity + - stem:[x_{0} = l_0 * \cos(\theta)] + +* Pathagorean Theorem + - stem:[a^2 + b^2 = c^2] + +NOTE: launch_initial is just an intial, almost arbitrary, value. It is just a + distance the launch pad is from the mouse and is used to control the + amount of velocity you want. diff --git a/2d-projectile-motion/docs/formulas.html b/2d-projectile-motion/docs/formulas.html new file mode 100644 index 0000000..a226e81 --- /dev/null +++ b/2d-projectile-motion/docs/formulas.html @@ -0,0 +1,550 @@ + + + + + + + +Formulas + + + + + +
+
+
    +
  • +

    Velocity of y

    +
    +
      +
    • +

      \$\Deltay = V_{0}t + 1/2at^2\$

      +
    • +
    +
    +
  • +
  • +

    Velocity of x

    +
    +
      +
    • +

      \$\Deltax = V_{0}t\$

      +
    • +
    +
    +
  • +
  • +

    Angle of trojectory

    +
    +
      +
    • +

      \$\theta = \arctan(frac{opposite}{adjactent})\$

      +
    • +
    +
    +
  • +
  • +

    Getting initial y velocity (going downward increases in value)

    +
    +
      +
    • +

      \$y_{0} = -l_0 * \sin(\theta)\$

      +
    • +
    +
    +
  • +
  • +

    Getting initial x velocity

    +
    +
      +
    • +

      \$x_{0} = l_0 * \cos(\theta)\$

      +
    • +
    +
    +
  • +
  • +

    Pathagorean Theorem

    +
    +
      +
    • +

      \$a^2 + b^2 = c^2\$

      +
    • +
    +
    +
  • +
+
+
+ + + + + +
+
Note
+
+launch_initial is just an intial, almost arbitrary, value. It is just a + distance the launch pad is from the mouse and is used to control the + amount of velocity you want. +
+
+
+ + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makehtml b/Makehtml new file mode 100755 index 0000000..a7e355a --- /dev/null +++ b/Makehtml @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +. docinfo.conf + +clean () { +rm -rf diag*.png +rm -rf $(find . \ + -type f \ + -regex '.*\.html' \ + -not -name "rendered.html" \ + | grep -v docinfo) +} + +build () { +FILENAME="$(find . -type f -name "*.adoc" -not -name "README.adoc" -not -name "notes.adoc")" +asciidoctor -b html5 $FILENAME +} + +make () { + clean + build +} + +# use this to call one of the above functions +$@ diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..42730b0 --- /dev/null +++ b/README.adoc @@ -0,0 +1,52 @@ += Balls + +This repository has physics examples using balls. Each folder has a different +example, with a link to the formulas used in the readme's. + +== 2D Projectile Motion +checkout this link +https://nyquistdev.net/git/?p=balls.git;a=blob_plain;f=2d-projectile-motion/docs/formulas.html;hb=HEAD[formulas]. +for a list of forumlas used. + +[%header,cols="1,1"] +|=== +| control | action +| right-click and drag | determine direction and velocity of ball +| right-click release | launch ball +|=== + +== Bouncing +check out this link +https://nyquistdev.net/git/?p=balls.git;a=blob_plain;f=bounce/docs/formulas.html;hb=HEAD[formulas]. +for the formulas used. + +== Collisions +Creates random balls. These balls will resolve any collision issues. Launching a +ball will show you an example of elastic collision. + +checkout this link +https://nyquistdev.net/git/?p=balls.git;a=blob_plain;f=collisions/docs/formulas.html;hb=HEAD[formulas]. +for a list of forumlas used. + +[%header,cols="1,1"] +|=== +| control | action +| right-click | select ball +| right-click and left-click and drag | flings ball on release, the distance + away from the ball determines the amount + of velocity to launch the ball at +|=== + +== Building html from formula _.adoc_ files + +---- +./Makehtml make +---- + +== Links +* https://www.studyplan.dev/sdl2/sdl2-relative-mode[sdl2-relative-mouse-mode] +* https://www.youtube.com/watch?v=XJnIdRXUi7A&t=315s[permutations and combinations] +* https://www.youtube.com/watch?v=CFbo_nBdBco[OCT | Elastic Collisions] +* https://www.wikihow.com/Normalize-a-Vector[Normalizing a Vector] +* https://www.youtube.com/watch?v=Fp7D5D8Bqjc[conservation of Momentum] + diff --git a/bounce/Makefile b/bounce/Makefile new file mode 100644 index 0000000..0ddb263 --- /dev/null +++ b/bounce/Makefile @@ -0,0 +1,11 @@ +LIBS = -lSDL2 -lSDL2_ttf -lm + +PROG = balls + +build: $(PROG).c + gcc -g -o $(PROG) $(PROG).c $(LIBS) + +clean: + rm -rf $(PROG) + +.PHONY: clean diff --git a/bounce/balls b/bounce/balls new file mode 100755 index 0000000..51a3a1c Binary files /dev/null and b/bounce/balls differ diff --git a/bounce/balls.c b/bounce/balls.c new file mode 100644 index 0000000..c794912 --- /dev/null +++ b/bounce/balls.c @@ -0,0 +1,336 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// NOTE: +// This is a less accurate depiction of gravity. I am using a different number +// as the mesurement in meters per second because you cannot have fractions of a +// pixel. In order to depict this more accurately I would need an approximate +// size of a pixel, and not every pixel is the same size. I have decided not to +// be THAT accurate. +#define METER_AS_PIXELS 3779U +// #define GRAVITY 0.5f + +#define END(check, str1, str2) \ + if (check) { \ + assert(check); \ + fprintf(stderr, "%s\n%s", str1, str2); \ + exit(1); \ + } \ + +#define SDL_main main + + +typedef struct _Game { + SDL_Renderer *renderer; + SDL_Window *window; + bool out_of_bounds; + uint16_t fps; + float gravity; + float terminal_velocity; + SDL_Rect screen_rect; +} Game; + +typedef struct _Ball { + SDL_Point center; + SDL_Point velocities; + uint16_t radius; + bool down; // check if the ball is + // these two varaibles reset with each fall + float time_falling; // time it takes to fall from peak + float height_falling; // max height for each fall + float e; // restition value +} Ball; + +typedef uint8_t (*Update_callback) (Game *game, + float seconds, + uint64_t frame, + SDL_KeyCode key, + bool keydown); + +enum {UPDATE_MAIN, UPDATE_NOTHING}; + +enum {COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_ORANGE, COLOR_GREY, + COLOR_BLACK, COLOR_SIZE}; + +void +setColor(SDL_Renderer *renderer, + uint8_t color) +{ + const SDL_Color colors[] = { + [COLOR_RED] = {.r = 217, .g = 100, .b = 89, .a = 255}, + [COLOR_GREEN] = {.r = 88, .g = 140, .b = 126, .a = 255}, + [COLOR_BLUE] = {.r = 146, .g = 161, .b = 185, .a = 255}, + [COLOR_ORANGE] = {.r = 242, .g = 174, .b = 114, .a = 255}, + [COLOR_GREY] = {.r = 89, .g = 89, .b = 89, .a = 89}, + [COLOR_BLACK] = {.r = 0, .g = 0, .b = 0, .a = 0}, + }; + + SDL_SetRenderDrawColor(renderer, colors[color].r, colors[color].g, + colors[color].b, colors[color].a); +} + +void +drawCircle(SDL_Renderer *renderer, + int radius, + SDL_Point center, + uint8_t color) +// (x - h)^2 + (y - k)^2 = r^2 +// Drawing a circle with the standard circle formula. +// There may be a more performant way to do this... +{ + int size = radius; + setColor(renderer, color); + + // diameter is radius * 2 + for (int x = 0; x < radius * 2; x++) { + for (int y = 0; y < radius * 2; y++) { + if (((x - size)*(x - size)) + ((y - size)*(y - size)) <= radius + * radius) + SDL_RenderDrawPoint(renderer, center.x - radius + x, center.y - radius + y); + } + } +} + +float clamp(float v, float min, float max) { + if (v < min) return min; + if (v > max) return max; + return v; +} + +bool circleRectCollide(SDL_Point circle, + float r, + SDL_Rect rect) +{ + float closestX = clamp(circle.x, rect.x, rect.x + rect.w); + float closestY = clamp(circle.y, rect.y, rect.y + rect.h); + + // this gets the distance form the center of the circle to a side of a + // rectangle. It could be negative or positive. Because we are squaring them + // it doesn't matter. + float dx = circle.x - closestX; + float dy = circle.y - closestY; + + // use the dot product to determine collision + return (dx*dx + dy*dy) <= (r*r); +} + +static uint8_t +updateNothing(Game *game, + float seconds, + uint64_t frame, + SDL_KeyCode key, + bool keydown) +{ + return UPDATE_NOTHING; +} + +void +worldBoundry(Game *game, Ball *ball) +{ + int padding = 1; + + if (ball->center.x - ball->radius <= 0) { + ball->center.x = ball->radius + padding; + } + + if (ball->center.y - ball->radius <= 0) { + ball->center.y = ball->radius + padding; + } + + if (ball->center.x + ball->radius >= game->screen_rect.w) { + ball->center.x = game->screen_rect.w - ball->radius - padding; + } + + if (ball->center.y + ball->radius >= game->screen_rect.h) { + ball->center.y = game->screen_rect.h - ball->radius - padding; + } +} + +static uint8_t +updateMain(Game *game, + float seconds, + uint64_t frame, + SDL_KeyCode key, + bool keydown) +{ + // as long as gravity and time are using the same units the fomrulas will + // work. in this case we will do frames as out unit + // * Rubber ball: (e \approx 0.8 - 0.9) + // * Basketball: (e \approx 0.75) + // * Tennis ball: (e \approx 0.6) + static Ball ball = { + .center = {.x = 400, .y = 400}, + .radius = 30, + .down = true, + .e = 0.9f + }; + + static float initial_y = 400; + static float y = 400; + static float restitution = 1; + static float f = 0; + static float acceleration = 0; + + + if (ball.down) // falling downward + acceleration += (game->gravity * f); + else + acceleration -= restitution - (game->gravity * f); + + // clip this to world border + ball.center.y = y + acceleration; + + // ball.height_falling = initial_y + (0.5f * game->gravity) * (f * f); + + // ball.time_falling = sqrt(2 * (initial_y - ball.radius) / game->gravity); + // + // This if statement cannot be used because + // if (f > time && ball.down) { + if ((ball.center.y + ball.radius >= game->screen_rect.h) && + restitution >= 0) { + f = 0; + ball.down = false; + restitution *= ball.e; + } + + worldBoundry(game, &ball); + + // Should not go out of bounds. but check if it does + game->out_of_bounds = + !circleRectCollide(ball.center, ball.radius, game->screen_rect); + + drawCircle(game->renderer, ball.radius, ball.center, COLOR_RED); + f++; + + return UPDATE_MAIN; +} + +void +Game_Update(Game *game) +// The main game loop. Sets up which callback will be used in the function loop. +// Each update callback determines what update callback will be called next by +// returning the appropriate enum value +{ + uint64_t frame = 0; + bool quit = false; + bool keydown = false; + uint8_t update_id = 0; + Update_callback update; + float mspd = (1.0f / (float)game->fps) * 1000.0f; + float seconds = 0; + SDL_Event event; + SDL_KeyCode key = 0; + uint32_t end = 0; + uint32_t elapsed_time = 0; + uint8_t color = COLOR_BLACK; + + while (!quit) { + uint32_t start = SDL_GetTicks(); + // clear screen + if (game->out_of_bounds) color = COLOR_GREEN; + setColor(game->renderer, color); + SDL_RenderClear(game->renderer); + // TODO: + // find milliseconds so you can update gravity more acurately + // (1 / frame) + seconds = ((float)frame / (float)game->fps); + // printf("%lu: %f\n", frame, seconds); + + + // Place update functions here + switch (update_id) { + case UPDATE_MAIN: update = updateMain; break; + case UPDATE_NOTHING: update = updateNothing; break; + // case UPDATE_LOSE: update = updateLose; break; + } + + + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_KEYDOWN: { + if (event.key.repeat == 0) { + key = event.key.keysym.sym; + keydown = true; + } + + break; + } + + case SDL_KEYUP: keydown = false; break; + case SDL_QUIT: quit = true; break; + } + } + + update_id = update(game, seconds, frame, key, keydown); + + end = SDL_GetTicks(); + elapsed_time = end - start; + + if (elapsed_time < mspd) { + elapsed_time = mspd - elapsed_time; + SDL_Delay(elapsed_time); + } + + SDL_RenderPresent(game->renderer); + frame++; + } +} + +void +Game_Init(Game *game) +// All the variable and data initialization needed for SDL and perhaps game +// variables +{ + memset(game, 0, sizeof(Game)); + game->screen_rect.x = 0; + game->screen_rect.y = 0; + game->screen_rect.w = 800; + game->screen_rect.h = 800; + + END(SDL_Init(SDL_INIT_VIDEO) != 0, "Could not create texture", + SDL_GetError()); + + END(TTF_Init() != 0, "Could not initialize TTF", TTF_GetError()); + + + + game->window = SDL_CreateWindow("balls", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, game->screen_rect.w, + game->screen_rect.h, SDL_WINDOW_SHOWN); + + END(game->window == NULL, "Could not create window", SDL_GetError()); + + game->renderer = SDL_CreateRenderer(game->window, 0, + SDL_RENDERER_SOFTWARE); + + END(game->renderer == NULL, "Could not create renderer", SDL_GetError()); + game->fps = 400; + // game->terminal_velocity = + game->gravity = 0.004f; +} + +void +Game_Quit(Game *game) +{ + SDL_DestroyWindow(game->window); + SDL_DestroyRenderer(game->renderer); + TTF_Quit(); + SDL_Quit(); +} + +int +main(void) +{ + Game game; + Game_Init(&game); + Game_Update(&game); + Game_Quit(&game); + return 0; +} diff --git a/bounce/docs/chatgpt_dump/circle-circle-collision.md b/bounce/docs/chatgpt_dump/circle-circle-collision.md new file mode 100644 index 0000000..cb838ac --- /dev/null +++ b/bounce/docs/chatgpt_dump/circle-circle-collision.md @@ -0,0 +1,151 @@ +Below is a clear explanation of **circle–circle collision detection**, including the **math formulas** and **C code examples**. + +--- + +# ✅ **Math Behind Circle Collision** + +Two circles collide (intersect or touch) when the **distance between their centers** is **less than or equal to** the **sum of their radii**. + +Let: + +* Circle 1 center: ((x_1, y_1)), radius (r_1) +* Circle 2 center: ((x_2, y_2)), radius (r_2) + +### **1. Compute distance between centers** + +[ +d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2} +] + +### **2. Collision condition** + +[ +d \le r_1 + r_2 +] + +A common optimization is to avoid the expensive `sqrt()`: + +### **Squared distance method (no sqrt)** + +[ +(x_2 - x_1)^2 + (y_2 - y_1)^2 \le (r_1 + r_2)^2 +] + +This is identical mathematically, but faster. + +--- + +# ✅ **C Example: Basic Circle Collision Check** + +```c +#include +#include +#include + +typedef struct { + float x; + float y; + float r; +} Circle; + +bool circlesCollide(Circle a, Circle b) { + float dx = b.x - a.x; + float dy = b.y - a.y; + float distance = sqrtf(dx * dx + dy * dy); + + return distance <= (a.r + b.r); +} + +int main() { + Circle c1 = {0.0f, 0.0f, 5.0f}; + Circle c2 = {6.0f, 0.0f, 5.0f}; + + if (circlesCollide(c1, c2)) { + printf("Circles collide!\n"); + } else { + printf("No collision.\n"); + } + + return 0; +} +``` + +--- + +# ✅ **C Example: Optimized (No sqrt)** + +```c +#include +#include + +typedef struct { + float x; + float y; + float r; +} Circle; + +bool circlesCollideFast(Circle a, Circle b) { + float dx = b.x - a.x; + float dy = b.y - a.y; + + float distanceSquared = dx * dx + dy * dy; + float radiusSum = a.r + b.r; + + return distanceSquared <= radiusSum * radiusSum; +} + +int main() { + Circle c1 = {0.0f, 0.0f, 5.0f}; + Circle c2 = {6.0f, 0.0f, 5.0f}; + + if (circlesCollideFast(c1, c2)) { + printf("Circles collide!\n"); + } else { + printf("No collision.\n"); + } + + return 0; +} +``` + +--- + +# 🚀 **Example Output** + +For the sample circles, they collide because distance = 6 and r1 + r2 = 10. + +``` +Circles collide! +``` + +--- + +# 🧠 Optional: Collision Penetration Depth (Extra Useful) + +Sometimes you want *how much* they overlap: + +[ +\text{penetration} = (r_1 + r_2) - d +] + +C example: + +```c +float penetrationDepth(Circle a, Circle b) { + float dx = b.x - a.x; + float dy = b.y - a.y; + float d = sqrtf(dx * dx + dy * dy); + return (a.r + b.r) - d; +} +``` + +--- + +If you'd like, I can also show: + +✅ Circle vs Rectangle collision +✅ Circle movement simulation +✅ Game-loop example using SDL2 +✅ Visual ASCII output + +Just tell me! diff --git a/bounce/docs/chatgpt_dump/circle-rectange-SDL2-collision.md b/bounce/docs/chatgpt_dump/circle-rectange-SDL2-collision.md new file mode 100644 index 0000000..d0c85ad --- /dev/null +++ b/bounce/docs/chatgpt_dump/circle-rectange-SDL2-collision.md @@ -0,0 +1,119 @@ +```c +#include +#include +#include + +// ----------------------------------------- +// Utility functions +// ----------------------------------------- +float clamp(float v, float min, float max) { + if (v < min) return min; + if (v > max) return max; + return v; +} + +bool circleRectCollide(float cx, float cy, float r, + float rx, float ry, float rw, float rh) +{ + float closestX = clamp(cx, rx, rx + rw); + float closestY = clamp(cy, ry, ry + rh); + + float dx = cx - closestX; + float dy = cy - closestY; + + return (dx*dx + dy*dy) <= (r*r); +} + +// ----------------------------------------- +// Main SDL demo +// ----------------------------------------- +int main() { + SDL_Init(SDL_INIT_VIDEO); + + SDL_Window* window = SDL_CreateWindow( + "Circle vs Rectangle Collision - SDL2", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + 800, 600, + SDL_WINDOW_SHOWN + ); + + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + + // Circle properties + float cx = 100.0f; + float cy = 100.0f; + float cr = 25.0f; + float speed = 200.0f; // pixels per second + + // Rectangle properties + float rx = 400.0f; + float ry = 250.0f; + float rw = 150.0f; + float rh = 100.0f; + + Uint32 last = SDL_GetTicks(); + bool running = true; + + while (running) { + Uint32 now = SDL_GetTicks(); + float dt = (now - last) / 1000.0f; + last = now; + + // ------------------ + // Input + // ------------------ + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) running = false; + } + + const Uint8* keys = SDL_GetKeyboardState(NULL); + if (keys[SDL_SCANCODE_ESCAPE]) running = false; + + // Move circle + if (keys[SDL_SCANCODE_W]) cy -= speed * dt; + if (keys[SDL_SCANCODE_S]) cy += speed * dt; + if (keys[SDL_SCANCODE_A]) cx -= speed * dt; + if (keys[SDL_SCANCODE_D]) cx += speed * dt; + + // ------------------ + // Collision check + // ------------------ + bool hit = circleRectCollide(cx, cy, cr, rx, ry, rw, rh); + + // ------------------ + // Render + // ------------------ + SDL_SetRenderDrawColor(renderer, 20, 20, 20, 255); + SDL_RenderClear(renderer); + + // Rectangle (turn red when hit) + if (hit) + SDL_SetRenderDrawColor(renderer, 255, 50, 50, 255); + else + SDL_SetRenderDrawColor(renderer, 100, 100, 255, 255); + + SDL_Rect rect = { (int)rx, (int)ry, (int)rw, (int)rh }; + SDL_RenderFillRect(renderer, &rect); + + // Draw circle (simple filled circle) + SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255); + for (int w = 0; w < cr * 2; w++) { + for (int h = 0; h < cr * 2; h++) { + int dx = cr - w; + int dy = cr - h; + if ((dx*dx + dy*dy) <= (cr * cr)) { + SDL_RenderDrawPoint(renderer, cx + dx, cy + dy); + } + } + } + + SDL_RenderPresent(renderer); + } + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} +``` diff --git a/bounce/docs/chatgpt_dump/circle-rectangle-collision.md b/bounce/docs/chatgpt_dump/circle-rectangle-collision.md new file mode 100644 index 0000000..332d5b9 --- /dev/null +++ b/bounce/docs/chatgpt_dump/circle-rectangle-collision.md @@ -0,0 +1,171 @@ +Below is a clear explanation of **Circle vs Rectangle collision detection**, including **math formulas**, **edge-case handling**, and **C programming examples**. + +--- + +# 🟢 **Circle vs Axis-Aligned Rectangle Collision** + +We assume the rectangle is **axis-aligned (AABB)** and defined by: + +* Rectangle min corner: ((rx, ry)) +* Width (w), height (h) +* So max corner is ((rx+w,; ry+h)) + +Circle: + +* Center: ((cx, cy)) +* Radius (r) + +--- + +# ✅ **Math Formula (Shortest Distance Method)** + +The basic idea: + +1. **Clamp** the circle center to the edges of the rectangle +2. Compute the closest point **inside the rectangle** +3. Compute distance from the circle center to that point +4. If distance ≤ radius → collision + +### 1. Clamp function + +[ +\text{clamp}(v, min, max) = \max(min,;\min(v, max)) +] + +### 2. Closest point on rectangle to circle + +[ +\text{closestX} = \text{clamp}(cx,; rx,; rx+w) +] +[ +\text{closestY} = \text{clamp}(cy,; ry,; ry+h) +] + +### 3. Distance from circle to closest point + +[ +dx = cx - \text{closestX} +] +[ +dy = cy - \text{closestY} +] + +### 4. Collision rule + +[ +dx^2 + dy^2 \le r^2 +] + +No square root needed → fast. + +--- + +# 🟦 **C Code Example: Circle vs Rectangle Collision** + +```c +#include +#include + +float clamp(float value, float min, float max) { + if (value < min) return min; + if (value > max) return max; + return value; +} + +typedef struct { + float x, y; // top-left corner + float w, h; +} Rect; + +typedef struct { + float x, y; // center + float r; +} Circle; + +bool circleRectCollide(Circle c, Rect r) { + // Find the closest point on the rectangle to the circle + float closestX = clamp(c.x, r.x, r.x + r.w); + float closestY = clamp(c.y, r.y, r.y + r.h); + + // Compute distance from circle center to that point + float dx = c.x - closestX; + float dy = c.y - closestY; + + // If within radius, collision + return (dx * dx + dy * dy) <= (c.r * c.r); +} + +int main() { + Circle c = { 5.0f, 5.0f, 3.0f }; + Rect r = { 7.0f, 4.0f, 6.0f, 4.0f }; + + if (circleRectCollide(c, r)) { + printf("Collision!\n"); + } else { + printf("No collision.\n"); + } +} +``` + +--- + +# 🧪 **Explanation of the above example** + +Circle at (5,5), radius 3 +Rectangle at (7,4), size 6×4 + +The closest point on the rect to the circle is (7,5). +Distance = 2 → less than radius 3 → collision. + +--- + +# 🧠 Extra: Detecting Penetration Vector / Push-out Direction + +If you're making a game, you often need to **push the circle out** of the rectangle. + +Short version: + +1. Compute closest point +2. Compute distance +3. Compute penetration vector + +Example: + +```c +#include + +void circleRectPenetration(Circle c, Rect r, float *outX, float *outY) { + float closestX = clamp(c.x, r.x, r.x + r.w); + float closestY = clamp(c.y, r.y, r.y + r.h); + + float dx = c.x - closestX; + float dy = c.y - closestY; + + float distSq = dx*dx + dy*dy; + float dist = sqrtf(distSq); + + float penetration = c.r - dist; + + if (penetration > 0) { + *outX = (dx / dist) * penetration; + *outY = (dy / dist) * penetration; + } else { + *outX = *outY = 0.0f; + } +} +``` + +This gives the shortest vector needed to push the circle out of the rectangle. + +--- + +# Want more? + +I can also show: + +✔ **Circle vs rotated rectangle (OBB)** +✔ **Collision response (velocity reflection)** +✔ **Graphical ASCII demo** +✔ **SDL2 example with moving circle and rectangle** + +Just tell me! diff --git a/bounce/docs/chatgpt_dump/physics.md b/bounce/docs/chatgpt_dump/physics.md new file mode 100644 index 0000000..66e51d9 --- /dev/null +++ b/bounce/docs/chatgpt_dump/physics.md @@ -0,0 +1,227 @@ +Below is a **clear physics explanation**, the **math**, and **C programming examples** showing how to simulate a bouncing ball. + +--- + +# 🌟 **Physics of a Bouncing Ball** + +A bouncing ball is controlled by three main ideas: + +--- + +# 1️⃣ **Gravity Accelerates the Ball Downward** + +The ball’s vertical motion follows: + +[ +h(t) = h_0 - \tfrac{1}{2} g t^2 +] + +Velocity increases downward as: + +[ +v(t) = -gt +] + +Where: + +* (h) = height +* (g = 9.8\ \text{m/s}^2) +* (v) = velocity +* Downward is negative + +--- + +# 2️⃣ **Impact With the Ground** + +When the ball hits the ground, it changes direction. + +A real ball **does NOT return to full height**; some energy is lost. + +This is described using the **coefficient of restitution**, (e): + +[ +e = \frac{v_{\text{after}}}{v_{\text{before}}} +] + +Typical values: + +* Rubber ball: (e \approx 0.8 - 0.9) +* Basketball: (e \approx 0.75) +* Tennis ball: (e \approx 0.6) + +Meaning: + +* If velocity before impact is (-10\ \text{m/s}) +* With (e = 0.8), after impact: + [ + v_{\text{after}} = -e \cdot v_{\text{before}} = 8\ \text{m/s} + ] + +(The sign flips because the direction reverses.) + +--- + +# 3️⃣ **Each Bounce Gets Shorter** + +Because energy is lost each bounce, the maximum height shrinks: + +[ +h_{\text{after}} = e^2 \cdot h_{\text{before}} +] + +--- + +# ⭐ **MATHEMATICAL MODEL** + +Let: + +* (h) = current height +* (v) = velocity +* (dt) = time step + +Update each frame: + +### Gravity: + +[ +v = v - g \cdot dt +] + +### Position: + +[ +h = h + v \cdot dt +] + +### Bounce: + +If (h < 0): +[ +h = 0 +] +[ +v = -e \cdot v +] + +--- + +# 🖥️ **C Programming Example — Simple Bounce Simulation** + +This uses a time-step loop to simulate the ball in text mode. + +```c +#include + +int main() { + double h = 10.0; // initial height in meters + double v = 0.0; // initial velocity + double g = 9.8; // gravity + double e = 0.8; // coefficient of restitution + double dt = 0.01; // time step (seconds) + + for (int i = 0; i < 5000; i++) { + // apply gravity + v = v - g * dt; + + // update position + h = h + v * dt; + + // bounce + if (h < 0) { + h = 0; + v = -v * e; + } + + printf("time = %.2f s, height = %.3f m, velocity = %.3f m/s\n", + i * dt, h, v); + } + + return 0; +} +``` + +This produces output like: + +``` +time = 0.00 s, height = 9.999 m, velocity = -0.098 m/s +time = 0.01 s, height = 9.997 m, velocity = -0.196 m/s +... +Bounce! +... +``` + +A very realistic-looking bounce. + +--- + +# 🖥️ **C Example — Detecting Ground Collision Inside a Loop** + +```c +if (h <= 0) { + h = 0; + v = -v * e; // reverse and reduce velocity +} +``` + +This is the typical “bounce line” used in game loops. + +--- + +# 🖥️ **C Example — Continuous Simulation with ASCII Display** + +```c +#include +#include + +int main() { + double h = 20.0; + double v = 0.0; + double g = 9.8; + double e = 0.85; + double dt = 0.02; + + while (1) { + v = v - g * dt; + h = h + v * dt; + + if (h < 0) { + h = 0; + v = -v * e; + } + + int screen_height = 25; + int pos = (int)(screen_height - h); + + printf("\033[2J\033[1;1H"); // clear screen + for (int i = 0; i < pos; i++) printf("\n"); + printf(" o\n"); // the ball + fflush(stdout); + + usleep(20000); + } +} +``` + +This animates the bouncing ball in your terminal. + +--- + +# 🎯 Summary + +| Concept | Equation | +| --------------------------- | ------------------------------------------ | +| Gravity acceleration | (v = v - g \cdot dt) | +| Position update | (h = h + v \cdot dt) | +| Bounce | (v = -e v) | +| New max height after bounce | (h_{\text{after}} = e^2 h_{\text{before}}) | + +This combination gives a physically accurate bouncing ball. + +--- + +If you want, I can also show: + +* horizontal + vertical motion (parabolic bounce) +* collisions with walls +* 2D physics with vectors +* full game loop frameworks for SDL, Raylib, or OpenGL diff --git a/bounce/docs/formulas.adoc b/bounce/docs/formulas.adoc new file mode 100644 index 0000000..d106b7e --- /dev/null +++ b/bounce/docs/formulas.adoc @@ -0,0 +1,32 @@ +:stem: latexmath +:source-highlighter: pygments +:pygments-style: monokai +:wrap: normal + += Formulas + +* velocity downward + - stem:[v = g t] + +* height + - stem:[h = h_0 - \frac{1}{2}t^2] + +* how long any object takes to fall from a given height + - stem:[t = \sqrt{\frac{2h}{g}}] + +* restitution + - stem:[v_1 = e * v_0] + +* rising + - stem:[ v = e * v - g t ] + +* falling + - stem:[ v = g t ] + +NOTE: the coefficent of restitution (e) represents energy loss. + +Typical values for restitution: + +* Rubber ball: stem:[e \approx 0.8 - 0.9] +* Basketball: stem:[e \approx 0.75] +* Tennis ball: stem:[e \approx 0.6] diff --git a/bounce/docs/formulas.html b/bounce/docs/formulas.html new file mode 100644 index 0000000..30e9175 --- /dev/null +++ b/bounce/docs/formulas.html @@ -0,0 +1,564 @@ + + + + + + + +Formulas + + + + + +
+
+
    +
  • +

    velocity downward

    +
    +
      +
    • +

      \(v = g t\)

      +
    • +
    +
    +
  • +
  • +

    height

    +
    +
      +
    • +

      \(h = h_0 - \frac{1}{2}t^2\)

      +
    • +
    +
    +
  • +
  • +

    how long any object takes to fall from a given height

    +
    +
      +
    • +

      \(t = \sqrt{\frac{2h}{g}}\)

      +
    • +
    +
    +
  • +
  • +

    restitution

    +
    +
      +
    • +

      \(v_1 = e * v_0\)

      +
    • +
    +
    +
  • +
  • +

    rising

    +
    +
      +
    • +

      \( v = e * v - g t \)

      +
    • +
    +
    +
  • +
  • +

    falling

    +
    +
      +
    • +

      \( v = g t \)

      +
    • +
    +
    +
  • +
+
+
+ + + + + +
+
Note
+
+the coefficent of restitution (e) represents energy loss. +
+
+
+

Typical values for restitution:

+
+
+
    +
  • +

    Rubber ball: \(e \approx 0.8 - 0.9\)

    +
  • +
  • +

    Basketball: \(e \approx 0.75\)

    +
  • +
  • +

    Tennis ball: \(e \approx 0.6\)

    +
  • +
+
+
+ + + + + \ No newline at end of file diff --git a/collisions/Makefile b/collisions/Makefile new file mode 100644 index 0000000..0ddb263 --- /dev/null +++ b/collisions/Makefile @@ -0,0 +1,11 @@ +LIBS = -lSDL2 -lSDL2_ttf -lm + +PROG = balls + +build: $(PROG).c + gcc -g -o $(PROG) $(PROG).c $(LIBS) + +clean: + rm -rf $(PROG) + +.PHONY: clean diff --git a/collisions/balls b/collisions/balls new file mode 100755 index 0000000..2ed592f Binary files /dev/null and b/collisions/balls differ diff --git a/collisions/balls.c b/collisions/balls.c new file mode 100644 index 0000000..d4f1ed7 --- /dev/null +++ b/collisions/balls.c @@ -0,0 +1,622 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define METER_AS_PIXELS 3779U +#define BALL_COUNT 30 + +// (BALL_COUNT * (BALL_COUNT - 1)) / 2 +// #define BALL_DISTINCT_UNORDERED_PAIRS 435 + +#define END(check, str1, str2) \ + if (check) { \ + assert(check); \ + fprintf(stderr, "%s\n%s", str1, str2); \ + exit(1); \ + } \ + +#define SDL_main main + +typedef struct _Ball { + float px, py, vx, vy, ax, ay; + float radius; + uint8_t color; + float mass; +} Ball; + +typedef struct _Mouse { + SDL_Point p; + bool down; + uint8_t button; +} Mouse; + +typedef struct _Game { + SDL_Renderer *renderer; + SDL_Surface *backbuffer; + SDL_Window *window; + const SDL_Rect screen_rect; + Ball balls[BALL_COUNT]; + uint32_t fps; + float terminal_velocity; + uint8_t ball_size_min; + uint8_t ball_size_max; + Ball **balls_colliding; + uint8_t collision_count; +} Game; + + +typedef uint8_t (*Update_callback) (Game *game, + float seconds, + uint32_t milliseconds, + SDL_KeyCode key, + Mouse mouse, + bool keydown); + +enum {UPDATE_MAIN, UPDATE_NOTHING}; + +// Make sure last color is always black +enum {COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_ORANGE, COLOR_GREY, + COLOR_PURPLE, COLOR_NEON_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_WHITE, + COLOR_BLACK, COLOR_SIZE}; + +const SDL_Color colors[] = { + [COLOR_RED] = {.r = 217, .g = 100, .b = 89, .a = 255}, + [COLOR_GREEN] = {.r = 88, .g = 140, .b = 126, .a = 255}, + [COLOR_BLUE] = {.r = 39, .g = 211, .b = 245, .a = 255}, + [COLOR_ORANGE] = {.r = 242, .g = 174, .b = 114, .a = 255}, + [COLOR_GREY] = {.r = 89, .g = 89, .b = 89, .a = 89}, + [COLOR_YELLOW] = {.r = 230, .g = 240, .b = 0, .a = 255}, + [COLOR_PINK] = {.r = 245, .g = 39, .b = 108, .a = 255}, + [COLOR_NEON_GREEN] = {.r = 39, .g = 245, .b = 176, .a = 255}, + [COLOR_PURPLE] = {.r = 176, .g = 39, .b = 245, .a = 255}, + [COLOR_WHITE] = {.r = 255, .g = 255, .b = 255, .a = 255}, + [COLOR_BLACK] = {.r = 0, .g = 0, .b = 0, .a = 0}, +}; + +void +setColor(SDL_Renderer *renderer, + uint8_t color) +{ + + SDL_SetRenderDrawColor(renderer, colors[color].r, colors[color].g, + colors[color].b, colors[color].a); +} + +float getHyp(float x1, float y1, float x2, float y2) { + return sqrtf((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); +} + +bool ballCollide(Ball b1, + Ball b2) +{ + return fabs((b1.px - b2.px) * (b1.px - b2.px) + + (b1.py - b2.py) * (b1.py - b2.py)) <= + (b1.radius + b2.radius) * (b1.radius + b2.radius); +} + +bool pointInBall(Ball b, + SDL_Point p) +{ + return fabs((b.px - p.x) * (b.px - p.x) + (b.py - p.y) * (b.py - p.y)) + <= (b.radius * b.radius); +} + +void +drawCircle(SDL_Renderer *renderer, + SDL_Rect border, + float radius, + int px, + int py, + int w, + uint8_t color) +{ + + float x = 0; + float y = 0; + for(float i = 0; i < 2 * M_PI; i += 0.001f) + { + x = cosf(i); + y = sinf(i); + int rx = (x * radius); + int ry = (y * radius); + int tx = rx + px; + int ty = ry + py; + // wraping + // This works except in case where a ball is in the corner. Than the + // ball will apear in all four courners + // SDL_Rect point = { + // .x = (tx > border.w) ? tx -= border.w : (tx < 0) ? tx += border.w : tx, + // .y = (ty > border.h) ? ty -= border.h : (ty < 0) ? ty += border.h : ty, + // w, + // w + // }; + SDL_Rect point = { + .x = tx, + .y = ty, + w, + w + }; + setColor(renderer, color); + SDL_RenderFillRect(renderer, &point); + } +} + +void +drawBall(SDL_Renderer *renderer, + Ball ball) +// (x - h)^2 + (y - k)^2 = r^2 +// Drawing a circle with the standard circle formula. +// There may be a more performant way to do this... +{ + setColor(renderer, ball.color); + + // diameter is radius * 2 + for (int x = 0; x < ball.radius * 2; x++) { + for (int y = 0; y < ball.radius * 2; y++) { + if ( ((x - ball.radius) * (x - ball.radius)) + + ((y - ball.radius) * (y - ball.radius)) <= + ball.radius * ball.radius) { + int px = ball.px - ball.radius + x; + int py = ball.py - ball.radius + y; + + // TODO: wrap circle around screen + SDL_RenderDrawPoint(renderer, px, py); + + } + } + } +} + +void +drawBall2(SDL_Surface *surface, + Ball ball) +// (x - h)^2 + (y - k)^2 = r^2 +// Drawing a circle with the standard circle formula. +// There may be a more performant way to do this... +{ + // setColor(renderer, ball.color); + + // diameter is radius * 2 + for (int x = 0; x < ball.radius * 2; x++) { + for (int y = 0; y < ball.radius * 2; y++) { + if ( ((x - ball.radius) * (x - ball.radius)) + + ((y - ball.radius) * (y - ball.radius)) <= + ball.radius * ball.radius) { + int px = ball.px - ball.radius + x; + int py = ball.py - ball.radius + y; + + // TODO: wrap circle around screen + // SDL_RenderDrawPoint(renderer, px, py); + SDL_Rect r = {.x = px, .y = py, .w = 1, .h = 1}; + uint32_t color = + ((colors[ball.color].r << 24) | (colors[ball.color].g << 16) | + (colors[ball.color].b << 8) | colors[ball.color].a); + SDL_FillRect(surface, &r, color); + + } + } + } +} + +float clamp(float v, + float min, + float max) { + if (v < min) return min; + if (v > max) return max; + return v; +} + +bool circleRectCollide(SDL_Point circle, + float r, + SDL_Rect rect) +{ + float closestX = clamp(circle.x, rect.x, rect.x + rect.w); + float closestY = clamp(circle.y, rect.y, rect.y + rect.h); + + // this gets the distance form the center of the circle to a side of a + // rectangle. It could be negative or positive. Because we are squaring them + // it doesn't matter. + float dx = circle.x - closestX; + float dy = circle.y - closestY; + + // use the dot product to determine collision + return (dx*dx + dy*dy) <= (r*r); +} + + +static uint8_t +updateNothing(Game *game, + float seconds, + uint32_t milliseconds, + SDL_KeyCode key, + Mouse mouse, + bool keydown) +{ + return UPDATE_NOTHING; +} + +void +worldBoundry(Game *game, Ball *ball) +{ + int padding = 1; + + if (ball->px - ball->radius <= 0) { + ball->px = ball->radius + padding; + } + + if (ball->py - ball->radius <= 0) { + ball->py = ball->radius + padding; + } + + if (ball->px + ball->radius >= game->screen_rect.w) { + ball->px = game->screen_rect.w - ball->radius - padding; + } + + if (ball->py + ball->radius >= game->screen_rect.h) { + ball->py = game->screen_rect.h - ball->radius - padding; + } +} + +void +drawCursor(SDL_Renderer *renderer, SDL_Point p) { + SDL_Rect r = { + .x = p.x - 5, + .y = p.y - 5, + .w = 15, + .h = 15 + }; + setColor(renderer, COLOR_WHITE); + SDL_RenderFillRect(renderer, &r); +} + +static uint8_t +updateMain(Game *game, + float seconds, + uint32_t milliseconds, + SDL_KeyCode key, + Mouse mouse, + bool keydown) +{ + static float elapsedTime = 0; + static int selected = -1; + + // Ball *colliding[BALL_DISTINCT_UNORDERED_PAIRS] = {0}; + game->balls_colliding = NULL; + game->collision_count = 0; + + + // game->ball_distinct_unordered_pairs is the total number of possible + // collisions, it is multiplied was two because there needed to be two values + // for each collision, a ball at the target ball + + // TODO + // Add a middle click feature that lets you look around. Then you could see + // balls that went off screen + + // NOTE: + // issues arise when mouse movement is too fast + // don't check mouse click more than needed + if(mouse.button == SDL_BUTTON_LEFT && (selected < 0)) { + for (int i = 0; i < BALL_COUNT; ++i) { + Ball *b = &game->balls[i]; + if (pointInBall(*b, mouse.p)) selected = i; + } + } + + if((!mouse.down) && (mouse.button == SDL_BUTTON_RIGHT) && (selected >= 0)) { + Ball *b = &game->balls[selected]; + b->vx = 5.0f * (b->px - (float)mouse.p.x); + b->vy = 5.0f * (b->py - (float)mouse.p.y); + } + + if (!mouse.down) selected = -1; + + if(selected >= 0 && mouse.button != SDL_BUTTON_RIGHT) { + elapsedTime = 0; + Ball *b = &game->balls[selected]; + b->px = mouse.p.x; + b->py = mouse.p.y; + } + + for (int i = 0; i < BALL_COUNT; ++i) { + Ball *b1 = &game->balls[i]; + // drag + b1->ax = -b1->vx * 0.8f; + b1->ay = -b1->vy * 0.8f; + + b1->vx += b1->ax * elapsedTime; + b1->vy += b1->ay * elapsedTime; + b1->px += b1->vx * elapsedTime; + b1->py += b1->vy * elapsedTime; + + // wrap around screen + // Looks really bad, I'm removing this + // if (b1->px < 0) b1->px += (float)game->screen_rect.w; + // if (b1->px >= game->screen_rect.w) b1->px -= (float)game->screen_rect.w; + // if (b1->py < 0) b1->py += (float)game->screen_rect.h; + // if (b1->py >= game->screen_rect.h) b1->py -= (float)game->screen_rect.h; + + if (fabs(b1->vx * b1->vx + b1->vy * b1->vy) < 0.01f) { + b1->vx = 0; + b1->vy = 0; + } + + } + + setColor(game->renderer, COLOR_BLACK); + SDL_RenderClear(game->renderer); + + for (int i = 0; i < BALL_COUNT; ++i) { + Ball *b1 = &game->balls[i]; + if (i == selected) drawBall(game->renderer, *b1); + else drawCircle(game->renderer, game->screen_rect, b1->radius, b1->px, + b1->py, 2, b1->color); + + } + + // TODO + // set up a list of values that will not conflict for i and j in gameInit + + // TODO + // there is an error were the collision_count varaible will not reset to + // zero + for (int i = 0; i < BALL_COUNT; ++i) { + Ball *b1 = &game->balls[i]; + + for (int j = i + 1; j < BALL_COUNT; ++j) { + Ball *b2 = &game->balls[j]; + + if (!ballCollide(*b1, *b2)) continue; + game->balls_colliding = calloc(sizeof(Ball *), + (game->collision_count + 2)); + END((!game->balls_colliding), "calloc()", "could not allocate game->balls_colliding()" ); + game->balls_colliding[game->collision_count++] = b1; + game->balls_colliding[game->collision_count++] = b2; + float distance = fabs(getHyp(b1->px, b1->py, b2->px, b2->py)); + + float overlap = (distance - b1->radius - b2->radius); + + if (distance == 0) continue; + + b1->px -= overlap * (b1->px - b2->px) / distance; + b1->py -= overlap * (b1->py - b2->py) / distance; + b2->px += overlap * (b1->px - b2->px) / distance; + b2->py += overlap * (b1->py - b2->py) / distance; + // } + } + } + // printf("collision count: %d\n", game->collision_count); + for (int i = 0; i < game->collision_count; ++i) { + Ball *b1 = game->balls_colliding[i]; + Ball *b2 = game->balls_colliding[i + 1]; + if (!b1 || !b2) continue; + float distance = getHyp(b1->px, b1->py, b2->px, b2->py); + + // normal + float nx = (b2->px - b1->px) / distance; + float ny = (b2->py - b1->py) / distance; + + // tangent + float tx = -ny; + float ty = nx; + + float dpTan1 = b1->vx * tx + b1->vy * ty; + float dpTan2 = b2->vx * tx + b2->vy * ty; + + float dpNorm1 = b1->vx * nx + b1->vy * ny; + float dpNorm2 = b2->vx * nx + b2->vy * ny; + + float m1 = + (dpNorm1 * (b1->mass - b2->mass) + 2.0f * b2->mass * dpNorm2) + / (b1->mass + b2->mass); + + float m2 = + (dpNorm2 * (b2->mass - b1->mass) + 2.0f * b1->mass * dpNorm1) + / (b1->mass + b2->mass); + + b1->vx = tx * dpTan1 + nx * m1; + b1->vy = ty * dpTan1 + ny * m1; + b2->vx = tx * dpTan2 + nx * m2; + b2->vy = ty * dpTan2 + ny * m2; + + } + + if((mouse.down) && (mouse.button == SDL_BUTTON_RIGHT) && (selected >= 0)) { + Ball *b = &game->balls[selected]; + setColor(game->renderer, COLOR_WHITE); + SDL_RenderDrawLine(game->renderer, b->px, b->py, mouse.p.x, mouse.p.y); + } + if (selected < 0) drawCursor(game->renderer, mouse.p); + + if (game->collision_count > 0) { + // I do not have free each individual ball in ball colliding because + // they are all on the stack + free(game->balls_colliding); + game->balls_colliding = NULL; + } + + elapsedTime += 0.0008f; + + return UPDATE_MAIN; +} + +void +Game_Update(Game *game) +// The main game loop. Sets up which callback will be used in the function loop. +// Each update callback determines what update callback will be called next by +// returning the appropriate enum value +{ + uint8_t update_id = 0; + uint64_t frame = 0; + bool quit = false; + bool keydown = false; + float mspf = (1.0f / (float)game->fps) * 1000.0f; + SDL_Event event; + SDL_KeyCode key = 0; + Update_callback update; + uint32_t ticks_start = SDL_GetTicks(); + Mouse mouse; + + // TODO + // add backbuffer + + while (!quit) { + + // Place update functions here + switch (update_id) { + case UPDATE_MAIN: update = updateMain; break; + case UPDATE_NOTHING: update = updateNothing; break; + } + + while (SDL_PollEvent(&event)) { + + switch (event.type) { + case SDL_KEYDOWN: { + if (event.key.repeat == 0) { + key = event.key.keysym.sym; + keydown = true; + } + + break; + } + case SDL_MOUSEBUTTONDOWN: { + mouse.button = event.button.button; + mouse.down = true; + break; + } + case SDL_MOUSEBUTTONUP: { + mouse.button = event.button.button; + mouse.down = false; + break; + } + case SDL_MOUSEMOTION: { + mouse.p.x = event.motion.x; + mouse.p.y = event.motion.y; + break; + } + + case SDL_KEYUP: keydown = false; break; + case SDL_QUIT: quit = true; break; + } + } + + update_id = update(game, SDL_GetTicks(), frame, key, mouse,keydown); + + if ((SDL_GetTicks() - ticks_start) >= mspf) { + ticks_start = SDL_GetTicks(); + // SDL_SetRenderDrawBlendMode(game->renderer, SDL_BLENDMODE_NONE); + // SDL_Texture *texture = + // SDL_CreateTextureFromSurface(game->renderer, game->backbuffer); + // SDL_RenderCopyEx(game->renderer, texture, NULL, &game->screen_rect, + // 0, NULL, SDL_FLIP_NONE); + SDL_RenderPresent(game->renderer); + // SDL_DestroyTexture(texture); + } + + frame++; + } +} + +void createBalls(Game *game) { + for (int i = 0; i < BALL_COUNT; ++i) { + Ball *b = &game->balls[i]; + b->vx = 0; + b->vy = 0; + b->ax = 0; + b->ay = 0; + b->radius = ((rand() / (float)RAND_MAX) * (game->ball_size_max - + game->ball_size_min)) + game->ball_size_min; + + b->px = (rand() / (float)RAND_MAX) * (float)game->screen_rect.w; + + b->py = (rand() / (float)RAND_MAX) * (float)game->screen_rect.h; + + b->color = (rand() / (float)RAND_MAX) * ((float)COLOR_SIZE - 2); + + b->mass = b->radius * 10; + } + +} + +Game * +Game_Init() +// All the variable and data initialization needed for SDL and perhaps game +// variables +{ + static Game game = { + .screen_rect = {.x = 0, .y = 0, .w = 800, .h = 800}, + .fps = 60, + .ball_size_min = 15, + .ball_size_max = 50, + .backbuffer = NULL, + }; + + END(SDL_Init(SDL_INIT_VIDEO) != 0, "Could not create texture", + SDL_GetError()); + + END(TTF_Init() != 0, "Could not initialize TTF", TTF_GetError()); + + + + game.window = SDL_CreateWindow("balls", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, game.screen_rect.w, + game.screen_rect.h, SDL_WINDOW_SHOWN); + + END(game.window == NULL, "Could not create window", SDL_GetError()); + + game.renderer = + SDL_CreateRenderer(game.window, 0, SDL_RENDERER_ACCELERATED); + + END(game.renderer == NULL, "Could not create renderer", SDL_GetError()); + + SDL_SetRelativeMouseMode(true); + + // setColor(game->backbuffer, COLOR_BLACK); + // SDL_RenderClear(game->renderer); + game.backbuffer = + SDL_CreateRGBSurface(0, game.screen_rect.w, game.screen_rect.h, + 32, 0xFF000000, 0x00FF0000, 0x0000FF00, + 0x000000FF); + // fill backbuffer with black + SDL_FillRect(game.backbuffer, &game.screen_rect, 0x00000000); + + // print system information + printf("big ending = %s\n", SDL_BYTEORDER == SDL_BIG_ENDIAN ? + "true": "False"); + + srand(time(NULL)); + createBalls(&game); + + return &game; +} + +void +Game_Quit(Game *game) +{ + if (game->collision_count > 0) { + free(game->balls_colliding); + game->balls_colliding = NULL; + } + SDL_DestroyWindow(game->window); + SDL_DestroyRenderer(game->renderer); + SDL_FreeSurface(game->backbuffer); + TTF_Quit(); + SDL_Quit(); +} + +int +main(void) +{ + Game *game = Game_Init(); + Game_Update(game); + Game_Quit(game); + return 0; +} diff --git a/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes.adoc b/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes.adoc new file mode 100644 index 0000000..2f303ba --- /dev/null +++ b/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes.adoc @@ -0,0 +1,192 @@ += Why Your Collision-Resolution Loop Freezes and How to Fix It +:wrap: normal + +[source,c] +---- +for (int i = 0; i < BALL_COUNT; ++i) { + Ball *b1 = &game->balls[i]; + + for (int j = i + 1; j < BALL_COUNT; ++j) { + Ball *b2 = &game->balls[j]; + + if (!ballCollide(*b1, *b2)) continue; + colliding = calloc(sizeof(Ball *), (collision_count + 2)); + END((!colliding), "calloc()", "could not allocate colliding()" ); + colliding[collision_count++] = b1; + colliding[collision_count++] = b2; + float distance = getHyp(b1->px, b1->py, b2->px, b2->py); + + float overlap = 0.5f * (distance - (float)b1->radius - + (float)b2->radius); + b1->px -= + overlap * (float)(b1->px - b2->px) / distance; + b1->py -= + overlap * (float)(b1->py - b2->py) / distance; + b2->px += + overlap * (float)(b1->px - b2->px) / distance; + b2->py += + overlap * (float)(b1->py - b2->py) / distance; + + // all collisions must be resolved + // TODO + // fix issue that freezes program from this loop + while(ballCollide(*b1, *b2)) { + b1->px -= (float)(b1->px - b2->px) / distance; + b1->py -= (float)(b1->py - b2->py) / distance; + b2->px += (float)(b1->px - b2->px) / distance; + b2->py += (float)(b1->py - b2->py) / distance; + } + } +} +---- + +Your code contains the classic problem of **over-correcting** and then +**rechecking using new positions**, which makes the loop never terminate. +Let’s walk through why this happens and how to fix it. + + +== 1. The critical problem: using updated positions inside the loop + +Inside your main collision-resolution: + +[source,c] +---- +b1->px -= overlap * (b1->px - b2->px) / distance; +b1->py -= overlap * (b1->py - b2->py) / distance; +b2->px += overlap * (b1->px - b2->px) / distance; +b2->py += overlap * (b1->py - b2->py) / distance; +---- + +This is correct. + +But then you do: + +[source,c] +---- +while (ballCollide(*b1, *b2)) { + b1->px -= (b1->px - b2->px) / distance; + b1->py -= (b1->py - b2->py) / distance; + b2->px += (b1->px - b2->px) / distance; + b2->py += (b1->py - b2->py) / distance; +} +---- + +== ❌ Why this freezes + +1. `distance` stays the **old** value (before modifying positions). +2. But `(b1->px - b2->px)` is updated each iteration. +3. This causes steps that: + * overshoot + * oscillate + * sometimes increase overlap instead of decreasing it +4. So `ballCollide()` keeps returning true forever → **infinite loop**. + +This is a well-known bug in naive iterative separation. + +== 2. Mathematical explanation + +Your update step behaves like: + + x ← x − (x / constant) + +This only converges to zero if: + + |1 − 1/constant| < 1 + +But because the denominator (`distance`) is incorrect after the first +shift, the term can jump back and forth. + +In other words: + +You are using a **fixed-length correction step based on old geometry**, +but applying it to **new geometry**. + +This makes the correction unstable. + + +== 3. Proper fix: recompute distance each iteration OR limit iterations + +Method A — Recompute distance every pass: + +[source,c] +---- +int loop_guard = 0; +while (ballCollide(*b1, *b2) && loop_guard++ < 4) { + float dx = b1->px - b2->px; + float dy = b1->py - b2->py; + float d = sqrtf(dx*dx + dy*dy); + + if (d == 0) break; + + float overlap = 0.5f * (d - b1->radius - b2->radius); + float nx = dx / d; + float ny = dy / d; + + b1->px -= overlap * nx; + b1->py -= overlap * ny; + b2->px += overlap * nx; + b2->py += overlap * ny; +} +---- + +Why this works: + +* `dx`, `dy`, and `distance` are recalculated from the new positions. +* The correction scales perfectly with the current geometry. +* The loop is guaranteed to converge. + +Method B — Avoid loops entirely +Just do **one correction step per frame**, which is how most engines and +the Jonathan Blow code handle it. + +== 4. Even better fix: use Blow’s displacement formula directly + +His code displaces balls only **once** per collision pass: + +[source,c] +---- +float fDistance = sqrtf(dx*dx + dy*dy); +float fOverlap = 0.5f * (fDistance - r1 - r2); + +b1->px -= fOverlap * (dx / fDistance); +b1->py -= fOverlap * (dy / fDistance); + +b2->px += fOverlap * (dx / fDistance); +b2->py += fOverlap * (dy / fDistance); +---- + +This resolves overlap in **one stable pass**, so you don’t need a loop. + +== 5. Bonus: Your calloc() usage is also wrong + +You do: + +[source,c] +---- +colliding = calloc(sizeof(Ball *), (collision_count + 2)); +---- + +Correct `calloc` order is: + + calloc(num_elements, element_size) + +So you should use: + +[source,c] +---- +colliding = calloc(collision_count + 2, sizeof(Ball *)); +---- + +== 6. Final Summary + +* The infinite loop comes from using **outdated distance values** while + updating positions. +* This causes oscillation → `ballCollide()` never returns false. +* Fixes: + - recompute distance each iteration, or + - apply the correct one-shot overlap resolution (recommended). +* Also fix your `calloc` order. + +If you want, I can rewrite your entire collision loop into a clean, +correct, minimal version similar to Jonathan Blow’s system. + diff --git a/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes.html b/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes.html new file mode 100644 index 0000000..371d920 --- /dev/null +++ b/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes.html @@ -0,0 +1,738 @@ + + + + + + + +Why Your Collision-Resolution Loop Freezes and How to Fix It + + + + + +
+
+
+
+
+
for (int i = 0; i < BALL_COUNT; ++i) {
+    Ball *b1 = &game->balls[i];
+
+    for (int j = i + 1; j < BALL_COUNT; ++j) {
+        Ball *b2 = &game->balls[j];
+
+        if (!ballCollide(*b1, *b2)) continue;
+            colliding = calloc(sizeof(Ball *), (collision_count + 2));
+            END((!colliding), "calloc()", "could not allocate colliding()" );
+            colliding[collision_count++] = b1;
+            colliding[collision_count++] = b2;
+            float distance = getHyp(b1->px, b1->py, b2->px, b2->py);
+
+            float overlap = 0.5f * (distance - (float)b1->radius -
+                            (float)b2->radius);
+            b1->px -=
+                overlap * (float)(b1->px - b2->px) / distance;
+            b1->py -=
+                overlap * (float)(b1->py - b2->py) / distance;
+            b2->px +=
+                overlap * (float)(b1->px - b2->px) / distance;
+            b2->py +=
+                overlap * (float)(b1->py - b2->py) / distance;
+
+        // all collisions must be resolved
+        // TODO
+        // fix issue that freezes program from this loop
+        while(ballCollide(*b1, *b2)) {
+            b1->px -= (float)(b1->px - b2->px) / distance;
+            b1->py -= (float)(b1->py - b2->py) / distance;
+            b2->px += (float)(b1->px - b2->px) / distance;
+            b2->py += (float)(b1->py - b2->py) / distance;
+        }
+    }
+}
+
+
+
+

Your code contains the classic problem of over-correcting and then +rechecking using new positions, which makes the loop never terminate. +Let’s walk through why this happens and how to fix it.

+
+
+
+
+

1. The critical problem: using updated positions inside the loop

+
+
+

Inside your main collision-resolution:

+
+
+
+
b1->px -= overlap * (b1->px - b2->px) / distance;
+b1->py -= overlap * (b1->py - b2->py) / distance;
+b2->px += overlap * (b1->px - b2->px) / distance;
+b2->py += overlap * (b1->py - b2->py) / distance;
+
+
+
+

This is correct.

+
+
+

But then you do:

+
+
+
+
while (ballCollide(*b1, *b2)) {
+    b1->px -= (b1->px - b2->px) / distance;
+    b1->py -= (b1->py - b2->py) / distance;
+    b2->px += (b1->px - b2->px) / distance;
+    b2->py += (b1->py - b2->py) / distance;
+}
+
+
+
+
+
+

❌ Why this freezes

+
+
+
    +
  1. +

    distance stays the old value (before modifying positions).

    +
  2. +
  3. +

    But (b1→px - b2→px) is updated each iteration.

    +
  4. +
  5. +

    This causes steps that:

    +
    +
      +
    • +

      overshoot

      +
    • +
    • +

      oscillate

      +
    • +
    • +

      sometimes increase overlap instead of decreasing it

      +
    • +
    +
    +
  6. +
  7. +

    So ballCollide() keeps returning true forever → infinite loop.

    +
  8. +
+
+
+

This is a well-known bug in naive iterative separation.

+
+
+
+
+

2. Mathematical explanation

+
+
+

Your update step behaves like:

+
+
+
+
x ← x − (x / constant)
+
+
+
+

This only converges to zero if:

+
+
+
+
|1 − 1/constant| < 1
+
+
+
+

But because the denominator (distance) is incorrect after the first +shift, the term can jump back and forth.

+
+
+

In other words:

+
+
+

You are using a fixed-length correction step based on old geometry, +but applying it to new geometry.

+
+
+

This makes the correction unstable.

+
+
+
+
+

3. Proper fix: recompute distance each iteration OR limit iterations

+
+
+

Method A — Recompute distance every pass:

+
+
+
+
int loop_guard = 0;
+while (ballCollide(*b1, *b2) && loop_guard++ < 4) {
+    float dx = b1->px - b2->px;
+    float dy = b1->py - b2->py;
+    float d  = sqrtf(dx*dx + dy*dy);
+
+    if (d == 0) break;
+
+    float overlap = 0.5f * (d - b1->radius - b2->radius);
+    float nx = dx / d;
+    float ny = dy / d;
+
+    b1->px -= overlap * nx;
+    b1->py -= overlap * ny;
+    b2->px += overlap * nx;
+    b2->py += overlap * ny;
+}
+
+
+
+

Why this works:

+
+
+
    +
  • +

    dx, dy, and distance are recalculated from the new positions.

    +
  • +
  • +

    The correction scales perfectly with the current geometry.

    +
  • +
  • +

    The loop is guaranteed to converge.

    +
  • +
+
+
+

Method B — Avoid loops entirely +Just do one correction step per frame, which is how most engines and +the Jonathan Blow code handle it.

+
+
+
+
+

4. Even better fix: use Blow’s displacement formula directly

+
+
+

His code displaces balls only once per collision pass:

+
+
+
+
float fDistance = sqrtf(dx*dx + dy*dy);
+float fOverlap = 0.5f * (fDistance - r1 - r2);
+
+b1->px -= fOverlap * (dx / fDistance);
+b1->py -= fOverlap * (dy / fDistance);
+
+b2->px += fOverlap * (dx / fDistance);
+b2->py += fOverlap * (dy / fDistance);
+
+
+
+

This resolves overlap in one stable pass, so you don’t need a loop.

+
+
+
+
+

5. Bonus: Your calloc() usage is also wrong

+
+
+

You do:

+
+
+
+
colliding = calloc(sizeof(Ball *), (collision_count + 2));
+
+
+
+

Correct calloc order is:

+
+
+
+
calloc(num_elements, element_size)
+
+
+
+

So you should use:

+
+
+
+
colliding = calloc(collision_count + 2, sizeof(Ball *));
+
+
+
+
+
+

6. Final Summary

+
+
+
    +
  • +

    The infinite loop comes from using outdated distance values while +updating positions.

    +
  • +
  • +

    This causes oscillation → ballCollide() never returns false.

    +
  • +
  • +

    Fixes:

    +
    +
      +
    • +

      recompute distance each iteration, or

      +
    • +
    • +

      apply the correct one-shot overlap resolution (recommended).

      +
    • +
    +
    +
  • +
  • +

    Also fix your calloc order.

    +
  • +
+
+
+

If you want, I can rewrite your entire collision loop into a clean, +correct, minimal version similar to Jonathan Blow’s system.

+
+
+
+
+ + + \ No newline at end of file diff --git a/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes2.adoc b/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes2.adoc new file mode 100644 index 0000000..4c2fcdb --- /dev/null +++ b/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes2.adoc @@ -0,0 +1,95 @@ += Why Recomputing Distance Still Freezes the Loop +Even when recomputing `distance` inside the loop, it can still freeze. This +happens when: + +* The distance between ball centers becomes **zero or extremely close to zero**. +* The overlap becomes **very tiny** but never fully resolves because floating- + point error prevents the condition from becoming false. +* The collision logic attempts to normalize a zero-length vector. + +Below is the real reason and the correct fix. + +== The Real Crash Scenario: Distance == 0 +If two balls overlap **perfectly**, meaning: + +---- +b1->px == b2->px +b1->py == b2->py +---- + +Then: + +---- +dx = 0 +dy = 0 +distance = sqrt(0 + 0) = 0 +---- + +Normalizing `(dx, dy)` becomes: + +---- +nx = dx / distance --> division by zero +---- + +This produces `NaN`, and once NaNs enter your coordinates, your loop **never +terminates**, because every test becomes nonsense. + +== Why This Happens +Your simulation allows balls to be pushed so close together (or initialized too +close) that they fully overlap before you start resolving. + +Your while-loop approach *cannot* recover when distance reaches 0. + +== The Fix: Replace the Loop With One Guaranteed Separation Step +Physics engines never use repeated loops; they use one mathematically correct +position correction. + +Here is the correct safe displacement code: + +[source,c] +---- +float dx = b1->px - b2->px; +float dy = b1->py - b2->py; +float d2 = dx*dx + dy*dy; + +// If centers are identical, push them apart arbitrarily +if (d2 == 0.0f) { + dx = 1.0f; + dy = 0.0f; + d2 = 1.0f; +} + +float distance = sqrtf(d2); +float overlap = (b1->radius + b2->radius) - distance; + +if (overlap > 0.0f) { + float nx = dx / distance; + float ny = dy / distance; + + float half = overlap * 0.5f; + + b1->px += nx * half; + b1->py += ny * half; + + b2->px -= nx * half; + b2->py -= ny * half; +} +---- + +== Why This Works +* **No loop**, so it can’t freeze. +* Handles the `distance == 0` case safely. +* Fully resolves the overlap mathematically. +* Guaranteed to stop balls from colliding in a single step. +* `collision_count` will now drop to zero. + +== Summary +Recomputing distance didn't fix the freeze because the loop becomes invalid +whenever the distance is zero or extremely tiny. +The correct solution is to completely remove the loop and use one safe, +mathematically correct separation step. + +== Want the Full Fixed Version? +If you want, I can rewrite your entire collision detection and resolution block +into clean, safe, minimal C code. + diff --git a/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes2.html b/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes2.html new file mode 100644 index 0000000..bdd17be --- /dev/null +++ b/collisions/docs/chatgpt_dump/Collision-Resolution-Loop-Freezes2.html @@ -0,0 +1,609 @@ + + + + + + + + +Why Recomputing Distance Still Freezes the Loop + + + + + +
+
+
+
+
    +
  • +

    The distance between ball centers becomes zero or extremely close to zero.

    +
  • +
  • +

    The overlap becomes very tiny but never fully resolves because floating- +point error prevents the condition from becoming false.

    +
  • +
  • +

    The collision logic attempts to normalize a zero-length vector.

    +
  • +
+
+
+

Below is the real reason and the correct fix.

+
+
+
+
+

The Real Crash Scenario: Distance == 0

+
+
+

If two balls overlap perfectly, meaning:

+
+
+
+
b1->px == b2->px
+b1->py == b2->py
+
+
+
+

Then:

+
+
+
+
dx = 0
+dy = 0
+distance = sqrt(0 + 0) = 0
+
+
+
+

Normalizing (dx, dy) becomes:

+
+
+
+
nx = dx / distance  --> division by zero
+
+
+
+

This produces NaN, and once NaNs enter your coordinates, your loop never +terminates, because every test becomes nonsense.

+
+
+
+
+

Why This Happens

+
+
+

Your simulation allows balls to be pushed so close together (or initialized too +close) that they fully overlap before you start resolving.

+
+
+

Your while-loop approach cannot recover when distance reaches 0.

+
+
+
+
+

The Fix: Replace the Loop With One Guaranteed Separation Step

+
+
+

Physics engines never use repeated loops; they use one mathematically correct +position correction.

+
+
+

Here is the correct safe displacement code:

+
+
+
+
float dx = b1->px - b2->px;
+float dy = b1->py - b2->py;
+float d2 = dx*dx + dy*dy;
+
+// If centers are identical, push them apart arbitrarily
+if (d2 == 0.0f) {
+    dx = 1.0f;
+    dy = 0.0f;
+    d2 = 1.0f;
+}
+
+float distance = sqrtf(d2);
+float overlap  = (b1->radius + b2->radius) - distance;
+
+if (overlap > 0.0f) {
+    float nx = dx / distance;
+    float ny = dy / distance;
+
+    float half = overlap * 0.5f;
+
+    b1->px += nx * half;
+    b1->py += ny * half;
+
+    b2->px -= nx * half;
+    b2->py -= ny * half;
+}
+
+
+
+
+
+

Why This Works

+
+
+
    +
  • +

    No loop, so it can’t freeze.

    +
  • +
  • +

    Handles the distance == 0 case safely.

    +
  • +
  • +

    Fully resolves the overlap mathematically.

    +
  • +
  • +

    Guaranteed to stop balls from colliding in a single step.

    +
  • +
  • +

    collision_count will now drop to zero.

    +
  • +
+
+
+
+
+

Summary

+
+
+

Recomputing distance didn’t fix the freeze because the loop becomes invalid +whenever the distance is zero or extremely tiny. +The correct solution is to completely remove the loop and use one safe, +mathematically correct separation step.

+
+
+
+
+

Want the Full Fixed Version?

+
+
+

If you want, I can rewrite your entire collision detection and resolution block +into clean, safe, minimal C code.

+
+
+
+
+ + + \ No newline at end of file diff --git a/collisions/docs/formulas.adoc b/collisions/docs/formulas.adoc new file mode 100644 index 0000000..91e3ad5 --- /dev/null +++ b/collisions/docs/formulas.adoc @@ -0,0 +1,126 @@ +:stem: latexmath +:source-highlighter: pygments +:pygments-style: monokai +:wrap: normal + += Formulas + +* Dot Product + +[stem] +++++ +\overrightarrow{a} \cdot \overrightarrow{b} \\ +\left[ \begin{array}{c} +a_{1} \\ +a_{2} \\ +\vdots \\ +a_{n} +\end{array} \right] + +\cdot + +\left[ \begin{array}{c} +b_{1} \\ +b_{2} \\ +\vdots \\ +b_{n} +\end{array} \right] += +a_{1} * b_{1} + a_{2} * b_{2} + * a_{n} * b_{n} +++++ + +* Circle Overlapping + +[stem] +++++ +|\sqrt{a^2 + b^2}| <= r_1 + r_2 +++++ + +NOTE: a and a is the opposite and adjacent sides found from + the centers of both circles + + +* Pythagrean Thereom to find distancce between balls centers + +[stem] +++++ +a^2 + b^2 = c^2 +++++ + +* Vector Normalization + +[stem] +++++ +\hat{u} = \frac{\overrightarrow{v}}{|\overrightarrow{v}|} +++++ + + +* Magnitude of a Vector + +[stem] +++++ +|\overrightarrow{a}| = \sqrt{a_{1}^2 + a_{2}^2 + \dots a_{n}^2} +++++ + +* Finding tangent vector + +[stem] +++++ +x = -y, y = x +++++ + +* 1D Conservation of Momentum + +[stem] +++++ +m_1 v_1 + m_2 v_2 = m_1 v_1 + m_2 v_2 +++++ + +* 1D Elastic Collision + +[stem] +++++ +v_1' = (v_1 * (m_1-m_2) + 2 * m_2 * v_2)) / (m_1 + m_2) \\ +v_2' = (v_2 * (m_2-m_1) + 2 * m_1 * v_1)) / (m_1 + m_2) +++++ + +* Finding magnitude in a specific direction + +[stem] +++++ +|\overrightarrow{v}| = \overrightarrow{n} \cdot \overrightarrow{v} +++++ + +* Distinct unordered pairs + +[stem] +++++ +(n,2) = \frac{n(n-1)}{2}] +++++ + + +== Seeing if circle collide +Instead of doing this: + +[source,c] +---- +sqrtf((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)) <= (r1 + r2); +---- + +We can save some computing speed by removing the square root and just +multiplying the left side by itself: + +[source,c] +---- +fabs((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)) <= (r1 + r2)*(r1 + r2); +---- + +== Clamping Velocity + +We can use the dot product to find the velocity in specific direction and then +clamp it + +[source,c] +---- +if (fabs(ball.vx*ball.vx + ball.vy*ball.vy) < 0.01f) +---- diff --git a/collisions/docs/formulas.html b/collisions/docs/formulas.html new file mode 100644 index 0000000..efb1004 --- /dev/null +++ b/collisions/docs/formulas.html @@ -0,0 +1,745 @@ + + + + + + + +Formulas + + + + + + +
+
+
+
+
    +
  • +

    Dot Product

    +
  • +
+
+
+
+\[\overrightarrow{a} \cdot \overrightarrow{b} \\ +\left[ \begin{array}{c} +a_{1} \\ +a_{2} \\ +\vdots \\ +a_{n} +\end{array} \right] + +\cdot + +\left[ \begin{array}{c} +b_{1} \\ +b_{2} \\ +\vdots \\ +b_{n} +\end{array} \right] += +a_{1} * b_{1} + a_{2} * b_{2} + * a_{n} * b_{n}\] +
+
+
+
    +
  • +

    Circle Overlapping

    +
  • +
+
+
+
+\[|\sqrt{a^2 + b^2}| <= r_1 + r_2\] +
+
+
+ + + + + +
+
Note
+
+a and a is the opposite and adjacent sides found from + the centers of both circles +
+
+
+
    +
  • +

    Pythagrean Thereom to find distancce between balls centers

    +
  • +
+
+
+
+\[a^2 + b^2 = c^2\] +
+
+
+
    +
  • +

    Vector Normalization

    +
  • +
+
+
+
+\[\hat{u} = \frac{\overrightarrow{v}}{|\overrightarrow{v}|}\] +
+
+
+
    +
  • +

    Magnitude of a Vector

    +
  • +
+
+
+
+\[|\overrightarrow{a}| = \sqrt{a_{1}^2 + a_{2}^2 + \dots a_{n}^2}\] +
+
+
+
    +
  • +

    Finding tangent vector

    +
  • +
+
+
+
+\[x = -y, y = x\] +
+
+
+
    +
  • +

    1D Conservation of Momentum

    +
  • +
+
+
+
+\[m_1 v_1 + m_2 v_2 = m_1 v_1 + m_2 v_2\] +
+
+
+
    +
  • +

    1D Elastic Collision

    +
  • +
+
+
+
+\[v_1' = (v_1 * (m_1-m_2) + 2 * m_2 * v_2)) / (m_1 + m_2) \\ +v_2' = (v_2 * (m_2-m_1) + 2 * m_1 * v_1)) / (m_1 + m_2)\] +
+
+
+
    +
  • +

    Finding magnitude in a specific direction

    +
  • +
+
+
+
+\[|\overrightarrow{v}| = \overrightarrow{n} \cdot \overrightarrow{v}\] +
+
+
+
    +
  • +

    Distinct unordered pairs

    +
  • +
+
+
+
+\[(n,2) = \frac{n(n-1)}{2}]\] +
+
+
+
+
+

Seeing if circle collide

+
+
+

Instead of doing this:

+
+
+
+
sqrtf((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)) <= (r1 + r2);
+
+
+
+

We can save some computing speed by removing the square root and just +multiplying the left side by itself:

+
+
+
+
fabs((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)) <= (r1 + r2)*(r1 + r2);
+
+
+
+
+
+

Clamping Velocity

+
+
+

We can use the dot product to find the velocity in specific direction and then +clamp it

+
+
+
+
if (fabs(ball.vx*ball.vx + ball.vy*ball.vy) < 0.01f)
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/docinfo.conf b/docinfo.conf new file mode 100644 index 0000000..57fa5b9 --- /dev/null +++ b/docinfo.conf @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +HOSTNAME=nyquistdev.net +DOCINFODIR=/usr/local/share/readme-to-html diff --git a/notes.adoc b/notes.adoc new file mode 100644 index 0000000..3288e63 --- /dev/null +++ b/notes.adoc @@ -0,0 +1,12 @@ +== GDB + +remember that conditional break points exist + +== Links +* https://www.youtube.com/watch?v=nXrEX6j-Mws&list=PLkhTWnF-dZA7fqD7CF8koSTsRbmfBfr2_ +* https://www.unitconverters.net/typography/pixel-x-to-meter.htm[pixels to meters] +* https://simulationshub.com/ball-drop/[interactive ball] +* https://www.physicslens.com/simulation-of-a-bouncing-ball/ +* https://www.youtube.com/watch?v=hG9SzQxaCm8 +* https://en.wikipedia.org/wiki/Bouncing_ball +* https://youtu.be/LPzyNOHY3A4?si=Vv4pVQ0cbLwPI9Zt[javidx9] diff --git a/rendered.html b/rendered.html new file mode 100644 index 0000000..77e5c8c --- /dev/null +++ b/rendered.html @@ -0,0 +1,951 @@ + + + + + + + +Balls + + + + + + + + + + + + + + + + + + + + +
+
+
+
+

This repository has physics examples using balls. Each folder has a different +example, with a link to the formulas used in the readme’s.

+
+
+
+
+

2D Projectile Motion

+
+
+

checkout this link +formulas. +for a list of forumlas used.

+
+ ++++ + + + + + + + + + + + + + + + + +
controlaction

right-click and drag

determine direction and velocity of ball

right-click release

launch ball

+
+
+
+

Bouncing

+
+
+

check out this link +formulas. +for the formulas used.

+
+
+
+
+

Collisions

+
+
+

Creates random balls. These balls will resolve any collision issues. Launching a +ball will show you an example of elastic collision.

+
+
+

checkout this link +formulas. +for a list of forumlas used.

+
+ ++++ + + + + + + + + + + + + + + + + +
controlaction

right-click

select ball

right-click and left-click and drag

flings ball on release, the distance + away from the ball determines the amount + of velocity to launch the ball at

+
+
+
+

Building html from formula .adoc files

+
+
+
+
./Makehtml make
+
+
+
+
+ +
+ + + + \ No newline at end of file